A discrete-event/continuous combined simulation system for railway interlocking with a graphical track editor and XML-based configuration.
BSc Thesis Project (2006/2007) Brno University of Technology Faculty of Information Technology Author: Bedrich Hovorka
InterlockSim is a Kotlin-based railway interlocking simulator that combines:
- Graphical Editor - Design railway track layouts with switches, semaphores, and entry/exit points
- Discrete Event Simulation - Simulate train movements and interlocking logic using the kDisco framework
- Continuous Simulation - Model continuous train positions and speeds
- XML Configuration - Define and save railway networks in a structured XML format
- Process-Oriented Modeling - Built-in examples demonstrate shunting yard operations
The simulator uses a combined discrete-continuous simulation approach powered by the kDisco library (Keld Helsgaun, Roskilde University).
- Interactive track editor with grid-based layout
- Animated GUI simulation ⭐ NEW - Real-time train movement visualization with event logging (30 FPS)
- XML schema-validated railway network definitions
- Discrete event simulation engine (kDisco-based)
- Built-in examples including shunting loop scenarios
- Swing GUI for visualization and editing
- Assertion-based validation for simulation integrity
- Dynamic state visualization - Track coloring (FREE/RESERVED/OCCUPIED), signal states, switch positions
- Docker and Docker Compose
- X11 Server (for GUI display)
- Linux: Usually already running
- macOS: Install XQuartz
- Windows: Install VcXsrv or Xming
- Java: JDK 21 or later (Java 21 LTS minimum)
- Build Tool: Gradle (wrapper included)
- Dependencies: Automatically managed via Gradle
- kdisco-core (from GitHub Packages)
- JUnit 5.11.4 (from Maven Central)
- AssertJ 3.27.6 (from Maven Central)
- MockK 1.13.14 (from Maven Central) - Kotlin-native mocking for sealed classes
- Mockito 5.21.0 (from Maven Central) - being phased out
- LaTeX, gnuplot, make, wmf2eps, sed
Dockerization: 2025 - Complete containerized build and runtime environment with no host dependencies.
# Build Docker images
docker compose build
# Run graphical editor (X11 forwarding)
docker compose up app
# Run simulation example
docker compose run app java -jar interlockSim.jar example shuntingLoop 60
# Build thesis PDF
docker compose up text
# PDF available in artifacts/text/bakalarka.pdfIf you encounter Can't connect to X11 window server:
# Allow Docker X11 access
xhost +local:docker
docker compose up app
# When done, revoke access
xhost -local:dockerFor more details, see the Docker section below or CLAUDE.md.
The default docker compose run app command opens the Swing editor GUI and requires an X11
server on the Windows host. Docker containers cannot use the Windows display directly, so a
separate X server is needed.
-
Install VcXsrv Download and install from https://sourceforge.net/projects/vcxsrv/
-
Launch XLaunch (included with VcXsrv) with these settings:
- Display settings: Multiple windows, Display number
0 - Client startup: Start no client
- Extra settings: check Disable access control
- Click Finish
- Display settings: Multiple windows, Display number
-
Allow VcXsrv through Windows Firewall Windows will prompt on first launch — click Allow access for both private and public networks. Without this, the Docker container connection is silently dropped.
-
Run the app
docker compose run app
The editor GUI will appear in the VcXsrv window.
The container sets DISPLAY=host.docker.internal:0 when $DISPLAY is not set in the
environment. This routes the X11 connection from the Linux container over TCP to port 6000 on
the Windows host, where VcXsrv is listening. The Unix socket path (/tmp/.X11-unix) used on
Linux does not exist on Windows.
# Clean and build (compiles main + tests, runs tests, creates JAR)
./gradlew clean build
# Run simulation (shunting loop example)
./gradlew runSim
# Run graphical editor
./gradlew runEditor
# Generate JavaDoc
./gradlew javadoc| Task | Description |
|---|---|
./gradlew build |
Compile all sources, run tests, create JAR (build fails if tests fail) |
./gradlew test |
Run unit tests only |
./gradlew integrationTest |
Run integration tests |
./gradlew jmh |
Run JMH performance benchmarks (see src/jmh/kotlin/README.md) |
./gradlew clean |
Remove build artifacts |
./gradlew shadowJar |
Create uber JAR file with all dependencies |
./gradlew runSim |
Run pre-configured shunting loop simulation |
./gradlew runEditor |
Launch graphical editor |
./gradlew runExample |
Run custom example with parameters |
./gradlew javadoc |
Generate JavaDoc documentation |
Note: Dependencies are automatically downloaded during build via Gradle. On Windows, use gradlew.bat instead of ./gradlew.
Open the track editor to design railway layouts:
./gradlew runEditorOr manually (after building):
java -jar build/libs/interlockSim.jar edit [xmlFile]The graphical track editor showing a simple shunting loop layout with entry/exit points and rail switches.
Run a simulation from an XML configuration file:
java -jar build/libs/interlockSim.jar sim [xmlFile]Run pre-configured simulation scenarios:
# List all available examples
java -jar build/libs/interlockSim.jar example
# Run shunting loop example for 300 time units
java -jar build/libs/interlockSim.jar example shuntingLoop 300Quick example:
# Build and run shunting yard simulation (5 minutes model time)
./gradlew clean build
java -jar build/libs/interlockSim.jar example shuntingLoop 300AnimatedSim Milestone - Real-time visual simulation with animated trains and event logging.
Run simulation examples with animated GUI:
# Recommended: Use Gradle task
./gradlew runExampleGui
# Or manually after building
java -jar build/libs/interlockSim.jar exampleGui shuntingLoop 300The animated GUI showing real-time train movement with dynamic track coloring (Gray/Yellow/Red), signal states (RED/GREEN), and event timeline with simulation time display.
Features:
- Real-time train animation - Watch trains move smoothly across the track network (30 FPS)
- Dynamic track coloring - Tracks change color based on state:
- 🟤 Gray - FREE (available for reservation)
- 🟡 Yellow - RESERVED (path set for train)
- 🔴 Red - OCCUPIED (train currently on track)
- Signal visualization - Semaphores show RED (stop) or GREEN (proceed)
- Switch position display - Rail switches indicate MAIN or BRANCH position
- Event timeline - Scrollable log of simulation events with filtering:
- Path commands (reservation, release)
- Node events (switch/signal state changes)
- Train events (stop, exit, arrival)
- Continuous updates (position/velocity at 1 Hz)
- Time display - Current simulation time in HH:MM:SS.mmm format
Example Usage:
# Run with default settings (shunting loop, 300 time units)
./gradlew runExampleGui
# Run specific example with custom duration
java -jar build/libs/interlockSim.jar exampleGui shuntingLoop 600
# Docker users
docker compose run app java -jar interlockSim.jar exampleGui shuntingLoop 300Performance:
- Smooth 30 FPS animation with multiple trains
- Event-driven updates (no polling overhead)
- Optimized state capture with caching (20-80× faster than polling)
Technical Details:
- Built with Swing GUI components
- Thread-safe animation controller (EDT marshaling)
- Immutable state snapshots for rendering
- PropertyChangeListener-based event propagation
See docs/IMPLEMENTATION_SUMMARY_ISSUE_205.md for detailed architecture documentation.
java -jar build/libs/interlockSim.jar (sim|edit|example|exampleGui) [arguments]
Modes:
sim [file.xml]- Run simulation from XML fileedit [file.xml]- Open editor (optionally load file)example [name] [endTime]- Run built-in example (console output)exampleGui [name] [endTime]- Run built-in example with animated GUI ⭐ NEW
Note: For memory-constrained environments, add -Xmx300.
interlockSim/
├── build.gradle.kts # Gradle build configuration (Kotlin DSL)
├── settings.gradle.kts # Gradle settings
├── gradle.properties # Version management
├── gradle/ # Gradle wrapper files
├── src/
│ ├── main/
│ │ ├── java/cz/vutbr/fit/interlockSim/
│ │ │ ├── Main.java # Application entry point
│ │ │ ├── context/ # Simulation context management
│ │ │ ├── gui/ # Swing-based editor
│ │ │ ├── objects/ # Domain model (tracks, cells, paths)
│ │ │ ├── sim/ # Simulation scenarios
│ │ │ ├── xml/ # XML parsing/serialization
│ │ │ └── util/ # Utilities
│ │ └── resources/cz/vutbr/fit/interlockSim/resource/
│ │ ├── data.xsd # XML schema
│ │ ├── vyhybna.xml # Example configuration
│ │ └── logback.xml # Logging configuration
│ └── test/
│ └── java/cz/vutbr/fit/interlockSim/
│ ├── context/ # Context and serialization tests
│ ├── sim/ # Simulation scenario tests
│ ├── util/ # Utility class tests
│ ├── testutil/ # Test utilities and builders
│ └── xml/ # XML parsing tests
├── kdisco/ # kDisco library (separate Maven module, Java 6)
├── build/ # Build output
│ ├── classes/java/main/ # Compiled main classes
│ ├── classes/java/test/ # Compiled test classes
│ ├── libs/ # JAR artifacts
│ ├── reports/ # Test and coverage reports
│ └── test-results/ # Test results
├── text/ # LaTeX thesis source
└── .github/workflows/ # CI/CD workflows
Railway networks are defined using XML with the following elements:
<RailSwitch>- Track switches (points)<RailSemaphore>- Signals<InOut>- Entry and exit points- Track connections with spatial coordinates
Example configuration: src/main/resources/cz/vutbr/fit/interlockSim/resource/vyhybna.xml
XML Schema: src/main/resources/cz/vutbr/fit/interlockSim/resource/data.xsd
Built on kDisco (Java framework for combined discrete and continuous simulation):
- Process-oriented simulation paradigm
- Discrete event scheduling
- Continuous variable support
- Time-determined and state-determined events
Context System - Composition-based architecture (Issue #153, 2026-01-20):
- BaseContext - Abstract base class with shared infrastructure (grid, graph, property change notification)
- DefaultEditingContext : BaseContext - Editing operations (putCell, removeCell, moveCell, joinCells)
- DefaultSimulationContext : BaseContext - Simulation operations (run, stop, pathToNextSemaphore)
- ContextTransformer - Factory for transforming EditingContext to SimulationContext
- Immutability enforcement via freeze() mechanism (simulation contexts are immutable after initialization)
- Factory pattern for context creation (EditingContextFactory, SimulationContextFactory, XMLContextFactory)
- Grid parameterization for type-safe cell access (RailwayNetGrid)
Context Separation:
- EditingContext and SimulationContext are separate interfaces (no inheritance between them)
- Both implementations extend BaseContext independently (composition over inheritance)
- Follows Interface Segregation Principle - simulation contexts do NOT support editing operations
- Network structure is frozen at simulation initialization time for correctness
Object Model - Track facilities, blocks, cells, and paths:
- Static objects (NodeCell: RailSwitch, RailSemaphore, InOut)
- Dynamic wrappers (DynamicPathSeparator, DynamicTrack) for simulation state
- Grid-based spatial representation with pathfinding
GUI - Swing-based editor with grid canvas
XML Factory - Schema-validated configuration loading
The project includes Docker support for both the Java application and LaTeX thesis compilation, eliminating the need to install Java 21, Gradle, or LaTeX tools on the host machine.
- app - Java application with GUI support (X11 forwarding)
- text - LaTeX thesis compilation
Build both services:
docker compose buildRun editor GUI:
# Method 1 (Recommended): Use .Xauthority file (more secure)
docker compose up app
# Method 2: If you get authorization errors, allow X11 connections from Docker
xhost +local:docker
docker compose up app
# When done with Method 2, revoke access for security:
xhost -local:dockerRun simulation example:
docker compose run app java -jar interlockSim.jar example shuntingLoop 60Run simulation with custom XML:
docker compose run -v $(pwd)/myfile.xml:/app/myfile.xml app java -jar interlockSim.jar sim myfile.xmlBuild thesis PDF:
docker compose up text
# PDF will be available in artifacts/text/bakalarka.pdfExtract compiled JAR:
docker compose build app
# JAR will be available in artifacts/app/interlockSim.jarRoot Dockerfile (multi-stage build):
- Builder stage - Uses Eclipse Temurin 21 JDK
- Builds kDisco dependency (Maven install, Java 6 compatibility)
- Resolves dependencies via Gradle (automatic download)
- Compiles Java sources (Java 21 target)
- Runs all tests (build fails if tests fail)
- Creates uber JAR with all dependencies
- Runner stage - Eclipse Temurin 21 JRE with X11 libraries
- Minimal runtime environment
- X11 forwarding for GUI support
- No build tools in final image
text/Dockerfile:
- Based on Debian Bookworm
- Full TeX Live installation with Czech language support
- Image conversion tools (wmf2eps, autotrace, gnuplot)
- Compiles thesis PDF from LaTeX sources
Both services copy build outputs to /artifacts inside the container, which is mounted to ./artifacts/ on the host:
artifacts/app/interlockSim.jar- Compiled applicationartifacts/text/bakalarka.pdf- Compiled thesis
The application uses SLF4J with Logback for comprehensive logging of simulation events and operations.
Main application: src/main/resources/logback.xml
kDisco tests: kdisco/src/test/resources/simplelogger.properties
Available log levels (from most to least verbose):
TRACE- Very detailed diagnostic informationDEBUG- Detailed information for debuggingINFO- General informational messages (default)WARN- Warning messages for potential issuesERROR- Error messages for failures
Method 1: Edit logback.xml (recommended for development)
Edit src/main/resources/logback.xml:
<!-- Change root logger level (affects all loggers) -->
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
</root>
<!-- Or change specific package/class level -->
<logger name="cz.vutbr.fit.interlockSim.sim.Train" level="TRACE"/>
<logger name="cz.vutbr.fit.interlockSim.sim.ShuntingLoop" level="DEBUG"/>Method 2: System property (runtime override)
java -Dlogback.level=DEBUG -cp "build/main:lib/compile/*" cz.vutbr.fit.interlockSim.Main example shuntingLoop 300Method 3: Environment variable (Docker)
docker compose run -e ROOT_LOG_LEVEL=DEBUG app java -jar interlockSim.jar example shuntingLoop 60The following loggers are pre-configured in logback.xml:
cz.vutbr.fit.interlockSim.simulation- Simulation events (INFO)kDisco.statistics- kDisco statistical reports (INFO)cz.vutbr.fit.interlockSim.sim.Train- Train behavior (DEBUG)cz.vutbr.fit.interlockSim.sim.ShuntingLoop- Shunting operations (DEBUG)cz.vutbr.fit.interlockSim.objects.paths.AbstractPath- Path management (DEBUG)cz.vutbr.fit.interlockSim.objects.tracks.SimpleTrack- Track operations (DEBUG)
Console: Real-time output with format: HH:mm:ss.SSS [thread] LEVEL Logger.method(File:Line) - message
File: logs/interlockSim.log with timestamp format: yyyy-MM-dd HH:mm:ss.SSS [thread] LEVEL Logger.method(File:Line) - message
Comprehensive JUnit 5.11.4 test suite with AssertK 0.28.1 assertions located in src/test/kotlin/cz/vutbr/fit/interlockSim/.
Test coverage statistics (February 2026):
- 1840 tests total (1836 passing, 4 skipped, 0 failing)
- 51% code coverage (8,824/17,070 instructions covered)
- 145 test classes across 6 expansion phases
- +1598 tests added in test coverage expansion initiative (baseline: 242 tests → 1840 tests)
Coverage by package:
- objects.tracks/ - 85% (excellent), xml/ - 85% (excellent)
- util/ - 75% (good), objects.cells/ - 72% (good), context/ - 70% (good)
- objects.paths/ - 52% (medium), sim/ - 33% (limited by kDisco framework)
Test expansion phases completed (2026-01-10):
- Safety-critical components (Train physics, Track state, RailSwitch, RailSemaphore)
- Simulation engine core (Train state transitions, path interaction, InOutWorker)
- Path and track integration (AbstractPath, path/track coordination)
- Main entry points and cell edge cases (CLI parsing, example loading, NodeCell)
- Generator and advanced simulations (Generator, shunting operations, timetables)
- Exception handling and edge cases (SimulationException, validation, deadlock detection)
Run tests:
# Run unit tests only
./gradlew test
# Run integration tests
./gradlew integrationTest
# Run all tests
./gradlew test integrationTest
# Run JMH performance benchmarks
./gradlew jmh
# Or as part of build
./gradlew clean buildTests are automatically executed during the build process. The build will fail if any test fails.
Performance Benchmarks: JMH benchmarks are available in src/jmh/ for accurate performance measurements. See src/jmh/kotlin/README.md for details.
CLAUDE.md- Comprehensive development guide (build, test, architecture)STATIC_DYNAMIC_SEPARATION_ARCHITECTURE.md- Complete static/dynamic separation architecture with diagramsdocs/CONTEXT_REFACTORING_DESIGN.md- Context refactoring and factory pattern designdocs/FACTORY_PATTERN_IMPLEMENTATION.md- Factory pattern implementation summarydocs/KOTLIN_STYLE_GUIDE.md- Kotlin coding standards and detekt configurationTEAM.md- Multi-agent development workflows and roles
Build the thesis PDF:
cd text
makeRequirements: LaTeX, gnuplot, make, wmf2eps, sed
Generate JavaDoc:
./gradlew javadocOutput: build/docs/javadoc/ directory
The project currently uses kDisco (2004, no longer maintained). Research has identified modern alternatives for potential migration:
- DSOL - Combined discrete-continuous simulation (Java 17+, actively maintained)
- Kalasim - Discrete event simulation (Kotlin-native with coroutines)
- SSJ - Stochastic simulation (Université de Montréal)
See jdisco-research.md for comprehensive analysis.
InterlockSim © 2006-2007 Bedrich Hovorka BSc Thesis, Brno University of Technology
kDisco Library © 2001-2004 Keld Helsgaun, Roskilde University, Denmark Research use only
This repository includes:
- Complete Java source code (interlockSim + kDisco library)
- Gradle build system with Kotlin DSL
- Comprehensive JUnit 5 test suite (237 tests)
- XML schemas and example configurations
- Docker support for containerized builds
- GitHub Actions CI/CD workflows
- LaTeX thesis source and images
- Documentation (JavaDoc, README, CLAUDE.md)
The project uses GitHub Actions for continuous integration and quality assurance:
- Gradle Build (Java 21) - Compiles, tests, and creates JAR artifacts
- SonarQube Analysis - Code quality analysis and coverage reporting
- Claude Code Review - Automated code review for pull requests
View workflow status: GitHub Actions
Current Status:
- 662 tests (628 passing, 34 skipped, 0 failing)
- 51% code coverage (8,824/17,070 instructions covered)
- 36 test classes with comprehensive unit and integration tests
Coverage Reports:
- JaCoCo reports generated with every build
- Available in CI artifacts:
jacoco-coverage-report-{sha} - View locally:
./gradlew jacocoTestReport→build/reports/jacoco/test/html/index.html
For information on:
- Running CI checks locally
- Verifying test coverage improvements
- Creating PRs with proper documentation
- Understanding CI/CD workflows
See:
- Contributing Guide - Complete contribution guidelines
- PR Template - Pull request checklist
- CI/CD Quick Reference - CI status badges and commands
Project: Railway Interlocking Simulator (InterlockSim v0.1-bachelor) Institution: Brno University of Technology, Faculty of Information Technology Year: 2006/2007
For development guidance, see CLAUDE.md.

