SafeAPI RBC 2oo2 Test Environment
End-to-end Robot Framework test suite + Docker Compose stack for the safeAPIRBC2oo2 system
Loading...
Searching...
No Matches
Per-Sim Message Interfaces

Status: proposed, not yet implemented - see DESIGN.md for the protocol shape (sendMessage/getMessage JSON commands) this document assumes. Field names and message kinds below are taken directly from the real wire protocol (rbc_wire_types.h, rbc_wire.py, ADR-029) - nothing here is invented; every field listed is an actual rbc_envelope_t member, and every message kind is an actual rbc_msg_kind_t value.

nidEngine/nid_engine below is always train_id on the wire (1 or 2

  • see SAFEAPI_EXAMPLE_MAX_TRAINS, common_config.h): which train/IL instance a message concerns, not which sim process handles it (each Train/IL sim instance has a fixed, single nid_engine set at startup via RBC_TRAIN_INDEX/train_id = RBC_TRAIN_INDEX + 1 - see train_sim.py/il_sim.py's own headers - but sendMessage/getMessage still take it explicitly so a future multi-train-per-process sim would not need a protocol change).

Train Sim (sims/train_sim.py)

The only sim that both sends and receives meaningfully - it originates the train's own reports and acks, and is the target of the RBC's own decisions.

Sends (test -> sim -> RBC)

Message Packet Fields Meaning
P0 P0 (none) Handshake - establishes this nid_engine with the RBC. Sent automatically once per site connection today (train_sim.py's own connect logic); exposing it via sendMessage mainly matters for tests that want to force a fresh P0 (e.g. simulating a re-handshake) rather than relying on the sim's own startup timing.
M136 M136 d_lrbg (position, meters), cycle (this train's own free-running report number) Cyclic position report. Today's REPORT <d_lrbg> control command already does this on demand - Send Train Message with M136 is the structured equivalent, additionally letting a test set cycle explicitly rather than always using the sim's own auto-incrementing counter.
M146 M146 ma_seq (echoes the MA sequence being acknowledged) Acks a received MA. Today sent automatically by train_sim.py immediately upon receiving an M3 - exposing it via sendMessage is mainly for fault-injection-style tests that want to test the RBC's OWN behavior when an ack is deliberately withheld or sent with a wrong ma_seq (not achievable at all with today's log-only interface).

Receives (RBC -> sim, queryable via getMessage)

Message Packet Fields Meaning
M24_M15 M24_M15 cycle (the M136 cycle being acked), msg_type (24 or 15 - which of the two position-report acks this is; see SAFEAPI_EXAMPLE_MSG_M24/_M15, common_config.h) Position-report ack. Today only visible as "WEST: M24 ack (cycle N)"/"M15 ack" log lines (train_sim.py) - getMessage <nid_engine> M24_M15 exposes the same fact with real fields, including telling M24 and M15 apart by msg_type rather than by which log-string substring matched.
M3 M3 ma_seq (monotonic MA sequence for this train), ma_length (cumulative granted length, meters) Movement Authority grant/extend. Today's Train Log Should Contain ... Movement Authority - length=${N}m becomes Verify Packet <nid_engine> M3 M3 ma_length ${N} - same fact, but also makes ma_seq checkable, which the log line never exposed at all.

IL Sim (sims/il_sim.py)

Sends (test -> sim -> RBC)

Message Packet Fields Meaning
ROUTE_ADD ROUTE_ADD route_len (meters granted/extended by this one route) Legacy flat-scheme route grant, route-tracking only now (MA comes from GP's own position gate). Set IL Route + Send IL Message.
IL_ROUTE_CMD (dedicated, il_wire.py) route_type {NoRoute,Locked}, route_status {NoStatus,FS,OS,SH,Used,Degraded}, degraded_status {NoStatus,Low,Medium,High}, release_route_request {No,Yes}, first_route_in_path {No,Yes}, route_id/start_signal/end_signal (int32, -1 = unset) The full IL route command. Drives the per-route state machine (ab_ga_route_fsm.c): NO_ROUTE / LOCKED / FS / USED / release are real, OS / SH / DEGRADED are tracked-but-stubbed. Keyword: Send IL Route Command (enum args accept a name or an int; every arg's default is the spec default).
IL_STATUS (dedicated, il_wire.py) il_status {Down,Restarting,Up} This IL's health. Announced UP at startup + every ROUTE_STATUS_PERIOD_S. Keyword: Set IL Status <down\|restarting\|up> (control command SET_IL_STATUS). The RBC's DOWN/RESTARTING hold reaction is stubbed (logged, not acted on).

Receives (RBC -> sim, queryable via getMessage)

Message Packet Fields Meaning
TrainPositionInRoute TrainPositionInRoute start_signal, end_signal, route_status (0 noTrain / 1 trainInRoute) Legacy liveness-first route occupancy indication. Wait For IL Message / Verify IL Packet.
IL_ROUTE_INDICATION IL_ROUTE_INDICATION (flat dict, dedicated il_wire.py) route_id, start_signal, end_signal, train_in_route (0/1), train_has_ma_in_route (0/1), allocated_train_nid_engine (-1 = none), emergency_in_route (0/1), emergency_type (-1 none / 0 SAM / 1 CEM / 2 UEM) The richer per-route indication, pushed on every route change alongside TrainPositionInRoute. emergency_* are always "none" for now (no per-route emergency tracking wired). Keyed under getMessage by allocated_train_nid_engine. Keywords: Wait For IL Route Indication <nid_engine> / Verify IL Route Indication Field <nid_engine> <field> <expected>.

CTC Sim (sims/ctc_sim.py)

Receive-only - CTC is a pure observer in this scenario (see ctc_sim.py's own header: "just logs whatever indications arrive").

Receives (RBC -> sim, queryable via getMessage)

nid_engine here identifies which train the indication is about, not a CTC identity (there is exactly one CTC instance, overseeing both sites - ctc_sim.py's own header) - getMessage against the CTC sim still takes nid_engine for symmetry with Train/IL, meaning "the indication concerning this train."

Message Packet Fields Meaning
CTC_CONNECTED CTC_CONNECTED d_lrbg (position at time of connection) Train connected + its position. Today's CTC Log Should Contain ... train N connected - position Nm ... becomes Verify Packet <nid_engine> CTC_CONNECTED CTC_CONNECTED d_lrbg ${N}.
CTC_MA_GRANTED CTC_MA_GRANTED ma_seq, ma_length Train has an MA (first grant specifically - see CTC_MA_EXTENDED below for the distinction the RBC itself already makes, channel_ab.c's own post_execute()).
CTC_MA_EXTENDED CTC_MA_EXTENDED ma_seq, ma_length MA length updated (a subsequent grant for a train that already had one - not a fresh CTC_MA_GRANTED). Because getMessage returns only the most-recently-received instance (DESIGN.md section 4.2), a test that grants then extends an MA and wants to assert both events happened, in order, needs to Wait Train Message for CTC_MA_GRANTED before commanding the second ADD_ROUTE that produces CTC_MA_EXTENDED - sequencing the test's own steps, not something the interface enforces for it.

Sends

None - CTC never transmits anything in this scenario.

Field reference (every field, once, for lookup)

Field Type (as encoded) Appears in
d_lrbg uint32, meters M136, CTC_CONNECTED
cycle uint32 M136, M24_M15
msg_type uint32 (24 or 15) M24_M15
route_len uint32, meters ROUTE_ADD
ma_seq uint32 M3, M146, CTC_MA_GRANTED, CTC_MA_EXTENDED
ma_length uint32, meters M3, CTC_MA_GRANTED, CTC_MA_EXTENDED

train_id/nid_engine and kind/message are not listed as "fields" here since they are the addressing/selection keys (sendMessage's nidEngine/message, getMessage's nidEngine/message), not part of a message's own fields/packets payload.