[ADD] lib/dxf_anlage_export.py: gebaute und von Hand nachbearbeitete Anlage aus der Zeichnung zurueck in Nachbau-Testdaten (Kreisel, GF, VF standard/etage, VF-Linienzug als unveraendertes Journal, Einzelbloecke) - tests/testdata/dematic_arvato.json jetzt aus Dematic_TST.dxf; [ADD] dxf_scan_components.py: Gefaellestrecken aus Staustrecken-LINEs (Layer 6-SP), Start/Ende auf die Kreisel-Ketten verlaengert, AS-Element und Separator auf der Achse werden verbraucht, Steigfoerderer erkannt, --ohne-hoehen und --kreisel-hoehe; [ADD] vf_spec.lsp: Replay-Kern vsp-bau-journal aus vsp-bau-aus-spec herausgeloest (auch fuer aufgezeichnete Journale); [CHANGE] TEST_DEMATIC_ARVATO baut GF, VF standard/etage und Linienzug-Journale (mubea:gf-bauen/-vario-bauen wiederverwendet); [FIX] GF-Winkel aus Kreisel-Hoehen: AS/ES-Abfall mit falschem Vorzeichen an gf-winkel-berechnen uebergeben (3.79 statt ~2.49 Grad, GF endete 258 mm zu tief); [ADD] Tests test_dxf_scan_gf.py, test_dxf_anlage_export.py

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 13:35:46 +02:00
parent 2f9cb7eaed
commit ba20d63f63
9 changed files with 2024 additions and 1328 deletions
+201 -24
View File
@@ -1,30 +1,53 @@
;; ============================================================
;; test_dematic_arvato.lsp - Nachbau des Abschnitts Flowpuffer/Kreisel der
;; Anlage Dematic/Arvato aus der Scanner-Vorlage.
;; Anlage Dematic/Arvato.
;;
;; Datenquelle: tests/testdata/dematic_arvato.json, erzeugt mit
;; python lib/dxf_scan_components.py \
;; C:/develop/sps_skel/data/Dematic_arvato.dxf data \
;; tests/testdata/dematic_arvato.json --projekt Arvato \
;; --fenster -3000 159000 49000 193000
;; Datenquelle: tests/testdata/dematic_arvato.json. Entstehung in zwei
;; Stufen:
;; 1. Vorlage aus der Fremdzeichnung (Scanner, raet vieles):
;; python lib/dxf_scan_components.py \
;; C:/develop/sps_skel/data/Dematic_arvato_reduced.dxf data \
;; tests/testdata/dematic_arvato.json --projekt Arvato \
;; --fenster -2534 -4549 49466 29451 --ohne-hoehen \
;; --kreisel-hoehe 35860.37 12519.72 2437 \
;; --kreisel-hoehe 36661.24 24878.8 1826
;; 2. Vorlage mit diesem Treiber bauen, in BricsCAD von Hand korrigieren
;; und ergaenzen (VF-Ketten, Hoehen, Sensoren aufgeraeumt), als DXF
;; speichern und daraus die Testdaten neu schreiben - AKTUELLER STAND:
;; python lib/dxf_anlage_export.py \
;; C:/develop/sps_skel/data/Dematic_TST.dxf \
;; tests/testdata/dematic_arvato.json --projekt Arvato
;;
;; Die Datei ist eine VORLAGE fuer den Nachbau, kein fertiger Testfall:
;; Hoehen fehlen (flaches 2D-Schema, alles auf Z=0), Seiten/Winkel einiger
;; Bloecke sind geraten (siehe "comment" je Eintrag).
;;
;; Eintragsarten (Schema von dxf_scan_components.py):
;; Eintragsarten:
;; "function": "insert" ohne "block" -> Kreisel (hundm05:bau-kreisel ->
;; kreisel-insert-script; x/y = Einfuegepunkt
;; einen Radius vor der AN8-Radmitte, abstand =
;; Mittenabstand AN8-SP8)
;; "function": "insert" + "block" -> Einzelblock (hundm05:bau-sensor,
;; Fallback Separator_SP bei unbekanntem Block)
;; "typ": "gerade" -> waagrechter Foerderer Antrieb->Umlenkung -
;; dafuer gibt es noch keinen Bau-Pfad, wird
;; gemeldet und uebersprungen
;; kreisel-insert-script; x/y = Einfuegepunkt,
;; abstand = Mittenabstand AN8-SP8)
;; "function": "insert" + "block" -> Einzelblock (hundm05:bau-sensor)
;; "typ": "standard"/"etage" + "test_id"
;; -> VarioFoerderer ueber mubea:vario-bauen
;; (mubea-Schema: deltaL, deltaH, richtung,
;; winkel, L_GF1, seite, hz, x/y/z), optional
;; "motorseite"
;; "typ": "linienzug_journal"
;; -> Linienzug-Kette: das aufgezeichnete Eingabe-
;; Journal (SSG_VF_EDIT) wird 1:1 abgespielt
;; (vsp-bau-journal) - keine Spec, weil der
;; Dialog neuer ist als die Spec-Grammatik
;; "modus": 1 -> Gefaellestrecke ueber mubea:gf-bauen
;; (start_mm + deltaL_mm, kein Endpunkt). Mit
;; hoehe_ende_mm (Hoehe des Kreisels am Ende)
;; rechnet dematic_arvato:gf-winkel den Winkel,
;; sonst gilt winkel_grad.
;; "typ": "gerade" -> waagrechter Foerderer aus der Scanner-Vorlage -
;; kein Bau-Pfad, wird gemeldet und uebersprungen
;;
;; Reihenfolge wie mubea.json (VF vor GF): gf-init-bibliothek setzt die
;; globalen AS/ES-Masse um, siehe mubea:ensure-gf-init.
;;
;; Keine eigene Bau-Logik: hundm05:bau-kreisel/-sensor aus
;; tests/test_hundm05.lsp wird mitgeladen (die Datei definiert nur Funktionen).
;; tests/test_hundm05.lsp und mubea:gf-bauen/-vario-bauen aus
;; tests/test_mubea.lsp werden mitgeladen (beide Dateien definieren nur
;; Funktionen), Linienzug-Ketten laufen ueber vsp-bau-journal (vf_spec.lsp).
;;
;; Aufruf in BricsCAD:
;; (load (strcat (getenv "DXFMAKRO") "/tests/test_dematic_arvato.lsp"))
@@ -40,12 +63,30 @@
(ssg-ensure "KreiselInsert")
(ssg-ensure "VarioFoerderer")
(ssg-ensure "SSG_LIB_Commands")
(ssg-ensure "Gefaellestrecke")
(if (null (car (atoms-family 1 '("HUNDM05:BAU-SENSOR"))))
(load (strcat (getenv "DXFMAKRO") "/tests/test_hundm05.lsp")))
(if (null (car (atoms-family 1 '("MUBEA:VARIO-BAUEN"))))
(load (strcat (getenv "DXFMAKRO") "/tests/test_mubea.lsp")))
(defun dematic_arvato:datei ()
(strcat (getenv "DXFMAKRO") "/tests/testdata/dematic_arvato.json"))
;; Record um den gebauten Block ergaenzen (oder als Abbruch markieren).
(defun dematic_arvato:rec-block (rec ent fehlertext / ed)
(if (and ent (= (type ent) 'ENAME))
(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 (strcat " FEHLER: " fehlertext))
(vsp-set (vsp-set rec "status" "abbruch") "fehler_text" fehlertext))))
;; Foerderer "gerade": noch kein Bau-Pfad - nur melden.
(defun dematic_arvato:gerade-melden (obj / tid rec)
(setq tid (ssg-val obj "test_id"))
@@ -56,15 +97,146 @@
(vsp-set (vsp-set rec "status" "uebersprungen") "fehler_text"
"kein Bau-Pfad fuer typ gerade"))
(defun dematic_arvato:bau-eintrag (obj / kind res)
;; Gefaellewinkel aus Start- und Endhoehe - derselbe Loeser wie Modus 3
;; (gf-winkel-berechnen, Gefaellestrecke.lsp) mit den AS/ES-Massen der
;; aktuellen 2D/3D-Bibliothek. Die Kette ist eine einzige Gerade plus der
;; eine Separator vor dem ES (extra-sep 1); ihre Laenge ohne AS/ES/Separator
;; ist deltaL - aus-dx - ein-dx - 300, damit ergibt der Loeser als
;; Gesamtlaenge wieder genau deltaL (deltaL reicht von AS- bis ES-Ursprung).
;; VORZEICHEN: vf-set-as-masse (vf_core) misst dz als KS_AUS.z - KS_EIN.z
;; (bei AS/ES negativ), gf-winkel-berechnen erwartet die Konvention von
;; gf-init-bibliothek (KS_EIN.z - KS_AUS.z, positiv) und zieht den Wert ab.
;; Mit der VF-Konvention wurde der feste AS/ES-Abfall addiert statt
;; abgezogen - real gebaut: 3.79 statt ~2.49 Grad, GF endete 258 mm unter
;; dem Kreisel (HOEHE_BIS 1568 statt 1826). Darum hier der Betrag.
(defun dematic_arvato:gf-winkel (deltaL hz as-seite es-seite as-winkel es-winkel
z-start z-ende / winkel)
(mubea:ensure-gf-init)
(vf-set-as-masse as-winkel as-seite)
(vf-set-es-masse es-winkel es-seite)
(setq aus-dz (abs (float aus-dz)) ein-dz (abs (float ein-dz)))
(setq winkel
(gf-winkel-berechnen
(list (list "Linie" hz (- deltaL (float aus-dx) (float ein-dx) 300.0) 0 "links"))
(- z-start z-ende) 1))
(vf-set-as-masse "90" as-seite)
(vf-set-es-masse "90" es-seite)
winkel)
;; Gefaellestrecke (Modus 1) bauen - Felder wie mubea:build-gf, ohne dessen
;; anzahl/delta-Vervielfaeltigung (jede GF steht einzeln in der Datei).
;; Mit "hoehe_ende_mm" (Hoehe des Kreisels am Ende) wird der Winkel daraus
;; gerechnet, sonst gilt "winkel_grad" (Default 3.0).
(defun dematic_arvato:bau-gf (obj / tid start deltaL hz winkel z-ende
as-seite es-seite as-winkel es-winkel rec)
(setq tid (ssg-val obj "test_id")
start (ssg-val obj "start_mm")
deltaL (ssg-val obj "deltaL_mm")
hz (ssg-val obj "hz_grad")
winkel (ssg-val obj "winkel_grad")
z-ende (ssg-val obj "hoehe_ende_mm")
as-seite (cond ((ssg-val obj "as_seite")) ("links"))
es-seite (cond ((ssg-val obj "es_seite")) ("links"))
as-winkel (cond ((ssg-val obj "as_winkel")) ("90"))
es-winkel (cond ((ssg-val obj "es_winkel")) ("90")))
(setq rec (vsp-rec-neu (if tid tid "(ohne id)")))
(setq rec (vsp-set rec "kind" "gefaellestrecke"))
(setq rec (vsp-set rec "dimension" (vsp-dim-text)))
(cond
((not (and (= (type start) 'LIST) (= (length start) 3)
(numberp deltaL) (numberp hz)))
(princ (strcat "\n [GF] " (if tid tid "?")
" - FEHLER: start_mm, deltaL_mm und hz_grad muessen gesetzt sein"))
(vsp-set (vsp-set rec "status" "spec-fehler") "fehler_text"
"start_mm, deltaL_mm oder hz_grad fehlt bzw. ist ungueltig"))
((and (numberp z-ende) (>= z-ende (caddr start)))
(princ (strcat "\n [GF] " (if tid tid "?")
" - FEHLER: hoehe_ende_mm liegt nicht unter der Starthoehe"))
(vsp-set (vsp-set rec "status" "spec-fehler") "fehler_text"
"hoehe_ende_mm >= start_mm.z - kein Gefaelle"))
(T
(setq winkel
(cond
((numberp z-ende)
(dematic_arvato:gf-winkel (float deltaL) (float hz) as-seite es-seite
as-winkel es-winkel
(float (caddr start)) (float z-ende)))
((numberp winkel) (float winkel))
(T 3.0)))
;; Winkel und Soll-Endhoehe als Meldung (das Record-Schema aus vf_spec
;; kennt dafuer kein Feld); die Ist-Endhoehe steht in attribs HOEHE_BIS.
(setq rec (vsp-set rec "meldungen"
(list (strcat "winkel_grad " (rtos winkel 2 3)
(if (numberp z-ende)
(strcat " aus Hoehe " (rtos (caddr start) 2 1)
" -> " (rtos z-ende 2 1))
"")))))
(dematic_arvato:rec-block rec
(mubea:gf-bauen tid (mapcar 'float start) (float deltaL) (float hz)
as-seite es-seite as-winkel es-winkel winkel)
"gefaellestrecke-einfuegen lieferte keinen GF-Block"))))
;; VarioFoerderer "standard"/"etage" - mubea-Schema, Bau ueber
;; mubea:vario-bauen. "motorseite" wirkt wie im Edit-Dialog ueber den
;; Override *vf-motorseite* (vf-block-erstellen liest ihn).
(defun dematic_arvato:bau-vf (obj / tid typ rec ent)
(setq tid (ssg-val obj "test_id")
typ (cond ((ssg-val obj "typ")) ("standard")))
(setq rec (vsp-rec-neu (if tid tid "(ohne id)")))
(setq rec (vsp-set rec "kind" "vario"))
(setq rec (vsp-set rec "dimension" (vsp-dim-text)))
(setq *vf-motorseite* (ssg-val obj "motorseite"))
(setq ent
(vl-catch-all-apply 'mubea:vario-bauen
(list tid typ (ssg-val obj "richtung")
(float (ssg-val obj "deltaL")) (float (ssg-val obj "deltaH"))
(ssg-val obj "winkel") (float (cond ((ssg-val obj "L_GF1")) (0.0)))
(ssg-val obj "seite") (ssg-val obj "hz")
(list (float (ssg-val obj "x")) (float (ssg-val obj "y"))
(float (ssg-val obj "z"))))))
(setq *vf-motorseite* nil)
(if (vl-catch-all-error-p ent)
(vsp-set (vsp-set rec "status" "abbruch") "fehler_text"
(vl-catch-all-error-message ent))
(dematic_arvato:rec-block rec ent
"kein VF-Block (deltaL beim gewuenschten Winkel nicht loesbar?)")))
;; Linienzug-Kette: aufgezeichnetes Journal ("ART:wert"-Tokens, wie in der
;; SSG_VF_EDIT-XDATA) 1:1 abspielen. Headless-Rahmen wie vsp-bau-datei; er
;; MUSS auch nach einem Fehler zurueck, sonst bleiben getstring/getpoint der
;; Sitzung ersetzt.
(defun dematic_arvato:bau-journal (obj / tid journal res)
(setq tid (cond ((ssg-val obj "test_id")) ("(ohne id)")))
(setq journal (mapcar 'vfl-string->entry (ssg-val obj "journal")))
(vsp-headless-an)
(setq res (vl-catch-all-apply 'vsp-bau-journal
(list tid journal nil
(if journal nil (list "Feld \"journal\" fehlt oder ist leer")))))
(vsp-headless-aus)
(if (vl-catch-all-error-p res)
(vsp-set (vsp-set (vsp-rec-neu tid) "status" "abbruch") "fehler_text"
(vl-catch-all-error-message res))
res))
(defun dematic_arvato:bau-eintrag (obj / kind res typ)
(setq typ (ssg-val obj "typ"))
(cond
((ssg-val obj "modus")
(setq kind "gefaellestrecke")
(setq res (vl-catch-all-apply 'dematic_arvato:bau-gf (list obj))))
((= typ "linienzug_journal")
(setq kind "linienzug")
(setq res (dematic_arvato:bau-journal obj)))
((and (member typ '("standard" "etage")) (ssg-val obj "test_id"))
(setq kind "vario")
(setq res (dematic_arvato:bau-vf obj)))
((and (= (ssg-val obj "function") "insert") (ssg-val obj "block"))
(setq kind "sensor")
(setq res (vl-catch-all-apply 'hundm05:bau-sensor (list obj))))
((= (ssg-val obj "function") "insert")
(setq kind "kreisel")
(setq res (vl-catch-all-apply 'hundm05:bau-kreisel (list obj))))
((= (ssg-val obj "typ") "gerade")
((= typ "gerade")
(setq kind "gerade")
(setq res (dematic_arvato:gerade-melden obj)))
(T
@@ -76,6 +248,7 @@
(princ (strcat "\n [" (strcase kind) "] Ausnahme: "
(vl-catch-all-error-message res)))
(vsp-set (vsp-set (vsp-set (vsp-rec-neu (cond ((ssg-val obj "id"))
((ssg-val obj "test_id"))
((ssg-val obj "block"))
("?")))
"kind" kind) "status" "abbruch")
@@ -85,11 +258,11 @@
(defun c:TEST_DEMATIC_ARVATO ( / datei daten out zaehler st)
(setq datei (dematic_arvato:datei))
(princ "\n\n================================================================")
(princ "\n TEST_DEMATIC_ARVATO - Abschnitt Flowpuffer/Kreisel (Vorlage)")
(princ "\n TEST_DEMATIC_ARVATO - Abschnitt Flowpuffer/Kreisel")
(princ (strcat "\n " datei))
(princ "\n================================================================")
(cond
((null (car (atoms-family 1 '("VSP-REC-NEU"))))
((null (car (atoms-family 1 '("VSP-BAU-JOURNAL"))))
(princ "\n[TEST_DEMATIC_ARVATO] FEHLER: vf_spec.lsp nicht geladen"
" (VarioFoerderer laden).")
(setq *dematic-arvato-records* nil))
@@ -104,6 +277,10 @@
(setvar "OSMODE" 0)
(setvar "ATTREQ" 0)
(setvar "ATTDIA" 0)
;; Vario-Bibliothek (Bogen-/AS-/ES-Masse) wie C:TEST_MUBEA, und die
;; GF-Elementmasse in jeder Zeichnung neu ermitteln.
(if (vsp-lib-noetig-p) (init-bibliothek))
(setq *mubea-gf-init-done* nil)
(setq out '())
(foreach obj daten
(setq out (cons (dematic_arvato:bau-eintrag obj) out)))