1"""Train sim's own adapters between message_catalog.json's flat
2{field: int} fields dict (the same shape every JSON sendMessage command
3already uses for the flat scheme) and rbc_ertms_wire.py's real, per-
4message-shaped encode_msg_*()/decode() functions - injected into
5SimCore's MessageCatalog at construction time (see that module's own
6header doc for why this lives here, not there: Subset-026 is the
7Train<->RBC air-gap protocol specifically, not something IL/CTC ever
8speak in this codebase, so it's this sim's own application-layer
9concern, same as message_catalog.json itself already is per-sim).
12from .
import rbc_ertms_wire
as ertms
25 "q_scale": fields.get(
"q_scale", ertms.Q_SCALE_1M),
26 "nid_c": fields.get(
"nid_c", 0),
27 "nid_bg": fields.get(
"nid_bg", 0),
28 "d_lrbg": fields.get(
"d_lrbg", 0),
29 "q_dirlrbg": fields.get(
"q_dirlrbg", ertms.Q_DIRLRBG_NOMINAL),
30 "q_dlrbg": fields.get(
"q_dlrbg", ertms.Q_DLRBG_NOMINAL),
31 "l_doubtover": fields.get(
"l_doubtover", 0),
32 "l_doubtunder": fields.get(
"l_doubtunder", 0),
33 "v_train": fields.get(
"v_train", 0),
34 "m_mode": fields.get(
"m_mode", 0),
35 "m_level": fields.get(
"m_level", 0),
40 """Same shape as _position_report_from_fields() plus Packet 1's own
41 extra NID_PRVLRBG pair (prv_nid_c/prv_nid_bg) - defaults to mirroring
42 nid_c/nid_bg when not explicitly given, since a stationary heartbeat's
43 "previous" LRBG is typically its own last-reported one."""
45 base[
"prv_nid_c"] = fields.get(
"prv_nid_c", base[
"nid_c"])
46 base[
"prv_nid_bg"] = fields.get(
"prv_nid_bg", base[
"nid_bg"])
52 "M155":
lambda nid_engine, fields, t_train: ertms.encode_msg_155(nid_engine, t_train),
53 "M156":
lambda nid_engine, fields, t_train: ertms.encode_msg_156(nid_engine, t_train),
54 "M159":
lambda nid_engine, fields, t_train: ertms.encode_msg_159(
55 nid_engine, fields.get(
"m_version", 0), t_train),
56 "M146":
lambda nid_engine, fields, t_train: ertms.encode_msg_146(
57 nid_engine, fields.get(
"t_train_ack", 0), t_train),
58 "M136":
lambda nid_engine, fields, t_train: ertms.encode_msg_136(
60 "M136_P1":
lambda nid_engine, fields, t_train: ertms.encode_msg_136_packet1(
62 "M132":
lambda nid_engine, fields, t_train: ertms.encode_msg_132(
64 "M129":
lambda nid_engine, fields, t_train: ertms.encode_msg_129(
66 fields.get(
"v_maxtrain", 0), fields.get(
"m_loadinggauge", 0), fields.get(
"m_axleload", 0),
67 bool(fields.get(
"m_airtight", 0)), t_train),
68 "M130":
lambda nid_engine, fields, t_train: ertms.encode_msg_130(
70 "M147":
lambda nid_engine, fields, t_train: ertms.encode_msg_147(
71 nid_engine, fields.get(
"nid_em", 0), fields.get(
"q_emergency_stop", 0),
73 "M150":
lambda nid_engine, fields, t_train: ertms.encode_msg_150(
75 "M154":
lambda nid_engine, fields, t_train: ertms.encode_msg_154(nid_engine, t_train),
76 "M157":
lambda nid_engine, fields, t_train: ertms.encode_msg_157(
78 "M158":
lambda nid_engine, fields, t_train: ertms.encode_msg_158(
84 """Msg 3 (Movement Authority) -> one flat {field: value} dict, same
85 shape decode_to_message() already returns for the flat scheme, plus
86 "packetsPresent" (this message's own optional-packet list) so a
87 Robot test can assert presence, not just field values. Packet-
88 specific fields are prefixed by packet (gradient_*/ssp_*/linking_*)
89 since e.g. gradient and ssp both have their own "segment_count" -
90 Msg 3 in this flow ALWAYS carries both at once (Packet 15+21+27),
91 so this collision is real, not theoretical."""
92 flat = dict(decoded[
"ma"])
93 if decoded[
"has_gradient"]:
94 flat[
"gradient_segment_count"] = decoded[
"gradient"][
"segment_count"]
95 flat[
"gradient_segments"] = decoded[
"gradient"][
"segments"]
96 if decoded[
"has_ssp"]:
97 flat[
"ssp_segment_count"] = decoded[
"ssp"][
"segment_count"]
98 flat[
"ssp_segments"] = decoded[
"ssp"][
"segments"]
99 if decoded[
"has_linking"]:
100 flat[
"linking_link_count"] = decoded[
"linking"][
"link_count"]
101 flat[
"linking_links"] = decoded[
"linking"][
"links"]
102 flat[
"packetsPresent"] = decoded[
"packets_present"]
107 """Msg 24 (General Message) -> one flat dict, same shape/rationale as
108 _flatten_msg3() above. NOTE a real, currently-latent collision: P65
109 (tsr) and P66 (tsr_revoke) both have their own "nid_tsr" field - not
110 prefixed below since this project's own SoM/Running/EoM flow never
111 carries both in the same Msg 24, but a future scenario that does
112 (dynamic TSR work) would need to prefix these the same way
113 gradient/ssp are above."""
115 if decoded[
"has_national_values"]:
116 flat.update(decoded[
"national_values"])
117 if decoded[
"has_ma_request_params"]:
118 flat.update(decoded[
"ma_request_params"])
119 if decoded[
"has_pr_params"]:
120 flat.update(decoded[
"pr_params"])
121 if decoded[
"has_tsr"]:
122 flat.update(decoded[
"tsr"])
123 if decoded[
"has_tsr_revoke"]:
124 flat.update(decoded[
"tsr_revoke"])
125 if decoded[
"has_text_message"]:
126 flat.update(decoded[
"text_message"])
127 flat[
"packetsPresent"] = decoded[
"packets_present"]
133 ertms.MSG_MOVEMENT_AUTHORITY: _flatten_msg3,
134 ertms.MSG_GENERAL_MESSAGE: _flatten_msg24,
_position_report_from_fields(fields)
_position_report_packet1_from_fields(fields)