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]]:
|
||||
"""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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user