|
| | 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) |
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.
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.