Weichenerkennung: nur Ausschleuselement zaehlt, Regel 2 mit Obergrenze
Zwei Korrekturen zu c788647, beide durch Review gefunden (HundM05):
1. Ein Einschleuselement (Wiedereinschleusen in den Kreisel) ist keine
Weiche - das ist ein passiver Merge ohne Richtungsentscheidung, nur das
Ausschleusen (Verlassen der Bahn) legt die Fahrtrichtung fest.
_find_switch_separators() beruecksichtigt jetzt ausschliesslich
Ausschleuselemente; ein Separator an einem Einschleuselement (z. B.
HundM05 Separator 0018) wird nicht mehr als eigener 1Sep1Swi
herausgeloest, sondern zaehlt normal zum Vario-TRO seines Streckensegments.
HundM05.csv: 6 -> 5 automatisch erkannte Weichen (deckt sich jetzt mit den
5 Ausschleuselementen im Layout).
2. Beim Pruefen an Mubea (500573) fiel auf, dass Regel 2 (naechster freier
Separator, bisher ohne Obergrenze) dort mehrere Separatoren dem naechsten
noch freien Ausschleus-/Einschleuselement zugeordnet hatte, obwohl das
3.5-9m entfernt lag - eindeutig keine Weiche. Neue Konstante
SWITCH_FALLBACK_MM (1500mm) begrenzt Regel 2: grosszuegig genug fuer die
bei HundM05 beobachtete Kalibrierungs-Drift (bis 1139mm), zu eng fuer
Mubeas Fehlzuordnungen. mubea.csv: 9 -> 4 automatisch erkannte Weichen
(die beiden bestaetigten Handkorrekturen in tro_overrides.ini bleiben
unveraendert).
Beide Projekte weiterhin ohne Fehler, keine nicht verbundenen TROs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
c7886477ff
commit
fa96a6e666
+57
-46
@@ -113,14 +113,24 @@ from tro_overrides import (
|
||||
# einem Separator) wird ein Speicherblock (PinStore_Auto) angenommen.
|
||||
MIN_STORE_LANES = 2
|
||||
|
||||
# Toleranz (mm), innerhalb derer ein Separator als "sitzt an einem Aus-/
|
||||
# Einschleuselement" gilt (siehe _find_switch_separators). Kalibriert an
|
||||
# hundmM05_1.csv: die tatsaechlich am Uebergang montierten Separatoren liegen
|
||||
# 250-350mm vom Element entfernt, die naechsten unbeteiligten (auf der
|
||||
# Toleranz (mm), innerhalb derer ein Separator als "sitzt an einem
|
||||
# Ausschleuselement" gilt (siehe _find_switch_separators, Regel 1). Kalibriert
|
||||
# an hundmM05_1.csv: die tatsaechlich am Uebergang montierten Separatoren
|
||||
# liegen 250-350mm vom Element entfernt, die naechsten unbeteiligten (auf der
|
||||
# eigenen Streckenmitte montierten) Separatoren erst ab ca. 885mm - die
|
||||
# Grenze liegt bewusst in der Mitte dieser Luecke.
|
||||
SWITCH_PROXIMITY_MM = 500.0
|
||||
|
||||
# Obergrenze (mm) fuer Regel 2 in _find_switch_separators (naechster freier
|
||||
# Separator ohne Treffer aus Regel 1). Grosszuegiger als SWITCH_PROXIMITY_MM,
|
||||
# aber bewusst NICHT unbegrenzt: an HundM05 (Review 2026-09-18) lagen die noch
|
||||
# echten Weichen bis 1139mm vom Ausschleuselement entfernt - das deckt dieser
|
||||
# Wert. Ohne diese Grenze hat sich Regel 2 an einer groesseren Anlage (Mubea,
|
||||
# 500573) als Fehlerquelle erwiesen: uebrig gebliebene, tatsaechlich
|
||||
# unbeteiligte Separatoren wurden dem naechsten noch freien Ausschleuselement
|
||||
# zugeordnet, auch wenn das 5-9 Meter entfernt lag.
|
||||
SWITCH_FALLBACK_MM = 1500.0
|
||||
|
||||
GENERATED_MARKER = "<!-- erzeugt von lib/tro_flow.py -->"
|
||||
|
||||
# Zeichnungsmassstab fuer --use-cords: Graphviz rechnet in Punkt, die Anlage in
|
||||
@@ -273,43 +283,46 @@ def _default_resolve_host(sep: Element, findings: list[str], graph: Graph) -> st
|
||||
def _find_switch_separators(
|
||||
separators: list[Element], elements: list[Element], tol_mm: float
|
||||
) -> dict[str, tuple[Element, float]]:
|
||||
"""Separator-Id -> (Aus-/Einschleuselement der Weiche, Abstand mm).
|
||||
"""Separator-Id -> (Ausschleuselement der Weiche, Abstand mm).
|
||||
|
||||
Ein Separator ist eine Weiche, wenn ihm ein ILS Aus- oder Einschleuselement
|
||||
Ein Separator ist eine Weiche, wenn ihm ein ILS Ausschleuselement
|
||||
zugeordnet werden kann - das Element ist die Weiche (siehe unten), der
|
||||
Separator nur der Steuerpunkt davor. Ein eventueller Scanner am selben
|
||||
Separator ist dabei kein Kriterium, sondern nur ein zusaetzliches Bauteil
|
||||
des TROs (siehe derive_tros/scanners_for) - viele Separatoren tragen einen
|
||||
Scanner, ohne eine Weiche zu sein.
|
||||
Separator nur der Steuerpunkt davor. NICHT das Einschleuselement: das
|
||||
Wiedereinschleusen ist ein passiver Merge ohne Richtungsentscheidung, nur
|
||||
das Ausschleusen (Verlassen der Kreisel-Bahn) ist eine Weichenstelle - vor
|
||||
Ort bestaetigt (HundM05, Review 2026-09-21): ein Separator an einem
|
||||
Einschleuselement ist keine eigene Uebergabestelle. Ein eventueller
|
||||
Scanner am selben Separator ist ebenfalls kein Kriterium, sondern nur ein
|
||||
zusaetzliches Bauteil des TROs (siehe derive_tros/scanners_for) - viele
|
||||
Separatoren tragen einen Scanner, ohne eine Weiche zu sein.
|
||||
|
||||
Zwei Durchlaeufe, um trotzdem jedem Aus-/Einschleuselement genau einen
|
||||
Zwei Durchlaeufe, um trotzdem jedem Ausschleuselement genau einen
|
||||
Separator zuzuordnen:
|
||||
|
||||
1. Geometrische Naehe (Distanz <= tol_mm). HundM05 hat an seinen
|
||||
Kreisel<->Strecke-Uebergaengen kein eigenes "ILS Weiche"-Bauteil
|
||||
(anders als ein Kreisel<->Kreisel-Beruehrpunkt, siehe Mubea) - das von
|
||||
dxfmakros synthetisierte Ausschleuselement (Kreisel -> Strecke, am
|
||||
Streckenanfang) bzw. Einschleuselement (Strecke -> Kreisel, am
|
||||
Streckenende) uebernimmt dort faktisch die Rolle der Weiche. Sitzt ein
|
||||
Separator direkt daneben, gehoert er zu dieser Weiche - unabhaengig
|
||||
Streckenanfang) uebernimmt dort faktisch die Rolle der Weiche. Sitzt
|
||||
ein Separator direkt daneben, gehoert er zu dieser Weiche - unabhaengig
|
||||
davon, ob seine CSV-Spalte "Zuordnung" auf die Kreisel-Bahn oder auf
|
||||
die Strecke zeigt (in den Daten kommt beides vor, siehe derive_tros).
|
||||
2. Naechstgelegener freier Separator je noch unbelegtem Aus-/
|
||||
Einschleuselement, jedes Paar nur einmal vergeben (kleinste Distanz
|
||||
zuerst), auf derselben Bahn und mit derselben Rolle (Aus- oder
|
||||
Einschleusung) wie ein bereits durch Regel 1 bestaetigter Treffer -
|
||||
verhindert, dass ein Separator versehentlich mit dem naeher liegenden
|
||||
Einschleuselement einer anderen Strecke verkoppelt wird. Grund fuer den
|
||||
zweiten Durchlauf (HundM05, Review 2026-09-18): an einem Kreisel mit 5
|
||||
Abzweigen lagen die tatsaechlichen Schalt-Separatoren pro Abzweig
|
||||
unterschiedlich weit vom Uebergabeelement entfernt (418-1139mm in der
|
||||
aktuellen Registrierung) - eine einzelne mm-Schwelle (Regel 1) trifft
|
||||
nicht alle. Durch die eindeutige Vergabe (jedes Element nur einmal)
|
||||
bleibt ein Separator ohne eigenes Uebergabeelement (z. B. ein
|
||||
gewoehnlicher Zwischenseparator auf der Bahn) trotzdem unbelegt, auch
|
||||
ohne feste mm-Grenze.
|
||||
2. Naechstgelegener freier Separator je noch unbelegtem Ausschleuselement,
|
||||
jedes Paar nur einmal vergeben (kleinste Distanz zuerst, hoechstens
|
||||
SWITCH_FALLBACK_MM), und nur auf einer Bahn, die bereits durch Regel 1
|
||||
einen bestaetigten Treffer hat (eine Bahn ohne jeden nahen Treffer hat
|
||||
vermutlich gar keine Weiche, z. B. Kreisel1 Bahn R bei HundM05). Grund
|
||||
fuer den zweiten Durchlauf (HundM05, Review 2026-09-18): an einem
|
||||
Kreisel mit 5 Abzweigen lagen die tatsaechlichen Schalt-Separatoren pro
|
||||
Abzweig unterschiedlich weit vom Uebergabeelement entfernt (418-1139mm
|
||||
in der damaligen Registrierung) - eine einzelne mm-Schwelle (Regel 1)
|
||||
trifft nicht alle. SWITCH_FALLBACK_MM begrenzt das trotzdem: ohne
|
||||
Obergrenze hat Regel 2 an einer groesseren Anlage (Mubea) uebrig
|
||||
gebliebene, tatsaechlich unbeteiligte Separatoren dem naechsten noch
|
||||
freien Ausschleuselement zugeordnet, auch wenn das 5-9 Meter entfernt
|
||||
lag - siehe SWITCH_FALLBACK_MM.
|
||||
"""
|
||||
transfers = [e for e in elements if e.kind in ("Ausschleus", "Einschleus")]
|
||||
transfers = [e for e in elements if e.kind == "Ausschleus"]
|
||||
result: dict[str, tuple[Element, float]] = {}
|
||||
|
||||
for sep in separators:
|
||||
@@ -325,28 +338,24 @@ def _find_switch_separators(
|
||||
if best is not None:
|
||||
result[sep.teile_id] = (best[1], best[0])
|
||||
|
||||
# Rolle je Kreisel-Anschluss aus einem bereits bestaetigten Treffer lesen -
|
||||
# keine Annahme, welche Bahn (-L/-R) Aus- bzw. Einschleusung uebernimmt.
|
||||
bahn_role: dict[str, str] = {}
|
||||
for sep in separators:
|
||||
hit = result.get(sep.teile_id)
|
||||
if hit and sep.assignment:
|
||||
bahn_role.setdefault(sep.assignment, hit[0].kind)
|
||||
# Bahnen mit mindestens einem durch Regel 1 bestaetigten Treffer - nur
|
||||
# dort darf Regel 2 ueberhaupt suchen (siehe Docstring).
|
||||
switch_bahnen = {sep.assignment for sep in separators
|
||||
if sep.teile_id in result and sep.assignment}
|
||||
|
||||
used_transfers = {hit[0].teile_id for hit in result.values()}
|
||||
remaining_seps = [s for s in separators if s.teile_id not in result
|
||||
and s.x is not None and s.y is not None
|
||||
and bahn_role.get(s.assignment or "") is not None]
|
||||
and s.assignment in switch_bahnen]
|
||||
|
||||
candidates: list[tuple[float, Element, Element]] = []
|
||||
for sep in remaining_seps:
|
||||
role = bahn_role[sep.assignment or ""]
|
||||
for transfer in transfers:
|
||||
if (transfer.kind != role or transfer.teile_id in used_transfers
|
||||
or transfer.x is None or transfer.y is None):
|
||||
if transfer.teile_id in used_transfers or transfer.x is None or transfer.y is None:
|
||||
continue
|
||||
d = math.hypot(transfer.x - sep.x, transfer.y - sep.y)
|
||||
candidates.append((d, sep, transfer))
|
||||
if d <= SWITCH_FALLBACK_MM:
|
||||
candidates.append((d, sep, transfer))
|
||||
candidates.sort(key=lambda c: c[0])
|
||||
|
||||
claimed_seps: set[str] = set()
|
||||
@@ -388,7 +397,7 @@ def derive_tros(
|
||||
(e for e in elements if e.kind == "Scanner"), key=lambda e: e.teile_id
|
||||
)
|
||||
|
||||
# Separatoren an einem Aus-/Einschleuselement (siehe _find_switch_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
|
||||
@@ -520,7 +529,7 @@ def derive_tros(
|
||||
)
|
||||
)
|
||||
|
||||
# 3b) Separatoren an einem Aus-/Einschleuselement -> eigene 1Sep1Swi-
|
||||
# 3b) Separatoren an einem Ausschleuselement -> eigene 1Sep1Swi-
|
||||
# Uebergabestelle. Das Element uebernimmt die Rolle der (hier fehlenden)
|
||||
# Weiche; siehe _find_switch_separators. Host bewusst die Strecke (nicht
|
||||
# die Kreisel-Bahn): beide Elementarten tragen "Strecke" oder "Foerderer"
|
||||
@@ -532,7 +541,7 @@ def derive_tros(
|
||||
strecke_id = str(_merkmal(transfer.merkmale, "Strecke", "Förderer") or "")
|
||||
ende = str(_merkmal(transfer.merkmale, "Streckenende") or "")
|
||||
own_scanners = sorted(sep_scanners.get(sep_id, []))
|
||||
art = "Ausschleuselement" if transfer.kind == "Ausschleus" else "Einschleuselement"
|
||||
art = "Ausschleuselement"
|
||||
tros.append(
|
||||
Tro(
|
||||
tro_id="",
|
||||
@@ -1391,8 +1400,10 @@ def render_doc(
|
||||
add("| Gefaellestrecke, einzelne Linie | `1Sep` |")
|
||||
add("| Strecke (angetrieben) | `Vario` (je Streckensegment) |")
|
||||
add("| Kreisel-Bahn | `1Sep` |")
|
||||
add(f"| Separator <= {SWITCH_PROXIMITY_MM:.0f}mm von einem Aus-/Einschleuselement "
|
||||
"| `1Sep1Swi` (Element uebernimmt die fehlende Weiche) |")
|
||||
add(f"| Separator <= {SWITCH_PROXIMITY_MM:.0f}mm (oder naechstgelegen bis "
|
||||
f"{SWITCH_FALLBACK_MM:.0f}mm) von einem Ausschleuselement "
|
||||
"| `1Sep1Swi` (Element uebernimmt die fehlende Weiche; ein Einschleuselement "
|
||||
"zaehlt nicht - passiver Merge ohne Richtungsentscheidung) |")
|
||||
add("| zusaetzlich: 2 bzw. 3 abgehende Wege | Aufwertung zu `1Sep1Swi` / `1Sep2Swi` |")
|
||||
add("")
|
||||
add("Ein **Scanner** am Separator aendert den Typ nicht. In der Referenzanlage "
|
||||
|
||||
Reference in New Issue
Block a user