TRO-Fluss: Separatoren ohne TrackIds-Mitgliedschaft als eigenstaendige 1Sep

Ein per Zuordnung einer Streckengruppe zugeordneter Separator, der nicht Teil
von deren eigener TrackIds-Kette ist, wird direkt als 1Sep eingestuft statt
ueber die Vario/letzter-Separator-Logik zu laufen - vor Ort bei Mubea
VF_1-VF_3 bestaetigt: jede Streckengruppe hat genau einen echten
Kettenseparator, alle weiteren Zuordnung-Treffer sind eigenstaendige
Vereinzeler ohne eigenes VF-Teilstueck. Damit ein solcher Separator nicht
zugleich faelschlich als Weichen-Indiz zaehlt (engster Treffer ueberhaupt bei
Mubea 0005, 163mm), werden echte VF-Kettenmitglieder vor der
Weichenerkennung (_find_switch_separators) ausgenommen - Naehe zum eigenen
Aus-/Einschleuselement ist bei ihnen der normale, erwartete Aufbau, kein
Weichenbeleg.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Samer Ayadi
2026-09-24 13:16:36 +02:00
co-authored by Claude Sonnet 5
parent 97965e8ec7
commit f4b0e4d508
+66 -13
View File
@@ -470,12 +470,31 @@ def derive_tros(
)
elements_by_id = {e.teile_id: e for e in elements}
# Separatoren, die bereits ein echtes VF-Kettenmitglied ihrer EIGENEN
# Zuordnung-Streckengruppe sind (siehe Schritt 2/_incoming_drop unten),
# werden von der Weichenerkennung ausgenommen: Naehe zum eigenen Aus-/
# Einschleuselement ist bei ihnen der NORMALE, erwartete Aufbau jeder
# angetriebenen Streckengruppe (TrackIds = [Aus-, 1x Separator, Ein-
# schleuselement], siehe Mubea VF_1-VF_3), keine Weichenevidenz. Ohne
# diesen Ausschluss faengt Regel 1 faelschlich genau diesen Separator ein
# (Mubea 0005, 163mm - der engste, staerkste Treffer ueberhaupt, aber kein
# Weichen-Indiz - Review 2026-09-24).
own_vf_separators = {
sep.teile_id
for sep in separators
if sep.assignment
and (host := elements_by_id.get(sep.assignment))
and host.kind == "Strecke"
and sep.teile_id in host.track_ids
}
switch_candidates = [s for s in separators if s.teile_id not in own_vf_separators]
# Separatoren an einem Ausschleuselement (siehe _find_switch_separators)
# werden VOR der normalen Host-Aufloesung herausgenommen: sie bilden gleich
# unten (3b) ihre eigene 1Sep1Swi-Uebergabestelle und duerfen nicht zugleich
# als gewoehnlicher Separator ihres Zuordnung-Hosts (Kreisel-Bahn oder
# Strecke - beides kommt vor) mitgezaehlt werden.
switch_hits = _find_switch_separators(separators, elements, SWITCH_PROXIMITY_MM)
switch_hits = _find_switch_separators(switch_candidates, elements, SWITCH_PROXIMITY_MM)
# Separator -> Host-Knoten
sep_host: dict[str, str] = {}
@@ -560,14 +579,19 @@ def derive_tros(
)
# 2) Angetriebene Strecken -> je SEPARATOR ein eigener Vario-TRO. Katalog
# (TRO_CATALOG in tro_catalog.py) definiert Vario als 1x Separator + 1x
# VarioDrive - mehrere Separatoren auf demselben Streckensegment sind
# mehrere eigenstaendige Uebergabestellen, keine gemeinsame (Review
# 2026-09-21). Ein Segment ganz ohne Separator behaelt weiterhin einen
# einzelnen TRO (leere Separatorliste), damit das Antriebssegment selbst
# nicht verschwindet.
# (TRO_CATALOG in tro_catalog.py) definiert Vario als genau 1x Separator +
# 1x VarioDrive - ein Vario-TRO ist die Paarung eines einzelnen VF-
# Teilstuecks mit dem einen Separator, den es antreibt, kein Sammel-TRO
# fuer die ganze Streckengruppe (Review 2026-09-21/24). Mehrere
# Separatoren auf demselben Streckensegment sind deshalb mehrere
# eigenstaendige Uebergabestellen. Ein Segment ganz ohne Separator bildet
# folgerichtig KEIN eigenes TRO (kein Separator zum Paaren) - anders als
# bisher wird es nicht mehr als Platzhalter-Vario mit leerer
# Separatorliste gefuehrt.
for node_id in sorted(n for n, node in graph.nodes.items() if node.kind == "Strecke"):
seps = seps_on([node_id])
if not seps:
continue
element = graph.nodes[node_id].element
drive = element.drive_dir or "unbekannt"
reason = f"angetriebenes Streckensegment (Antriebfahrtrichtung: {drive})"
@@ -584,14 +608,43 @@ def derive_tros(
# bleibt Vario (er ist der einzige Antriebspunkt der Strecke).
seps_set = set(seps)
track_seps = [tid for tid in element.track_ids if tid in seps_set]
track_seps_set = set(track_seps)
last_sep_id = track_seps[-1] if len(track_seps) > 1 else None
for sep_id in seps or [None]:
sep_list = [sep_id] if sep_id else []
for sep_id in seps:
if sep_id not in track_seps_set:
# Zuordnung zeigt zwar auf diese Streckengruppe, der Separator
# ist aber nicht Teil von deren eigener TrackIds-Kette - kein
# VF-Teilstueck treibt ihn an, also plain 1Sep statt Vario
# (Mubea VF_1-VF_3 vor Ort bestaetigt, Review 2026-09-24: pro
# Streckengruppe genau 1 echter Kettenseparator, alle
# uebrigen Zuordnung-Treffer sind eigenstaendige Vereinzeler).
tros.append(
Tro(
tro_id="",
type_name="1Sep",
label=element.name,
hosts=[node_id],
separators=[sep_id],
scanners=scanners_for([sep_id]),
reason=(
f"Separator {sep_id}: Zuordnung zeigt auf "
f"{element.name}, ist aber nicht Teil von dessen "
f"TrackIds-Kette - kein eigenes VF treibt ihn an"
),
confidence="mittel",
)
)
findings.append(
f"{element.name} Separator {sep_id}: Zuordnung ohne "
f"TrackIds-Mitgliedschaft - als 1Sep statt Vario eingestuft"
)
continue
type_name = "Vario"
sep_reason = reason
confidence = "hoch"
if sep_id is not None and sep_id == last_sep_id:
if sep_id == last_sep_id:
type_name = "1Sep"
confidence = "mittel"
sep_reason = (
@@ -603,7 +656,7 @@ def derive_tros(
f"{element.name} Separator {sep_id}: letzter Separator vor dem "
f"Wiedereinschleusen - als 1Sep statt Vario eingestuft"
)
elif sep_id is not None:
else:
seg_drive, seg_drop = _incoming_drop(element, sep_id, elements_by_id)
if seg_drive == "horizontal" and seg_drop is not None and seg_drop <= -GEFAELLE_DROP_MM:
type_name = "1Sep"
@@ -625,8 +678,8 @@ def derive_tros(
type_name=type_name,
label=element.name,
hosts=[node_id],
separators=sep_list,
scanners=scanners_for(sep_list),
separators=[sep_id],
scanners=scanners_for([sep_id]),
reason=sep_reason,
confidence=confidence,
)