Home

06 radio stop

4.6 Radio Stop – layout-wide emergency halt

Radio Stop (radiostop) is a layout-wide emergency signal modelled on the real railway practice where a driver broadcasts an immediate halt instruction over the radio. In BigFred it is distinct from:

Radio Stop is a deliberate, human-triggered, layout-scoped halt with audible feedback on every open throttle session.

4.6.1 Behaviour

When a user triggers Radio Stop:

  1. Every drivable vehicle on the layout roster receives a DCC emergency stop (SetSpeed with the EMG-stop bit, speed step 1 on the wire) on every command station attached to the layout, regardless of who is currently driving it or which command station their session has picked.
  2. Every open throttle session in the layout (any user, any command-station pick) receives a system.radioStop push event and plays the radiostop sound locally. The sound is a bundled UI asset (not a DCC function); it is the same clip on every client so all operators hear the alarm simultaneously.
  3. Running scripts owned by any user on affected vehicles are interrupted with reason "radio_stop" (same class of side effect as the dead-man's switch path in §4.5.3a).
  4. Every connected driver's dead-man's-switch emergency plan is fired as a second, coordinated effect (§4.6.1a), so the same fail-safe machinery a lost session would trigger runs deliberately for everyone at once.
  5. The action is audited as system.radio_stop (§3a.5).

Vehicles already at standstill (cached speed 0) are still included in the audit row but may be skipped on the wire to avoid spurious speed-1 frames (same rule as manual system.estop in §7e.5).

4.6.1a Hybrid execution – roster halt and per-user dead-man's plan

Radio Stop is implemented as two coordinated effects, not one:

The two effects are complementary: (a) guarantees the track is quiet even for un-driven vehicles; (b) guarantees each operator's own fail-safe (scripts, leases) is honoured exactly as if their last session had just dropped.

Two guardrails keep Radio Stop a halt, never a power escalation:

In other words, the radiostop path runs each user's plan clamped to the [stop_my_vehicles, release_my_leases] band.

4.6.2 Authorization

Two independent grounds authorize Radio Stop in the active layout:

  1. Drive scope — any user who may drive at least one vehicle or train:
  2. a driver on an owned or leased vehicle;
  3. a user with a temporary driver grant that covers at least one roster vehicle.
  4. Signalman role — any user who holds the signalman role in the active layout (layout-scoped grant or self-grant). This is independent of whether they currently occupy an interlocking or hold a takeover: a signalman directing traffic must be able to halt the layout in an emergency even when they are not driving anything. This is the explicit broadening over the earlier rule, which only let a signalman trigger Radio Stop while holding active takeover authority.

Users who satisfy neither ground cannot trigger Radio Stop. admin alone is still not sufficient – the permanent admin role implies neither drive rights nor the signalman role (§7a.5). Admins who also hold driver or signalman follow the rules above.

The check is implemented once in RadioStopSecurityContext.CanTrigger (§7a.3) and reused by the WS handler, MCP tool surface and any future REST alias. Its inputs are therefore the drivable roster and the caller's effective roles in the layout (so the signalman ground can be evaluated); Allow if either ground passes.

4.6.3 UI affordance

Radio Stop is exposed as a dedicated button on the throttle overlay's left toolbar (§6.3b), separate from the per-session emergency brake (system.estop).

Placement. The throttle overlay carries a vertical/horizontal toolbar pinned to the left edge of the driving surface. Its first control is the Fullscreen toggle (browser Fullscreen API on the overlay container); the Radio Stop button sits immediately to its right. The Radio Stop button is visually distinct (red, with a radio-handset / RadioButtonChecked icon) so it is never confused with the narrower per-vehicle estop control.

Second placement — interlocking view. The staffed interlocking view (§6.3d) also surfaces Radio Stop, above its panels, so a signalman can halt the layout without entering throttle mode. It reuses the same component and confirm overlay but renders the icon together with the text label „Radio stop" (the throttle placement is icon-only). It is shown to any signalman staffing the box per the signalman ground in §4.6.2.

Confirmation overlay (destructive action). Tapping Radio Stop does not fire immediately. It opens a modal overlay centred on the screen (MUI Dialog/Backdrop) containing, stacked vertically:

  1. a primary red button „Uruchom radiostop” (Trigger radio stop) — sends system.radioStop {} on the control plane and closes the overlay;
  2. below it, a neutral „Anuluj” (Cancel) button — dismisses the overlay with no side effect.

Only „Uruchom radiostop” emits the WS frame; the dialog is the sole guard against accidental layout-wide halts.

Audible feedback. On receipt of the system.radioStop push event (§4.6.5), every open throttle session plays the bundled alarm asset served at /sounds/radiostop.ogg. It is a static UI asset (not a DCC function and not locale-dependent); the same clip plays on every client so all operators hear the alarm simultaneously. Playback is best-effort: browsers that block autoplay until a user gesture will still have one (the operator who pressed the button), and the alarm is unlocked for the rest of the session on first interaction.

Strings live in throttle.json (throttle.radioStop.*, throttle.fullscreen.*).

4.6.4 Cross-process coordination

Radio Stop is a layout-level action; a layout may span multiple command stations (§3a.4). loco-server owns the orchestration:

  1. Client sends system.radioStop {} on the control-plane WebSocket (/api/v1/ws).
  2. loco-server validates RadioStopSecurityContext.CanTrigger, then:
  3. interrupts all running scripts on the layout (ScriptService.StopAllForLayout, reason "radio_stop") so no script can race the halt regardless of owner;
  4. runs the per-user dead-man's plan (§4.6.1a effect b) for every connected user in the layout that has active DriveTargets, via the existing emergency-plan executor (action clamped to the [stop_my_vehicles, release_my_leases] band);
  5. fans out a control command to every running dcc-bus daemon for the layout (Redis pub/sub on bigfred:layout:<L>:radio_stop, same fan-out pattern as bigfred:layout:<L>:emergency:<userId> in §4.5.3b) to cover the roster (§4.6.1a effect a).
  6. Each dcc-bus runs its local applyEStopAll against the vehicles on its command station and publishes affected addresses on dcc-bus:evt:<L>:<C>.
  7. loco-server aggregates the per-station results, writes the audit row, and broadcasts system.radioStop to every control-plane session in the layout (not only throttle sessions – the event is harmless on the dashboard; clients without an open throttle overlay ignore the audio hook).

Debounce: at most one Radio Stop per layout per 2 s so a double-tap or two operators pressing simultaneously do not stampede the command stations.

4.6.5 WebSocket message types

Client → Server (control plane only):

Server → Client (control plane, every session in the layout):

There is intentionally no system.radioStop action on the dcc-bus data-plane WebSocket – the halt is never scoped to a single command-station pick the way system.estop is.

4.6.6 Relation to walkie-talkie radio

The walkie-talkie channel (§4.4) and Radio Stop solve different problems. STOP_IMMEDIATELY is a phrase addressed to one user or one interlocking; Radio Stop is a system command that brakes the entire layout and sounds the alarm everywhere. A driver may use both in the same operating session, but they do not subsume one another.