β©οΈ This repo is Iteration 5 of a software-defined-vehicle (#SDV) prototype. Each iteration grows on its predecessor β reusing and refactoring what still fits, and changing structure where the next goal requires it.
π For a summary (capstone 1.0) of all the phases we have done so far, read here. This phase builds upon the the preceding phases.
| Iteration | Repository | Focus |
|---|---|---|
| 1 | sdv_simulation_1 |
First working CAN control loop |
| 2 | sdv_simulation_2 |
Zone contexts, transition ledger, diagnostics |
| 3 | sdv_simulation_3 |
Headlamp twinlet, quiescent commit |
| 4 | sdv_simulation_4 |
Brain FSM redesign, ROB, Wiper twinlet |
| 5 | sdv_simulation_5 (this repo) |
Gateway/Dashboard split, observation files + live UDS|Zenoh, Dashboard presentation, weather/wiper on ledger |
Iteration 4 twin / ROB design (still the coordination core):
docs/archive/DESIGN-iteration-4.md.
A multi-process SDV prototype on Linux CAN (vcan0):
| Process | Crate | Owns |
|---|---|---|
| Gateway | gateway |
Digital twin, CAN ingress, actuation, observation file tee, optional live publish |
| Dashboard | tui_dashboard |
Observation-only TUI (driver / engineer / ledger tail) |
| Emulator | emulator |
Finite or Ctrl+C CAN lifecycle + RPM / lux / rain |
| Actuators | front_headlamp_actuator, wiper_actuator |
CMD responses on CAN |
Shared library: common (twin, FSM, published records). Persistence / live wire: observation
(schema v3 JSON envelopes).
This README is the source of truth for what the repo is and how to run it. Narrative /
blog drafts live under blog-inputs/ and are accompanying text only.
- Gateway
- Owns the Digital Twin (Actors/FSM/Diagnostic Emitter/Transition Ledger Emitter)
- Connects to either a Unix Domain Socket or a Zenoh peer (based on a command-line parameter), using a given Key-Expression and emits records
- Stores the Ledger in local files as well ( tee mechanism ); readiness for 'replaying logs facility' in future simulations
- Dashboard
- #ratatui-based TUI application; having separate panes for displaying Diagnostics, Transition and Ledger-stream
- Works as the Observation console
- Observation capture and emission
- Dashboard uses versioned
manifest.json+diagnostic.jsonl+ledger.jsonlemitted by Gatway; transportation takes place through Unix Domain Socket or Zenoh (peer mode) - Dashboard displays the records captured, live
- Dashboard panes present Speed Bar, weather/wiper/visibility glyphs, Transition Ledgers
- Diagnostics pane is meant for Drivers; Transition Ledger is meant for Engineers, watching behaviour of the Digital Twin
- Dashboard uses versioned
- Controlled CAN data generation by emulator
- Can emit N records (
--readings); optional--tick-ms(default 100) slows ticks for demos - Ensures that Digital Twin receives a
PowerOnbefore any emulated CAN message and aPowerOffas the last emulated CAN message
- Can emit N records (
Roadmap and deferred gaps: docs/PLAN.md.
Design decisions (this simulation): docs/DESIGN.md.
Topology / gap register: docs/ARCHITECTURE-OVERVIEW.md.
# Terminals β start actuators, then Gateway, then Dashboard, then emulator:
cargo run -p front_headlamp_actuator
cargo run -p wiper_actuator
cargo run -p gateway -- --uds observation.sock
cargo run -p tui_dashboard -- --uds observation.sock
EMULATOR_TUNNEL_PROB=0.01 EMULATOR_RAIN_PROB=0.008 \
cargo run -p emulator -- --readings 30
# Optional: slow ticks for demos (default --tick-ms 100):
# cargo run -p emulator -- --readings 30 --tick-ms 400UDS paths resolve under <cwd>/tmp/ (e.g. ./tmp/observation.sock).
Zenoh peer (instead of UDS):
cargo run -p gateway -- --zenoh --keyexpr sdv/twin/observation
cargo run -p tui_dashboard -- --zenoh --keyexpr sdv/twin/observationHeadless capture (files only): cargo run -p gateway -- --no-live
Runs land under ./observations/<run-id>/.
Smoke scripts: scripts/smoke-two-process.sh, scripts/smoke-zenoh-peer.sh.
Emulator ββCANβββΊ Gateway (twin + ObservationTee) ββfilesβββΊ ./observations/<run-id>/
β
βββ live UDS or Zenoh βββΊ Dashboard (TUI)
βββ CAN CMD ββββΊ Headlamp / Wiper actuators
sequenceDiagram
autonumber
participant E as Emulator
participant HL as Headlamp actuator
participant WP as Wiper actuator
participant G as Gateway (twin + tee)
participant D as Dashboard
participant Disk as ./observations/<run-id>/
Note over HL,WP: Start first β listen on vcan0
HL-->>HL: listen CMD
WP-->>WP: listen CMD
Note over G,D: Live link before twin install
G->>G: bind UDS or open Zenoh
G-->>D: wait for Dashboard
D->>G: connect (UDS accept / Zenoh subscribe)
G->>G: install twin, emit boot diagnostic
G->>Disk: tee boot (+ later records)
G->>D: live boot (+ later records)
Note over E,G: Finite session on CAN (vcan0)
E->>G: CAN 0x100 PowerOn
loop readings (RPM / lux / rain)
E->>G: CAN sensor frames
G->>G: FSM / ROB / assemblies
opt headlamp needed
G->>HL: CAN CMD
HL-->>G: CAN response
end
opt wiper needed
G->>WP: CAN CMD
WP-->>G: CAN response
end
G->>Disk: tee diagnostic + ledger
G->>D: live diagnostic + ledger
D->>D: update Driver / Engineer panes
end
E->>G: CAN 0x100 PowerOff
G->>Disk: tee final records
G->>D: live final records
- Library pyramid L0βL6:
commonis acyclic; Gateway/Dashboard importcommon::facadeonly for twin types. Detail:docs/design-notes-pyramid-layers.md. - Observation schema mirrors published Rust structs (currently v3: weather + wiper + real rain events).
- Twin still uses the Iteration-4 ROB (
barrier_queue) and PreparingToStart/Stop lifecycle β see archived Iter 4 design anddiagrams/.
Both assemblies are peers in the Brain (ROB, tell-back, PreparingToStart/Stop). Headlamp uses
hardware ACK on lux-driven on/off; Wiper is fire-and-forget on rain Start/Stop. Lifecycle
BecomeOff is deliberately incomplete on both: jump to Off with no physical stop CMD
(RequestOff / StopWiping). Source of truth: crates/common/src/vehicle_state/{front_headlamp,wiper}.rs.
Lifecycle (Brain Actor issues StartAssemblies / StopAssemblies):
BecomeOn
Off βββββββββββββββββββΊ Ready
β² β
β β BecomeOff (any β Off;
ββββββββββββββββββββββββββ no RequestOff)
Operational (lux + hardware ACK β assembly stays βupβ):
Ready ββluxβ€ONβββΊ OnRequested ββAckOnβββΊ On
β² β β
β β incomplete/ β luxβ₯OFF
β β timeout βΌ
β | OffRequested ββAckOffβββΊ Ready
βββββββββββββββββββββ β
β β incomplete/timeout
βββββββββββββββββββββββββββββββββββββββββββββ (back to On)
BecomeOn
Off βββββββββββββββββββΊ Ready βββββββ Start ββββββββΊ Running
β² β β² β
β β βββββββββ Stop βββββββββββββ
β β (RainsStopped) β
β β β
βββββ BecomeOff ββββββββββ΄ββββββ BecomeOff βββββββββββββ
(any β Off; (any β Off;
no StopWiping) no StopWiping)
Operational Start/Stop emit StartWiping/StopWiping (β CAN CMD, no ACK).
The Dashboard is observation-only: every pane is driven by what the Twin publishes (diagnostics + ledger), not by a parallel UI model. The same surfaces show the Twin just after start and after PowerOff β only the published state changes.
Session / Driver / Engineer / ledger all agree: FSM Idle, assemblies Ready, boot notice,
ledger through PreparingToStart β Idle.
Same layout; Twin has shut down: FSM Off, Headlamp/Wiper Off, ledger ends PreparingToStop β
SwitchedOff. Weather/wiper glyphs still come from the last published context.
| Pane | Shows |
|---|---|
| Driver (Diagnostic) | Notice, speed bar, visibility (+ lux glyph), weather/wiper glyphs + text |
| Engineer | Current state, last event, Headlamp / Wiper assembly context |
| Ledger tail | Last 20 transition hops (> on newest) |
cargo test -p common -p observation -p gateway -p tui_dashboard -p emulatorContract tests under crates/common/src/test/; observation goldens under
crates/observation/testdata/golden/v3/.
sdv_simulation_5/
βββ Cargo.toml # Workspace root
βββ README.md
βββ assets/
β βββ dashboard-live.gif # README live TUI capture
βββ blog-inputs/ # Stage narratives (accompanying prose)
βββ diagrams/ # Mermaid + Dashboard start/end screenshots
βββ scripts/
β βββ smoke-two-process.sh # Gateway + UDS + emulator
β βββ smoke-zenoh-peer.sh
β βββ check-gateway-facade-imports.sh
βββ docs/
β βββ PLAN.md # Roadmap + TBDs
β βββ DESIGN.md # Stage 5 decisions
β βββ ARCHITECTURE-OVERVIEW.md
β βββ TODO-*.md
β βββ archive/ # Iter 4 DESIGN, detailed PHASES, old specs/plans
βββ observations/ # Runtime capture output (gitignored runs)
βββ crates/
βββ common/ # Twin, FSM, ROB, assemblies, published records, facade
βββ observation/ # Schema DTOs, RunWriter/Reader, tee, UDS/Zenoh live
βββ gateway/ # Twin host, CAN, tee, live publish
βββ tui_dashboard/ # Observation-only TUI
βββ emulator/ # CAN lifecycle + RPM / lux / rain
βββ vehicle_device_bus/ # Headlamp / wiper CAN codecs
βββ front_headlamp_actuator/
βββ wiper_actuator/ # Fire-and-forget motor stand-in
Deeper common pyramid (L0βL5): docs/design-notes-pyramid-layers.md, docs/project-structure.md.
| Doc | Role |
|---|---|
| This README | What the repo contains; how to run |
docs/PLAN.md |
Roadmap + important TBDs |
docs/DESIGN.md |
Stage 5 design decisions |
docs/ARCHITECTURE-OVERVIEW.md |
Topology, gaps, run notes |
docs/archive/ |
Historical design notes (Iter 4 twin, agent specs) |
blog-inputs/ |
Narrative drafts (not repo truth) |
docs/TODO-twin-lifecycle.md |
Shutdown / disband checklist |
docs/TODO-simulation-5.md |
Engineering backlog (actuation, tests, β¦) |
Major future work (detail in docs/PLAN.md):
| Item | Compact plan |
|---|---|
| Standalone replay | Dashboard (or a new tool) plays observations/<run-id>/ without a live Gateway. |
| Shutdown / disband | Quit β Stop β wait Off β tear down actors / ingress (docs/TODO-twin-lifecycle.md). |
| Active ROB turns | Engineer live N from barrier_queue; emit on queue push/drain (not ledger-hop stamps). |
Visibility Swatch |
Colour chips for lux bands (low / hold / bright) instead of unicode boxes alone. |
| Notice colours | Style Notice by DiagnosticLevel / kind (labels already cyan). |
| Glyph ASCII / motion | Optional ASCII fallback for weak terminals; optional wiper/weather animation. |
| Richer assemblies | Deeper Headlamp / Wiper twinlet detail once Twin publishes it. |
| Headlamp unconfirmed | Finish / relocate HeadlampActuationUnconfirmed (zone tell-back candidate). |
| Non-blocking actuation | Engineering backlog β async CMD path without stalling the twin loop (docs/TODO-simulation-5.md). |
| Zenoh as the router | Currently, Zenoh is used in peer more; it should be used as a Router |


