diff --git a/Lisp/README.md b/Lisp/README.md index ed1672d..9faecfd 100644 --- a/Lisp/README.md +++ b/Lisp/README.md @@ -423,12 +423,11 @@ Sammelt INSERT-Blöcke aus der Zeichnung und exportiert sie als JSON oder ruft P | `c:EXPORTSIVAS` | Alle Blöcke als JSON sammeln und `export_sivas.py` aufrufen (Sivas-CSV) | | `c:EXPORTCSV` | Alle Blöcke als JSON sammeln und `export_csv.py` aufrufen (einfache Item-Liste, inkl. Omniflo Bogen/Weiche/Gerade und abschließender "Omniflo Sum"-Zeile) | | `csv:collect-export-blocks` | Alle INSERT-Blöcke der Zeichnung als JSON-Array sammeln | -| `csv:run-export py-skript json-datei output-csv` | Python-Skript mit gesammelten Daten aufrufen; legt vorher fuer in VF_n/GF_n/KREISEL_n **verpackte** Separator_SP_2D/_3D-Sub-INSERTs (Sensor-Symbol; siehe `ssg-collect-nested-inserts` in `ssg_core.lsp` - NICHT der interne Staustrecke_Separator_SP-Trennsteg) je eine echte, temporaere Kopie **an derselben Stelle** an (`csv:sep-proxies-erzeugen`, Position/Drehung/Skalierung ueber alle Verschachtelungsebenen hinweg identisch zum Vorbild - kein kosmetischer Versatz mehr, der die geometrische Sensor-Zuordnung verfaelschen wuerde), damit sie ganz normal ueber `ssg-id-check-all`/`csv:collect-export-blocks` erfasst werden, und entfernt sie danach wieder (`csv:sep-proxies-loeschen`). Die verpackten **AS-/ES-Schleuselemente** derselben Kette bekommen stattdessen einen DAUERHAFTEN Marker (`csv:asmark-aktualisieren`, siehe unten); beide Wege teilen sich die Platzierungsrechnung (`csv:nested-welt-records` + `csv:block-einfuegen-welt`). Reihenfolge ist wesentlich: **erst alle** Kopien aus allen Wrappern erzeugen, **dann** `ssg-id-check-all` (vergibt IDs oberhalb des globalen Maximums), **dann** `csv:sep-proxies-zuordnung-setzen` und `csv:sepliste-ids-setzen` | -| `csv:sep-proxies-zuordnung-setzen proxy-liste` | ZUORDNUNG jeder Separator-Kopie auf die **ID ihres Wrapper-Blocks** setzen (ein in VF_n mit ID 0010 verpackter Separator erhaelt ZUORDNUNG 0010) und die Zuordnung ueber `*cs-sep-fix-by-handle*` fuer `cs-zuordnung-lauf` festnageln - fuer verpackte Separatoren ist der Carrier bekannt und wird nicht per Boundingbox geraten. Muss NACH `ssg-id-check-all` laufen, da ein frisch gebauter Wrapper vorher noch keine ID hat | +| `csv:run-export py-skript json-datei output-csv` | Python-Skript mit gesammelten Daten aufrufen; legt vorher fuer in VF_n/GF_n/KREISEL_n **verpackte** Separator_SP_2D/_3D-Sub-INSERTs (Sensor-Symbol; siehe `ssg-collect-nested-inserts` in `ssg_core.lsp` - NICHT der interne Staustrecke_Separator_SP-Trennsteg) je eine echte, temporaere Kopie **an derselben Stelle** an (`csv:sep-proxies-erzeugen`, Position/Drehung/Skalierung ueber alle Verschachtelungsebenen hinweg identisch zum Vorbild - kein kosmetischer Versatz mehr, der die geometrische Sensor-Zuordnung verfaelschen wuerde), damit sie ganz normal ueber `ssg-id-check-all`/`csv:collect-export-blocks` erfasst werden, und entfernt sie danach wieder (`csv:sep-proxies-loeschen`). Die verpackten **AS-/ES-Schleuselemente** derselben Kette bekommen stattdessen einen DAUERHAFTEN Marker (`csv:asmark-aktualisieren`, siehe unten); beide Wege teilen sich die Platzierungsrechnung (`csv:nested-welt-records` + `csv:block-einfuegen-welt`). Reihenfolge ist wesentlich: **erst** `ssg-id-check-all` (Wrapper-IDs endgueltig), **dann** die Kopien/Marker (ZUORDNUNG direkt beim Erzeugen), **dann** `csv:ketten-verarbeiten` (IDs in Kettenfolge, Separator-Typ, sepliste-Stellvertreter), **dann** ein zweites `ssg-id-check-all` (Restnummerierung) | | `csv:asmark-aktualisieren` | AS-/ES-Kettenenden sichtbar machen: setzt an jedes verpackte `AS_Element*`/`ES_Element*` einer VF_n/GF_n-Kette ein kleines Rechteck (Block `ZZ_ASES_MARKER`, Groesse aus `cfg/export.cfg` `[Marker]`, Layer aus `cfg/layer.cfg` `ases_marker`), das nach dem Export **stehen bleibt**. Unsichtbare Attribute ID/ZUORDNUNG/TYP/LFDNR/ELEMENT/ARTINR; die `ID` fuellt `ssg-id-check-all` und der naechste Export **uebernimmt sie**, ein AS/ES behaelt seine TeileId also ueber Laeufe hinweg. Baut bei jedem Export alle Marker neu auf (Wiederfinden ueber ZUORDNUNG+TYP+LFDNR) und entfernt verwaiste | -| `csv:asmark-ids-setzen marker-liste` | Nach `ssg-id-check-all`: ZUORDNUNG (Wrapper-ID) am Marker nachtragen und seine ID am `sepliste`-Eintrag vermerken | -| `csv:sepliste-ids-setzen proxy-liste` | ID jeder Kopie (Separator UND AS/ES, beide Listen zusammen) an ihren `sepliste`-Eintrag des Wrappers haengen; Ergebnis in `*csv-sepliste-id-by-handle*` (Key = Wrapper-Handle), von `csv:block-to-json` als Feld `id` je Eintrag geschrieben. Muss NACH `ssg-id-check-all` laufen | -| `csv:sepliste-paare eintraege proxies` | Zuordnung `sepliste`-Eintrag -> Kopie: nur innerhalb desselben Typmarkers (`AS`/`ES`/`SEP`) und **exklusiv** (kuerzester Abstand zuerst, danach sind Eintrag und Kopie verbraucht). Damit verschiebt EIN fehlerhafter Koordinatenwert in der `sepliste` keine anderen Zuordnungen. Reine Rechenfunktion, getestet in `tests/test_unit.lsp` | +| `csv:ketten-verarbeiten sep-proxies element-proxies asmarks` | Je VF_n/GF_n aus DERSELBEN KS-geordneten Kette (`csv:kette-von`, einmal je Lauf berechnet): Einheiten-Proxies (`group_vf_gf`), IDs in Kettenfolge, Separator-Typ VF/GF (`csv:kette-sep-typen` -> `*cs-sep-typ-by-handle*`) und der Stellvertreter jedes `sepliste`-Eintrags (`*csv-sepliste-id-by-handle*`, Key = Wrapper-Handle; `csv:block-to-json` liest dessen ID beim Schreiben als Feld `id`) | +| `csv:sepliste-kette-paare eintraege typen` | Zuordnung `sepliste`-Eintrag -> Kettenbaustein ueber die **Reihenfolge je Typ** (k-tes `AS`/`SEP`/`ES` der sepliste = k-tes der KS-Kette), ohne die (teils gespiegelten) sepliste-Koordinaten. Reine Rechenfunktion, getestet in `tests/test_unit.lsp` | +| `csv:element-familien` | EINZIGE Tabelle Familie -> Blockmuster (`pattern_nested_*` aus `cfg/export.cfg`) fuer Suche (`csv:pattern-nested-element`) UND Klassifizierung (`csv:element-teileart`, darauf aufbauend `csv:kette-typ`) | | `csv:read-attribs ename` | Attribute eines INSERT als JSON-Objekt lesen | | `c:OMNI_UPDATE_ATTRIBS` | HOEHE/DREHUNG aller Omniflo-Elemente aus der Zeichnung aktualisieren | diff --git a/Lisp/export.lsp b/Lisp/export.lsp index c4e88d3..8236a77 100644 --- a/Lisp/export.lsp +++ b/Lisp/export.lsp @@ -565,21 +565,29 @@ bbox ) -;; --- Baustein-Geometrie (K-Punkte + Boundingbox) unter dem Handle seines -;; Stellvertreters merken --- rec wie csv:nested-welt-records: (wrapper -;; nested-ent bname (x y z) R sx sy sz). Ob gemerkt wird, entscheidet der -;; Aufrufer je Modus (siehe oben). Traegt der Baustein keine KS, bleibt die +;; --- K-Punkte + Boundingbox unter dem Handle eines Stellvertreters merken --- +;; EINZIGE Stelle, die *csv-baustein-by-handle* fuellt. punkte = Alist +;; (("K1" x y z) ...) in Welt, bbox wie csv:get-bbox. Ohne Punkte bleibt die ;; K-Liste nil und csv:k-liste faellt auf die KS des Stellvertreters zurueck. -(defun csv:baustein-merken (stellvertreter rec / punkte) - (setq punkte (csv:ks-welt-punkte (nth 2 rec) (nth 3 rec) (nth 4 rec) - (nth 5 rec) (nth 6 rec) (nth 7 rec))) +(defun csv:geometrie-merken (stellvertreter punkte bbox) (setq *csv-baustein-by-handle* (cons (list (cdr (assoc 5 (entget stellvertreter))) (csv:k-liste-kodieren punkte) - (csv:baustein-bbox rec)) + bbox) *csv-baustein-by-handle*)) ) +;; --- Baustein-Geometrie (K-Punkte + Boundingbox) unter dem Handle seines +;; Stellvertreters merken --- rec wie csv:nested-welt-records: (wrapper +;; nested-ent bname (x y z) R sx sy sz). Ob gemerkt wird, entscheidet der +;; Aufrufer je Modus (siehe oben). +(defun csv:baustein-merken (stellvertreter rec) + (csv:geometrie-merken stellvertreter + (csv:ks-welt-punkte (nth 2 rec) (nth 3 rec) (nth 4 rec) + (nth 5 rec) (nth 6 rec) (nth 7 rec)) + (csv:baustein-bbox rec)) +) + ;; --- Gemerkte Baustein-Boundingbox eines Stellvertreters, sonst nil --- (defun csv:baustein-bbox-gemerkt (handle) (caddr (assoc handle *csv-baustein-by-handle*)) @@ -809,20 +817,6 @@ ) -;; --- K1/K2 eines ZZ_UNIT_MARKER lesen --- -;; Fallback fuer csv:block-to-json (siehe dort): die Werte stehen bereits -;; fertig kodiert in den ATTDEFs K1/K2 (siehe csv:ketten-verarbeiten), -;; hier nur noch als K-Liste im Format von csv:get-k-kos-strings verpackt. -(defun csv:unitmark-k-kos-strings (ename / attribs k1 k2 result) - (setq attribs (ssg-attrib-read ename)) - (setq k1 (cdr (assoc "K1" attribs))) - (setq k2 (cdr (assoc "K2" attribs))) - (setq result nil) - (if (and k1 (> (strlen k1) 0)) (setq result (cons (cons "K1" k1) result))) - (if (and k2 (> (strlen k2) 0)) (setq result (cons (cons "K2" k2) result))) - result -) - ;; --- Einen INSERT-Block als JSON-Objekt schreiben --- ;; include-bbox = T -> zusaetzlich Bounding-Box (Mitte + Ausdehnung) ermitteln ;; und als "bbox"-Objekt anhaengen (nur fuer EXPORTCSV, nicht fuer EXPORTSIVAS). @@ -835,8 +829,8 @@ insert-quat insert-kos k-list warnung-json warnung-eintrag zuordnung-fix-json zuordnung-fix-eintrag strecke-typ-json strecke-typ-eintrag - sepliste-json sepliste-app sepliste - sepliste-teile sepliste-first sepliste-ids sep-id) + sepliste-json sepliste + sepliste-teile sepliste-first sepliste-ids sep-id sep-ent) (setq ed (entget ename)) (setq blk-name (cdr (assoc 2 ed))) (setq layer (cdr (assoc 8 ed))) @@ -884,14 +878,6 @@ (wcmatch (strcase blk-name) "VF_*,GF_*")) (setq k-list (csv:vfgf-k-kos-strings ename pt rotation)) ) - ;; ZZ_UNIT_MARKER (Foerdereinheit/Gefaelleeinheit): K1/K2 stehen bereits - ;; fertig kodiert als eigene ATTDEFs am Marker (csv:ketten-verarbeiten - ;; berechnet sie einmal bei der Marker-Erzeugung aus dem ersten/letzten - ;; zugeordneten Element) - keine erneute KS-Suche noetig. - (if (and (null k-list) - (wcmatch (strcase blk-name) "ZZ_UNIT_MARKER*")) - (setq k-list (csv:unitmark-k-kos-strings ename)) - ) (setq bbox-json "") (if include-bbox (progn @@ -928,7 +914,7 @@ ;; Bekannte (nicht geratene) Separator-Zuordnung durchreichen: fuer die ;; temporaeren Kopien VERPACKTER Separator_SP-Symbole steht der Carrier fest ;; (der Wrapper-Block, in dem das Symbol steckt) - siehe - ;; csv:sep-proxies-zuordnung-setzen und *cs-sep-fix-by-handle*. export_csv.py + ;; csv:sep-proxies-erzeugen und *cs-sep-fix-by-handle*. export_csv.py ;; berechnet die Zuordnung sonst ein zweites Mal per Boundingbox- ;; Ueberschneidung (compute_sensor_zuordnung, Prioritaet GF > VF > Kreisel- ;; haelfte) und wuerde den bekannten Wert damit ueberschreiben - z.B. auf @@ -960,54 +946,45 @@ ;; ohne dieses Feld - export_neighbors.py faellt dann auf den bisherigen ;; BBox-Kollisionspfad zurueck). (setq sepliste-json "") - (if (car (atoms-family 1 '("SSG-SEPLISTE-XDATA-LESEN"))) + (setq sepliste (csv:sepliste-lesen ename)) + (if sepliste (progn - (setq sepliste-app - (cond - ((wcmatch (strcase blk-name) "VF_*") "SSG_VF_SEP") - ((wcmatch (strcase blk-name) "GF_*") "SSG_GF_SEP") - (t nil))) - (if sepliste-app - (setq sepliste (ssg-sepliste-xdata-lesen ename sepliste-app))) - (if sepliste - (progn - ;; ID des Bauteils, das zu diesem Eintrag gehoert: beim Export wird - ;; fuer JEDEN Eintrag (AS/SEP/ES) eine echte, temporaere Kopie des - ;; verpackten Originals eingefuegt und ueber ssg-id-check-all - ;; nummeriert; csv:sepliste-ids-setzen haengt die vergebene ID an den - ;; Eintrag (Key = Handle dieses Wrappers). Fehlt sie (Kopie nicht - ;; erzeugbar, Block ohne ID-ATTDEF, Altbestand), bleibt das Feld weg - - ;; der Python-Teil faellt dann auf seine Koordinaten-Zuordnung zurueck. - ;; - ;; KEIN x/y mehr im JSON: die sepliste-XDATA-Koordinaten sind der - ;; rohe Gruppencode-10-Punkt des jeweiligen Einfuege-Primitivs - ;; (siehe gf-insert-hz-with-ks-Kommentar, ssg_ks_insert.lsp) und - ;; koennen gespiegelt/verschoben sein (Nutzer-Befund 2026-09-25: - ;; Sepliste-x/y von SEP-Eintraegen wich um bis zu mehrere 100mm bzw. - ;; vorzeichenverkehrt von der tatsaechlichen, aus der echten - ;; Proxy-Kopie berechneten Position ab - siehe die "x"/"y"-Felder - ;; der eigenen Bauteil-Zeile, die verlaesslich sind). x/y wurden nie - ;; fuer die ID-Zuordnung gebraucht (die laeuft VORHER, LISP-seitig, - ;; ueber csv:sepliste-paare/csv:sepliste-ids-setzen mit einer - ;; eigenen, unabhaengigen XDATA-Lesung) - nur der Python-Naehe- - ;; Fallback (map_sepliste_kette_items in export_csv.py, fuer - ;; SEP-Eintraege ohne "id", Altbestand ohne ID-ATTDEF) konsumierte - ;; sie und degradiert jetzt auf 0.0/0.0 (siehe dort). - (setq sepliste-ids (cdr (assoc handle *csv-sepliste-id-by-handle*))) - (setq sepliste-teile "" sepliste-first t) - (foreach e sepliste - (setq sep-id (cdr (assoc (nth 1 e) sepliste-ids))) - (setq sepliste-teile - (strcat sepliste-teile (if sepliste-first "" ",") - "{\"typ\":\"" (nth 0 e) "\"" - ",\"lfdnr\":" (itoa (nth 1 e)) - (if sep-id (strcat ",\"id\":\"" (csv:json-escape sep-id) "\"") "") - "}")) - (setq sepliste-first nil) - ) - (setq sepliste-json (strcat ",\"sepliste\":[" sepliste-teile "]")) - ) + ;; ID des Bauteils, das zu diesem Eintrag gehoert: beim Export + ;; steht fuer jeden Eintrag (AS/SEP/ES) ein temporaerer + ;; Stellvertreter (Separator-Kopie bzw. AS-/ES-Marker); + ;; csv:ketten-verarbeiten hat ihn dem Eintrag in Kettenfolge + ;; zugeordnet (Key = Handle dieses Wrappers), seine ID wird erst hier + ;; gelesen. Fehlt sie (Stellvertreter nicht erzeugbar, Block ohne + ;; ID-ATTDEF, Altbestand), bleibt das Feld weg. + ;; + ;; KEIN x/y mehr im JSON: die sepliste-XDATA-Koordinaten sind der + ;; rohe Gruppencode-10-Punkt des jeweiligen Einfuege-Primitivs + ;; (siehe gf-insert-hz-with-ks-Kommentar, ssg_ks_insert.lsp) und + ;; koennen gespiegelt/verschoben sein (Nutzer-Befund 2026-09-25: + ;; Sepliste-x/y von SEP-Eintraegen wich um bis zu mehrere 100mm bzw. + ;; vorzeichenverkehrt von der tatsaechlichen, aus der echten + ;; Proxy-Kopie berechneten Position ab - siehe die "x"/"y"-Felder + ;; der eigenen Bauteil-Zeile, die verlaesslich sind). x/y wurden nie + ;; fuer die ID-Zuordnung gebraucht (die laeuft VORHER, LISP-seitig, + ;; ueber die Kettenfolge, csv:sepliste-kette-paare) - nur der Python-Naehe- + ;; Fallback (map_sepliste_kette_items in export_csv.py, fuer + ;; SEP-Eintraege ohne "id", Altbestand ohne ID-ATTDEF) konsumierte + ;; sie und degradiert jetzt auf 0.0/0.0 (siehe dort). + (setq sepliste-ids (cdr (assoc handle *csv-sepliste-id-by-handle*))) + (setq sepliste-teile "" sepliste-first t) + (foreach e sepliste + (setq sep-ent (cdr (assoc (nth 1 e) sepliste-ids))) + (setq sep-id (if sep-ent (cdr (assoc "ID" (ssg-attrib-read sep-ent))))) + (if (= sep-id "") (setq sep-id nil)) + (setq sepliste-teile + (strcat sepliste-teile (if sepliste-first "" ",") + "{\"typ\":\"" (nth 0 e) "\"" + ",\"lfdnr\":" (itoa (nth 1 e)) + (if sep-id (strcat ",\"id\":\"" (csv:json-escape sep-id) "\"") "") + "}")) + (setq sepliste-first nil) ) + (setq sepliste-json (strcat ",\"sepliste\":[" sepliste-teile "]")) ) ) (strcat @@ -1048,22 +1025,31 @@ "Separator_SP_2D,Separator_SP_3D" ) +;; --- TeileArt-Familien der uebrigen verpackten VF_n/GF_n-Bausteine --- +;; EINZIGE Tabelle fuer Suche UND Klassifizierung: ((familie . muster) ...), +;; Muster aus cfg/export.cfg [blockpattern] pattern_nested_* (Default wie +;; dort). csv:pattern-nested-element sucht damit, csv:element-teileart +;; ordnet einen Fund damit seiner Familie zu - ein in der cfg ergaenztes +;; Muster wirkt dadurch an beiden Stellen gleich (frueher war die +;; Klassifizierung fest verdrahtet: ein neuer cfg-Eintrag wurde gefunden, +;; aber als nil klassifiziert und fiel still aus der Kette). +;; Vario_Bogen_* deckt die _TEF_-Variante (VF-Vertikalboegen) und die reine +;; GF-Variante ab, beide auf "BOGEN". +(defun csv:element-familien () + (list + (cons "BOGEN" (export:pattern "pattern_nested_bogen" "Gefaellebogen_*,Vario_Bogen_*,Vario_Kurve_*")) + (cons "STRECKENELEMENT" (export:pattern "pattern_nested_streckenelement" "Staustrecke_SP_1000_mm*")) + (cons "UMLENKSTATION" (export:pattern "pattern_nested_umlenkstation" "Vario_Umlenkstation_*")) + (cons "MOTORSTATION" (export:pattern "pattern_nested_motorstation" "Vario_Motorstation_*"))) +) + ;; --- Muster der uebrigen verpackten VF_n/GF_n-Bausteine (Boegen, gerade ;; Foerderer-/Gefaellestrecken-Segmente, Umlenk-/Motorstation) --- -;; Wie csv:pattern-nested-separator/-schleus: diese Bausteine stecken als -;; Sub-INSERTs in der Wrapper-Blockdefinition und sind fuer (ssget "X" ...) -;; unsichtbar. EIN kombiniertes Muster fuer alle vier Familien (statt vier -;; getrennter csv:nested-welt-records-Aufrufe) haelt den zusaetzlichen -;; Scan-Aufwand auf EINEN weiteren Durchlauf der ganzen Zeichnung beschraenkt -;; - csv:element-teileart klassifiziert danach anhand des Blocknamens, in -;; welche TeileArt-Familie ein Treffer faellt. +;; Diese Bausteine stecken als Sub-INSERTs in der Wrapper-Blockdefinition und +;; sind fuer (ssget "X" ...) unsichtbar. EIN kombiniertes Muster fuer alle +;; Familien haelt den zusaetzlichen Scan auf EINEN Durchlauf beschraenkt. (defun csv:pattern-nested-element () - (strcat - (export:pattern "pattern_nested_bogen" "Gefaellebogen_*,Vario_Bogen_*,Vario_Kurve_*") - "," (export:pattern "pattern_nested_streckenelement" "Staustrecke_SP_1000_mm*") - "," (export:pattern "pattern_nested_umlenkstation" "Vario_Umlenkstation_*") - "," (export:pattern "pattern_nested_motorstation" "Vario_Motorstation_*") - ) + (ssg-join (mapcar 'cdr (csv:element-familien)) ",") ) ;; --- Muster der verpackten AS-/ES-Schleuselemente --- @@ -1078,41 +1064,15 @@ (export:pattern "pattern_schleuselement" "AS_Element*,ES_Element*") ) -;; --- Typmarker eines ausgepackten Originals aus seinem Blocknamen --- -;; Liefert dieselben drei Marker, die ssg-sepliste-sammeln (ssg_ks_insert.lsp) -;; in die sepliste schreibt: "AS"/"ES"/"SEP". Ueber ihn wird eine Kopie ihrem -;; sepliste-Eintrag zugeordnet (csv:sepliste-paare) - zugeordnet wird nur -;; innerhalb desselben Typs. -(defun csv:proxy-typ (bname) - (cond - ((wcmatch (strcase bname) "AS_ELEMENT*") "AS") - ((wcmatch (strcase bname) "ES_ELEMENT*") "ES") - (t "SEP") - ) -) - ;; --- TeileArt-Familie eines ausgepackten Elements aus seinem Blocknamen --- -;; Reiner Namensklassifikator (parallel zu csv:proxy-typ), file-frei testbar -;; (tests/test_unit.lsp). Vario_Bogen_* deckt sowohl die _TEF_-Variante (VF- -;; Vertikalboegen) als auch die reine Variante ohne _TEF_ (GF-eigene Vertikal- -;; boegen) ab - beide auf denselben TeileArt-Bucket "BOGEN", darum ist - -;; anders als bei vfl-kette-typ (vf_linienzug.lsp) - keine _TEF_-zuerst- -;; Reihenfolge noetig. Vario_Kurve_* ist laut Code-Kommentar (vf_linienzug.lsp) -;; aktuell nirgends im Projekt tatsaechlich eingefuegt, aber zur Vollstaendig- -;; keit mit aufgenommen. Liefert nil fuer alles andere (sollte bei einem -;; ueber csv:pattern-nested-element gefundenen Treffer nie vorkommen - -;; defensive Absicherung, falls Muster und cond je auseinanderlaufen). -(defun csv:element-teileart (bname / bn) +;; Erste Familie aus csv:element-familien, deren Muster passt (Gross-/ +;; Kleinschreibung egal), sonst nil. File-frei testbar (tests/test_unit.lsp). +(defun csv:element-teileart (bname / bn fam treffer) (setq bn (strcase bname)) - (cond - ((wcmatch bn "GEFAELLEBOGEN_*") "BOGEN") - ((wcmatch bn "VARIO_KURVE_*") "BOGEN") - ((wcmatch bn "VARIO_BOGEN_*") "BOGEN") - ((wcmatch bn "STAUSTRECKE_SP_1000_MM*") "STRECKENELEMENT") - ((wcmatch bn "VARIO_UMLENKSTATION_*") "UMLENKSTATION") - ((wcmatch bn "VARIO_MOTORSTATION_*") "MOTORSTATION") - (t nil) - ) + (foreach fam (csv:element-familien) + (if (and (null treffer) (wcmatch bn (strcase (cdr fam)))) + (setq treffer (car fam)))) + treffer ) ;; --- Muster der Compound-("Wrapper"-)Bloecke, deren Definition ueberhaupt @@ -1326,7 +1286,23 @@ ;; csv:ketten-verarbeiten), nil fuer ein Symbol ohne Trennsteg (Kreisel- ;; Separator). Zuordnung Symbol<->Trennsteg ueber die Symbollage innerhalb ;; des Trennstegs (csv:trennsteg-zu-symbol). -(defun csv:sep-proxies-erzeugen ( / ms recs aussen-paare rec aussen-rec proxy proxy-liste) +;; +;; ZUORDNUNG wird hier EINMAL gesetzt: die Zuordnung eines VERPACKTEN +;; Separators ist keine Schaetzung, er steckt in genau diesem Wrapper +;; (VF_n/GF_n/KREISEL_n). Laeuft nach dem ersten ssg-id-check-all +;; (csv:export-vorbereiten), die Wrapper-ID steht also schon endgueltig fest. +;; Zusaetzlich wird die Kopie in *cs-sep-fix-by-handle* als "bekannt" +;; festgenagelt: count_sep_scan.lsp (cs-zuordnung-lauf) und export_csv.py +;; (Feld "zuordnung_fix") rechnen die Zuordnung eigenstaendiger Sensoren bei +;; jedem Lauf geometrisch neu und brauchen diese Markierung, um verpackte +;; Separatoren davon auszunehmen - bei ueberlappenden Carrier-Boxen +;; (Kettenende an einem Kreisel) laege die Geometrie leicht beim Nachbarn. +;; Wrapper ohne ID-Attribut (Altbestand ohne ID-ATTDEF): keine bekannte +;; Zuordnung, die Kopie faellt auf die geometrische Zuordnung zurueck. +(defun csv:sep-proxies-erzeugen ( / ms recs aussen-paare rec aussen-rec proxy proxy-liste + wrapper-id gesetzt ohne-id) + (setq gesetzt 0 ohne-id 0) + (setq *cs-sep-fix-by-handle* nil) (setq recs (csv:nested-welt-records (csv:pattern-nested-separator))) (setq aussen-paare (csv:trennsteg-paare (csv:nested-welt-records (csv:pattern-nested-separator-aussen)))) @@ -1343,13 +1319,27 @@ (if (and proxy aussen-rec (csv:export-alle-elemente-p)) (csv:baustein-merken proxy aussen-rec)) (if proxy - (setq proxy-liste - (cons (list proxy (nth 0 rec) (csv:proxy-typ (nth 2 rec)) (nth 3 rec) - (if aussen-rec (nth 1 aussen-rec))) - proxy-liste))) + (progn + (setq wrapper-id (cdr (assoc "ID" (ssg-attrib-read (nth 0 rec))))) + (if (and wrapper-id (> (strlen wrapper-id) 0)) + (progn + (ssg-attrib-set-on proxy (list (cons "ZUORDNUNG" wrapper-id))) + (setq *cs-sep-fix-by-handle* + (cons (cons (cdr (assoc 5 (entget proxy))) wrapper-id) + *cs-sep-fix-by-handle*)) + (setq gesetzt (1+ gesetzt))) + (setq ohne-id (1+ ohne-id))) + (setq proxy-liste + (cons (list proxy (nth 0 rec) "SEP" (nth 3 rec) + (if aussen-rec (nth 1 aussen-rec))) + proxy-liste)))) ) ) ) + (if (> gesetzt 0) + (princ (ssg-textf "exp-sep-zuordnung" (list (itoa gesetzt))))) + (if (> ohne-id 0) + (princ (ssg-textf "exp-sep-zuordnung-ohne-id" (list (itoa ohne-id))))) (reverse proxy-liste) ) @@ -1364,7 +1354,7 @@ ;; ;; proxy-liste-Eintrag: (proxy-ename wrapper-ename teileart-key (x y z) ;; baustein-ent) - baustein-ent = der verpackte Baustein (Schluessel fuer die -;; ID-Vergabe in Kettenfolge, csv:ketten-ids-vergeben). +;; ID-Vergabe in Kettenfolge, csv:ketten-verarbeiten). ;; teileart-key (aus csv:element-teileart) wird NICHT ins JSON geschrieben - ;; Python klassifiziert die TeileArt ohnehin ueber block_name + die vier ;; pattern_nested_*-Keys aus cfg/export.cfg, wie jede andere Familie auch. @@ -1406,241 +1396,82 @@ (princ) ) -;; --- ZUORDNUNG der temporaeren Separator-Kopien aus ihrem Wrapper setzen --- -;; MUSS nach ssg-id-check-all laufen: erst dann steht die ID des Wrapper- -;; Blocks (VF_n/GF_n/KREISEL_n) fest - ein frisch gebauter Wrapper kann beim -;; Erzeugen der Kopien noch gar keine ID haben. -;; -;; Die Zuordnung eines VERPACKTEN Separators ist keine Schaetzung, sondern -;; bekannt: er steckt in genau diesem Wrapper. Ein Separator, der in einem -;; VF-Block mit ID 0010 verpackt ist, bekommt also ZUORDNUNG = 0010. Die -;; geometrische Zuordnung in count_sep_scan.lsp (Carrier-Boundingbox bzw. -;; naechster Carrier) wuerde diesen Wert bei jedem Lauf neu berechnen und ggf. -;; ueberschreiben - bei ueberlappenden Boxen (Kettenende an einem Kreisel) -;; landet der Separator dort leicht beim Nachbarn. Darum wird die bekannte -;; Zuordnung ueber *cs-sep-fix-by-handle* (Key = Entity-Handle der Kopie) -;; festgenagelt; cs-zuordnung-lauf uebernimmt sie unveraendert und zaehlt den -;; Separator beim richtigen Carrier. -;; -;; proxy-liste = Records (proxy-ename wrapper-ename typ (x y z) trennsteg-ent), -;; siehe csv:sep-proxies-erzeugen. Rueckgabe: Anzahl festgenagelter Kopien. -(defun csv:sep-proxies-zuordnung-setzen (proxy-liste / rec proxy wrapper wrapper-id - handle gesetzt ohne-id) - (setq gesetzt 0 ohne-id 0) - (setq *cs-sep-fix-by-handle* nil) - (foreach rec proxy-liste - (setq proxy (nth 0 rec)) - (setq wrapper (nth 1 rec)) - (setq handle (cdr (assoc 5 (entget proxy)))) - (setq wrapper-id (cdr (assoc "ID" (ssg-attrib-read wrapper)))) - (if (and wrapper-id (> (strlen wrapper-id) 0)) - (progn - (ssg-attrib-set-on proxy (list (cons "ZUORDNUNG" wrapper-id))) - (setq *cs-sep-fix-by-handle* - (cons (cons handle wrapper-id) *cs-sep-fix-by-handle*)) - (setq gesetzt (1+ gesetzt)) - ) - ;; Wrapper ohne ID-Attribut (Altbestand ohne ID-ATTDEF): keine bekannte - ;; Zuordnung - die Kopie faellt auf die geometrische Zuordnung zurueck. - (setq ohne-id (1+ ohne-id)) - ) - ) - (if (> gesetzt 0) - (princ (ssg-textf "exp-sep-zuordnung" (list (itoa gesetzt))))) - (if (> ohne-id 0) - (princ (ssg-textf "exp-sep-zuordnung-ohne-id" (list (itoa ohne-id))))) - gesetzt -) - -;; --- Globale Zuordnung sepliste-Eintrag -> ID der zugehoerigen Kopie --- -;; Key = Handle des VF_n/GF_n-Wrappers, Wert = Alist ((lfdnr . "0031") ...). -;; Wird von csv:sepliste-ids-setzen NACH der ID-Vergabe gefuellt, von -;; csv:block-to-json beim Schreiben des "sepliste"-Arrays gelesen und von -;; csv:sep-proxies-loeschen wieder zurueckgesetzt - die IDs gehoeren den -;; temporaeren Kopien dieses EINEN Exportlaufs. +;; --- Globale Zuordnung sepliste-Eintrag -> Bauteil --- +;; Key = Handle des VF_n/GF_n-Wrappers, Wert = Alist ((lfdnr . ename) ...), +;; ename = der Stellvertreter (Separator-Kopie bzw. AS-/ES-Marker) dieses +;; Eintrags. Gefuellt von csv:ketten-verarbeiten (Kettenfolge), gelesen von +;; csv:block-to-json beim Schreiben des "sepliste"-Arrays - die ID wird ERST +;; DORT vom Stellvertreter gelesen, damit eine spaetere Duplikat-Korrektur in +;; ssg-id-check-all sie nicht veralten laesst. Zurueckgesetzt von +;; csv:export-vorbereiten und csv:sep-proxies-loeschen. (setq *csv-sepliste-id-by-handle* nil) -;; --- Einen sepliste-Eintrag mit der ID seines Bauteils vermerken --- -;; Zwei Quellen schreiben in dieselbe Zuordnung - die AS/ES-Marker -;; (csv:asmark-ids-setzen) und die temporaeren Separator-Kopien -;; (csv:sepliste-ids-setzen). Beide muessen sich den Eintrag EINES Wrappers -;; teilen: ein zweites Paar (handle . alist) waere fuer assoc unsichtbar, die -;; zuerst eingetragene Liste wuerde immer gewinnen. Darum hier zusammenfuehren -;; statt vorne anzuhaengen. -(defun csv:sepliste-id-merken (handle lfdnr id / eintrag) +;; --- Einen sepliste-Eintrag mit seinem Stellvertreter vermerken --- +;; Alle Eintraege EINES Wrappers teilen sich ein Paar (handle . alist): ein +;; zweites Paar waere fuer assoc unsichtbar. Darum zusammenfuehren statt +;; vorne anzuhaengen. +(defun csv:sepliste-id-merken (handle lfdnr ename / eintrag) (setq eintrag (assoc handle *csv-sepliste-id-by-handle*)) (if eintrag (setq *csv-sepliste-id-by-handle* - (subst (cons handle (append (cdr eintrag) (list (cons lfdnr id)))) + (subst (cons handle (append (cdr eintrag) (list (cons lfdnr ename)))) eintrag *csv-sepliste-id-by-handle*)) (setq *csv-sepliste-id-by-handle* - (cons (cons handle (list (cons lfdnr id))) *csv-sepliste-id-by-handle*)) + (cons (cons handle (list (cons lfdnr ename))) *csv-sepliste-id-by-handle*)) ) - id + ename ) -;; --- Abstands-Schwelle (mm) fuer die Meldung einer auffaelligen Zuordnung --- -;; Oberhalb dieses Abstands zwischen sepliste-Koordinate und tatsaechlicher -;; Lage der zugehoerigen Kopie wird gemeldet (die Zuordnung selbst bleibt -;; gueltig, siehe csv:sepliste-paare). Grosszuegig angesetzt: der SEP-Eintrag -;; nennt den Einfuegepunkt des Trennstegs (Staustrecke_Separator_SP_300_mm), -;; die Kopie das darin steckende Sensor-Symbol - ein Versatz in dieser -;; Groessenordnung ist normal, benachbarte Kettenglieder liegen dagegen -;; deutlich weiter auseinander (Staustrecken-Module ab 500 mm). -(defun csv:sepliste-abstand-warnung-mm () 500.0) +;; --- XDATA-App der sepliste eines Wrappers aus seinem Blocknamen --- +(defun csv:sepliste-app (bname) + (cond + ((wcmatch (strcase bname) "VF_*") "SSG_VF_SEP") + ((wcmatch (strcase bname) "GF_*") "SSG_GF_SEP") + (t nil)) +) -;; --- sepliste-Eintraege und ausgepackte Kopien einander zuordnen --- +;; --- sepliste eines Wrappers lesen --- +;; Rueckgabe: ((typ lfdnr x y) ...) wie ssg-sepliste-xdata-lesen, oder nil +;; (kein VF_n/GF_n, Altbestand ohne sepliste-XDATA). +(defun csv:sepliste-lesen (wrapper / app) + (setq app (csv:sepliste-app (cdr (assoc 2 (entget wrapper))))) + (if (and app (car (atoms-family 1 '("SSG-SEPLISTE-XDATA-LESEN")))) + (ssg-sepliste-xdata-lesen wrapper app)) +) + +;; --- sepliste-Eintraege den Bausteinen der geordneten Kette zuordnen --- ;; eintraege = ((typ lfdnr x y) ...) wie ssg-sepliste-xdata-lesen sie liefert. -;; proxies = ((typ x y) ...) - Reihenfolge beliebig, der INDEX in dieser -;; Liste (0-basiert) ist der Schluessel im Ergebnis. -;; Rueckgabe: ((lfdnr index abstand) ...), aufsteigend nach lfdnr; Eintraege -;; ohne Partner fehlen darin. +;; typen = Bausteintypen der geordneten Kette (csv:kette-typ, Kettenfolge). +;; Rueckgabe: ((lfdnr . index) ...), aufsteigend nach lfdnr; index = 0-basiert +;; in typen. Eintraege ohne Partner fehlen darin. ;; -;; Zugeordnet wird nur innerhalb desselben Typmarkers (AS/ES/SEP) und -;; EXKLUSIV: alle Paare eines Typs werden nach Abstand sortiert, das kuerzeste -;; zuerst genommen, danach sind Eintrag UND Kopie verbraucht. Das ist bewusst -;; nicht "je Eintrag die naechste Kopie" (so macht es map_separator_kette_items -;; in export_csv.py): waehlen zwei Eintraege dieselbe Kopie, bleibt eine Kopie -;; ohne Eintrag und ein Eintrag ohne ID. Die exklusive Variante ist ausserdem -;; gegen EINEN fehlerhaften Koordinatenwert unempfindlich - alle uebrigen -;; Eintraege finden ihre Kopie ueber den kurzen Abstand zuerst, der fehlerhafte -;; bekommt den Rest zugeteilt (mit entsprechend grossem Abstand, den -;; csv:sepliste-ids-setzen meldet). +;; Zuordnung ueber die REIHENFOLGE je Typ: das k-te AS/SEP/ES der sepliste +;; (nach lfdnr) ist das k-te AS/SEP/ES der Kette. Die sepliste entsteht beim +;; Bau aus einer entnext-Wanderung (ssg-sepliste-sammeln), also in +;; Baureihenfolge, und gebaut wird in Foerderrichtung - dieselbe Folge, die +;; csv:kette-ordnen ueber die KS rekonstruiert. Die x/y-Koordinaten der +;; sepliste werden NICHT benutzt (sie koennen beim Bau gespiegelt sein, siehe +;; ssg-sepliste-sammeln). ;; -;; Die Sortierung ist eine STRIKTE Totalordnung (Abstand, dann lfdnr, dann -;; Index): vl-sort wirft Elemente weg, die unter der Vergleichsfunktion als -;; gleich gelten - zwei Paare mit identischem Abstand wuerden sonst zu einem -;; verschmelzen und ein Eintrag bliebe ohne ID. -;; -;; Reine Rechenfunktion (keine Zeichnung, keine Datei) - getestet in -;; tests/test_unit.lsp (tu-tests-export, Testfaelle "csv:sepliste-paare/*"). -(defun csv:sepliste-paare (eintraege proxies / kandidaten e p i dx dy - benutzt-e benutzt-p erg) - (setq kandidaten nil) - (foreach e eintraege - (setq i 0) - (foreach p proxies - (if (= (nth 0 e) (nth 0 p)) - (progn - (setq dx (- (nth 2 e) (nth 1 p))) - (setq dy (- (nth 3 e) (nth 2 p))) - (setq kandidaten - (cons (list (sqrt (+ (* dx dx) (* dy dy))) (nth 1 e) i) kandidaten)) - ) - ) - (setq i (1+ i)) - ) - ) - (setq kandidaten - (vl-sort kandidaten - (function (lambda (a b) - (cond - ((/= (nth 0 a) (nth 0 b)) (< (nth 0 a) (nth 0 b))) - ((/= (nth 1 a) (nth 1 b)) (< (nth 1 a) (nth 1 b))) - (t (< (nth 2 a) (nth 2 b))) - ))))) - (setq erg nil benutzt-e nil benutzt-p nil) - (foreach k kandidaten - (if (and (not (member (nth 1 k) benutzt-e)) - (not (member (nth 2 k) benutzt-p))) - (progn - (setq benutzt-e (cons (nth 1 k) benutzt-e)) - (setq benutzt-p (cons (nth 2 k) benutzt-p)) - (setq erg (cons (list (nth 1 k) (nth 2 k) (nth 0 k)) erg)) - ) - ) +;; Reine Rechenfunktion - getestet in tests/test_unit.lsp. +(defun csv:sepliste-kette-paare (eintraege typen / erg typ lfdnrs idxs i x) + (foreach typ '("AS" "SEP" "ES") + (setq lfdnrs nil) + (foreach x eintraege + (if (= (car x) typ) (setq lfdnrs (cons (cadr x) lfdnrs)))) + (setq lfdnrs (vl-sort lfdnrs '<)) + (setq idxs nil i 0) + (foreach x typen + (if (= x typ) (setq idxs (cons i idxs))) + (setq i (1+ i))) + (setq idxs (reverse idxs)) + (while (and lfdnrs idxs) + (setq erg (cons (cons (car lfdnrs) (car idxs)) erg)) + (setq lfdnrs (cdr lfdnrs) idxs (cdr idxs))) ) (vl-sort erg (function (lambda (a b) (< (car a) (car b))))) ) -;; --- Nach der ID-Vergabe: ID jeder Kopie an ihren sepliste-Eintrag haengen --- -;; proxy-liste = Records (proxy-ename wrapper-ename typ (x y z) trennsteg-ent) -;; aus csv:sep-proxies-erzeugen (das 5. Feld wird hier nicht gebraucht). -;; MUSS nach ssg-id-check-all laufen: vorher hat eine frisch erzeugte Kopie -;; noch keine ID. -;; -;; Ergebnis steht in *csv-sepliste-id-by-handle*; csv:block-to-json schreibt -;; es als Feld "id" in jeden sepliste-Eintrag. Damit muss der Python-Teil die -;; Kette nicht mehr ueber Koordinaten-Naehe auf reale Teile zurueckrechnen - -;; die Zuordnung steht bereits im Export. -;; Rueckgabe: Anzahl der Eintraege, die eine ID bekommen haben. -(defun csv:sepliste-ids-setzen (proxy-liste / wrapper-enames rec wrapper ed bname - handle app sepliste eigene proxies paare paar - proxy-rec id typen e relevant gesetzt ohne-id fehlend) - (setq gesetzt 0 ohne-id 0) - ;; Wrapper einsammeln (jeder nur einmal, in Reihenfolge seiner ersten Kopie). - (setq wrapper-enames nil) - (foreach rec proxy-liste - (if (not (member (nth 1 rec) wrapper-enames)) - (setq wrapper-enames (cons (nth 1 rec) wrapper-enames)))) - (foreach wrapper (reverse wrapper-enames) - (setq ed (entget wrapper)) - (setq bname (cdr (assoc 2 ed))) - (setq handle (cdr (assoc 5 ed))) - (setq app - (cond - ((wcmatch (strcase bname) "VF_*") "SSG_VF_SEP") - ((wcmatch (strcase bname) "GF_*") "SSG_GF_SEP") - (t nil))) - (setq sepliste - (if (and app (car (atoms-family 1 '("SSG-SEPLISTE-XDATA-LESEN")))) - (ssg-sepliste-xdata-lesen wrapper app))) - (if sepliste - (progn - ;; Nur die Kopien DIESES Wrappers, in Erzeugungsreihenfolge - der Index - ;; im Ergebnis von csv:sepliste-paare zeigt in genau diese Liste. - (setq eigene nil) - (foreach rec proxy-liste - (if (eq (nth 1 rec) wrapper) (setq eigene (cons rec eigene)))) - (setq eigene (reverse eigene)) - (setq proxies - (mapcar (function (lambda (r) (list (nth 2 r) (car (nth 3 r)) (cadr (nth 3 r))))) - eigene)) - (setq paare (csv:sepliste-paare sepliste proxies)) - (foreach paar paare - (setq proxy-rec (nth (nth 1 paar) eigene)) - (setq id (cdr (assoc "ID" (ssg-attrib-read (nth 0 proxy-rec))))) - (if (and id (> (strlen id) 0)) - (progn - (csv:sepliste-id-merken handle (nth 0 paar) id) - (setq gesetzt (1+ gesetzt)) - ) - (setq ohne-id (1+ ohne-id)) - ) - ;; Auffaellig weit auseinander: die sepliste-Koordinate passt nicht - ;; zur tatsaechlichen Lage des Bauteils (bekannter Fehlerfall: - ;; gespiegelte x-Koordinate beim Bau). Die Zuordnung bleibt gueltig - - ;; sie entsteht exklusiv, nicht ueber diesen Abstand -, wird aber - ;; gemeldet, damit die Ursache nicht unbemerkt bleibt. - (if (> (nth 2 paar) (csv:sepliste-abstand-warnung-mm)) - (princ (ssg-textf "exp-sepliste-abstand" - (list bname (nth 2 proxy-rec) (itoa (nth 0 paar)) - (rtos (nth 2 paar) 2 0)))) - ) - ) - ;; Fehlende Partner NUR unter den Typen zaehlen, die diese Liste - ;; ueberhaupt bedient: AS/ES kommen hier nicht vor (dafuer gibt es die - ;; Marker, siehe csv:asmark-ids-setzen), ihre Eintraege waeren sonst - ;; jedes Mal als "ohne Kopie" gemeldet. - (setq typen nil) - (foreach rec eigene - (if (not (member (nth 2 rec) typen)) (setq typen (cons (nth 2 rec) typen)))) - (setq relevant 0) - (foreach e sepliste - (if (member (nth 0 e) typen) (setq relevant (1+ relevant)))) - (setq fehlend (- relevant (length paare))) - (if (> fehlend 0) - (princ (ssg-textf "exp-sepliste-ohne-proxy" (list bname (itoa fehlend))))) - ) - ) - ) - (if (> gesetzt 0) - (princ (ssg-textf "exp-sepliste-ids" (list (itoa gesetzt))))) - (if (> ohne-id 0) - (princ (ssg-textf "exp-sepliste-ohne-id" (list (itoa ohne-id))))) - gesetzt -) - ;; =========================================================================== ;; Foerdereinheit/Gefaelleeinheit-Gruppierung (Modus group_vf_gf) ;; =========================================================================== @@ -1663,8 +1494,9 @@ (defun csv:kette-toleranz-mm () 2.0) ;; --- Bausteintyp aus dem Blocknamen --- -;; "AS"/"ES"/"SEP" wie csv:proxy-typ, sonst die Familien aus -;; csv:element-teileart (BOGEN/STRECKENELEMENT/UMLENKSTATION/MOTORSTATION). +;; EINZIGER Typ-Klassifikator der Kette: "AS"/"ES" (Schleuselemente), "SEP" +;; (Trennsteg), sonst die Familien aus csv:element-teileart (BOGEN/ +;; STRECKENELEMENT/UMLENKSTATION/MOTORSTATION); nil fuer alles andere. (defun csv:kette-typ (bname / bn) (setq bn (strcase bname)) (cond @@ -1861,7 +1693,7 @@ ;; csv:run-export loescht ihn am Ende wieder, nur der Debug-Schalter ;; "csv_leave_proxies" (csv:hilfskopien-loeschen) laesst ihn zur Ansicht stehen. ;; -;; Die ID ist trotzdem stabil: csv:asmark-ids-setzen legt sie als reservierte +;; Die ID ist trotzdem stabil: csv:res-ids-schreiben legt sie als reservierte ;; ID (XDATA SSG_ID_RES, Schluessel "AS|1", siehe ssg-id-res-* in ssg_id.lsp) ;; am Wrapper ab, der naechste Export gibt sie dem neuen Marker wieder. Weil ;; ssg-id-max-in-ss die reservierten IDs mitzaehlt, vergibt ssg-id-check-all @@ -1970,10 +1802,10 @@ ;; --- Den Marker eines Kettenendes im Bestand suchen --- ;; Schluessel: Wrapper-ID + Typ + lfdnr (alles Strings). Bewusst NICHT ueber ;; die Position: eine Kette, die verschoben oder umgebaut wurde, soll ihre -;; TeileIds behalten. Ein Wrapper OHNE ID (frisch gebaut, ssg-id-check-all lief -;; noch nicht) findet folgerichtig nichts - seine Marker entstehen neu; die -;; ZUORDNUNG wird nach der ID-Vergabe nachgetragen (csv:asmark-ids-setzen), -;; sodass es beim naechsten Export passt. +;; TeileIds behalten. Die Wrapper-ID steht beim Suchen schon fest (erster +;; ssg-id-check-all in csv:export-vorbereiten); ein Wrapper ganz ohne +;; ID-Attribut (Altbestand) findet folgerichtig nichts - seine Marker +;; entstehen bei jedem Export neu. (defun csv:asmark-finden (bestand zuordnung typ lfdnr / m treffer) (foreach m bestand (if (and (null treffer) @@ -1993,67 +1825,53 @@ ) ;; --- Alle AS-/ES-Marker der Zeichnung neu aufbauen --- -;; Laeuft VOR ssg-id-check-all (csv:export-vorbereiten): neue Marker haben dann -;; noch kein ID-Attribut und werden dort nummeriert, uebernommene tragen die +;; Laeuft NACH dem ersten ssg-id-check-all (csv:export-vorbereiten): jeder +;; Wrapper traegt dann seine endgueltige ID, ZUORDNUNG wird hier also EINMAL +;; und richtig gesetzt. Neue Marker haben noch kein ID-Attribut und werden in +;; csv:ketten-verarbeiten in Kettenfolge nummeriert, uebernommene tragen die ;; reservierte ID des Wrappers (bzw. die ihres noch stehenden Vorgaengers). ;; ;; Ablauf je VF_n/GF_n-Wrapper mit sepliste: -;; 1. Weltlage aller verpackten AS-/ES-Elemente holen (csv:nested-welt-records) -;; 2. sie den AS-/ES-Eintraegen der sepliste zuordnen (csv:sepliste-paare - -;; dieselbe exklusive Zuordnung wie bei den Separator-Kopien; SEP-Eintraege -;; finden hier keinen Partner und bleiben unberuehrt) +;; 1. geordnete Kette holen (csv:kette-von) - AS/ES sind Kettenbausteine +;; 2. sepliste-Eintraege den Bausteinen zuordnen (csv:sepliste-kette-paare, +;; k-tes AS/ES der sepliste = k-tes AS/ES der Kette); nur AS/ES +;; bekommen hier einen Marker ;; 3. je Paar die bisherige ID holen (reservierte ID am Wrapper, sonst ein ;; noch stehender Marker - der wird geloescht) und den Marker an der ;; AKTUELLEN Lage neu setzen ;; Was danach im Bestand uebrig ist, gehoert zu keiner Kette mehr und wird ;; geloescht. ;; -;; Rueckgabe: Liste von Records (marker-ename wrapper-ename typ lfdnr) fuer -;; csv:asmark-ids-setzen. -(defun csv:asmark-aktualisieren ( / recs bestand blockname ms wrapper-enames rec - wrapper ed bname wrapper-id app sepliste eigene - proxies paare paar elem lfdnr typ alt alt-id - mark artinr m neu uebernommen verwaist ergebnis - reserviert) +;; Rueckgabe: Liste von Records (marker-ename wrapper-ename typ lfdnr +;; element-ent) - element-ent = das verpackte AS-/ES-Element (Schluessel fuer +;; die ID-Vergabe in Kettenfolge, csv:ketten-verarbeiten). +(defun csv:asmark-aktualisieren ( / bestand blockname ms wrapper wrapper-id + sepliste teile teil paar elem lfdnr typ alt + alt-id mark artinr m neu uebernommen verwaist + ergebnis reserviert) (setq neu 0 uebernommen 0 verwaist 0) - (setq recs (csv:nested-welt-records (csv:pattern-nested-schleus))) (setq bestand (csv:asmark-sammeln)) - (setq blockname (if recs (csv:asmark-blockdef-sicherstellen))) - (if blockname - (progn - (setq ms (vla-get-ModelSpace (vla-get-ActiveDocument (vlax-get-acad-object)))) - (setq wrapper-enames nil) - (foreach rec recs - (if (not (member (nth 0 rec) wrapper-enames)) - (setq wrapper-enames (cons (nth 0 rec) wrapper-enames)))) - (foreach wrapper (reverse wrapper-enames) - (setq ed (entget wrapper)) - (setq bname (cdr (assoc 2 ed))) + (setq ms (vla-get-ModelSpace (vla-get-ActiveDocument (vlax-get-acad-object)))) + ;; Absteigende Wrapper-ID: die Marker entstehen in derselben Reihenfolge wie + ;; frueher (Zeichnungsreihenfolge), damit die Laufnummer der Bezeichnung + ;; ("ILS Ausschleuselement :1"), die der Export aus der Erzeugungsreihenfolge + ;; zaehlt, je Kette gleich bleibt. + (foreach wrapper (reverse (csv:wrapper-nach-id)) + (setq sepliste (csv:sepliste-lesen wrapper)) + (if (and sepliste (null blockname)) + (setq blockname (csv:asmark-blockdef-sicherstellen))) + (if (and sepliste blockname) + (progn (setq wrapper-id (cond ((cdr (assoc "ID" (ssg-attrib-read wrapper)))) (""))) - (setq app - (cond - ((wcmatch (strcase bname) "VF_*") "SSG_VF_SEP") - ((wcmatch (strcase bname) "GF_*") "SSG_GF_SEP") - (t nil))) - (setq sepliste - (if (and app (car (atoms-family 1 '("SSG-SEPLISTE-XDATA-LESEN")))) - (ssg-sepliste-xdata-lesen wrapper app))) - (if sepliste - (progn - (setq eigene nil) - (foreach rec recs - (if (eq (nth 0 rec) wrapper) (setq eigene (cons rec eigene)))) - (setq eigene (reverse eigene)) - (setq proxies - (mapcar (function (lambda (r) - (list (csv:proxy-typ (nth 2 r)) (car (nth 3 r)) (cadr (nth 3 r))))) - eigene)) - (setq paare (csv:sepliste-paare sepliste proxies)) - (setq reserviert (ssg-id-res-lesen wrapper)) - (foreach paar paare - (setq lfdnr (nth 0 paar)) - (setq elem (nth (nth 1 paar) eigene)) - (setq typ (csv:proxy-typ (nth 2 elem))) + (setq teile (csv:kette-von wrapper)) + (setq reserviert (ssg-id-res-lesen wrapper)) + (foreach paar (csv:sepliste-kette-paare sepliste (mapcar 'car teile)) + (setq lfdnr (car paar)) + (setq teil (nth (cdr paar) teile)) + (setq typ (car teil)) + (if (member typ '("AS" "ES")) + (progn + (setq elem (nth 1 teil)) ;; ID-Quelle: reservierte ID am Wrapper, sonst (Altbestand / ;; csv_leave_proxies) der noch stehende Marker. (setq alt (csv:asmark-finden bestand wrapper-id typ (itoa lfdnr))) @@ -2075,7 +1893,7 @@ ;; waere als Positionszeichen unbrauchbar. Orientierung folgt ;; dagegen dem Element (auch bei geneigter Kette). (setq mark (csv:block-einfuegen-welt ms blockname (nth 3 elem) (nth 4 elem) - 1.0 1.0 1.0 bname)) + 1.0 1.0 1.0 (cdr (assoc 2 (entget wrapper))))) (if mark (progn ;; ARTINR nur lesen, wenn der Sub-INSERT ueberhaupt ATTRIBs @@ -2101,8 +1919,6 @@ ;; Kreisel. Siehe csv:baustein-merken. (if (not (csv:export-separator-track-p)) (csv:baustein-merken mark elem)) - ;; 5. Feld: das verpackte Element (Schluessel fuer die ID- - ;; Vergabe in Kettenfolge, csv:ketten-ids-vergeben). (setq ergebnis (cons (list mark wrapper typ lfdnr (nth 1 elem)) ergebnis)) ;; ID nach dem Export als reservierte ID am Wrapper ablegen ;; (csv:res-ids-schreiben). @@ -2128,43 +1944,6 @@ (reverse ergebnis) ) -;; --- Nach der ID-Vergabe: Marker-ID an den sepliste-Eintrag haengen --- -;; marker-liste = Records (marker-ename wrapper-ename typ lfdnr) aus -;; csv:asmark-aktualisieren. Zwei Aufgaben: -;; 1. ZUORDNUNG nachtragen - beim Setzen der Marker kann der Wrapper noch -;; keine ID gehabt haben (frisch gebaute Kette). Ohne sie faende der -;; NAECHSTE Export den Marker nicht wieder und legte ihn samt neuer ID -;; erneut an; die TeileId waere dann doch nicht stabil. -;; 2. die ID des Markers unter *csv-sepliste-id-by-handle* eintragen, damit -;; csv:block-to-json sie als Feld "id" am AS-/ES-Eintrag schreibt. -;; Die ID selbst legt csv:res-ids-schreiben als reservierte ID am Wrapper ab. -;; Rueckgabe: Anzahl der Eintraege mit ID. -(defun csv:asmark-ids-setzen (marker-liste / rec mark wrapper handle wrapper-id id - gesetzt ohne-id) - (setq gesetzt 0 ohne-id 0) - (foreach rec marker-liste - (setq mark (nth 0 rec)) - (setq wrapper (nth 1 rec)) - (setq handle (cdr (assoc 5 (entget wrapper)))) - (setq wrapper-id (cdr (assoc "ID" (ssg-attrib-read wrapper)))) - (if (and wrapper-id (> (strlen wrapper-id) 0)) - (ssg-attrib-set-on mark (list (cons "ZUORDNUNG" wrapper-id)))) - (setq id (cdr (assoc "ID" (ssg-attrib-read mark)))) - (if (and id (> (strlen id) 0)) - (progn - (csv:sepliste-id-merken handle (nth 3 rec) id) - (setq gesetzt (1+ gesetzt)) - ) - (setq ohne-id (1+ ohne-id)) - ) - ) - (if (> gesetzt 0) - (princ (ssg-textf "exp-asmark-ids" (list (itoa gesetzt))))) - (if (> ohne-id 0) - (princ (ssg-textf "exp-sepliste-ohne-id" (list (itoa ohne-id))))) - gesetzt -) - ;; --- Temporaere Kopien (csv:sep-proxies-erzeugen / ;; csv:schleus-proxies-erzeugen) nach dem Export wieder entfernen --- ;; proxy-liste = Records (proxy-ename wrapper-ename typ (x y z)), nur der car @@ -2200,19 +1979,22 @@ ;; Attribut-Schema. Alle ATTDEFs unsichtbar (wie *csv-asmark-attrib-defs*). ;; ID - fuellt ssg-id-check-all -;; ZUORDNUNG - ID des VF_n/GF_n-Wrappers (csv:unitmark-ids-setzen traegt -;; sie nach der ID-Vergabe nach) +;; ZUORDNUNG - ID des VF_n/GF_n-Wrappers (beim Erzeugen gesetzt - die +;; Wrapper-ID steht nach dem ersten ssg-id-check-all fest) ;; TYP - "VF" (Foerdereinheit) oder "GF" (Gefaelleeinheit) ;; LFDNR - 1-basierte Einheiten-Nummer INNERHALB des Wrappers ;; LAENGE_m - Summe der Laengen K1->K2 aller enthaltenen Streckenelemente ;; (Meter) - geometrisch, weil die Staustrecke beim Bau per ;; Laenge skaliert wird und ihr Attribut das nicht wiedergibt ;; ANZAHL_BOEGEN - Anzahl der enthaltenen Boegen (inkl. Vario-Kurven) -;; K1/K2 - KS_EIN des ERSTEN bzw. KS_AUS des LETZTEN Bausteins, als -;; csv:trans-encode-String (NICHT rtos, siehe DIMZIN-Hinweis) +;; K1/K2 (KS_EIN des ERSTEN bzw. KS_AUS des LETZTEN Bausteins) stehen NICHT als +;; Attribut am Marker, sondern wie bei jedem Stellvertreter in +;; *csv-baustein-by-handle* (csv:geometrie-merken) - csv:k-liste liest sie dort. +;; Aeltere Blockdefinitionen in Zeichnungen haben noch K1/K2-ATTDEFs; die +;; bleiben leer und werden nicht gelesen. (setq *csv-unitmark-attrib-defs* '(("ID" "") ("ZUORDNUNG" "") ("TYP" "") ("LFDNR" "") - ("LAENGE_m" "") ("ANZAHL_BOEGEN" "") ("K1" "") ("K2" "")) + ("LAENGE_m" "") ("ANZAHL_BOEGEN" "")) ) ;; --- Blockdefinition des Proxies bei Bedarf anlegen --- @@ -2310,20 +2092,14 @@ (cons "TYP" typ) (cons "LFDNR" (itoa lfdnr)) (cons "LAENGE_m" (rtos (/ laenge-mm 1000.0) 2 3)) - (cons "ANZAHL_BOEGEN" (itoa anzahl-boegen)) - (cons "K1" (if k1 (csv:trans-encode (car k1) (cadr k1) (caddr k1)) "")) - (cons "K2" (if k2 (csv:trans-encode (car k2) (cadr k2) (caddr k2)) "")))) + (cons "ANZAHL_BOEGEN" (itoa anzahl-boegen)))) (ssg-layer-setzen mark "ases_marker") ;; Export-Geometrie wie bei jedem Stellvertreter: die gemessene 3D- - ;; Vereinigung der Bausteine statt der Box des flachen Rechtecks, - ;; K1/K2 wie in den Attributen (siehe *csv-baustein-by-handle*). - (setq *csv-baustein-by-handle* - (cons (list (cdr (assoc 5 (entget mark))) - (csv:k-liste-kodieren - (append (if k1 (list (cons "K1" k1))) - (if k2 (list (cons "K2" k2))))) - bbox) - *csv-baustein-by-handle*)))) + ;; Vereinigung der Bausteine statt der Box des flachen Rechtecks. + (csv:geometrie-merken mark + (append (if k1 (list (cons "K1" k1))) + (if k2 (list (cons "K2" k2)))) + bbox))) ) ) mark @@ -2413,10 +2189,26 @@ naechste-id ) +;; --- Geordnete Kette eines Wrappers, je Exportlauf nur einmal berechnet --- +;; csv:kette-teile + csv:kette-ordnen (Rueckgabe wie csv:kette-ordnen). Beide +;; Nutzer - csv:asmark-aktualisieren und csv:ketten-verarbeiten - sehen damit +;; garantiert DIESELBE Kette (und eine Luecken-Meldung erscheint nur einmal). +;; *csv-ketten* = ((wrapper-ename . teile) ...), csv:export-vorbereiten setzt +;; es zu Beginn jedes Laufs zurueck. +(setq *csv-ketten* nil) +(defun csv:kette-von (wrapper / e) + (if (setq e (assoc wrapper *csv-ketten*)) + (cdr e) + (progn + (setq e (csv:kette-ordnen (csv:kette-teile wrapper) (cdr (assoc 2 (entget wrapper))))) + (setq *csv-ketten* (cons (cons wrapper e) *csv-ketten*)) + e)) +) + ;; --- Ketten verarbeiten: Einheiten-Proxies bauen und IDs in Kettenfolge --- -;; Laeuft VOR ssg-id-check-all (csv:export-vorbereiten). Je VF_n/GF_n-Wrapper -;; (nach Wrapper-ID aufsteigend): -;; 1. Bausteine holen und als Vektorkette ordnen (csv:kette-teile/-ordnen) +;; Laeuft zwischen den beiden ssg-id-check-all-Laeufen (csv:export- +;; vorbereiten). Je VF_n/GF_n-Wrapper (nach Wrapper-ID aufsteigend): +;; 1. geordnete Kette holen (csv:kette-von) ;; 2. nur Modus group_vf_gf: in Einheiten schneiden (csv:kette-einheiten) ;; und je Einheit einen Proxy setzen (csv:unitmark-einfuegen) ;; 3. alle Export-Objekte dieser Kette, die noch keine ID haben, entlang der @@ -2429,9 +2221,13 @@ ;; Die Export-CSV ist nach TeileId sortiert, damit stehen die Zeilen jeder ;; Kette jetzt von AS nach ES. Bereits vorhandene IDs (AS/ES-Marker mit ;; reservierter ID vom Wrapper) bleiben unangetastet. -;; Nebenbei (in BEIDEN Modi): Streckentyp VF/GF jeder Separator-Kopie aus -;; ihrer Einheit (csv:kette-sep-typen) nach *cs-sep-typ-by-handle* - -;; csv:block-to-json schreibt ihn als "strecke_typ" in die Separator-Zeile. +;; Nebenbei (in BEIDEN Modi), aus derselben Kette: +;; - Streckentyp VF/GF jeder Separator-Kopie aus ihrer Einheit +;; (csv:kette-sep-typen) nach *cs-sep-typ-by-handle* - csv:block-to-json +;; schreibt ihn als "strecke_typ" in die Separator-Zeile +;; - Stellvertreter jedes sepliste-Eintrags (csv:sepliste-kette-paare) nach +;; *csv-sepliste-id-by-handle* - csv:block-to-json schreibt dessen ID als +;; Feld "id" in den sepliste-Eintrag ;; sep-proxies/element-proxies/asmarks = die Stellvertreter-Listen dieses ;; Laufs (letztes Feld jedes Records = verpackter Baustein). ;; Rueckgabe: Einheiten-Proxy-Records (proxy-ename wrapper-ename typ lfdnr) - @@ -2439,8 +2235,11 @@ (defun csv:ketten-verarbeiten (sep-proxies element-proxies asmarks / alt gruppiert blockname ms traeger r naechste-id wrapper teile einheit lfdnr mark starts - i teil u tr ergebnis res key sep-typen) + i teil u tr ergebnis res key sep-typen + sepliste paar sl-gesetzt sl-fehlend) (setq *cs-sep-typ-by-handle* nil) + (setq *csv-sepliste-id-by-handle* nil) + (setq sl-gesetzt 0) (setq alt (csv:unitmark-bestand-loeschen)) (setq gruppiert (= *csv-export-modus* "group_vf_gf")) (if gruppiert (setq blockname (csv:unitmark-blockdef-sicherstellen))) @@ -2454,8 +2253,7 @@ (if (nth 4 r) (setq traeger (cons (cons (list (nth 1 r) (nth 4 r)) (nth 0 r)) traeger)))) (setq naechste-id (ssg-id-max)) (foreach wrapper (csv:wrapper-nach-id) - (setq teile (csv:kette-ordnen (csv:kette-teile wrapper) - (cdr (assoc 2 (entget wrapper))))) + (setq teile (csv:kette-von wrapper)) (setq res (ssg-id-res-lesen wrapper)) (setq sep-typen (csv:kette-sep-typen (mapcar 'car teile))) ;; Einheiten (nur gruppiert); starts = ((erster-index key . proxy) ...) @@ -2494,30 +2292,31 @@ (setq naechste-id (csv:id-vergeben tr naechste-id (cdr (assoc key res)))) (csv:res-merken wrapper key tr))))) (setq i (1+ i))) + ;; sepliste-Eintraege -> Stellvertreter (Kettenfolge statt Koordinaten). + (setq sepliste (csv:sepliste-lesen wrapper)) + (if sepliste + (progn + (setq sl-fehlend (length sepliste)) + (foreach paar (csv:sepliste-kette-paare sepliste (mapcar 'car teile)) + (if (setq tr (cdr (assoc (list wrapper (nth 1 (nth 1 (nth (cdr paar) teile)))) + traeger))) + (progn + (csv:sepliste-id-merken (cdr (assoc 5 (entget wrapper))) (car paar) tr) + (setq sl-gesetzt (1+ sl-gesetzt) sl-fehlend (1- sl-fehlend))))) + ;; Mehr sepliste-Eintraege als Kettenbausteine mit Stellvertreter: + ;; sepliste und Kette passen nicht zusammen (Kette umgebaut, Luecke in + ;; der KS-Kette) - die uebrigen Eintraege bleiben ohne ID. + (if (> sl-fehlend 0) + (princ (ssg-textf "exp-sepliste-ohne-proxy" + (list (cdr (assoc 2 (entget wrapper))) (itoa sl-fehlend))))))) ) + (if (> sl-gesetzt 0) + (princ (ssg-textf "exp-sepliste-ids" (list (itoa sl-gesetzt))))) (if gruppiert (princ (ssg-textf "exp-unitmark-bilanz" (list (itoa (length ergebnis)) (itoa alt))))) (reverse ergebnis) ) -;; --- Nach der ID-Vergabe: ZUORDNUNG nachtragen --- -;; Beim Erzeugen kann ein frisch gebauter Wrapper noch keine ID gehabt haben; -;; erst nach ssg-id-check-all steht sie fest. -(defun csv:unitmark-ids-setzen (marker-liste / rec wrapper-id gesetzt) - (setq gesetzt 0) - (foreach rec marker-liste - (setq wrapper-id (cdr (assoc "ID" (ssg-attrib-read (nth 1 rec))))) - (if (and wrapper-id (> (strlen wrapper-id) 0)) - (ssg-attrib-set-on (nth 0 rec) (list (cons "ZUORDNUNG" wrapper-id)))) - (if (> (strlen (cond ((cdr (assoc "ID" (ssg-attrib-read (nth 0 rec))))) (""))) 0) - (setq gesetzt (1+ gesetzt))) - ) - (if (> gesetzt 0) - (princ (ssg-textf "exp-unitmark-ids" (list (itoa gesetzt))))) - gesetzt -) - - ;; --- Relevante Bloecke sammeln --- ;; Filtert INSERT-Entities auf exportierbare Bloecke: ;; KR_* = Kreisel Compound-Bloecke @@ -2639,21 +2438,38 @@ ;; label = Anzeigename (z.B. "EXPORTCSV") ;; --- Export-Vorbereitung: Zeichnungszustand fuer den Export herrichten --- ;; Aus csv:run-export ausgelagert: alle Schritte, die VOR dem Sammeln/Schreiben -;; den Zeichnungszustand herrichten - Separator-Proxies einpacken, IDs -;; sicherstellen, Proxy-Zuordnung setzen, Omniflo-Attribute (HOEHE/DREHUNG) -;; aktualisieren, Separator-/Scanner-Zuordnung pruefen, global regenerieren und -;; die bbox-Fehlerdiagnose zuruecksetzen. Rueckgabe: (sep-proxies -;; element-proxies unitmarks asmarks) - alle temporaer, der Aufrufer entfernt -;; sie am Ende wieder. label dient nur den Meldungen. +;; den Zeichnungszustand herrichten - IDs sicherstellen, Stellvertreter der +;; verpackten Kettenbausteine erzeugen und nummerieren, Omniflo-Attribute +;; (HOEHE/DREHUNG) aktualisieren, Separator-/Scanner-Zuordnung pruefen, global +;; regenerieren und die bbox-Fehlerdiagnose zuruecksetzen. Rueckgabe: +;; (sep-proxies element-proxies unitmarks asmarks) - alle temporaer, der +;; Aufrufer entfernt sie am Ende wieder. label dient nur den Meldungen. +;; +;; Reihenfolge (jeder Wert wird genau EINMAL gesetzt): +;; 1. ssg-id-check-all - Wrapper-IDs endgueltig (fehlende vergeben, +;; Duplikate korrigiert), BEVOR ein Stellvertreter entsteht. Damit kann +;; jeder Stellvertreter seine ZUORDNUNG beim Erzeugen bekommen. +;; 2. Stellvertreter erzeugen (Separator-Kopien, AS-/ES-Marker, Element- +;; Kopien) - ZUORDNUNG direkt dabei +;; 3. csv:ketten-verarbeiten - Einheiten-Proxies, IDs in Kettenfolge, +;; Separator-Typ und sepliste-Stellvertreter aus derselben Kette +;; 4. ssg-id-check-all - Restnummerierung (z.B. Kreisel-Separator ohne +;; Kette) und Duplikat-Korrektur unter den neuen Stellvertretern (defun csv:export-vorbereiten (label / sep-proxies asmarks element-proxies unitmarks) - ;; Baustein-Geometrie der Stellvertreter (Separator-Kopie, AS-/ES-Marker) - ;; wird beim Erzeugen neu gemerkt - Stand eines frueheren Laufs verwerfen. + ;; Stand eines frueheren Laufs verwerfen: Baustein-Geometrie der + ;; Stellvertreter, Anmeldungen fuer reservierte IDs (csv:res-merken), + ;; geordnete Ketten (csv:kette-von), sepliste-Stellvertreter. (setq *csv-baustein-by-handle* nil) - ;; Anmeldungen fuer die reservierten IDs (csv:res-merken) dieses Laufs. (setq *csv-res-sammlung* nil) - ;; Verpackte Separator_SP-Symbole (siehe csv:sep-proxies-erzeugen) VOR der - ;; ID-Vergabe durch temporaere, echte Kopien ersetzen - sie werden am Ende - ;; von csv:run-export wieder entfernt (csv:sep-proxies-loeschen). + (setq *csv-ketten* nil) + (setq *csv-sepliste-id-by-handle* nil) + ;; 1. IDs aller Bloecke sicherstellen und Duplikate korrigieren - hier nur + ;; fuer die dauerhaften Bloecke, Stellvertreter gibt es noch keine. + (princ (ssg-textf "exp-check-ids" (list label))) + (ssg-id-check-all) + ;; 2. Verpackte Separator_SP-Symbole durch temporaere, echte Kopien + ;; vertreten (csv:sep-proxies-erzeugen) - am Ende von csv:run-export wieder + ;; entfernt (csv:sep-proxies-loeschen). (setq sep-proxies (csv:sep-proxies-erzeugen)) ;; Die AS-/ES-Kettenenden stecken ebenfalls verpackt im Wrapper und ;; bekommen einen temporaeren Marker (ZZ_ASES_MARKER); ihre ID bleibt als @@ -2662,42 +2478,22 @@ ;; Die uebrigen Kettenbausteine (Boegen, gerade Foerderer-/Gefaellestrecken- ;; Segmente, Umlenk-/Motorstation) als temporaere Kopien - NUR im Modus ;; all_elements, der sie als eigene Zeilen exportiert (siehe csv:element- - ;; proxies-erzeugen). MUSS wie sep-proxies VOR ssg-id-check-all stehen, - ;; damit die frischen Kopien in dessen Lauf mit nummeriert werden (siehe - ;; ssg-id-collect-blocks, ssg_id.lsp). + ;; proxies-erzeugen). (setq element-proxies (if (csv:export-alle-elemente-p) (csv:element-proxies-erzeugen))) - ;; Ketten verarbeiten: Foerdereinheit-/Gefaelleeinheit-Proxies (ZZ_UNIT_ - ;; MARKER, nur Modus group_vf_gf) aus den KS-verketteten Bausteinen bauen und + ;; 3. Ketten verarbeiten: Foerdereinheit-/Gefaelleeinheit-Proxies (ZZ_UNIT_ + ;; MARKER, nur Modus group_vf_gf) aus den KS-verketteten Bausteinen bauen, ;; alle neuen Export-Objekte jeder Kette von AS nach ES aufsteigend - ;; nummerieren - VOR ssg-id-check-all, siehe csv:ketten-verarbeiten. + ;; nummerieren, Separator-Typ und sepliste-Stellvertreter eintragen. (setq unitmarks (csv:ketten-verarbeiten sep-proxies element-proxies asmarks)) - ;; Vor dem Export: IDs aller Bloecke sicherstellen und Duplikate korrigieren. - ;; Erst hier - also erst nachdem ALLE Kopien aus allen Wrapper-Bloecken - ;; stehen - wird ueberhaupt eine ID vergeben. ssg-id-check-all ermittelt - ;; dabei zuerst das globale Maximum der bereits vergebenen IDs (Phase 0) - ;; und nummeriert die noch ID-losen Kopien erst danach - sonst bekaemen sie - ;; IDs, die die Wrapper (VF_n/GF_n/Kreisel) selbst schon tragen. - (princ (ssg-textf "exp-check-ids" (list label))) + ;; 4. Restnummerierung + Duplikat-Korrektur. ssg-id-check-all ermittelt + ;; zuerst das globale Maximum der vergebenen IDs (Phase 0) und nummeriert + ;; erst danach - neue IDs liegen damit ueber jeder vorhandenen. (ssg-id-check-all) - ;; Jetzt steht die ID jedes Wrappers fest: die bekannte (nicht geratene) - ;; ZUORDNUNG der verpackten Separatoren auf die Wrapper-ID setzen und fuer - ;; cs-zuordnung-lauf festnageln. Nur fuer die Separator-Kopien - die - ;; AS/ES-Marker sind keine Sensoren und werden nicht gezaehlt (ihre - ;; ZUORDNUNG setzt csv:asmark-ids-setzen). - (csv:sep-proxies-zuordnung-setzen sep-proxies) - ;; Ebenfalls erst jetzt moeglich: die ID jedes Bauteils an seinen sepliste- - ;; Eintrag haengen (csv:block-to-json schreibt sie als Feld "id" mit) - - ;; AS/ES ueber die Marker, SEP ueber die temporaeren Kopien. Beide schreiben - ;; in dieselbe Zuordnung, die hier einmal zurueckgesetzt wird. - (setq *csv-sepliste-id-by-handle* nil) - (csv:asmark-ids-setzen asmarks) - (csv:sepliste-ids-setzen sep-proxies) - (csv:unitmark-ids-setzen unitmarks) ;; IDs aller temporaeren Kettenobjekte als reservierte IDs am Wrapper ;; ablegen - der naechste Export gibt sie ihnen zurueck (csv:res-merken). (csv:res-ids-schreiben) - ;; Vor dem Export HOEHE/DREHUNG aller Omniflo-Elemente aktualisieren +;; Vor dem Export HOEHE/DREHUNG aller Omniflo-Elemente aktualisieren (princ (ssg-textf "exp-update-hoehe-drehung" (list label))) (omni:update-all-attribs) ;; Vor dem Export: Separator-/Scanner-Zuordnung pruefen und @@ -2877,7 +2673,7 @@ ;; Temporaere Separator-/Element-Kopien und AS-/ES-Marker wieder entfernen - ;; sie haben ihren Zweck (ID-Vergabe + JSON-Zeile ueber die obigen, ;; unveraenderten Sammel-Funktionen) erfuellt. Die AS-/ES-IDs bleiben als - ;; reservierte IDs am Wrapper (csv:asmark-ids-setzen). + ;; reservierte IDs am Wrapper (csv:res-ids-schreiben). (csv:sep-proxies-loeschen sep-proxies) (csv:hilfskopien-loeschen asmarks "exp-art-ases") (csv:hilfskopien-loeschen element-proxies "exp-art-element") diff --git a/doc/csv-export.md b/doc/csv-export.md index 1031f94..851fe4e 100644 --- a/doc/csv-export.md +++ b/doc/csv-export.md @@ -43,7 +43,14 @@ Datei: `Lisp/export.lsp`. `c:EXPORTCSV` ruft `csv:run-export` mit `include-bbox=T` auf (im Unterschied zu `c:EXPORTSIVAS`, das keine Bounding-Boxen braucht). -1. **`csv:sep-proxies-erzeugen`** — Separator_SP-Symbole, die beim +Jeder Wert wird dabei genau EINMAL gesetzt; die Reihenfolge sorgt dafuer, +dass er beim Setzen schon feststeht (`csv:export-vorbereiten`): + +1. **`ssg-id-check-all`** — ZUERST, noch bevor ein Stellvertreter existiert: + vergibt fehlende IDs und korrigiert Duplikate. Danach ist die ID jedes + Wrappers (`VF_n`/`GF_n`/`KREISEL_n`) endgueltig, die folgenden Schritte + koennen `ZUORDNUNG` direkt beim Erzeugen setzen. +2. **`csv:sep-proxies-erzeugen`** — Separator_SP-Symbole, die beim Zusammenbau von `KREISEL_n`/`ECKRAD_n`/`VF_n`/`GF_n` in die Compound-Blockdefinition verpackt wurden, sind fuer `(ssget "X" ...)` unsichtbar. Diese Funktion fuegt fuer jeden Fund eine ECHTE, temporaere @@ -51,32 +58,30 @@ Bounding-Boxen braucht). durchlaeuft danach ID-Vergabe und Export ganz normal ueber den freien- Separator-Pfad (Blockname-Muster `Separator_SP*` trifft zu) und bekommt dadurch eine echte `vla-getboundingbox`-Box, die das verpackte Original nie - haben koennte. Rueckgabe: Liste von Records - `(proxy-ename wrapper-ename typ (x y z))`. -1b. **`csv:asmark-aktualisieren`** — die AS-/ES-Enden derselben Kette + haben koennte. Dabei bekommt die Kopie `ZUORDNUNG = Wrapper-ID` (bekannt, + nicht geraten) und wird in `*cs-sep-fix-by-handle*` als "bekannt" + vermerkt: `count_sep_scan.lsp` und `export_csv.py` (Feld `zuordnung_fix`) + berechnen die Zuordnung EIGENSTAENDIGER Sensoren bei jedem Lauf + geometrisch neu und nehmen verpackte Separatoren damit davon aus. + Rueckgabe: Liste von Records `(proxy-ename wrapper-ename "SEP" (x y z) + trennsteg-ent)`. +2b. **`csv:asmark-aktualisieren`** — die AS-/ES-Enden derselben Kette (`AS_Element*`/`ES_Element*`, Muster `pattern_schleuselement`) bekommen - KEINE Kopie des Elements, sondern einen eigenen (ebenfalls temporaeren) Marker — siehe eigener - Abschnitt "Der AS/ES-Marker" unten. Beide Wege teilen sich die - Platzierungsrechnung (`csv:nested-welt-records` liefert die Weltlage jedes - verpackten Fundes, `csv:block-einfuegen-welt` setzt ein Objekt dorthin). -2. **`ssg-id-check-all`** — vergibt fehlende IDs (inkl. der frisch erzeugten - Proxies) und korrigiert Duplikate. Muss NACH den Proxies laufen, sonst - wuerden diese Kopien numerisch nie erfasst. `ssg-id-collect-blocks` - (`ssg_id.lsp`) erfasst dafuer auch `AS_Element*`/`ES_Element*`. -3. **`csv:sep-proxies-zuordnung-setzen`** — jetzt steht die ID jedes Wrappers - fest: die Proxy-Kopie bekommt `ZUORDNUNG = Wrapper-ID` fest zugewiesen - (nicht geraten) und wird zusaetzlich in `*cs-sep-fix-by-handle*` vermerkt, - damit `count_sep_scan.lsp` diesen Wert spaeter nicht per Geometrie - ueberschreibt. Nur fuer die Separator-Kopien — AS/ES sind keine Sensoren. -3b. **`csv:asmark-ids-setzen` + `csv:sepliste-ids-setzen`** — ebenfalls erst - jetzt moeglich: jedes Bauteil wird seinem `sepliste`-Eintrag zugeordnet und - seine ID unter `*csv-sepliste-id-by-handle*` (Key = Wrapper-Handle) - abgelegt; `csv:block-to-json` schreibt sie als Feld `id` in den Eintrag. - AS/ES laufen ueber die Marker (die ihre ID schon kennen, die Zuordnung - steht im Marker-Attribut `LFDNR`), SEP ueber die temporaeren Kopien und - `csv:sepliste-paare` — siehe eigener Abschnitt unten. `csv:asmark-ids-setzen` - traegt zusaetzlich die `ZUORDNUNG` (Wrapper-ID) am Marker nach; ohne sie - faende der naechste Export den Marker nicht wieder. + KEINE Kopie des Elements, sondern einen eigenen (ebenfalls temporaeren) + Marker, mit `ZUORDNUNG` direkt beim Erzeugen — siehe eigener Abschnitt + "Der AS/ES-Marker" unten. +2c. **`csv:element-proxies-erzeugen`** — nur Modus `all_elements`: Kopien der + uebrigen Kettenbausteine (Boegen, Streckenelemente, Umlenk-/Motorstation). +3. **`csv:ketten-verarbeiten`** — je `VF_n`/`GF_n` die KS-geordnete Kette + (`csv:kette-von`, einmal je Lauf berechnet) auswerten: Einheiten-Proxies + (Modus `group_vf_gf`), IDs in Kettenfolge, Separator-Typ VF/GF + (`csv:kette-sep-typen`) und die Stellvertreter der `sepliste`-Eintraege + (`csv:sepliste-kette-paare`, siehe eigener Abschnitt unten). Alles aus + DERSELBEN Kette - Einheiten-BB, Separator-Typ und TrackIds passen damit + zusammen. +3b. **`ssg-id-check-all`** (zweiter Lauf) — Restnummerierung (z.B. Kreisel- + Separator ohne Kette) und Duplikat-Korrektur unter den neuen + Stellvertretern; danach `csv:res-ids-schreiben` (reservierte IDs). 4. **`omni:update-all-attribs`** — Hoehe/Drehung aller Omniflo-Elemente vor dem Export aktualisieren. 5. **`count_sep_scan.lsp::cs-zuordnung-lauf`** (nur bei Bedarf, siehe @@ -229,8 +234,9 @@ zu zaehlen. ### Das Feld `id` je sepliste-Eintrag -Seit `csv:sepliste-ids-setzen` traegt JEDER Eintrag zusaetzlich die `id` des -realen Bauteils, das ihn in der Zeichnung vertritt: +JEDER Eintrag traegt zusaetzlich die `id` des realen Bauteils, das ihn in der +Zeichnung vertritt (zugeordnet in `csv:ketten-verarbeiten`, die ID selbst +liest `csv:block-to-json` erst beim Schreiben vom Stellvertreter): ```json {"typ": "AS", "lfdnr": 1, "x": 5070.1, "y": 1199.2, "id": "0031"} @@ -286,26 +292,21 @@ jeder, der keinem Kettenende mehr entspricht. Im Export ist der Marker eine eigene Zeile (`pattern_ases_marker` in `cfg/export.cfg`) und liefert die `id` des AS-/ES-Eintrags in der `sepliste`. -**Zuordnung Eintrag -> Kopie (`csv:sepliste-paare`).** Verglichen wird nur -innerhalb desselben Typmarkers (`AS`/`ES`/`SEP`) und EXKLUSIV: alle Paare -eines Typs werden nach Abstand sortiert, das kuerzeste zuerst genommen, danach -sind Eintrag UND Kopie verbraucht. Zwei Gruende gegen das naheliegende "je -Eintrag die naechste Kopie": +**Zuordnung Eintrag -> Bauteil (`csv:sepliste-kette-paare`).** Ueber die +REIHENFOLGE je Typ: das k-te `AS`/`SEP`/`ES` der sepliste (nach `lfdnr`) ist +das k-te `AS`/`SEP`/`ES` der KS-geordneten Kette (`csv:kette-von`). Die +sepliste entsteht beim Bau per `entnext`-Wanderung in Baureihenfolge, und +gebaut wird in Foerderrichtung - dieselbe Folge, die die KS-Kette +rekonstruiert. Die x/y-Koordinaten der sepliste werden dafuer NICHT benutzt. -- waehlen zwei Eintraege dieselbe Kopie, bleibt eine Kopie ohne Eintrag und - ein Eintrag ohne ID (genau dieses Muster verursacht in Python die - Separatoren ohne `Nachbarn`/`TrackIds`); -- EIN fehlerhafter Koordinatenwert in der sepliste (bekannter Fall: gespiegeltes - `x` bei `lfdnr=2`) verschiebt so keine anderen Zuordnungen mehr — alle - uebrigen Eintraege finden ihre Kopie ueber den kurzen Abstand zuerst, der - fehlerhafte bekommt den Rest zugeteilt. Liegt dieser Rest weiter als - `csv:sepliste-abstand-warnung-mm` (500 mm) entfernt, meldet der Export das - (`exp-sepliste-abstand`), ohne die Zuordnung zu verwerfen. - -Die Sortierung ist eine strikte Totalordnung (Abstand, dann `lfdnr`, dann -Index) — `vl-sort` wirft Elemente weg, die unter der Vergleichsfunktion gleich -sind, zwei Paare mit identischem Abstand wuerden sonst zu einem verschmelzen. -Getestet in `tests/test_unit.lsp` (`csv:sepliste-paare/*`, zeichnungsfrei). +Frueher lief die Zuordnung ueber den kuerzesten Abstand zwischen sepliste- +Koordinate und Kopie (`csv:sepliste-paare`). Die sepliste-Koordinaten sind +aber teils gespiegelt (bekannter Fall: `x` bei `lfdnr=2`) - das vertauschte +real zwei Separatoren von HundM05 `VF_1` in TrackIds und Nachbarn. Passen +sepliste und Kette in der Anzahl nicht zusammen, meldet der Export das +(`exp-sepliste-ohne-proxy`); die ueberzaehligen Eintraege bleiben ohne `id`. +Getestet in `tests/test_unit.lsp` (`csv:sepliste-kette-paare/*`, +zeichnungsfrei). ## Phase 2 (Python): `lib/export_csv.py::process_blocks` diff --git a/lang/de_DE.json b/lang/de_DE.json index cb17162..0370193 100644 --- a/lang/de_DE.json +++ b/lang/de_DE.json @@ -704,17 +704,13 @@ "exp-sep-zuordnung": "\n[%1] verpackte Separator-Kopie(n): ZUORDNUNG aus der ID des Wrapper-Blocks gesetzt.", "exp-sep-zuordnung-ohne-id": "\n[%1] verpackte Separator-Kopie(n) ohne Wrapper-ID - ZUORDNUNG wird geometrisch bestimmt.", "exp-sepliste-ids": "\n[EXPORT] %1 sepliste-Eintrag/Eintraege mit der ID des zugehoerigen Bauteils versehen.", - "exp-sepliste-ohne-id": "\n[EXPORT] %1 sepliste-Eintrag/Eintraege ohne ID - die Kopie hat kein gefuelltes ID-Attribut.", "exp-sepliste-ohne-proxy": "\n[EXPORT] WARNUNG: Block '%1': %2 sepliste-Eintrag/Eintraege ohne passende Kopie - diese Eintraege bleiben im Export ohne ID.", - "exp-sepliste-abstand": "\n[EXPORT] WARNUNG: Block '%1': %2-Eintrag lfdnr=%3 liegt %4 mm von seinem Bauteil entfernt - sepliste-Koordinate pruefen (Zuordnung bleibt gueltig).", "exp-asmark-blockdef": "\n[EXPORT] Blockdefinition '%1' fuer die AS/ES-Marker angelegt.", "exp-asmark-blockdef-fehler": "\n[EXPORT] WARNUNG: Blockdefinition '%1' konnte nicht angelegt werden - es werden keine AS/ES-Marker gesetzt.", "exp-asmark-bilanz": "\n[EXPORT] AS/ES-Marker: %1 neu, %2 mit bisheriger ID uebernommen, %3 verwaiste entfernt.", - "exp-asmark-ids": "\n[EXPORT] %1 AS/ES-Marker mit ihrer ID am sepliste-Eintrag vermerkt.", "exp-unitmark-blockdef": "\n[EXPORT] Blockdefinition '%1' fuer die Einheiten-Proxies angelegt.", "exp-unitmark-blockdef-fehler": "\n[EXPORT] WARNUNG: Blockdefinition '%1' konnte nicht angelegt werden - es werden keine Foerder-/Gefaelleeinheiten exportiert.", "exp-unitmark-bilanz": "\n[EXPORT] Foerder-/Gefaelleeinheiten: %1 Proxy(s) erzeugt, %2 alte entfernt.", - "exp-unitmark-ids": "\n[EXPORT] %1 Einheiten-Proxy(s) mit ID versehen.", "exp-kette-luecke": "\n[EXPORT] WARNUNG: Kette '%1' reisst ab (kein K1 auf dem letzten K2) - %2 Baustein(e) als eigene Teilkette weitergefuehrt, Einheitengrenzen dort pruefen.", "exp-hilfskopien-behalten": "\n[EXPORT] csv_leave_proxies=1: %1 %2 bleiben in der Zeichnung (Abschalten: (dbg-schalter-off \"csv_leave_proxies\"))", "exp-art-separator": "Separator-Kopie(n)", diff --git a/lang/en_GB.json b/lang/en_GB.json index fd56492..382fd23 100644 --- a/lang/en_GB.json +++ b/lang/en_GB.json @@ -704,17 +704,13 @@ "exp-sep-zuordnung": "\n[%1] packed separator copy/copies: ZUORDNUNG set from the wrapper block ID.", "exp-sep-zuordnung-ohne-id": "\n[%1] packed separator copy/copies without a wrapper ID - ZUORDNUNG determined geometrically.", "exp-sepliste-ids": "\n[EXPORT] %1 sepliste entry/entries tagged with the ID of the matching part.", - "exp-sepliste-ohne-id": "\n[EXPORT] %1 sepliste entry/entries without an ID - the copy has no ID attribute value.", "exp-sepliste-ohne-proxy": "\n[EXPORT] WARNING: block '%1': %2 sepliste entry/entries without a matching copy - those entries stay without an ID in the export.", - "exp-sepliste-abstand": "\n[EXPORT] WARNING: block '%1': %2 entry lfdnr=%3 is %4 mm away from its part - check the sepliste coordinate (the mapping stays valid).", "exp-asmark-blockdef": "\n[EXPORT] Block definition '%1' for the AS/ES markers created.", "exp-asmark-blockdef-fehler": "\n[EXPORT] WARNING: block definition '%1' could not be created - no AS/ES markers are placed.", "exp-asmark-bilanz": "\n[EXPORT] AS/ES markers: %1 new, %2 kept with their previous ID, %3 orphaned ones removed.", - "exp-asmark-ids": "\n[EXPORT] %1 AS/ES marker ID(s) recorded at their sepliste entry.", "exp-unitmark-blockdef": "\n[EXPORT] Block definition '%1' for the unit proxies created.", "exp-unitmark-blockdef-fehler": "\n[EXPORT] WARNING: block definition '%1' could not be created - no conveyor/gravity units are exported.", "exp-unitmark-bilanz": "\n[EXPORT] Conveyor/gravity units: %1 proxy(s) created, %2 old ones removed.", - "exp-unitmark-ids": "\n[EXPORT] %1 unit proxy(s) given an ID.", "exp-kette-luecke": "\n[EXPORT] WARNING: chain '%1' is broken (no K1 at the last K2) - %2 element(s) continued as a separate sub-chain, check the unit boundaries there.", "exp-hilfskopien-behalten": "\n[EXPORT] csv_leave_proxies=1: %1 %2 remain in the drawing (switch off: (dbg-schalter-off \"csv_leave_proxies\"))", "exp-art-separator": "separator copy(ies)", diff --git a/lib/export_csv.py b/lib/export_csv.py index 9a014a7..e6e98ed 100644 --- a/lib/export_csv.py +++ b/lib/export_csv.py @@ -597,8 +597,8 @@ def map_sepliste_kette_items(items, sep_kette, dbg=None): Separator_SP-Kopie (csv:sep-proxies-erzeugen, export.lsp). Beide Wege wuerden sonst dieselben physischen Bauteile DOPPELT zaehlen. - Zuordnung: EXAKTER ID-Abgleich. Seit csv:sepliste-ids-setzen/ - csv:asmark-ids-setzen (Lisp/export.lsp) traegt praktisch jeder sepliste- + Zuordnung: EXAKTER ID-Abgleich. Seit csv:ketten-verarbeiten + (Lisp/export.lsp, Zuordnung ueber die KS-Kette) traegt praktisch jeder sepliste- Eintrag bereits die TeileId des ihn vertretenden Items als Feld "id" - das Item mit GENAU dieser teileid IST der gesuchte Partner. Kein Rate-/ Naeheverfahren mehr noetig, keine der frueheren Ambiguitaeten (ein diff --git a/lib/export_neighbors.py b/lib/export_neighbors.py index 5fc5f67..3b099fe 100644 --- a/lib/export_neighbors.py +++ b/lib/export_neighbors.py @@ -1175,7 +1175,7 @@ def compute_sep_kette(items, dbg=None): compute_kreisel_umlauf). "id" ist die TeileId des Bauteils, das diesen Eintrag in der Zeichnung vertritt (AS/ES ueber den ZZ_ASES_MARKER, SEP ueber die beim Export ausgepackte temporaere - Separator_SP-Kopie - siehe csv:sepliste-ids-setzen, Lisp/export.lsp) - + Separator_SP-Kopie - siehe csv:ketten-verarbeiten, Lisp/export.lsp) - leerer String, wenn (noch) keine ID zugewiesen wurde (Altbestand). Kein x/y mehr: die sepliste-XDATA traegt seit 2026-09-25 keine diff --git a/tests/test_export_kreisel_umlauf.py b/tests/test_export_kreisel_umlauf.py index f52ca72..2d712b3 100644 --- a/tests/test_export_kreisel_umlauf.py +++ b/tests/test_export_kreisel_umlauf.py @@ -221,7 +221,7 @@ class TestSeplisteNachbarnAufgeloest: 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).""" + csv:ketten-verarbeiten, Lisp/export.lsp).""" k = _kreisel("0001", 0.0, 0.0, 0.0, abstand=1000.0) strecke = { "teileart": "ILS 2.0 Gefaellestrecke", diff --git a/tests/test_export_separatorliste.py b/tests/test_export_separatorliste.py index cec9286..870b364 100644 --- a/tests/test_export_separatorliste.py +++ b/tests/test_export_separatorliste.py @@ -5,8 +5,8 @@ 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 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 x/y sowie (seit csv:ketten-verarbeiten, Lisp/export.lsp) ein +Feld "id" - die TeileId des Bauteils, das diesen Eintrag in der Zeichnung vertritt (AS/ES ueber den ZZ_ASES_MARKER, SEP ueber die beim Export ausgepackte temporaere Separator_SP-Kopie). write_separatorliste_merkmale() soll daraus, ueber die diff --git a/tests/test_unit.lsp b/tests/test_unit.lsp index 1bbe92c..918774e 100644 --- a/tests/test_unit.lsp +++ b/tests/test_unit.lsp @@ -311,15 +311,6 @@ ;; ------------------------------------------------------------ ;; TESTS: export.lsp - CSV/KOS-Kodierung ;; ------------------------------------------------------------ -;; Hilfsfunktion der csv:sepliste-paare-Tests: nur (lfdnr index) vergleichen, -;; nicht den Fliesskomma-Abstand. Bewusst auf Top-Level (nicht im Testblock): -;; per defun IN einem Testblock definierte Funktionen sind in BricsCAD im -;; laufenden Durchlauf nicht zuverlaessig gebunden (siehe Ladearchitektur oben). -(defun tu-paare (e p) - (mapcar (function (lambda (x) (list (nth 0 x) (nth 1 x)))) - (csv:sepliste-paare e p)) -) - (defun tu-tests-export () (princ "\n[export]") @@ -344,40 +335,27 @@ ;; json-escape: Anfuehrungszeichen maskieren (tu-eq "csv:json-escape/quote" "a\\\"b" (csv:json-escape "a\"b")) - ;; --- csv:sepliste-paare: sepliste-Eintrag <-> ausgepackte Kopie --------- - ;; Reine Rechenfunktion (Eintraege + Kopien rein, (lfdnr index abstand) - ;; raus) - hier ohne Zeichnung testbar. Im Export liefert - ;; csv:sepliste-ids-setzen die Kopien in Erzeugungsreihenfolge; nur der - ;; Index zaehlt, darum vergleichen die Tests (lfdnr index) statt der - ;; Fliesskomma-Abstaende. - (tu-eq "csv:sepliste-paare/leer" nil (csv:sepliste-paare nil nil)) - - ;; Gerade Kette AS -> SEP -> SEP -> ES; Kopien bewusst in anderer - ;; Reihenfolge als die sepliste (die Erzeugungsreihenfolge der Kopien folgt - ;; der Blockdefinition, nicht der Bauabfolge). - (setq _e '(("AS" 1 0.0 0.0) ("SEP" 2 1000.0 0.0) ("SEP" 3 2000.0 0.0) ("ES" 4 3000.0 0.0))) - (setq _p '(("SEP" 2000.0 0.0) ("AS" 0.0 0.0) ("ES" 3000.0 0.0) ("SEP" 1000.0 0.0))) - (tu-eq "csv:sepliste-paare/kette" '((1 1) (2 3) (3 0) (4 2)) (tu-paare _e _p)) - - ;; EIN fehlerhafter Koordinatenwert (bekannter Fehlerfall: gespiegeltes x - ;; bei lfdnr=2) darf die uebrigen Zuordnungen nicht verschieben - die - ;; exklusive Vergabe teilt dem fehlerhaften Eintrag den Rest zu. - (setq _e '(("SEP" 1 1000.0 0.0) ("SEP" 2 -1000.0 -5000.0) ("SEP" 3 3000.0 0.0))) - (setq _p '(("SEP" 1000.0 0.0) ("SEP" 2000.0 0.0) ("SEP" 3000.0 0.0))) - (tu-eq "csv:sepliste-paare/gespiegelte-koordinate" '((1 0) (2 1) (3 2)) (tu-paare _e _p)) - - ;; Gleiche Abstaende duerfen kein Paar verschlucken: vl-sort wirft - ;; Elemente weg, die unter der Vergleichsfunktion gleich sind - deshalb - ;; sortiert csv:sepliste-paare nach (Abstand, lfdnr, Index). - (setq _e '(("SEP" 1 0.0 0.0) ("SEP" 2 0.0 0.0))) - (setq _p '(("SEP" 0.0 0.0) ("SEP" 0.0 0.0))) - (tu-eq "csv:sepliste-paare/gleiche-abstaende" '((1 0) (2 1)) (tu-paare _e _p)) - - ;; Typ trennt: eine AS-Kopie wird nie einem SEP-Eintrag zugeteilt, der - ;; SEP-Eintrag bleibt ohne Partner (und im Export ohne ID). - (setq _e '(("AS" 1 0.0 0.0) ("SEP" 2 10.0 0.0))) - (setq _p '(("AS" 5.0 0.0))) - (tu-eq "csv:sepliste-paare/typ-trennt" '((1 0)) (tu-paare _e _p)) + ;; --- csv:sepliste-kette-paare: sepliste-Eintrag <-> Kettenbaustein ------- + ;; Zuordnung ueber die Reihenfolge je Typ (k-tes AS/SEP/ES der sepliste = + ;; k-tes der geordneten Kette), Rueckgabe ((lfdnr . index) ...). + (tu-nil "csv:sepliste-kette-paare/leer" (csv:sepliste-kette-paare nil nil)) + ;; HundM05-artige Kette: Koordinaten spielen keine Rolle (auch gespiegelte + ;; x-Werte aendern nichts), Bausteine ohne sepliste-Typ werden uebersprungen. + (setq _e '(("AS" 1 0.0 0.0) ("SEP" 2 -9999.0 0.0) ("SEP" 3 2000.0 0.0) ("ES" 4 3000.0 0.0))) + (tu-eq "csv:sepliste-kette-paare/kette" + '((1 . 0) (2 . 2) (3 . 5) (4 . 7)) + (csv:sepliste-kette-paare _e + '("AS" "STRECKENELEMENT" "SEP" "UMLENKSTATION" "MOTORSTATION" "SEP" "BOGEN" "ES"))) + ;; lfdnr-Reihenfolge zaehlt, nicht die Reihenfolge der Eintraege in der Liste. + (setq _e '(("SEP" 3 0.0 0.0) ("AS" 1 0.0 0.0) ("SEP" 2 0.0 0.0))) + (tu-eq "csv:sepliste-kette-paare/unsortiert" + '((1 . 0) (2 . 1) (3 . 2)) + (csv:sepliste-kette-paare _e '("AS" "SEP" "SEP"))) + ;; Mehr Eintraege als Bausteine: die ueberzaehligen bleiben ohne Partner. + (setq _e '(("AS" 1 0.0 0.0) ("SEP" 2 0.0 0.0) ("SEP" 3 0.0 0.0))) + (tu-eq "csv:sepliste-kette-paare/fehlender-baustein" + '((1 . 0) (2 . 2)) + (csv:sepliste-kette-paare _e '("AS" "BOGEN" "SEP" "ES"))) ;; --- csv:asmark-finden: bisherigen AS/ES-Marker im Bestand wiederfinden --- ;; Schluessel ist (Wrapper-ID, Typ, lfdnr) - alle drei muessen passen, sonst @@ -415,10 +393,18 @@ (vl-symbol-name (car b))))))) (setq *csv-res-sammlung* _rs) - ;; csv:proxy-typ: Typmarker aus dem Blocknamen (Grundlage der Zuordnung). - (tu-eq "csv:proxy-typ/as" "AS" (csv:proxy-typ "AS_Element_90_links_3D")) - (tu-eq "csv:proxy-typ/es" "ES" (csv:proxy-typ "ES_Element_30_rechts_2D")) - (tu-eq "csv:proxy-typ/sep" "SEP" (csv:proxy-typ "Separator_SP_3D")) + ;; csv:pattern-nested-element: Suchmuster = alle Familienmuster aus + ;; csv:element-familien - jede Familie ist also auch klassifizierbar. + (tu-eq "csv:pattern-nested-element/aus-familien" + (ssg-join (mapcar 'cdr (csv:element-familien)) ",") + (csv:pattern-nested-element)) + (tu-true "csv:element-familien/jedes-muster-klassifiziert" + (not (vl-some (function (lambda (f) + (not (equal (car f) (csv:element-teileart + (vl-string-subst "X" "*" + (substr (cdr f) 1 (cond ((vl-string-search "," (cdr f))) + ((strlen (cdr f))))))))))) + (csv:element-familien)))) ;; csv:element-teileart: TeileArt-Familie eines ausgepackten VF_n/GF_n- ;; Bausteins aus dem Blocknamen (Boegen/Streckenelement/Umlenk-/Motor-