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 <noreply@anthropic.com>
This commit is contained in:
Michael Stangl
2026-09-28 10:14:02 +02:00
co-authored by Claude Opus 5.5
parent 5a13595289
commit 96ca43eff5
10 changed files with 290 additions and 84 deletions
+149 -55
View File
@@ -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 ""))
(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")))
(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)
+58 -2
View File
@@ -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))
)
)
+41 -17
View File
@@ -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
+1
View File
@@ -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.",
+1
View File
@@ -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.",
+2 -2
View File
@@ -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.
+4 -4
View File
@@ -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
+1 -1
View File
@@ -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-
+1 -1
View File
@@ -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
+30
View File
@@ -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"))