Weichenerkennung: Kriterium ist das Uebergabeelement, nicht der Scanner

Korrektur zu 72d0a21: Regel 2 in _find_switch_separators() pruefte bisher,
ob ein Separator einen eigenen Scanner traegt, bevor sie ihn (ohne feste
mm-Grenze) einem Aus-/Einschleuselement zuordnete. Der Scanner ist aber nur
ein zusaetzliches Bauteil des TROs, kein Kennzeichen einer Weiche - viele
Separatoren tragen einen Scanner, ohne eine Weiche zu sein (siehe Docstring
und Hinweis in tro_flow.py selbst: "Ein Scanner am Separator aendert den Typ
NICHT").

Regel 2 ist jetzt reine naechster-freier-Nachbar-Zuordnung: jedes noch
unbelegte Aus-/Einschleuselement bekommt den naechstgelegenen freien
Separator derselben Rolle/Bahn, kleinste Distanz zuerst, jedes Paar nur
einmal - ohne Scanner-Kriterium und ohne feste mm-Grenze. Ein gewoehnlicher
Zwischenseparator ohne eigenes Uebergabeelement bleibt dadurch weiterhin
unbelegt, weil ihm kein Element mehr uebrig bleibt.

Ergebnis fuer HundM05.csv unveraendert (13 TROs, 6x 1Sep1Swi, keine Fehler) -
mit der zwischenzeitlich aktualisierten CSV liegen die echten Weichen jetzt
ohnehin wieder innerhalb der 500mm-Grenze aus Regel 1; Regel 2 bleibt als
Absicherung gegen erneute Kalibrierungs-Drift bestehen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Samer Ayadi
2026-09-18 16:19:50 +02:00
co-authored by Claude Sonnet 5
parent 29d466f696
commit c7886477ff
+39 -34
View File
@@ -275,7 +275,15 @@ def _find_switch_separators(
) -> dict[str, tuple[Element, float]]:
"""Separator-Id -> (Aus-/Einschleuselement der Weiche, Abstand mm).
Zwei Regeln, angewandt in dieser Reihenfolge:
Ein Separator ist eine Weiche, wenn ihm ein ILS Aus- oder Einschleuselement
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.
Zwei Durchlaeufe, um trotzdem jedem Aus-/Einschleuselement genau einen
Separator zuzuordnen:
1. Geometrische Naehe (Distanz <= tol_mm). HundM05 hat an seinen
Kreisel<->Strecke-Uebergaengen kein eigenes "ILS Weiche"-Bauteil
@@ -286,21 +294,20 @@ def _find_switch_separators(
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. Separator mit eigenem Scanner (Scanner-Merkmal "Naechster Separator"),
auf noch unbelegten Uebergabestellen der SELBEN Rolle (Aus- oder
Einschleusung) und dem SELBEN Kreisel-Anschluss wie ein bereits durch
Regel 1 bestaetigter Treffer auf dieser Bahn - keine feste mm-Grenze,
stattdessen die naechstgelegene freie Uebergabestelle dieser Rolle.
Grund (HundM05, Review 2026-09-18): an einem Kreisel mit 5 Abzweigen
liegen 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.
Ein Scanner direkt am Separator ist hier aber der CAD-belegte
Lesepunkt vor der Weichenentscheidung (Barcode -> Richtung), also ein
verlaesslicherer Marker als der reine Abstand. Nach Rolle (Aus- vs.
Einschleusung) gefiltert, damit z. B. ein Separator auf der
Ausschleus-Bahn nicht versehentlich mit dem naeher liegenden
Einschleuselement einer anderen Strecke verkoppelt wird.
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.
"""
transfers = [e for e in elements if e.kind in ("Ausschleus", "Einschleus")]
result: dict[str, tuple[Element, float]] = {}
@@ -326,31 +333,29 @@ def _find_switch_separators(
if hit and sep.assignment:
bahn_role.setdefault(sep.assignment, hit[0].kind)
scanner_targets = {
str(_merkmal(e.merkmale, "Naechster Separator") or "").strip()
for e in elements if e.kind == "Scanner"
}
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]
for sep in separators:
if sep.teile_id in result or sep.teile_id not in scanner_targets:
continue
if sep.x is None or sep.y is None:
continue
role = bahn_role.get(sep.assignment or "")
if role is None:
continue
best: tuple[float, Element] | None = None
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):
continue
d = math.hypot(transfer.x - sep.x, transfer.y - sep.y)
if best is None or d < best[0]:
best = (d, transfer)
if best is not None:
result[sep.teile_id] = (best[1], best[0])
used_transfers.add(best[1].teile_id)
candidates.append((d, sep, transfer))
candidates.sort(key=lambda c: c[0])
claimed_seps: set[str] = set()
for d, sep, transfer in candidates:
if sep.teile_id in claimed_seps or transfer.teile_id in used_transfers:
continue
result[sep.teile_id] = (transfer, d)
claimed_seps.add(sep.teile_id)
used_transfers.add(transfer.teile_id)
return result