;; ============================================================ ;; test_hundm05.lsp - Integrationstest: baut den Kreisel-Abschnitt der ;; Anlage HundM aus der Fremdzeichnung ST500592_05.dxf nach. ;; ;; Liest tests/testdata/hundm05.json (erzeugt von lib/dxf_scan_components.py ;; mit --fenster 216000 144000 228000 156000) und baut in EINER Zeichnung: ;; - Kreisel (Eintraege mit "id", OHNE "type") Block KREISEL_* ;; "function": "connect" -> kreisel-connect-script (Start-/ ;; Endpunkt, abstand+rotation werden intern berechnet) ;; "function": "insert" -> kreisel-insert-script ;; - Vario (test_id "VF_*") Block VF_* via variofoerderer-einfuegen ;; - Gefaelle (test_id "GF_*") Block GF_* via gefaellestrecke-einfuegen ;; - Gerade (test_id "GERADE_*") waagrechte Foerderstrecke via ;; insert-hz-scaled-block-by-ks (Staustrecke_SP_1000_mm) ;; - Omniflo (Eintraege mit "type") - NICHT gebaut, siehe hundm05:build-omniflo ;; - Sensoren (Eintraege mit "block") via _.INSERT ;; ;; Speichert (via SSG_RUN_ALL_TESTS bei "save":"dxf"): ;; tests/output/hundm05_tests.dxf ;; tests/output/hundm05_results.json (via hundm05:export-results) ;; ;; Unterschiede zu tests/test_mubea.lsp - warum eine eigene Datei und kein ;; gemeinsamer Treiber: ;; 1. Der Kreisel kommt hier als "connect" (zwei Punkte aus dem DXF), bei ;; Mubea als "insert" (Punkt + abstand + rotation von Hand). ;; 2. Die VarioFoerderer nennen ihren Startpunkt "startpunkt" (Liste, wie ;; object_data.md 3.1) statt x/y/z, und sie haben kein "L_GF1" - der ;; L_GF-Split ist hier "gleichmaessig" (Default nach object_data.md). ;; 3. Es gibt den Eintragstyp "gerade", den mubea.json nicht kennt. ;; 4. Eintraege mit "type" (Omniflo) muessen VOR "id" abgefragt werden, weil ;; sie beides tragen - bei Mubea gab es diesen Fall nicht. ;; test_mubea.lsp bleibt dadurch unangetastet und die bestehende Suite ;; unveraendert lauffaehig. ;; ;; Voraussetzungen: ;; - SSG_LIB geladen (KreiselInsert, VarioFoerderer, Gefaellestrecke, ;; SSG_LIB_Commands, ssg_core, vf_core) ;; - Umgebungsvariable DXFMAKRO gesetzt ;; ;; Aufruf in BricsCAD: ;; (load (strcat (getenv "DXFMAKRO") "/tests/test_hundm05.lsp")) ;; TEST_HUNDM05 ;; ============================================================ ;; --- Ergebnis eines Bau-Schritts als JSON-String erzeugen --- (defun hundm05:result-json (test-id kind status block-name block-handle insert-point attribs / json tag val first) (setq json (strcat " {\n" " \"test_id\": \"" test-id "\",\n" " \"kind\": \"" kind "\",\n" " \"status\": \"" status "\",\n" " \"block_name\": \"" (if block-name block-name "") "\",\n" " \"block_handle\": \"" (if block-handle block-handle "") "\",\n" " \"insert_point\": [" (if insert-point (strcat (rtos (car insert-point) 2 1) ", " (rtos (cadr insert-point) 2 1) ", " (rtos (if (caddr insert-point) (caddr insert-point) 0.0) 2 1)) "0.0, 0.0, 0.0") "],\n" " \"actual_attributes\": {")) (setq first T) (foreach att attribs (setq tag (car att) val (cdr att)) (if (not first) (setq json (strcat json ","))) (setq json (strcat json "\n \"" tag "\": \"" (if val val "") "\"")) (setq first nil)) (setq json (strcat json "\n }\n }")) json ) ;; --- Aus einer Block-ENAME ein Ergebnis-JSON erzeugen --- (defun hundm05:ent-json (test-id kind ent / ed) (if ent (progn (setq ed (entget ent)) (hundm05:result-json test-id kind "executed" (cdr (assoc 2 ed)) (cdr (assoc 5 ed)) (cdr (assoc 10 ed)) (ssg-attrib-read ent))) (hundm05:result-json test-id kind "failed" nil nil nil nil)) ) ;; --- Letzten INSERT mit Blocknamen-Praefix (rueckwaerts) suchen --- (defun hundm05:last-insert-prefix (pref / ent found ed bn) (setq ent (entlast)) (while (and ent (null found)) (setq ed (entget ent) bn (cdr (assoc 2 ed))) (if (and (= (cdr (assoc 0 ed)) "INSERT") bn (>= (strlen bn) (strlen pref)) (= (substr bn 1 (strlen pref)) pref)) (setq found ent)) (setq ent (entprev ent))) found ) ;; --- Zahl aus JSON lesen, mit Default statt Fehler bei fehlendem Feld --- ;; (float nil) bricht in AutoLISP ab. hundm05.json laesst - anders als ;; mubea.json - bewusst Felder weg (z.B. "winkel" und "L_GF1" beim Vario, damit ;; der Solver selbst waehlt), darum hier grundsaetzlich abgesichert lesen. (defun hundm05:num (e key default / v) (setq v (ssg-val e key)) (if (numberp v) (float v) default) ) ;; --- Punkt-Liste aus JSON lesen ("startpunkt"/"start_mm": [x, y, z]) --- ;; Liefert NIEMALS einen Ersatzpunkt. Ein Ursprungs-Fallback (0 0 0) waere hier ;; fatal: er baut die Anlage sichtbar, aber komplett an der falschen Stelle - ;; genau das passiert, wenn das Array in der JSON ueber mehrere Zeilen ;; umgebrochen ist (siehe hundm05:startpunkt). (defun hundm05:pt (e key / v) (setq v (ssg-val e key)) (if (and v (listp v) (>= (length v) 3)) (mapcar 'float v) nil) ) ;; --- Startpunkt eines Eintrags lesen: Array-Feld, sonst flache x/y/z --- ;; Rueckgabe: Punkt oder nil. Bei nil MUSS der Aufrufer den Eintrag als ;; "failed" melden und darf nichts bauen. ;; ;; Haeufigste Ursache fuer nil: ssg-parse-json-array (ssg_core.lsp) liest die ;; JSON ZEILENWEISE, und ssg-cfg-parse-array braucht das ganze Array in einer ;; Zeile. Ein von json.dump(indent=2) umgebrochenes Array ;; "start_mm": [ ;; 219540.85, ;; ... ;; ] ;; liefert nil. Die Ausgabe von lib/dxf_scan_components.py zieht Zahlen-Arrays ;; darum wieder auf eine Zeile (arrays_einzeilig). (defun hundm05:startpunkt (e feld / p) (setq p (hundm05:pt e feld)) ;; Ersatzweise flache x/y/z-Felder zulassen (Schema wie mubea.json) (if (and (null p) (numberp (ssg-val e "x"))) (setq p (list (hundm05:num e "x" 0.0) (hundm05:num e "y" 0.0) (hundm05:num e "z" 0.0)))) p ) ;; --- Meldung fuer einen nicht lesbaren Startpunkt --- (defun hundm05:kein-startpunkt (tid feld) (princ (strcat "\n FEHLER: " tid " - Feld \"" feld "\" nicht lesbar." " Steht das Array in EINER Zeile? ssg-cfg-parse-array kann" " keine umgebrochenen Arrays lesen. Nichts gebaut.")) ) ;; ============================================================ ;; Bau-Funktionen je Elementtyp ;; ============================================================ ;; --- Kreisel: "connect" (Start-/Endpunkt) oder "insert" (Punkt+abstand) --- ;; Der Scanner liefert fuer AN8/SP8-Paare "connect": beide Stationen stehen im ;; DXF, abstand und rotation berechnet kreisel-connect-script daraus selbst ;; (abstand = Distanz - *kreisel-durchmesser*). "insert" bleibt fuer von Hand ;; gepflegte Eintraege moeglich (Schema wie mubea.json). (defun hundm05:build-kreisel (e / id fn typ hoehe ptStart ptEnd x y z abstand rotation ent) (setq id (ssg-val e "id") fn (ssg-val e "function") typ (ssg-val e "typ") hoehe (ssg-val e "hoehe")) (if (null typ) (setq typ "STANDARD")) (if (numberp hoehe) (setq hoehe (float hoehe)) (setq hoehe nil)) (if (= fn "connect") (progn (setq ptStart (list (hundm05:num e "start_x" 0.0) (hundm05:num e "start_y" 0.0) (hundm05:num e "start_z" 0.0))) (setq ptEnd (list (hundm05:num e "end_x" 0.0) (hundm05:num e "end_y" 0.0) (hundm05:num e "end_z" 0.0))) (princ (strcat "\n [KREISEL] " id " connect (" (rtos (car ptStart) 2 0) "," (rtos (cadr ptStart) 2 0) ") -> (" (rtos (car ptEnd) 2 0) "," (rtos (cadr ptEnd) 2 0) ")" " H=" (if hoehe (rtos hoehe 2 0) "-"))) (setq ent (kreisel-connect-script ptStart ptEnd typ hoehe))) (progn (setq x (hundm05:num e "x" 0.0) y (hundm05:num e "y" 0.0) z (hundm05:num e "z" 0.0) abstand (hundm05:num e "abstand" *kreisel-default-laenge*) rotation (hundm05:num e "rotation" 0.0)) (princ (strcat "\n [KREISEL] " id " insert (" (rtos x 2 0) "," (rtos y 2 0) "," (rtos z 2 0) ") A=" (rtos abstand 2 0) " R=" (rtos rotation 2 1))) (setq ent (kreisel-insert-script (list x y z) abstand rotation typ (if hoehe hoehe z))))) (hundm05:ent-json id "kreisel" ent) ) ;; --- Vario (VF_*) --- ;; Wie mubea:build-vario-one, aber ohne "winkel"-Zwang und ohne L_GF1-Ziel: ;; hundm05.json gibt nur deltaL/deltaH/richtung/seite/hz vor, den Bogenwinkel ;; waehlt berechne-alle-winkel selbst (kleinster gueltiger Kandidat) und der ;; L_GF-Split ist "gleichmaessig" (beide Haelften L_GF/2, Default nach ;; object_data.md 3.1). Ist "L_GF1" doch gesetzt, wird es als Vorlauf-Ziel ;; verwendet - dann verhaelt sich der Treiber wie der Mubea-Treiber. (defun hundm05:build-vario (e / tid typ richtung deltaL deltaH seite hz startpunkt gf1-ziel ergebnis best-winkel L_GF L_VF gf1 gf2 hoehe-von hoehe-bis lastEnt ent vf-nummer) (setq tid (ssg-val e "test_id") typ (ssg-val e "typ") richtung (ssg-val e "richtung") deltaL (hundm05:num e "deltaL" 0.0) deltaH (hundm05:num e "deltaH" 0.0) seite (ssg-val e "seite") hz (hundm05:num e "hz" 0.0) gf1-ziel (ssg-val e "L_GF1")) (if (or (null typ) (= typ "")) (setq typ "standard")) (if (or (null seite) (= seite "")) (setq seite "rechts")) (if (or (null richtung) (= richtung "")) (setq richtung "Auf")) (setq startpunkt (hundm05:startpunkt e "startpunkt")) (if (null startpunkt) (hundm05:kein-startpunkt tid "startpunkt")) (princ (strcat "\n [VARIO] " tid " (" typ ") -> deltaL=" (rtos deltaL 2 0) " deltaH=" (rtos deltaH 2 0) " " richtung " hz=" (rtos hz 2 0))) (setq ergebnis (if (= typ "etage") (berechne-etage deltaL deltaH richtung seite) (berechne-alle-winkel deltaL deltaH richtung nil))) (setq best-winkel (car ergebnis) L_GF (cadr ergebnis) L_VF (caddr ergebnis)) ;; startpunkt MUSS mit in die Bedingung: ohne ihn wuerde am Ursprung gebaut. (if (and startpunkt best-winkel L_GF L_VF (> L_GF 0) (> L_VF 0)) (progn ;; L_GF-Split: mit L_GF1-Vorgabe wie bei Mubea (Vorlauf-Ziel), sonst ;; gleichmaessig auf Vor- und Nachlauf verteilt. (if (numberp gf1-ziel) (setq gf1 (min (float gf1-ziel) L_GF)) (setq gf1 (/ L_GF 2.0))) (setq gf2 (max 0.0 (- L_GF gf1))) (princ (strcat " -> Winkel=" (itoa best-winkel) " L_GF=" (rtos L_GF 2 1) " L_VF=" (rtos L_VF 2 1) " L_GF1=" (rtos gf1 2 1) " L_GF2=" (rtos gf2 2 1))) ;; Absolute Hoehen eintragen. hoehe-von/hoehe-bis gehen NUR in Label und ;; Attribute (MONTAGEHOEHE_m, HOEHE_VON_mm - vf_core.lsp), die Geometrie ;; kommt aus startpunkt. test_mubea.lsp uebergibt hier 0 und traegt damit ;; RELATIVE Hoehen ein; fuer diese Zeichnung stehen die echten Z-Werte im ;; DXF, also werden sie auch verwendet - sonst exportiert EXPORTCSV eine ;; falsche Montagehoehe (bei VF_HundM05_11 waeren es 2.914 m statt ;; -1.028 m). (setq hoehe-von (caddr startpunkt)) (setq hoehe-bis (+ hoehe-von (if (= richtung "Auf") deltaH (- deltaH)))) (setq lastEnt (vf-lastent-ohne-attribute)) (if (= typ "etage") (etage-foerderanlage-einfuegen deltaL deltaH richtung best-winkel gf1 gf2 L_VF startpunkt seite hz) (variofoerderer-einfuegen deltaL deltaH richtung best-winkel gf1 gf2 L_VF startpunkt seite hz)) ;; Blocknummer wie in der Produktion ueber vf-next-number holen (NICHT ;; fest auf 1) - sonst trifft das _.BLOCK des zweiten Foerderers auf einen ;; schon vorhandenen Namen, BricsCAD fragt "Neu definieren?" und ;; verschluckt die feste Antwortfolge (siehe test_mubea.lsp). (setq vf-nummer (vf-next-number)) (setq ent (vf-block-erstellen typ seite vf-nummer 0 hoehe-von hoehe-bis deltaH deltaL L_VF gf1 gf2 richtung best-winkel startpunkt lastEnt hz "0" "Schoenenberger Geruest")) (if (or (null ent) (/= (type ent) 'ENAME)) (setq ent (hundm05:last-insert-prefix "VF_"))) ;; SSG_VF_EDIT-XDATA schreiben, sonst verweigert VARIOFOERDERER_EDIT beim ;; Doppelklick die Bearbeitung (vfs-edit-keine-xdata). (if (and ent (= (type ent) 'ENAME) (member typ '("standard" "etage"))) (vf-edit-xdata-schreiben ent typ hz)) (hundm05:ent-json tid "vario" ent)) (progn ;; Ursache benennen, nicht pauschal "nicht loesbar" melden: ein fehlender ;; Startpunkt hat nichts mit dem Winkel-Solver zu tun, und eine falsche ;; Diagnose schickt die Fehlersuche in die falsche Richtung. (if (null startpunkt) (princ (strcat "\n FEHLER: " tid " nicht gebaut - Startpunkt fehlt" " (siehe Meldung oben).")) (princ (strcat "\n FEHLER: " tid " deltaL=" (rtos deltaL 2 0) " deltaH=" (rtos deltaH 2 0) " nicht loesbar" " (kein gueltiges L_GF/L_VF)."))) (hundm05:result-json tid "vario" "failed" nil nil startpunkt nil))) ) ;; --- Gefaellestrecke: Init-Konflikt umgehen ----------------------------- ;; gefaellestrecke-einfuegen prueft "(if (not *lib-initialized*) ;; (gf-init-bibliothek))" - dasselbe Flag, das init-bibliothek (Vario) fuer ;; SEINE eigenen aus-dx/ein-dx setzt. Wird zuerst ein Vario gebaut, kaeme ;; gf-init-bibliothek nie mehr zum Zug und aus-dx/ein-dx blieben die ;; Vario-Werte. Darum einmal je Lauf erzwingen (wie in test_mubea.lsp). (defun hundm05:ensure-gf-init ( / ) (if (not *hundm05-gf-init-done*) (progn (setq *lib-initialized* nil) (gf-init-bibliothek) (setq *lib-initialized* t) (setq *hundm05-gf-init-done* T))) ) ;; --- Eine Gefaellestrecke bauen (Modus 1) --- ;; Aufbau ausschliesslich aus Anfangspunkt und horizontaler Gesamtlaenge ;; deltaL - KEIN Endpunkt (wie Produktion/gefaellestrecke.dcl Modus 1, siehe ;; object_data.md 2.1). L_stau aus deltaL abgeleitet, Formel identisch zu ;; test_gefaellestrecke.lsp/test_mubea.lsp. (defun hundm05:build-gf (e / tid start-mm deltaL hz as-seite es-seite as-winkel es-winkel winkel) (setq tid (ssg-val e "test_id") start-mm (hundm05:startpunkt e "start_mm") deltaL (hundm05:num e "deltaL_mm" 0.0) hz (hundm05:num e "hz_grad" 0.0) as-seite (ssg-val e "as_seite") es-seite (ssg-val e "es_seite") as-winkel(ssg-val e "as_winkel") es-winkel(ssg-val e "es_winkel") winkel (hundm05:num e "winkel_grad" 3.0)) (if (or (null as-seite) (= as-seite "")) (setq as-seite "links")) (if (or (null es-seite) (= es-seite "")) (setq es-seite "links")) (if (or (null as-winkel) (= as-winkel "")) (setq as-winkel "90")) (if (or (null es-winkel) (= es-winkel "")) (setq es-winkel "90")) (if (null start-mm) (progn (hundm05:kein-startpunkt tid "start_mm") (hundm05:result-json tid "gefaellestrecke" "failed" nil nil nil nil)) (hundm05:build-gf-punkt tid start-mm deltaL hz as-seite es-seite as-winkel es-winkel winkel)) ) ;; --- Gefaellestrecke mit gepruefter Startkoordinate bauen --- (defun hundm05:build-gf-punkt (tid start-mm deltaL hz as-seite es-seite as-winkel es-winkel winkel / rad sep-x L_stau ent) (hundm05:ensure-gf-init) ;; Element-Masse zur gewaehlten AS/ES-Variante setzen, damit aus-dx/ein-dx ;; zur Variante (90/30 Grad) passen; danach auf 90 zuruecksetzen. (vf-set-as-masse as-winkel as-seite) (vf-set-es-masse es-winkel es-seite) (setq rad (* winkel (/ pi 180.0))) (setq sep-x (* 300.0 (cos rad))) (setq L_stau (/ (- deltaL (float aus-dx) (float ein-dx) sep-x) (cos rad))) (princ (strcat "\n [GF] " tid " -> Start=(" (rtos (car start-mm) 2 0) "," (rtos (cadr start-mm) 2 0) "," (rtos (caddr start-mm) 2 0) ") hz=" (rtos hz 2 0) " deltaL=" (rtos deltaL 2 0) " winkel=" (rtos winkel 2 2) " L_stau=" (rtos L_stau 2 1))) (if (<= L_stau 0.0) (progn (princ (strcat " -> uebersprungen: L_stau <= 0 (deltaL " (rtos deltaL 2 0) " zu kurz fuer AS/ES-Elemente + Separator)")) (vf-set-as-masse "90" as-seite) (vf-set-es-masse "90" es-seite) (hundm05:result-json tid "gefaellestrecke" "failed" nil nil start-mm nil)) (progn (setq ent (car (gefaellestrecke-einfuegen L_stau winkel start-mm as-seite es-seite hz deltaL as-winkel es-winkel "0" "Schoenenberger Geruest"))) (vf-set-as-masse "90" as-seite) (vf-set-es-masse "90" es-seite) (hundm05:ent-json tid "gefaellestrecke" ent))) ) ;; --- Waagrechte Foerderstrecke (GERADE_*) --- ;; Antrieb/Umlenk-Paare ohne Hoehendifferenz sind keine VarioFoerderer (deren ;; Winkel-Solver braucht eine Hoehendifferenz, siehe GERADE_MAX_DELTA_H in ;; lib/dxf_scan_components.py). Gebaut wird mit derselben Produktions-Primitive, ;; die der Etage-Foerderer fuer seine waagrechten Abschnitte nutzt: ;; insert-hz-scaled-block-by-ks skaliert Staustrecke_SP_1000_mm auf die Laenge ;; und richtet sie an hz aus (vert-winkel 0 = waagrecht). ;; Rueckgabe der Primitive ist der Ausgangspunkt, nicht die ENAME - der Block ;; wird darum ueber (entlast) eingesammelt. (defun hundm05:build-gerade (e / tid laenge hz startpunkt blk ent) (setq tid (ssg-val e "test_id") laenge (hundm05:num e "laenge" 0.0) hz (hundm05:num e "hz" 0.0) startpunkt (hundm05:startpunkt e "startpunkt")) (if (null startpunkt) (hundm05:kein-startpunkt tid "startpunkt") (princ (strcat "\n [GERADE] " tid " -> Start=(" (rtos (car startpunkt) 2 0) "," (rtos (cadr startpunkt) 2 0) "," (rtos (caddr startpunkt) 2 0) ") laenge=" (rtos laenge 2 0) " hz=" (rtos hz 2 0)))) (if (or (null startpunkt) (<= laenge 0.0)) (progn (if startpunkt (princ " -> uebersprungen: laenge <= 0")) (hundm05:result-json tid "gerade" "failed" nil nil startpunkt nil)) (progn ;; Block VORHER laden und pruefen: insert-hz-scaled-block-by-ks ruft bei ;; fehlendem Block (exit) auf und wuerde den gesamten Testlauf abbrechen. (setq blk (ssg-ils-block-laden "Staustrecke_SP_1000_mm")) (if (null blk) (progn (princ " -> uebersprungen: Block Staustrecke_SP_1000_mm nicht ladbar") (hundm05:result-json tid "gerade" "failed" "Staustrecke_SP_1000_mm" nil startpunkt nil)) (progn (insert-hz-scaled-block-by-ks "Staustrecke_SP_1000_mm" startpunkt laenge hz 0) (setq ent (entlast)) (if (and ent (atoms-family 1 '("ssg-id-generate"))) (ssg-id-generate ent)) (hundm05:ent-json tid "gerade" ent))))) ) ;; --- Omniflo (Eintraege mit "type": "bogen"/"weiche") --- ;; BEWUSST nicht gebaut. omni:insert-dxf (Lisp/OmniModulInsert.lsp) holt den ;; Einfuegepunkt zwingend per (command "_.INSERT" dxf-pfad pause ...) - es gibt ;; in Produktion keinen Aufrufpfad, der X/Y programmgesteuert uebergibt (siehe ;; object_data.md 4.1). Ein JSON-Treiber muesste den _.INSERT selbst nachbauen. ;; Im gewaehlten Abschnitt (Kreisel-Bereich) kommt kein einziger Omniflo-Eintrag ;; vor, darum wird hier nichts auf Verdacht implementiert, was der Testlauf nie ;; ausfuehren koennte. Der eigene Zweig ist trotzdem noetig: Omniflo-Eintraege ;; tragen ein "id"-Feld und wuerden sonst als Kreisel gebaut werden. (defun hundm05:build-omniflo (e / tid) (setq tid (cond ((ssg-val e "id")) ((ssg-val e "sivasnr")) ("OMNIFLO"))) (princ (strcat "\n [OMNIFLO] " tid " -> uebersprungen (nicht scriptbar, " "siehe object_data.md 4.1)")) (hundm05:result-json tid "omniflo" "unsupported" (ssg-val e "sivasnr") nil (list (hundm05:num e "x" 0.0) (hundm05:num e "y" 0.0) (hundm05:num e "z" 0.0)) nil) ) ;; --- Ein einzelner Sensor/Block: Blockdefinition laden, dann per Name einfuegen --- ;; Wie mubea:build-separator-one. Der in JSON genannte Block wird ueber ;; ssg-ils-block-laden vorab definiert (loest 2D/3D-Suffix auf); schlaegt das ;; fehl, wird auf Separator_SP zurueckgefallen - genau das greift bei den ;; Blocknamen "S-LP"/"S-SP", die es in data/ils/ nicht gibt. (defun hundm05:build-sensor (e idx / blockname x y z rot tid eff n pt) (setq blockname (ssg-val e "block") x (hundm05:num e "x" 0.0) y (hundm05:num e "y" 0.0) z (hundm05:num e "z" 0.0) rot (hundm05:num e "rotation" 0.0) tid (cond ((ssg-val e "test_id")) ((strcat "SENSOR_" (itoa idx))))) (setq pt (list x y z)) (princ (strcat "\n [SENSOR] " tid " '" (if blockname blockname "?") "' -> (" (rtos x 2 0) "," (rtos y 2 0) "," (rtos z 2 0) ") R=" (rtos rot 2 0))) ;; NICHT (and blockname (ssg-ils-block-laden ...)) verwenden: AutoLISPs AND ;; liefert T statt des letzten Wertes, das _.INSERT wuerde dann einen Block ;; namens T einfuegen (siehe Kommentar in test_mubea.lsp). (setq eff (if blockname (ssg-ils-block-laden blockname))) (if (null eff) (setq eff (ssg-ils-block-laden "Separator_SP"))) (if eff (progn (command "_.INSERT" eff pt "" "" rot) ;; Verbleibende Prompts GEZAEHLT mit Enter abfangen - ein Prompt, der die ;; Leer-Antwort ablehnt, haelt CMDACTIVE dauerhaft > 0 und eine ungebremste ;; Schleife wuerde BricsCAD haengen lassen. (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.")))) (if (atoms-family 1 '("ssg-id-generate")) (ssg-id-generate (entlast))) (hundm05:ent-json tid "sensor" (entlast))) (progn ;; Kein Block gefunden -> sichtbaren Platzhalter zeichnen (if (not (tblsearch "LAYER" "HUNDM05_SENSOR")) (entmake (list '(0 . "LAYER") '(2 . "HUNDM05_SENSOR") '(70 . 0) '(62 . 1)))) (entmake (list '(0 . "CIRCLE") (cons 10 pt) (cons 40 150.0) '(8 . "HUNDM05_SENSOR"))) (princ " (Block fehlt - Platzhalter)") (hundm05:result-json tid "sensor" "placeholder" (if blockname blockname "Separator_SP") nil pt nil))) ) ;; --- JSON-Export der Ergebnisse --- (defun hundm05:export-results (tests-out-dir / out-json f first) (if (null *hundm05-test-results*) (princ "\n Keine HundM05-Ergebnisse vorhanden.") (progn (vl-mkdir tests-out-dir) (setq out-json (strcat tests-out-dir "/hundm05_results.json")) (setq f (open out-json "w")) (if f (progn (write-line "[" f) (setq first T) (foreach r *hundm05-test-results* (if (not first) (write-line "," f)) (write-line r f) (setq first nil)) (write-line "]" f) (close f) (princ (strcat "\n Ergebnisse: " out-json))) (princ (strcat "\n FEHLER: Kann " out-json " nicht schreiben!"))))) ) ;; ============================================================ ;; C:TEST_HUNDM05 - Hauptbefehl ;; ============================================================ (defun c:TEST_HUNDM05 ( / json-datei daten eintrag tid res r results-list sensor-idx anz-ok anz-fehler) (dbgopen "hundm05_id.dbg" "DXFM_LOG") ;; Benoetigte Feature-Module laden (ssg-ensure "KreiselInsert") (ssg-ensure "VarioFoerderer") (ssg-ensure "Gefaellestrecke") (ssg-ensure "SSG_LIB_Commands") (ssg-start "TEST_HUNDM05" '(("OSMODE") ("CECOLOR") ("ATTREQ") ("ATTDIA"))) (setvar "OSMODE" 0) (setvar "ATTREQ" 0) (setvar "ATTDIA" 0) ;; Block-Bibliothek (Bogen-/AS-/ES-Masse) initialisieren - fuer Vario+GF noetig (if (or (not *lib-initialized*) (null bogen-auf)) (init-bibliothek)) ;; Cache fuer den GF-Init-Workaround pro Lauf zuruecksetzen (setq *hundm05-gf-init-done* nil) ;; Testdaten laden (setq json-datei (strcat (getenv "DXFMAKRO") "/tests/testdata/hundm05.json")) (if (not (findfile json-datei)) (progn (princ (strcat "\n[TEST_HUNDM05] FEHLER: " json-datei " nicht gefunden!")) (ssg-end) (dbgclose) (exit))) (setq daten (ssg-load-json json-datei)) (if (null daten) (progn (princ "\n[TEST_HUNDM05] FEHLER: Testdaten konnten nicht geladen werden.") (ssg-end) (dbgclose) (exit))) (princ "\n\n================================================================") (princ "\n TEST_HUNDM05 - HundM Kreisel-Abschnitt (ST500592_05.dxf)") (princ (strcat "\n " (itoa (length daten)) " Eintraege geladen")) (princ "\n================================================================") (setq results-list '() sensor-idx 0 anz-ok 0 anz-fehler 0) (foreach eintrag daten (setq tid (ssg-val eintrag "test_id")) (setq res (cond ;; Sensor/Insert: hat "block" ((ssg-val eintrag "block") (setq sensor-idx (1+ sensor-idx)) (hundm05:build-sensor eintrag sensor-idx)) ;; Omniflo VOR Kreisel pruefen: diese Eintraege tragen "id" UND "type" ((ssg-val eintrag "type") (hundm05:build-omniflo eintrag)) ;; Kreisel: hat "id" (ohne "type") ((ssg-val eintrag "id") (hundm05:build-kreisel eintrag)) ;; Vario: test_id "VF_*" ((and tid (>= (strlen tid) 3) (= (substr tid 1 3) "VF_")) (hundm05:build-vario eintrag)) ;; Gefaellestrecke: test_id "GF_*" ((and tid (>= (strlen tid) 3) (= (substr tid 1 3) "GF_")) (hundm05:build-gf eintrag)) ;; Waagrechte Strecke: test_id "GERADE_*" ((and tid (>= (strlen tid) 7) (= (substr tid 1 7) "GERADE_")) (hundm05:build-gerade eintrag)) (T (princ "\n WARNUNG: Unbekannter Eintrag uebersprungen") nil))) ;; Alle Builder liefern hier je einen String (keine Templates mit "anzahl" ;; wie bei Mubea) - der Vollstaendigkeit halber trotzdem Listen zulassen. (if res (progn (if (= (type res) 'STR) (setq res (list res))) (foreach r res (setq results-list (cons r results-list)) (if (or (vl-string-search "\"executed\"" r) (vl-string-search "\"placeholder\"" r) (vl-string-search "\"unsupported\"" r)) (setq anz-ok (1+ anz-ok)) (setq anz-fehler (1+ anz-fehler))))))) (princ "\n================================================================") (princ (strcat "\n Ergebnis: " (itoa anz-ok) " OK, " (itoa anz-fehler) " Fehler")) (princ "\n================================================================") (setq *hundm05-test-results* (reverse results-list)) (ssg-end) (princ "\n TEST_HUNDM05 abgeschlossen.") (princ) (dbgclose) )