56def encode(kind, train_id, nid_message=0, l_message=ENVELOPE_SIZE, t_train=0,
57 cycle=0, d_lrbg=0, msg_type=0, route_len=0, ma_seq=0, ma_length=0, t_train_ack=0,
58 nid_lrbg=0, q_dirlrbg=0, q_dlrbg=0, l_doubtover=0, l_doubtunder=0,
59 q_length=0, v_train=0, q_dirtrain=0, m_mode=0, m_level=0,
60 start_signal=0, end_signal=0, route_type=0, route_status=0):
61 """Packs the full fixed 96-byte envelope shape - every parameter is
62 exactly one wire field, positional meaning only (see this module's
63 own doc), no kind-based branching here. @l_message is NOT computed
64 from @kind by this function - the caller (rbc_messages.py, which has
65 the catalog data needed to know) must pass the right value."""
67 kind, train_id, nid_message, 0,
69 t_train, cycle, d_lrbg, msg_type, route_len, ma_seq, ma_length, t_train_ack,
70 nid_lrbg, q_dirlrbg, q_dlrbg, l_doubtover, l_doubtunder, q_length, v_train, q_dirtrain, m_mode, m_level,
71 start_signal, end_signal, route_type, route_status,
76 """Builds a fixed @frame_size frame carrying an OPAQUE payload rather
77 than the typed struct encode() packs: byte 0 = @kind, byte 1 = payload
78 length, bytes 2.. = @payload (ASCII str or bytes), the rest zero.
80 For the C side's domain-blind control kinds that ride the envelope
81 slot without being real Subset-026 messages (rbc_wire_types.h:
82 RBC_MSG_RELAY_KEEPALIVE / RBC_MSG_SET_LOG_LEVEL / RBC_MSG_SIM_STATUS)
83 - the caller owns the kind number and the payload meaning, this
84 module stays kind-blind (same split as encode()'s own doc). A payload
85 longer than @frame_size-2 is truncated.
87 @param frame_size: defaults to ENVELOPE_SIZE (every sim's own channel
88 historically this size) - a caller whose OWN channel uses a different
89 fixed slot size (e.g. TrainRBCSim's Train relay channel, widened to
90 RBC_TRAIN_ENVELOPE_WIRE_SIZE for RBC_MSG_ERTMS_ENVELOPE) must pass
91 that size here too, or the receiving side's own exact-datagram-size
92 check rejects this frame - see that project's own train_sim.py for
93 the concrete call site."""
94 if isinstance(payload, str):
95 payload = payload.encode(
"ascii",
"replace")
96 payload = payload[: frame_size - 2]
97 frame = bytearray(frame_size)
98 frame[0] = kind & 0xFF
99 frame[1] = len(payload)
100 frame[2 : 2 + len(payload)] = payload
105 """Returns a dict of every wire field, or None if @data doesn't hold a
106 structurally-valid ENVELOPE_SIZE-byte flat frame - mirrors
107 rbc_wire_decode()'s own REQ-RBC-002 structural-validity contract.
108 Does NOT judge whether `kind`/`l_message` are a real/known/consistent
109 combination - that is application-layer knowledge this transport-layer
110 codec deliberately does not have; a caller wanting that asks its OWN
111 sim's MessageCatalog (rbc_messages.py's decode_to_message(), which
112 already returns None for an out-of-catalog kind rather than raising).
114 @data may be LONGER than ENVELOPE_SIZE - a caller whose own channel is
115 wider than the flat scheme's own 96-byte slot (e.g. TrainRBCSim's Train
116 relay channel, widened to RBC_TRAIN_ENVELOPE_WIRE_SIZE for
117 RBC_MSG_ERTMS_ENVELOPE, but still carrying this project's own
118 pre-existing flat P0/M136/M146 traffic on the very same train_id/wire)
119 zero-pads every send up to its own fixed frame width, flat sends
120 included - see that project's own train_sim.py/_pad(). Only the first
121 ENVELOPE_SIZE bytes are ever meaningful for this shape; anything past
122 that MUST be all-zero padding, never non-zero content silently
123 ignored - a non-zero tail is exactly the kind of "wrong shape on this
124 wire" a caller wants to know about, so it is treated the same as a
125 too-short buffer: not decodable."""
126 if len(data) < ENVELOPE_SIZE:
128 if any(data[ENVELOPE_SIZE:]):
130 (kind, train_id, nid_message, _reserved0,
131 l_message, _reserved1,
132 t_train, cycle, d_lrbg, msg_type, route_len, ma_seq, ma_length, t_train_ack,
133 nid_lrbg, q_dirlrbg, q_dlrbg, l_doubtover, l_doubtunder,
134 q_length, v_train, q_dirtrain, m_mode, m_level,
135 start_signal, end_signal, route_type, route_status) = _STRUCT.unpack_from(data)
138 "train_id": train_id,
139 "nid_message": nid_message,
140 "l_message": l_message,
144 "msg_type": msg_type,
145 "route_len": route_len,
147 "ma_length": ma_length,
148 "t_train_ack": t_train_ack,
149 "nid_lrbg": nid_lrbg,
150 "q_dirlrbg": q_dirlrbg,
152 "l_doubtover": l_doubtover,
153 "l_doubtunder": l_doubtunder,
154 "q_length": q_length,
156 "q_dirtrain": q_dirtrain,
159 "start_signal": start_signal,
160 "end_signal": end_signal,
161 "route_type": route_type,
162 "route_status": route_status,
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)