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
+113 -5
View File
@@ -220,6 +220,14 @@
(+ (car einfuegepunkt) (- (* chv dx-loc) (* shv dy-loc)))
(+ (cadr einfuegepunkt) (+ (* shv dx-loc) (* chv dy-loc)))
(+ (caddr einfuegepunkt) dz-loc)))
(ssg-sepliste-dbg
(strcat "insert-block-by-ks " blockname
" hz=" (rtos (float hz) 2 2)
" einfuegepunkt=" (ssg-sepliste-dbg-pt einfuegepunkt)
" ks_ein_lokal=" (ssg-sepliste-dbg-pt (car ks-ein))
" ks_aus_lokal=" (ssg-sepliste-dbg-pt (car ks-aus))
" offset(=Gruppencode 10)=" (ssg-sepliste-dbg-pt offset)
" ausgang=" (ssg-sepliste-dbg-pt ausgang)))
(princ (ssg-textf "vfc-ks-aus-xyz"
(list (rtos (car ausgang) 2 2) (rtos (cadr ausgang) 2 2) (rtos (caddr ausgang) 2 2))))
ausgang
@@ -266,6 +274,12 @@
(+ (car startpunkt) (* laenge chv cvv))
(+ (cadr startpunkt) (* laenge shv cvv))
(+ (caddr startpunkt) (* laenge (- svv)))))
(ssg-sepliste-dbg
(strcat "insert-inclined-scaled-block " blockname
" laenge=" (rtos laenge 2 2) " winkel=" (rtos (float winkel) 2 2)
" hz=" (rtos (float hz) 2 2) " scale=" (rtos scale 2 4)
" startpunkt(=Gruppencode 10)=" (ssg-sepliste-dbg-pt startpunkt)
" endpunkt=" (ssg-sepliste-dbg-pt endpunkt)))
(princ (ssg-textf "vfc-endpunkt-xyz"
(list (rtos (car endpunkt) 2 2) (rtos (cadr endpunkt) 2 2) (rtos (caddr endpunkt) 2 2))))
endpunkt
@@ -346,6 +360,17 @@
(+ (car startpunkt) (+ (* dx chv cvv) (* dz chv svv)))
(+ (cadr startpunkt) (+ (* dx shv cvv) (* dz shv svv)))
(+ (caddr startpunkt) (+ (* dx (- svv)) (* dz cvv)))))
(ssg-sepliste-dbg
(strcat "insert-rotated-block-with-ks " blockname
" winkel=" (rtos (float winkel) 2 2) " hz=" (rtos (float hz) 2 2)
" startpunkt=" (ssg-sepliste-dbg-pt startpunkt)
" ks_ein_lokal=" (ssg-sepliste-dbg-pt (list ein-x ein-y ein-z))
" ks_aus_lokal=" (if ks-aus-raw
(ssg-sepliste-dbg-pt (list aus-x aus-y aus-z))
"nil (Fallback dx/dz aus cfg)")
" dx=" (rtos dx 2 2) " dz=" (rtos dz 2 2)
" ins-pt(=Gruppencode 10)=" (ssg-sepliste-dbg-pt ins-pt)
" endpunkt=" (ssg-sepliste-dbg-pt endpunkt)))
(princ (ssg-textf "vfc-endpunkt-xyz-dxdz"
(list (rtos (car endpunkt) 2 2) (rtos (cadr endpunkt) 2 2) (rtos (caddr endpunkt) 2 2)
(rtos dx 2 1) (rtos dz 2 1))))
@@ -371,6 +396,73 @@
;; bekommt nur eine laufende Nummer innerhalb der Liste. Der Export bildet
;; daraus bei Bedarf eigene, global fortlaufende TeileIds.
;; --- Diagnose-Protokoll (Schalter "vf-sepliste", siehe ssg_dbg.lsp) ---------
;; TEMPORAER fuer die Fehlersuche "gespiegelte Sepliste-Koordinate (lfdnr=2)" -
;; nach Abschluss mitsamt Schalter wieder entfernen.
;; Der Schalter ist in ssg_dbg.lsp mit Default 1 vorregistriert, das Protokoll
;; laeuft also nach einem BricsCAD-Neustart ohne weiteres Zutun. Es genuegt,
;; EINE VF-/GF-Kette zu bauen - das Protokoll landet in
;; DXFM_LOG/vf_sepliste.dbg. Protokolliert wird der komplette Weg der
;; Sepliste-Koordinaten: je Einfuegeprimitive der Startpunkt, die am Block
;; gemessenen KS_EIN/KS_AUS-Lokalpunkte, der daraus berechnete
;; Block-Einfuegepunkt (ins-pt = genau der Wert, den die Sepliste spaeter als
;; Gruppencode 10 einsammelt) und der Folgepunkt - sowie am Ende die
;; entnext-Wanderung von ssg-sepliste-sammeln mit JEDEM gefundenen INSERT.
;; Hintergrund: im Export tauchen Sepliste-Koordinaten auf, die nicht zur
;; sichtbaren Geometrie passen (x exakt negiert) - die Herkunft dieses Werts
;; ist ohne Laufzeit-Protokoll nicht zu klaeren.
;;
;; Nutzt NUR die Schalterverwaltung aus ssg_dbg.lsp, aber BEWUSST einen eigenen
;; Dateihandle statt dbgopen/dbgmsg: ssg_dbg fuehrt mit *dbg-path* genau EINE
;; global offene Datei. Der Linienzug-Bau (vfl-modus1, Schalter per Default EIN)
;; oeffnet seine vfl_modus1.dbg und haelt *dbg-path* damit besetzt - die
;; Sepliste-Zeilen waeren sonst stillschweigend dort gelandet statt in einer
;; eigenen Datei. Genau dieser Bau ist aber der zu protokollierende Vorgang.
(if (not (boundp '*ssg-sepliste-dbg-pfad*)) (setq *ssg-sepliste-dbg-pfad* nil))
;; Legt die Datei beim ERSTEN Aufruf je Sitzung an (leert sie dabei) und
;; liefert den Pfad; danach nur noch den gemerkten Pfad. nil = Schalter aus.
(defun ssg-sepliste-dbg-oeffnen ( / dir fh)
(if (and (car (atoms-family 1 '("DBG-SCHALTER-P")))
(dbg-schalter-p "vf-sepliste"))
(progn
(if (null *ssg-sepliste-dbg-pfad*)
(progn
(setq dir (getenv "DXFM_LOG"))
(if (or (null dir) (= dir "")) (setq dir "."))
(if (and (/= (substr dir (strlen dir) 1) "\\")
(/= (substr dir (strlen dir) 1) "/"))
(setq dir (strcat dir "\\")))
(setq *ssg-sepliste-dbg-pfad* (strcat dir "vf_sepliste.dbg"))
(setq fh (open *ssg-sepliste-dbg-pfad* "w"))
(if fh
(progn
(write-line "=== vf-sepliste Diagnose ===" fh)
(close fh)
(princ (strcat "\n[DBG] vf-sepliste-Protokoll: " *ssg-sepliste-dbg-pfad*)))
(progn
(princ (strcat "\n[DBG] FEHLER: " *ssg-sepliste-dbg-pfad*
" nicht schreibbar"))
(setq *ssg-sepliste-dbg-pfad* nil)))))
*ssg-sepliste-dbg-pfad*)
nil))
;; Eine Zeile anhaengen (kein Puffer - ein Bau erzeugt nur wenige hundert
;; Zeilen, dafuer geht keine Zeile verloren, wenn der Bau abbricht).
(defun ssg-sepliste-dbg (msg / pfad fh)
(if (setq pfad (ssg-sepliste-dbg-oeffnen))
(progn
(setq fh (open pfad "a"))
(if fh (progn (write-line msg fh) (close fh)))))
(princ))
;; Punkt (x y z) kompakt als "(x, y, z)" - nil-sicher.
(defun ssg-sepliste-dbg-pt (pt)
(if (and pt (listp pt))
(strcat "(" (rtos (car pt) 2 2) ", " (rtos (cadr pt) 2 2) ", "
(rtos (cond ((caddr pt)) (0.0)) 2 2) ")")
"nil"))
;; Einfaches Chunking eines Strings in Stuecke <= n Zeichen (DXF-Limit einer
;; 1000-Gruppe ist 255 Byte) - eigenstaendige Kopie statt vfl-chunk-string
;; (vf_linienzug.lsp), da diese Datei laut Lademechanismus VOR vf_linienzug
@@ -387,22 +479,32 @@
;; Baureihenfolge (identische entnext-Wanderung wie die Auswahlsatz-Sammlung
;; der Aufrufer, nur mit wcmatch-Filter). Rueckgabe: Liste von
;; (typ lfdnr x y), typ = "SEP"/"AS"/"ES", lfdnr 1-basiert in Baureihenfolge.
(defun ssg-sepliste-sammeln (lastEnt / ent bname typ lfdnr pt liste)
(defun ssg-sepliste-sammeln (lastEnt / ent ed bname typ lfdnr pt liste)
(ssg-sepliste-dbg "=== ssg-sepliste-sammeln: entnext-Wanderung ab lastEnt ===")
(setq liste '() lfdnr 0)
(setq ent (if lastEnt (entnext lastEnt) (entnext)))
(while ent
(if (= (cdr (assoc 0 (entget ent))) "INSERT")
(setq ed (entget ent))
(if (= (cdr (assoc 0 ed)) "INSERT")
(progn
(setq bname (cdr (assoc 2 (entget ent))))
(setq bname (cdr (assoc 2 ed)))
(setq typ (cond
((wcmatch bname "Staustrecke_Separator_SP_300_mm*") "SEP")
((wcmatch bname "AS_Element_*") "AS")
((wcmatch bname "ES_Element_*") "ES")
(t nil)))
(setq pt (cdr (assoc 10 ed)))
;; JEDEN INSERT protokollieren (auch die nicht klassifizierten) - nur so
;; laesst sich im .dbg nachvollziehen, welches Entity einen auffaelligen
;; Einfuegepunkt liefert und in welcher Reihenfolge es entstand.
(ssg-sepliste-dbg
(strcat "INSERT " bname
" handle=" (cond ((cdr (assoc 5 ed))) ("-"))
" typ=" (cond (typ) ("-"))
" pt=" (ssg-sepliste-dbg-pt pt)))
(if typ
(progn
(setq lfdnr (1+ lfdnr))
(setq pt (cdr (assoc 10 (entget ent))))
(setq liste (cons (list typ lfdnr (car pt) (cadr pt)) liste))
)
)
@@ -410,7 +512,13 @@
)
(setq ent (entnext ent))
)
(reverse liste)
(setq liste (reverse liste))
(ssg-sepliste-dbg (strcat "Ergebnis: " (itoa (length liste)) " Sepliste-Eintraege"))
(foreach e liste
(ssg-sepliste-dbg
(strcat " " (nth 0 e) " lfdnr=" (itoa (nth 1 e))
" x=" (rtos (nth 2 e) 2 2) " y=" (rtos (nth 3 e) 2 2))))
liste
)
;; Koordinate mit GENAU 2 Nachkommastellen formatieren - bewusst OHNE rtos.