From b197bf815051ed01b029aaabed0148d92dab2140 Mon Sep 17 00:00:00 2001 From: Samer Ayadi Date: Mon, 21 Sep 2026 13:01:59 +0200 Subject: [PATCH] TRO-Fluss: Reihenfolge auf der Kreisel-Bahn auch fuer Weichen-TROs Seit der Weichenerkennung (c788647/fa96a6e) bekommt ein Weichen-TRO als Host die gefuetterte Strecke statt die Kreisel-Bahn (derive_tros Schritt 3b) - fachlich richtig fuer den Materialfluss, aber die Positions-Verkettung in link_tros() gruppierte bisher rein nach Host: sobald nur noch ein einzelner "echter" Separator auf einer Bahn uebrig blieb (HundM05 Kreisel1 Bahn L: 0020, nachdem 0008/0010/0012/0014 eigene Weichen-TROs wurden), verschwand die Reihenfolge 0008->0010->0012->0014->0020 komplett - im TRO-Flussdiagramm sah es aus, als haenge jede der 5 Abzweigungen direkt und gleichrangig an genau diesem einen TRO (Review 2026-09-21: "es gibt viele unnoetige Linien"). _tro_bahn() bestimmt die Kreisel-Bahn eines TROs jetzt zusaetzlich ueber die Zuordnung seines eigenen Separators, wenn der TRO-Host selbst keine Bahn ist - link_tros() verkettet danach alle TROs derselben Bahn per Position, nicht nur die mit identischem Host. Die bestehenden Materialfluss-Kanten (Weiche/Vario -> Ruecklauf-Bahn) bleiben unveraendert bestehen; die Verkettung ergaenzt nur die bislang fehlende Reihenfolge der Abzweige. Die Colocated-Markierung ("gleiche Bahn", gepunktete Linie) wird jetzt pro Paar statt pro TRO unterdrueckt (verkettete_paare statt verkettet als TRO-Id- Menge) - sonst waere z. B. TRO01 (Weiche, auf der Bahn verkettet) faelschlich auch von seiner Colocated-Markierung mit TRO02 (Vario, gleiche Strecke) ausgenommen worden, obwohl das zwei unabhaengige Beziehungen sind. HundM05.csv: 24 -> 28 Kanten (4 neue Bahn-Kanten 0008->0010->0012->0014->0020). Mubea unveraendert (13 TROs, 55 Kanten), keine Fehler in beiden Projekten. Co-Authored-By: Claude Sonnet 5 --- lib/tro_flow.py | 73 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 16 deletions(-) diff --git a/lib/tro_flow.py b/lib/tro_flow.py index 8ee1f78..067edd3 100644 --- a/lib/tro_flow.py +++ b/lib/tro_flow.py @@ -714,8 +714,32 @@ def _bahn_richtung(node) -> tuple[float, float] | None: return achse if mit_achse else (-achse[0], -achse[1]) +def _tro_bahn(tro: Tro, graph: Graph, sep_assignment: dict[str, str]) -> str | None: + """Kreisel-Bahn, auf der ein TRO physisch sitzt - fuer die Positions- + Verkettung in link_tros(). Meist einfach der TRO-Host (wenn er direkt auf + der Bahn liegt, z. B. ein gewoehnlicher Kreisel-Separator). Bei einem + Weichen-TRO zeigt der Host dagegen auf die gefuetterte Strecke, nicht auf + die Bahn (siehe derive_tros Schritt 3b) - dort zaehlt stattdessen die + Zuordnung des eigenen Separators, falls die auf eine Kreisel-Bahn zeigt. + Zeigt sie stattdessen auf eine Strecke (z. B. HundM05 Separator 0016/0018, + die auf ihrer eigenen Strecke montiert sind, nicht auf der Kreisel-Bahn), + gibt es keine Bahn-Zugehoerigkeit - der TRO gehoert dann nicht in die + Verkettung. + """ + for host in tro.hosts: + node = graph.nodes.get(host) + if node and node.kind == "Kreisel": + return host + for sep_id in tro.separators: + bahn = sep_assignment.get(sep_id) + node = graph.nodes.get(bahn or "") + if node and node.kind == "Kreisel": + return bahn + return None + + def link_tros( - graph: Graph, tros: list[Tro] + graph: Graph, tros: list[Tro], elements: list[Element] ) -> tuple[list[tuple[str, str]], dict[tuple[str, str], str]]: """ TROs verknuepfen. @@ -762,17 +786,32 @@ def link_tros( # nebeneinander: das Material laeuft die Bahn in Foerderrichtung ab. Sie # werden daher der Reihe nach verkettet statt nur als "gleiche Bahn" # markiert - sonst fehlt z. B. die Kante von der einen Sperre zur naechsten. - verkettet: set[str] = set() - for host, ids in owners.items(): - if len(ids) < 2: + # Gruppiert wird ueber _tro_bahn() (Separator-Zuordnung), NICHT ueber + # owners/host: ein Weichen-TRO liegt zwar physisch auf der Bahn, sein Host + # wurde in derive_tros Schritt 3b aber auf die gefuetterte Strecke gelegt + # (review 2026-09-21 - sonst fehlt bei HundM05 die Reihenfolge 0008->0010-> + # 0012->0014->0020 auf Kreisel1 Bahn L komplett, sobald deren Weichen einen + # eigenen Host haben und nur noch ein einzelner Separator auf der Bahn + # selbst uebrig bleibt). + sep_assignment = {e.teile_id: e.assignment for e in elements + if e.kind == "Separator" and e.assignment} + bahn_gruppen: dict[str, list[Tro]] = {} + for tro in tros: + bahn = _tro_bahn(tro, graph, sep_assignment) + if bahn: + bahn_gruppen.setdefault(bahn, []).append(tro) + + verkettete_paare: set[frozenset[str]] = set() + for bahn, gruppe in bahn_gruppen.items(): + if len(gruppe) < 2: continue - node = graph.nodes.get(host) - if node is None or node.kind != "Kreisel" or not node.lane: + node = graph.nodes[bahn] + if not node.lane: continue richtung = _bahn_richtung(node) if richtung is None: continue - auf_bahn = [t for t in tros if t.tro_id in ids and t.position] + auf_bahn = [t for t in gruppe if t.position] if len(auf_bahn) < 2: continue # Projektion auf die Foerderrichtung: kleinster Wert kommt zuerst @@ -782,18 +821,20 @@ def link_tros( pair = (vorher.tro_id, nachher.tro_id) edges.add(pair) edge_kinds.setdefault(pair, "bahn") - verkettet.update(pair) + verkettete_paare.add(frozenset(pair)) - # gleicher Host = physisch dieselbe Strecke/Bahn. Verkettete Bahnen sind - # oben schon gerichtet verbunden und brauchen die ungerichtete Notiz nicht. + # gleicher Host = physisch dieselbe Strecke/Bahn. Ein bereits verkettetes + # PAAR (nicht: irgendein verketteter TRO - ein Weichen-TRO kann zugleich + # auf der Bahn verkettet UND mit seinem Vario-TRO auf derselben Strecke + # colocated sein, das sind zwei verschiedene Beziehungen) braucht die + # ungerichtete Notiz nicht zusaetzlich. for host, ids in owners.items(): for tro_id in ids: tro = next(t for t in tros if t.tro_id == tro_id) - if tro_id in verkettet: - continue - tro.colocated = sorted( - set(tro.colocated) | {o for o in ids if o != tro_id and o not in verkettet} - ) + others = {o for o in ids if o != tro_id + and frozenset((tro_id, o)) not in verkettete_paare} + if others: + tro.colocated = sorted(set(tro.colocated) | others) for tro in tros: tro.successors = sorted(dst for src, dst in edges if src == tro.tro_id) @@ -878,7 +919,7 @@ def analyse_tros( # Verorten vor dem Verknuepfen: die Reihenfolge mehrerer TROs auf einer # Kreisel-Bahn wird ueber ihre Koordinate bestimmt. locate_tros(linked, elements, graph) - analysis.edges, analysis.edge_kinds = link_tros(graph, linked) + analysis.edges, analysis.edge_kinds = link_tros(graph, linked, elements) # Vor Ort aufgenommene Topologie schlaegt die abgeleitete. Die Differenz # wird protokolliert, damit sichtbar bleibt, was die Ableitung falsch hatte.