tro_flow: Weichen aus der Umlaufreihenfolge, 1Sep2Swi, keine "gleiche Bahn"-Notiz an Weichen-TROs
- _weichen_aus_umlauf: ein Separator, auf den in Laufrichtung ein Ausschleuselement folgt (ohne Separator dazwischen), ist dessen Weiche - auch ohne CSV-Nachbarschaft (Dematic 0043 -> VF_5, Nachbarn nur Einschleuselemente); Speicherlinien ausgenommen - Schritt 3b': weitere Ausschleuselemente bis zum naechsten Separator gehoeren zum selben Separator, zwei -> 1Sep2Swi (Dematic 0048 -> VF_4 + VF_6), Finding bei fehlendem Scanner bzw. mehr als zwei Weichen - Weichenkante je Host; Weichen-TRO ohne Bahn-Nachfolger (einziger Separator seines Kreisels) zaehlt als praezise Quelle - entfernt die Contraction-Walk-Kanten an VF_4/VF_5/VF_6 vorbei - keine gestrichelte "gleiche Bahn"-Notiz an Weichen-TROs (Host ist die Strecke, in die sie abzweigen, nicht ihre Bahn) - HundM05 (28 Kanten) und Mubea (26) inhaltlich unveraendert, --use-cords-Ausgabe byte-identisch Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e91b754d90
commit
90cafec99c
+137
-12
@@ -362,6 +362,56 @@ def _weichen_nach_umlauf(
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _weichen_aus_umlauf(
|
||||||
|
switch_hits: dict[str, tuple[Element, float]],
|
||||||
|
kandidaten: list[Element],
|
||||||
|
elements: list[Element],
|
||||||
|
findings: list[str],
|
||||||
|
) -> dict[str, tuple[Element, float]]:
|
||||||
|
"""Weichen-Separatoren ergaenzen, die die CSV-Nachbarn nicht zeigen.
|
||||||
|
|
||||||
|
Ein Separator, auf den in Laufrichtung ein Ausschleuselement folgt, ohne
|
||||||
|
Separator dazwischen, steuert diese Weiche - auch wenn das Element nicht
|
||||||
|
in seinen "Nachbarn" steht (dort stehen nur die unmittelbaren Ring-
|
||||||
|
Nachbarn, oft ein Einschleuselement). Dematic/Arvato Kreisel1 (Review
|
||||||
|
2026-10-08): 0043 -> Ausschleuselement 0071 (VF_5), Nachbarn von 0043
|
||||||
|
sind nur die Einschleuselemente von VF_4/VF_6. Weitere Elemente bis zum
|
||||||
|
naechsten Separator ergaenzt derive_tros Schritt 3b' (1Sep2Swi).
|
||||||
|
|
||||||
|
Ausschleuselemente einer Gefaellestrecke bleiben aussen vor: das sind
|
||||||
|
Speicherlinien, ihr Steuer-Separator entsteht in _speicher_bloecke nur
|
||||||
|
aus der CSV-Nachbarschaft (Mubea 0044 bleibt eigener 1Sep vor dem Block).
|
||||||
|
"""
|
||||||
|
by_id = {e.teile_id: e for e in elements}
|
||||||
|
erlaubt = {s.teile_id for s in kandidaten}
|
||||||
|
belegt = {t.teile_id for t, _d in switch_hits.values()}
|
||||||
|
result = dict(switch_hits)
|
||||||
|
for kreisel in (e for e in elements if e.kind == "Kreisel" and e.track_ids):
|
||||||
|
ids = list(kreisel.track_ids)
|
||||||
|
ring = kreisel.rotation in (ROTATION_CW, ROTATION_CCW)
|
||||||
|
for i, as_id in enumerate(ids):
|
||||||
|
transfer = by_id.get(as_id)
|
||||||
|
if transfer is None or transfer.kind != "Ausschleus" or as_id in belegt:
|
||||||
|
continue
|
||||||
|
strecke = by_id.get(str(_merkmal(transfer.merkmale, "Strecke", "Förderer") or ""))
|
||||||
|
if strecke is not None and strecke.kind == "Gefaellestrecke":
|
||||||
|
continue
|
||||||
|
davor = ids[:i][::-1] + (ids[i + 1:][::-1] if ring else [])
|
||||||
|
sep_id = next((t for t in davor
|
||||||
|
if (el := by_id.get(t)) and el.kind == "Separator"), None)
|
||||||
|
if sep_id is None or sep_id in result or sep_id not in erlaubt:
|
||||||
|
continue
|
||||||
|
sep = by_id[sep_id]
|
||||||
|
d = (math.hypot(transfer.x - sep.x, transfer.y - sep.y)
|
||||||
|
if None not in (sep.x, sep.y, transfer.x, transfer.y) else 0.0)
|
||||||
|
result[sep_id] = (transfer, d)
|
||||||
|
belegt.add(as_id)
|
||||||
|
findings.append(
|
||||||
|
f"Separator {sep_id}: steuert Ausschleuselement {as_id} (folgt in "
|
||||||
|
f"Laufrichtung ohne Separator dazwischen, kein CSV-Nachbar) - Weiche")
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _speicher_bloecke(
|
def _speicher_bloecke(
|
||||||
lanes: list[str],
|
lanes: list[str],
|
||||||
elements: list[Element],
|
elements: list[Element],
|
||||||
@@ -730,8 +780,10 @@ def derive_tros(
|
|||||||
# unten (3b) ihre eigene 1Sep1Swi-Uebergabestelle und duerfen nicht zugleich
|
# unten (3b) ihre eigene 1Sep1Swi-Uebergabestelle und duerfen nicht zugleich
|
||||||
# als gewoehnlicher Separator ihres Zuordnung-Hosts (Kreisel-Bahn oder
|
# als gewoehnlicher Separator ihres Zuordnung-Hosts (Kreisel-Bahn oder
|
||||||
# Strecke - beides kommt vor) mitgezaehlt werden.
|
# Strecke - beides kommt vor) mitgezaehlt werden.
|
||||||
switch_hits = _weichen_nach_umlauf(
|
switch_hits = _weichen_aus_umlauf(
|
||||||
_find_switch_separators(switch_candidates, elements), elements, findings)
|
_weichen_nach_umlauf(
|
||||||
|
_find_switch_separators(switch_candidates, elements), elements, findings),
|
||||||
|
switch_candidates, elements, findings)
|
||||||
# Separatoren an einer echten ILS Weiche (siehe _find_weiche_separators) -
|
# Separatoren an einer echten ILS Weiche (siehe _find_weiche_separators) -
|
||||||
# wie switch_hits vor der Host-Aufloesung herausgenommen, Schritt 3c.
|
# wie switch_hits vor der Host-Aufloesung herausgenommen, Schritt 3c.
|
||||||
weiche_hits = _find_weiche_separators(
|
weiche_hits = _find_weiche_separators(
|
||||||
@@ -1054,6 +1106,58 @@ def derive_tros(
|
|||||||
)
|
)
|
||||||
tros.append(tro)
|
tros.append(tro)
|
||||||
|
|
||||||
|
# 3b') Weitere Weichen desselben Separators. Ein Separator steuert ALLE
|
||||||
|
# Ausschleuselemente, die in Laufrichtung bis zum naechsten Separator auf
|
||||||
|
# ihn folgen - nicht nur das eine, das in seinen CSV-Nachbarn steht
|
||||||
|
# (_find_switch_separators). Dematic/Arvato Kreisel4 (GUZ): 0048 ->
|
||||||
|
# Ausschleuselement 0069 (VF_4) -> Ausschleuselement 0077 (VF_6); 0077 ist
|
||||||
|
# kein CSV-Nachbar von 0048, wird aber von ihm gesteuert (Review
|
||||||
|
# 2026-10-07). Mit zwei Weichen wird der TRO zum 1Sep2Swi (Katalog: 1x
|
||||||
|
# Separator, 2x Switch, 1x Scanner). Ausschleuselemente von Speicherlinien
|
||||||
|
# gehoeren dem PinStore_Auto (Schritt 1) und zaehlen hier nicht.
|
||||||
|
speicher_hosts = {h for t in tros if t.type_name == "PinStore_Auto" for h in t.hosts}
|
||||||
|
sep_switch_tro = {t.separators[0]: t for t in as_switch_tro.values() if t.separators}
|
||||||
|
weichen_je_tro: dict[int, list[str]] = {
|
||||||
|
id(t): [a for a, tt in as_switch_tro.items() if tt is t] for t in as_switch_tro.values()}
|
||||||
|
for kreisel in (e for e in elements if e.kind == "Kreisel" and e.track_ids):
|
||||||
|
ids = list(kreisel.track_ids)
|
||||||
|
ring = kreisel.rotation in (ROTATION_CW, ROTATION_CCW)
|
||||||
|
for i, as_id in enumerate(ids):
|
||||||
|
transfer = elements_by_id.get(as_id)
|
||||||
|
if transfer is None or transfer.kind != "Ausschleus" or as_id in as_switch_tro:
|
||||||
|
continue
|
||||||
|
strecke_id = str(_merkmal(transfer.merkmale, "Strecke", "Förderer") or "")
|
||||||
|
if strecke_id in speicher_hosts:
|
||||||
|
continue
|
||||||
|
davor = ids[:i][::-1] + (ids[i + 1:][::-1] if ring else [])
|
||||||
|
steuer = next((t for t in davor if (el := elements_by_id.get(t))
|
||||||
|
and el.kind == "Separator"), None)
|
||||||
|
tro = sep_switch_tro.get(steuer or "")
|
||||||
|
if tro is None:
|
||||||
|
continue
|
||||||
|
as_switch_tro[as_id] = tro
|
||||||
|
weichen = weichen_je_tro[id(tro)]
|
||||||
|
weichen.append(as_id)
|
||||||
|
if strecke_id in graph.nodes and strecke_id not in tro.hosts:
|
||||||
|
tro.hosts.append(strecke_id)
|
||||||
|
tro.label = (f"Separator {steuer} an Ausschleuselementen "
|
||||||
|
f"{', '.join(weichen)} (Strecken {', '.join(tro.hosts)})")
|
||||||
|
tro.reason += (f"; steuert auch Ausschleuselement {as_id} - folgt in "
|
||||||
|
f"Laufrichtung ohne Separator dazwischen")
|
||||||
|
if len(weichen) == 2:
|
||||||
|
tro.type_name = "1Sep2Swi"
|
||||||
|
else:
|
||||||
|
findings.append(
|
||||||
|
f"Separator {steuer}: {len(weichen)} Ausschleuselemente "
|
||||||
|
f"({', '.join(weichen)}) ohne Separator dazwischen - der "
|
||||||
|
f"Katalog kennt hoechstens 1Sep2Swi, bitte pruefen")
|
||||||
|
for tro in sep_switch_tro.values():
|
||||||
|
if tro.type_name == "1Sep2Swi" and not tro.scanners:
|
||||||
|
findings.append(
|
||||||
|
f"{tro.label}: 1Sep2Swi ohne Scanner - der Katalog verlangt einen "
|
||||||
|
f"Barcode-Scanner am Eingang, im Layout fehlt einer vor Separator "
|
||||||
|
f"{tro.separators[0]}")
|
||||||
|
|
||||||
# 3c) Separatoren an einer echten ILS Weiche -> 1Sep1Swi (siehe
|
# 3c) Separatoren an einer echten ILS Weiche -> 1Sep1Swi (siehe
|
||||||
# _find_weiche_separators). Host wie bei 3b: der aufgeloeste Zuordnung-Host
|
# _find_weiche_separators). Host wie bei 3b: der aufgeloeste Zuordnung-Host
|
||||||
# des Separators, sonst die Weiche selbst.
|
# des Separators, sonst die Weiche selbst.
|
||||||
@@ -1625,18 +1729,31 @@ def link_tros(
|
|||||||
# ueber die Strecken-Kette oben an den ersten angeschlossen). Gerichtete
|
# ueber die Strecken-Kette oben an den ersten angeschlossen). Gerichtete
|
||||||
# Kante statt "gleiche Bahn"-Punktlinie, mit dem bereits vorhandenen
|
# Kante statt "gleiche Bahn"-Punktlinie, mit dem bereits vorhandenen
|
||||||
# "weiche"-Kantenstil (orange, Label "Weiche"). Review 2026-09-21.
|
# "weiche"-Kantenstil (orange, Label "Weiche"). Review 2026-09-21.
|
||||||
|
# Je Host eine Weichenkante: ein 1Sep2Swi hat zwei Strecken (Schritt 3b'
|
||||||
|
# in derive_tros). Hat ein Weichen-TRO keinen Nachfolger auf der Bahn
|
||||||
|
# (einziger Separator seines Kreisels - Dematic Kreisel1/Kreisel4: ohne
|
||||||
|
# Abzweig laeuft das Material auf dem Kreisel zu ihm selbst zurueck),
|
||||||
|
# beschreiben die Weichenkanten seine Weiterfuehrung trotzdem vollstaendig;
|
||||||
|
# er zaehlt dann wie ein Bahn-TRO als praezise Quelle, die groben
|
||||||
|
# Contraction-Walk-Kanten entfallen (sie fuehrten an VF_5 vorbei direkt
|
||||||
|
# auf Kreisel2, Review 2026-10-08).
|
||||||
for tro in tros:
|
for tro in tros:
|
||||||
definition = tro.definition()
|
definition = tro.definition()
|
||||||
if not (definition and definition.has_item(TroItem.SWITCH)) or len(tro.hosts) != 1:
|
if not (definition and definition.has_item(TroItem.SWITCH)):
|
||||||
continue
|
continue
|
||||||
ziel_id = strecke_first_owned.get(tro.hosts[0])
|
weichen_kanten = 0
|
||||||
if ziel_id is None or ziel_id == tro.tro_id:
|
for host in tro.hosts:
|
||||||
continue
|
ziel_id = strecke_first_owned.get(host)
|
||||||
pair = (tro.tro_id, ziel_id)
|
if ziel_id is None or ziel_id == tro.tro_id:
|
||||||
edges.add(pair)
|
continue
|
||||||
edge_kinds.setdefault(pair, "weiche")
|
pair = (tro.tro_id, ziel_id)
|
||||||
verkettete_paare.add(frozenset(pair))
|
edges.add(pair)
|
||||||
directed_paare.add(pair)
|
edge_kinds.setdefault(pair, "weiche")
|
||||||
|
verkettete_paare.add(frozenset(pair))
|
||||||
|
directed_paare.add(pair)
|
||||||
|
weichen_kanten += 1
|
||||||
|
if tro.hosts and weichen_kanten == len(tro.hosts):
|
||||||
|
precise_sources.add(tro.tro_id)
|
||||||
|
|
||||||
# Jedes TRO, fuer das oben ein praeziser Nachfolger in der Umlaufkette
|
# Jedes TRO, fuer das oben ein praeziser Nachfolger in der Umlaufkette
|
||||||
# gefunden wurde (precise_sources), hat damit seine gesamte reale
|
# gefunden wurde (precise_sources), hat damit seine gesamte reale
|
||||||
@@ -1660,11 +1777,19 @@ def link_tros(
|
|||||||
# ungerichtete "gleiche Bahn"-Notiz nicht zusaetzlich - ein Weichen-TRO
|
# ungerichtete "gleiche Bahn"-Notiz nicht zusaetzlich - ein Weichen-TRO
|
||||||
# kann durchaus auf der Bahn verkettet UND mit seinem Vario-TRO ueber die
|
# kann durchaus auf der Bahn verkettet UND mit seinem Vario-TRO ueber die
|
||||||
# Weiche verbunden sein, das sind zwei unabhaengige Beziehungen.
|
# Weiche verbunden sein, das sind zwei unabhaengige Beziehungen.
|
||||||
|
# Weichen-TROs bekommen keine "gleiche Bahn"-Notiz: ihr Host ist die
|
||||||
|
# Strecke, IN die sie abzweigen - sie selbst sitzen auf der Kreisel-Bahn,
|
||||||
|
# nicht auf dieser Strecke. Die Notiz verband sonst einen 1Sep2Swi mit
|
||||||
|
# jedem TRO beider Strecken (Dematic TRO13 mit VF_4 und VF_6, Review
|
||||||
|
# 2026-10-08); die Verbindung zeigt bereits die Weichenkante.
|
||||||
|
weichen_tros = {t.tro_id for t in tros
|
||||||
|
if (d := t.definition()) and d.has_item(TroItem.SWITCH)}
|
||||||
for host, ids in owners.items():
|
for host, ids in owners.items():
|
||||||
for tro_id in ids:
|
for tro_id in ids:
|
||||||
tro = next(t for t in tros if t.tro_id == tro_id)
|
tro = next(t for t in tros if t.tro_id == tro_id)
|
||||||
others = {o for o in ids if o != tro_id
|
others = {o for o in ids if o != tro_id
|
||||||
and frozenset((tro_id, o)) not in verkettete_paare}
|
and frozenset((tro_id, o)) not in verkettete_paare
|
||||||
|
and not {tro_id, o} & weichen_tros}
|
||||||
if others:
|
if others:
|
||||||
tro.colocated = sorted(set(tro.colocated) | others)
|
tro.colocated = sorted(set(tro.colocated) | others)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user