TRO-Fluss: tro_overrides.ini standardmaessig aus, ungelistete Strecken-Separatoren lagerichtig verketten

- tro_flow/tro_annotate: cfg/tro_overrides.ini nur noch mit --use-overrides
  (die Abschnitte haengen an laufenden TRO-Nummern und am CSV-Namen und
  veralten mit jedem Export; [export.csv.connections] ersetzte alle
  abgeleiteten Kanten und liess TRO13-20 unverbunden)
- link_tros: Separatoren mit Zuordnung auf eine Strecke, die in keiner
  TrackIds-Liste stehen, per Projektion AS->ES in die Strecken-Kette
  einsortieren (Mubea VF_3: 0037/0038 erzeugten je 9 Fehlkanten;
  mubea 38 -> 22 Verbindungen, HundM05 unveraendert)
- tro_catalog: typ-eigene Fuellfarbe je TRO-Typ, Mermaid-classDefs je Typ
- tro_overrides/tro_flow: connect.ini zurueck einlesen (--from-connect)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Samer Ayadi
2026-10-01 10:50:40 +02:00
co-authored by Claude Opus 5.5
parent 1479519bdc
commit 02db9a95b0
4 changed files with 381 additions and 26 deletions
+101
View File
@@ -490,3 +490,104 @@ def apply_overrides(tros, overrides: Overrides) -> None:
overrides.applied.append(
f"{tro.tro_id}: {vorher} -> {override.type_name} ({grund})"
)
# ---------------------------------------------------------------------------
# connect.ini (TRO-Ebene) zurueck einlesen - tro_flow.py --from-connect
# ---------------------------------------------------------------------------
# Vertrauensangabe fuer Typen, die aus einer (evtl. von Hand geaenderten)
# connect.ini stammen - in der Doku von Layout und Begehung unterscheidbar.
CONNECT_CONFIDENCE = "connect.ini"
def _connect_pair(raw: str) -> tuple[str, str, str] | None:
"""'<von> -> <nach> | <JamArea> | <kind>' -> (von, nach, kind); None wenn
kein Pfeil. Die JamArea wird (noch) nicht ausgewertet."""
pfeil, _, rest = str(raw).partition("|")
src, sep, dst = (t.strip() for t in pfeil.partition("->"))
if not sep or not src or not dst:
return None
felder = [f.strip() for f in rest.split("|")]
kind = felder[1] if len(felder) > 1 else ""
return src, dst, kind
def load_connect_ini(path: Path, base: Overrides | None = None) -> Overrides:
"""
Eine von tro_flow.py --connect geschriebene connect.ini als Korrekturen lesen.
Die Datei wird dabei nicht als eigenes Modell behandelt, sondern auf die
vorhandenen Korrekturarten abgebildet - so laeuft sie durch dieselbe
Pipeline wie cfg/tro_overrides.ini:
[<Anlage>.nodes] TRO-ID = <Typ> | ... -> Typkorrektur je TRO-ID
[<Anlage>.connections] cNNN = von -> nach | JamArea | kind
[<Anlage>.externals] eNNN = SYSn -> TRO | ... bzw. TRO -> SYSn | ...
-> zusammen die gesetzte Topologie, die die
abgeleiteten Kanten vollstaendig ersetzt
[<Anlage>.tros] (Vorgaenger/Nachfolger je TRO) ist nur eine Uebersicht und
wird ignoriert - massgeblich sind die Verbindungen.
`base` (die Korrekturen aus cfg/tro_overrides.ini) bleibt erhalten; Typen
aus der connect.ini gewinnen, die Topologie der connect.ini ersetzt eine
dort gesetzte.
"""
result = base if base is not None else Overrides()
parser = configparser.ConfigParser(inline_comment_prefixes=(";",),
interpolation=None)
parser.optionxform = str # TRO-IDs nicht in Kleinschreibung wandeln
# utf-8-sig: von Hand gespeicherte Dateien (Windows-Editoren, PowerShell)
# tragen oft ein BOM, an dem configparser sonst in Zeile 1 scheitert.
try:
parser.read(path, encoding="utf-8-sig")
except configparser.Error as exc:
raise ValueError(f"{path.name} nicht lesbar: {exc}") from exc
connections: list[ConnectionSpec] = []
for section in parser.sections():
art = section.rsplit(".", 1)[-1].lower()
values = parser[section]
if art == "nodes":
for tro_id, raw in values.items():
type_name = str(raw).partition("|")[0].strip()
# Systemgrenzen (SYSn) sind kein TRO, ihr "Typ" ist nur Anzeige
if tro_id.upper().startswith("SYS") or not type_name:
continue
if get_tro(type_name) is None:
result.warnings.append(
f"{path.name} [{section}] {tro_id}: Typ '{type_name}' steht "
f"nicht im TRO-Katalog - Typ bleibt wie abgeleitet"
)
continue
result.tros[tro_id.upper()] = TroOverride(
key=tro_id.upper(), type_name=type_name,
reason=f"aus {path.name}", confidence=CONNECT_CONFIDENCE,
)
elif art in ("connections", "externals"):
for key, raw in values.items():
pair = _connect_pair(raw)
if pair is None:
result.warnings.append(
f"{path.name} [{section}] {key}: '{raw}' ist keine "
f"Verbindung '<von> -> <nach>'"
)
continue
src, dst, kind = pair
if "EXTERN" in (src.upper(), dst.upper()):
result.warnings.append(
f"{path.name} [{section}] {key}: EXTERN wird nicht "
f"ausgewertet - Systemgrenze als SYSn angeben"
)
continue
connections.append(ConnectionSpec(src=src, dst=dst, kind=kind))
if connections:
result.connections = connections
else:
result.warnings.append(
f"{path.name}: keine Verbindungen gefunden - Topologie bleibt wie abgeleitet"
)
result.applied.append(f"connect.ini eingelesen: {path}")
return result