From 96ca43eff5075383c6d7f0c75fac87d68ac3acb7 Mon Sep 17 00:00:00 2001 From: Michael Stangl Date: Mon, 28 Sep 2026 10:14:02 +0200 Subject: [PATCH] export: Hilfskopien nach dem Export loeschen, IDs stabil ueber reservierte IDs AS/ES-Marker werden wie alle Hilfskopien geloescht (ausser csv_leave_proxies). IDs von AS/ES, Separator-Kopien und Einheiten bleiben ueber reservierte IDs (XDATA SSG_ID_RES) am VF_n/GF_n-Wrapper stabil: AS|n/ES|n, K|i, U|n. Co-Authored-By: Claude Opus 5.5 --- Lisp/export.lsp | 208 ++++++++++++++++++++-------- Lisp/ssg_id.lsp | 60 +++++++- doc/csv-export.md | 58 +++++--- lang/de_DE.json | 1 + lang/en_GB.json | 1 + lib/export_csv.py | 4 +- lib/export_neighbors.py | 8 +- tests/test_export_kreisel_umlauf.py | 2 +- tests/test_export_separatorliste.py | 2 +- tests/test_unit.lsp | 30 ++++ 10 files changed, 290 insertions(+), 84 deletions(-) diff --git a/Lisp/export.lsp b/Lisp/export.lsp index f5ac8bd..c3cc20f 100644 --- a/Lisp/export.lsp +++ b/Lisp/export.lsp @@ -1072,7 +1072,7 @@ ;; Symbole - als Sub-INSERTs in der Wrapper-Blockdefinition und sind damit ;; fuer (ssget "X" ...) unsichtbar. Dieses Muster findet sie in der ;; Blockdefinition (csv:nested-welt-records); gesetzt wird an ihrer Stelle -;; NICHT eine Kopie des Elements, sondern der dauerhafte Marker +;; NICHT eine Kopie des Elements, sondern der temporaere Marker ;; ZZ_ASES_MARKER (csv:asmark-aktualisieren). (defun csv:pattern-nested-schleus () (export:pattern "pattern_schleuselement" "AS_Element*,ES_Element*") @@ -1147,8 +1147,8 @@ ;; wird, entscheidet der Aufrufer: ;; - csv:sep-proxies-erzeugen -> temporaere Kopie des Symbols selbst ;; (wird nach dem Export wieder geloescht) -;; - csv:asmark-aktualisieren -> dauerhafter AS/ES-Marker (bleibt stehen -;; und traegt die ID ueber Laeufe hinweg) +;; - csv:asmark-aktualisieren -> AS/ES-Marker (ebenfalls temporaer, seine +;; ID bleibt als reservierte ID am Wrapper) ;; ;; Die Kopie steht bewusst EXAKT auf der Weltposition/-drehung/-skalierung ;; ihres verpackten Vorbilds (frueher: +500 mm Versatz in Blockrichtung, rein @@ -1322,7 +1322,7 @@ ;; Setzt csv:nested-welt-records + csv:block-einfuegen-welt zusammen: fuer ;; jeden Fund eine ECHTE Kopie DESSELBEN Blocks an derselben Weltlage. ;; Die AS-/ES-Kettenenden laufen NICHT mehr hierueber - fuer sie setzt -;; csv:asmark-aktualisieren einen dauerhaften Marker (siehe dort). +;; csv:asmark-aktualisieren einen eigenen Marker (siehe dort). ;; ;; proxy-liste-Eintrag: (proxy-ename wrapper-ename typ (x y z) strecke-typ ;; trennsteg-ent) - trennsteg-ent = der verpackte Trennsteg (Kettenbaustein, @@ -1485,7 +1485,7 @@ (setq *csv-sepliste-id-by-handle* nil) ;; --- Einen sepliste-Eintrag mit der ID seines Bauteils vermerken --- -;; Zwei Quellen schreiben in dieselbe Zuordnung - die dauerhaften AS/ES-Marker +;; 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 @@ -1863,22 +1863,22 @@ ;; AS-/ES-Marker (ZZ_ASES_MARKER) ;; =========================================================================== ;; Ein kleines Rechteck, das der Export an JEDES AS-/ES-Ende einer VF_n/GF_n- -;; Kette setzt und das dort DAUERHAFT stehen bleibt. Es ist zweierlei: +;; Kette setzt. Die Kettenenden stecken verpackt in der Wrapper- +;; Blockdefinition (AS_Element_*/ES_Element_*); der Marker vertritt sie im +;; Export als eigene Zeile. Wie die Separator-/Element-Kopien ist er TEMPORAER: +;; csv:run-export loescht ihn am Ende wieder, nur der Debug-Schalter +;; "csv_leave_proxies" (csv:hilfskopien-loeschen) laesst ihn zur Ansicht stehen. ;; -;; Sichtbarmachung - die Kettenenden stecken verpackt in der Wrapper- -;; Blockdefinition (AS_Element_*/ES_Element_*) und sind in der Zeichnung -;; sonst weder anklickbar noch als eigenes Objekt erkennbar. -;; ID-Traeger - das (unsichtbare) ID-Attribut des Markers fuellt -;; ssg-id-check-all, und der naechste Export VERWENDET DENSELBEN MARKER -;; WIEDER. Ein AS/ES behaelt seine TeileId dadurch ueber Exportlaeufe -;; hinweg - anders als die temporaeren Separator-Kopien, die bei jedem Lauf -;; neu entstehen und neue IDs bekommen. +;; Die ID ist trotzdem stabil: csv:asmark-ids-setzen 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 +;; sie in der Zwischenzeit an niemand anderen. ;; -;; Der Marker ist abgeleitete Geometrie: csv:asmark-aktualisieren baut bei -;; JEDEM Export alle Marker neu auf (Lage folgt damit automatisch einer -;; umgebauten Kette) und uebernimmt dabei nur die ID des bisherigen Markers. -;; Nicht wiederverwendete Marker werden geloescht - ein Marker ohne Kette -;; bleibt also nicht als Karteileiche stehen. +;; Aeltere Zeichnungen (und ein Lauf mit csv_leave_proxies) haben noch Marker +;; in der Zeichnung stehen: deren ID wird uebernommen, falls der Wrapper noch +;; keine reservierte ID fuehrt (csv:asmark-finden), danach werden sie +;; geloescht. (defun csv:asmark-blockname () "ZZ_ASES_MARKER") @@ -1994,18 +1994,25 @@ treffer ) +;; --- Schluessel der reservierten ID eines Kettenendes am Wrapper --- +;; typ = "AS"/"ES", lfdnr = Integer oder String. Beispiel: "AS|1". +(defun csv:asmark-res-key (typ lfdnr) + (strcat typ "|" (if (numberp lfdnr) (itoa lfdnr) lfdnr)) +) + ;; --- 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 ID -;; ihres Vorgaengers und behalten sie. +;; noch kein ID-Attribut und werden dort 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) -;; 3. je Paar den bisherigen Marker suchen, seine ID merken, ihn loeschen und -;; an der AKTUELLEN Lage neu setzen +;; 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. ;; @@ -2014,7 +2021,8 @@ (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) + 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)) @@ -2049,19 +2057,25 @@ (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))) + ;; 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))) - (setq alt-id "") + (setq alt-id (cond ((cdr (assoc (csv:asmark-res-key typ lfdnr) reserviert))) + (alt (nth 4 alt)) + (""))) (if alt (progn - (setq alt-id (nth 4 alt)) (entdel (nth 0 alt)) (setq bestand (vl-remove alt bestand)) - (setq uebernommen (1+ uebernommen)) ) + ) + (if (> (strlen alt-id) 0) + (setq uebernommen (1+ uebernommen)) (setq neu (1+ neu)) ) ;; Marker mit Skalierung 1.0 einfuegen - die Skalierung des @@ -2098,6 +2112,9 @@ ;; 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). + (csv:res-merken wrapper (csv:asmark-res-key typ lfdnr) mark) ) ) ) @@ -2128,6 +2145,7 @@ ;; 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) @@ -2334,14 +2352,72 @@ ib (atoi (cond ((cdr (assoc "ID" (ssg-attrib-read b)))) ("")))) (cond ((= ia 0) nil) ((= ib 0) t) (t (< ia ib))))))) +;; =========================================================================== +;; Stabile IDs der temporaeren Kettenobjekte (reservierte IDs am Wrapper) +;; =========================================================================== +;; AS-/ES-Marker, Separator-/Element-Kopien und Einheiten-Proxies entstehen +;; bei jedem Export neu und werden danach geloescht. Ihre ID bleibt trotzdem +;; gleich: sie wird als reservierte ID (XDATA SSG_ID_RES, ssg-id-res-* in +;; ssg_id.lsp) am VF_n/GF_n-Wrapper abgelegt und beim naechsten Export dem +;; neuen Objekt an derselben Stelle wiedergegeben. Schluessel: +;; "AS|n"/"ES|n" - AS-/ES-Marker, n = sepliste-lfdnr (csv:asmark-res-key) +;; "K|i" - Kopie des i-ten Bausteins der geordneten Kette +;; (0-basiert, Reihenfolge von csv:kette-ordnen) +;; "U|n" - n-te Einheit des Wrappers (Modus group_vf_gf) +;; Die Positionen sind deterministisch, solange der Wrapper unveraendert ist; +;; ein Edit baut einen NEUEN Wrapper (ohne diese XDATA) - dann entstehen neue +;; IDs, verschobene Zuordnungen aber nicht. +;; +;; Ablauf: die Erzeuger melden jedes Objekt mit csv:res-merken an, NACH +;; ssg-id-check-all schreibt csv:res-ids-schreiben je Wrapper den kompletten +;; Stand (ersetzt den alten - Eintraege fuer entfallene Objekte verschwinden). + +;; Sammlung dieses Laufs: ((wrapper (key . ename) ...) ...) +(setq *csv-res-sammlung* nil) + +(defun csv:res-key-kette (i) (strcat "K|" (itoa i))) +(defun csv:res-key-einheit (lfdnr) (strcat "U|" (itoa lfdnr))) + +;; Objekt ename unter key fuer den Wrapper anmelden. +(defun csv:res-merken (wrapper key ename / eintrag) + (setq eintrag (assoc wrapper *csv-res-sammlung*)) + (if eintrag + (setq *csv-res-sammlung* + (subst (append eintrag (list (cons key ename))) eintrag *csv-res-sammlung*)) + (setq *csv-res-sammlung* + (cons (list wrapper (cons key ename)) *csv-res-sammlung*))) + ename +) + +;; Nach der ID-Vergabe: je Wrapper die IDs aller angemeldeten Objekte als +;; reservierte IDs schreiben. Rueckgabe: Anzahl geschriebener IDs. +(defun csv:res-ids-schreiben ( / eintrag paare p id n) + (setq n 0) + (foreach eintrag *csv-res-sammlung* + (setq paare nil) + (foreach p (cdr eintrag) + (setq id (cdr (assoc "ID" (ssg-attrib-read (cdr p))))) + (if (and id (> (strlen id) 0)) + (setq paare (cons (cons (car p) id) paare)))) + (ssg-id-res-schreiben (car eintrag) (reverse paare)) + (setq n (+ n (length paare))) + ) + (setq *csv-res-sammlung* nil) + n +) + ;; --- Einem Export-Objekt die naechste ID geben, falls es noch keine hat --- -;; naechste-id = zuletzt vergebene Nummer. Rueckgabe: neue letzte Nummer. -(defun csv:id-vergeben (ename naechste-id / id) +;; naechste-id = zuletzt vergebene Nummer, res-id = reservierte ID fuer dieses +;; Objekt (oder nil) - wird bevorzugt und verbraucht keine neue Nummer. +;; Rueckgabe: neue letzte Nummer. +(defun csv:id-vergeben (ename naechste-id res-id / id) (setq id (cdr (assoc "ID" (ssg-attrib-read ename)))) (if (or (null id) (= id "")) - (progn - (setq naechste-id (1+ naechste-id)) - (ssg-id-set-and-verify ename (ssg-id-format naechste-id) "Kettenfolge"))) + (if (and res-id (> (strlen res-id) 0)) + (ssg-id-set-and-verify ename res-id "reservierte ID") + (progn + (setq naechste-id (1+ naechste-id)) + (ssg-id-set-and-verify ename (ssg-id-format naechste-id) "Kettenfolge")))) naechste-id ) @@ -2359,8 +2435,8 @@ ;; Ohne diese Vergabe nummeriert erst ssg-id-check-all - in der Reihenfolge ;; von (ssget "X"), in BricsCAD also rueckwaerts (zuletzt erzeugt zuerst). ;; Die Export-CSV ist nach TeileId sortiert, damit stehen die Zeilen jeder -;; Kette jetzt von AS nach ES. Bereits vorhandene IDs (dauerhafte AS/ES- -;; Marker) bleiben unangetastet. +;; Kette jetzt von AS nach ES. Bereits vorhandene IDs (AS/ES-Marker mit +;; reservierter ID vom Wrapper) bleiben unangetastet. ;; 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) - @@ -2368,7 +2444,7 @@ (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) + i teil u tr ergebnis res key) (setq alt (csv:unitmark-bestand-loeschen)) (setq gruppiert (= *csv-export-modus* "group_vf_gf")) (if gruppiert (setq blockname (csv:unitmark-blockdef-sicherstellen))) @@ -2384,7 +2460,8 @@ (foreach wrapper (csv:wrapper-nach-id) (setq teile (csv:kette-ordnen (csv:kette-teile wrapper) (cdr (assoc 2 (entget wrapper))))) - ;; Einheiten (nur gruppiert); starts = ((erster-index . proxy) ...) + (setq res (ssg-id-res-lesen wrapper)) + ;; Einheiten (nur gruppiert); starts = ((erster-index key . proxy) ...) (setq starts nil lfdnr 0) (if (and gruppiert blockname) (foreach einheit (csv:kette-einheiten (mapcar 'car teile)) @@ -2394,14 +2471,26 @@ (if mark (progn (setq ergebnis (cons (list mark wrapper (car einheit) lfdnr) ergebnis)) - (setq starts (cons (cons (car (cadr einheit)) mark) starts)))))) - ;; IDs entlang der Kette + (setq starts (cons (list (car (cadr einheit)) (csv:res-key-einheit lfdnr) mark) + starts)))))) + ;; IDs entlang der Kette - reservierte ID des Platzes zuerst (siehe + ;; csv:res-merken); AS-/ES-Marker haben ihre ID schon und sind bereits + ;; unter ihrem eigenen Schluessel angemeldet (csv:asmark-aktualisieren). (setq i 0) (foreach teil teile (foreach u starts - (if (= (car u) i) (setq naechste-id (csv:id-vergeben (cdr u) naechste-id)))) + (if (= (nth 0 u) i) + (progn + (setq naechste-id (csv:id-vergeben (nth 2 u) naechste-id + (cdr (assoc (nth 1 u) res)))) + (csv:res-merken wrapper (nth 1 u) (nth 2 u))))) (if (setq tr (cdr (assoc (list wrapper (nth 1 (nth 1 teil))) traeger))) - (setq naechste-id (csv:id-vergeben tr naechste-id))) + (if (member (car teil) '("AS" "ES")) + (setq naechste-id (csv:id-vergeben tr naechste-id nil)) + (progn + (setq key (csv:res-key-kette i)) + (setq naechste-id (csv:id-vergeben tr naechste-id (cdr (assoc key res)))) + (csv:res-merken wrapper key tr)))) (setq i (1+ i))) ) (if gruppiert @@ -2551,21 +2640,22 @@ ;; 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: die Liste ALLER erzeugten -;; Kopien - Separatoren UND AS/ES-Schleuselemente (der Aufrufer entfernt sie am -;; Ende wieder). label dient nur den Meldungen. +;; 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. (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. (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 sep-proxies (csv:sep-proxies-erzeugen)) - ;; Die AS-/ES-Kettenenden stecken ebenfalls verpackt im Wrapper, bekommen - ;; aber KEINE temporaere Kopie, sondern einen DAUERHAFTEN Marker - ;; (ZZ_ASES_MARKER): er macht das Kettenende in der Zeichnung sichtbar und - ;; traegt dessen ID ueber Exportlaeufe hinweg (siehe csv:asmark-*). + ;; Die AS-/ES-Kettenenden stecken ebenfalls verpackt im Wrapper und + ;; bekommen einen temporaeren Marker (ZZ_ASES_MARKER); ihre ID bleibt als + ;; reservierte ID am Wrapper ueber Exportlaeufe hinweg (siehe csv:asmark-*). (setq asmarks (csv:asmark-aktualisieren)) ;; Die uebrigen Kettenbausteine (Boegen, gerade Foerderer-/Gefaellestrecken- ;; Segmente, Umlenk-/Motorstation) als temporaere Kopien - NUR im Modus @@ -2602,6 +2692,9 @@ (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 (princ (ssg-textf "exp-update-hoehe-drehung" (list label))) (omni:update-all-attribs) @@ -2637,10 +2730,9 @@ ;; bbox-Fehlerdiagnose fuer diesen Lauf zuruecksetzen (siehe csv:get-bbox). (setq *csv-bbox-fail-count* 0) (setq *csv-bbox-fail-first* nil) - ;; Nur die TEMPORAEREN Hilfsmittel zurueckgeben (Separator-/Element-Kopien, - ;; Einheiten-Proxies) - die AS/ES-Marker bleiben bewusst in der Zeichnung - ;; stehen und duerfen nicht mitgeloescht werden. - (list sep-proxies element-proxies unitmarks)) + ;; Alle temporaeren Hilfsmittel zurueckgeben (Separator-/Element-Kopien, + ;; Einheiten-Proxies, AS-/ES-Marker) - csv:run-export entfernt sie wieder. + (list sep-proxies element-proxies unitmarks asmarks)) ;; --- Python-Export-Skript aufrufen und Ergebnis auswerten --------------- ;; Aus csv:run-export ausgelagert: das fertige raw-JSON (out-pfad) an das @@ -2706,14 +2798,15 @@ ;; (siehe csv:block-to-json); fuer EXPORTSIVAS nil. (defun csv:run-export (label py-name csv-name include-bbox / ss i ename fh out-pfad first-block count out-dir sep-proxies element-proxies - unitmarks vorbereitung-ergebnis) + unitmarks asmarks vorbereitung-ergebnis) ;; Zeichnungszustand fuer den Export herrichten (Proxies, IDs, Zuordnung, ;; Attribut-Update, Sep/Scan-Zaehlung, REGENALL, bbox-Reset) - ausgelagert. - ;; Rueckgabe: (sep-proxies element-proxies unitmarks), alle unten wieder zu entfernen. + ;; Rueckgabe: (sep-proxies element-proxies unitmarks asmarks), alle unten wieder zu entfernen. (setq vorbereitung-ergebnis (csv:export-vorbereiten label)) (setq sep-proxies (nth 0 vorbereitung-ergebnis)) (setq element-proxies (nth 1 vorbereitung-ergebnis)) (setq unitmarks (nth 2 vorbereitung-ergebnis)) + (setq asmarks (nth 3 vorbereitung-ergebnis)) (princ (ssg-textf "exp-collect-blocks" (list label))) (setq ss (csv:collect-export-blocks)) @@ -2779,11 +2872,12 @@ ) ) ) - ;; Temporaere Separator-/Element-Kopien wieder entfernen - sie haben ihren - ;; Zweck (ID-Vergabe + JSON-Zeile ueber die obigen, unveraenderten - ;; Sammel-Funktionen) erfuellt. Die AS-/ES-Marker (ZZ_ASES_MARKER) bleiben - ;; bewusst stehen (siehe csv:export-vorbereiten). + ;; 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). (csv:sep-proxies-loeschen sep-proxies) + (csv:hilfskopien-loeschen asmarks "exp-art-ases") (csv:hilfskopien-loeschen element-proxies "exp-art-element") (csv:hilfskopien-loeschen unitmarks "exp-art-einheit") (princ) diff --git a/Lisp/ssg_id.lsp b/Lisp/ssg_id.lsp index 099b3d7..b4a936a 100644 --- a/Lisp/ssg_id.lsp +++ b/Lisp/ssg_id.lsp @@ -69,8 +69,8 @@ ;; ZZ_ASES_MARKER: die Marker der AS-/ES-Kettenenden (siehe ;; csv:asmark-aktualisieren, export.lsp). Sie brauchen eine ID - ;; sie vertreten das Kettenende im Export ("sepliste"-Feld "id" - ;; und eigene JSON-Zeile) und behalten sie ueber Laeufe hinweg, - ;; weil der Marker in der Zeichnung stehen bleibt. + ;; und eigene JSON-Zeile). Der Marker selbst ist temporaer, seine + ;; ID bleibt als reservierte ID am Wrapper (ssg-id-res-*). (wcmatch bname "ZZ_ASES_MARKER*") ;; ZZ_UNIT_MARKER: die Foerdereinheit-/Gefaelleeinheit-Marker ;; (siehe csv:ketten-verarbeiten, export.lsp) - temporaere @@ -93,9 +93,64 @@ ) +;; ============================================================ +;; Reservierte IDs (XDATA "SSG_ID_RES") +;; ============================================================ +;; IDs von Objekten, die NICHT dauerhaft in der Zeichnung stehen, aber ueber +;; Laeufe hinweg dieselbe ID behalten sollen - z.B. die AS-/ES-Kettenenden +;; eines VF_n/GF_n-Wrappers: ihr Export-Stellvertreter (ZZ_ASES_MARKER) wird +;; nach jedem Export geloescht, die ID bleibt als XDATA am Wrapper stehen +;; (csv:asmark-*, export.lsp). Format: (("AS|1" . "0032") ("ES|4" . "0033")), +;; Schluessel frei waehlbar, aber ohne "=". +;; +;; ssg-id-max-in-ss rechnet reservierte IDs mit ein: sonst saehe die +;; Neuvergabe die ID eines gerade nicht existierenden Stellvertreters als frei +;; an und vergaebe sie an einen anderen Block. + +(defun ssg-id-res-app () "SSG_ID_RES") + +;; Rein (file-/zeichnungsfrei): Paare <-> 1000er-Strings "AS|1=0032". +(defun ssg-id-res->strings (paare) + (mapcar (function (lambda (p) (strcat (car p) "=" (cdr p)))) paare) +) +(defun ssg-id-res-parse (strings / s pos out) + (foreach s strings + (if (setq pos (vl-string-search "=" s)) + (setq out (cons (cons (substr s 1 pos) (substr s (+ pos 2))) out)))) + (reverse out) +) +;; Hoechste numerische ID der Paare (0, wenn keine). +(defun ssg-id-res-max (paare / m n) + (setq m 0) + (foreach p paare + (if (> (setq n (atoi (cdr p))) m) (setq m n))) + m +) + +;; Liest die reservierten IDs eines Entities. Rueckgabe: Paare oder nil. +(defun ssg-id-res-lesen (ent / app-data) + (setq app-data (cdr (assoc -3 (entget ent (list (ssg-id-res-app)))))) + (if app-data + (ssg-id-res-parse (mapcar 'cdr (cdr (car app-data)))) + ) +) + +;; Schreibt die reservierten IDs eines Entities (ersetzt den bisherigen Stand +;; komplett). paare = nil entfernt die XDATA. +(defun ssg-id-res-schreiben (ent paare) + (regapp (ssg-id-res-app)) + (entmod (append (entget ent) + (list (list -3 (cons (ssg-id-res-app) + (mapcar (function (lambda (s) (cons 1000 s))) + (ssg-id-res->strings paare))))))) + ent +) + + ;; --- Hoechste bereits vergebene ID in einem Auswahlsatz ermitteln --- ;; ss = Auswahlsatz (i.d.R. aus ssg-id-collect-blocks) oder nil. ;; Rueckgabe: Integer (hoechste ID) oder 0 wenn keine gefunden. +;; Beruecksichtigt auch die reservierten IDs (ssg-id-res-lesen) der Bloecke. ;; ;; Bewusst als eigene Funktion: ssg-id-check-all MUSS diesen Wert ueber den ;; GESAMTEN Auswahlsatz kennen, BEVOR es die erste neue ID vergibt (siehe @@ -118,6 +173,7 @@ ) ) ) + (setq max-id (max max-id (ssg-id-res-max (ssg-id-res-lesen ename)))) (setq i (1+ i)) ) ) diff --git a/doc/csv-export.md b/doc/csv-export.md index 7f7cbd0..ed6c8db 100644 --- a/doc/csv-export.md +++ b/doc/csv-export.md @@ -55,7 +55,7 @@ Bounding-Boxen braucht). `(proxy-ename wrapper-ename typ (x y z))`. 1b. **`csv:asmark-aktualisieren`** — die AS-/ES-Enden derselben Kette (`AS_Element*`/`ES_Element*`, Muster `pattern_schleuselement`) bekommen - KEINE temporaere Kopie, sondern einen DAUERHAFTEN Marker — siehe eigener + 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). @@ -140,6 +140,26 @@ AS/ES-Marker, Einheiten-Proxies) werden vor `ssg-id-check-all` entlang der Kette von AS nach ES aufsteigend vergeben (`csv:ketten-verarbeiten`) - die CSV ist nach TeileId sortiert, die Zeilen stehen damit in Kettenfolge. +**Stabile IDs ueber Exportlaeufe.** Alle diese Objekte sind temporaer und +werden nach dem Export geloescht (ausser mit `csv_leave_proxies`). Ihre ID +bleibt trotzdem gleich: nach `ssg-id-check-all` schreibt +`csv:res-ids-schreiben` sie als **reservierte IDs** an den VF_n/GF_n-Wrapper +(XDATA `SSG_ID_RES`, Helfer `ssg-id-res-*` in `Lisp/ssg_id.lsp`), der naechste +Export gibt sie dem neuen Objekt an derselben Stelle zurueck. Schluessel: + +| Schluessel | Objekt | +|---|---| +| `AS\|n` / `ES\|n` | AS-/ES-Marker, n = sepliste-`lfdnr` | +| `K\|i` | Separator-/Element-Kopie des i-ten Bausteins der geordneten Kette (0-basiert, `csv:kette-ordnen`) | +| `U\|n` | n-te Foerder-/Gefaelleeinheit des Wrappers (`group_vf_gf`) | + +`ssg-id-max-in-ss` zaehlt die reservierten IDs mit, damit sie in der +Zwischenzeit an keinen anderen Block vergeben werden. Nur Objekte OHNE +reservierte ID (erster Export einer Kette, Modus neu gewaehlt) bekommen eine +neue Nummer - oberhalb aller vorhandenen, also nicht mehr zwingend in +Kettenfolge neben den uebrigen Zeilen der Kette. Ein Edit baut einen neuen +Wrapper ohne diese XDATA; dessen Kettenobjekte werden dann neu nummeriert. + Umgesetzt ueber `csv:baustein-merken`: beim Erzeugen des Stellvertreters werden K-Punkte (`csv:ks-welt-punkte`) und bbox (kurzlebige Kopie des Bausteins, `csv:baustein-bbox`) unter seinem Handle gemerkt @@ -211,7 +231,7 @@ realen Bauteils, das ihn in der Zeichnung vertritt: ``` Das ist dieselbe ID, die die zugehoerige temporaere Kopie (SEP) bzw. der -dauerhafte `ZZ_ASES_MARKER` (AS/ES) in ihrer eigenen JSON-Zeile traegt — die +temporaere `ZZ_ASES_MARKER` (AS/ES) in ihrer eigenen JSON-Zeile traegt — die Verbindung "Kettenglied <-> reales Teil" steht damit schon im Export und muss in Python nicht mehr ueber Koordinaten-Naehe geraten werden. `map_sepliste_ kette_items` (siehe Abschnitt 5 unten) loest genau darueber jeden sepliste- @@ -226,7 +246,9 @@ Die beiden Kettenenden stecken als `AS_Element_*`/`ES_Element_*` verpackt in der Wrapper-Blockdefinition: in der Zeichnung sind sie weder anklickbar noch einzeln erkennbar, und `(ssget "X" ...)` findet sie nicht. `csv:asmark-*` (`Lisp/export.lsp`) setzt darum an jedes Ende ein kleines Rechteck, das nach -dem Export in der Zeichnung STEHEN BLEIBT: +nach dem Export wie alle Hilfskopien wieder GELOESCHT wird (nur der +Debug-Schalter `csv_leave_proxies` bzw. der Haken im Dialog +`CSV_EXPORT_KONFIG` laesst ihn zur Ansicht stehen): - **Sichtbarmachung** — Kantenlaenge aus `cfg/export.cfg` `[Marker]` `ases_groesse_mm` (Default 200 mm), mittig auf dem Einfuegepunkt des @@ -238,20 +260,22 @@ dem Export in der Zeichnung STEHEN BLEIBT: - **ID-Traeger** — die Attribute (alle unsichtbar) sind `ID`, `ZUORDNUNG` (Wrapper-ID), `TYP` (`AS`/`ES`), `LFDNR` (sepliste-Eintrag), `ELEMENT` (Blockname des echten Elements, haelt die Variante 30/90, - links/rechts, 2D/3D fest) und `ARTINR`. `ssg-id-check-all` fuellt die `ID`, - und weil der Marker liegen bleibt, **behaelt ein AS/ES seine TeileId ueber - Exportlaeufe hinweg** — im Unterschied zu den Separator-Kopien, die jeder - Lauf neu erzeugt und neu nummeriert. + links/rechts, 2D/3D fest) und `ARTINR`. `ssg-id-check-all` fuellt die `ID`. + Danach legt `csv:res-ids-schreiben` die ID als **reservierte ID** am + VF_n/GF_n-Wrapper ab (Eintrag `AS|1=0032`, siehe "Stabile IDs ueber + Exportlaeufe" oben). Der naechste Export gibt sie dem neu gesetzten Marker + zurueck, **ein AS/ES behaelt seine TeileId also ueber Exportlaeufe + hinweg**, obwohl der Marker dazwischen nicht existiert. Der Marker ist abgeleitete Geometrie: jeder Export baut ihn neu auf (die Lage -folgt damit automatisch einer umgebauten Kette) und uebernimmt vom bisherigen -Marker nur dessen `ID`. Wiedergefunden wird er ueber (`ZUORDNUNG`, `TYP`, -`LFDNR`) — bewusst nicht ueber die Position, damit eine verschobene Kette ihre -TeileIds behaelt. Was am Ende keinem Kettenende mehr entspricht (Kette -geloescht, Eintrag entfallen), wird geloescht; Karteileichen entstehen nicht. -Ein frisch gebauter Wrapper hat beim Setzen der Marker noch keine ID — seine -Marker bekommen die `ZUORDNUNG` nach der ID-Vergabe nachgetragen und sind ab -dem naechsten Export wiederfindbar. +folgt damit automatisch einer umgebauten Kette) und uebernimmt die ID ueber +den Schluessel (`TYP`, `LFDNR`) aus der reservierten ID des Wrappers — bewusst +nicht ueber die Position, damit eine verschobene Kette ihre TeileIds behaelt. +Steht noch ein Marker in der Zeichnung (aeltere Zeichnung, in der die Marker +frueher dauerhaft blieben, oder ein Lauf mit `csv_leave_proxies`), wird seine +ID ueber (`ZUORDNUNG`, `TYP`, `LFDNR`) uebernommen, falls der Wrapper noch +keine reservierte ID fuehrt; der alte Marker wird dabei geloescht, ebenso +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`. @@ -411,7 +435,7 @@ Zu beachten: in Baureihenfolge, unabhaengig vom Kreisel-Umlauf. - Die AS/ES-Enden (`0021`=AS, `0024`=ES) sind eigene Zeilen (Zeilentyp "ILS Ausschleuselement"/"ILS Einschleuselement"), direkt aus dem - dauerhaften `ZZ_ASES_MARKER` klassifiziert — in diesem gekuerzten Beispiel + `ZZ_ASES_MARKER` klassifiziert — in diesem gekuerzten Beispiel nicht einzeln aufgefuehrt, aber ihre TeileIds tauchen in `TrackIds` beider Seiten auf. - Die Separatoren 3 und 5 (`0012`/`0022`) unterscheiden sich nur durch ihre @@ -499,7 +523,7 @@ Geometrie bereits klassifizierter Items abgeleitet: AS/ES-Kettenenden werden NICHT mehr synthetisiert (fruehere Version: `compute_strecke_kreisel_schleus`/`build_strecke_schleus_items` suchten den anschliessenden Kreisel per Kollisionstest und erfanden dabei eine eigene, -bei jedem Export neue TeileId). Der dauerhafte `ZZ_ASES_MARKER` +bei jedem Export neue TeileId). Der `ZZ_ASES_MARKER` (Lisp/export.lsp, siehe oben) ist ein echter Block mit echter Geometrie - `"ILS Ausschleuselement"`/`"ILS Einschleuselement"` sind darum eine ganz normale Zeile aus Schritt 1 (Klassifikation), TeileId/Position/Boundingbox diff --git a/lang/de_DE.json b/lang/de_DE.json index b9457cc..cb17162 100644 --- a/lang/de_DE.json +++ b/lang/de_DE.json @@ -720,6 +720,7 @@ "exp-art-separator": "Separator-Kopie(n)", "exp-art-element": "Element-Kopie(n)", "exp-art-einheit": "Einheiten-Proxy(s)", + "exp-art-ases": "AS-/ES-Marker", "exp-update-hoehe-drehung": "\n[%1] Aktualisiere Hoehe/Drehung...", "exp-collect-blocks": "\n[%1] Sammle relevante Bloecke...", "exp-no-blocks-found": "\n[%1] Keine relevanten Bloecke gefunden.", diff --git a/lang/en_GB.json b/lang/en_GB.json index 7877a4f..fd56492 100644 --- a/lang/en_GB.json +++ b/lang/en_GB.json @@ -720,6 +720,7 @@ "exp-art-separator": "separator copy(ies)", "exp-art-element": "element copy(ies)", "exp-art-einheit": "unit proxy(ies)", + "exp-art-ases": "AS/ES marker(s)", "exp-update-hoehe-drehung": "\n[%1] Updating height/rotation...", "exp-collect-blocks": "\n[%1] Collecting relevant blocks...", "exp-no-blocks-found": "\n[%1] No relevant blocks found.", diff --git a/lib/export_csv.py b/lib/export_csv.py index 94f0326..d0785b8 100644 --- a/lib/export_csv.py +++ b/lib/export_csv.py @@ -592,7 +592,7 @@ def map_sepliste_kette_items(items, sep_kette, dbg=None): besitzenden Strecke (siehe write_separatorliste_merkmale). ERZEUGT KEINE neuen Zeilen: AS/ES liegen bereits als eigene Items vor - (direkt aus dem dauerhaften ZZ_ASES_MARKER klassifiziert, siehe + (direkt aus dem ZZ_ASES_MARKER klassifiziert, siehe process_blocks), SEP als die beim Export ausgepackte temporaere Separator_SP-Kopie (csv:sep-proxies-erzeugen, export.lsp). Beide Wege wuerden sonst dieselben physischen Bauteile DOPPELT zaehlen. @@ -805,7 +805,7 @@ def write_separatorliste_merkmale(items, sep_kette_by_key, dbg=None): dict-Reihenfolge ist nicht garantiert, die sepliste-Liste dagegen schon. AS/ES zaehlen HIER mit (anders als beim "Separatorlist"-Merkmal frueher) - sie sind eigene Items ("ILS Ausschleuselement"/"ILS Einschleuselement", - direkt aus dem dauerhaften ZZ_ASES_MARKER klassifiziert, siehe + direkt aus dem ZZ_ASES_MARKER klassifiziert, siehe process_blocks), analog zu den AS/ES-Enden, die beim Kreisel bereits ueber compute_kreisel_umlauf in dessen TrackIds auftauchen - ohne sie fehlten hier die Kettenenden, obwohl sie beim Kreisel schon drin sind. diff --git a/lib/export_neighbors.py b/lib/export_neighbors.py index 8f89e08..5fc5f67 100644 --- a/lib/export_neighbors.py +++ b/lib/export_neighbors.py @@ -89,7 +89,7 @@ SENSOR_TEILEARTEN = {"ILS 2.0 Separator", "ILS 2.0 Scanner"} # Umlauf-Punkte gefuehrt - so tauchen sie, wenn sie auf einer Kreiselbahn # liegen, in dessen "TrackIds"-Spalte auf (Nutzeranforderung: wie # Separator/AS/ES behandeln). AUSSCHLEUS_TEILEART/EINSCHLEUS_TEILEART (AS/ES- -# Kettenenden, siehe unten): seit sie als dauerhafter ZZ_ASES_MARKER real in +# Kettenenden, siehe unten): seit sie als ZZ_ASES_MARKER real in # der Zeichnung stehen (statt wie frueher nur ueber eine Kreisel<->Strecke- # Kollisionsberechnung synthetisiert zu werden, siehe git-Historie # compute_strecke_kreisel_schleus), reicht ihre EIGENE Bounding-Box fuer die @@ -114,7 +114,7 @@ WEICHE_TEILEART = "ILS Weiche" # TeileArten der Ein-/Ausschleuselemente an den Strecken-Enden (AS/ES) einer # Gefaellestrecke/eines Foerderers - siehe process_blocks (export_csv.py): -# direkt aus dem dauerhaften ZZ_ASES_MARKER klassifiziert (dessen Attribut +# direkt aus dem ZZ_ASES_MARKER klassifiziert (dessen Attribut # TYP="AS"/"ES" waehlt die TeileArt), keine gesonderte Erzeugung mehr hier. AUSSCHLEUS_TEILEART = "ILS Ausschleuselement" EINSCHLEUS_TEILEART = "ILS Einschleuselement" @@ -1173,7 +1173,7 @@ def compute_sep_kette(items, dbg=None): "nachfolger"}} - vorgaenger/nachfolger sind wieder solche keys, oder None am jeweiligen Kettenende (Verkettung mit dem Kreisel-Umlauf uebernimmt compute_kreisel_umlauf). "id" ist die TeileId des Bauteils, das diesen - Eintrag in der Zeichnung vertritt (AS/ES ueber den dauerhaften + 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) - leerer String, wenn (noch) keine ID zugewiesen wurde (Altbestand). @@ -1215,7 +1215,7 @@ def compute_kreisel_umlauf(items, tolerance_mm, durchmesser_mm=800.0, UND die AS/ES-Enden aller andockenden VF_n/GF_n-Ketten (alle zusammen KREISEL_UMLAUF_TEILEARTEN, einheitlich per BBox-Ueberschneidung einer Kreiselhaelfte/eines Kreisels zugeordnet - wie compute_sensor_zuordnung; - ein AS/ES bringt seine eigene Bounding-Box vom dauerhaften ZZ_ASES_MARKER + ein AS/ES bringt seine eigene Bounding-Box vom ZZ_ASES_MARKER mit, seit es als reales Item existiert - keine gesonderte Naeherungslogik mehr noetig) UND die Kreisel-Kreisel-Weichen (touch_switches, siehe compute_kreisel_touch_switches) - in eine geschlossene Umlauf-Reihenfolge diff --git a/tests/test_export_kreisel_umlauf.py b/tests/test_export_kreisel_umlauf.py index 50e8ab6..f52ca72 100644 --- a/tests/test_export_kreisel_umlauf.py +++ b/tests/test_export_kreisel_umlauf.py @@ -206,7 +206,7 @@ class TestSeplisteNachbarnAufgeloest: Architektur: SEP-Eintraege liegen bereits als echte (Proxy-)Separator_SP- Items vor (der LISP-Export fuegt je verpacktem Symbol eine temporaere - Kopie ein), AS-/ES-Eintraege als eigene Items, direkt aus dem dauerhaften + Kopie ein), AS-/ES-Eintraege als eigene Items, direkt aus dem ZZ_ASES_MARKER klassifiziert (siehe export_csv.py process_blocks) - beide tragen bereits ihre TeileId als sepliste-Feld "id". map_sepliste_kette_ items erzeugt daher KEINE neuen Zeilen, sondern ordnet jedem sepliste- diff --git a/tests/test_export_separatorliste.py b/tests/test_export_separatorliste.py index ad3a82a..cec9286 100644 --- a/tests/test_export_separatorliste.py +++ b/tests/test_export_separatorliste.py @@ -7,7 +7,7 @@ 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 in der Zeichnung vertritt (AS/ES ueber den dauerhaften +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 bereits von map_sepliste_kette_items per exaktem ID-Abgleich aufgeloesten diff --git a/tests/test_unit.lsp b/tests/test_unit.lsp index 3672d95..fb14263 100644 --- a/tests/test_unit.lsp +++ b/tests/test_unit.lsp @@ -247,6 +247,16 @@ (tu-eq "ssg-id-format/42" "0042" (ssg-id-format 42)) (tu-eq "ssg-id-format/1000" "1000" (ssg-id-format 1000)) (tu-eq "ssg-id-format/12345" "12345" (ssg-id-format 12345)) + + ;; Reservierte IDs (XDATA SSG_ID_RES): Serialisierung + Maximum. + (setq _rp '(("AS|1" . "0032") ("ES|4" . "0107"))) + (tu-eq "ssg-id-res->strings" '("AS|1=0032" "ES|4=0107") (ssg-id-res->strings _rp)) + (tu-eq "ssg-id-res-parse/rundlauf" _rp (ssg-id-res-parse (ssg-id-res->strings _rp))) + (tu-eq "ssg-id-res-parse/ohne-gleich-ignoriert" '(("AS|1" . "0032")) + (ssg-id-res-parse '("kaputt" "AS|1=0032"))) + (tu-eq "ssg-id-res-parse/leerer-wert" '(("AS|1" . "")) (ssg-id-res-parse '("AS|1="))) + (tu-eq "ssg-id-res-max" 107 (ssg-id-res-max _rp)) + (tu-eq "ssg-id-res-max/leer" 0 (ssg-id-res-max nil)) ) @@ -385,6 +395,26 @@ (tu-nil "csv:asmark-finden/ohne-wrapper-id" (csv:asmark-finden _mk "" "AS" "1")) (tu-nil "csv:asmark-finden/leerer-bestand" (csv:asmark-finden nil "0021" "AS" "1")) + ;; csv:asmark-res-key: Schluessel der reservierten ID am Wrapper - lfdnr als + ;; Integer (csv:asmark-aktualisieren) und String muss denselben liefern. + (tu-eq "csv:asmark-res-key/int" "AS|1" (csv:asmark-res-key "AS" 1)) + (tu-eq "csv:asmark-res-key/str" "ES|4" (csv:asmark-res-key "ES" "4")) + (tu-eq "csv:res-key-kette" "K|7" (csv:res-key-kette 7)) + (tu-eq "csv:res-key-einheit" "U|2" (csv:res-key-einheit 2)) + + ;; csv:res-merken: Anmeldungen je Wrapper zusammenfuehren (ein Eintrag je + ;; Wrapper, Reihenfolge der Anmeldung). Symbole statt enames - rein + ;; listenbasiert. Globale Sammlung sichern und wiederherstellen. + (setq _rs *csv-res-sammlung* *csv-res-sammlung* nil) + (csv:res-merken 'W1 "AS|1" 'M1) + (csv:res-merken 'W2 "K|0" 'M2) + (csv:res-merken 'W1 "K|3" 'M3) + (tu-eq "csv:res-merken/je-wrapper" + '((W1 ("AS|1" . M1) ("K|3" . M3)) (W2 ("K|0" . M2))) + (vl-sort *csv-res-sammlung* (function (lambda (a b) (< (vl-symbol-name (car a)) + (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"))