All notable changes to the Railway Interlocking Simulator project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Adds a --debug flag to the :fast-sim native CLI so developers can enable DEBUG-level
kotlin-logging output when diagnosing native-specific issues, while keeping simulation
results on stdout.
StderrAppender— newFormattingAppenderthat routes log messages via POSIXfprintf(stderr, ...), keeping stdout clean for simulation outputconfigureLogging(debug: Boolean)— replaces the hard-codedLevel.OFF; enablesLevel.DEBUG+StderrAppenderwhentrue, restores the default appender and setsLevel.OFFwhenfalsefilterPositionalArgs(args)— extracted internal helper that strips--debug,--verbose, and--quietfrom the raw args list; tested independentlyDebugFlagTest— 14 unit tests covering the constant,configureLogginglevel and appender behaviour (including round-trip and appender identity), and allfilterPositionalArgscombinations
Usage:
# Default: all logging suppressed (unchanged)
fast-sim example shuntingLoop 60
# With --debug: DEBUG-level output on stderr, simulation results on stdout
fast-sim --debug example shuntingLoop 60
fast-sim --debug --verbose sim network.xml 300 2>debug.logDocumentation: docs/superpowers/specs/2026-03-21-fast-sim-design.md updated — CLI
interface table and Logging on Native section reflect the implemented strategy.
Complete animated GUI visualization system for real-time railway simulation.
Core Animation Infrastructure (#201-#205):
- AnimationState - Immutable state snapshots for thread-safe rendering
- TrainState (position, velocity, acceleration, grid location)
- TrackState (FREE/RESERVED/OCCUPIED with ownership tracking)
- SignalState (RED/GREEN semaphore visualization)
- SwitchState (MAIN/BRANCH position display)
- AnimationController - 30 FPS rendering with EDT marshaling
- Swing Timer-driven repaint loop (33ms interval)
- PropertyChangeListener integration for event-driven updates
- Cache optimization for O(1) state queries (20-80× faster than polling)
- Proper resource cleanup (timers, listeners, caches)
- AnimationStateCapture - Efficient state snapshot creation
- Single grid scan at startup, reused throughout animation
- Dynamic cell detection (DynamicRailSemaphore, DynamicRailSwitch support)
- AnimatedSimulationCellRenderer - State-based color rendering
- InOut-based color coding for train direction visualization
- Track state colors: Gray (FREE), Yellow (RESERVED), Red (OCCUPIED)
- ControlPanel - Time display and status updates (10 Hz)
- EventTimelinePanel - Scrollable event log with type-based filtering
- Path commands, node events, train events, continuous updates
- Real-time event display with HH:MM:SS.mmm timestamps
Entry Point (#206):
exampleGuicommand mode for animated simulation examples- Gradle task:
./gradlew runExampleGui - Manual:
java -jar interlockSim.jar exampleGui [name] [endTime]
Performance Optimizations (#207, Performance commits):
- Event-driven animation (eliminated O(n²) polling overhead)
- AnimationController caching: 20-80× faster state updates
- TrainPositionCalculator O(1) cache: 2,500× faster position lookups
- Smooth 30 FPS with multiple trains, zero degradation
Bug Fixes:
- Issue #278: DynamicRailSemaphore signal state capture
- Issue #291: Round-robin path selection for multi-track usage
- Issue #280: Train deadlock resolution
- Animation timer memory leak fixes
Quality Verification (#273):
- Resource cleanup verified (timers, listeners, caches)
- Code quality: Zero detekt violations
- KDoc coverage: 100% across 8 animation files (~2,013 lines)
- Comprehensive test report:
docs/ISSUE_273_TEST_EXECUTION_REPORT.md
Documentation:
docs/IMPLEMENTATION_SUMMARY_ISSUE_205.md- Complete implementation guidedocs/MANUAL_TEST_PLAN_ISSUE_205.md- Manual testing proceduresdocs/ANIMATED_SIM_MILESTONE_PREP.md- Milestone preparation and retrospectivedocs/ANIMATED_SIM_STATUS_MEETING_2026_02_04.md- Team status meeting summary- README.md updated with AnimatedSim feature section
- CLAUDE.md updated with
exampleGuicommand usage
Architecture Achievements:
- Clean separation: animation state (immutable) vs. rendering (stateful)
- Thread-safe: EDT enforcement, immutable snapshots, marshaling
- Performant: Caching, event-driven, optimized update rates
- Maintainable: Comprehensive KDoc, clean architecture
Test Coverage: 1,321+ tests passing, zero regressions, golden output validated
- TopologyNavigator - Static topology navigation service for pure graph traversal
EditingContext.getTopologyNavigator()- Access topology navigator from editing contextTopologyNavigator.findPath()- Find path between separators using static topologyTopologyNavigator.getNextTrackSection()- Navigate topology without state checksTopologyNavigator.findPathToNextSemaphore()- Find path to next semaphore (static)
- PathReservationService - Dispatcher logic for atomic path reservation
SimulationEnvironment.getPathReservationService()- Access reservation servicePathReservationService.reservePath()- Atomically reserve path for trainPathReservationService.releasePath()- Release all blocks owned by trainPathReservationService.findReservablePaths()- Find all FREE paths between separatorsReservationResultsealed class - Success/Failure result type with reason
- TrainNavigationService - Train-specific navigation with ownership validation
SimulationEnvironment.getTrainNavigationService()- Access train navigation serviceTrainNavigationService.findReservedPathForTrain()- Find path through owned blocks onlyTrainNavigationService.isPathReservedForTrain()- Check if path is reserved for trainTrainNavigationService.getReservedBlocks()- Get all blocks owned by train
- PathReservationRegistry - Bidirectional train↔block ownership tracking
- Scoped lifecycle (one registry per SimulationContext)
- O(1) ownership queries in both directions (train → blocks, block → train)
- Shared by PathReservationService and TrainNavigationService within same context
- Documentation
docs/PATH_DISCOVERY_ARCHITECTURE.md- Architecture design, rationale, trade-offsdocs/PATH_DISCOVERY_MIGRATION_GUIDE.md- Migration guide with before/after examples
- Removed ~100 lines of manual path construction logic (
constructPath()method) - Integrated TopologyNavigator for dynamic path finding on-demand
- Simplified path setup using PathReservationService architecture
- Maintains backward compatibility with existing tests (35 tests passing)
- Golden output validation confirms zero behavior changes
- REMOVED
SimulationEnvironment.pathToNextSemaphore(separator, next)- Replaced by:TrainNavigationService.findReservedPathForTrain()for train navigation through owned blocksPathReservationService.reservePath()for dispatcher logic (atomic reservation)TopologyNavigator.findPathToNextSemaphore()for static topology analysis
- REMOVED
SimulationEnvironment.getNextTrackSection(separator, current)- Replaced by:TrainNavigationService.findReservedPathForTrain()for train navigationTopologyNavigator.getNextTrackSection()for static topology
- REMOVED Issue #291 workaround code from ShuntingLoop (~100 lines of manual path construction)
Breaking Change: All code must migrate to new specialized services
Rationale: Original methods mixed three distinct concerns (static topology, dynamic reservation, train navigation), leading to fragility, race conditions (Issue #291), and ownership validation issues (Issue #282). New specialized services provide clean separation with explicit ownership tracking.
Migration Status: All internal callers migrated (Train, InOutWorker, ShuntingLoop)
Migration Guide: See docs/PATH_DISCOVERY_MIGRATION_GUIDE.md for detailed instructions and examples
- Koin 3.5.6 framework integration (Kotlin-native, lightweight DI)
- Module organization:
utilModule- Utility classesxmlModule- XML parsing, XMLContextFactoryeditingModule- Editing context factoriessimulationModule- Simulation context factories, SimulationProcessFactorynavigationModule- Navigation services (TopologyNavigator, PathReservationService, TrainNavigationService)guiModule- Swing components (conditionally loaded)objectsModule- Domain model (minimal by design)
- Scope-per-context pattern for lifecycle management
- AutoCloseable pattern for resource cleanup (
Context.close()) - Full documentation in
KOTLIN-MIGRATION-STATUS.mdanddocs/KOTLIN_STYLE_GUIDE.md
- Issue #98 (2026-01-14): Split DefaultContext into DefaultEditingContext and DefaultSimulationContext
- Issue #153 (2026-01-20): Composition over inheritance - BaseContext abstract base class
- Network immutability enforcement via
freeze()mechanism - Both EditingContext and SimulationContext extend BaseContext independently
- Interface Segregation Principle (SimulationContext no longer extends EditingContext)
- ContextTransformer factory for editing→simulation transformation
- Network immutability enforcement via
- Issue #94 (2026-01-21): SimulationEnvironment facade interface
- Decouples sim/ classes from full SimulationContext contract
- 11 essential methods grouped by concern (network queries, state management, simulation control)
- Enables future DSOL/Kalasim migration via adapter pattern
- Wrapper pattern separates static configuration from dynamic simulation state
Context.toDynamic(track)conversion API- See
docs/STATIC_DYNAMIC_SEPARATION_ARCHITECTURE.md
- Type-safe grid with parameterized cell types (
RailwayNetGrid<out T : Cell>,Context<out C : Cell>) - Covariant return types for EditingContext and SimulationContext
- See
docs/GRID_PARAMETERIZATION_*.md
- 100% of 94 files migrated from Java to Kotlin
- Conservative structure-preserving approach
- Full test parity (242 tests passing)
- Physics calculations validated against Java baseline (tolerance: 1e-9s, 1e-6m)
- kDisco simulation library integration
- Gradle with Kotlin DSL (replaces Ant)
- Java 21 LTS minimum version (migrated from Java 11)
- Shadow JAR plugin for uber JAR creation
- GitHub Actions CI/CD integration
- Docker support for containerized builds
- SonarQube integration with JaCoCo coverage (51% coverage, 662 tests)
- Detekt (Kotlin static analysis) with dual-level configuration:
detekt.yml- Permissive rules for legacy Java→Kotlin converted codedetekt-strict.yml- Strict rules for new Kotlin code
- Ktlint (Kotlin formatting) respecting
.editorconfigtab indentation - See
docs/KOTLIN_STYLE_GUIDE.md
- kotlin-logging (SLF4J wrapper) with Logback backend
- Lambda-based lazy evaluation for performance
- Runtime log level override support
- Build tool: Ant → Gradle with Kotlin DSL
- Java version: 11 → 21 LTS
- Dependency management: Manual JAR files → Gradle with Maven repositories
- jDisco extracted to separate repository: https://github.com/bedaHovorka/jdisco
java.util.Observable/Observer→PropertyChangeSupport/PropertyChangeListener(Issue #167)- Logging: SLF4J → kotlin-logging (wrapper for SLF4J with Kotlin idioms)
- Main source:
src/main/java/(Kotlin files, legacy structure preserved) - Test source:
src/test/java/→src/test/kotlin/(Kotlin tests) - Documentation:
docs/directory added with architecture guides
DefaultContextclass - UseDefaultEditingContextorDefaultSimulationContextinstead (Issue #98)
- Ant build system and
build.xml(replaced by Gradle) - Java 11 compatibility (minimum is now Java 21 LTS)
java.util.Observable/ObserverAPI (replaced by PropertyChangeSupport)
- Fixed train deadlock in path reservation cache
- Migrated from static cells to dynamic wrappers (DynamicInOut, DynamicRailSemaphore, DynamicRailSwitch)
- Updated grid coordinate lookups to retrieve dynamic wrappers
- All paths now use dynamic wrappers for consistent identity
- Added block reservation validation before navigation
- Prevents trains from navigating into blocks not reserved for them
- Validation logic: blocks must be RESERVED from correct separator or OCCUPIED
- No known security vulnerabilities
Initial BSc thesis release (Brno University of Technology, 2006/2007)
- Railway interlocking simulator with discrete event simulation
- Graphical editor for railway network construction
- XML-based network configuration (
data.xsdschema) - kDisco discrete event simulation framework integration
- Example scenarios (shunting loop -
vyhybna.xml) - Swing-based GUI with track layout editor
- Track facilities: switches, semaphores, entry/exit points (InOut)
- Train physics simulation (acceleration, deceleration, braking)
- Path reservation and block management
- Static/dynamic track block representation
- [Unreleased]: Path Discovery Restructuring (Issue #292), Phase 5 complete
- [1.0.0] - 2026-01-27: Kotlin migration, Koin DI, context refactoring, build system modernization
- [0.1.0] - 2007-01-01: Initial BSc thesis release
Breaking Changes:
- Java 11 → Java 21 LTS required
- Ant → Gradle build system (use
./gradlew buildinstead ofant) java.util.Observable/Observerremoved (migrate toPropertyChangeSupport)DefaultContextdeprecated (useDefaultEditingContextorDefaultSimulationContext)
Recommended Steps:
- Update JDK to version 21 LTS or later
- Replace Ant build commands with Gradle equivalents (see
CLAUDE.mdfor commands) - Update code using
Observable/ObservertoPropertyChangeSupport/PropertyChangeListener - Replace
DefaultContextusage withDefaultEditingContextorDefaultSimulationContext - Run full test suite:
./gradlew test(662 tests should pass)
Deprecation Timeline: Issue #292 Phase 5 (2026-01-27)
Old APIs (pathToNextSemaphore, getNextTrackSection) are deprecated with WARNING level. Code still compiles but shows deprecation warnings.
Migration Scenarios:
- Static topology navigation → Use
context.getTopologyNavigator().findPathToNextSemaphore() - Dispatcher finding routes → Use
env.getPathReservationService().reservePath() - Train navigation → Use
env.getTrainNavigationService().findReservedPathForTrain()
See docs/PATH_DISCOVERY_MIGRATION_GUIDE.md for detailed migration instructions with before/after examples.
See TEAM.md for agent roles and decision authority hierarchy when contributing to this project.
This project is part of a BSc thesis from Brno University of Technology (2006/2007).