export_csv: AS/ES direkt aus ZZ_ASES_MARKER klassifizieren, Kreisel<->Strecke-Kollisionserzeugung entfernen
Stufe 2 (Python-Seite von "sepliste: Bauteil-IDs eintragen"): AS/ES-Kettenenden sind jetzt ganz normale Items, direkt aus dem dauerhaften ZZ_ASES_MARKER klassifiziert (TeileId/Position/Boundingbox vom Marker-Block selbst) - keine Kollisionsberechnung mehr, die sie erfindet. - lib/export_neighbors.py: compute_strecke_kreisel_schleus entfernt (suchte bisher per Kollisionstest den anschliessenden Kreisel und erzeugte dabei bei JEDEM Export eine neue TeileId fuer denselben physischen Punkt). AUSSCHLEUS_/ EINSCHLEUS_TEILEART sind jetzt Teil von KREISEL_UMLAUF_TEILEARTEN - ein AS/ES bringt seine eigene Bounding-Box vom Marker mit und wird dadurch, wie jeder freie Separator, ueber die normale BBox-Ueberschneidung einem Kreisel zugeordnet; die gesonderte "naechster Kreisel per sep_kette"-Suche in compute_kreisel_umlauf (dessen sep_kette-Parameter damit entfaellt) ist ueberfluessig geworden. compute_sep_kette gibt je Eintrag zusaetzlich das Feld "id" weiter (Bauteil-TeileId, siehe vorheriger Commit). - lib/export_csv.py: process_blocks klassifiziert ZZ_ASES_MARKER direkt (TYP- Attribut waehlt Ausschleus-/Einschleuselement); build_strecke_schleus_items entfaellt komplett. map_separator_kette_items -> map_sepliste_kette_items: ordnet jetzt AS/SEP/ES exakt per ID-Gleichheit zu (sepliste-Feld "id" gegen die TeileId der klassifizierten Items) statt per Koordinaten-Naehe - SEP faellt bei fehlendem "id"-Feld weiterhin auf den alten Naehe-Abgleich zurueck (Altbestand), AS/ES ohne "id" bleiben unaufgeloest. - Neu: pruefe_ases_in_beiden_containern prueft am Ende von process_blocks, ob jede AS-/ES-Zeile sowohl in den TrackIds ihrer eigenen Strecke als auch in denen mindestens eines Kreisels auftaucht, und traegt bei Abweichung eine Warnung ein (Spalte "Warnungen") - Konsistenzpruefung fuer genau die beiden "Container", zu denen ein AS/ES gehoert. - Aufgeraeumt (tote Routinen): _strecke_ends, _closest_pt_on_segment, _distinct_kreisel_ids (nur von den entfernten Funktionen genutzt), sowie der temporaere Diagnose-Filter DEBUG_HIGHLIGHT_TEILEIDS/dbg_highlight (an die jetzt behobene Koordinaten-Matching-Ambiguitaet gebunden, in seinem eigenen Kommentar bereits als Wegwerf-Code markiert). LISP-seitiger Dead-Code-Scan (export.lsp, ssg_ks_insert.lsp - die diese Woche bearbeiteten Module): keine Funde. Verifiziert an den echten HundM05-Daten (results/export_raw.json, 45 Bloecke): TeileId-Bereich luecken- und duplikatfrei 1-45 (vorher 1-55 mit Luecke 24-33 + Zusatz-IDs 46-55), alle 10 AS/ES-IDs korrekt in ihrer Strecke UND im Kreisel 06 TrackIds, keine Warnungen der neuen Pruefung. Zwei Testdateien auf die neue Architektur umgestellt (test_export_kreisel_umlauf.py: AS/ES als Item statt Kollisions-Erzeugung, neue Klasse TestAsEsImKreiselUmlauf fuer die BBox- Teilnahme; test_export_separatorliste.py: id-basierte Zuordnung, Klasse TestMarkerIdWiederverwendung durch TestMapSeplisteKetteItemsFallback ersetzt - die alte testete inzwischen entfernte Mint-Logik). Volle Suite: 224 bestanden (4 vorbestehende, unabhaengige Fixture-Failures unveraendert). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
719ffc2385
commit
ab3958e0e4
@@ -22,8 +22,7 @@ from export_neighbors import ( # noqa: E402
|
||||
compute_kreisel_umlauf,
|
||||
)
|
||||
from export_csv import ( # noqa: E402
|
||||
build_strecke_schleus_items,
|
||||
map_separator_kette_items,
|
||||
map_sepliste_kette_items,
|
||||
resolve_umlauf_und_sepliste_nachbarn,
|
||||
write_kreisel_separatorliste_merkmale,
|
||||
write_separatorliste_merkmale,
|
||||
@@ -53,6 +52,20 @@ def _separator(teileid, x, y):
|
||||
}
|
||||
|
||||
|
||||
def _ases(teileart, teileid, x, y, zuordnung):
|
||||
"""AS-/ES-Item, wie es process_blocks direkt aus einem ZZ_ASES_MARKER
|
||||
klassifiziert: eigene Bounding-Box vom Marker-Rechteck, "_zuordnung" =
|
||||
ZUORDNUNG-Attribut (die eigene Strecke), teilnahmefaehig am Kreisel-
|
||||
Umlauf ueber KREISEL_UMLAUF_TEILEARTEN wie ein freier Separator."""
|
||||
return {
|
||||
"teileart": teileart,
|
||||
"teileid": teileid,
|
||||
"_zuordnung": zuordnung,
|
||||
"merkmale": {},
|
||||
"_bbox": {"cx": x, "cy": y, "cz": 0.0, "dx": 200.0, "dy": 200.0},
|
||||
}
|
||||
|
||||
|
||||
def _btmt(teileart, teileid, x, y):
|
||||
"""BTMT-Beladung/-Entladung-Stationsblock ("ILS 2.0 BTMT Beladung"/
|
||||
"ILS 2.0 BTMT Entladung") - frei auf der Bahn platziert wie ein
|
||||
@@ -105,7 +118,7 @@ class TestKreiselKreiselWeicheImUmlauf:
|
||||
items = _zwei_beruehrende_kreisel()
|
||||
switches = compute_kreisel_touch_switches(items, tolerance_mm=0.0,
|
||||
durchmesser_mm=DURCHMESSER_MM)
|
||||
umlauf = compute_kreisel_umlauf(items, sep_kette={}, tolerance_mm=0.0,
|
||||
umlauf = compute_kreisel_umlauf(items, tolerance_mm=0.0,
|
||||
durchmesser_mm=DURCHMESSER_MM,
|
||||
touch_switches=switches)
|
||||
weiche_key = switches[0]["key"]
|
||||
@@ -130,7 +143,7 @@ class TestKreiselKreiselWeicheImUmlauf:
|
||||
assert len(switches) == 1
|
||||
weiche_key = switches[0]["key"]
|
||||
|
||||
umlauf = compute_kreisel_umlauf(items, sep_kette={}, tolerance_mm=50.0,
|
||||
umlauf = compute_kreisel_umlauf(items, tolerance_mm=50.0,
|
||||
durchmesser_mm=DURCHMESSER_MM,
|
||||
touch_switches=switches)
|
||||
|
||||
@@ -166,7 +179,7 @@ class TestKreiselBoundsRotationsUnabhaengig:
|
||||
k = self._langer_kreisel_90grad()
|
||||
sep = _separator("9001", 0.0, 10000.0) # liegt mitten auf der Achse
|
||||
items = [k, sep]
|
||||
umlauf = compute_kreisel_umlauf(items, sep_kette={}, tolerance_mm=200.0,
|
||||
umlauf = compute_kreisel_umlauf(items, tolerance_mm=200.0,
|
||||
durchmesser_mm=DURCHMESSER_MM)
|
||||
keys = [p[0] for p in umlauf["0002"]]
|
||||
assert "9001" in keys, "Separator auf der gedrehten Kreiselachse wurde nicht erkannt"
|
||||
@@ -179,7 +192,7 @@ class TestKreiselBoundsRotationsUnabhaengig:
|
||||
k = self._langer_kreisel_90grad()
|
||||
sep = _separator("0065", 958.0, 5100.0)
|
||||
items = [k, sep]
|
||||
umlauf = compute_kreisel_umlauf(items, sep_kette={}, tolerance_mm=200.0,
|
||||
umlauf = compute_kreisel_umlauf(items, tolerance_mm=200.0,
|
||||
durchmesser_mm=DURCHMESSER_MM)
|
||||
keys = [p[0] for p in umlauf["0002"]]
|
||||
assert "0065" not in keys, (
|
||||
@@ -191,26 +204,24 @@ class TestSeplisteNachbarnAufgeloest:
|
||||
"""Regressionstest fuer einen dritten realen Mubea-Befund: die meisten
|
||||
Separatoren blieben trotz sepliste-XDATA komplett ohne "Nachbarn".
|
||||
|
||||
Architektur (nach dem Umbau "Proxy liefert Zeilen, sepliste nur
|
||||
Reihenfolge"): die in einer Kette verpackten Separatoren liegen bereits als
|
||||
echte (Proxy-)Separator_SP-Items vor (der LISP-Export fuegt je verpacktem
|
||||
Symbol eine temporaere Kopie ein). map_separator_kette_items erzeugt daher
|
||||
KEINE neuen Zeilen mehr, sondern ordnet jedem sepliste-SEP das vorhandene
|
||||
Separator-Item zu (Match ueber Wrapper-ID + INSERT-Punkt) und traegt es in
|
||||
sep_kette_by_key ein; die AS/ES-Zeilen kommen weiter aus
|
||||
build_strecke_schleus_items. resolve_umlauf_und_sepliste_nachbarn setzt
|
||||
dann die Nachbarn als ECHTE TeileIds (nicht die rohen sep_kette-Schluessel,
|
||||
Architektur: SEP-Eintraege liegen bereits als echte (Proxy-)Separator_SP-
|
||||
Items vor (der LISP-Export fuegt je verpacktem Symbol eine temporaere
|
||||
Kopie ein), AS-/ES-Eintraege als eigene Items, direkt aus dem dauerhaften
|
||||
ZZ_ASES_MARKER klassifiziert (siehe export_csv.py process_blocks) - beide
|
||||
tragen bereits ihre TeileId als sepliste-Feld "id". map_sepliste_kette_
|
||||
items erzeugt daher KEINE neuen Zeilen, sondern ordnet jedem sepliste-
|
||||
Eintrag sein vorhandenes Item exakt per ID zu und traegt es in
|
||||
sep_kette_by_key ein. resolve_umlauf_und_sepliste_nachbarn setzt dann die
|
||||
Nachbarn als ECHTE TeileIds (nicht die rohen sep_kette-Schluessel,
|
||||
frueherer Bug)."""
|
||||
|
||||
def _sepliste_kette(self):
|
||||
"""Ein Kreisel + eine Gefaellestrecke mit sepliste AS(1) -> SEP(2) ->
|
||||
ES(3) PLUS das bereits vorhandene Proxy-Separator-Item fuer den SEP
|
||||
(wie es der freie-Separator-Pfad in process_blocks erzeugt: TeileId,
|
||||
Merkmal "Zuordnung" = Wrapper-ID, INSERT-Punkt _x/_y an der sepliste-
|
||||
SEP-Koordinate). "nachbarn" auf der Strecke simuliert das Ergebnis von
|
||||
compute_neighbor_ids (BBox-Ueberschneidung Strecke <-> Kreisel) - die
|
||||
Vorbedingung, unter der compute_strecke_kreisel_schleus die AS/ES-
|
||||
Schleuselemente erzeugt."""
|
||||
ES(3), PLUS die drei bereits vorhandenen Items, die diese Eintraege
|
||||
vertreten (AS/ES ueber den ZZ_ASES_MARKER, SEP ueber den freien-
|
||||
Separator-Pfad in process_blocks) - jeder sepliste-Eintrag traegt
|
||||
bereits die TeileId seines Items als Feld "id" (siehe
|
||||
csv:asmark-ids-setzen/csv:sepliste-ids-setzen, Lisp/export.lsp)."""
|
||||
k = _kreisel("0001", 0.0, 0.0, 0.0, abstand=1000.0)
|
||||
strecke = {
|
||||
"teileart": "ILS 2.0 Gefaellestrecke",
|
||||
@@ -220,13 +231,14 @@ class TestSeplisteNachbarnAufgeloest:
|
||||
"_bbox": {"cx": 5000.0, "cy": 0.0, "cz": 0.0, "dx": 8000.0, "dy": 300.0},
|
||||
"k1": "", "k2": "",
|
||||
"sepliste": [
|
||||
{"typ": "AS", "lfdnr": 1, "x": 1500.0, "y": 0.0},
|
||||
{"typ": "SEP", "lfdnr": 2, "x": 5000.0, "y": 0.0},
|
||||
{"typ": "ES", "lfdnr": 3, "x": 8500.0, "y": 0.0},
|
||||
{"typ": "AS", "lfdnr": 1, "x": 1500.0, "y": 0.0, "id": "0056"},
|
||||
{"typ": "SEP", "lfdnr": 2, "x": 5000.0, "y": 0.0, "id": "0055"},
|
||||
{"typ": "ES", "lfdnr": 3, "x": 8500.0, "y": 0.0, "id": "0057"},
|
||||
],
|
||||
}
|
||||
# Proxy-Separator-Item (bereits vorhanden), Zuordnung = Wrapper-ID 0010,
|
||||
# INSERT-Punkt an der sepliste-SEP-Koordinate (5000, 0).
|
||||
# SEP: Proxy-Separator-Item (INSERT-Punkt an der sepliste-SEP-
|
||||
# Koordinate - nur als Fallback-Feld gefuehrt, die Zuordnung selbst
|
||||
# laeuft exakt per ID).
|
||||
proxy_sep = {
|
||||
"teileart": "ILS 2.0 Separator",
|
||||
"teileid": "0055",
|
||||
@@ -235,7 +247,12 @@ class TestSeplisteNachbarnAufgeloest:
|
||||
"_bbox": {"cx": 5000.0, "cy": 0.0, "cz": 0.0, "dx": 150.0, "dy": 210.0},
|
||||
"nachbarn": "",
|
||||
}
|
||||
return [k, strecke, proxy_sep]
|
||||
# AS/ES: eigene Items wie aus dem ZZ_ASES_MARKER klassifiziert.
|
||||
as_item = _ases("ILS Ausschleuselement", "0056", 1500.0, 0.0, "0010")
|
||||
as_item["nachbarn"] = ""
|
||||
es_item = _ases("ILS Einschleuselement", "0057", 8500.0, 0.0, "0010")
|
||||
es_item["nachbarn"] = ""
|
||||
return [k, strecke, proxy_sep, as_item, es_item]
|
||||
|
||||
def test_keine_rohen_sep_kette_schluessel_in_nachbarn(self):
|
||||
from export_neighbors import compute_sep_kette
|
||||
@@ -246,20 +263,15 @@ class TestSeplisteNachbarnAufgeloest:
|
||||
touch_switches = []
|
||||
weiche_by_key = {}
|
||||
kreisel_umlauf = compute_kreisel_umlauf(
|
||||
items, sep_kette, tolerance_mm=50.0, durchmesser_mm=DURCHMESSER_MM,
|
||||
items, tolerance_mm=50.0, durchmesser_mm=DURCHMESSER_MM,
|
||||
touch_switches=touch_switches)
|
||||
|
||||
sep_kette_by_key = {}
|
||||
build_strecke_schleus_items(
|
||||
items, None, 100, sep_kette=sep_kette,
|
||||
sep_kette_by_key=sep_kette_by_key)
|
||||
map_separator_kette_items(
|
||||
items, sep_kette, sep_kette_by_key=sep_kette_by_key)
|
||||
sep_kette_by_key = map_sepliste_kette_items(items, sep_kette)
|
||||
|
||||
resolve_umlauf_und_sepliste_nachbarn(
|
||||
items, kreisel_umlauf, sep_kette, sep_kette_by_key, weiche_by_key)
|
||||
|
||||
# AS/ES (neu erzeugt) + der bereits vorhandene Proxy-Separator.
|
||||
# AS/ES + der bereits vorhandene Proxy-Separator.
|
||||
sepliste_items = [it for it in items
|
||||
if it.get("teileart") in
|
||||
("ILS 2.0 Separator", "ILS Ausschleuselement", "ILS Einschleuselement")]
|
||||
@@ -282,14 +294,9 @@ class TestSeplisteNachbarnAufgeloest:
|
||||
items = self._sepliste_kette()
|
||||
sep_kette = compute_sep_kette(items)
|
||||
kreisel_umlauf = compute_kreisel_umlauf(
|
||||
items, sep_kette, tolerance_mm=50.0, durchmesser_mm=DURCHMESSER_MM,
|
||||
items, tolerance_mm=50.0, durchmesser_mm=DURCHMESSER_MM,
|
||||
touch_switches=[])
|
||||
sep_kette_by_key = {}
|
||||
build_strecke_schleus_items(
|
||||
items, None, 100, sep_kette=sep_kette,
|
||||
sep_kette_by_key=sep_kette_by_key)
|
||||
map_separator_kette_items(
|
||||
items, sep_kette, sep_kette_by_key=sep_kette_by_key)
|
||||
sep_kette_by_key = map_sepliste_kette_items(items, sep_kette)
|
||||
resolve_umlauf_und_sepliste_nachbarn(
|
||||
items, kreisel_umlauf, sep_kette, sep_kette_by_key, {})
|
||||
|
||||
@@ -299,6 +306,8 @@ class TestSeplisteNachbarnAufgeloest:
|
||||
|
||||
# Der SEP-Schluessel muss auf das vorhandene Proxy-Separator-Item zeigen.
|
||||
assert sep_item["teileid"] == "0055", "sepliste-SEP nicht auf Proxy-Separator gemappt"
|
||||
assert as_item["teileid"] == "0056", "sepliste-AS nicht auf sein Item gemappt"
|
||||
assert es_item["teileid"] == "0057", "sepliste-ES nicht auf sein Item gemappt"
|
||||
|
||||
sep_nachbarn = {n.strip() for n in sep_item["nachbarn"].split(",") if n.strip()}
|
||||
assert sep_nachbarn == {as_item["teileid"], es_item["teileid"]}, (
|
||||
@@ -313,12 +322,7 @@ class TestSeplisteNachbarnAufgeloest:
|
||||
from export_neighbors import compute_sep_kette
|
||||
items = self._sepliste_kette()
|
||||
sep_kette = compute_sep_kette(items)
|
||||
sep_kette_by_key = {}
|
||||
build_strecke_schleus_items(
|
||||
items, None, 100, sep_kette=sep_kette,
|
||||
sep_kette_by_key=sep_kette_by_key)
|
||||
map_separator_kette_items(
|
||||
items, sep_kette, sep_kette_by_key=sep_kette_by_key)
|
||||
sep_kette_by_key = map_sepliste_kette_items(items, sep_kette)
|
||||
write_separatorliste_merkmale(items, sep_kette_by_key)
|
||||
|
||||
strecke = next(it for it in items if it.get("teileid") == "0010")
|
||||
@@ -363,7 +367,7 @@ class TestKreiselUmlaufStartetBeiNullGrad:
|
||||
# 170/80 Grad - der kleinste (und damit Listenanfang) ist S280 (80
|
||||
# Grad), NICHT S010 (350 Grad, liegt kurz VOR dem vollen Umlauf).
|
||||
items = self._kreisel_mit_vier_separatoren("UZS")
|
||||
umlauf = compute_kreisel_umlauf(items, sep_kette={}, tolerance_mm=0.0,
|
||||
umlauf = compute_kreisel_umlauf(items, tolerance_mm=0.0,
|
||||
durchmesser_mm=DURCHMESSER_MM)
|
||||
keys = [p[0] for p in umlauf["0001"]]
|
||||
assert keys[0] == "S280", f"Umlauf startet nicht bei 0 Grad (UZS): {keys}"
|
||||
@@ -374,7 +378,7 @@ class TestKreiselUmlaufStartetBeiNullGrad:
|
||||
# monoton mit 10/100/190/280 Grad - der Umlauf startet beim kleinsten
|
||||
# (S010) und laeuft aufsteigend weiter.
|
||||
items = self._kreisel_mit_vier_separatoren("GUZ")
|
||||
umlauf = compute_kreisel_umlauf(items, sep_kette={}, tolerance_mm=0.0,
|
||||
umlauf = compute_kreisel_umlauf(items, tolerance_mm=0.0,
|
||||
durchmesser_mm=DURCHMESSER_MM)
|
||||
keys = [p[0] for p in umlauf["0001"]]
|
||||
assert keys[0] == "S010", f"Umlauf startet nicht bei 0 Grad (GUZ): {keys}"
|
||||
@@ -396,7 +400,7 @@ class TestKreiselTrackidsSpalte:
|
||||
items = [k, sep_a, sep_b]
|
||||
|
||||
kreisel_umlauf = compute_kreisel_umlauf(
|
||||
items, sep_kette={}, tolerance_mm=200.0, durchmesser_mm=DURCHMESSER_MM)
|
||||
items, tolerance_mm=200.0, durchmesser_mm=DURCHMESSER_MM)
|
||||
write_kreisel_separatorliste_merkmale(
|
||||
items, kreisel_umlauf, sep_kette_by_key={}, weiche_by_key={})
|
||||
|
||||
@@ -408,7 +412,7 @@ class TestKreiselTrackidsSpalte:
|
||||
k["merkmale"] = {}
|
||||
items = [k]
|
||||
kreisel_umlauf = compute_kreisel_umlauf(
|
||||
items, sep_kette={}, tolerance_mm=0.0, durchmesser_mm=DURCHMESSER_MM)
|
||||
items, tolerance_mm=0.0, durchmesser_mm=DURCHMESSER_MM)
|
||||
write_kreisel_separatorliste_merkmale(
|
||||
items, kreisel_umlauf, sep_kette_by_key={}, weiche_by_key={})
|
||||
assert "trackids" not in items[0]
|
||||
@@ -425,7 +429,7 @@ class TestKreiselTrackidsSpalte:
|
||||
items = [k, sep, bel, ent]
|
||||
|
||||
kreisel_umlauf = compute_kreisel_umlauf(
|
||||
items, sep_kette={}, tolerance_mm=200.0, durchmesser_mm=DURCHMESSER_MM)
|
||||
items, tolerance_mm=200.0, durchmesser_mm=DURCHMESSER_MM)
|
||||
keys = [p[0] for p in kreisel_umlauf["0001"]]
|
||||
assert set(keys) == {"0020", "0030", "0031"}, (
|
||||
f"BTMT Beladung/Entladung fehlen im Kreisel-Umlauf: {keys}")
|
||||
@@ -447,7 +451,7 @@ class TestKreiselTrackidsSpalte:
|
||||
items = [k, sep, ein, aus]
|
||||
|
||||
kreisel_umlauf = compute_kreisel_umlauf(
|
||||
items, sep_kette={}, tolerance_mm=200.0, durchmesser_mm=DURCHMESSER_MM)
|
||||
items, tolerance_mm=200.0, durchmesser_mm=DURCHMESSER_MM)
|
||||
keys = [p[0] for p in kreisel_umlauf["0001"]]
|
||||
assert set(keys) == {"0020", "0040", "0041"}, (
|
||||
f"Eingang/Ausgang fehlen im Kreisel-Umlauf: {keys}")
|
||||
@@ -455,3 +459,29 @@ class TestKreiselTrackidsSpalte:
|
||||
write_kreisel_separatorliste_merkmale(
|
||||
items, kreisel_umlauf, sep_kette_by_key={}, weiche_by_key={})
|
||||
assert k["trackids"] == "0041, 0020, 0040"
|
||||
|
||||
|
||||
class TestAsEsImKreiselUmlauf:
|
||||
"""Neu (Marker-Architektur): AS/ES sind jetzt ein ganz normaler
|
||||
KREISEL_UMLAUF_TEILEARTEN-Eintrag - ihre eigene Bounding-Box (vom
|
||||
ZZ_ASES_MARKER) entscheidet, ob sie im Umlauf eines Kreisels auftauchen,
|
||||
genau wie ein freier Separator. Keine gesonderte Naeherungslogik mehr."""
|
||||
|
||||
def test_as_in_reichweite_taucht_im_umlauf_auf(self):
|
||||
k = _kreisel("0001", 0.0, 0.0, 0.0, abstand=1000.0)
|
||||
as_item = _ases("ILS Ausschleuselement", "0056", 300.0, 300.0, "0010")
|
||||
items = [k, as_item]
|
||||
umlauf = compute_kreisel_umlauf(
|
||||
items, tolerance_mm=200.0, durchmesser_mm=DURCHMESSER_MM)
|
||||
keys = [p[0] for p in umlauf["0001"]]
|
||||
assert "0056" in keys, "AS-Item mit eigener BBox wurde nicht im Umlauf erkannt"
|
||||
|
||||
def test_es_ausserhalb_der_reichweite_fehlt_im_umlauf(self):
|
||||
k = _kreisel("0001", 0.0, 0.0, 0.0, abstand=1000.0)
|
||||
es_item = _ases("ILS Einschleuselement", "0057", 20000.0, 0.0, "0010")
|
||||
items = [k, es_item]
|
||||
umlauf = compute_kreisel_umlauf(
|
||||
items, tolerance_mm=200.0, durchmesser_mm=DURCHMESSER_MM)
|
||||
keys = [p[0] for p in umlauf["0001"]]
|
||||
assert "0057" not in keys, (
|
||||
"ES-Item weit ausserhalb der Kreiselbox wurde faelschlich erkannt")
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
test_export_separatorliste.py - Merkmal "Separatorlist" einer Strecke.
|
||||
test_export_separatorliste.py - "TrackIds"-Spalte einer Strecke (VF_n/GF_n).
|
||||
Laeuft komplett ohne BricsCAD (reine Python-Logik, siehe lib/export_csv.py).
|
||||
|
||||
Eine Strecke (VF_n/GF_n) traegt in ihrer "sepliste"-XDATA nur x/y je AS-/SEP-/
|
||||
ES-Eintrag (Rohkoordinaten aus der Zeichnung, kein TeileId). write_separator-
|
||||
liste_merkmale() soll daraus, ueber die bereits von map_separator_kette_items
|
||||
per x/y-Abgleich aufgeloesten sep_kette_by_key-Eintraege, das Merkmal
|
||||
"Separatorlist" der Strecke fuellen: die TeileIds ihrer SEP-Eintraege (NICHT
|
||||
AS/ES) in Baureihenfolge, kommagetrennt (z.B. "0052, 0053, 0054").
|
||||
Eine Strecke (VF_n/GF_n) traegt in ihrer "sepliste"-XDATA je AS-/SEP-/ES-
|
||||
Eintrag x/y sowie (seit csv:asmark-ids-setzen/csv:sepliste-ids-setzen,
|
||||
Lisp/export.lsp) ein Feld "id" - die TeileId des Bauteils, das diesen
|
||||
Eintrag in der Zeichnung vertritt (AS/ES ueber den dauerhaften
|
||||
ZZ_ASES_MARKER, SEP ueber die beim Export ausgepackte temporaere
|
||||
Separator_SP-Kopie). write_separatorliste_merkmale() soll daraus, ueber die
|
||||
bereits von map_sepliste_kette_items per exaktem ID-Abgleich aufgeloesten
|
||||
sep_kette_by_key-Eintraege, die Spalte "TrackIds" der Strecke fuellen: die
|
||||
TeileIds von AS-Ende, SEP-Eintraegen und ES-Ende in Baureihenfolge,
|
||||
kommagetrennt (z.B. "0056, 0052, 0053, 0057").
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -17,11 +21,9 @@ import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "lib"))
|
||||
|
||||
from export_neighbors import compute_sep_kette, compute_kreisel_umlauf # noqa: E402
|
||||
from export_neighbors import compute_sep_kette # noqa: E402
|
||||
from export_csv import ( # noqa: E402
|
||||
build_strecke_schleus_items,
|
||||
map_separator_kette_items,
|
||||
max_numeric_teileid,
|
||||
map_sepliste_kette_items,
|
||||
write_separatorliste_merkmale,
|
||||
)
|
||||
|
||||
@@ -40,13 +42,27 @@ def _kreisel(teileid, x0, y0, rotation, abstand=1000.0):
|
||||
}
|
||||
|
||||
|
||||
def _ases(teileart, teileid, x, y, zuordnung):
|
||||
"""AS-/ES-Item, wie es process_blocks direkt aus einem ZZ_ASES_MARKER
|
||||
klassifiziert - siehe test_export_kreisel_umlauf.py fuer dasselbe
|
||||
Fixture-Muster."""
|
||||
return {
|
||||
"teileart": teileart,
|
||||
"teileid": teileid,
|
||||
"_zuordnung": zuordnung,
|
||||
"merkmale": {},
|
||||
"nachbarn": "",
|
||||
"_bbox": {"cx": x, "cy": y, "cz": 0.0, "dx": 200.0, "dy": 200.0},
|
||||
}
|
||||
|
||||
|
||||
def _strecke_mit_zwei_separatoren():
|
||||
"""Ein Kreisel + eine Gefaellestrecke mit sepliste AS(1) -> SEP(2) ->
|
||||
SEP(3) -> ES(4), PLUS die zwei bereits vorhandenen Proxy-Separator-Items
|
||||
(wie sie der freie-Separator-Pfad in process_blocks erzeugt: TeileId,
|
||||
Merkmal "Zuordnung" = Wrapper-ID, INSERT-Punkt _x/_y an der jeweiligen
|
||||
sepliste-SEP-Koordinate) - siehe TestSeplisteNachbarnAufgeloest in
|
||||
test_export_kreisel_umlauf.py fuer dasselbe Fixture-Muster."""
|
||||
SEP(3) -> ES(4), PLUS die vier bereits vorhandenen Items, die diese
|
||||
Eintraege vertreten (zwei Proxy-Separator-Items wie sie der freie-
|
||||
Separator-Pfad in process_blocks erzeugt, sowie das AS-/ES-Item wie aus
|
||||
dem ZZ_ASES_MARKER klassifiziert) - jeder sepliste-Eintrag traegt bereits
|
||||
die TeileId seines Items als Feld "id"."""
|
||||
k = _kreisel("0001", 0.0, 0.0, 0.0, abstand=1000.0)
|
||||
strecke = {
|
||||
"teileart": "ILS 2.0 Gefaellestrecke",
|
||||
@@ -57,10 +73,10 @@ def _strecke_mit_zwei_separatoren():
|
||||
"k1": "", "k2": "",
|
||||
"merkmale": {"Anzahl_Separator": "2", "Anzahl_Scanner": "0"},
|
||||
"sepliste": [
|
||||
{"typ": "AS", "lfdnr": 1, "x": 1500.0, "y": 0.0},
|
||||
{"typ": "SEP", "lfdnr": 2, "x": 4000.0, "y": 0.0},
|
||||
{"typ": "SEP", "lfdnr": 3, "x": 6000.0, "y": 0.0},
|
||||
{"typ": "ES", "lfdnr": 4, "x": 8500.0, "y": 0.0},
|
||||
{"typ": "AS", "lfdnr": 1, "x": 1500.0, "y": 0.0, "id": "0056"},
|
||||
{"typ": "SEP", "lfdnr": 2, "x": 4000.0, "y": 0.0, "id": "0052"},
|
||||
{"typ": "SEP", "lfdnr": 3, "x": 6000.0, "y": 0.0, "id": "0053"},
|
||||
{"typ": "ES", "lfdnr": 4, "x": 8500.0, "y": 0.0, "id": "0057"},
|
||||
],
|
||||
}
|
||||
proxy_sep_1 = {
|
||||
@@ -79,7 +95,9 @@ def _strecke_mit_zwei_separatoren():
|
||||
"_bbox": {"cx": 6000.0, "cy": 0.0, "cz": 0.0, "dx": 150.0, "dy": 210.0},
|
||||
"nachbarn": "",
|
||||
}
|
||||
return [k, strecke, proxy_sep_1, proxy_sep_2]
|
||||
as_item = _ases("ILS Ausschleuselement", "0056", 1500.0, 0.0, "0010")
|
||||
es_item = _ases("ILS Einschleuselement", "0057", 8500.0, 0.0, "0010")
|
||||
return [k, strecke, proxy_sep_1, proxy_sep_2, as_item, es_item]
|
||||
|
||||
|
||||
class TestSeparatorlisteMerkmal:
|
||||
@@ -87,53 +105,36 @@ class TestSeparatorlisteMerkmal:
|
||||
def test_separatorlist_in_baureihenfolge(self):
|
||||
items = _strecke_mit_zwei_separatoren()
|
||||
sep_kette = compute_sep_kette(items)
|
||||
compute_kreisel_umlauf( # AS/ES muessen als Punkte existieren, sonst
|
||||
items, sep_kette, tolerance_mm=50.0, # unveraendert fuer diesen Test
|
||||
durchmesser_mm=DURCHMESSER_MM, touch_switches=[])
|
||||
|
||||
sep_kette_by_key = {}
|
||||
build_strecke_schleus_items(
|
||||
items, None, 100, sep_kette=sep_kette,
|
||||
sep_kette_by_key=sep_kette_by_key)
|
||||
map_separator_kette_items(
|
||||
items, sep_kette, sep_kette_by_key=sep_kette_by_key)
|
||||
sep_kette_by_key = map_sepliste_kette_items(items, sep_kette)
|
||||
|
||||
write_separatorliste_merkmale(items, sep_kette_by_key)
|
||||
|
||||
# write_separatorliste_merkmale fuellt seit dem Umbau das Top-Level-Feld
|
||||
# write_separatorliste_merkmale fuellt das Top-Level-Feld
|
||||
# item["trackids"] (CSV-Spalte "TrackIds") mit AS-Ende + Separatoren +
|
||||
# ES-Ende in Baureihenfolge - NICHT mehr das Merkmal "Separatorlist" mit
|
||||
# nur den SEP-Eintraegen. AS/ES bekommen ueber build_strecke_schleus_items
|
||||
# eigene TeileIds (hier 0054/0055), die SEP-Proxies sind 0052/0053.
|
||||
# ES-Ende in Baureihenfolge. AS/ES tragen ihre eigene TeileId (0056/
|
||||
# 0057), die SEP-Proxies 0052/0053 - alle exakt per sepliste-Feld
|
||||
# "id" aufgeloest, kein Minten mehr noetig.
|
||||
strecke = next(it for it in items if it.get("teileid") == "0010")
|
||||
assert strecke.get("trackids") == "0054, 0052, 0053, 0055", (
|
||||
assert strecke.get("trackids") == "0056, 0052, 0053, 0057", (
|
||||
f'trackids = {strecke.get("trackids")!r}, '
|
||||
f'erwartet "0054, 0052, 0053, 0055" (AS, SEP, SEP, ES in Baureihenfolge)')
|
||||
f'erwartet "0056, 0052, 0053, 0057" (AS, SEP, SEP, ES in Baureihenfolge)')
|
||||
|
||||
def test_vertauschte_proxy_reihenfolge_in_items_aendert_nichts(self):
|
||||
"""Die Reihenfolge in "items" (bzw. in welcher Reihenfolge die Proxy-
|
||||
Separatoren im Export auftauchen) darf die Separatorlist-Reihenfolge
|
||||
nicht beeinflussen - massgeblich ist allein die lfdnr aus der
|
||||
sepliste, nicht die Item-Liste."""
|
||||
Separatoren im Export auftauchen) darf die TrackIds-Reihenfolge nicht
|
||||
beeinflussen - massgeblich ist allein die lfdnr aus der sepliste,
|
||||
nicht die Item-Liste."""
|
||||
items = _strecke_mit_zwei_separatoren()
|
||||
k, strecke, proxy_1, proxy_2 = items
|
||||
items = [k, strecke, proxy_2, proxy_1] # vertauscht
|
||||
k, strecke, proxy_1, proxy_2, as_item, es_item = items
|
||||
items = [k, strecke, proxy_2, proxy_1, es_item, as_item] # vertauscht
|
||||
|
||||
sep_kette = compute_sep_kette(items)
|
||||
compute_kreisel_umlauf(
|
||||
items, sep_kette, tolerance_mm=50.0,
|
||||
durchmesser_mm=DURCHMESSER_MM, touch_switches=[])
|
||||
sep_kette_by_key = {}
|
||||
build_strecke_schleus_items(
|
||||
items, None, 100, sep_kette=sep_kette,
|
||||
sep_kette_by_key=sep_kette_by_key)
|
||||
map_separator_kette_items(
|
||||
items, sep_kette, sep_kette_by_key=sep_kette_by_key)
|
||||
sep_kette_by_key = map_sepliste_kette_items(items, sep_kette)
|
||||
|
||||
write_separatorliste_merkmale(items, sep_kette_by_key)
|
||||
|
||||
strecke = next(it for it in items if it.get("teileid") == "0010")
|
||||
assert strecke.get("trackids") == "0054, 0052, 0053, 0055"
|
||||
assert strecke.get("trackids") == "0056, 0052, 0053, 0057"
|
||||
|
||||
def test_strecke_ohne_sepliste_bleibt_unveraendert(self):
|
||||
"""Altbestand ohne "sepliste"-XDATA darf kein leeres/kaputtes
|
||||
@@ -148,57 +149,32 @@ class TestSeparatorlisteMerkmal:
|
||||
assert "Separatorlist" not in strecke["merkmale"]
|
||||
|
||||
|
||||
class TestMarkerIdWiederverwendung:
|
||||
"""build_strecke_schleus_items darf die ID eines dauerhaften
|
||||
ZZ_ASES_MARKER (siehe csv:asmark-aktualisieren, Lisp/export.lsp; im
|
||||
sepliste-Eintrag als Feld "id" gespiegelt) nicht ignorieren und
|
||||
stattdessen eine neue erfinden - genau das erzeugte die vom Nutzer
|
||||
gemeldete TeileId-Luecke (Marker-ID blieb ungenutzt, gleichzeitig
|
||||
entstand fuer denselben Punkt eine zusaetzliche, hoehere ID)."""
|
||||
class TestMapSeplisteKetteItemsFallback:
|
||||
"""map_sepliste_kette_items faellt fuer SEP-Eintraege OHNE "id"-Feld
|
||||
(Altbestand-Export vor der Marker-Funktion) weiterhin auf den
|
||||
Naehe-Abgleich zurueck - AS/ES ohne "id" bleiben dagegen unaufgeloest
|
||||
(fuer sie existiert seit dem Wegfall der Kreisel<->Strecke-Kollisions-
|
||||
erzeugung kein Item mehr, das sie vertreten koennte)."""
|
||||
|
||||
def test_marker_id_wiederverwendet_statt_neu_gemintet(self):
|
||||
def test_sep_ohne_id_faellt_auf_naehe_zurueck(self):
|
||||
items = _strecke_mit_zwei_separatoren()
|
||||
strecke = next(it for it in items if it.get("teileid") == "0010")
|
||||
# AS = sepliste[0] (lfdnr=1), ES = sepliste[-1] (lfdnr=4) - siehe
|
||||
# ssg-sepliste-sammeln (Lisp/ssg_ks_insert.lsp): AS immer zuerst,
|
||||
# ES immer zuletzt. Bewusst NIEDRIGER als die Proxy-Separator-IDs
|
||||
# 0052/0053 gewaehlt - genau wie im real beobachteten HundM05-Export
|
||||
# (Marker 0024-0033, Separator-Proxies 0034-0045).
|
||||
strecke["sepliste"][0]["id"] = "0024"
|
||||
strecke["sepliste"][-1]["id"] = "0033"
|
||||
for entry in strecke["sepliste"]:
|
||||
if entry["typ"] == "SEP":
|
||||
entry["id"] = ""
|
||||
|
||||
sep_kette = compute_sep_kette(items)
|
||||
sep_kette_by_key = {}
|
||||
build_strecke_schleus_items(
|
||||
items, None, 100, sep_kette=sep_kette,
|
||||
sep_kette_by_key=sep_kette_by_key)
|
||||
sep_kette_by_key = map_sepliste_kette_items(items, sep_kette)
|
||||
|
||||
as_item = sep_kette_by_key["0010#1"]
|
||||
es_item = sep_kette_by_key["0010#4"]
|
||||
assert as_item["teileid"] == "0024", (
|
||||
f'AS-Zeile hat teileid={as_item["teileid"]!r}, erwartet die '
|
||||
f'wiederverwendete Marker-ID "0024"')
|
||||
assert es_item["teileid"] == "0033", (
|
||||
f'ES-Zeile hat teileid={es_item["teileid"]!r}, erwartet die '
|
||||
f'wiederverwendete Marker-ID "0033"')
|
||||
assert sep_kette_by_key["0010#2"]["teileid"] == "0052"
|
||||
assert sep_kette_by_key["0010#3"]["teileid"] == "0053"
|
||||
|
||||
# Kein spaeterer Mint in DIESEM Lauf darf eine wiederverwendete
|
||||
# Marker-ID erneut vergeben - der laufende Zaehler muss mindestens
|
||||
# auf die hoechste wiederverwendete ID hochgezogen worden sein.
|
||||
assert max_numeric_teileid(items) >= 33
|
||||
|
||||
def test_strecke_ohne_marker_id_mintet_wie_bisher(self):
|
||||
"""Fehlt das "id"-Feld (Altbestand-Wrapper vor der Marker-Funktion,
|
||||
oder Marker-Zuweisung in Lisp fehlgeschlagen), bleibt das Mint-
|
||||
Verhalten unveraendert - dieselben TeileIds wie vor diesem Fix."""
|
||||
def test_as_ohne_id_bleibt_unaufgeloest(self):
|
||||
items = _strecke_mit_zwei_separatoren()
|
||||
sep_kette = compute_sep_kette(items)
|
||||
sep_kette_by_key = {}
|
||||
build_strecke_schleus_items(
|
||||
items, None, 100, sep_kette=sep_kette,
|
||||
sep_kette_by_key=sep_kette_by_key)
|
||||
strecke = next(it for it in items if it.get("teileid") == "0010")
|
||||
strecke["sepliste"][0]["id"] = ""
|
||||
|
||||
as_item = sep_kette_by_key["0010#1"]
|
||||
es_item = sep_kette_by_key["0010#4"]
|
||||
assert as_item["teileid"] == "0054"
|
||||
assert es_item["teileid"] == "0055"
|
||||
sep_kette = compute_sep_kette(items)
|
||||
sep_kette_by_key = map_sepliste_kette_items(items, sep_kette)
|
||||
|
||||
assert "0010#1" not in sep_kette_by_key
|
||||
|
||||
Reference in New Issue
Block a user