tro_flow: Weichen-Separator nach Laufrichtung, Weichen-Slots mit Bauteil
Steuernder Separator einer ILS Weiche (_find_weiche_separators) ist der letzte freie Separator in Laufrichtung VOR der Weiche, gemessen auf dem Kreisel-Umlauf aus Lage und Drehrichtung (kreisel_junctions.separators_before) - nicht mehr der CSV-Nachbar. Ein Nachbar hinter der Weiche steuert sie nie; Hoechstabstand weiche_vorlauf_max in cfg/parameter.ini [tro_flow]. Mubea (Export 2026-10-16): 2Sep2Swi = 0005 (Ausschleus 0061) + 0036 (Weiche 0130) statt 0005 + 0012; 0012 bleibt 1Sep, Kreisel1 wieder angebunden. Dematic, HundM05, showroom, test_system unveraendert. TRO-JSON: DerivedTro.switches (optional) nennt die Weichen-Bauteile in Slot-Reihenfolge (switch<i> gehoert bei 2Sep-Typen zu separator<i>); derive_slots traegt sie in die switch<n>-Slots, merge_hw_layout bindet das Ventil dort. e_planning.ini: Rollen ventil -> valve (Slot-Rolle der SCL), neue Regel Ausschleus_Weiche.valve fuer Ausschleusventile an Kreisel-Weichen. Mubea: MB0715, MB0724, MB0627, MB0671 statt TODO(E-Planung). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+84
-15
@@ -324,6 +324,89 @@ def _group(contacts: list[Contact]) -> list[list[Contact]]:
|
||||
return list(groups.values())
|
||||
|
||||
|
||||
def separator_positions(
|
||||
loops: dict[str, Loop], separators: list[Element]
|
||||
) -> tuple[dict[str, tuple[str, float]], dict[str, str]]:
|
||||
"""Separatoren auf ihren Umlauf legen - nach Lage, nicht nach TrackIds.
|
||||
|
||||
Parameter:
|
||||
loops: Kreisel-Id -> Loop (siehe find_contacts).
|
||||
separators: die zu legenden Separatoren.
|
||||
|
||||
Rueckgabe:
|
||||
(Separator -> (Kreisel, s), Separator -> "<Kreisel>-L/R"). Ein
|
||||
Separator liegt auf dem naechsten Umlauf, wenn er hoechstens
|
||||
separator_tolerance davon entfernt ist; sonst fehlt er.
|
||||
"""
|
||||
sep_pos: dict[str, tuple[str, float]] = {}
|
||||
sep_lane: dict[str, str] = {}
|
||||
for sep in separators:
|
||||
if sep.x is None or sep.y is None:
|
||||
continue
|
||||
best: tuple[float, str, float] | None = None
|
||||
for k, lp in loops.items():
|
||||
s, dist, _sec = lp.project(sep.x, sep.y)
|
||||
if dist <= SEPARATOR_TOLERANCE and (best is None or dist < best[0]):
|
||||
best = (dist, k, s)
|
||||
if best is not None:
|
||||
sep_pos[sep.teile_id] = (best[1], best[2])
|
||||
sep_lane[sep.teile_id] = f"{best[1]}-{loops[best[1]].lane(sep.x, sep.y)}"
|
||||
return sep_pos, sep_lane
|
||||
|
||||
|
||||
@dataclass
|
||||
class Upstream:
|
||||
"""Letzter Separator vor einem Punkt auf einem Umlauf."""
|
||||
|
||||
kreisel: str
|
||||
separator: str
|
||||
distance: float # Laufweg Separator -> Punkt in Drehrichtung (mm)
|
||||
|
||||
|
||||
def separators_before(
|
||||
elements: list[Element], x: float, y: float
|
||||
) -> tuple[list[Upstream], list[str]]:
|
||||
"""Je Kreisel, auf dem der Punkt (x, y) liegt, der letzte Separator davor.
|
||||
|
||||
"Davor" heisst: in Drehrichtung des Kreisels kommt zuerst dieser
|
||||
Separator, dann der Punkt - gemessen auf dem Umlauf aus Geometrie und
|
||||
Drehrichtung (kreisel_loop), nicht ueber die TrackIds-Reihenfolge. So
|
||||
bleibt das Ergebnis richtig, auch wenn ein Export die Kette anders
|
||||
sortiert oder einen Separator gar nicht in TrackIds fuehrt.
|
||||
|
||||
Parameter:
|
||||
elements: alle Elemente des CSV-Exports (Kreisel, Separatoren).
|
||||
x, y: der Punkt, z. B. eine ILS Weiche (mm).
|
||||
|
||||
Rueckgabe:
|
||||
(je Kreisel ein Upstream, sortiert nach Laufweg; Separatoren auf
|
||||
diesen Kreiseln, die auf dem Umlauf naeher HINTER dem Punkt liegen
|
||||
als davor - nur fuer Hinweise).
|
||||
Der Punkt liegt auf einem Kreisel, wenn er hoechstens
|
||||
weiche_tolerance von dessen Umlauf entfernt ist.
|
||||
"""
|
||||
loops = {e.teile_id: lp for e in elements if e.kind == "Kreisel"
|
||||
and (lp := kreisel_loop(e)) is not None}
|
||||
sep_pos, _lane = separator_positions(
|
||||
loops, [e for e in elements if e.kind == "Separator"])
|
||||
before: list[Upstream] = []
|
||||
after: list[str] = []
|
||||
for k, lp in loops.items():
|
||||
s_point, dist, _sec = lp.project(x, y)
|
||||
if dist > WEICHE_TOLERANCE:
|
||||
continue
|
||||
auf_k = [(sid, s) for sid, (kk, s) in sep_pos.items() if kk == k]
|
||||
if not auf_k:
|
||||
continue
|
||||
vor = min(auf_k, key=lambda e: (s_point - e[1]) % lp.length)
|
||||
d_vor = (s_point - vor[1]) % lp.length
|
||||
before.append(Upstream(k, vor[0], d_vor))
|
||||
after += [sid for sid, s in auf_k
|
||||
if (s - s_point) % lp.length < (s_point - s) % lp.length]
|
||||
before.sort(key=lambda u: u.distance)
|
||||
return before, sorted(after)
|
||||
|
||||
|
||||
def find_junctions(
|
||||
elements: list[Element],
|
||||
findings: list[str],
|
||||
@@ -344,21 +427,7 @@ def find_junctions(
|
||||
loops, contacts = find_contacts(elements, findings)
|
||||
if separators is None:
|
||||
separators = [e for e in elements if e.kind == "Separator"]
|
||||
|
||||
# Separator -> (Kreisel, s): auf dem naechstgelegenen Umlauf
|
||||
sep_pos: dict[str, tuple[str, float]] = {}
|
||||
sep_lane: dict[str, str] = {}
|
||||
for sep in separators:
|
||||
if sep.x is None or sep.y is None:
|
||||
continue
|
||||
best: tuple[float, str, float] | None = None
|
||||
for k, lp in loops.items():
|
||||
s, dist, _sec = lp.project(sep.x, sep.y)
|
||||
if dist <= SEPARATOR_TOLERANCE and (best is None or dist < best[0]):
|
||||
best = (dist, k, s)
|
||||
if best is not None:
|
||||
sep_pos[sep.teile_id] = (best[1], best[2])
|
||||
sep_lane[sep.teile_id] = f"{best[1]}-{loops[best[1]].lane(sep.x, sep.y)}"
|
||||
sep_pos, sep_lane = separator_positions(loops, separators)
|
||||
|
||||
junctions: list[Junction] = []
|
||||
for group in _group(contacts):
|
||||
|
||||
Reference in New Issue
Block a user