diff --git a/lib/tro_flow.py b/lib/tro_flow.py index 15fdf78..0b716e4 100644 --- a/lib/tro_flow.py +++ b/lib/tro_flow.py @@ -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, )