sepliste: Bauteil-IDs eintragen, AS/ES ueber dauerhaften Marker sichtbar machen

Stufe 1 (LISP-Export-Pfad): export_raw.json enthaelt jetzt fuer jeden
sepliste-Eintrag (AS/SEP/ES) einer VF_n/GF_n-Kette die ID des realen
Bauteils, das ihn vertritt.

- csv:sep-proxies-erzeugen (Separatoren) und die neuen AS/ES-Marker teilen
  sich csv:nested-welt-records/csv:block-einfuegen-welt zur Platzierung.
- csv:sepliste-paare ordnet sepliste-Eintraege ihrem Bauteil exklusiv nach
  Abstand zu (kuerzester zuerst, dann verbraucht) statt "jeder Eintrag
  nimmt sich das naechste" - ein einzelner fehlerhafter Koordinatenwert
  (bekannter Bug: gespiegeltes x bei lfdnr=2) verschiebt dadurch keine
  andere Zuordnung mehr.
- Neu: ZZ_ASES_MARKER - ein kleines Rechteck an jedem AS/ES-Kettenende,
  das nach dem Export STEHEN BLEIBT (anders als die temporaeren
  Separator-Kopien). Macht das sonst unsichtbare, in der Wrapper-
  Blockdefinition verpackte Kettenende in BricsCAD sichtbar/anklickbar
  und traegt seine ID unsichtbar am Attribut ID - dadurch behaelt ein
  AS/ES seine TeileId ueber Exportlaeufe hinweg. Wird bei jedem Export
  neu aufgebaut (Lage folgt der Kette), Wiederfund ueber ZUORDNUNG+TYP+
  LFDNR, verwaiste Marker werden entfernt.

In BricsCAD verifiziert (5 HundM05-Ketten, zwei Exportlaeufe: gleiche
Marker-IDs, korrekte sepliste-"id"-Felder). Dabei einen realen Bug
gefunden und behoben: die Blockdefinition des Markers uebernahm ueber
einen ungefilterten entnext-Walk versehentlich auch die Attribute des
zuletzt eingefuegten Blocks (VF_5 verlor dadurch alle Attribute inkl.
ID) - jetzt auf LWPOLYLINE/ATTDEF gefiltert.

9 neue zeichnungsfreie Unit-Tests (csv:sepliste-paare/*, csv:asmark-
finden/*, csv:proxy-typ/*), 4 neue de/en-Textschluessel (Paritaet
geprueft). bin\run_tests.bat --lisp: 257/257 bestanden.

Noch nicht enthalten (Stufe 2, Python): map_separator_kette_items nutzt
das neue "id"-Feld noch nicht, build_strecke_schleus_items synthetisiert
AS/ES-Zeilen weiterhin selbst statt die exportierten zu lesen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Samer Ayadi
2026-09-22 13:09:05 +02:00
co-authored by Claude Sonnet 5
parent 9671fe4e04
commit 618ff3d86c
15 changed files with 1205 additions and 216 deletions
+672 -190
View File
@@ -653,7 +653,7 @@
handle attribs bbox bbox-json
insert-quat insert-kos k-list warnung-json warnung-eintrag
zuordnung-fix-json zuordnung-fix-eintrag sepliste-json sepliste-app sepliste
sepliste-teile sepliste-first)
sepliste-teile sepliste-first sepliste-ids sep-id)
(setq ed (entget ename))
(setq blk-name (cdr (assoc 2 ed)))
(setq layer (cdr (assoc 8 ed)))
@@ -765,14 +765,24 @@
(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.
(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))
",\"x\":" (rtos (nth 2 e) 2 2)
",\"y\":" (rtos (nth 3 e) 2 2)
(if sep-id (strcat ",\"id\":\"" (csv:json-escape sep-id) "\"") "")
"}"))
(setq sepliste-first nil)
)
@@ -818,6 +828,31 @@
"Separator_SP_2D,Separator_SP_3D"
)
;; --- Muster der verpackten AS-/ES-Schleuselemente ---
;; AS_Element_*/ES_Element_* sind die beiden Kettenenden einer VF_n/GF_n-
;; Strecke (Ein-/Ausschleusung zum Kreisel). Sie stecken - wie die Separator-
;; 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
;; ZZ_ASES_MARKER (csv:asmark-aktualisieren).
(defun csv:pattern-nested-schleus ()
(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")
)
)
;; --- Muster der Compound-("Wrapper"-)Bloecke, deren Definition ueberhaupt
;; auf verpackte Separator_SP-Symbole durchsucht wird (KR_n/KREISEL_n/
;; ECKRAD_n/VF_n/GF_n) - alle anderen INSERTs (Omniflo, eigenstaendige
@@ -835,18 +870,23 @@
(if (or (null f) (equal f 0.0 1e-12)) 1.0 f)
)
;; --- Fuer jedes verpackte Separator_SP-Symbol eine ECHTE, temporaere Kopie
;; an DERSELBEN Stelle in den Modellraum einfuegen ---
;; Separator_SP-Sensor-Symbole, die beim Zusammenbau von VF_n/GF_n/
;; KREISEL_n in die Compound-Blockdefinition "verpackt" wurden (siehe
;; ssg-collect-nested-inserts), sind fuer (ssget "X" ...) unsichtbar - und
;; damit weder fuer ssg-id-check-all (eigene ID) noch csv:collect-export-
;; blocks (JSON-Zeile) erreichbar. Statt beide Sammel-Funktionen dafuer
;; anzupassen, wird hier VOR der ID-Vergabe (csv:run-export) fuer jeden Fund
;; eine ECHTE Kopie desselben Blocks eingefuegt.
;; Diese Kopie ist ein normaler, top-level INSERT und durchlaeuft ID-
;; Vergabe/Export danach unveraendert ueber die bestehenden Funktionen -
;; deren Blockname-Muster (Separator_SP*) trifft ja bereits zu.
;; --- Fuer jedes verpackte Original die Weltlage ermitteln und daran ein
;; echtes Objekt in den Modellraum setzen ---
;; nested-pattern = wcmatch-Muster der zu suchenden Sub-INSERTs, siehe
;; csv:pattern-nested-separator (Separator-Symbole) bzw.
;; csv:pattern-nested-schleus (AS-/ES-Schleuselemente).
;;
;; Sub-Bloecke, die beim Zusammenbau von VF_n/GF_n/KREISEL_n in die Compound-
;; Blockdefinition "verpackt" wurden (siehe ssg-collect-nested-inserts), sind
;; fuer (ssget "X" ...) unsichtbar - und damit weder fuer ssg-id-check-all
;; (eigene ID) noch csv:collect-export-blocks (JSON-Zeile) erreichbar, und in
;; der Zeichnung auch nicht als eigenes Objekt anklickbar. Diese Funktion
;; liefert ihre Weltlage, ohne die Zeichnung anzufassen; was daran gesetzt
;; 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)
;;
;; Die Kopie steht bewusst EXAKT auf der Weltposition/-drehung/-skalierung
;; ihres verpackten Vorbilds (frueher: +500 mm Versatz in Blockrichtung, rein
@@ -859,39 +899,24 @@
;; Skalierung relativ zur Wrapper-Definition); hier kommt nur noch die
;; Platzierung des top-level INSERT darauf.
;;
;; Einfuegen ueber vla-InsertBlock (ActiveX) statt "command _.INSERT": das
;; legt fuer einen Block mit ATTDEFs automatisch ATTRIB-Entities mit den
;; Vorgabewerten an (wie TEFInsert.lsp/tefl-insert-element, ssg_ks_insert.lsp
;; u.a. bereits nutzen) - OHNE jeden einzelnen Insert-Prompt (Blockname/
;; Einfuegepunkt/Skalierung/Drehwinkel) ins Kommandozeilen-Fenster zu
;; schreiben. "command _.INSERT" wuerde das bei JEDEM verpackten Fund tun,
;; selbst wenn alle Werte per LISP vorgegeben sind - bei mehreren Funden
;; entsprechend viel Bildschirm-Rauschen. vla-get-ModelSpace wird hier LOKAL
;; ermittelt statt den (evtl. noch unbelegten) globalen "modelspace" aus
;; vf_core.lsp/Gefaellestrecke.lsp/ssg_ks_insert.lsp vorauszusetzen - die
;; koennten in dieser Sitzung noch gar nicht geladen worden sein, falls die
;; Zeichnung mit bereits vorhandenen VF_n/GF_n-Bloecken geoeffnet und direkt
;; exportiert wird, ohne vorher FOERDERANLAGE/GEFAELLESTRECKE aufzurufen.
;;
;; Rueckgabe: Liste von Paaren (proxy-ename . wrapper-ename) - der Wrapper
;; wird mitgefuehrt, weil erst NACH der ID-Vergabe daraus die ZUORDNUNG der
;; Kopie gesetzt werden kann (csv:sep-proxies-zuordnung-setzen). Die
;; proxy-enames dienen ausserdem csv:sep-proxies-loeschen nach dem Export.
(defun csv:sep-proxies-erzeugen ( / ms ss-all i ename ed bname nested pt rotation
;; Rueckgabe: Liste von Records
;; (wrapper-ename nested-ent nested-bname (x y z) R sx sy sz)
;; mit der fertigen Weltlage jedes Fundes (R = volle 3x3-Orientierung).
(defun csv:nested-welt-records (nested-pattern
/ ss-all i ename ed bname nested pt rotation
extrusion0 R0 psx psy psz rec nested-ent nested-bname
local-pt R-nested R-final scaled-pt rot-pt world-pt
T4 block-obj xform-fehler proxy-liste)
records)
(setq ss-all (ssget "X" (list (cons 0 "INSERT"))))
(if ss-all
(progn
(setq ms (vla-get-ModelSpace (vla-get-ActiveDocument (vlax-get-acad-object))))
(setq i 0)
(while (setq ename (ssname ss-all i))
(setq ed (entget ename))
(setq bname (cdr (assoc 2 ed)))
(setq nested
(if (wcmatch bname (csv:pattern-wrapper-fuer-proxies))
(ssg-collect-nested-inserts bname (csv:pattern-nested-separator))
(ssg-collect-nested-inserts bname nested-pattern)
)
)
(if nested
@@ -922,50 +947,10 @@
(+ (cadr pt) (cadr rot-pt))
(+ (caddr pt) (caddr rot-pt))))
(setq R-final (mat3-mul-mat3 R0 R-nested))
;; Volle 3D-Orientierung statt bisher nur eines Z-Drehwinkels:
;; ein einzelner Skalar-Drehwinkel kann eine Kippung ausserhalb
;; der XY-Ebene (Gefaellestrecke-/VF-Neigung) gar nicht
;; darstellen. Deshalb - wie insert-block-ks-to-ks (vf_core.lsp)
;; - am Ursprung mit Rotation=0 einfuegen und die komplette
;; Lage (Drehung UND Verschiebung) in EINER vla-TransformBy
;; anwenden, statt Rotation als vla-InsertBlock-Parameter.
(setq T4 (vlax-tmatrix
(list (list (car (car R-final)) (cadr (car R-final)) (caddr (car R-final)) (car world-pt))
(list (car (cadr R-final)) (cadr (cadr R-final)) (caddr (cadr R-final)) (cadr world-pt))
(list (car (caddr R-final)) (cadr (caddr R-final)) (caddr (caddr R-final)) (caddr world-pt))
(list 0.0 0.0 0.0 1.0))))
;; Gekapselt, weil hier - anders als beim frueheren festen
;; 1.0/1.0/1.0 - die tatsaechlichen Skalierungsfaktoren
;; durchgereicht werden: ein einzelner Sonderfall (entartete
;; Skalierung, gespiegelter Zwischenblock) darf nicht den
;; kompletten Export der Zeichnung abbrechen, sondern nur diese
;; eine Kopie ausfallen lassen - mit Meldung, welcher Block.
(setq block-obj
(vl-catch-all-apply 'vla-InsertBlock
(list ms (vlax-3D-point '(0.0 0.0 0.0)) nested-bname
(csv:skalierung-oder-eins (* psx (nth 3 rec)))
(csv:skalierung-oder-eins (* psy (nth 4 rec)))
(csv:skalierung-oder-eins (* psz (nth 5 rec)))
0.0)))
(cond
((vl-catch-all-error-p block-obj)
(princ (ssg-textf "exp-sep-proxy-fehler"
(list nested-bname bname
(vl-catch-all-error-message block-obj)))))
(t
(setq xform-fehler (vl-catch-all-apply 'vla-TransformBy (list block-obj T4)))
(if (vl-catch-all-error-p xform-fehler)
(progn
(princ (ssg-textf "exp-sep-proxy-fehler"
(list nested-bname bname
(vl-catch-all-error-message xform-fehler))))
(vl-catch-all-apply 'vla-Delete (list block-obj))
)
(setq proxy-liste
(cons (cons (vlax-vla-object->ename block-obj) ename) proxy-liste))
)
)
)
(setq records
(cons (list ename nested-ent nested-bname world-pt R-final
(* psx (nth 3 rec)) (* psy (nth 4 rec)) (* psz (nth 5 rec)))
records))
)
)
)
@@ -973,7 +958,91 @@
)
)
)
proxy-liste
(reverse records)
)
;; --- Einen Block an einer vollen 3D-Weltlage einfuegen ---
;; ms = ModelSpace-Objekt (der Aufrufer holt es EINMAL, nicht je Block)
;; bname = einzufuegende Blockdefinition
;; world-pt/R = Weltposition und volle 3x3-Orientierung (siehe
;; csv:nested-welt-records)
;; sx/sy/sz = Skalierung; der AS/ES-Marker fuegt bewusst mit 1.0 ein (ein
;; gestauchtes Rechteck waere als Marker unbrauchbar), die
;; Separator-Kopie mit der tatsaechlichen Skalierung ihres
;; Vorbilds.
;; kontext = Blockname des Wrappers, nur fuer die Fehlermeldung.
;;
;; Volle 3D-Orientierung statt eines einzelnen Z-Drehwinkels: ein Skalar-
;; Drehwinkel kann eine Kippung ausserhalb der XY-Ebene (Gefaellestrecke-/
;; VF-Neigung) gar nicht darstellen. Deshalb - wie insert-block-ks-to-ks
;; (vf_core.lsp) - am Ursprung mit Rotation=0 einfuegen und die komplette Lage
;; (Drehung UND Verschiebung) in EINER vla-TransformBy anwenden.
;;
;; Einfuegen ueber vla-InsertBlock (ActiveX) statt "command _.INSERT": das legt
;; fuer einen Block mit ATTDEFs automatisch ATTRIB-Entities mit den Vorgabe-
;; werten an - OHNE jeden einzelnen Insert-Prompt (Blockname/Einfuegepunkt/
;; Skalierung/Drehwinkel) ins Kommandozeilen-Fenster zu schreiben.
;;
;; Beide ActiveX-Aufrufe sind gekapselt: ein einzelner Sonderfall (entartete
;; Skalierung, gespiegelter Zwischenblock) darf nicht den kompletten Export der
;; Zeichnung abbrechen, sondern nur dieses eine Objekt ausfallen lassen - mit
;; Meldung, welcher Block. Rueckgabe: ename des neuen INSERT oder nil.
(defun csv:block-einfuegen-welt (ms bname world-pt R sx sy sz kontext
/ T4 block-obj xform-fehler erg)
(setq T4 (vlax-tmatrix
(list (list (car (car R)) (cadr (car R)) (caddr (car R)) (car world-pt))
(list (car (cadr R)) (cadr (cadr R)) (caddr (cadr R)) (cadr world-pt))
(list (car (caddr R)) (cadr (caddr R)) (caddr (caddr R)) (caddr world-pt))
(list 0.0 0.0 0.0 1.0))))
(setq block-obj
(vl-catch-all-apply 'vla-InsertBlock
(list ms (vlax-3D-point '(0.0 0.0 0.0)) bname
(csv:skalierung-oder-eins sx)
(csv:skalierung-oder-eins sy)
(csv:skalierung-oder-eins sz)
0.0)))
(cond
((vl-catch-all-error-p block-obj)
(princ (ssg-textf "exp-sep-proxy-fehler"
(list bname kontext (vl-catch-all-error-message block-obj))))
(setq erg nil))
(t
(setq xform-fehler (vl-catch-all-apply 'vla-TransformBy (list block-obj T4)))
(if (vl-catch-all-error-p xform-fehler)
(progn
(princ (ssg-textf "exp-sep-proxy-fehler"
(list bname kontext (vl-catch-all-error-message xform-fehler))))
(vl-catch-all-apply 'vla-Delete (list block-obj))
(setq erg nil))
(setq erg (vlax-vla-object->ename block-obj))
)
)
)
erg
)
;; --- Temporaere Kopie je verpacktem Separator_SP-Symbol ---
;; 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).
(defun csv:sep-proxies-erzeugen ( / ms recs rec proxy proxy-liste)
(setq recs (csv:nested-welt-records (csv:pattern-nested-separator)))
(if recs
(progn
(setq ms (vla-get-ModelSpace (vla-get-ActiveDocument (vlax-get-acad-object))))
(foreach rec recs
(setq proxy (csv:block-einfuegen-welt ms (nth 2 rec) (nth 3 rec) (nth 4 rec)
(nth 5 rec) (nth 6 rec) (nth 7 rec)
(cdr (assoc 2 (entget (nth 0 rec))))))
(if proxy
(setq proxy-liste
(cons (list proxy (nth 0 rec) (csv:proxy-typ (nth 2 rec)) (nth 3 rec))
proxy-liste)))
)
)
)
(reverse proxy-liste)
)
;; --- ZUORDNUNG der temporaeren Separator-Kopien aus ihrem Wrapper setzen ---
@@ -991,14 +1060,15 @@
;; 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.
;; Rueckgabe: Anzahl festgenagelter Kopien.
(defun csv:sep-proxies-zuordnung-setzen (proxy-liste / paar proxy wrapper wrapper-id
;; proxy-liste = Records (proxy-ename wrapper-ename typ (x y z)), siehe
;; csv:nested-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 paar proxy-liste
(setq proxy (car paar))
(setq wrapper (cdr paar))
(foreach rec proxy-liste
(setq proxy (nth 0 rec))
(setq wrapper (nth 1 rec))
(setq wrapper-id (cdr (assoc "ID" (ssg-attrib-read wrapper))))
(if (and wrapper-id (> (strlen wrapper-id) 0))
(progn
@@ -1020,107 +1090,507 @@
gesetzt
)
;; --- Fuer jeden Sepliste-Eintrag (AS/SEP/ES) eines VF_n/GF_n-Wrappers eine
;; ECHTE, temporaere Separator_SP-Kopie an dessen Weltkoordinate einfuegen ---
;; --- 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.
(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
;; (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)
(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))))
eintrag *csv-sepliste-id-by-handle*))
(setq *csv-sepliste-id-by-handle*
(cons (cons handle (list (cons lfdnr id))) *csv-sepliste-id-by-handle*))
)
id
)
;; --- 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)
;; --- sepliste-Eintraege und ausgepackte Kopien einander 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.
;;
;; Hintergrund: Seit die in einer VF_n/GF_n-Kette liegenden Separatoren (und die
;; AS/ES-Elemente) beim Bau nur noch als Koordinaten-Eintraege in der
;; "sepliste"-XDATA gefuehrt werden (ssg-sepliste-sammeln, ssg_ks_insert.lsp),
;; gibt es fuer sie KEIN reales INSERT mehr in der Zeichnung - vla-getboundingbox
;; kann also keine Box liefern, und der frueher genutzte Proxy-Mechanismus
;; csv:sep-proxies-erzeugen findet nichts (der sucht VERPACKTE Separator_SP-
;; Sub-Bloecke, die es nicht mehr gibt). Ohne Box fielen diese Separatoren aus
;; der Nachbarschafts-/Umlauf-/Zuordnungserkennung des Python-Exports.
;; 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).
;;
;; Diese Funktion schliesst die Luecke analog zu csv:sep-proxies-erzeugen: fuer
;; JEDEN Sepliste-Eintrag (typ SEP, AS ODER ES) wird ein echter, top-level
;; Separator_SP-INSERT der aktuellen Dimension an die gespeicherte Weltposition
;; (x y) gesetzt. Diese Kopie durchlaeuft danach unveraendert ID-Vergabe und
;; Export (Blockname-Muster Separator_SP* trifft zu) und liefert per
;; vla-getboundingbox die echte Symbol-Box. AS/ES bekommen dieselbe Symbol-Box
;; wie ein SEP - fuer die Nachbarschaftserkennung genuegt das; die AS/ES-
;; Sonderrolle (Kettenende am Kreisel) ergibt sich im Python-Teil aus der
;; sepliste-Reihenfolge, nicht aus der Boxgroesse.
;; 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.
;;
;; Die Drehung des Symbols ist in der sepliste NICHT gespeichert (nur typ/lfdnr/
;; x/y) - naeherungsweise die Wrapper-Drehung uebernehmen (bei ~210x150mm
;; Symbol und 200mm Nachbarschaftstoleranz unkritisch). Einfuegen ueber
;; vla-InsertBlock (kein Kommandozeilen-Prompt, legt ATTRIBs mit Vorgabewerten
;; an - identisch zu csv:sep-proxies-erzeugen).
;; 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))
)
)
)
(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)) aus
;; csv:sep-proxies-erzeugen UND csv:schleus-proxies-erzeugen - beide Listen
;; zusammen uebergeben, AS/ES und SEP stehen in DERSELBEN sepliste.
;; MUSS nach ssg-id-check-all laufen: vorher hat eine frisch erzeugte Kopie
;; noch keine ID.
;;
;; Rueckgabe: Liste von Paaren (proxy-ename . wrapper-ename) - gleiches Format
;; wie csv:sep-proxies-erzeugen, sodass csv:sep-proxies-zuordnung-setzen (setzt
;; ZUORDNUNG = Wrapper-ID) und csv:sep-proxies-loeschen unveraendert wirken.
(defun csv:sepliste-proxies-erzeugen ( / ss-all ms i ename ed bname sepliste-app
sepliste blk-eff rotation wpt eintrag
block-obj xform-fehler T4 cosr sinr
proxy-liste)
(setq ss-all (ssget "X" (list (cons 0 "INSERT"))))
(if (and ss-all (car (atoms-family 1 '("SSG-SEPLISTE-XDATA-LESEN"))))
(progn
(setq ms (vla-get-ModelSpace (vla-get-ActiveDocument (vlax-get-acad-object))))
(setq i 0)
(while (setq ename (ssname ss-all i))
(setq ed (entget ename))
(setq bname (cdr (assoc 2 ed)))
(setq sepliste-app
(cond
((wcmatch (strcase bname) "VF_*") "SSG_VF_SEP")
((wcmatch (strcase bname) "GF_*") "SSG_GF_SEP")
(t nil)))
(setq sepliste (if sepliste-app (ssg-sepliste-xdata-lesen ename sepliste-app)))
(if sepliste
(progn
;; Symbol-Blockdefinition der aktuellen Dimension sicherstellen.
(setq blk-eff (ssg-ils-block-laden "Separator_SP"))
(setq rotation (cond ((cdr (assoc 50 ed))) (0.0)))
(setq cosr (cos rotation) sinr (sin rotation))
(if blk-eff
(foreach eintrag sepliste
;; (typ lfdnr x y) - Weltkoordinate direkt aus der Sepliste.
(setq wpt (list (nth 2 eintrag) (nth 3 eintrag) 0.0))
(setq block-obj
(vl-catch-all-apply 'vla-InsertBlock
(list ms (vlax-3D-point '(0.0 0.0 0.0)) blk-eff 1.0 1.0 1.0 0.0)))
(cond
((vl-catch-all-error-p block-obj)
(princ (ssg-textf "exp-sep-proxy-fehler"
(list blk-eff bname
(vl-catch-all-error-message block-obj)))))
(t
;; Rz(rotation) + Verschiebung an die Sepliste-Weltposition
;; in EINER TransformBy (wie csv:sep-proxies-erzeugen).
(setq T4 (vlax-tmatrix
(list (list cosr (- sinr) 0.0 (car wpt))
(list sinr cosr 0.0 (cadr wpt))
(list 0.0 0.0 1.0 (caddr wpt))
(list 0.0 0.0 0.0 1.0))))
(setq xform-fehler (vl-catch-all-apply 'vla-TransformBy (list block-obj T4)))
(if (vl-catch-all-error-p xform-fehler)
(progn
(princ (ssg-textf "exp-sep-proxy-fehler"
(list blk-eff bname
(vl-catch-all-error-message xform-fehler))))
(vl-catch-all-apply 'vla-Delete (list block-obj)))
(setq proxy-liste
(cons (cons (vlax-vla-object->ename block-obj) ename) proxy-liste)))))
)
;; 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
)
;; ===========================================================================
;; 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:
;;
;; 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.
;;
;; 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.
(defun csv:asmark-blockname () "ZZ_ASES_MARKER")
;; Kantenlaenge (mm) des Rechtecks, aus cfg/export.cfg [Marker].
(defun csv:asmark-groesse-mm ()
(atof (export:cfg "Marker" "ases_groesse_mm" "200"))
)
;; Attribut-Schema des Markers. Alle ATTDEFs sind UNSICHTBAR
;; (ssg-attrib-make-defs setzt Flag 70=1) - sichtbar ist nur das Rechteck.
;; ID - fuellt ssg-id-check-all; wird beim naechsten Export uebernommen
;; ZUORDNUNG - ID des VF_n/GF_n-Wrappers, zu dem das Kettenende gehoert
;; (zugleich der Schluessel, ueber den der Marker wiedergefunden
;; wird, siehe csv:asmark-finden)
;; TYP - "AS" oder "ES", wie in der sepliste (ssg-sepliste-sammeln)
;; LFDNR - lfdnr des zugehoerigen sepliste-Eintrags
;; ELEMENT - Blockname des echten Schleuselements (AS_Element_90_links_3D):
;; haelt die Variante (30/90, links/rechts, 2D/3D) fest, die der
;; Marker selbst nicht mehr zeigt
;; ARTINR - Sivas-Nummer aus dem Attribut des echten Elements
(setq *csv-asmark-attrib-defs*
'(("ID" "") ("ZUORDNUNG" "") ("TYP" "") ("LFDNR" "") ("ELEMENT" "") ("ARTINR" ""))
)
;; --- Blockdefinition des Markers bei Bedarf anlegen ---
;; Es gibt bewusst KEINE DWG dafuer: der Marker ist reine Hilfsgeometrie und
;; soll in jeder Zeichnung ohne Blockbibliothek entstehen koennen. Aufbau wie
;; bei den Geruest-Baugruppen (GeruestInsert.lsp): Geometrie + ATTDEFs in den
;; Modellraum zeichnen, alles seit lastEnt einsammeln, per "_.-BLOCK" zur
;; Definition machen (das entfernt die Vorlage-Entities wieder).
;; Die Geometrie liegt auf Layer "0", damit der Marker den Layer seiner
;; Blockreferenz annimmt (den setzt csv:asmark-aktualisieren aus cfg/layer.cfg).
;;
;; WICHTIG - der Auswahlsatz nimmt NUR die beiden hier erzeugten Entity-Typen
;; auf: (entlast) liefert bei einer Blockreferenz MIT Attributen die Referenz
;; selbst, und (entnext) steigt von dort in deren ATTRIB-Unterentities ab. Ohne
;; Filter landen also die Attribute des zuletzt eingefuegten Blocks (z.B. eines
;; gerade gebauten VF_n) mit im Auswahlsatz - "_.-BLOCK" verschiebt sie in die
;; neue Blockdefinition und der Block steht danach OHNE Attribute da (real
;; beobachtet: VF_5 verlor beim ersten Export saemtliche Attribute, inklusive
;; seiner ID).
;; Rueckgabe: Blockname oder nil.
(defun csv:asmark-blockdef-sicherstellen ( / name h lastEnt ss e typ)
(setq name (csv:asmark-blockname))
(if (tblsearch "BLOCK" name)
name
(progn
(setq h (/ (csv:asmark-groesse-mm) 2.0))
(setq lastEnt (entlast))
(entmake (list '(0 . "LWPOLYLINE") '(100 . "AcDbEntity") '(8 . "0")
'(100 . "AcDbPolyline") '(90 . 4) '(70 . 1)
(cons 10 (list (- h) (- h)))
(cons 10 (list h (- h)))
(cons 10 (list h h))
(cons 10 (list (- h) h))))
(ssg-attrib-make-defs *csv-asmark-attrib-defs* (/ h 2.0))
(setq ss (ssadd))
(setq e (if lastEnt (entnext lastEnt) (entnext)))
(while e
(setq typ (cdr (assoc 0 (entget e))))
(if (member typ '("LWPOLYLINE" "ATTDEF")) (ssadd e ss))
(setq e (entnext e))
)
(command "_.-BLOCK" name (list 0.0 0.0 0.0) ss "")
(if (tblsearch "BLOCK" name)
(progn (princ (ssg-textf "exp-asmark-blockdef" (list name))) name)
(progn (princ (ssg-textf "exp-asmark-blockdef-fehler" (list name))) nil)
)
)
)
)
;; --- Vorhandene Marker der Zeichnung einsammeln ---
;; Rueckgabe: Liste von (ename zuordnung typ lfdnr id), alle Werte als String
;; (fehlende Attribute als "").
(defun csv:asmark-sammeln ( / ss i ename attribs out)
(setq ss (ssget "X" (list (cons 0 "INSERT") (cons 2 (csv:asmark-blockname)))))
(if ss
(progn
(setq i 0)
(while (setq ename (ssname ss i))
(setq attribs (ssg-attrib-read ename))
(setq out
(cons (list ename
(cond ((cdr (assoc "ZUORDNUNG" attribs))) (""))
(cond ((cdr (assoc "TYP" attribs))) (""))
(cond ((cdr (assoc "LFDNR" attribs))) (""))
(cond ((cdr (assoc "ID" attribs))) ("")))
out))
(setq i (1+ i))
)
)
)
proxy-liste
(reverse out)
)
;; --- Temporaere Separator_SP-Kopien (csv:sep-proxies-erzeugen /
;; csv:sepliste-proxies-erzeugen) nach dem Export wieder entfernen ---
;; proxy-liste = Paare (proxy-ename . wrapper-ename), nur der car wird geloescht.
(defun csv:sep-proxies-loeschen (proxy-liste)
(foreach paar proxy-liste (entdel (car paar)))
;; --- 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.
(defun csv:asmark-finden (bestand zuordnung typ lfdnr / m treffer)
(foreach m bestand
(if (and (null treffer)
(= (nth 1 m) zuordnung)
(= (nth 2 m) typ)
(= (nth 3 m) lfdnr))
(setq treffer m)
)
)
treffer
)
;; --- 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.
;;
;; 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
;; 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)
(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 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))
(foreach paar paare
(setq lfdnr (nth 0 paar))
(setq elem (nth (nth 1 paar) eigene))
(setq typ (csv:proxy-typ (nth 2 elem)))
(setq alt (csv:asmark-finden bestand wrapper-id typ (itoa lfdnr)))
(setq alt-id "")
(if alt
(progn
(setq alt-id (nth 4 alt))
(entdel (nth 0 alt))
(setq bestand (vl-remove alt bestand))
(setq uebernommen (1+ uebernommen))
)
(setq neu (1+ neu))
)
;; Marker mit Skalierung 1.0 einfuegen - die Skalierung des
;; Elements gehoert dem Element, ein gestauchtes Marker-Rechteck
;; 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))
(if mark
(progn
;; ARTINR nur lesen, wenn der Sub-INSERT ueberhaupt ATTRIBs
;; fuehrt (Gruppe 66 = 1): ssg-attrib-read wandert per entnext
;; bis zum SEQEND - fehlt der, laeuft es ueber das Element
;; hinaus durch die restliche Blockdefinition und liefert das
;; ARTINR IRGENDEINES spaeteren Bausteins der Kette.
(setq artinr "")
(if (equal 1 (cdr (assoc 66 (entget (nth 1 elem)))))
(setq artinr
(cond ((cdr (assoc "ARTINR" (ssg-attrib-read (nth 1 elem))))) (""))))
(ssg-attrib-set-on mark
(list (cons "ID" alt-id)
(cons "ZUORDNUNG" wrapper-id)
(cons "TYP" typ)
(cons "LFDNR" (itoa lfdnr))
(cons "ELEMENT" (nth 2 elem))
(cons "ARTINR" artinr)))
(ssg-layer-setzen mark "ases_marker")
(setq ergebnis (cons (list mark wrapper typ lfdnr) ergebnis))
)
)
)
)
)
)
)
)
;; Rest des Bestands: kein Kettenende mehr dahinter (Kette geloescht, Eintrag
;; entfallen, Wrapper ohne sepliste). Marker sind abgeleitete Geometrie -
;; ohne Kette bleiben sie nicht stehen.
(foreach m bestand
(entdel (nth 0 m))
(setq verwaist (1+ verwaist))
)
(if (or (> neu 0) (> uebernommen 0) (> verwaist 0))
(princ (ssg-textf "exp-asmark-bilanz"
(list (itoa neu) (itoa uebernommen) (itoa verwaist)))))
(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.
;; 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
;; (die Kopie selbst) wird geloescht.
;;
;; Der Debug-Schalter "csv_leave_proxies" (ssg_dbg.lsp) unterdrueckt das
;; Loeschen: die Kopien bleiben zur Ansicht in der Zeichnung stehen, damit sich
;; die tatsaechliche Lage der exportierten Separatoren gegen die
;; sepliste-Koordinaten pruefen laesst. *cs-sep-fix-by-handle* und
;; *csv-sepliste-id-by-handle* werden trotzdem zurueckgesetzt - sie gelten nur
;; fuer den gerade gelaufenen Export.
(defun csv:sep-proxies-loeschen (proxy-liste / behalten)
(setq behalten (and (car (atoms-family 1 '("DBG-SCHALTER-P")))
(dbg-schalter-p "csv_leave_proxies")))
(if behalten
(princ (strcat "\n[EXPORT] csv_leave_proxies=1: "
(itoa (length proxy-liste))
" Separator-Proxy-Kopie(n) bleiben in der Zeichnung"
" (Abschalten: (dbg-schalter-off \"csv_leave_proxies\"))"))
(foreach rec proxy-liste (entdel (car rec)))
)
(setq *cs-sep-fix-by-handle* nil)
(setq *csv-sepliste-id-by-handle* nil)
(princ)
)
@@ -1132,6 +1602,12 @@
;; Omniflo Boegen/Weichen = rein numerische Blocknamen (SivasNr)
;; Vario* = VarioFoerderer-Bloecke
;; GF_* = Gefaellestrecke Compound-Bloecke
;; ZZ_ASES_MARKER = die Marker der AS-/ES-Kettenenden einer VF_n/GF_n-Strecke
;; (siehe csv:asmark-aktualisieren; pattern_ases_marker in cfg/export.cfg).
;; Das Kettenende selbst steckt verpackt in der Wrapper-Blockdefinition und
;; waere sonst weder sichtbar noch exportierbar; der Marker vertritt es -
;; mit eigener, ueber Exportlaeufe stabiler ID und den Attributen
;; TYP/LFDNR/ELEMENT/ARTINR.
;; Separator_SP*/S-LP*, Scanner* = eigenstaendige Sensor-Bloecke (siehe
;; pattern_separator/pattern_scanner in cfg/export.cfg). Landen in
;; export_raw.json fuer BEIDE Exporte (EXPORTCSV/EXPORTSIVAS teilen sich
@@ -1170,6 +1646,7 @@
(wcmatch (strcase bname) (export:pattern "pattern_gerade" "AP110*,AP_110*"))
(wcmatch bname (export:pattern "pattern_strecke"
"Vario*,Staustrecke*,AUS_Element*,EIN_Element*,VF_*,GF_*"))
(wcmatch bname (export:pattern "pattern_ases_marker" "ZZ_ASES_MARKER*"))
(wcmatch bname (export:pattern "pattern_separator" "Separator_SP*,S-LP*"))
(wcmatch bname (export:pattern "pattern_scanner" "Scanner*"))
(wcmatch bname (export:pattern "pattern_btmt_beladung" "BTMT-Beladung*"))
@@ -1227,25 +1704,19 @@
;; 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 der erzeugten
;; Separator-Proxies (der Aufrufer entfernt sie am Ende wieder). label dient
;; nur den Meldungen.
(defun csv:export-vorbereiten (label / sep-proxies)
;; 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.
(defun csv:export-vorbereiten (label / sep-proxies asmarks)
;; 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).
;;
;; HINWEIS: csv:sepliste-proxies-erzeugen (echte Kopien fuer die nur noch in
;; der sepliste-XDATA gefuehrten SEP/AS/ES-Eintraege der VF_n/GF_n-Wrapper)
;; ist BEWUSST NOCH NICHT eingehaengt: solange der Python-Export dieselben
;; Ketten-Separatoren bereits aus der sepliste erzeugt (build_separator_kette_
;; items), wuerden beide Wege jeden Separator DOPPELT zaehlen. Der Umbau auf
;; "LISP-Proxies fuehren, Python-Sonderweg raus" (inkl. Typmarker AS/ES) haengt
;; ausserdem am generellen vla-getboundingbox-Ausfall (Kreisel/GF/VF haben in
;; betroffenen Exporten ebenfalls keine bbox) - der ist zuerst zu klaeren.
;; Die Funktion bleibt erhalten und ist einsatzbereit, sobald der Python-
;; Sonderweg entfernt und AS/ES markiert sind.
(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-*).
(setq asmarks (csv:asmark-aktualisieren))
;; 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
@@ -1256,8 +1727,17 @@
(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.
;; 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)
;; Vor dem Export HOEHE/DREHUNG aller Omniflo-Elemente aktualisieren
(princ (ssg-textf "exp-update-hoehe-drehung" (list label)))
(omni:update-all-attribs)
@@ -1293,6 +1773,8 @@
;; 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 Kopien zurueckgeben - die AS/ES-Marker bleiben
;; bewusst in der Zeichnung stehen und duerfen nicht mitgeloescht werden.
sep-proxies)
;; --- Python-Export-Skript aufrufen und Ergebnis auswerten ---------------
@@ -1361,7 +1843,7 @@
first-block count out-dir sep-proxies)
;; Zeichnungszustand fuer den Export herrichten (Proxies, IDs, Zuordnung,
;; Attribut-Update, Sep/Scan-Zaehlung, REGENALL, bbox-Reset) - ausgelagert.
;; Rueckgabe: die erzeugten Separator-Proxies (unten wieder zu entfernen).
;; Rueckgabe: alle erzeugten Kopien (unten wieder zu entfernen).
(setq sep-proxies (csv:export-vorbereiten label))
(princ (ssg-textf "exp-collect-blocks" (list label)))
@@ -1428,7 +1910,7 @@
)
)
)
;; Temporaere Separator-/Scanner-Kopien wieder entfernen - sie haben ihren
;; Temporaere Separator-/AS-/ES-Kopien wieder entfernen - sie haben ihren
;; Zweck (ID-Vergabe + JSON-Zeile ueber die obigen, unveraenderten
;; Sammel-Funktionen) erfuellt.
(csv:sep-proxies-loeschen sep-proxies)