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
|
||||
|
||||
|
||||
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(
|
||||
lanes: list[str],
|
||||
elements: list[Element],
|
||||
@@ -730,8 +780,10 @@ def derive_tros(
|
||||
# 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 = _weichen_nach_umlauf(
|
||||
_find_switch_separators(switch_candidates, elements), elements, findings)
|
||||
switch_hits = _weichen_aus_umlauf(
|
||||
_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) -
|
||||
# wie switch_hits vor der Host-Aufloesung herausgenommen, Schritt 3c.
|
||||
weiche_hits = _find_weiche_separators(
|
||||
@@ -1054,6 +1106,58 @@ def derive_tros(
|
||||
)
|
||||
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
|
||||
# _find_weiche_separators). Host wie bei 3b: der aufgeloeste Zuordnung-Host
|
||||
# des Separators, sonst die Weiche selbst.
|
||||
@@ -1625,18 +1729,31 @@ def link_tros(
|
||||
# ueber die Strecken-Kette oben an den ersten angeschlossen). Gerichtete
|
||||
# Kante statt "gleiche Bahn"-Punktlinie, mit dem bereits vorhandenen
|
||||
# "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:
|
||||
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
|
||||
ziel_id = strecke_first_owned.get(tro.hosts[0])
|
||||
if ziel_id is None or ziel_id == tro.tro_id:
|
||||
continue
|
||||
pair = (tro.tro_id, ziel_id)
|
||||
edges.add(pair)
|
||||
edge_kinds.setdefault(pair, "weiche")
|
||||
verkettete_paare.add(frozenset(pair))
|
||||
directed_paare.add(pair)
|
||||
weichen_kanten = 0
|
||||
for host in tro.hosts:
|
||||
ziel_id = strecke_first_owned.get(host)
|
||||
if ziel_id is None or ziel_id == tro.tro_id:
|
||||
continue
|
||||
pair = (tro.tro_id, ziel_id)
|
||||
edges.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
|
||||
# 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
|
||||
# kann durchaus auf der Bahn verkettet UND mit seinem Vario-TRO ueber die
|
||||
# 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 tro_id in ids:
|
||||
tro = next(t for t in tros if t.tro_id == 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:
|
||||
tro.colocated = sorted(set(tro.colocated) | others)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user