RBC_Test_Sim_Train 0.1.0 build a98ee69
ADR-029 Train (EVC) simulator — dual-homed TCP client speaking the RBC wire protocol
Loading...
Searching...
No Matches
rbc_ertms_wire.py
Go to the documentation of this file.
1"""Real Subset-026 ERTMS message codec - Python mirror of
2RBC_GP/src/application/AB/GP/ertms/train/ab_gp_ertms_train_codec.c
3(byte shape and order only). Keep this file in exact sync with that C
4file by hand; there is no shared schema between the two languages, same
5"by-hand port, not a generated binding" precedent rbc_wire.py's own
6module doc already states for the flat ADR-029 scheme.
7
8**Deliberately a DIFFERENT wire shape from rbc_wire.py, not a variant of
9it** - this is not an oversight, don't "fix" the two into consistency:
10 - rbc_wire.py: one fixed 96-byte struct, little-endian, every field at
11 a fixed offset regardless of kind - mirrors rbc_wire_types.h/
12 rbc_wire.c's own flat ADR-029 envelope.
13 - This module: real Subset-026 message-specific shapes, BIG-endian,
14 genuinely variable length (optional packets each self-identify with
15 their own leading NID_PACKET byte) - mirrors ab_gp_ertms_train_codec.c,
16 a byte-aligned TLV codec (not literally the real spec's own sub-byte
17 bit-packing - see that C file's own header doc for why: a deliberate,
18 already-made MISRA-friendly simplification this module also follows).
19
20Both scheme's bytes are wrapped identically at the TRANSPORT level by one
21new envelope kind, RBC_MSG_ERTMS_ENVELOPE (rbc_wire_types.h) - see that
22kind's own doc: byte 0 = the kind (22 - moved from 21 after a real
23collision with this project's own flat TRAIN_CONNECT=21, see
24rbc_wire_types.h's/rbc_messages.py's own comments), byte 1 = train_id, bytes 2..
25= this module's own encode()/decode() output verbatim. rbc_messages.py's
26MessageCatalog is what actually builds/parses that 2-byte prefix (its own
27"ertms" wireFormat branch) - this module only ever sees/produces the
28bytes AFTER that prefix, same "RBC_Test_Sim_Core doesn't know kind numbers"
29boundary rbc_wire.py's own module doc draws for the flat scheme.
30
31Real NID_MESSAGE/NID_PACKET numbers only, never this project's own flat-
32scheme project-specific numbering (rbc_wire.py's `kind` byte) - see
33ab_gp_ertms_train_interface.h/ab_gp_ertms_train_packets.h for the C-side
34`#define`s these mirror.
35"""
36
37import struct
38
39# ---- Real NID_MESSAGE numbers (Subset-026 Ch. 8) --------------------------
40# Train -> RBC (this module's own ENCODE side - what RBC_Test_Sim_Train originates)
41MSG_VALIDATED_TRAIN_DATA = 129
42MSG_REQUEST_FOR_SHUNTING = 130
43MSG_MA_REQUEST = 132
44MSG_POSITION_REPORT = 136
45MSG_ACKNOWLEDGEMENT = 146
46MSG_ACK_EMERGENCY_STOP = 147
47MSG_INIT_SESSION = 155
48MSG_TERMINATE_SESSION = 156
49MSG_SESSION_ESTABLISHED = 159
50# RBC -> Train (this module's own DECODE side - what RBC_Test_Sim_Train receives)
51MSG_MOVEMENT_AUTHORITY = 3
52MSG_UNCONDITIONAL_EMERGENCY_STOP = 16
53MSG_REVOCATION_EMERGENCY_STOP = 18
54MSG_GENERAL_MESSAGE = 24
55MSG_SH_REFUSED = 27
56MSG_SH_AUTHORISED = 28
57
58# ---- Real NID_PACKET numbers (Subset-026 Ch. 7) ----------------------------
59PACKET_NATIONAL_VALUES = 3
60PACKET_LINKING_INFO = 5
61PACKET_LEVEL_2_3_MA = 15
62PACKET_GRADIENT_PROFILE = 21
63PACKET_STATIC_SPEED_PROFILE = 27
64PACKET_MA_REQUEST_PARAMS = 57
65PACKET_POSITION_REPORT_PARAMS = 58
66PACKET_TEMPORARY_SPEED_RESTRICT = 65
67PACKET_TSR_REVOCATION = 66
68PACKET_TEXT_MESSAGE = 72
69
70_HEADER_T2T = struct.Struct(">BHII") # nid_message, l_message, t_train, nid_engine (11 bytes)
71_HEADER_C2T = struct.Struct(">BHIBHH") # nid_message, l_message, t_train, m_ack, nid_c, nid_bg (12 bytes)
72# Full Packet 0 field set, in rbc_packet_0_position_report_t's own declared
73# order (ab_gp_ertms_train_packets.h) - q_scale, nid_c, nid_bg, d_lrbg,
74# q_dirlrbg, q_dlrbg, l_doubtover, l_doubtunder, v_train, m_mode, m_level
75# (23 bytes). Extended this session, on direct request, from a reduced
76# subset (nid_c/nid_bg/d_lrbg/v_train/m_mode/m_level only, 12 bytes) that
77# never modeled q_scale/q_dirlrbg/q_dlrbg/l_doubtover/l_doubtunder at all -
78# see ab_gp_ertms_train_packet_codec.c's own rbc_packet_read_0_position_
79# report() doc, which this MUST stay byte-identical to.
80_POSITION_REPORT = struct.Struct(">BHHIBBIIHBB")
81_POSITION_REPORT_PACKET1 = struct.Struct(">HHHHIHBB") # nid_c, nid_bg, prv_nid_c, prv_nid_bg,
82 # d_lrbg, v_train, m_mode, m_level (16 bytes) -
83 # Packet 1 does NOT carry q_scale/q_dirlrbg/
84 # q_dlrbg/l_doubtover/l_doubtunder (no such
85 # fields in rbc_packet_1_position_report_2bg_t).
86
87# Real Subset-026 Q_SCALE/Q_DIRLRBG/Q_DLRBG enum values (ab_gp_ertms_types.h) -
88# mirrored here as plain ints (no shared C/Python enum binding, same
89# "by-hand port" precedent this whole module already follows).
90Q_SCALE_10CM = 0
91Q_SCALE_1M = 1
92Q_SCALE_10M = 2
93Q_DIRLRBG_REVERSE = 0
94Q_DIRLRBG_NOMINAL = 1
95Q_DIRLRBG_UNKNOWN = 2
96Q_DLRBG_REVERSE = 0
97Q_DLRBG_NOMINAL = 1
98Q_DLRBG_UNKNOWN = 2
99
100# Real Subset-026 NID_PACKET values for Msg 136 specifically (Packet 0 vs
101# Packet 1 - see ab_gp_ertms_train_packets.h's own RBC_PACKET_0_POSITION_
102# REPORT/RBC_PACKET_1_POSITION_REPORT_2BG). Msg 129/132 never carry this
103# leading byte at all - only Msg 136's own decode case reads one, mirror
104# that exactly here (see rbc_train_codec_decode_train_to_track()'s own
105# RBC_MSG_TRAIN_TO_TRACK_POSITION_REPORT case).
106PACKET_0_POSITION_REPORT = 0
107PACKET_1_POSITION_REPORT_2BG = 1
108
109
111 """@param pos: dict with the full Packet 0 field set - q_scale, nid_c,
112 nid_bg, d_lrbg, q_dirlrbg, q_dlrbg, l_doubtover, l_doubtunder, v_train,
113 m_mode, m_level - matching rbc_packet_read_0_position_report()'s own
114 field order exactly (ab_gp_ertms_train_packet_codec.c)."""
115 return _POSITION_REPORT.pack(pos["q_scale"], pos["nid_c"], pos["nid_bg"], pos["d_lrbg"],
116 pos["q_dirlrbg"], pos["q_dlrbg"], pos["l_doubtover"], pos["l_doubtunder"],
117 pos["v_train"], pos["m_mode"], pos["m_level"])
118
119
120def _unpack_position_report(buf, offset):
121 """Unused today (this sim only ENCODES Packet 0, as the message
122 originator - see _pack_position_report()'s own doc) - kept for
123 encode/decode symmetry with the rest of this module."""
124 (q_scale, nid_c, nid_bg, d_lrbg, q_dirlrbg, q_dlrbg, l_doubtover, l_doubtunder,
125 v_train, m_mode, m_level) = _POSITION_REPORT.unpack_from(buf, offset)
126 return {"q_scale": q_scale, "nid_c": nid_c, "nid_bg": nid_bg, "d_lrbg": d_lrbg,
127 "q_dirlrbg": q_dirlrbg, "q_dlrbg": q_dlrbg, "l_doubtover": l_doubtover,
128 "l_doubtunder": l_doubtunder, "v_train": v_train, "m_mode": m_mode,
129 "m_level": m_level}, offset + _POSITION_REPORT.size
130
131
132def _finalize(nid_message, body_after_header, nid_engine, t_train):
133 """Builds the full Train-to-Track frame and patches l_message with the
134 real total length, exactly matching
135 rbc_train_codec_encode_track_to_train()'s own "l_message computed
136 here, not caller-supplied" fix on the C side - never trust a
137 placeholder length, always the real encoded size."""
138 header = _HEADER_T2T.pack(nid_message, 0, t_train, nid_engine)
139 frame = bytearray(header + body_after_header)
140 l_message = len(frame)
141 frame[1:3] = struct.pack(">H", l_message)
142 return bytes(frame)
143
144
145# ---- Train -> RBC encoders (what RBC_Test_Sim_Train sends) ------------------------
146
147def encode_msg_155(nid_engine, t_train=0):
148 """Msg 155: Initiation of a Communication Session - header only."""
149 return _finalize(MSG_INIT_SESSION, b"", nid_engine, t_train)
150
151
152def encode_msg_156(nid_engine, t_train=0):
153 """Msg 156: Termination of a Communication Session - header only."""
154 return _finalize(MSG_TERMINATE_SESSION, b"", nid_engine, t_train)
155
156
157def encode_msg_159(nid_engine, m_version, t_train=0):
158 """Msg 159: Session Established."""
159 return _finalize(MSG_SESSION_ESTABLISHED, struct.pack(">B", m_version), nid_engine, t_train)
160
161
162def encode_msg_146(nid_engine, t_train_ack, t_train=0):
163 """Msg 146: Acknowledgement - real Subset-026 146 acks by TIMESTAMP of
164 the acked message, not a sequence number (see message_catalog.json's
165 own _subset026Reference comment on this)."""
166 return _finalize(MSG_ACKNOWLEDGEMENT, struct.pack(">I", t_train_ack), nid_engine, t_train)
167
168
169def encode_msg_136(nid_engine, position_report, t_train=0):
170 """Msg 136: Train Position Report, Packet 0 - the normal active-mission
171 cadence. Real Subset-026 Msg 136 self-identifies which packet variant
172 it carries with a leading NID_PACKET byte (see
173 rbc_train_codec_decode_train_to_track()'s own Msg 136 case) - this
174 encoder writes PACKET_0_POSITION_REPORT then the Packet 0 body."""
175 body = struct.pack(">B", PACKET_0_POSITION_REPORT) + _pack_position_report(position_report)
176 return _finalize(MSG_POSITION_REPORT, body, nid_engine, t_train)
177
178
179def encode_msg_136_packet1(nid_engine, packet1_report, t_train=0):
180 """Msg 136: Train Position Report, Packet 1 (2-balise-group variant) -
181 the post-EoM idle heartbeat cadence (see train_sim.py's own post-EoM
182 phase tracking). @param packet1_report: dict with nid_c, nid_bg,
183 prv_nid_c, prv_nid_bg, d_lrbg, v_train, m_mode, m_level - same field
184 set read_position_report_packet1() reads on the C side."""
185 body = struct.pack(">B", PACKET_1_POSITION_REPORT_2BG) + _POSITION_REPORT_PACKET1.pack(
186 packet1_report["nid_c"], packet1_report["nid_bg"],
187 packet1_report["prv_nid_c"], packet1_report["prv_nid_bg"],
188 packet1_report["d_lrbg"], packet1_report["v_train"],
189 packet1_report["m_mode"], packet1_report["m_level"])
190 return _finalize(MSG_POSITION_REPORT, body, nid_engine, t_train)
191
192
193def encode_msg_132(nid_engine, q_marqstreason, position_report, t_train=0):
194 """Msg 132: MA Request - Q_MARQSTREASON then Packet 0."""
195 body = struct.pack(">B", q_marqstreason) + _pack_position_report(position_report)
196 return _finalize(MSG_MA_REQUEST, body, nid_engine, t_train)
197
198
199def encode_msg_129(nid_engine, position_report, l_train, v_maxtrain, m_loadinggauge,
200 m_axleload, m_airtight, t_train=0):
201 """Msg 129: Validated Train Data - Packet 0 then the flat train-data
202 fields (same field subset ab_gp_ertms_train_codec.c's own decode
203 reads)."""
204 body = (_pack_position_report(position_report) +
205 struct.pack(">IHBBB", l_train, v_maxtrain, m_loadinggauge, m_axleload,
206 1 if m_airtight else 0))
207 return _finalize(MSG_VALIDATED_TRAIN_DATA, body, nid_engine, t_train)
208
209
210def encode_msg_130(nid_engine, position_report, t_train=0):
211 """Msg 130: Request for Shunting - Packet 0."""
212 return _finalize(MSG_REQUEST_FOR_SHUNTING, _pack_position_report(position_report), nid_engine, t_train)
213
214
215def encode_msg_147(nid_engine, nid_em, q_emergency_stop, position_report, t_train=0):
216 """Msg 147: Ack Emergency Stop - NID_EM, Q_EMERGENCY_STOP then Packet 0."""
217 body = struct.pack(">BB", nid_em, q_emergency_stop) + _pack_position_report(position_report)
218 return _finalize(MSG_ACK_EMERGENCY_STOP, body, nid_engine, t_train)
219
220
221def encode_msg_150(nid_engine, position_report, t_train=0):
222 """Msg 150: End of Mission - Packet 0."""
223 return _finalize(150, _pack_position_report(position_report), nid_engine, t_train)
224
225
226def encode_msg_154(nid_engine, t_train=0):
227 """Msg 154: No compatible version supported - header only."""
228 return _finalize(154, b"", nid_engine, t_train)
229
230
231def encode_msg_157(nid_engine, q_status, position_report, t_train=0):
232 """Msg 157: SoM Position Report - Q_STATUS then Packet 0."""
233 body = struct.pack(">B", q_status) + _pack_position_report(position_report)
234 return _finalize(157, body, nid_engine, t_train)
235
236
237def encode_msg_158(nid_engine, nid_textmessage, position_report, t_train=0):
238 """Msg 158: Text Message Ack - NID_TEXTMESSAGE then Packet 0."""
239 body = struct.pack(">B", nid_textmessage) + _pack_position_report(position_report)
240 return _finalize(158, body, nid_engine, t_train)
241
242
243# ---- RBC -> Train decoders (what RBC_Test_Sim_Train receives) ---------------------
244
245class ErtmsDecodeError(ValueError):
246 """@payload could not be decoded as a real Subset-026 track-to-train
247 message - malformed length, unknown nid_message, or an unrecognized
248 NID_PACKET inside a variable-length message (Msg 3/24)."""
249
250
252 if len(buf) < _HEADER_C2T.size:
253 raise ErtmsDecodeError(f"buffer too short for track-to-train header: {len(buf)} bytes")
254 nid_message, l_message, t_train, m_ack, nid_c, nid_bg = _HEADER_C2T.unpack_from(buf, 0)
255 if l_message > len(buf):
256 raise ErtmsDecodeError(f"l_message={l_message} exceeds buffer length {len(buf)}")
257 header = {"nid_message": nid_message, "l_message": l_message, "t_train": t_train,
258 "m_ack": m_ack, "nid_lrbg": {"nid_c": nid_c, "nid_bg": nid_bg}}
259 return header, _HEADER_C2T.size
260
261
263 """Msg 3: Movement Authority. @return dict with header, ma (Packet 15,
264 always present), and optional gradient/ssp/linking (Packets 21/27/5) -
265 same has_X/X pairing ab_gp_ertms_train_interface.h's own
266 rbc_msg_3_movement_authority_t struct uses."""
267 header, offset = _decode_header_c2t(buf)
268 if header["nid_message"] != MSG_MOVEMENT_AUTHORITY:
269 raise ErtmsDecodeError(f"decode_msg_3 called on nid_message={header['nid_message']}")
270 l_message = header["l_message"]
271
272 packet_id, = struct.unpack_from(">B", buf, offset)
273 offset += 1
274 if packet_id != PACKET_LEVEL_2_3_MA:
275 raise ErtmsDecodeError(f"Msg 3 expected Packet 15 first, got {packet_id}")
276 ma_seq, ma_length, v_loa, t_loa_s = struct.unpack_from(">IIHI", buf, offset)
277 offset += 14
278 result = {"header": header,
279 "ma": {"ma_seq": ma_seq, "ma_length": ma_length, "v_loa": v_loa, "t_loa_s": t_loa_s},
280 "has_gradient": False, "gradient": None,
281 "has_ssp": False, "ssp": None,
282 "has_linking": False, "linking": None,
283 "packets_present": ["P15"]}
284
285 while offset < l_message:
286 packet_id, = struct.unpack_from(">B", buf, offset)
287 offset += 1
288 if packet_id == PACKET_GRADIENT_PROFILE:
289 count, = struct.unpack_from(">B", buf, offset)
290 offset += 1
291 segments = []
292 for _ in range(count):
293 d_gradient, g_a = struct.unpack_from(">IB", buf, offset)
294 offset += 5
295 segments.append({"d_gradient": d_gradient, "g_a": g_a})
296 result["has_gradient"] = True
297 result["gradient"] = {"segment_count": count, "segments": segments}
298 result["packets_present"].append("P21")
299 elif packet_id == PACKET_STATIC_SPEED_PROFILE:
300 count, = struct.unpack_from(">B", buf, offset)
301 offset += 1
302 segments = []
303 for _ in range(count):
304 d_static, v_static = struct.unpack_from(">IB", buf, offset)
305 offset += 5
306 segments.append({"d_static": d_static, "v_static": v_static})
307 result["has_ssp"] = True
308 result["ssp"] = {"segment_count": count, "segments": segments}
309 result["packets_present"].append("P27")
310 elif packet_id == PACKET_LINKING_INFO:
311 count, = struct.unpack_from(">B", buf, offset)
312 offset += 1
313 links = []
314 for _ in range(count):
315 nid_bg, d_link, q_linkdir = struct.unpack_from(">HIB", buf, offset)
316 offset += 7
317 links.append({"nid_bg": nid_bg, "d_link": d_link, "q_linkdir": q_linkdir})
318 result["has_linking"] = True
319 result["linking"] = {"link_count": count, "links": links}
320 result["packets_present"].append("P5")
321 else:
322 raise ErtmsDecodeError(f"Msg 3: unrecognized NID_PACKET {packet_id} at offset {offset - 1}")
323 return result
324
325
327 """Msg 24: General Message. @return dict with header and whichever of
328 Packet 3/57/58/65/66/72 were present - same has_X/X pairing
329 ab_gp_ertms_train_interface.h's own rbc_msg_24_general_message_t
330 struct uses."""
331 header, offset = _decode_header_c2t(buf)
332 if header["nid_message"] != MSG_GENERAL_MESSAGE:
333 raise ErtmsDecodeError(f"decode_msg_24 called on nid_message={header['nid_message']}")
334 l_message = header["l_message"]
335
336 result = {"header": header,
337 "has_national_values": False, "national_values": None,
338 "has_ma_request_params": False, "ma_request_params": None,
339 "has_pr_params": False, "pr_params": None,
340 "has_tsr": False, "tsr": None,
341 "has_tsr_revoke": False, "tsr_revoke": None,
342 "has_text_message": False, "text_message": None,
343 "packets_present": []}
344
345 while offset < l_message:
346 packet_id, = struct.unpack_from(">B", buf, offset)
347 offset += 1
348 if packet_id == PACKET_NATIONAL_VALUES:
349 v_nvshunt, v_nvstff, v_nvunfit, t_nvcontact, d_nvroll = struct.unpack_from(">HHHHH", buf, offset)
350 offset += 10
351 result["has_national_values"] = True
352 result["national_values"] = {"v_nvshunt": v_nvshunt, "v_nvstff": v_nvstff,
353 "v_nvunfit": v_nvunfit, "t_nvcontact": t_nvcontact,
354 "d_nvroll": d_nvroll}
355 result["packets_present"].append("P3")
356 elif packet_id == PACKET_MA_REQUEST_PARAMS:
357 t_mar_s, d_mar_m = struct.unpack_from(">II", buf, offset)
358 offset += 8
359 result["has_ma_request_params"] = True
360 result["ma_request_params"] = {"t_mar_s": t_mar_s, "d_mar_m": d_mar_m}
361 result["packets_present"].append("P57")
362 elif packet_id == PACKET_POSITION_REPORT_PARAMS:
363 t_cycloc_s, d_cycloc_m = struct.unpack_from(">II", buf, offset)
364 offset += 8
365 result["has_pr_params"] = True
366 result["pr_params"] = {"t_cycloc_s": t_cycloc_s, "d_cycloc_m": d_cycloc_m}
367 result["packets_present"].append("P58")
368 elif packet_id == PACKET_TEMPORARY_SPEED_RESTRICT:
369 nid_tsr, d_tsr, l_tsr, v_tsr, q_front = struct.unpack_from(">HIIHB", buf, offset)
370 offset += 13
371 result["has_tsr"] = True
372 result["tsr"] = {"nid_tsr": nid_tsr, "d_tsr": d_tsr, "l_tsr": l_tsr,
373 "v_tsr": v_tsr, "q_front": q_front}
374 result["packets_present"].append("P65")
375 elif packet_id == PACKET_TSR_REVOCATION:
376 nid_tsr, = struct.unpack_from(">H", buf, offset)
377 offset += 2
378 result["has_tsr_revoke"] = True
379 result["tsr_revoke"] = {"nid_tsr": nid_tsr}
380 result["packets_present"].append("P66")
381 elif packet_id == PACKET_TEXT_MESSAGE:
382 ack_required, duration_s, text_len = struct.unpack_from(">BIB", buf, offset)
383 offset += 6
384 text = buf[offset:offset + text_len].decode("ascii", errors="replace")
385 offset += text_len
386 result["has_text_message"] = True
387 result["text_message"] = {"ack_required": bool(ack_required), "duration_s": duration_s,
388 "text": text}
389 result["packets_present"].append("P72")
390 else:
391 raise ErtmsDecodeError(f"Msg 24: unrecognized NID_PACKET {packet_id} at offset {offset - 1}")
392 return result
393
394
396 """Msg 27 SH Refused (Subset-026 5.6): header + the T_TRAIN of the refused Msg 130
397 (32-bit, same shape as Msg 8)."""
398 header, offset = _decode_header_c2t(buf)
399 if header["nid_message"] != MSG_SH_REFUSED:
400 raise ErtmsDecodeError(f"decode_msg_27 called on nid_message={header['nid_message']}")
401 if len(buf) < offset + 4:
402 raise ErtmsDecodeError(f"Msg 27 too short: {len(buf)} bytes")
403 (t_train_ref,) = struct.unpack_from(">I", buf, offset)
404 return {"header": header, "t_train_ref": t_train_ref}
405
406
408 """Msg 28 SH Authorised (Subset-026 5.6): header + the T_TRAIN of the authorised Msg 130
409 (RBC_GP sends no optional packets)."""
410 header, offset = _decode_header_c2t(buf)
411 if header["nid_message"] != MSG_SH_AUTHORISED:
412 raise ErtmsDecodeError(f"decode_msg_28 called on nid_message={header['nid_message']}")
413 if len(buf) < offset + 4:
414 raise ErtmsDecodeError(f"Msg 28 too short: {len(buf)} bytes")
415 (t_train_ref,) = struct.unpack_from(">I", buf, offset)
416 return {"header": header, "t_train_ref": t_train_ref}
417
418
419_DECODERS = {
420 MSG_MOVEMENT_AUTHORITY: decode_msg_3,
421 MSG_GENERAL_MESSAGE: decode_msg_24,
422 MSG_SH_REFUSED: decode_msg_27,
423 MSG_SH_AUTHORISED: decode_msg_28,
424}
425
426
427def decode(buf):
428 """@return (nid_message, decoded_dict) by peeking buf[0], dispatching
429 to the matching decode_msg_*() above. Raises ErtmsDecodeError for an
430 empty buffer or an nid_message this module doesn't yet decode
431 (Msg 16/18 - Emergency Stop - have real C-side encoders already but
432 no Python decoder yet, out of scope for the SoM/Running/EoM flow)."""
433 if len(buf) < 1:
434 raise ErtmsDecodeError("empty buffer")
435 nid_message = buf[0]
436 decoder = _DECODERS.get(nid_message)
437 if decoder is None:
438 raise ErtmsDecodeError(f"no Python decoder registered for nid_message={nid_message}")
439 return nid_message, decoder(buf)
encode_msg_159(nid_engine, m_version, t_train=0)
encode_msg_146(nid_engine, t_train_ack, t_train=0)
_finalize(nid_message, body_after_header, nid_engine, t_train)
encode_msg_157(nid_engine, q_status, position_report, t_train=0)
encode_msg_130(nid_engine, position_report, t_train=0)
encode_msg_136(nid_engine, position_report, t_train=0)
encode_msg_155(nid_engine, t_train=0)
encode_msg_150(nid_engine, position_report, t_train=0)
encode_msg_158(nid_engine, nid_textmessage, position_report, t_train=0)
encode_msg_154(nid_engine, t_train=0)
encode_msg_132(nid_engine, q_marqstreason, position_report, t_train=0)
encode_msg_136_packet1(nid_engine, packet1_report, t_train=0)
encode_msg_156(nid_engine, t_train=0)
encode_msg_129(nid_engine, position_report, l_train, v_maxtrain, m_loadinggauge, m_axleload, m_airtight, t_train=0)
encode_msg_147(nid_engine, nid_em, q_emergency_stop, position_report, t_train=0)
_unpack_position_report(buf, offset)