Repository navigation
District Simulators and Train Tracking
Status: plan (2026-11-02). Built (2026-11-02): steps 1 to 8. Step 1:
--sim-district(src/district_sim.rs). Step 2:--session, bodies cross between district simulators on the world channel. Step 3:--layouton a node (src/district_track.rs): named blocks and turnouts, the interrogate at bus start. Step 4: following trains (src/train_tracking.rs):--place-train, block by block, ghosts and ambiguities, the loco register's speed and the known turnout positions deciding between candidates, the picture onhive/<session>/node/<id>/trains. Step 5: the handoff: the receiving node takes a train from its neighbour's picture and the loco into its district (LocoRegister::enter_district), the sending node lets go once its border block is free. Step 6, safety: default Stop at the border (a train running towards a border is held at speed 0 on its district's DCC-EX, and the boundary signal in its edge block shows Hp 0, unless the neighbour is heard from, both districts are ON and no train of the neighbour's stands in front of that edge block); the district simulator flags a train running into a district without power. Tested with the neighbour switched off (tools/district_sim_step6.py), its node lost and started again (step6_lost.py) and cut off by a network split (step6_split.py). Step 7, simulated operators (--sim-operators,src/sim_operators.rs): each runs a train back and forth between two blocks through the nodes' throttles, setting the turnouts on its way through the nodes and seeing its train in the simulators' truth (hive-sim/<session>/world/truth); tested over two round trips across the border (step7.py). Timetables are still to come. Step 8, the web view from the mesh (--web-mesh, with--truthfor the simulators' trains over it), tested against the truth (step8.py). All eight steps are built. Since: a lite node's district is adopted by the full node that carries it (--lite-viafor testing,tools/district_sim_step9.py); a station is one module, withboxeswhen it is built of several (the example's Cdorf merged intotimo-cdorf).
Contents
Today one layout simulator (--web-sim, src/layout_sim.rs) does everything: it moves the trains, reserves their routes, sets the signals and turnouts, and knows which train is where. The nodes only switch power, run the fast clock and share the loco register. That is fine for a demo, but it cannot test the core idea (Architecture §10): a simulator that knows everything also knows which train reaches a border, so the nodes never have to work it out.
So a second simulator is built beside the first: one district simulator per district, which plays only that district's hardware. The nodes do the thinking.
-
The layout simulator stays as it is: demo, web view, regression tests, and the bench (
scripts/bench-m5.sh). The district simulator is an evolution next to it, not a replacement. It reuses the layout simulator's pure parts (the track graph,src/topology.rs) and does not change its behaviour. - The simulator plays hardware, nothing more. It tells Hive-Rail only what real hardware would: occupancy, a decoder's output report, a track overload. It never tells which train it is.
- Any bus. Each district has its own bus; which bus does not matter to the mesh. LocoNet is the first, CAN bus, RocNet modules or others can follow. The node's device map works on district, bus, address space and address, with a bus adapter underneath.
-
One LocoNet never spans two districts. That is what the design forbids and Hive-Rail fixes, so addresses only have to be unique within a district (
--check-layoutchecks that). - Each step runs on its own and is shown before the next.
The layout simulator plays three roles at once. They come apart:
| Role | What | Where |
|---|---|---|
| World | Trains move, detectors become occupied, turnouts move, decoders obey their command station | One district simulator per district |
| Logic | Following trains, the train picture, handoff at the border, default Stop at the border, signals | The Hive-Rail nodes |
| Operators | When a train leaves, which way, how fast: drivers and station operators | Simulated people, who drive through a throttle as a person does |
hive-rail --sim-district <layout.json> --district <name> plays one district as a Hive-Rail node board: DCC-EX over TCP (port 2560) and the board's LocoNet buffer (LbServer, port 1234). A node joins it as it joins a real board:
hive-rail --sim-district examples/layout/bench-m5.json --district MacBook --body 4444:1200@timo-adorf/adorf_1
hive-rail --mode node --session bench --id MacBook --rocnet-address 101 \
--dccex 127.0.0.1:2560 --loconet-connect 127.0.0.1:1234 --loconet-connect-slots node --loconet-tcp| The node sends (DCC-EX) | The district simulator |
|---|---|
<1 MAIN>, <0>
|
Track power on and off, answered <p1 A> / <p0 A>
|
<!> |
Emergency stop: every decoder on the district's track to speed 0 |
<t cab speed dir>, <F …>
|
The decoder with that address, if it stands on the district's track, takes the speed and direction |
<a address 1|0> |
The turnout with that address moves (closed 1) |
| The district simulator reports | How |
|---|---|
| A block becomes occupied or free |
OPC_INPUT_REP on its LocoNet buffer, from the block's detector |
| A collision, a run-through turnout, a buffer stop hit | A track overload, <* TRACK A POWER OVERLOAD *>, and the district's power off, as a real short does; logged as a fault |
Bodies, not trains. A body has a decoder address, a length and a top speed. It moves at the speed its decoder holds, along the turnouts as they lie, not along a route. A decoder keeps its last speed until its command station tells it otherwise, as DCC decoders do; without track power it stops. The decoder address is part of the physical loco, so the simulator knows it, but it never reports it.
| Step | What | Shown by |
|---|---|---|
| 1. One district's world | The district simulator for one district: bodies, decoders, turnouts, occupancy on the LocoNet buffer, DCC-EX for power, locos and turnouts. No node change | A throttle on the node's LocoNet over TCP drives loco 4444; the node logs the occupancy as the train moves; a turnout thrown from the throttle moves in the simulator |
| 2. The border | Simulators of neighbouring districts pass a body on a world channel of their own (hive-sim/<session>/world/…, never read by Hive-Rail), physics only. The decoder runs on at its last speed until the neighbour's command station sends to it |
A train driven from MacBook runs into Windows at its last speed: Windows' node sees occupancy but does not know which train it is. The gap the next steps close |
| 3. The node knows its track | The node reads the layout (--layout), takes its own district, and builds the device map behind bus adapters (LocoNet first; DCC-EX; the address made wide enough for CBUS, RocNet modules and LCC later). Its log, events and the mesh name what happened: "block timo-adorf/adorf_1 occupied" |
The node's dashboard and the dispatcher show named blocks |
| 4. Following trains | On the node: placing a train, following it block by block over the turnout positions, gaps, ghosts. The train picture replicated on every node with versions, as the loco register is | The district simulators publish the truth on their world channel; a test tool compares it with every node's train picture |
| 5. Handoff | A followed train occupying the border block is taken by the receiving node (as 8.7 takes a loco today) and released by the sending node once its blocks are free; throttle commands go to the district that holds the loco (8.4) | A train driven by hand runs from MacBook through Windows and back without stopping, and keeps its name |
| 6. Safety | Default Stop at the border: the boundary signal shows proceed only when the neighbour is fresh, powered and has granted the handoff; on a fault, locos in the edge block stop. Signals as outputs (LocoNet switch, DCC-EX <a>). Faults injected into the simulators: a node killed, a short, a network split |
Invariants checked on the truth: no collision, no train into an unpowered or unconfirmed district |
| 7. Operators | Simulated drivers and station operators drive through the node's throttle interfaces, with timetables | The bench runs by itself, as the layout simulator does today |
| 8. Web view on the mesh | A third mode beside --web-sim: the layout drawn from the nodes' train picture and device states, with the simulators' truth as an overlay while testing |
The same picture as --web-sim, from the mesh |
A small step apart: duplicate loco addresses (two owners with loco 3: a warning and the operator decides), checking the addresses of --show-sensors against the layout, and RocNet IDs in --check-layout.
| # | Question | Notes |
|---|---|---|
| DS-Q1 | How fast does a turnout move in the simulator, and does it report its position (OPC_SW_REP)? |
Start with an instant move and no report; slow motors later |
| DS-Q2 | Does a body need a top speed per loco, or one for all? | Per body, from --body; the layout simulator's train types later |
| DS-Q3 | What does the world channel carry when a body spans two districts? | The owner (the head's district) publishes the body; every simulator computes occupancy for its own track from it |