f2b72df6e0
ssg-id-generate wird bereits ueberall sonst direkt beim Einfuegen aufgerufen (KreiselInsert.lsp, Gefaellestrecke.lsp, vf_core.lsp, vf_linienzug.lsp, OmniModulInsert.lsp, TEFInsert.lsp). Separator/Scanner waren die einzige Ausnahme: weder ils-insert-sensor (Produktion) noch mubea:build-separator-one (Testharness) riefen es auf - ihre ID kam bisher ausschliesslich aus dem einmaligen ssg-id-check-all-Durchlauf vor dem Export. Das war vermutlich die Ursache fuer die beobachtete doppelte ID (Kreisel und Separator teilten sich "0003"). - Lisp/SSG_LIB_Commands.lsp (ils-insert-sensor) und tests/test_mubea.lsp (mubea:build-separator-one) rufen jetzt ssg-id-generate direkt nach dem INSERT auf. - ssg_id.lsp: ssg-id-set-and-verify prueft nach jedem ssg-attrib-set-on sofort zurueck und warnt laut, falls das Schreiben (z.B. mangels ID-ATTDEF an der Instanz) stillschweigend fehlschlaegt. Ausfuehrliches dbg-Logging (ssg-id-collect-blocks/-max/-generate/-check-all/-set-and- verify) fuer die weitere Diagnose. - ssg-id-check-all bleibt als Sicherheitsnetz fuer Faelle, die kein Insert-Hook abfangen kann (Copy/Array/Spiegeln in BricsCAD, Drawing- Merges). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
273 lines
10 KiB
Common Lisp
273 lines
10 KiB
Common Lisp
;; ============================================================
|
|
;; SSG_ID - Eindeutige ID-Verwaltung fuer SSG_LIB Bloecke
|
|
;;
|
|
;; Jedes eingefuegte Element erhaelt ein Attribut "ID" mit
|
|
;; einer eindeutigen, vierstelligen Nummer (z.B. "0001").
|
|
;;
|
|
;; Befehle:
|
|
;; IDGENERATE - Weist dem zuletzt eingefuegten Block eine neue ID zu
|
|
;; IDSCHECK - Prueft alle Bloecke auf doppelte IDs und korrigiert sie
|
|
;;
|
|
;; Funktionen:
|
|
;; (ssg-id-max) - Hoechste ID in der Zeichnung ermitteln
|
|
;; (ssg-id-generate ent) - Neue ID erzeugen und auf Block setzen
|
|
;; (ssg-id-check-all) - Doubletten pruefen und korrigieren
|
|
;; (ssg-id-format n) - Zahl als 4-stelligen String formatieren
|
|
;; ============================================================
|
|
|
|
|
|
;; --- ID als 4-stelligen String formatieren ---
|
|
;; n = Integer (z.B. 1 -> "0001", 42 -> "0042")
|
|
(defun ssg-id-format (n / s)
|
|
(setq s (itoa n))
|
|
(while (< (strlen s) 4)
|
|
(setq s (strcat "0" s))
|
|
)
|
|
s
|
|
)
|
|
|
|
|
|
;; --- Alle exportierbaren INSERT-Entities sammeln ---
|
|
;; Gleiche Filterlogik wie csv:collect-export-blocks (siehe dort auch
|
|
;; pattern_separator/pattern_scanner in cfg/export.cfg - Separator_SP/S-LP/
|
|
;; Scanner sind eigenstaendige Sensor-Bloecke, die inzwischen ein eigenes
|
|
;; ID-ATTDEF haben und daher hier ebenfalls erfasst werden muessen, sonst
|
|
;; bleibt ihr ID-Attribut trotz ATTDEF für immer leer - ssg-attrib-set-on
|
|
;; kann nur einen Wert SETZEN, wenn der Block ueberhaupt erst hier landet).
|
|
(defun ssg-id-collect-blocks ( / ss-all ss-out i ename ed bname)
|
|
(dbgf "ssg-id-collect-blocks")
|
|
(setq ss-all (ssget "X" (list (cons 0 "INSERT"))))
|
|
(dbgreturn (if (null ss-all)
|
|
nil
|
|
(progn
|
|
(setq ss-out (ssadd))
|
|
(setq i 0)
|
|
(while (setq ename (ssname ss-all i))
|
|
(setq ed (entget ename))
|
|
(setq bname (cdr (assoc 2 ed)))
|
|
(if (or
|
|
(wcmatch bname "KR_*,KREISEL_*,ECKRAD_*")
|
|
(wcmatch (strcase bname) "AP110*,AP_110*,AP60*,AP_60*,APG110*")
|
|
(wcmatch bname "Vario*,Staustrecke*,AUS_Element*,EIN_Element*,VF_*,GF_*")
|
|
(wcmatch bname "Separator_SP*,S-LP*,Scanner*")
|
|
(wcmatch bname "#*")
|
|
)
|
|
(ssadd ename ss-out)
|
|
)
|
|
(setq i (1+ i))
|
|
)
|
|
(if (= (sslength ss-out) 0) nil ss-out)
|
|
)
|
|
))
|
|
)
|
|
|
|
|
|
;; --- Hoechste ID in der Zeichnung ermitteln ---
|
|
;; Durchsucht alle exportierbaren Bloecke nach dem Attribut "ID".
|
|
;; Rueckgabe: Integer (hoechste ID) oder 0 wenn keine gefunden.
|
|
(defun ssg-id-max ( / ss i ename attribs id-val id-num max-id)
|
|
(dbgf "ssg-id-max")
|
|
(setq max-id 0)
|
|
(setq ss (ssg-id-collect-blocks))
|
|
(if ss
|
|
(progn
|
|
(setq i 0)
|
|
(while (setq ename (ssname ss i))
|
|
(setq attribs (ssg-attrib-read ename))
|
|
(setq id-val (cdr (assoc "ID" attribs)))
|
|
(if (and id-val (> (strlen id-val) 0))
|
|
(progn
|
|
(setq id-num (atoi id-val))
|
|
(if (> id-num max-id)
|
|
(setq max-id id-num)
|
|
)
|
|
)
|
|
)
|
|
(setq i (1+ i))
|
|
)
|
|
)
|
|
)
|
|
(dbgreturn max-id)
|
|
)
|
|
|
|
|
|
;; --- Neue ID erzeugen und auf einen Block setzen ---
|
|
;; ent = Entity-Name eines INSERT-Blocks mit ID-Attribut
|
|
;; Rueckgabe: Die zugewiesene ID als String oder nil bei Fehler
|
|
(defun ssg-id-generate (ent / max-id new-id new-id-str)
|
|
(dbgf "ssg-id-generate")
|
|
(dbg 'ent)
|
|
(dbgreturn (if (and ent (= (cdr (assoc 0 (entget ent))) "INSERT"))
|
|
(progn
|
|
(setq max-id (ssg-id-max))
|
|
(setq new-id (1+ max-id))
|
|
(setq new-id-str (ssg-id-format new-id))
|
|
(ssg-attrib-set-on ent (list (cons "ID" new-id-str)))
|
|
(princ (ssg-textf "id-new-assigned" (list new-id-str)))
|
|
new-id-str
|
|
)
|
|
(progn
|
|
(princ (ssg-text "id-invalid-block"))
|
|
nil
|
|
)
|
|
))
|
|
)
|
|
|
|
|
|
;; --- ID-Attribut setzen UND sofort verifizieren ---
|
|
;; ssg-attrib-set-on kann eine Tag/Wert-Zuweisung STILL VERWERFEN, wenn der
|
|
;; INSTANZ-spezifische ATTRIB-Eintrag fuer diesen Tag gar nicht existiert
|
|
;; (z.B. bei einer Blockreferenz, die vor dem Hinzufuegen des ID-ATTDEF
|
|
;; eingefuegt wurde und seither nicht per ATTSYNC aktualisiert wurde) -
|
|
;; ohne Fehlermeldung. Das fuehrt genau zu der Art von "unsichtbarem"
|
|
;; doppelten ID-Fehler, der ssg-id-check-all eigentlich verhindern soll:
|
|
;; die Funktion GLAUBT, sie habe eine neue ID vergeben, aber am Block
|
|
;; steht weiterhin die alte (bzw. gar keine) ID. Diese Hilfsfunktion liest
|
|
;; nach dem Setzen sofort zurueck und warnt laut, wenn es nicht geklappt hat.
|
|
(defun ssg-id-set-and-verify (ename new-id-str kontext-text / attribs-danach)
|
|
(dbgf "ssg-id-set-and-verify")
|
|
(dbg 'ename)
|
|
(dbg 'new-id-str)
|
|
(dbg 'kontext-text)
|
|
(ssg-attrib-set-on ename (list (cons "ID" new-id-str)))
|
|
(setq attribs-danach (ssg-attrib-read ename))
|
|
(dbgreturn (if (not (equal (cdr (assoc "ID" attribs-danach)) new-id-str))
|
|
(princ (strcat "\n[SSG_ID] WARNUNG: ID=" new-id-str " konnte NICHT auf "
|
|
(cdr (assoc 2 (entget ename))) " (Handle "
|
|
(cdr (assoc 5 (entget ename))) ", " kontext-text
|
|
") geschrieben werden - Block hat vermutlich kein "
|
|
"ID-ATTDEF (Attribut-Sync/Redefine noetig)."))
|
|
))
|
|
)
|
|
|
|
;; --- Alle Bloecke auf doppelte IDs pruefen und korrigieren ---
|
|
;; Findet Bloecke mit gleicher ID. Behaelt die erste Instanz,
|
|
;; weist Duplikaten neue IDs zu.
|
|
;; Rueckgabe: Anzahl korrigierter IDs
|
|
(defun ssg-id-check-all ( / ss i ename attribs id-val id-num
|
|
id-map entry max-id fixed-count new-id-str bname)
|
|
(dbgf "ssg-id-check-all")
|
|
(setq ss (ssg-id-collect-blocks))
|
|
(dbg 'ss)
|
|
(dbgp (strcat "Anzahl erfasster Bloecke: " (if ss (itoa (sslength ss)) "0")))
|
|
(dbgreturn (if (null ss)
|
|
(progn
|
|
(princ (ssg-text "id-check-no-blocks"))
|
|
0
|
|
)
|
|
(progn
|
|
;; Phase 1: Alle IDs sammeln und hoechste ID ermitteln
|
|
(dbgp "=== Phase 1: IDs sammeln ===")
|
|
(setq id-map nil)
|
|
(setq max-id 0)
|
|
(setq i 0)
|
|
(while (setq ename (ssname ss i))
|
|
(setq attribs (ssg-attrib-read ename))
|
|
(setq id-val (cdr (assoc "ID" attribs)))
|
|
(setq bname (cdr (assoc 2 (entget ename))))
|
|
(dbgp (strcat " [" (itoa i) "] " bname " (Handle " (cdr (assoc 5 (entget ename)))
|
|
") ID-Attrib-vorhanden=" (if (assoc "ID" attribs) "JA" "NEIN")
|
|
" id-val=" (if id-val (strcat "\"" id-val "\"") "nil")))
|
|
(if (and id-val (> (strlen id-val) 0))
|
|
(progn
|
|
(setq id-num (atoi id-val))
|
|
(if (> id-num max-id) (setq max-id id-num))
|
|
;; In Map eintragen: (id-num . (ename1 ename2 ...))
|
|
(setq entry (assoc id-num id-map))
|
|
(if entry
|
|
(progn
|
|
(dbgp (strcat " ID " id-val " bereits gesehen bei "
|
|
(itoa (length (cdr entry))) " anderen Block(en) -> DUPLIKAT-KANDIDAT"))
|
|
(setq id-map (subst (cons id-num (append (cdr entry) (list ename)))
|
|
entry id-map))
|
|
)
|
|
(setq id-map (cons (cons id-num (list ename)) id-map))
|
|
)
|
|
)
|
|
;; Block ohne ID -> neue ID zuweisen
|
|
(progn
|
|
(setq max-id (1+ max-id))
|
|
(setq new-id-str (ssg-id-format max-id))
|
|
(dbgp (strcat " -> keine/leere ID, weise neu zu: " new-id-str))
|
|
(ssg-id-set-and-verify ename new-id-str "Block ohne ID")
|
|
(princ (ssg-textf "id-check-missing-set"
|
|
(list new-id-str (cdr (assoc 2 (entget ename))))))
|
|
)
|
|
)
|
|
(setq i (1+ i))
|
|
)
|
|
(dbgp (strcat "Phase 1 abgeschlossen: max-id=" (itoa max-id)
|
|
" id-map hat " (itoa (length id-map)) " unterschiedliche ID-Werte"))
|
|
(foreach entry id-map
|
|
(if (> (length (cdr entry)) 1)
|
|
(dbgp (strcat " DUPLIKAT ID=" (ssg-id-format (car entry)) ": "
|
|
(itoa (length (cdr entry))) " Bloecke -> "
|
|
(apply 'strcat (mapcar
|
|
'(lambda (e) (strcat (cdr (assoc 2 (entget e))) "(Handle "
|
|
(cdr (assoc 5 (entget e))) ") "))
|
|
(cdr entry)))))
|
|
)
|
|
)
|
|
|
|
;; Phase 2: Duplikate korrigieren
|
|
(dbgp "=== Phase 2: Duplikate korrigieren ===")
|
|
(setq fixed-count 0)
|
|
(foreach entry id-map
|
|
(if (> (length (cdr entry)) 1)
|
|
(progn
|
|
;; Erste Instanz behalten, Duplikate neu nummerieren
|
|
(dbgp (strcat "ID=" (ssg-id-format (car entry)) ": behalte "
|
|
(cdr (assoc 2 (entget (car (cdr entry))))) " (Handle "
|
|
(cdr (assoc 5 (entget (car (cdr entry))))) "), nummeriere "
|
|
(itoa (1- (length (cdr entry)))) " weitere um"))
|
|
(foreach ename (cdr (cdr entry))
|
|
(setq max-id (1+ max-id))
|
|
(setq new-id-str (ssg-id-format max-id))
|
|
(dbgp (strcat " " (cdr (assoc 2 (entget ename))) " (Handle "
|
|
(cdr (assoc 5 (entget ename))) ") ID=" (ssg-id-format (car entry))
|
|
" -> " new-id-str))
|
|
(ssg-id-set-and-verify ename new-id-str
|
|
(strcat "Duplikat von ID=" (ssg-id-format (car entry))))
|
|
(princ (ssg-textf "id-check-duplicate"
|
|
(list (ssg-id-format (car entry)) new-id-str
|
|
(cdr (assoc 2 (entget ename))))))
|
|
(setq fixed-count (1+ fixed-count))
|
|
)
|
|
)
|
|
)
|
|
)
|
|
(dbgp (strcat "Phase 2 abgeschlossen: " (itoa fixed-count) " Duplikate korrigiert"))
|
|
|
|
(princ (ssg-textf "id-check-done" (list fixed-count)))
|
|
fixed-count
|
|
)
|
|
))
|
|
)
|
|
|
|
|
|
;; --- Befehl: IDSCHECK ---
|
|
;; Prueft alle Bloecke auf doppelte IDs und korrigiert sie.
|
|
(defun c:IDSCHECK ( / count)
|
|
(ssg-start "IDSCHECK" nil)
|
|
(setq count (ssg-id-check-all))
|
|
(princ (ssg-textf "id-check-corrected" (list count)))
|
|
(ssg-end)
|
|
(princ)
|
|
)
|
|
|
|
;; --- Befehl: IDGENERATE ---
|
|
;; Fordert den Benutzer auf, einen Block zu waehlen und weist eine neue ID zu.
|
|
(defun c:IDGENERATE ( / ent ed)
|
|
(ssg-start "IDGENERATE" nil)
|
|
(setq ent (car (entsel (ssg-text "id-select-block-prompt"))))
|
|
(if (and ent (= (cdr (assoc 0 (entget ent))) "INSERT"))
|
|
(ssg-id-generate ent)
|
|
(princ (ssg-text "id-generate-invalid"))
|
|
)
|
|
(ssg-end)
|
|
(princ)
|
|
)
|
|
|
|
(princ "\n[SSG_ID] Geladen.")
|
|
(princ)
|