SimCore
Shared transport-layer plumbing for the ADR-029 Train/IL/CTC RBC simulators
Loading...
Searching...
No Matches
simcore.rbc_wire Namespace Reference

Functions

 encode (kind, train_id, nid_message=0, l_message=ENVELOPE_SIZE, t_train=0, cycle=0, d_lrbg=0, msg_type=0, route_len=0, ma_seq=0, ma_length=0, t_train_ack=0, nid_lrbg=0, q_dirlrbg=0, q_dlrbg=0, l_doubtover=0, l_doubtunder=0, q_length=0, v_train=0, q_dirtrain=0, m_mode=0, m_level=0, start_signal=0, end_signal=0, route_type=0, route_status=0)
 encode_opaque (kind, payload, frame_size=ENVELOPE_SIZE)
 decode (data)

Variables

int ENVELOPE_SIZE = 96
 _STRUCT = struct.Struct("<4B2H22I")

Detailed Description

RBC envelope codec (ADR-029) - Python mirror of the WIRE LAYOUT in
safeAPIRBC2oo2/src/application/rbc_wire.c/rbc_wire_types.h (struct shape
and byte order only). Keep the struct format string and field order in
exact sync with that C file; there is no shared schema between the two
languages, so this is a by-hand port, not a generated binding.

Deliberately generic - this module knows the envelope's SHAPE (which
byte offsets exist and what type each is), not which `kind`/`nid_message`
values MEAN, or which of them get which content fields. Naming/owning
specific kind numbers (MSG_P0, MSG_M136, MSG_ROUTE_ADD, MSG_CTC_CONNECTED,
...) and deciding nid_message/l_message/which optional fields apply to
which message is each sim's own application-layer concern (its own
message_catalog.json, resolved by rbc_messages.py's MessageCatalog before
it ever calls encode() here) - SimCore is transport plumbing, not a
registry of what every adaptation built on it sends or receives. This
mirrors the C side's own split: rbc_wire_types.h/rbc_wire.c DOES branch on
kind (RBC_MSG_M146/RBC_MSG_M136 specifically) because it is
safeAPIRBC2oo2's own single application-layer file, not a shared-across-
adaptations transport layer the way this module is - the two are not
equivalent, on purpose.

Wire layout (ENVELOPE_SIZE = 96 bytes, little-endian) - REQ-RBC-001
(revised, safeAPIFreamwork/docs/requirements/SRS.md), rbc_wire_types.h's
own doc for the full rationale:
  kind(1) + train_id(1) + nid_message(1) + reserved(1) +
  l_message(2) + reserved(2) + t_train(4) +
  cycle(4) + d_lrbg(4) + msg_type(4) + route_len(4) + ma_seq(4) +
  ma_length(4) + t_train_ack(4) +
  nid_lrbg(4) + q_dirlrbg(4) + q_dlrbg(4) + l_doubtover(4) +
  l_doubtunder(4) + q_length(4) + v_train(4) + q_dirtrain(4) +
  m_mode(4) + m_level(4) +
  start_signal(4) + end_signal(4) + route_type(4) + route_status(4)
The last 4 fields (offsets 80-95) were appended in the IL<->RBC
route-identity pass (ENVELOPE_SIZE grew 80 -> 96) - same additive-growth
precedent the M136 block and t_train_ack already set: never a repurposed
existing field, always appended after everything that came before.
Every field is ALWAYS packed/unpacked at these same fixed, non-
overlapping offsets regardless of kind (t_train_ack, the ten M136-only
fields, and the four route-identity fields included) - see rbc_wire.c's
own header for why NOT to branch on kind to decide WHERE something goes
(would alias different kinds' own extension fields onto the same wire
bytes for different meanings). l_message says how much of this fixed
96-byte slot is actually MEANINGFUL for a given envelope (36 for most
kinds, 40 for RBC_MSG_M146, 80 for RBC_MSG_M136, 96 - the whole slot -
for RBC_MSG_ROUTE_ADD/_ROUTE_RELEASE/_TRAIN_POSITION_IN_ROUTE) - the
caller (rbc_messages.py) decides that per message from its own catalog
data, this module has no opinion.

Function Documentation

◆ decode()

decode ( data)
Returns a dict of every wire field, or None if @data doesn't hold a
structurally-valid ENVELOPE_SIZE-byte flat frame - mirrors
rbc_wire_decode()'s own REQ-RBC-002 structural-validity contract.
Does NOT judge whether `kind`/`l_message` are a real/known/consistent
combination - that is application-layer knowledge this transport-layer
codec deliberately does not have; a caller wanting that asks its OWN
sim's MessageCatalog (rbc_messages.py's decode_to_message(), which
already returns None for an out-of-catalog kind rather than raising).

@data may be LONGER than ENVELOPE_SIZE - a caller whose own channel is
wider than the flat scheme's own 96-byte slot (e.g. TrainRBCSim's Train
relay channel, widened to RBC_TRAIN_ENVELOPE_WIRE_SIZE for
RBC_MSG_ERTMS_ENVELOPE, but still carrying this project's own
pre-existing flat P0/M136/M146 traffic on the very same train_id/wire)
zero-pads every send up to its own fixed frame width, flat sends
included - see that project's own train_sim.py/_pad(). Only the first
ENVELOPE_SIZE bytes are ever meaningful for this shape; anything past
that MUST be all-zero padding, never non-zero content silently
ignored - a non-zero tail is exactly the kind of "wrong shape on this
wire" a caller wants to know about, so it is treated the same as a
too-short buffer: not decodable.

Definition at line 104 of file rbc_wire.py.

◆ encode()

encode ( kind,
train_id,
nid_message = 0,
l_message = ENVELOPE_SIZE,
t_train = 0,
cycle = 0,
d_lrbg = 0,
msg_type = 0,
route_len = 0,
ma_seq = 0,
ma_length = 0,
t_train_ack = 0,
nid_lrbg = 0,
q_dirlrbg = 0,
q_dlrbg = 0,
l_doubtover = 0,
l_doubtunder = 0,
q_length = 0,
v_train = 0,
q_dirtrain = 0,
m_mode = 0,
m_level = 0,
start_signal = 0,
end_signal = 0,
route_type = 0,
route_status = 0 )
Packs the full fixed 96-byte envelope shape - every parameter is
exactly one wire field, positional meaning only (see this module's
own doc), no kind-based branching here. @l_message is NOT computed
from @kind by this function - the caller (rbc_messages.py, which has
the catalog data needed to know) must pass the right value.

Definition at line 56 of file rbc_wire.py.

◆ encode_opaque()

encode_opaque ( kind,
payload,
frame_size = ENVELOPE_SIZE )
Builds a fixed @frame_size frame carrying an OPAQUE payload rather
than the typed struct encode() packs: byte 0 = @kind, byte 1 = payload
length, bytes 2.. = @payload (ASCII str or bytes), the rest zero.

For the C side's domain-blind control kinds that ride the envelope
slot without being real Subset-026 messages (rbc_wire_types.h:
RBC_MSG_RELAY_KEEPALIVE / RBC_MSG_SET_LOG_LEVEL / RBC_MSG_SIM_STATUS)
- the caller owns the kind number and the payload meaning, this
module stays kind-blind (same split as encode()'s own doc). A payload
longer than @frame_size-2 is truncated.

@param frame_size: defaults to ENVELOPE_SIZE (every sim's own channel
historically this size) - a caller whose OWN channel uses a different
fixed slot size (e.g. TrainRBCSim's Train relay channel, widened to
RBC_TRAIN_ENVELOPE_WIRE_SIZE for RBC_MSG_ERTMS_ENVELOPE) must pass
that size here too, or the receiving side's own exact-datagram-size
check rejects this frame - see that project's own train_sim.py for
the concrete call site.

Definition at line 75 of file rbc_wire.py.

Variable Documentation

◆ _STRUCT

simcore.rbc_wire._STRUCT = struct.Struct("<4B2H22I")
protected

Definition at line 53 of file rbc_wire.py.

◆ ENVELOPE_SIZE

int simcore.rbc_wire.ENVELOPE_SIZE = 96

Definition at line 52 of file rbc_wire.py.