Files
dxfmakros/Lisp/export.lsp
T

387 lines
13 KiB
Common Lisp

;; ============================================================
;; export.lsp - Export-Funktionen fuer INSERT-Bloecke
;;
;; Enthaelt:
;; - JSON-Sammler (csv:collect-export-blocks, csv:run-export)
;; - Python-basierter Export: EXPORTSIVAS, EXPORTCSV
;;
;; Alle Blockname-Muster und Datei-/Skriptnamen dieses Moduls sind
;; zentral in cfg/export.cfg (INI-Style, Abschnitte [blockpattern]
;; und [filenames]) deklariert und werden beim Laden des Moduls
;; einmalig eingelesen (export:load-cfg).
;; ============================================================
;; --- Export-Konfiguration aus cfg/export.cfg laden ---
;; Nutzt den zentralen INI-Parser aus ssg_core.lsp (ssg-load-ini / ssg-ini-get /
;; ssg-ini-list), damit fuer alle Module derselbe .cfg-Standard gilt.
(defun export:load-cfg ( / cfg-pfad)
(setq cfg-pfad (strcat (getenv "DXFM_CFG") "/export.cfg"))
(setq *export-cfg* (ssg-load-ini cfg-pfad))
(if (null *export-cfg*)
(princ (strcat "\n[export] WARNUNG: " cfg-pfad " nicht gefunden/leer, verwende Vorgaben."))
)
(princ)
)
;; --- Einzelwert aus *export-cfg* lesen, mit Fallback ---
;; section = "blockpattern" oder "filenames"
(defun export:cfg (section key default)
(ssg-ini-get *export-cfg* section key default)
)
;; --- Muster aus [blockpattern] als normalisierter wcmatch-String ---
;; key = Config-Key, z.B. "pattern_kreisel"
;; default-str = Fallback-Wert als kommagetrennter String, falls Config fehlt
;; Rueckgabe: kommagetrennter String ohne Leerzeichen, z.B. "KR_*,KREISEL_*"
(defun export:pattern (key default-str)
(ssg-ini-list *export-cfg* "blockpattern" key default-str)
)
;; Beim Laden des Moduls einmalig einlesen (Lazy-Loading via ssg-ensure "export")
(export:load-cfg)
;; --- String fuer JSON escapen (Anfuehrungszeichen/Backslash) ---
;; Konvertiert nicht-String-Werte (z.B. T, nil, Zahlen) defensiv in String.
(defun csv:json-escape (s)
(if (not (= (type s) 'STR))
(setq s (if s (vl-princ-to-string s) ""))
)
(setq s (vl-string-subst "\\\\" "\\" s))
(vl-string-subst "\\\"" "\"" s)
)
;; --- Attribute eines INSERT-Blocks als JSON-Objekt lesen ---
;; ename = Entity-Name des INSERT
;; Rueckgabe: String wie {"TAG1":"Wert1","TAG2":"Wert2"}
(defun csv:read-attribs (ename / obj ed typ tag wert parts)
(setq obj (entnext ename))
(setq parts nil)
(while obj
(setq ed (entget obj))
(setq typ (cdr (assoc 0 ed)))
(if (equal typ "SEQEND") (setq obj nil)
(progn
(if (equal typ "ATTRIB")
(setq parts (cons (strcat "\"" (csv:json-escape (cdr (assoc 2 ed)))
"\":\"" (csv:json-escape (cdr (assoc 1 ed))) "\"")
parts))
)
(setq obj (entnext obj))
)
)
)
(if parts
(progn
(setq parts (reverse parts))
(strcat "{" (car parts)
(apply 'strcat (mapcar '(lambda (p) (strcat "," p)) (cdr parts)))
"}")
)
"{}"
)
)
;; --- Einen INSERT-Block als JSON-Objekt schreiben ---
(defun csv:block-to-json (ename / ed blk-name layer pt rotation handle attribs)
(setq ed (entget ename))
(setq blk-name (cdr (assoc 2 ed)))
(setq layer (cdr (assoc 8 ed)))
(setq pt (cdr (assoc 10 ed)))
(setq rotation (cdr (assoc 50 ed)))
(setq handle (cdr (assoc 5 ed)))
(if (null rotation) (setq rotation 0.0))
(if (cdr (assoc 66 ed))
(setq attribs (csv:read-attribs ename))
(setq attribs "{}")
)
(strcat
" {\"block_name\":\"" (csv:json-escape blk-name) "\""
",\"layer\":\"" (csv:json-escape layer) "\""
",\"handle\":\"" (csv:json-escape handle) "\""
",\"x\":" (rtos (car pt) 2 4)
",\"y\":" (rtos (cadr pt) 2 4)
",\"z\":" (rtos (if (caddr pt) (caddr pt) 0.0) 2 4)
",\"rotation\":" (rtos (* (/ rotation pi) 180.0) 2 4)
",\"attribs\":" attribs
"}"
)
)
;; --- Relevante Bloecke sammeln ---
;; Filtert INSERT-Entities auf exportierbare Bloecke:
;; KR_* = Kreisel Compound-Bloecke
;; AP110* = Omniflo Geraden
;; Omniflo Boegen/Weichen = rein numerische Blocknamen (SivasNr)
;; Vario* = VarioFoerderer-Bloecke
;; GF_* = Gefaellestrecke Compound-Bloecke
;; Gibt Auswahlsatz zurueck oder nil.
(defun csv:collect-export-blocks ( / ss-all ss-out i ename ed bname)
(setq ss-all (ssget "X" (list (cons 0 "INSERT"))))
(if (null ss-all) (setq ss-out 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 (export:pattern "pattern_kreisel" "KR_*,KREISEL_*,ECKRAD_*"))
(wcmatch (strcase bname) (export:pattern "pattern_gerade" "AP110*,AP_110*"))
(wcmatch bname (export:pattern "pattern_strecke"
"Vario*,Staustrecke*,AUS_Element*,EIN_Element*,VF_*,GF_*"))
;; Rein numerische Namen = Omniflo SivasNr (Boegen/Weichen)
(wcmatch bname (export:pattern "pattern_numerisch" "#*"))
)
(ssadd ename ss-out)
)
(setq i (1+ i))
)
(if (= (sslength ss-out) 0) (setq ss-out nil))
)
)
ss-out
)
;; --- HOEHE/DREHUNG aller Omniflo-Elemente still aktualisieren ---
;; Wird automatisch vor jedem Export aufgerufen.
(defun omni:update-all-attribs ( / ss i ename ed bname teileart)
(setq ss (ssget "X" (list (cons 0 "INSERT"))))
(if ss
(progn
(setq i 0)
(while (setq ename (ssname ss i))
(setq ed (entget ename))
(setq bname (cdr (assoc 2 ed)))
(setq teileart (csv:get-attrib ename "TEILEART"))
(if (or teileart
(wcmatch (strcase bname) (export:pattern "pattern_gerade" "AP110*,AP_110*")))
(progn
(omni:set-hoehe-attrib ename)
(omni:set-drehung-attrib ename)
)
)
(setq i (1+ i))
)
)
)
)
;; --- Gemeinsame Export-Funktion ---
;; Sammelt relevante Bloecke, schreibt JSON, ruft Python auf.
;; label = Anzeigename (z.B. "EXPORTCSV")
;; py-name = Python-Skript-Name (z.B. "export_csv.py")
;; csv-name = Ergebnis-CSV-Name (z.B. "export.csv")
(defun csv:run-export (label py-name csv-name / ss i ename fh out-pfad
py-skript ergebnis-pfad cmd first-block count)
;; Vor dem Export: IDs aller Bloecke sicherstellen und Duplikate korrigieren
(princ (strcat "\n[" label "] Pruefe und vergebe IDs..."))
(ssg-id-check-all)
;; Vor dem Export HOEHE/DREHUNG aller Omniflo-Elemente aktualisieren
(princ (strcat "\n[" label "] Aktualisiere Hoehe/Drehung..."))
(omni:update-all-attribs)
(princ (strcat "\n[" label "] Sammle relevante Bloecke..."))
(setq ss (csv:collect-export-blocks))
(if (null ss)
(progn
(princ (strcat "\n[" label "] Keine relevanten Bloecke gefunden."))
(princ)
)
(progn
(setq count (sslength ss))
(princ (strcat "\n[" label "] " (itoa count) " Bloecke gefunden."))
;; JSON-Datei zusammenbauen
(setq out-pfad (strcat (getenv "DXFM_RESULTS") "/"
(export:cfg "filenames" "raw_json_datei" "export_raw.json")))
(setq fh (open out-pfad "w"))
(if (null fh)
(progn
(princ (strcat "\n[" label "] FEHLER: Kann Datei nicht oeffnen: " out-pfad))
(princ)
)
(progn
(write-line "[" fh)
(setq i 0)
(setq first-block T)
(while (setq ename (ssname ss i))
(if (not first-block)
(write-line "," fh)
)
(write-line (csv:block-to-json ename) fh)
(setq first-block nil)
(setq i (1+ i))
)
(write-line "]" fh)
(close fh)
(princ (strcat "\n[" label "] JSON geschrieben: " out-pfad))
;; Python-Skript aufrufen
(setq py-skript (strcat (getenv "DXFM_LIB") "/" py-name))
(if (findfile py-skript)
(progn
(setq ergebnis-pfad (strcat (getenv "DXFM_RESULTS") "/" csv-name))
(setq cmd (strcat (export:cfg "filenames" "python_exe" "python") " \"" py-skript "\""
" \"" out-pfad "\""
" \"" (getenv "DXFM_DATA") "\""
" \"" ergebnis-pfad "\""))
(princ (strcat "\n[" label "] Rufe Python auf..."))
(startapp "cmd" (strcat "/c " cmd))
(princ (strcat "\n[" label "] Export gestartet: " ergebnis-pfad))
)
(princ (strcat "\n[" label "] FEHLER: Python-Skript nicht gefunden: " py-skript))
)
)
)
)
)
(princ)
)
;; --- EXPORTSIVAS: Sivas-spezifischer CSV-Export mit Summierungszeilen ---
(defun c:EXPORTSIVAS ()
(csv:run-export "EXPORTSIVAS"
(export:cfg "filenames" "sivas_script" "export_sivas.py")
(export:cfg "filenames" "sivas_csv" "export_sivas.csv"))
)
;; --- EXPORTCSV: Einfache Item-Liste aller Bloecke (ohne Summierung) ---
(defun c:EXPORTCSV ()
(csv:run-export "EXPORTCSV"
(export:cfg "filenames" "csv_script" "export_csv.py")
(export:cfg "filenames" "csv_csv" "export.csv"))
)
;; ============================================================
;; OMNIFLO HOEHE/DREHUNG AKTUALISIEREN
;; ============================================================
;; --- Attribut-Wert eines INSERT-Blocks nach Tag lesen ---
(defun csv:get-attrib (ename tag / obj ed typ atag)
(setq obj (entnext ename))
(while obj
(setq ed (entget obj))
(setq typ (cdr (assoc 0 ed)))
(if (equal typ "SEQEND")
(setq obj nil)
(progn
(if (and (equal typ "ATTRIB")
(= (strcase (cdr (assoc 2 ed))) (strcase tag)))
(progn (setq obj nil) (setq atag (cdr (assoc 1 ed))))
(setq obj (entnext obj))
)
)
)
)
atag
)
;; --- Hoehe eines Omniflo-Blocks aus der Zeichnung ermitteln ---
;; Liest z-Koordinate des Einfuegepunkts.
;; Falls der Block KS_EIN, KS_AUS oder K1-K4 enthaelt,
;; wird die minimale Z-Koordinate dieser Unterelemente + Block-Z verwendet.
(defun omni:get-block-hoehe (ename / ed bname pt z-insert blk-tbl sub-ent sub-ed sub-bname sub-pt z-list z-min)
(setq ed (entget ename))
(setq bname (cdr (assoc 2 ed)))
(setq pt (cdr (assoc 10 ed)))
(setq z-insert (if (caddr pt) (caddr pt) 0.0))
(setq blk-tbl (tblsearch "BLOCK" bname))
(if (null blk-tbl)
z-insert
(progn
(setq sub-ent (entnext (cdr (assoc -2 blk-tbl))))
(setq z-list nil)
(while sub-ent
(setq sub-ed (entget sub-ent))
(if (= (cdr (assoc 0 sub-ed)) "INSERT")
(progn
(setq sub-bname (strcase (cdr (assoc 2 sub-ed))))
(if (wcmatch sub-bname
(export:pattern "pattern_ks_subblocks" "KS_EIN,KS_AUS,K1,K2,K3,K4"))
(progn
(setq sub-pt (cdr (assoc 10 sub-ed)))
(setq z-list (cons (if (caddr sub-pt) (caddr sub-pt) 0.0) z-list))
)
)
)
)
(setq sub-ent (entnext sub-ent))
)
(if z-list
(progn
(setq z-min (car z-list))
(foreach z (cdr z-list)
(if (< z z-min) (setq z-min z))
)
(+ z-insert z-min)
)
z-insert
)
)
)
)
;; --- HOEHE-Attribut eines Omniflo-Blocks setzen ---
;; Liest Z-Koordinate (ggf. aus KS-Unterblock) und schreibt als HOEHE-Attribut.
(defun omni:set-hoehe-attrib (ename / hoehe)
(setq hoehe (omni:get-block-hoehe ename))
(ssg-attrib-set-on ename (list (cons "HOEHE" (rtos hoehe 2 1))))
hoehe
)
;; --- DREHUNG-Attribut eines Omniflo-Blocks setzen ---
;; Liest CAD-Rotationswinkel (Gruppe 50, Bogenmass) und schreibt als DREHUNG-Attribut.
(defun omni:set-drehung-attrib (ename / ed rotation deg)
(setq ed (entget ename))
(setq rotation (cdr (assoc 50 ed)))
(if (null rotation) (setq rotation 0.0))
(setq deg (* (/ rotation pi) 180.0))
(ssg-attrib-set-on ename (list (cons "DREHUNG" (rtos deg 2 1))))
deg
)
;; ============================================================
;; C:OMNI_UPDATE_ATTRIBS - HOEHE und DREHUNG aller Omniflo-Elemente aktualisieren
;; Setzt HOEHE aus Z-Koordinate (bzw. min. KS-Sub-Block) und
;; DREHUNG aus CAD-Rotationswinkel fuer alle Bloecke mit TEILEART-Attribut.
;; ============================================================
(defun c:OMNI_UPDATE_ATTRIBS ( / ss i ename ed bname teileart count)
(setq ss (ssget "X" (list (cons 0 "INSERT"))))
(if (null ss)
(princ "\n[OMNI_UPDATE] Keine INSERT-Bloecke in der Zeichnung.")
(progn
(setq count 0)
(setq i 0)
(while (setq ename (ssname ss i))
(setq ed (entget ename))
(setq bname (cdr (assoc 2 ed)))
;; Nur Omniflo-Elemente (erkennbar an TEILEART-Attribut oder AP110-Blockname)
(setq teileart (csv:get-attrib ename "TEILEART"))
(if (or teileart
(wcmatch (strcase bname) (export:pattern "pattern_gerade" "AP110*,AP_110*")))
(progn
(omni:set-hoehe-attrib ename)
(omni:set-drehung-attrib ename)
(setq count (1+ count))
)
)
(setq i (1+ i))
)
(princ (strcat "\n[OMNI_UPDATE] " (itoa count) " Elemente aktualisiert (HOEHE, DREHUNG)."))
)
)
(princ)
)