TrainRBCSim
ADR-029 Train (EVC) simulator — dual-homed TCP client speaking the RBC wire protocol
Loading...
Searching...
No Matches
site_data.py
Go to the documentation of this file.
1"""site_data.py - Train sim's own tiny RailML reader.
2
3Independently re-parses safeAPIRBC2oo2SA/etc/site/ab_site.railml.xml -
4the same single source of truth safeAPIRBC2oo2GP/etc/scripts/
5generate_ab_site_gp.py and safeAPIRBC2oo2GA/etc/scripts/generate_ab_site_ga.py
6already generate GP's/GA's own C site-data tables from, and
7safeAPIRBC2oo2TestEnv/src/web_runner/site_data.py already reads for the
8live schematic - deliberately a FOURTH independent reader, not a call
9into any of those three, matching the "each generator re-parses the file
10itself, no pipeline coupling" convention those two C-side generators'
11own header comments already establish (see either one's own doc for the
12full rationale: no build-order coupling, and a real cross-check
13opportunity instead of one silently trusting another's derived output).
14
15Train only ever needs ONE thing from the file: the `nid_lrbg -> name`
16mapping this sim uses purely for its own log-line decoration (see
17train_sim.py's own get_balise_name()) - replaces what used to be a
18hardcoded BALISE_NAMES dict that was, by actual comparison, a byte-for-
19byte duplicate of this file's own <baliseGroup> data.
20"""
21
22import logging
23import os
24import xml.etree.ElementTree as ET
25
26_RAILML_NS = "https://www.railml.org/schemas/3.3"
27_SAPI_NS = "https://safeapirbc2oo2.example/schemas/sapi-ext/1"
28_NS = {"r": _RAILML_NS, "sapi": _SAPI_NS}
29
30# TrainRBCSim/src/train/ -> up 3 (train, src, TrainRBCSim) -> the SAPI
31# workspace root -> safeAPIRBC2oo2SA's own sibling checkout. Same
32# sibling-directory convention web_runner/server.py's own
33# DEFAULT_RAILML_PATH already uses - see that file's own doc.
34_WORKSPACE_ROOT = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "..", ".."))
35DEFAULT_RAILML_PATH = os.path.join(_WORKSPACE_ROOT, "safeAPIRBC2oo2SA", "etc", "site", "ab_site.railml.xml")
36
37# Byte-for-byte what BALISE_NAMES (train_sim.py, before this module
38# existed) hardcoded - kept ONLY as a fallback for a deployment that
39# genuinely has no sibling safeAPIRBC2oo2SA checkout available (e.g. a
40# Docker image built from just this project's own source tree, which
41# does not copy that sibling project's own etc/site/ in today - a real,
42# separate packaging gap, not something this module can fix from here).
43# balise_names() logs a clear warning whenever this fallback is actually
44# used, so the gap stays visible rather than silently masking itself.
45_FALLBACK_BALISE_NAMES = {
46 1001: "BG_W_TRK1", 1002: "BG_W_TRK2", 1003: "BG_W_TRK3", 1004: "BG_W_TRK4",
47 1005: "BG_UP_1", 1006: "BG_UP_2", 1007: "BG_DOWN_1", 1008: "BG_DOWN_2",
48 1009: "BG_E_TRK1", 1010: "BG_E_TRK2", 1011: "BG_E_TRK3", 1012: "BG_E_TRK4",
49}
50
51
52def balise_names(railml_path=None):
53 """@return {nid_lrbg: name} parsed fresh from the RailML file (no
54 caching - this sim's own catalog/config loading is likewise done
55 once at import/startup time, matching that same "read once, live
56 for the process's lifetime" posture). Falls back to a hardcoded copy
57 of today's known-good data (see _FALLBACK_BALISE_NAMES's own doc) and
58 logs a warning if the file can't be found/parsed, rather than raising
59 - a missing balise-name mapping is a log-decoration cosmetic, never
60 worth failing sim startup over."""
61 path = railml_path or os.environ.get("SAPI_RAILML_PATH") or DEFAULT_RAILML_PATH
62 try:
63 tree = ET.parse(path)
64 root = tree.getroot()
65 infra = root.find("r:infrastructure", _NS)
66 fi = infra.find("r:functionalInfrastructure", _NS)
67 balise_groups = fi.find("r:baliseGroups", _NS)
68 names = {}
69 for bg in balise_groups.findall("r:baliseGroup", _NS):
70 ext = bg.find("r:extensions/sapi:etcsBaliseGroup", _NS)
71 nid_lrbg = int(ext.get("nidLrbg"))
72 names[nid_lrbg] = bg.get("name", f"BG_{nid_lrbg}")
73 if not names:
74 raise ValueError("RailML file parsed but yielded zero balise groups")
75 return names
76 except Exception as e:
77 logging.getLogger("train").warning(
78 "[GENERAL] [train] [internal] [CONFIG] [could not read balise names from RailML "
79 "(%s) - falling back to the hardcoded copy] [path=%s]", e, path)
80 return dict(_FALLBACK_BALISE_NAMES)
balise_names(railml_path=None)
Definition site_data.py:52