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>
This commit is contained in:
Michael StanglandClaude Opus 5.5 committed 2026-11-03 16:39:37 +01:00
1 parent d2729140ff
commit 8abb6b3c65
20 files changed
+4176 -183

No files matched your search

+103 -3
View File
@@ -3595,15 +3595,115 @@
)
)
;; HW-Planung: Ausgabename der Materialfluss-Skizze
(defun tu-tests-hw ()
;; HW-Planung: Ausgabename, JSON-Zahlen, Symbol-Regelwerk (Zuordnung Block ->
;; Bauteil -> Katalogsymbol, IO-Vorlage, Plan + JSON) und das Zuordnen der
;; create_numbers-Ergebnisse
(defun tu-tests-hw ( / _regeln _plan _json _echt _fehlt _eintrag)
(princ "\n[HwPlanung]")
(if (not (car (atoms-family 1 '("HW-DXF-NAME"))))
(tu-record nil "HwPlanung-Modul geladen"
"HwPlanung.lsp nicht geladen - siehe tests/test_unit.scr")
(progn
(tu-eq "hw-dxf-name/dwg" "Mubea_HW.dxf" (hw-dxf-name "Mubea.dwg"))
(tu-eq "hw-dxf-name/punkt" "A.B_HW.dxf" (hw-dxf-name "A.B.dwg")))
(tu-eq "hw-dxf-name/punkt" "A.B_HW.dxf" (hw-dxf-name "A.B.dwg"))
;; Ausgabe-Dialog: Dateiname und Eingabepruefung
(tu-eq "hw-name-ohne-dxf" "Anlage_HW" (hw-name-ohne-dxf " Anlage_HW.DXF "))
(tu-eq "hw-name-ohne-dxf/ohne" "Plan 2" (hw-name-ohne-dxf "Plan 2"))
(tu-true "hw-name-gueltig-p/ok" (hw-name-gueltig-p "Mubea_HW"))
(tu-nil "hw-name-gueltig-p/leer" (hw-name-gueltig-p " "))
(tu-nil "hw-name-gueltig-p/pfad" (hw-name-gueltig-p "C:/x/Mubea"))
(tu-nil "hw-name-gueltig-p/stern" (hw-name-gueltig-p "a*b"))
(tu-eq "hw-neu-pfad" "C:/erg/Mubea_HW.dxf" (hw-neu-pfad "C:/erg" "Mubea_HW.dxf"))
(tu-nil "hw-ausgabe-pruefen/aktuell" (hw-ausgabe-pruefen "aktuell" nil "" nil))
(tu-nil "hw-ausgabe-pruefen/ergaenzen" (hw-ausgabe-pruefen "ergaenzen" T "" nil))
(tu-eq "hw-ausgabe-pruefen/ergaenzen-fehlt" "hw-aus-datei-fehlt"
(hw-ausgabe-pruefen "ergaenzen" nil "" nil))
(tu-nil "hw-ausgabe-pruefen/neu" (hw-ausgabe-pruefen "neu" nil "Neu_HW" nil))
(tu-eq "hw-ausgabe-pruefen/neu-ungueltig" "hw-aus-name-ungueltig"
(hw-ausgabe-pruefen "neu" nil "a?b" nil))
(tu-eq "hw-ausgabe-pruefen/neu-existiert" "hw-aus-name-existiert"
(hw-ausgabe-pruefen "neu" nil "Mubea_HW" T))
(tu-eq "hw-json-zahl/neg" "-12.500" (hw-json-zahl -12.5))
(tu-eq "hw-json-zahl/klein" "0.050" (hw-json-zahl 0.05))
(tu-eq "hw-json-zahl/rund" "1001.000" (hw-json-zahl 1000.9999))
(tu-eq "hw-json-zahl/null" "0.000" (hw-json-zahl -0.0001))
;; Struktur wie ssg-json-parse sie aus cfg/io_regeln.json liefert
(setq _regeln
(list '("io_vorlage" . "1@@@")
(list "rollen"
'(("rolle" . "sep_ventil") ("praefix" . "MB") ("block" . "SYM_MB"))
'(("rolle" . "sep_sensor") ("praefix" . "BG") ("block" . "SYM_BG"))
'(("rolle" . "vario_motor") ("praefix" . "MA") ("block" . "SYM_MA_LI")
("block_nach_name" (("muster" . "*RECHTS*") ("block" . "SYM_MA_RE")))))
(list "bauteile"
'(("bauteil" . "separator") ("bloecke" "Separator_SP*" "S-SP")
("benoetigt" ("sep_ventil") ("sep_sensor")))
'(("bauteil" . "vario") ("bloecke" "Vario_Motorstation*")
("benoetigt" ("vario_motor" "andere_alternative"))))))
(tu-eq "hw-sym-io-vorlage" "MB1@@@" (hw-sym-io-vorlage _regeln "MB"))
(tu-eq "hw-sym-io-vorlage/vorgabe" "BG1@@@" (hw-sym-io-vorlage nil "BG"))
(tu-eq "hw-sym-bauteil-fuer-block/sep" "separator"
(ssg-json-get (hw-sym-bauteil-fuer-block _regeln "separator_sp_3D") "bauteil"))
(tu-nil "hw-sym-bauteil-fuer-block/fremd" (hw-sym-bauteil-fuer-block _regeln "AN8_3D"))
(tu-eq "hw-sym-muster" "SEPARATOR_SP*,S-SP,VARIO_MOTORSTATION*" (hw-sym-muster _regeln))
(tu-eq "hw-sym-block-fuer/rechts" "SYM_MA_RE"
(hw-sym-block-fuer (hw-sym-rolle _regeln "vario_motor") "Vario_Motorstation_500mm_rechts_3D"))
(tu-eq "hw-sym-block-fuer/links" "SYM_MA_LI"
(hw-sym-block-fuer (hw-sym-rolle _regeln "vario_motor") "Vario_Motorstation_500mm_links_3D"))
(tu-eq "hw-sym-ziel/flach" '(1000.0 2000.0 0.0) (hw-sym-ziel '(1000.0 2000.0 5.0)))
;; Plan: zwei Separatoren (Sortierung nach x) + eine Motorstation
(setq _plan
(hw-sym-plan _regeln
(hw-sym-instanzen-sortieren _regeln
(list (list (hw-sym-bauteil-fuer-block _regeln "Vario_Motorstation_rechts")
"Vario_Motorstation_rechts" '(0.0 0.0 0.0) "V1")
(list (hw-sym-bauteil-fuer-block _regeln "Separator_SP_3D")
"Separator_SP_3D" '(500.0 0.0 0.0) "S2")
(list (hw-sym-bauteil-fuer-block _regeln "Separator_SP_3D")
"Separator_SP_3D" '(100.0 0.0 2000.0) "S1")))))
(tu-eq "hw-sym-plan/keys" '("S1" "S1" "S2" "S2" "V1") (mapcar '(lambda (r) (nth 5 r)) _plan))
;; Zielpunkt = Bauteilpunkt (flach), beide Symbole eines Bauteils gleich -
;; die Lage daneben/uebereinander rechnet hw_symbole.py
(tu-eq "hw-sym-plan/erster" '("MB" "MB1@@@" "SYM_MB" (100.0 0.0 0.0) "sep_ventil" "S1" "separator")
(car _plan))
(tu-eq "hw-sym-plan/sensor-gleiches-ziel" '(100.0 0.0 0.0) (nth 3 (cadr _plan)))
(tu-eq "hw-sym-plan/stapelfolge" '("sep_ventil" "sep_sensor")
(list (nth 4 (car _plan)) (nth 4 (cadr _plan))))
(tu-eq "hw-sym-plan/motor-rechts" "SYM_MA_RE" (nth 2 (last _plan)))
;; JSON fuer hw_symbole.py: wieder einlesbar, Komma-Setzung korrekt
(setq _json (ssg-json-parse (ssg-join (hw-sym-plan->json '("MB" "MA") _plan) "\n")))
(tu-eq "hw-sym-plan->json/praefixe" '("MB" "MA") (ssg-json-get _json "praefixe"))
(tu-eq "hw-sym-plan->json/anzahl" 5 (length (ssg-json-get _json "symbole")))
(tu-eq "hw-sym-plan->json/x" 100.0 (ssg-json-get (car (ssg-json-get _json "symbole")) "x"))
(tu-eq "hw-sym-plan->json/leer" nil
(ssg-json-get (ssg-json-parse (ssg-join (hw-sym-plan->json '("BX") nil) "\n")) "symbole"))
;; Ergebnis von create_numbers zuordnen (Wert + Einfuegepunkt)
(setq _eintrag '(("old_value" . "MB1@@@") ("new_value" . "MB1001")
("position" ("x" . 400.0) ("y" . 0))))
(tu-true "hw-num-eintrag-p/passt" (hw-num-eintrag-p _eintrag "MB1@@@" '(400.004 0.0 0.0)))
(tu-nil "hw-num-eintrag-p/wert" (hw-num-eintrag-p _eintrag "BG1@@@" '(400.0 0.0 0.0)))
(tu-nil "hw-num-eintrag-p/lage" (hw-num-eintrag-p _eintrag "MB1@@@" '(400.0 350.0 0.0)))
;; Echtes Regelwerk: jede benoetigte Rolle hat eine Rollen-Definition
(setq _echt (if (getenv "DXFM_CFG")
(ssg-json-laden (strcat (getenv "DXFM_CFG") "/io_regeln.json"))))
(if _echt
(progn
(foreach b (ssg-json-get _echt "bauteile")
(foreach alt (ssg-json-get b "benoetigt")
(foreach r alt (if (null (hw-sym-rolle _echt r)) (setq _fehlt (cons r _fehlt))))))
(tu-nil "hw-sym-regelwerk/rollen-vollstaendig" _fehlt)
(tu-eq "hw-sym-regelwerk/io-vorlage" "MB1@@@" (hw-sym-io-vorlage _echt "MB"))
(tu-eq "hw-sym-regelwerk/as-element" "ausschleuser"
(ssg-json-get (hw-sym-bauteil-fuer-block _echt "AS_Element_90_links_3D") "bauteil")))
(tu-record nil "hw-sym-regelwerk" "cfg/io_regeln.json nicht gefunden (DXFM_CFG?)"))
)
)
)