7242e815f8
- lib/dxf_scan_components.py: erkennt ILS-/Omniflo-Komponenten in fremden
Projektzeichnungen und schreibt sie im Testdaten-JSON-Schema aus
- lib/dxf_abbild.py: getreue Abbildung der Bauteile einer Fremdzeichnung
(Attribute, Weltkoordinaten, Unterkomponenten) ohne Interpretation
- tests/test_hundm05.{lsp,py}, testdata/hundm05.json, conftest-Fixtures und
alltests.json-Eintrag fuer den Kreisel-Abschnitt aus ST500592_05.dxf
- menu: TEST_HUNDM05 im Testmenue, Connection_Insert/Edit in SSG_LIB.cui
- Doku: hartkodierte Pfade durch (getenv "DXFMAKRO") ersetzt
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
602 lines
27 KiB
Common Lisp
602 lines
27 KiB
Common Lisp
;; ============================================================
|
|
;; 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)
|
|
)
|