Files
dxfmakros/Lisp/HwPlanung.lsp
T
Michael StanglandClaude Opus 5.5 8abb6b3c65 feat: HW Planung - Symbole erzeugen, Nummerierung, Ausgabeziel-Dialog
- Symbole erzeugen (HW_SYMBOLE, Einzelbefehle HW_SYM_*): Bauteile nach
  cfg/io_regeln.json (reduzierte Kopie aus kabellaengen), Symbole aus
  cfg/hw_symbole.json katalog-identisch nachgebaut (lib/hw_symbole.py),
  REALE_POSITION mit x auf dem Bauteil, Paare uebereinander, Ausweichen
  in y ohne Ueberlappung
- Symbole nummerieren (HW_NUMMERIEREN): lib/create_numbers.py samt
  error_collector.py und cfg/enumerate.cfg aus kabellaengen uebernommen
- Ausgabeziel per Dialog dcl/hw_ausgabe.dcl: aktuelle Zeichnung,
  bestehende Datei ergaenzen oder neue Datei
- Materialfluss mit XDATA SSG_HW_MF, ergaenzt vorhandene Datei
- Doku doc/hw_planung.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 16:39:37 +01:00

610 lines
26 KiB
Common Lisp

;; ============================================================
;; HwPlanung.lsp - Hardware-Planung (Menue "HW Planung")
;;
;; Ziel der Ausgabe: vor jedem Erzeugungsbefehl fragt der Dialog
;; dcl/hw_ausgabe.dcl (hw-ausgabe-waehlen), wohin:
;; - aktuelle Zeichnung: Python schreibt eine einheitenlose Zwischendatei
;; <dwg>_HW_tmp.dxf, die als Block bei 0,0 eingefuegt und aufgeloest wird
;; (hw-in-aktuelle-zeichnung); die fruehere Ausgabe (XDATA SSG_HW_MF bzw.
;; SSG_HW_SYM) wird danach entfernt
;; - bestehende Datei ergaenzen (Dateiauswahl)
;; - neue Datei (Name, Ordner DXFM_RESULTS)
;; In einer Datei ersetzt jeder Befehl nur seinen eigenen Anteil - was der
;; Anwender dort ergaenzt (Renamer-Rahmen, Polylinien), bleibt erhalten.
;;
;; HW_MATERIALFLUSS ("Materialfluss erzeugen", in der Ursprungszeichnung):
;; EXPORTCSV -> lib/hw_materialfluss.py zeichnet Kreisel-Rechtecke in
;; Drehrichtung, Strecken-Polylinien AS -> Separatoren -> ES, Weichen und
;; Be-/Entladungen auf die HW_*-Layer der _HW.dxf.
;;
;; HW_SYMBOLE ("Symbole erzeugen", in der Ursprungszeichnung): sucht die
;; Mechanik-Bauteile nach cfg/io_regeln.json (auch verschachtelt in
;; VF_n/GF_n/KREISEL_n), schreibt den Symbolplan als JSON und laesst
;; lib/hw_symbole.py die Symbole in die _HW.dxf setzen - deren Aussehen und
;; Attribute stehen in cfg/hw_symbole.json (keine Symbol-Bibliothek). Die Geraete-ID ist noch eine VORLAGE (z.B. MB1@@@) -
;; nummeriert wird erst mit HW_NUMMERIEREN, entlang der Rahmen/Polylinien,
;; die der Anwender in der _HW.dxf zieht. Einzelbefehle HW_SYM_VENTILE usw.
;;
;; HW_NUMMERIEREN ("Symbole nummerieren", in der geoeffneten _HW.dxf):
;; speichert die Zeichnung, laesst lib/create_numbers.py (Renamer-Bereiche,
;; cfg/enumerate.cfg) die Nummern berechnen und traegt sie in die offene
;; Zeichnung ein.
;;
;; Setzt export.lsp voraus (laedt die MNL vor).
;; ============================================================
(setq *hw-skript* "hw_materialfluss.py")
(setq *hw-sym-skript* "hw_symbole.py")
(setq *hw-num-skript* "create_numbers.py")
;; XDATA der erzeugten Objekte (gesetzt von lib/hw_materialfluss.py bzw.
;; lib/hw_symbole.py) - daran erkennt hw-alte-ausgabe die fruehere Ausgabe
(setq *hw-mf-app* "SSG_HW_MF")
(setq *hw-sym-app* "SSG_HW_SYM")
;; Letzte Wahl im Ausgabe-Dialog (modus datei name), je Sitzung
(if (not (boundp '*hw-ausgabe-letzt*)) (setq *hw-ausgabe-letzt* nil))
;; Geraeteart -> Praefix (Einzelbefehle unten, noch nicht im Menue)
(setq *hw-sym-arten*
'(("ventile" "MB") ("sensoren" "BG") ("motoren" "MA")
("motorschutz" "FC") ("encoder" "GF") ("scanner" "BX")))
;; ------------------------------------------------------------
;; Reine Kerne (tests/test_unit.lsp, tu-tests-hw)
;; ------------------------------------------------------------
;; Name der Ausgabedatei aus dem Zeichnungsnamen: "Mubea.dwg" -> "Mubea_HW.dxf"
(defun hw-dxf-name (dwgname)
(strcat (vl-filename-base dwgname) "_HW.dxf")
)
;; Zahl fuer JSON, unabhaengig von DIMZIN/LUPREC (rtos wuerde bei DIMZIN-
;; Unterdrueckung der fuehrenden Null ".5" liefern - kein gueltiges JSON).
;; Auf 0.001 gerundet: (hw-json-zahl -12.5) -> "-12.500"
(defun hw-json-zahl (x / n r s)
(setq n (fix (+ (* (abs x) 1000.0) 0.5))
r (itoa (rem n 1000)))
(while (< (strlen r) 3) (setq r (strcat "0" r)))
(setq s (strcat (itoa (/ n 1000)) "." r))
(if (and (< x 0.0) (> n 0)) (strcat "-" s) s)
)
(defun hw-json-text (s)
(strcat "\"" (ssg-json-escape s) "\"")
)
;; Rollen-Objekt zum Rollennamen
(defun hw-sym-rolle (regeln name / erg)
(foreach r (ssg-json-get regeln "rollen")
(if (and (null erg) (= (ssg-json-get r "rolle") name)) (setq erg r)))
erg
)
;; Rollennamen eines Bauteils - je Eintrag in "benoetigt" die erste Alternative
(defun hw-sym-bauteil-rollen (bauteil)
(mapcar 'car (ssg-json-get bauteil "benoetigt"))
)
;; wcmatch-Muster aller Mechanik-Bloecke (gross, kommagetrennt)
(defun hw-sym-muster (regeln / muster)
(foreach b (ssg-json-get regeln "bauteile")
(setq muster (append muster (ssg-json-get b "bloecke"))))
(strcase (ssg-join muster ","))
)
;; Bauteil-Objekt zum Blocknamen (erster Treffer) oder nil
(defun hw-sym-bauteil-fuer-block (regeln blockname / erg)
(foreach b (ssg-json-get regeln "bauteile")
(if (and (null erg)
(wcmatch (strcase blockname)
(strcase (ssg-join (ssg-json-get b "bloecke") ","))))
(setq erg b)))
erg
)
;; Symbol (Blockname) einer Rolle, ggf. abhaengig vom Blocknamen des Bauteils
;; (block_nach_name, z.B. Vario-Motorstation rechts -> Motor VARIO rechts)
(defun hw-sym-block-fuer (rolle bauteil-blockname / erg)
(foreach v (ssg-json-get rolle "block_nach_name")
(if (and (null erg)
(wcmatch (strcase bauteil-blockname) (strcase (ssg-json-get v "muster"))))
(setq erg (ssg-json-get v "block"))))
(cond (erg) ((ssg-json-get rolle "block")))
)
;; IO-Vorlage eines Symbols: Praefix + io_vorlage, z.B. "MB" + "1@@@" -> "MB1@@@"
(defun hw-sym-io-vorlage (regeln praefix)
(strcat praefix (cond ((ssg-json-get regeln "io_vorlage")) ("1@@@")))
)
;; Zielpunkt eines Bauteils in der _HW.dxf: flach (Z = 0) - die _HW.dxf ist
;; ein Grundriss, die Nummerierung arbeitet in XY.
(defun hw-sym-ziel (pt)
(list (car pt) (cadr pt) 0.0)
)
;; Plan aus Bauteil-Instanzen. instanz = (bauteil blockname pt key).
;; Rueckgabe: Liste von Records, je Rolle in der Reihenfolge von "benoetigt"
;; (= Stapelfolge der Symbole am Bauteil, erstes oben):
;; (praefix io-vorlage symbolblock zielpunkt rolle key bauteilname)
;; Den Einfuegepunkt rechnet lib/hw_symbole.py aus dem Zielpunkt und der
;; Symbolgeometrie (Marke REALE_POSITION auf dem Ziel, ohne Ueberlappung).
(defun hw-sym-plan (regeln instanzen / erg rolle)
(foreach ins instanzen
(foreach name (hw-sym-bauteil-rollen (car ins))
(if (setq rolle (hw-sym-rolle regeln name))
(setq erg (cons (list (ssg-json-get rolle "praefix")
(hw-sym-io-vorlage regeln (ssg-json-get rolle "praefix"))
(hw-sym-block-fuer rolle (nth 1 ins))
(hw-sym-ziel (nth 2 ins))
name
(nth 3 ins)
(ssg-json-get (car ins) "bauteil"))
erg)))))
(reverse erg)
)
;; Sortierung der Instanzen: Bauteil-Reihenfolge im Regelwerk, dann x, dann y
(defun hw-sym-instanzen-sortieren (regeln instanzen / bauteile)
(setq bauteile (mapcar '(lambda (b) (ssg-json-get b "bauteil"))
(ssg-json-get regeln "bauteile")))
;; Rang vorab anhaengen (rang . instanz), sortieren, wieder abnehmen
(mapcar 'cdr
(vl-sort
(mapcar '(lambda (ins)
(cons (vl-position (ssg-json-get (car ins) "bauteil") bauteile) ins))
instanzen)
'(lambda (a b)
(cond ((/= (car a) (car b)) (< (car a) (car b)))
((not (equal (car (nth 3 a)) (car (nth 3 b)) 0.5))
(< (car (nth 3 a)) (car (nth 3 b))))
(t (< (cadr (nth 3 a)) (cadr (nth 3 b))))))))
)
;; Symbolplan als JSON-Zeilen fuer lib/hw_symbole.py. praefixe = die
;; Geraetearten, die dieser Lauf einfuegt bzw. ersetzt; der Plan selbst ist
;; immer vollstaendig, damit die Lage nicht davon abhaengt.
(defun hw-sym-plan->json (praefixe plan / zeilen)
(setq zeilen
(mapcar
'(lambda (r)
(strcat " {\"praefix\": " (hw-json-text (nth 0 r))
", \"io\": " (hw-json-text (nth 1 r))
", \"block\": " (hw-json-text (nth 2 r))
", \"x\": " (hw-json-zahl (car (nth 3 r)))
", \"y\": " (hw-json-zahl (cadr (nth 3 r)))
", \"rolle\": " (hw-json-text (nth 4 r))
", \"key\": " (hw-json-text (nth 5 r))
", \"bauteil\": " (hw-json-text (nth 6 r)) "}"))
plan))
(append
(list "{"
(strcat " \"praefixe\": [" (ssg-join (mapcar 'hw-json-text praefixe) ", ") "],")
" \"symbole\": [")
(if zeilen
(append (mapcar '(lambda (z) (strcat z ",")) (reverse (cdr (reverse zeilen))))
(list (last zeilen))))
(list " ]" "}"))
)
;; Name einer neuen Ausgabedatei: Leerzeichen am Rand und eine Endung .dxf
;; werden entfernt (" Anlage_HW.DXF " -> "Anlage_HW")
(defun hw-name-ohne-dxf (name / n)
(setq n (vl-string-trim " " (cond (name) (""))))
(if (wcmatch (strcase n) "*.DXF")
(substr n 1 (- (strlen n) 4))
n)
)
;; Gueltiger Dateiname: nicht leer, keine Pfad- oder Sonderzeichen
(defun hw-name-gueltig-p (name / n)
(setq n (hw-name-ohne-dxf name))
(and (> (strlen n) 0)
(not (wcmatch n "*[\\/:`*?\"<>|]*")))
)
(defun hw-neu-pfad (ordner name)
(strcat ordner "/" (hw-name-ohne-dxf name) ".dxf")
)
;; Eingaben des Ausgabe-Dialogs pruefen. datei-da / neu-da = ob die gewaehlte
;; bzw. die neue Datei schon existiert. Rueckgabe: nil oder ssg-text-Key.
(defun hw-ausgabe-pruefen (modus datei-da name neu-da)
(cond
((= modus "ergaenzen") (if datei-da nil "hw-aus-datei-fehlt"))
((= modus "neu")
(cond ((not (hw-name-gueltig-p name)) "hw-aus-name-ungueltig")
(neu-da "hw-aus-name-existiert")))
((= modus "aktuell") nil)
(t "hw-aus-datei-fehlt"))
)
;; Nummerierung uebertragen: passt ein Eintrag aus der create_numbers-Info
;; (old_value + Einfuegepunkt) auf ein Attribut? pos/wert aus der Zeichnung.
(defun hw-num-eintrag-p (eintrag wert pos / p)
(setq p (ssg-json-get eintrag "position"))
(and (= (ssg-json-get eintrag "old_value") wert)
(equal (ssg-json-get p "x") (car pos) 0.01)
(equal (ssg-json-get p "y") (cadr pos) 0.01))
)
;; ------------------------------------------------------------
;; Gemeinsam: Ausgabeordner und Python-Aufruf
;; ------------------------------------------------------------
;; DXFM_RESULTS, ausser waehrend TEST_EXPORT_ALL (*export-test-override*)
(defun hw-ausgabe-ordner ( / d)
(setq d (if *export-test-override* *export-test-override* (getenv "DXFM_RESULTS")))
(if (and d (not (vl-file-directory-p d))) (vl-mkdir d))
d
)
;; Python-Skript aus DXFM_LIB synchron ausfuehren, Log nach DXFM_LOG.
;; Rueckgabe: Exitcode (0 = ok) oder nil, wenn gar nicht gestartet.
;; ok-codes = Exitcodes, die KEIN Python-Fehler sind (create_numbers endet
;; mit 1, wenn es Planungsfehler meldet - die stehen in seiner Fehlerdatei).
(defun hw-python (label skript args log-name ok-codes / log-pfad rc)
(setq log-pfad (strcat (cond ((getenv "DXFM_LOG")) ((hw-ausgabe-ordner))) "/" log-name))
(setq rc (export:run-python (export:python-exe)
(strcat (getenv "DXFM_LIB") "/" skript) args log-pfad))
(if (null rc) (princ (ssg-textf "exp-python-call-failed" (list label))))
(export:log-ausgeben log-pfad 15)
(if (and rc (not (member rc (cons 0 ok-codes))))
(princ (ssg-textf "exp-python-error" (list label rc log-pfad))))
rc
)
(defun hw-umgebung-ok-p (label)
(if (getenv "DXFM_LIB")
T
(progn (princ (ssg-textf "exp-dxfm-lib-missing" (list label))) nil))
)
;; ------------------------------------------------------------
;; Ausgabeziel: Dialog dcl/hw_ausgabe.dcl
;; ------------------------------------------------------------
(defun hw-ausgabe-modus-tiles (modus)
(mode_tile "datei" (if (= modus "ergaenzen") 0 1))
(mode_tile "waehlen" (if (= modus "ergaenzen") 0 1))
(mode_tile "name" (if (= modus "neu") 0 1))
)
;; OK-Knopf: Eingaben pruefen, bei Fehler im Dialog bleiben. Arbeitet auf den
;; Variablen von hw-ausgabe-waehlen (modus datei name ordner pfad - AutoLISP
;; bindet dynamisch, der Rueckruf laeuft waehrend dessen start_dialog).
(defun hw-ausgabe-ok ( / fehler)
(setq modus (get_tile "modus")
datei (get_tile "datei")
name (get_tile "name"))
(setq fehler (hw-ausgabe-pruefen modus
(if (and (/= datei "") (findfile datei)) T)
name
(if (findfile (hw-neu-pfad ordner name)) T)))
(if fehler
(set_tile "error" (ssg-text fehler))
(progn
(setq pfad (cond ((= modus "aktuell")
(strcat ordner "/" (vl-filename-base (getvar "DWGNAME")) "_HW_tmp.dxf"))
((= modus "ergaenzen") datei)
(t (hw-neu-pfad ordner name))))
(done_dialog 1)))
)
;; Ziel der Ausgabe erfragen. aktion = Text oben im Dialog.
;; Rueckgabe: (modus pfad) mit modus "aktuell"/"ergaenzen"/"neu" - bei
;; "aktuell" ist pfad die Zwischendatei - oder nil bei Abbruch. Ohne GUI
;; (automatische Tests): Standarddatei <dwg>_HW.dxf, anlegen oder ergaenzen.
(defun hw-ausgabe-waehlen (aktion / ordner modus datei name pfad dcl dat r f erg weiter)
(setq ordner (hw-ausgabe-ordner))
(cond
((not (ssg-gui-p))
(list "ergaenzen" (strcat ordner "/" (hw-dxf-name (getvar "DWGNAME")))))
((or (null (getenv "DXFM_DCL"))
(not (findfile (setq dcl (strcat (getenv "DXFM_DCL") "/hw_ausgabe.dcl"))))
(< (setq dat (load_dialog dcl)) 0))
(princ (ssg-textf "hw-aus-dcl-fehlt" (list (cond (dcl) ("hw_ausgabe.dcl")))))
nil)
(t
(setq modus (cond ((car *hw-ausgabe-letzt*)) ("neu"))
datei (cond ((cadr *hw-ausgabe-letzt*))
((strcat ordner "/" (hw-dxf-name (getvar "DWGNAME")))))
name (cond ((caddr *hw-ausgabe-letzt*))
((vl-filename-base (hw-dxf-name (getvar "DWGNAME")))))
weiter T)
(while weiter
(if (not (new_dialog "hw_ausgabe" dat))
(setq weiter nil)
(progn
(set_tile "aktion" aktion)
(set_tile "modus" modus)
(set_tile "datei" datei)
(set_tile "name" name)
(set_tile "ordner" (ssg-textf "hw-aus-ordner" (list ordner)))
(hw-ausgabe-modus-tiles modus)
(action_tile "modus" "(setq modus $value) (hw-ausgabe-modus-tiles modus) (set_tile \"error\" \"\")")
;; getfiled geht nicht aus einem Rueckruf: Dialog schliessen,
;; waehlen, Dialog neu zeigen
(action_tile "waehlen"
"(setq datei (get_tile \"datei\") name (get_tile \"name\")) (done_dialog 2)")
(action_tile "accept" "(hw-ausgabe-ok)")
(action_tile "cancel" "(done_dialog 0)")
(setq r (start_dialog))
(cond
((= r 2)
(if (setq f (getfiled (ssg-text "hw-aus-waehlen")
(if (and (/= datei "") (findfile datei)) datei (strcat ordner "/"))
"dxf" 0))
(setq datei f modus "ergaenzen")))
((= r 1) (setq weiter nil erg (list modus pfad)))
(t (setq weiter nil))))))
(unload_dialog dat)
(if erg (setq *hw-ausgabe-letzt* (list modus datei name)))
erg))
)
;; Zieldatei fuer Python vorbereiten: die Zwischendatei der aktuellen
;; Zeichnung wird jedesmal frisch angelegt. Rueckgabe: Pfad.
(defun hw-ziel-pfad (ziel)
(if (and (= (car ziel) "aktuell") (findfile (cadr ziel)))
(vl-file-delete (cadr ziel)))
(cadr ziel)
)
;; Zusatzargumente fuer Python: Zwischendatei ohne Einheit (sonst skaliert
;; BricsCAD sie beim Einfuegen in eine Zeichnung mit anderer Einheit)
(defun hw-zusatz-args (ziel)
(if (= (car ziel) "aktuell") '("--einheitenlos"))
)
;; Fruehere Ausgabe in der aktuellen Zeichnung: alle Objekte mit XDATA app,
;; bei praefixe nur die, deren erster 1000er-Wert (Praefix) darin steht.
(defun hw-alte-ausgabe (app praefixe / ss i e xd erg)
(if (setq ss (ssget "_X" (list (list -3 (list app)))))
(progn
(setq i 0)
(while (setq e (ssname ss i))
(setq xd (cdr (assoc app (cdr (assoc -3 (entget e (list app)))))))
(if (or (null praefixe) (member (cdr (assoc 1000 xd)) praefixe))
(setq erg (cons e erg)))
(setq i (1+ i)))))
erg
)
;; Zwischendatei als Block bei 0,0 einfuegen und aufloesen; erst wenn das
;; geklappt hat, die fruehere Ausgabe (alte) loeschen. Rueckgabe: T/nil.
(defun hw-in-aktuelle-zeichnung (pfad alte / doc obj teile blk leer)
(setq doc (vla-get-ActiveDocument (vlax-get-acad-object)))
(setq obj (vl-catch-all-apply 'vla-InsertBlock
(list (vla-get-ModelSpace doc) (vlax-3D-point '(0 0 0)) pfad 1.0 1.0 1.0 0.0)))
(if (vl-catch-all-error-p obj)
nil
(progn
;; Leere Zwischendatei (HW_SYM_LOESCHEN, keine Bauteile): nichts
;; aufzuloesen - ein Explode wuerde scheitern, gilt aber als Erfolg
(setq blk (vl-catch-all-apply 'vla-Item (list (vla-get-Blocks doc) (vl-filename-base pfad))))
(setq leer (and (not (vl-catch-all-error-p blk)) (= (vla-get-Count blk) 0)))
(if (not leer) (setq teile (vl-catch-all-apply 'vla-Explode (list obj))))
(vla-Delete obj)
(if (not (vl-catch-all-error-p blk)) (vl-catch-all-apply 'vla-Delete (list blk)))
(if (vl-catch-all-error-p teile)
nil
(progn
(foreach e alte (if (entget e) (entdel e)))
(vl-file-delete pfad)
T))))
)
;; Ergebnis eines erfolgreichen Python-Laufs melden bzw. in die aktuelle
;; Zeichnung uebernehmen. app/praefixe: welche fruehere Ausgabe ersetzt wird.
(defun hw-ergebnis-uebernehmen (ziel app praefixe meldung / alte)
(if (= (car ziel) "aktuell")
(progn
(setq alte (hw-alte-ausgabe app praefixe))
(if (hw-in-aktuelle-zeichnung (cadr ziel) alte)
(princ (ssg-textf "hw-fertig-aktuell" (list (itoa (length alte)))))
(princ (ssg-textf "hw-aktuell-fehler" (list (cadr ziel))))))
(princ (ssg-textf meldung (list (cadr ziel)))))
)
;; ------------------------------------------------------------
;; MATERIALFLUSS
;; ------------------------------------------------------------
(defun c:HW_MATERIALFLUSS ( / label ziel out-dir csv-pfad dxf-pfad)
(setq label "HW_MATERIALFLUSS")
(if (and (hw-umgebung-ok-p label)
(setq ziel (hw-ausgabe-waehlen (ssg-text "hw-aus-materialfluss"))))
(progn
(setq out-dir (hw-ausgabe-ordner))
(setq csv-pfad (strcat out-dir "/" (export:dwg-praefix)
(export:filename "csv_csv")))
(setq dxf-pfad (hw-ziel-pfad ziel))
;; Alte CSV entfernen - sonst wuerde nach einem gescheiterten Export
;; still die Skizze aus dem vorigen Stand gezeichnet.
(if (findfile csv-pfad) (vl-file-delete csv-pfad))
(c:EXPORTCSV)
(if (not (findfile csv-pfad))
(princ (ssg-textf "hw-csv-fehlt" (list csv-pfad)))
(if (equal 0 (hw-python label *hw-skript*
(append (list csv-pfad dxf-pfad) (hw-zusatz-args ziel))
"hw_materialfluss.log" nil))
(hw-ergebnis-uebernehmen ziel *hw-mf-app* nil "hw-fertig")))))
(princ)
)
;; ------------------------------------------------------------
;; SYMBOLE ERZEUGEN (HW_SYMBOLE + Einzelbefehle je Geraeteart)
;; ------------------------------------------------------------
(defun hw-sym-regeln-laden ( / pfad regeln)
(setq pfad (strcat (cond ((getenv "DXFM_CFG")) ("")) "/io_regeln.json"))
(setq regeln (if (findfile pfad) (ssg-json-laden pfad)))
(if (null regeln) (princ (ssg-textf "hw-sym-regeln-fehlt" (list pfad))))
regeln
)
;; Alle Bauteile der Ursprungszeichnung: top-level INSERTs und in VF_n/GF_n/
;; KREISEL_n verschachtelte Mechanik-Bloecke (Weltlage ueber
;; csv:wrapper-welt-records, export.lsp). Katalogsymbole (ILS_*) werden
;; nicht durchsucht. Rueckgabe: sortierte Liste (bauteil blockname pt key).
(defun hw-sym-bauteile-sammeln (regeln / muster ss i ename ed name bt erg)
(setq muster (hw-sym-muster regeln))
(if (setq ss (ssget "_X" '((0 . "INSERT") (410 . "Model"))))
(progn
(setq i 0)
(while (setq ename (ssname ss i))
(setq ed (entget ename)
name (cdr (assoc 2 ed)))
(cond
((wcmatch (strcase name) muster)
(if (setq bt (hw-sym-bauteil-fuer-block regeln name))
(setq erg (cons (list bt name (trans (cdr (assoc 10 ed)) ename 0)
(cdr (assoc 5 ed)))
erg))))
((not (wcmatch (strcase name) "ILS_*,`**"))
(foreach rec (csv:wrapper-welt-records ename muster)
(if (setq bt (hw-sym-bauteil-fuer-block regeln (nth 2 rec)))
(setq erg (cons (list bt (nth 2 rec) (nth 3 rec)
(strcat (cdr (assoc 5 ed)) "/"
(cdr (assoc 5 (entget (nth 1 rec))))))
erg))))))
(setq i (1+ i)))))
(hw-sym-instanzen-sortieren regeln erg)
)
(defun hw-sym-zaehlen (schluessel alist / alt)
(setq alt (assoc schluessel alist))
(if alt
(subst (cons schluessel (1+ (cdr alt))) alt alist)
(cons (cons schluessel 1) alist))
)
;; Symbolplan als JSON schreiben und hw_symbole.py die Symbole der
;; angegebenen Praefixe ins gewaehlte Ziel setzen lassen (nil = alle Praefixe
;; des Regelwerks). mit-bauteilen = nil -> leerer Plan (nur entfernen).
(defun hw-sym-erzeugen (praefixe mit-bauteilen / label regeln instanzen plan out-dir
json-pfad dxf-pfad sym-pfad fh bt-zahl ziel)
(setq label "HW_SYMBOLE")
(if (and (hw-umgebung-ok-p label)
(setq regeln (hw-sym-regeln-laden))
(setq ziel (hw-ausgabe-waehlen (ssg-text "hw-aus-symbole"))))
(progn
(if (null praefixe)
(foreach r (ssg-json-get regeln "rollen")
(if (not (member (ssg-json-get r "praefix") praefixe))
(setq praefixe (append praefixe (list (ssg-json-get r "praefix")))))))
(if mit-bauteilen
(progn
(setq instanzen (hw-sym-bauteile-sammeln regeln))
(if (null instanzen) (princ (ssg-text "hw-sym-keine-bauteile")))
(foreach ins instanzen
(setq bt-zahl (hw-sym-zaehlen (ssg-json-get (car ins) "bauteil") bt-zahl)))
(foreach z (reverse bt-zahl)
(princ (ssg-textf "hw-sym-bauteile" (list (car z) (itoa (cdr z))))))
(setq plan (hw-sym-plan regeln instanzen))))
(setq out-dir (hw-ausgabe-ordner)
json-pfad (strcat out-dir "/" (vl-filename-base (getvar "DWGNAME")) "_hw_symbole.json")
dxf-pfad (hw-ziel-pfad ziel)
sym-pfad (strcat (cond ((getenv "DXFM_CFG")) ("")) "/hw_symbole.json"))
(if (not (setq fh (open json-pfad "w")))
(princ (ssg-textf "exp-file-open-error" (list label json-pfad)))
(progn
(foreach z (hw-sym-plan->json praefixe plan) (write-line z fh))
(close fh)
(if (equal 0 (hw-python label *hw-sym-skript*
(append (list json-pfad dxf-pfad "--symbole" sym-pfad)
(hw-zusatz-args ziel))
"hw_symbole.log" nil))
(hw-ergebnis-uebernehmen ziel *hw-sym-app* praefixe "hw-fertig-symbole"))))))
(princ)
)
(defun hw-sym-art (art)
(hw-sym-erzeugen (cdr (assoc art *hw-sym-arten*)) T)
)
;; Menue: HW Planung -> Symbole erzeugen
(defun c:HW_SYMBOLE () (hw-sym-erzeugen nil T))
;; Einzelbefehle je Geraeteart - noch nicht im Menue, zum schrittweisen
;; Erzeugen und Pruefen. Jeder ersetzt in der _HW.dxf nur die Symbole
;; seiner Art.
(defun c:HW_SYM_VENTILE () (hw-sym-art "ventile"))
(defun c:HW_SYM_SENSOREN () (hw-sym-art "sensoren"))
(defun c:HW_SYM_MOTOREN () (hw-sym-art "motoren"))
(defun c:HW_SYM_MOTORSCHUTZ () (hw-sym-art "motorschutz"))
(defun c:HW_SYM_ENCODER () (hw-sym-art "encoder"))
(defun c:HW_SYM_SCANNER () (hw-sym-art "scanner"))
;; Alle erzeugten Symbole aus dem gewaehlten Ziel entfernen
(defun c:HW_SYM_LOESCHEN () (hw-sym-erzeugen nil nil))
;; ------------------------------------------------------------
;; NUMMERIEREN (in der geoeffneten _HW.dxf)
;; ------------------------------------------------------------
;; Nummern aus der create_numbers-Info in die offene Zeichnung eintragen:
;; jedes Attribut mit '@', dessen Wert und Einfuegepunkt zu einem Eintrag
;; passen, bekommt new_value. Rueckgabe: Anzahl geaenderter Attribute.
(defun hw-num-anwenden (eintraege / ss i e pos obj ed wert treffer n)
(setq n 0)
(if (setq ss (ssget "_X" '((0 . "INSERT") (66 . 1) (410 . "Model"))))
(progn
(setq i 0)
(while (setq e (ssname ss i))
(setq pos (cdr (assoc 10 (entget e))))
(setq obj (entnext e))
(while (and obj (= (cdr (assoc 0 (setq ed (entget obj)))) "ATTRIB"))
(setq wert (cdr (assoc 1 ed)))
(if (vl-string-search "@" wert)
(progn
(setq treffer nil)
(foreach z eintraege
(if (and (null treffer) (hw-num-eintrag-p z wert pos)) (setq treffer z)))
(if treffer
(progn
(entmod (subst (cons 1 (ssg-json-get treffer "new_value")) (assoc 1 ed) ed))
(setq n (1+ n))))))
(setq obj (entnext obj)))
(entupd e)
(setq i (1+ i)))))
n
)
(defun c:HW_NUMMERIEREN ( / label pfad basis info-pfad fehler-pfad rc info n)
(setq label "HW_NUMMERIEREN")
(setq pfad (strcat (getvar "DWGPREFIX") (getvar "DWGNAME")))
(cond
((not (hw-umgebung-ok-p label)))
((or (= (getvar "DWGTITLED") 0)
(/= (strcase (vl-filename-extension pfad)) ".DXF"))
(princ (ssg-textf "hw-num-kein-dxf" (list (getvar "DWGNAME")))))
(t
;; create_numbers liest die Datei von der Platte - erst speichern
(command "_.QSAVE")
(setq basis (strcat (getvar "DWGPREFIX") (vl-filename-base pfad))
info-pfad (strcat basis "_nummern.json")
fehler-pfad (strcat basis "_nummern_fehler.json"))
(foreach f (list info-pfad fehler-pfad) (if (findfile f) (vl-file-delete f)))
;; --dryrun: create_numbers schreibt keine DXF, nur die Info - die
;; Nummern werden hier in die OFFENE Zeichnung eingetragen.
(setq rc (hw-python label *hw-num-skript*
(list "--filename" pfad "--dryrun"
"--info" info-pfad "--errorfile" fehler-pfad)
"hw_nummerieren.log" '(1)))
(if (and rc (setq info (if (findfile info-pfad) (ssg-json-laden info-pfad))))
(progn
(setq n (hw-num-anwenden (ssg-json-get info "renamed_symbols")))
(princ (ssg-textf "hw-num-ergebnis" (list (itoa n))))))
(if (findfile fehler-pfad)
(princ (ssg-textf "hw-num-fehler" (list fehler-pfad))))))
(princ)
)
(princ "\n>>> HwPlanung.lsp geladen")
(princ)