Sepliste-XDATA fuer Separator/AS/ES-Reihenfolge + Unit-Tests, makunbound-Fix
Sammelt beim Bau von VF_n/GF_n-Ketten die Baureihenfolge der Separator-/ AS-/ES-Sub-Bloecke vor dem Block-Sweep und schreibt sie als XDATA auf den fertigen Wrapper (ssg_ks_insert.lsp); export.lsp liest sie fuer das neue "sepliste"-Feld im CSV-Export, export_neighbors.py nutzt es als Fallback zur BBox-Kollisionspruefung. Dazu Unit-Tests fuer die reinen Serialisierungs- funktionen (Chunking, Roundtrip) in test_unit.lsp. Ausserdem: makunbound (existiert nicht in BricsCAD-AutoLISP) aus test_run_all.lsp entfernt - verursachte Laufzeitfehler bei SSG_RUN_ALL_TESTS_EXPORT/_OFFEN. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+139
-17
@@ -122,6 +122,8 @@ from export_neighbors import (
|
||||
compute_scanner_nearest_separator,
|
||||
compute_kreisel_touch_switches,
|
||||
compute_strecke_kreisel_schleus,
|
||||
compute_sep_kette,
|
||||
compute_kreisel_umlauf,
|
||||
SENSOR_TEILEARTEN,
|
||||
WEICHE_TEILEART,
|
||||
GF_VF_TEILEARTEN,
|
||||
@@ -529,28 +531,50 @@ def build_kreisel_weiche_items(items, planquadrat_cfg, start_elem_nr, dbg=None):
|
||||
return elem_nr
|
||||
|
||||
|
||||
def build_strecke_schleus_items(items, planquadrat_cfg, start_elem_nr, dbg=None):
|
||||
def _strecke_typ_feld(teileart):
|
||||
"""Merkmals-Feldname fuer die Ketten-Zugehoerigkeit einer AS/ES-/Separator-
|
||||
Zeile, je Kettentyp unterschiedlich (User-Vorgabe): VF_n ("ILS 2.0
|
||||
Strecke") -> "Förderer", GF_n ("ILS 2.0 Gefaellestrecke") -> "Strecke"
|
||||
(wie bisher)."""
|
||||
return "Strecke" if teileart == "ILS 2.0 Gefaellestrecke" else "Förderer"
|
||||
|
||||
|
||||
def build_strecke_schleus_items(items, planquadrat_cfg, start_elem_nr, sep_kette=None, dbg=None):
|
||||
"""Haengt fuer jedes Strecken-Ende mit anschliessendem Kreisel eine
|
||||
"ILS Ausschleuselement"- (Anfang) bzw. "ILS Einschleuselement"-Zeile
|
||||
(Ende) an items an (siehe export_neighbors.compute_strecke_kreisel_schleus).
|
||||
(Ende) an items an (siehe export_neighbors.compute_strecke_kreisel_schleus
|
||||
fuer Position/Boundingbox - reine Geometrie, unveraendert).
|
||||
|
||||
Muss NACH build_kreisel_weiche_items laufen, damit die TeileId-Zaehlung
|
||||
(max_numeric_teileid) auch die bereits erzeugten Weichen einschliesst.
|
||||
Position = Kollisionsposition Kreisel<->Strecke; Nachbarn = Kreisel-ID +
|
||||
Strecken-ID; Anzahl 1; K1-K4/Insertpoint leer; Boundingbox = die statische,
|
||||
an der Kreiselachse ausgerichtete Default-Box.
|
||||
Anzahl 1; K1-K4/Insertpoint leer; Boundingbox = die statische, an der
|
||||
Kreiselachse ausgerichtete Default-Box.
|
||||
|
||||
sep_kette = Rueckgabe von export_neighbors.compute_sep_kette (keys
|
||||
"<strecke_id>#<lfdnr>" -> Eintrag mit typ/vorgaenger/nachfolger). Traegt
|
||||
die zugehoerige Strecke eine "sepliste" (neue LISP-XDATA, siehe
|
||||
ssg-sepliste-xdata-schreiben), enthaelt "Nachbarn" NUR noch die IDs des
|
||||
ketteninternen Vorgaengers/Nachfolgers (Separator oder je anderem
|
||||
Kreisel-Ende) statt Kreisel-ID + Strecken-ID; die Kreisel-/Foerderer-
|
||||
Zugehoerigkeit steht dann stattdessen in den Merkmalen ("Kreisel" +
|
||||
"Förderer"/"Strecke" je Typ, analog zum "Zuordnung"-Feld der Separatoren).
|
||||
Fehlt die sepliste (Altbestand-Wrapper ohne die neue XDATA), bleibt der
|
||||
bisherige Nachbarn-Inhalt [kreisel_id, strecke_id] unveraendert erhalten.
|
||||
|
||||
Rueckgabe: fortgeschriebene Elementnummer.
|
||||
"""
|
||||
if sep_kette is None:
|
||||
sep_kette = {}
|
||||
schleus, warnings = compute_strecke_kreisel_schleus(
|
||||
items, durchmesser_mm=KREISEL_DURCHMESSER_MM,
|
||||
element_box_mm=ELEMENT_BOX_MM, dbg=dbg)
|
||||
|
||||
strecke_by_id = {it.get("teileid", ""): it for it in items
|
||||
if it.get("teileart", "") in GF_VF_TEILEARTEN}
|
||||
|
||||
# Warnungen (>2 Kreisel-BBox-Beruehrungen je Strecke) auf die Strecken-Zeile
|
||||
# schreiben (Spalte "Warnungen") und in die Konsole ausgeben.
|
||||
if warnings:
|
||||
strecke_by_id = {it.get("teileid", ""): it for it in items
|
||||
if it.get("teileart", "") in GF_VF_TEILEARTEN}
|
||||
for sid, text in warnings.items():
|
||||
if sid in strecke_by_id:
|
||||
strecke_by_id[sid]["warnung"] = text
|
||||
@@ -567,6 +591,26 @@ def build_strecke_schleus_items(items, planquadrat_cfg, start_elem_nr, dbg=None)
|
||||
zaehler[teileart] = zaehler.get(teileart, 0) + 1
|
||||
planquadrat = (compute_planquadrat(sp["x"], sp["y"], planquadrat_cfg)
|
||||
if planquadrat_cfg is not None else "")
|
||||
|
||||
strecke = strecke_by_id.get(sp["strecke_id"])
|
||||
sepliste = strecke.get("sepliste") if strecke else None
|
||||
merkmale = {"Erzeugt aus": "Kreisel-Strecke-Kollision"}
|
||||
if sepliste:
|
||||
# Neue XDATA vorhanden: Nachbarn nur noch aus der Ketten-internen
|
||||
# Vorgaenger/Nachfolger-Beziehung (AS/ES ist immer das erste bzw.
|
||||
# letzte Sepliste-Element seiner Strecke).
|
||||
lfdnr = sepliste[0]["lfdnr"] if sp["ende_label"] == "Anfang" else sepliste[-1]["lfdnr"]
|
||||
key = f"{sp['strecke_id']}#{lfdnr}"
|
||||
eintrag = sep_kette.get(key, {})
|
||||
nachbarn = [n for n in (eintrag.get("vorgaenger"), eintrag.get("nachfolger")) if n]
|
||||
merkmale["Kreisel"] = sp["kreisel_id"]
|
||||
merkmale[_strecke_typ_feld(strecke.get("teileart", ""))] = sp["strecke_id"]
|
||||
else:
|
||||
nachbarn = [sp["kreisel_id"], sp["strecke_id"]]
|
||||
merkmale["Kreisel"] = sp["kreisel_id"]
|
||||
merkmale["Strecke"] = sp["strecke_id"]
|
||||
merkmale["Streckenende"] = sp["ende_label"]
|
||||
|
||||
items.append({
|
||||
"nr": elem_nr,
|
||||
"teileart": teileart,
|
||||
@@ -577,14 +621,64 @@ def build_strecke_schleus_items(items, planquadrat_cfg, start_elem_nr, dbg=None)
|
||||
"boundingbox": f'{sp["dx"]:.2f}, {sp["dy"]:.2f}, {sp["dz"]:.2f}',
|
||||
"insertpoint": "",
|
||||
"k1": "", "k2": "", "k3": "", "k4": "",
|
||||
"nachbarn": ", ".join([sp["kreisel_id"], sp["strecke_id"]]),
|
||||
"nachbarn": ", ".join(nachbarn),
|
||||
"fehler": "",
|
||||
"merkmale": {
|
||||
"Erzeugt aus": "Kreisel-Strecke-Kollision",
|
||||
"Kreisel": sp["kreisel_id"],
|
||||
"Strecke": sp["strecke_id"],
|
||||
"Streckenende": sp["ende_label"],
|
||||
},
|
||||
"merkmale": merkmale,
|
||||
})
|
||||
return elem_nr
|
||||
|
||||
|
||||
def build_separator_kette_items(items, planquadrat_cfg, start_elem_nr, sep_kette, dbg=None):
|
||||
"""Haengt fuer jeden in einer VF_n/GF_n-Kette eingebetteten Separator
|
||||
("Staustrecke_Separator_SP_300_mm", ueber die neue "sepliste"-XDATA
|
||||
bekannt - siehe ssg-sepliste-xdata-schreiben/compute_sep_kette) eine
|
||||
eigene "ILS 2.0 Separator"-Zeile an. Bisher wurden diese internen
|
||||
Ketten-Separatoren nur gezaehlt (ANZAHL_SEPARATOR), nicht einzeln
|
||||
exportiert - anders als frei platzierte Separator_SP-Symbole.
|
||||
|
||||
Nachbarn = ketteninterner Vorgaenger/Nachfolger (Separator oder AS/ES),
|
||||
analog zur AS/ES-Nachbarn-Spalte in build_strecke_schleus_items.
|
||||
Merkmale bekommen "Zuordnung" = Strecken-TeileId (wie bei frei
|
||||
platzierten Separatoren ueber compute_sensor_zuordnung).
|
||||
|
||||
Rueckgabe: fortgeschriebene Elementnummer.
|
||||
"""
|
||||
strecke_by_id = {it.get("teileid", ""): it for it in items
|
||||
if it.get("teileart", "") in GF_VF_TEILEARTEN}
|
||||
|
||||
naechste_id = max_numeric_teileid(items)
|
||||
elem_nr = start_elem_nr
|
||||
zaehler = 0
|
||||
for key, eintrag in sep_kette.items():
|
||||
if eintrag.get("typ") != "SEP":
|
||||
continue
|
||||
elem_nr += 1
|
||||
naechste_id += 1
|
||||
zaehler += 1
|
||||
teileid = f"{naechste_id:04d}"
|
||||
planquadrat = (compute_planquadrat(eintrag["x"], eintrag["y"], planquadrat_cfg)
|
||||
if planquadrat_cfg is not None else "")
|
||||
nachbarn = [n for n in (eintrag.get("vorgaenger"), eintrag.get("nachfolger")) if n]
|
||||
strecke = strecke_by_id.get(eintrag["strecke_id"])
|
||||
merkmale = {
|
||||
"Erzeugt aus": "VF/GF-Sepliste",
|
||||
"Zuordnung": eintrag["strecke_id"],
|
||||
}
|
||||
if strecke:
|
||||
merkmale[_strecke_typ_feld(strecke.get("teileart", ""))] = eintrag["strecke_id"]
|
||||
items.append({
|
||||
"nr": elem_nr,
|
||||
"teileart": "ILS 2.0 Separator",
|
||||
"teileid": teileid,
|
||||
"bezeichnung": f"Separator :{zaehler}",
|
||||
"planquadrat": planquadrat,
|
||||
"position": f'{eintrag["x"]:.2f}, {eintrag["y"]:.2f}, 0.00',
|
||||
"boundingbox": "",
|
||||
"insertpoint": "",
|
||||
"k1": "", "k2": "", "k3": "", "k4": "",
|
||||
"nachbarn": ", ".join(nachbarn),
|
||||
"fehler": "",
|
||||
"merkmale": merkmale,
|
||||
})
|
||||
return elem_nr
|
||||
|
||||
@@ -921,14 +1015,42 @@ def process_blocks(blocks, lookup, dbg=None):
|
||||
if item.get("teileart", "") == "ILS 2.0 Scanner" and separator_id:
|
||||
item["merkmale"]["Naechster Separator"] = separator_id
|
||||
|
||||
# Ketteninterne Separator-/AS-/ES-Reihenfolge aus der neuen "sepliste"-
|
||||
# XDATA (siehe ssg-sepliste-xdata-schreiben) - leeres dict, wenn keine
|
||||
# Strecke eine sepliste traegt (reiner Altbestand ohne neue XDATA).
|
||||
sep_kette = compute_sep_kette(items, dbg=dbg)
|
||||
|
||||
# Umlauf-Reihenfolge an jedem Kreisel: freie Separatoren (per Zuordnung
|
||||
# oben bereits einem Kreisel zugeordnet) UND die AS/ES-Enden andockender
|
||||
# Ketten (aus sep_kette), sortiert nach Winkel um die Kreiselachse (siehe
|
||||
# compute_kreisel_umlauf - DREHRICHTUNG-Attribut bestimmt den Umlaufsinn).
|
||||
# Freie Separator-Zeilen bekommen ihre "Nachbarn"-Spalte hier aus dem
|
||||
# Umlauf statt leer zu bleiben; die AS/ES-Nachbarn selbst setzt weiterhin
|
||||
# build_strecke_schleus_items (ketteninterner Vorgaenger/Nachfolger).
|
||||
kreisel_umlauf = compute_kreisel_umlauf(
|
||||
items, sep_kette, NEIGHBOR_TOLERANCE_MM,
|
||||
durchmesser_mm=KREISEL_DURCHMESSER_MM, dbg=dbg)
|
||||
if kreisel_umlauf:
|
||||
separator_by_id = {it.get("teileid", ""): it for it in items
|
||||
if it.get("teileart", "") == "ILS 2.0 Separator"}
|
||||
for punkte in kreisel_umlauf.values():
|
||||
for punkt_key, vorgaenger, nachfolger in punkte:
|
||||
item = separator_by_id.get(punkt_key)
|
||||
if item is None:
|
||||
continue
|
||||
nachbarn = [n for n in (vorgaenger, nachfolger) if n]
|
||||
item["nachbarn"] = ", ".join(nachbarn)
|
||||
|
||||
# Nach der Kollisions-/Nachbarschaftserkennung: an jedem Kreisel-
|
||||
# Beruehrpunkt eine ILS-Weiche anhaengen (eigene Zeilen am Listenende -
|
||||
# sie nehmen an der Nachbarschaftserkennung selbst nicht teil, ihre
|
||||
# Nachbarn sind fest die beiden beruehrenden Kreisel). Danach an jedem
|
||||
# Strecken-Ende mit anschliessendem Kreisel ein Aus-/Einschleuselement -
|
||||
# die TeileId-Zaehlung laeuft ueber beide hinweg weiter (Weichen zuerst).
|
||||
# Strecken-Ende mit anschliessendem Kreisel ein Aus-/Einschleuselement,
|
||||
# gefolgt von je einer Zeile pro in einer Kette eingebettetem Separator -
|
||||
# die TeileId-Zaehlung laeuft ueber alle drei hinweg weiter.
|
||||
elem_nr = build_kreisel_weiche_items(items, planquadrat_cfg, elem_nr, dbg=dbg)
|
||||
build_strecke_schleus_items(items, planquadrat_cfg, elem_nr, dbg=dbg)
|
||||
elem_nr = build_strecke_schleus_items(items, planquadrat_cfg, elem_nr, sep_kette=sep_kette, dbg=dbg)
|
||||
build_separator_kette_items(items, planquadrat_cfg, elem_nr, sep_kette, dbg=dbg)
|
||||
|
||||
# Nach dem Sammelheimen aller Zeilen: sortiert nach TeileId ausgeben, damit
|
||||
# Elementnummer (erste Spalte) und TeileId nach Moeglichkeit uebereinstimmen
|
||||
|
||||
Reference in New Issue
Block a user