;; ============================================================ ;; test_hundm05.lsp - Integrationstest: baut die 5 VarioFoerderer-Linienzuege ;; der Anlage HundM (Kreisel mit fuenf daran haengenden Ketten) aus ihrer ;; SPEC nach. ;; ;; Datenquelle: tests/testdata/hundm05.json - die Ketten im Spec-Format ;; (Domaenenwerte statt Menue-Codes, siehe Lisp/vf_spec.lsp und ;; doc/variofoerderer.md Abschnitt 8b). Erzeugt aus den echten ;; Aufzeichnungen mit ;; python lib/vf_spec_export.py tests/testdata/hm_recformat.json \ ;; tests/testdata/hundm05.json ;; und damit nachweislich dasselbe Journal wie die Vorlage ;; (Rundlauf-Beweis in tests/test_vf_spec.py). Die Datei ist die Stelle, an ;; der weitere Ketten der Anlage nachgetragen werden. ;; ;; Dieser Test hat KEINE eigene Bau-Logik: er ruft vsp-bau-datei aus ;; Lisp/vf_spec.lsp - denselben Weg, den auch der Befehl VF_SPEC_BAU nimmt. ;; Es gibt also genau einen Code-Pfad, der aus einer Spec Geometrie erzeugt. ;; ;; ABGRENZUNG zu TEST_HM_RECFORMAT (tests/test_hm_recformat.lsp): dort werden ;; dieselben Ketten aus dem ROHEN Journal der Kundenzeichnung gebaut. Das ist ;; der Regressionstest fuer das Aufzeichnungsformat (bleibt ein bestehender ;; VF_n-Block abspielbar - Doppelklick-Edit, 2D/3D-Konvertierung?) und ;; deshalb dort auf "disabled": true, um nicht zweimal dieselbe Geometrie zu ;; bauen. ;; ;; Voraussetzung: ;; - SSG_LIB geladen (VarioFoerderer inkl. vf_linienzug + vf_spec, ;; Gefaellestrecke, ssg_core, ssg_dbg) ;; - Umgebungsvariable DXFMAKRO gesetzt ;; ;; Speichert (via SSG_RUN_ALL_TESTS bei "save":"dxf"): ;; tests/output/hundm05_tests.dxf ;; tests/output/hundm05_results.json (via hundm05:export-results) ;; ;; Aufruf in BricsCAD: ;; (load (strcat (getenv "DXFMAKRO") "/tests/test_hundm05.lsp")) ;; TEST_HUNDM05 ; in der aktuellen Dimension (ssg-ils-dim-aktuell) ;; TEST_HUNDM05_2D ; erzwungen 2D ;; TEST_HUNDM05_3D ; erzwungen 3D ;; ============================================================ (if (not (boundp '*hundm05-records*)) (setq *hundm05-records* nil)) (defun hundm05:spec-datei () (strcat (getenv "DXFMAKRO") "/tests/testdata/hundm05.json")) ;; ============================================================ ;; Weitere Objekte der Anlage (nicht-Ketten) ;; ============================================================ ;; Neben den Spec-Ketten (Kopf "spec_id" + "glied"/"sub") duerfen in ;; hundm05.json auch andere Objekte der Anlage stehen - erkennbar an ;; "function". Format wie in tests/testdata/kreisel_tests.json, damit die ;; Felder nicht zweimal beschrieben werden muessen (Schema: siehe ;; tests/testdata/object_data.md Abschnitt 1). ;; vsp-gruppieren ignoriert diese Objekte, sie stoeren die Ketten also nicht. ;; lib/vf_spec_export.py uebernimmt sie beim Neuerzeugen der Spec-Daten ;; unveraendert ans Dateiende (sonst waeren sie beim naechsten Lauf weg). ;; ;; Unterstuetzt: Kreisel (function "insert", wie test_kreisel.lsp) und ;; Separator/Scanner (zusaetzlich "block", wie in mubea.json - Schema ;; tests/testdata/object_data.md Abschnitt 5). Unterscheidung ueber das ;; Feld "block": nur Separator/Scanner-Eintraege haben es. ;; Weitere Arten kommen als eigener cond-Zweig dazu. (defun hundm05:zusatz-objekt-p (obj) (and (ssg-val obj "function") (null (ssg-val obj "spec_id")) (null (ssg-val obj "glied")) (null (ssg-val obj "sub")))) ;; Kreisel bauen (kreisel-insert-script wie in tests/test_kreisel.lsp). ;; Rueckgabe: Ergebnis-Record in derselben Form wie die Ketten, damit ;; vsp-results-schreiben beide in EINE Datei schreibt (unterschieden ueber ;; das Feld "kind"). (defun hundm05:bau-kreisel (obj / id x y z abstand rotation typ pt ent ed rec) (setq id (ssg-val obj "id")) (setq x (ssg-val obj "x") y (ssg-val obj "y") z (ssg-val obj "z") abstand (ssg-val obj "abstand") rotation (ssg-val obj "rotation") typ (ssg-val obj "typ")) (if (numberp x) (setq x (float x))) (if (numberp y) (setq y (float y))) (if (numberp z) (setq z (float z))) (if (numberp abstand) (setq abstand (float abstand))) (if (numberp rotation) (setq rotation (float rotation))) (if (null typ) (setq typ "STANDARD")) (setq rec (vsp-rec-neu (if id id "(ohne id)"))) (setq rec (vsp-set rec "kind" "kreisel")) (setq rec (vsp-set rec "dimension" (vsp-dim-text))) (cond ((not (and (numberp x) (numberp y) (numberp abstand))) (princ (strcat "\n [KREISEL] " (if id id "?") " - FEHLER: x, y und abstand muessen Zahlen sein")) (vsp-set (vsp-set rec "status" "spec-fehler") "fehler_text" "x, y oder abstand fehlt bzw. ist keine Zahl")) (T (setq pt (list x y z)) (princ (strcat "\n [KREISEL] " id " -> (" (rtos x 2 0) ", " (rtos y 2 0) ", " (rtos (if z z 0.0) 2 0) ") A=" (rtos abstand 2 0) " R=" (rtos (if rotation rotation 0.0) 2 1) " " typ)) (setq ent (kreisel-insert-script pt abstand (if rotation rotation 0.0) typ z)) (if ent (progn (setq ed (entget ent)) (princ (strcat " -> Block " (cdr (assoc 2 ed)))) (setq rec (vsp-set rec "block_name" (cdr (assoc 2 ed)))) (setq rec (vsp-set rec "block_handle" (cdr (assoc 5 ed)))) (setq rec (vsp-set rec "insert_point" (cdr (assoc 10 ed)))) (setq rec (vsp-set rec "attribs" (ssg-attrib-read ent))) (vsp-set rec "status" "executed")) (progn (princ " FEHLER: kein Kreisel-Block entstanden") (vsp-set (vsp-set rec "status" "abbruch") "fehler_text" "kreisel-insert-script lieferte nil")))))) ;; Separator/Scanner bauen ("block"-Eintraege, siehe object_data.md ;; Abschnitt 5). ils-insert-sensor (SSG_LIB_Commands.lsp) ist dafuer NICHT ;; scriptbar (fragt Punkt/Rotation per getpoint/pause in einer Schleife) - ;; wir bauen den _.INSERT darum direkt selbst nach, wie ;; mubea:build-separator-one in tests/test_mubea.lsp es fuer die ;; Mubea-Anlage schon vorgemacht hat (dieselbe Vorlage, hier auf das ;; hundm05:bau-*/vsp-rec-neu-Record-Format dieser Datei uebertragen statt ;; mubeas eigenes result-json). "sensor" als Sammelbegriff fuer den "kind"- ;; Wert, weil dieselbe Routine Separator UND Scanner einfuegt - genau wie ;; ils-insert-sensor selbst. (defun hundm05:bau-sensor (obj / id blockname x y z rotation pt eff n ent ed rec) (setq id (ssg-val obj "id")) (setq blockname (ssg-val obj "block")) (setq x (ssg-val obj "x") y (ssg-val obj "y") z (ssg-val obj "z") rotation (ssg-val obj "rotation")) (if (numberp x) (setq x (float x))) (if (numberp y) (setq y (float y))) (if (numberp z) (setq z (float z))) (setq rotation (if (numberp rotation) (float rotation) 0.0)) (setq rec (vsp-rec-neu (cond (id) (blockname) ("(ohne id)")))) (setq rec (vsp-set rec "kind" "sensor")) (setq rec (vsp-set rec "dimension" (vsp-dim-text))) (cond ((not (and (numberp x) (numberp y) (numberp z))) (princ (strcat "\n [SENSOR] " (if id id "?") " - FEHLER: x, y und z muessen Zahlen sein")) (vsp-set (vsp-set rec "status" "spec-fehler") "fehler_text" "x, y oder z fehlt bzw. ist keine Zahl")) (T (setq pt (list x y z)) ;; NICHT (and blockname (ssg-ils-block-laden blockname)) - and liefert ;; T/nil statt des Blocknamen-Strings (siehe CLAUDE.md, and/or-Falle). (setq eff (if blockname (ssg-ils-block-laden blockname))) (if (null eff) (setq eff (ssg-ils-block-laden "Separator_SP"))) (cond ((null eff) (princ (strcat "\n [SENSOR] " (if id id "?") " - FEHLER: weder '" blockname "' noch Fallback 'Separator_SP' als Blockdatei gefunden")) (vsp-set (vsp-set rec "status" "abbruch") "fehler_text" "kein Block gefunden")) (T (princ (strcat "\n [SENSOR] " id " '" blockname "' -> (" (rtos x 2 0) ", " (rtos y 2 0) ", " (rtos z 2 0) ") R=" (rtos rotation 2 1))) (command "_.INSERT" eff pt "" "" rotation) ;; Verbleibende Prompts mit Enter abfangen, aber GEZAEHLT - siehe ;; mubea:build-separator-one (ein Prompt, der Leer-Antworten ;; ablehnt, haelt CMDACTIVE sonst dauerhaft > 0). (setq n 0) (while (and (> (getvar "CMDACTIVE") 0) (< n 5)) (command "") (setq n (1+ n))) (if (> (getvar "CMDACTIVE") 0) (progn (command) (princ (strcat "\n WARNUNG: INSERT von '" eff "' nicht sauber beendet - abgebrochen.")))) (setq ent (entlast)) (if (atoms-family 1 '("ssg-id-generate")) (ssg-id-generate ent)) (setq ed (entget ent)) (princ (strcat " -> Block " (cdr (assoc 2 ed)))) (setq rec (vsp-set rec "block_name" (cdr (assoc 2 ed)))) (setq rec (vsp-set rec "block_handle" (cdr (assoc 5 ed)))) (setq rec (vsp-set rec "insert_point" (cdr (assoc 10 ed)))) (setq rec (vsp-set rec "attribs" (ssg-attrib-read ent))) (vsp-set rec "status" "executed")))))) ;; Alle Zusatzobjekte der Datei bauen. Eigener ssg-start-Rahmen, weil ;; vsp-bau-datei seinen schon geschlossen hat; ATTREQ/ATTDIA 0 ist Pflicht - ;; sonst fragt (command "_.INSERT" ...) nach Attributwerten. (defun hundm05:bau-zusatzobjekte (datei / daten objekte out rec builder kind) (setq daten (ssg-load-json datei)) (setq objekte '()) (foreach obj daten (if (hundm05:zusatz-objekt-p obj) (setq objekte (cons obj objekte)))) (setq objekte (reverse objekte)) (cond ((null objekte) nil) (T (princ (strcat "\n " (itoa (length objekte)) " weitere(s) Objekt(e) der Anlage")) (ssg-start "TEST_HUNDM05_ZUSATZ" '(("OSMODE") ("CECOLOR") ("ATTREQ") ("ATTDIA"))) (setvar "OSMODE" 0) (setvar "ATTREQ" 0) (setvar "ATTDIA" 0) (setq out '()) (foreach obj objekte ;; Unterscheidung Kreisel/Sensor ueber "block" (nur Separator/ ;; Scanner-Eintraege haben es), je eigener Builder + "kind"-Wert. (setq kind (if (ssg-val obj "block") "sensor" "kreisel")) (setq builder (if (ssg-val obj "block") 'hundm05:bau-sensor 'hundm05:bau-kreisel)) (cond ((and (= kind "kreisel") (null (car (atoms-family 1 '("KREISEL-INSERT-SCRIPT"))))) (princ (strcat "\n [KREISEL] " (ssg-val obj "id") " uebersprungen - KreiselInsert nicht geladen.")) (setq rec (vsp-set (vsp-set (vsp-rec-neu (ssg-val obj "id")) "kind" kind) "status" "spec-fehler"))) (T (setq rec (vl-catch-all-apply builder (list obj))) (if (vl-catch-all-error-p rec) (progn (princ (strcat "\n [" (strcase kind) "] Ausnahme: " (vl-catch-all-error-message rec))) (setq rec (vsp-set (vsp-set (vsp-set (vsp-rec-neu (ssg-val obj "id")) "kind" kind) "status" "abbruch") "fehler_text" (vl-catch-all-error-message rec))))))) (setq out (cons rec out))) (ssg-end) (reverse out)))) ;; ============================================================ ;; Testlauf ;; ============================================================ (defun c:TEST_HUNDM05 ( / datei) (setq datei (hundm05:spec-datei)) (princ "\n\n================================================================") (princ "\n TEST_HUNDM05 - Anlage HundM aus der Spec (5 Linienzuege)") (princ (strcat "\n " datei)) (princ "\n================================================================") (cond ((null (car (atoms-family 1 '("VSP-BAU-DATEI")))) (princ "\n[TEST_HUNDM05] FEHLER: vf_spec.lsp nicht geladen" " (VarioFoerderer laden).") (setq *hundm05-records* nil)) ((null (findfile datei)) (princ (strcat "\n[TEST_HUNDM05] FEHLER: " datei " nicht gefunden!")) (setq *hundm05-records* nil)) (T (vsp-modul-sichern "KreiselInsert") (setq *hundm05-records* (vsp-bau-datei datei)) (setq *hundm05-records* (append *hundm05-records* (hundm05:bau-zusatzobjekte datei))))) (princ)) ;; ============================================================ ;; Dieselbe Anlage in einer bestimmten Dimension bauen ;; ============================================================ ;; Die Spec ist dimensionsfrei - sie beschreibt die Eingaben, mit denen die ;; Ketten gebaut wurden. Ob daraus flache 2D-Symbole oder 3D-Modelle werden, ;; entscheidet allein die Blockvariante (_2D/_3D), die ssg-ils-dim-aktuell ;; aufloest. Unsere Bibliothek hat beide, also laesst sich dieselbe Anlage ;; ohne Datenaenderung in 2D ODER 3D aufbauen. ;; *vsp-dim-override* wird von vsp-bau-aus-spec JE KETTE angewandt (der ;; Abbruch-Handler in vf-linienzug-modus setzt *ssg-ils-dim* zurueck). (defun hundm05:mit-dimension (dim / alt) (setq alt (if (boundp '*vsp-dim-override*) *vsp-dim-override*)) (setq *vsp-dim-override* dim) (vl-catch-all-apply 'c:TEST_HUNDM05 '()) (setq *vsp-dim-override* alt) (princ)) (defun c:TEST_HUNDM05_2D () (hundm05:mit-dimension "2D")) (defun c:TEST_HUNDM05_3D () (hundm05:mit-dimension "3D")) ;; ============================================================ ;; Ergebnis-Export (Konvention :export-results fuer test_run_all.lsp) ;; ============================================================ ;; Geschrieben wird hier und nicht in c:TEST_HUNDM05, damit der Testrunner ;; das Zielverzeichnis bestimmt (er uebergibt tests/output). (defun hundm05:export-results (out-dir / pfad) (if (null *hundm05-records*) (princ "\n Keine HundM05-Ergebnisse vorhanden.") (progn (vl-mkdir out-dir) (setq pfad (strcat out-dir "/hundm05_results.json")) (vsp-results-schreiben *hundm05-records* pfad))) (princ)) (princ "\nTEST_HUNDM05 geladen. Aufruf: TEST_HUNDM05 (bzw. _2D / _3D)") (princ)