Home

Index

§7e DCC bus daemon (dcc-bus)

This section specifies the dcc-bus sibling daemon that owns one DCC connection to a command station (centralka) in the context of one layout (makieta) and exposes a dedicated WebSocket port to the frontend for the throttle (tryb sterowania) data plane. It is the runtime counterpart of the Throttle UI specified in §6.3b.

Until §7e is implemented, the throttle dispatch from §5.3 – LocoService.SetSpeed / LocoService.ToggleFn – lives inside loco-server itself, sharing the WebSocket Hub with takeover, radio, scripts and presence. §7e splits the data plane out of loco-server into a per-(layout, command_station) daemon. Authorization rules, domain model, REST surface and security policies (§7a.3) are unchanged – dcc-bus carries its own throttle policy in pkgs/bigfred/dcc-bus/security (DrivePolicy, TrainPolicy); server-side §7a.3 policies stay on loco-server. Only the location of the DCC bus and the delivery of throttle traffic move.

Overview & design goals

Why a sibling daemon, the (layout × command station) cardinality rule, control plane vs. data plane split.

Process model, CLI & supervisord wiring

dcc-bus cobra command, flags, port allocation, lifecycle under SupervisordService (§7d).

State & Redis cache

Vehicle subscription set per layout, polling, key layout for the loco-state hash, command channel for server-initiated DCC writes (scripts, takeover, dead-man).

WebSocket protocol

The throttle action set hosted by dcc-bus (loco.*, system.estop, ping), how it relates to loco-server's WS (session.*, takeover.*, radio.*, script.*).

Authorization & session awareness

JWT validation in dcc-bus, pkgs/bigfred/dcc-bus/security drive policy, takeover / lease propagation via Redis pub/sub, dead-man's switch scoped to the daemon.

Server integration & orchestration

How loco-server decides which dcc-bus programs must run, port assignment, hot reload via supervisord, server-initiated DCC commands (scripts, dead-man, takeover release), session.opened payload extension.

Frontend integration

Dual-WebSocket model in the browser, throttle overlay (§6.3b) wiring, command-station dropdown, reconnect logic, the Throttle component tree.

Acceptance criteria

Externally observable behaviour the milestone must demonstrate.

External state observation

Reflecting speed/direction/function changes made by external physical throttles; driver subscription-vs-polling research, the commandstation.StateObserver capability, and Z21 drive encoding (direction-at-stop, SET S=3 vs. INFO KKK=4).

Quick reference

Concern Decision
Process cardinality One dcc-bus per (layoutId, commandStationId) pair that has at least one active drive session pinned to it. Lazy-started by loco-server; long-lived until shutdown.
Binary The loco-server Go binary exposes a dcc-bus cobra subcommand (single binary, multiple main entry points — same pattern as scripts-executor §7d).
Imports pkgs/loco/commandstation (DCC), pkgs/bigfred/server/domain (entities), pkgs/bigfred/contract (Redis wire types), pkgs/bigfred/dcc-bus/{state,cmd,service,security,…} — see §3.0 / repository tree. Does not import pkgs/bigfred/server/repo, pkgs/bigfred/server/http, pkgs/bigfred/server/ws, or open SQLite. cmd does not import dcc-bus/ws (adapter lives in ws).
Listener Plain http.Server upgraded with coder/websocket on the CLI-supplied port. Binds to 127.0.0.1 by default; loco-server may reverse-proxy it.
Authentication JWT issued by loco-server (shared secret); ?token= query parameter on the WS upgrade, identical to §7a.1. Verifies that the JWT's layoutId matches the daemon's --layout-id.
Authorization Drive commands are gated by in-memory roster snapshots from Redis (allowed_vehiclescontrollerUserIds per DCC address). Lease / takeover expansion of controllerUserIds is planned; today the server publishes the vehicle owner.
Coordination with loco-server Redis only between processes, typed by pkgs/bigfred/contract: loco:state cache, roster snapshots (bigfred:layout:<L>:allowed_vehicles, defined_trains), command/event pub/sub. Command-station connection parameters are passed on the supervisord command line (--station-*). Catalogue truth stays in loco-server's SQLite.
Throttle WS actions loco.subscribe, loco.unsubscribe, loco.setSpeed, loco.toggleFn, train.setSpeed, system.estop, ping. All other WS actions stay on loco-server's /api/v1/ws.
Dead-man's switch Per-daemon: each dcc-bus runs the heartbeat for its own WS clients and executes the user's EmergencyPlan against drive targets on its own command station only (§4.5 still applies for cross-cutting concerns like scripts).
Supervisord group dcc-bus (alongside loco for scripts-executor); programs named dcc-bus-<layoutId>-<commandStationId>.
Failure isolation A crashing dcc-bus does not bring down loco-server; supervisord (§7d) restarts it with autorestart=true. Affected throttles see a stale-cache banner and re-connect on RUNNING.