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:
co-authored by
Claude Sonnet 5
parent
29d466f696
commit
c7886477ff
+39
-34
@@ -275,7 +275,15 @@ def _find_switch_separators(
|
|||||||
) -> dict[str, tuple[Element, float]]:
|
) -> dict[str, tuple[Element, float]]:
|
||||||
"""Separator-Id -> (Aus-/Einschleuselement der Weiche, Abstand mm).
|
"""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
|
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
|
||||||
@@ -286,21 +294,20 @@ def _find_switch_separators(
|
|||||||
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. Separator mit eigenem Scanner (Scanner-Merkmal "Naechster Separator"),
|
2. Naechstgelegener freier Separator je noch unbelegtem Aus-/
|
||||||
auf noch unbelegten Uebergabestellen der SELBEN Rolle (Aus- oder
|
Einschleuselement, jedes Paar nur einmal vergeben (kleinste Distanz
|
||||||
Einschleusung) und dem SELBEN Kreisel-Anschluss wie ein bereits durch
|
zuerst), auf derselben Bahn und mit derselben Rolle (Aus- oder
|
||||||
Regel 1 bestaetigter Treffer auf dieser Bahn - keine feste mm-Grenze,
|
Einschleusung) wie ein bereits durch Regel 1 bestaetigter Treffer -
|
||||||
stattdessen die naechstgelegene freie Uebergabestelle dieser Rolle.
|
verhindert, dass ein Separator versehentlich mit dem naeher liegenden
|
||||||
Grund (HundM05, Review 2026-09-18): an einem Kreisel mit 5 Abzweigen
|
Einschleuselement einer anderen Strecke verkoppelt wird. Grund fuer den
|
||||||
liegen die tatsaechlichen Schalt-Separatoren pro Abzweig unterschiedlich
|
zweiten Durchlauf (HundM05, Review 2026-09-18): an einem Kreisel mit 5
|
||||||
weit vom Uebergabeelement entfernt (418-1139mm in der aktuellen
|
Abzweigen lagen die tatsaechlichen Schalt-Separatoren pro Abzweig
|
||||||
Registrierung) - eine einzelne mm-Schwelle (Regel 1) trifft nicht alle.
|
unterschiedlich weit vom Uebergabeelement entfernt (418-1139mm in der
|
||||||
Ein Scanner direkt am Separator ist hier aber der CAD-belegte
|
aktuellen Registrierung) - eine einzelne mm-Schwelle (Regel 1) trifft
|
||||||
Lesepunkt vor der Weichenentscheidung (Barcode -> Richtung), also ein
|
nicht alle. Durch die eindeutige Vergabe (jedes Element nur einmal)
|
||||||
verlaesslicherer Marker als der reine Abstand. Nach Rolle (Aus- vs.
|
bleibt ein Separator ohne eigenes Uebergabeelement (z. B. ein
|
||||||
Einschleusung) gefiltert, damit z. B. ein Separator auf der
|
gewoehnlicher Zwischenseparator auf der Bahn) trotzdem unbelegt, auch
|
||||||
Ausschleus-Bahn nicht versehentlich mit dem naeher liegenden
|
ohne feste mm-Grenze.
|
||||||
Einschleuselement einer anderen Strecke verkoppelt wird.
|
|
||||||
"""
|
"""
|
||||||
transfers = [e for e in elements if e.kind in ("Ausschleus", "Einschleus")]
|
transfers = [e for e in elements if e.kind in ("Ausschleus", "Einschleus")]
|
||||||
result: dict[str, tuple[Element, float]] = {}
|
result: dict[str, tuple[Element, float]] = {}
|
||||||
@@ -326,31 +333,29 @@ def _find_switch_separators(
|
|||||||
if hit and sep.assignment:
|
if hit and sep.assignment:
|
||||||
bahn_role.setdefault(sep.assignment, hit[0].kind)
|
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()}
|
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:
|
candidates: list[tuple[float, Element, Element]] = []
|
||||||
if sep.teile_id in result or sep.teile_id not in scanner_targets:
|
for sep in remaining_seps:
|
||||||
continue
|
role = bahn_role[sep.assignment or ""]
|
||||||
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
|
|
||||||
for transfer in transfers:
|
for transfer in transfers:
|
||||||
if (transfer.kind != role or transfer.teile_id in used_transfers
|
if (transfer.kind != role or 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)
|
||||||
if best is None or d < best[0]:
|
candidates.append((d, sep, transfer))
|
||||||
best = (d, transfer)
|
candidates.sort(key=lambda c: c[0])
|
||||||
if best is not None:
|
|
||||||
result[sep.teile_id] = (best[1], best[0])
|
claimed_seps: set[str] = set()
|
||||||
used_transfers.add(best[1].teile_id)
|
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
|
return result
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user