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:
+57
-46
@@ -113,14 +113,24 @@ from tro_overrides import (
|
|||||||
# einem Separator) wird ein Speicherblock (PinStore_Auto) angenommen.
|
# einem Separator) wird ein Speicherblock (PinStore_Auto) angenommen.
|
||||||
MIN_STORE_LANES = 2
|
MIN_STORE_LANES = 2
|
||||||
|
|
||||||
# Toleranz (mm), innerhalb derer ein Separator als "sitzt an einem Aus-/
|
# Toleranz (mm), innerhalb derer ein Separator als "sitzt an einem
|
||||||
# Einschleuselement" gilt (siehe _find_switch_separators). Kalibriert an
|
# Ausschleuselement" gilt (siehe _find_switch_separators, Regel 1). Kalibriert
|
||||||
# hundmM05_1.csv: die tatsaechlich am Uebergang montierten Separatoren liegen
|
# an hundmM05_1.csv: die tatsaechlich am Uebergang montierten Separatoren
|
||||||
# 250-350mm vom Element entfernt, die naechsten unbeteiligten (auf der
|
# liegen 250-350mm vom Element entfernt, die naechsten unbeteiligten (auf der
|
||||||
# eigenen Streckenmitte montierten) Separatoren erst ab ca. 885mm - die
|
# eigenen Streckenmitte montierten) Separatoren erst ab ca. 885mm - die
|
||||||
# Grenze liegt bewusst in der Mitte dieser Luecke.
|
# Grenze liegt bewusst in der Mitte dieser Luecke.
|
||||||
SWITCH_PROXIMITY_MM = 500.0
|
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 -->"
|
GENERATED_MARKER = "<!-- erzeugt von lib/tro_flow.py -->"
|
||||||
|
|
||||||
# Zeichnungsmassstab fuer --use-cords: Graphviz rechnet in Punkt, die Anlage in
|
# 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(
|
def _find_switch_separators(
|
||||||
separators: list[Element], elements: list[Element], tol_mm: float
|
separators: list[Element], elements: list[Element], tol_mm: float
|
||||||
) -> dict[str, tuple[Element, 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
|
zugeordnet werden kann - das Element ist die Weiche (siehe unten), der
|
||||||
Separator nur der Steuerpunkt davor. Ein eventueller Scanner am selben
|
Separator nur der Steuerpunkt davor. NICHT das Einschleuselement: das
|
||||||
Separator ist dabei kein Kriterium, sondern nur ein zusaetzliches Bauteil
|
Wiedereinschleusen ist ein passiver Merge ohne Richtungsentscheidung, nur
|
||||||
des TROs (siehe derive_tros/scanners_for) - viele Separatoren tragen einen
|
das Ausschleusen (Verlassen der Kreisel-Bahn) ist eine Weichenstelle - vor
|
||||||
Scanner, ohne eine Weiche zu sein.
|
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:
|
Separator zuzuordnen:
|
||||||
|
|
||||||
1. Geometrische Naehe (Distanz <= tol_mm). HundM05 hat an seinen
|
1. Geometrische Naehe (Distanz <= tol_mm). HundM05 hat an seinen
|
||||||
Kreisel<->Strecke-Uebergaengen kein eigenes "ILS Weiche"-Bauteil
|
Kreisel<->Strecke-Uebergaengen kein eigenes "ILS Weiche"-Bauteil
|
||||||
(anders als ein Kreisel<->Kreisel-Beruehrpunkt, siehe Mubea) - das von
|
(anders als ein Kreisel<->Kreisel-Beruehrpunkt, siehe Mubea) - das von
|
||||||
dxfmakros synthetisierte Ausschleuselement (Kreisel -> Strecke, am
|
dxfmakros synthetisierte Ausschleuselement (Kreisel -> Strecke, am
|
||||||
Streckenanfang) bzw. Einschleuselement (Strecke -> Kreisel, am
|
Streckenanfang) uebernimmt dort faktisch die Rolle der Weiche. Sitzt
|
||||||
Streckenende) uebernimmt dort faktisch die Rolle der Weiche. Sitzt ein
|
ein Separator direkt daneben, gehoert er zu dieser Weiche - unabhaengig
|
||||||
Separator direkt daneben, gehoert er zu dieser Weiche - unabhaengig
|
|
||||||
davon, ob seine CSV-Spalte "Zuordnung" auf die Kreisel-Bahn oder auf
|
davon, ob seine CSV-Spalte "Zuordnung" auf die Kreisel-Bahn oder auf
|
||||||
die Strecke zeigt (in den Daten kommt beides vor, siehe derive_tros).
|
die Strecke zeigt (in den Daten kommt beides vor, siehe derive_tros).
|
||||||
2. Naechstgelegener freier Separator je noch unbelegtem Aus-/
|
2. Naechstgelegener freier Separator je noch unbelegtem Ausschleuselement,
|
||||||
Einschleuselement, jedes Paar nur einmal vergeben (kleinste Distanz
|
jedes Paar nur einmal vergeben (kleinste Distanz zuerst, hoechstens
|
||||||
zuerst), auf derselben Bahn und mit derselben Rolle (Aus- oder
|
SWITCH_FALLBACK_MM), und nur auf einer Bahn, die bereits durch Regel 1
|
||||||
Einschleusung) wie ein bereits durch Regel 1 bestaetigter Treffer -
|
einen bestaetigten Treffer hat (eine Bahn ohne jeden nahen Treffer hat
|
||||||
verhindert, dass ein Separator versehentlich mit dem naeher liegenden
|
vermutlich gar keine Weiche, z. B. Kreisel1 Bahn R bei HundM05). Grund
|
||||||
Einschleuselement einer anderen Strecke verkoppelt wird. Grund fuer den
|
fuer den zweiten Durchlauf (HundM05, Review 2026-09-18): an einem
|
||||||
zweiten Durchlauf (HundM05, Review 2026-09-18): an einem Kreisel mit 5
|
Kreisel mit 5 Abzweigen lagen die tatsaechlichen Schalt-Separatoren pro
|
||||||
Abzweigen lagen die tatsaechlichen Schalt-Separatoren pro Abzweig
|
Abzweig unterschiedlich weit vom Uebergabeelement entfernt (418-1139mm
|
||||||
unterschiedlich weit vom Uebergabeelement entfernt (418-1139mm in der
|
in der damaligen Registrierung) - eine einzelne mm-Schwelle (Regel 1)
|
||||||
aktuellen Registrierung) - eine einzelne mm-Schwelle (Regel 1) trifft
|
trifft nicht alle. SWITCH_FALLBACK_MM begrenzt das trotzdem: ohne
|
||||||
nicht alle. Durch die eindeutige Vergabe (jedes Element nur einmal)
|
Obergrenze hat Regel 2 an einer groesseren Anlage (Mubea) uebrig
|
||||||
bleibt ein Separator ohne eigenes Uebergabeelement (z. B. ein
|
gebliebene, tatsaechlich unbeteiligte Separatoren dem naechsten noch
|
||||||
gewoehnlicher Zwischenseparator auf der Bahn) trotzdem unbelegt, auch
|
freien Ausschleuselement zugeordnet, auch wenn das 5-9 Meter entfernt
|
||||||
ohne feste mm-Grenze.
|
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]] = {}
|
result: dict[str, tuple[Element, float]] = {}
|
||||||
|
|
||||||
for sep in separators:
|
for sep in separators:
|
||||||
@@ -325,28 +338,24 @@ def _find_switch_separators(
|
|||||||
if best is not None:
|
if best is not None:
|
||||||
result[sep.teile_id] = (best[1], best[0])
|
result[sep.teile_id] = (best[1], best[0])
|
||||||
|
|
||||||
# Rolle je Kreisel-Anschluss aus einem bereits bestaetigten Treffer lesen -
|
# Bahnen mit mindestens einem durch Regel 1 bestaetigten Treffer - nur
|
||||||
# keine Annahme, welche Bahn (-L/-R) Aus- bzw. Einschleusung uebernimmt.
|
# dort darf Regel 2 ueberhaupt suchen (siehe Docstring).
|
||||||
bahn_role: dict[str, str] = {}
|
switch_bahnen = {sep.assignment for sep in separators
|
||||||
for sep in separators:
|
if sep.teile_id in result and sep.assignment}
|
||||||
hit = result.get(sep.teile_id)
|
|
||||||
if hit and sep.assignment:
|
|
||||||
bahn_role.setdefault(sep.assignment, hit[0].kind)
|
|
||||||
|
|
||||||
used_transfers = {hit[0].teile_id for hit in result.values()}
|
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
|
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 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]] = []
|
candidates: list[tuple[float, Element, Element]] = []
|
||||||
for sep in remaining_seps:
|
for sep in remaining_seps:
|
||||||
role = bahn_role[sep.assignment or ""]
|
|
||||||
for transfer in transfers:
|
for transfer in transfers:
|
||||||
if (transfer.kind != role or transfer.teile_id in used_transfers
|
if transfer.teile_id in used_transfers or transfer.x is None or transfer.y is None:
|
||||||
or transfer.x is None or transfer.y is None):
|
|
||||||
continue
|
continue
|
||||||
d = math.hypot(transfer.x - sep.x, transfer.y - sep.y)
|
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])
|
candidates.sort(key=lambda c: c[0])
|
||||||
|
|
||||||
claimed_seps: set[str] = set()
|
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
|
(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
|
# werden VOR der normalen Host-Aufloesung herausgenommen: sie bilden gleich
|
||||||
# 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
|
||||||
@@ -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)
|
# Uebergabestelle. Das Element uebernimmt die Rolle der (hier fehlenden)
|
||||||
# Weiche; siehe _find_switch_separators. Host bewusst die Strecke (nicht
|
# Weiche; siehe _find_switch_separators. Host bewusst die Strecke (nicht
|
||||||
# die Kreisel-Bahn): beide Elementarten tragen "Strecke" oder "Foerderer"
|
# 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 "")
|
strecke_id = str(_merkmal(transfer.merkmale, "Strecke", "Förderer") or "")
|
||||||
ende = str(_merkmal(transfer.merkmale, "Streckenende") or "")
|
ende = str(_merkmal(transfer.merkmale, "Streckenende") or "")
|
||||||
own_scanners = sorted(sep_scanners.get(sep_id, []))
|
own_scanners = sorted(sep_scanners.get(sep_id, []))
|
||||||
art = "Ausschleuselement" if transfer.kind == "Ausschleus" else "Einschleuselement"
|
art = "Ausschleuselement"
|
||||||
tros.append(
|
tros.append(
|
||||||
Tro(
|
Tro(
|
||||||
tro_id="",
|
tro_id="",
|
||||||
@@ -1391,8 +1400,10 @@ def render_doc(
|
|||||||
add("| Gefaellestrecke, einzelne Linie | `1Sep` |")
|
add("| Gefaellestrecke, einzelne Linie | `1Sep` |")
|
||||||
add("| Strecke (angetrieben) | `Vario` (je Streckensegment) |")
|
add("| Strecke (angetrieben) | `Vario` (je Streckensegment) |")
|
||||||
add("| Kreisel-Bahn | `1Sep` |")
|
add("| Kreisel-Bahn | `1Sep` |")
|
||||||
add(f"| Separator <= {SWITCH_PROXIMITY_MM:.0f}mm von einem Aus-/Einschleuselement "
|
add(f"| Separator <= {SWITCH_PROXIMITY_MM:.0f}mm (oder naechstgelegen bis "
|
||||||
"| `1Sep1Swi` (Element uebernimmt die fehlende Weiche) |")
|
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("| zusaetzlich: 2 bzw. 3 abgehende Wege | Aufwertung zu `1Sep1Swi` / `1Sep2Swi` |")
|
||||||
add("")
|
add("")
|
||||||
add("Ein **Scanner** am Separator aendert den Typ nicht. In der Referenzanlage "
|
add("Ein **Scanner** am Separator aendert den Typ nicht. In der Referenzanlage "
|
||||||
|
|||||||
Reference in New Issue
Block a user