Merge branch 'master' of https://gitea.schoenenberger.de/Schoenenberger_Systeme_GmbH/dxfmakros
This commit is contained in:
@@ -135,6 +135,7 @@ venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
_setenv.bat
|
||||
|
||||
# cui folder for briscad
|
||||
aiassist_next_command/
|
||||
|
||||
@@ -92,7 +92,7 @@ Detaillierte Dokumentation aller Funktionen und Befehle: siehe `Lisp/README.md`.
|
||||
| `KreiselInsert.lsp` | KreiselInsert, KreiselConnect, KreiselRedraw, KreiselQuick, KreiselEdit, KreiselParams, KreiselLabelSetup, KreiselLabelPos, KreiselLabelHoehe, ILS_Eckrad | ILS Kreisel und Eckrad komplett in AutoLISP |
|
||||
| `VarioFoerderer.lsp` | FOERDERANLAGE | Vario-Foerderanlagen Generator |
|
||||
| `Gefaellestrecke.lsp` | GEFAELLESTRECKE | Gefaelle-Foerderanlage (AUS -> Staustrecke skaliert -> Separator -> EIN) |
|
||||
| `export.lsp` | EXPORTSIVAS, EXPORTCSV, OMNI_EXPORT, OMNI_UPDATE_ATTRIBS | JSON-Sammlung und Export (Python-Aufruf oder LISP-nativ) |
|
||||
| `export.lsp` | EXPORTSIVAS, EXPORTCSV, OMNI_UPDATE_ATTRIBS | JSON-Sammlung und Python-Export (Omniflo-Merkmale/Sum-Zeile via lib/export_csv.py) |
|
||||
| `OmniModulInsert.lsp` | OMNI_LOAD, OMNI_APB_*, OMNI_W*_*, OMNI_AP60/AP110, OMNI_TEF_*, OMNI_TV_*, OMNI_APBW_*, OMNI_EDIT | Omniflo-Komponenten: Boegen, Weichen, Verbinder, Transferwagen, Edit-Dialog |
|
||||
| `SSG_LIB_Commands.lsp` | ILS_BTMT_*, ILS_ETF_*, ILS_K*_*, ILS_GK*_*, SSG_BLOCKEDIT | Dummy-Befehle fuer Menue + Doppelklick/Kontextmenue-Dispatcher |
|
||||
| `menu-inventory.lsp` | MenuInventory | BricsCAD Diagnostik (Menuegruppen, Plugins, Startdateien) |
|
||||
|
||||
@@ -1368,6 +1368,7 @@
|
||||
("GF_Bogen_R_90" "0")
|
||||
("GF_Bogen_R_60" "0")
|
||||
("GF_Bogen_R_30" "0")
|
||||
("ID" "")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
+7
-123
@@ -15,129 +15,11 @@
|
||||
;; ============================================================
|
||||
|
||||
|
||||
;; --- Datei zeilenweise einlesen ---
|
||||
(defun omni:read-file-lines (datei / f zeile ergebnis)
|
||||
(setq f (open datei "r"))
|
||||
(if (null f)
|
||||
(progn
|
||||
(princ (strcat "\n[OMNI] FEHLER: Datei nicht gefunden: " datei))
|
||||
nil
|
||||
)
|
||||
(progn
|
||||
(setq ergebnis nil)
|
||||
(while (setq zeile (read-line f))
|
||||
(setq ergebnis (cons zeile ergebnis))
|
||||
)
|
||||
(close f)
|
||||
(reverse ergebnis)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
;; --- Whitespace und Komma am Ende einer Zeile entfernen ---
|
||||
(defun omni:trim (s / len ch)
|
||||
(setq s (vl-string-trim " \t\r\n" s))
|
||||
(setq len (strlen s))
|
||||
(if (and (> len 0) (= (substr s len 1) ","))
|
||||
(setq s (substr s 1 (1- len)))
|
||||
)
|
||||
(vl-string-trim " \t\r\n" s)
|
||||
)
|
||||
|
||||
|
||||
;; --- JSON-Wert parsen (String, Zahl, null) ---
|
||||
(defun omni:parse-value (val-str / len)
|
||||
(setq val-str (omni:trim val-str))
|
||||
(cond
|
||||
((= val-str "null") nil)
|
||||
((= (substr val-str 1 1) "\"")
|
||||
;; String: Anfuehrungszeichen entfernen
|
||||
(setq len (strlen val-str))
|
||||
(if (and (> len 1) (= (substr val-str len 1) "\""))
|
||||
(substr val-str 2 (- len 2))
|
||||
val-str
|
||||
)
|
||||
)
|
||||
(T
|
||||
;; Zahl
|
||||
(if (vl-string-search "." val-str)
|
||||
(atof val-str)
|
||||
(atoi val-str)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
;; --- Eine Key-Value-Zeile parsen -> dotted pair oder nil ---
|
||||
(defun omni:parse-kv-line (zeile / pos key rest val)
|
||||
(setq zeile (omni:trim zeile))
|
||||
;; Muss mit " beginnen (Key)
|
||||
(if (and (> (strlen zeile) 0) (= (substr zeile 1 1) "\""))
|
||||
(progn
|
||||
;; Key extrahieren: zweites Anfuehrungszeichen finden
|
||||
;; vl-string-search ist 0-basiert, substr ist 1-basiert
|
||||
(setq pos (vl-string-search "\"" zeile 1))
|
||||
(if pos
|
||||
(progn
|
||||
(setq key (substr zeile 2 (1- pos)))
|
||||
;; Rest nach dem schliessenden " extrahieren
|
||||
(setq rest (substr zeile (+ pos 2)))
|
||||
;; ":" im Rest finden
|
||||
(setq pos (vl-string-search ":" rest))
|
||||
(if pos
|
||||
(progn
|
||||
(setq val (substr rest (+ pos 2)))
|
||||
(cons key (omni:parse-value val))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
;; --- JSON-Array von flachen Objekten parsen ---
|
||||
(defun omni:parse-json-array (zeilen / in-obj obj ergebnis zeile kv trimmed)
|
||||
(setq in-obj nil
|
||||
obj nil
|
||||
ergebnis nil
|
||||
)
|
||||
(foreach zeile zeilen
|
||||
(setq trimmed (omni:trim zeile))
|
||||
(cond
|
||||
;; Objekt-Start
|
||||
((= trimmed "{")
|
||||
(setq in-obj T
|
||||
obj nil
|
||||
)
|
||||
)
|
||||
;; Objekt-Ende
|
||||
((or (= trimmed "}") (= trimmed "},"))
|
||||
(if (and in-obj obj)
|
||||
(setq ergebnis (cons (reverse obj) ergebnis))
|
||||
)
|
||||
(setq in-obj nil)
|
||||
)
|
||||
;; Key-Value-Zeile innerhalb eines Objekts
|
||||
(in-obj
|
||||
(setq kv (omni:parse-kv-line zeile))
|
||||
(if kv (setq obj (cons kv obj)))
|
||||
)
|
||||
)
|
||||
)
|
||||
(reverse ergebnis)
|
||||
)
|
||||
|
||||
|
||||
;; --- JSON-Datei laden und als Liste von Alists zurueckgeben ---
|
||||
(defun omni:load-json (datei / zeilen)
|
||||
(setq zeilen (omni:read-file-lines datei))
|
||||
(if zeilen
|
||||
(omni:parse-json-array zeilen)
|
||||
)
|
||||
;; Duenner Wrapper um den zentralen JSON-Parser aus ssg_core.lsp
|
||||
;; (ssg-load-json), damit fuer alle Module derselbe Parser-Standard gilt.
|
||||
(defun omni:load-json (datei)
|
||||
(ssg-load-json datei)
|
||||
)
|
||||
|
||||
|
||||
@@ -245,8 +127,10 @@
|
||||
;; eintrag = Assoziationsliste (Rueckgabe von omni:get-bogen etc.)
|
||||
;; key = Schluesselname z.B. "Radius"
|
||||
;; Beispiel: (omni:val (omni:get-bogen 821104025) "Radius") -> 550
|
||||
;; Duenner Wrapper um ssg-val (ssg_core.lsp), damit fuer alle Module
|
||||
;; derselbe Alist-Zugriffsstandard gilt.
|
||||
(defun omni:val (eintrag key)
|
||||
(cdr (assoc key eintrag))
|
||||
(ssg-val eintrag key)
|
||||
)
|
||||
|
||||
|
||||
|
||||
+5
-6
@@ -238,16 +238,15 @@ Sammelt INSERT-Blöcke aus der Zeichnung und exportiert sie als JSON oder ruft P
|
||||
| Befehl / Funktion | Beschreibung |
|
||||
| --- | --- |
|
||||
| `c:EXPORTSIVAS` | Alle Blöcke als JSON sammeln und `export_sivas.py` aufrufen (Sivas-CSV) |
|
||||
| `c:EXPORTCSV` | Alle Blöcke als JSON sammeln und `export_csv.py` aufrufen (einfache Item-Liste) |
|
||||
| `c:OMNI_EXPORT` | LISP-basierter Omniflo-Export: Blöcke direkt in CSV schreiben (ohne Python) |
|
||||
| `c:EXPORTCSV` | Alle Blöcke als JSON sammeln und `export_csv.py` aufrufen (einfache Item-Liste, inkl. Omniflo Bogen/Weiche/Gerade und abschließender "Omniflo Sum"-Zeile) |
|
||||
| `csv:collect-export-blocks` | Alle INSERT-Blöcke der Zeichnung als JSON-Array sammeln |
|
||||
| `csv:run-export py-skript json-datei output-csv` | Python-Skript mit gesammelten Daten aufrufen |
|
||||
| `csv:read-attribs ename` | Attribute eines INSERT als JSON-Objekt lesen |
|
||||
| `csv:omni-bogen-merkmale ename rot` | Omniflo-Bogen-Merkmale als JSON-String |
|
||||
| `csv:omni-weiche-merkmale ename rot` | Omniflo-Weichen-Merkmale als JSON-String |
|
||||
| `csv:omni-gerade-merkmale ename rot` | Omniflo-Gerade-Merkmale als JSON-String (Länge via `infrastruktur/mm_pro_meter`) |
|
||||
| `c:OMNI_UPDATE_ATTRIBS` | HOEHE/DREHUNG aller Omniflo-Elemente aus der Zeichnung aktualisieren |
|
||||
|
||||
Default-Werte (HOEHE, DREHUNG, Längen) kommen via `ssg-cfg-or` aus Config-Sektion `omniflo`.
|
||||
Der Omniflo-CSV-Export (Bogen/Weiche/Gerade, inkl. Merkmale-Aufbau und Sum-Zeile) läuft
|
||||
seit Entfernung von `c:OMNI_EXPORT` ausschließlich über `c:EXPORTCSV` → `lib/export_csv.py`
|
||||
(`build_bogen_merkmale`, `build_weiche_merkmale`, `build_gerade_merkmale`, `build_omni_sum_merkmale`).
|
||||
|
||||
---
|
||||
|
||||
|
||||
+74
-263
@@ -4,10 +4,61 @@
|
||||
;; Enthaelt:
|
||||
;; - JSON-Sammler (csv:collect-export-blocks, csv:run-export)
|
||||
;; - Python-basierter Export: EXPORTSIVAS, EXPORTCSV
|
||||
;; - LISP-basierter Omniflo-Export: OMNI_EXPORT
|
||||
;;
|
||||
;; Alle Blockname-Muster dieses Moduls sind zentral in cfg/export.cfg
|
||||
;; (INI-Style, Abschnitt [blockpattern]) deklariert und werden beim
|
||||
;; Laden des Moduls einmalig eingelesen (export:load-cfg).
|
||||
;; Datei-/Skriptnamen sind fest im Code hinterlegt (*export-filenames*).
|
||||
;; ============================================================
|
||||
|
||||
|
||||
;; --- Export-Konfiguration aus cfg/export.cfg laden ---
|
||||
;; Nutzt den zentralen INI-Parser aus ssg_core.lsp (ssg-load-ini / ssg-ini-get /
|
||||
;; ssg-ini-list), damit fuer alle Module derselbe .cfg-Standard gilt.
|
||||
(defun export:load-cfg ( / cfg-pfad)
|
||||
(setq cfg-pfad (strcat (getenv "DXFM_CFG") "/export.cfg"))
|
||||
(setq *export-cfg* (ssg-load-ini cfg-pfad))
|
||||
(if (null *export-cfg*)
|
||||
(princ (strcat "\n[export] WARNUNG: " cfg-pfad " nicht gefunden/leer, verwende Vorgaben."))
|
||||
)
|
||||
(princ)
|
||||
)
|
||||
|
||||
;; --- Einzelwert aus *export-cfg* lesen, mit Fallback ---
|
||||
;; section = "blockpattern"
|
||||
(defun export:cfg (section key default)
|
||||
(ssg-ini-get *export-cfg* section key default)
|
||||
)
|
||||
|
||||
;; --- Muster aus [blockpattern] als normalisierter wcmatch-String ---
|
||||
;; key = Config-Key, z.B. "pattern_kreisel"
|
||||
;; default-str = Fallback-Wert als kommagetrennter String, falls Config fehlt
|
||||
;; Rueckgabe: kommagetrennter String ohne Leerzeichen, z.B. "KR_*,KREISEL_*"
|
||||
(defun export:pattern (key default-str)
|
||||
(ssg-ini-list *export-cfg* "blockpattern" key default-str)
|
||||
)
|
||||
|
||||
;; Beim Laden des Moduls einmalig einlesen (Lazy-Loading via ssg-ensure "export")
|
||||
(export:load-cfg)
|
||||
|
||||
|
||||
;; --- Feste Datei-/Skriptnamen dieses Moduls (keine Config, direkt im Code) ---
|
||||
(setq *export-filenames*
|
||||
(list
|
||||
(cons "raw_json_datei" "export_raw.json")
|
||||
(cons "sivas_script" "export_sivas.py")
|
||||
(cons "sivas_csv" "export_sivas.csv")
|
||||
(cons "csv_script" "export_csv.py")
|
||||
(cons "csv_csv" "export.csv")
|
||||
)
|
||||
)
|
||||
|
||||
;; --- Dateinamen aus *export-filenames* lesen ---
|
||||
(defun export:filename (key)
|
||||
(cdr (assoc key *export-filenames*))
|
||||
)
|
||||
|
||||
|
||||
;; --- String fuer JSON escapen (Anfuehrungszeichen/Backslash) ---
|
||||
;; Konvertiert nicht-String-Werte (z.B. T, nil, Zahlen) defensiv in String.
|
||||
(defun csv:json-escape (s)
|
||||
@@ -60,10 +111,7 @@
|
||||
(setq rotation (cdr (assoc 50 ed)))
|
||||
(setq handle (cdr (assoc 5 ed)))
|
||||
(if (null rotation) (setq rotation 0.0))
|
||||
(if (cdr (assoc 66 ed))
|
||||
(setq attribs (csv:read-attribs ename))
|
||||
(setq attribs "{}")
|
||||
)
|
||||
(setq attribs (csv:read-attribs ename))
|
||||
(strcat
|
||||
" {\"block_name\":\"" (csv:json-escape blk-name) "\""
|
||||
",\"layer\":\"" (csv:json-escape layer) "\""
|
||||
@@ -84,6 +132,7 @@
|
||||
;; AP110* = Omniflo Geraden
|
||||
;; Omniflo Boegen/Weichen = rein numerische Blocknamen (SivasNr)
|
||||
;; Vario* = VarioFoerderer-Bloecke
|
||||
;; GF_* = Gefaellestrecke Compound-Bloecke
|
||||
;; Gibt Auswahlsatz zurueck oder nil.
|
||||
(defun csv:collect-export-blocks ( / ss-all ss-out i ename ed bname)
|
||||
(setq ss-all (ssget "X" (list (cons 0 "INSERT"))))
|
||||
@@ -95,10 +144,14 @@
|
||||
(setq ed (entget ename))
|
||||
(setq bname (cdr (assoc 2 ed)))
|
||||
(if (or
|
||||
(wcmatch bname "KR_*,KREISEL_*,ECKRAD_*")
|
||||
(wcmatch (strcase bname) "AP110*,AP_110*")
|
||||
(wcmatch bname "Vario*,Staustrecke*,AUS_Element*,EIN_Element*")
|
||||
(wcmatch bname (export:pattern "pattern_kreisel" "KR_*,KREISEL_*,ECKRAD_*"))
|
||||
(wcmatch (strcase bname) (export:pattern "pattern_gerade" "AP110*,AP_110*"))
|
||||
(wcmatch bname (export:pattern "pattern_strecke"
|
||||
"Vario*,Staustrecke*,AUS_Element*,EIN_Element*,VF_*,GF_*"))
|
||||
;; Rein numerische Namen = Omniflo SivasNr (Boegen/Weichen)
|
||||
;; Rein LISP-intern (Python klassifiziert Omniflo stattdessen
|
||||
;; ueber exakten Katalog-Match), daher fest im Code statt in
|
||||
;; cfg/export.cfg.
|
||||
(wcmatch bname "#*")
|
||||
)
|
||||
(ssadd ename ss-out)
|
||||
@@ -124,7 +177,7 @@
|
||||
(setq bname (cdr (assoc 2 ed)))
|
||||
(setq teileart (csv:get-attrib ename "TEILEART"))
|
||||
(if (or teileart
|
||||
(wcmatch (strcase bname) "AP110*,AP_110*"))
|
||||
(wcmatch (strcase bname) (export:pattern "pattern_gerade" "AP110*,AP_110*")))
|
||||
(progn
|
||||
(omni:set-hoehe-attrib ename)
|
||||
(omni:set-drehung-attrib ename)
|
||||
@@ -163,7 +216,8 @@
|
||||
(princ (strcat "\n[" label "] " (itoa count) " Bloecke gefunden."))
|
||||
|
||||
;; JSON-Datei zusammenbauen
|
||||
(setq out-pfad (strcat (getenv "DXFM_RESULTS") "/export_raw.json"))
|
||||
(setq out-pfad (strcat (getenv "DXFM_RESULTS") "/"
|
||||
(export:filename "raw_json_datei")))
|
||||
(setq fh (open out-pfad "w"))
|
||||
(if (null fh)
|
||||
(progn
|
||||
@@ -211,22 +265,21 @@
|
||||
|
||||
;; --- EXPORTSIVAS: Sivas-spezifischer CSV-Export mit Summierungszeilen ---
|
||||
(defun c:EXPORTSIVAS ()
|
||||
(csv:run-export "EXPORTSIVAS" "export_sivas.py" "export_sivas.csv")
|
||||
(csv:run-export "EXPORTSIVAS"
|
||||
(export:filename "sivas_script")
|
||||
(export:filename "sivas_csv"))
|
||||
)
|
||||
|
||||
;; --- EXPORTCSV: Einfache Item-Liste aller Bloecke (ohne Summierung) ---
|
||||
(defun c:EXPORTCSV ()
|
||||
(csv:run-export "EXPORTCSV" "export_csv.py" "export.csv")
|
||||
(csv:run-export "EXPORTCSV"
|
||||
(export:filename "csv_script")
|
||||
(export:filename "csv_csv"))
|
||||
)
|
||||
|
||||
|
||||
;; ============================================================
|
||||
;; LISP-basierter Omniflo-Export (ohne Python)
|
||||
;;
|
||||
;; Erkennt Omniflo Bloecke anhand Attribute:
|
||||
;; TEILEART = "Omniflo Kurve" -> Bogen
|
||||
;; TEILEART = "Omniflo Weiche" -> Weiche
|
||||
;; Blockname AP110* -> Gerade
|
||||
;; OMNIFLO HOEHE/DREHUNG AKTUALISIEREN
|
||||
;; ============================================================
|
||||
|
||||
;; --- Attribut-Wert eines INSERT-Blocks nach Tag lesen ---
|
||||
@@ -249,249 +302,6 @@
|
||||
atag
|
||||
)
|
||||
|
||||
|
||||
;; --- Omniflo-Block klassifizieren ---
|
||||
;; Gibt "bogen", "weiche", "gerade" oder nil zurueck
|
||||
(defun csv:omni-classify (ename bname / teileart)
|
||||
(setq teileart (csv:get-attrib ename "TEILEART"))
|
||||
(cond
|
||||
((and teileart (= teileart "Omniflo Kurve")) "bogen")
|
||||
((and teileart (= teileart "Omniflo Weiche")) "weiche")
|
||||
((wcmatch (strcase bname) "AP110*,AP_110*") "gerade")
|
||||
(t nil)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
;; --- Weichen-Subtyp fuer Omniflo Sum Zaehlung bestimmen ---
|
||||
(defun csv:omni-weichensubtyp (ename / wtyp beschr)
|
||||
(setq wtyp (csv:get-attrib ename "WEICHENTYP"))
|
||||
(setq beschr (csv:get-attrib ename "BESCHR"))
|
||||
(if (null wtyp) (setq wtyp ""))
|
||||
(if (null beschr) (setq beschr ""))
|
||||
(cond
|
||||
((vl-string-search "WEICHENK" (strcase beschr)) "weichenkoerper")
|
||||
((= wtyp "Einzelweiche") "einzelweiche")
|
||||
((= wtyp "Doppelweiche") "doppelweiche")
|
||||
((= wtyp "Dreiwegeweiche") "dreiwegeweiche")
|
||||
((= wtyp "Deltaweiche") "deltaweiche")
|
||||
((= wtyp "Dreifachweiche") "deltaweiche")
|
||||
((= wtyp "Sternweiche") "sternweiche")
|
||||
((vl-string-search "DELTA" (strcase beschr)) "deltaweiche")
|
||||
((vl-string-search "STAR" (strcase beschr)) "sternweiche")
|
||||
(t "einzelweiche")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
;; --- Merkmale-JSON fuer einen Bogen bauen ---
|
||||
(defun csv:omni-bogen-merkmale (ename rotation / winkel radius sivasnr hoehe drehung)
|
||||
(setq winkel (or (csv:get-attrib ename "WINKEL") "0"))
|
||||
(setq radius (or (csv:get-attrib ename "RADIUS") "0"))
|
||||
(setq sivasnr (or (csv:get-attrib ename "ARTINR") ""))
|
||||
(setq hoehe (or (csv:get-attrib ename "HOEHE") (ssg-cfg-or "omniflo" "default_hoehe" "2000")))
|
||||
(setq drehung (or (csv:get-attrib ename "DREHUNG") (rtos rotation 2 1)))
|
||||
(strcat "{\"Kurvenwinkel\":" winkel
|
||||
",\"Radius\":" radius
|
||||
",\"H\\u00f6he\":\"" (csv:json-escape hoehe) "\""
|
||||
",\"Drehung\":" drehung
|
||||
",\"SivasNummer\":\"" (csv:json-escape sivasnr) "\"}")
|
||||
)
|
||||
|
||||
|
||||
;; --- Merkmale-JSON fuer eine Weiche bauen ---
|
||||
(defun csv:omni-weiche-merkmale (ename rotation / wtyp winkel sivasnr tef-str hoehe drehung)
|
||||
(setq wtyp (or (csv:get-attrib ename "WEICHENTYP") "Einzelweiche"))
|
||||
(setq winkel (or (csv:get-attrib ename "WINKEL") "0"))
|
||||
(setq sivasnr (or (csv:get-attrib ename "ARTINR") ""))
|
||||
(setq tef-str (csv:get-attrib ename "SIVASNR_TEF"))
|
||||
(setq hoehe (or (csv:get-attrib ename "HOEHE") (ssg-cfg-or "omniflo" "default_hoehe" "2000")))
|
||||
(setq drehung (or (csv:get-attrib ename "DREHUNG") (rtos rotation 2 1)))
|
||||
(strcat "{\"Weichentyp\":\"" (csv:json-escape wtyp) "\""
|
||||
",\"Richtung\":\"False\""
|
||||
",\"Weichenwinkel\":" winkel
|
||||
",\"H\\u00f6he\":\"" (csv:json-escape hoehe) "\""
|
||||
",\"Drehung\":" drehung
|
||||
",\"Antrieb Kurve\":" (if (and tef-str (> (strlen tef-str) 0)) "true" "false")
|
||||
",\"SivasNummer\":\"" (csv:json-escape sivasnr) "\"}")
|
||||
)
|
||||
|
||||
|
||||
;; --- Merkmale-JSON fuer eine Gerade bauen ---
|
||||
(defun csv:omni-gerade-merkmale (ename rotation / laenge hoehe drehung)
|
||||
(setq laenge (csv:get-attrib ename "LAENGE"))
|
||||
(if (null laenge) (setq laenge (csv:get-attrib ename "A")))
|
||||
(if (null laenge) (setq laenge (ssg-cfg-or "omniflo" "default_gerade_laenge" "2000")))
|
||||
(setq hoehe (or (csv:get-attrib ename "HOEHE") (ssg-cfg-or "omniflo" "default_hoehe" "2000")))
|
||||
(setq drehung (or (csv:get-attrib ename "DREHUNG") (rtos rotation 2 1)))
|
||||
(strcat "{\"Anzahl der Separatoren\":\"0\""
|
||||
",\"L\\u00e4nge in Meter\":\"" (csv:json-escape (rtos (/ (atof laenge) (ssg-cfg-or "infrastruktur" "mm_pro_meter" 1000.0)) 2 2)) "\""
|
||||
",\"Winkel\":\"0\""
|
||||
",\"Anzahl der Scanner\":0"
|
||||
",\"H\\u00f6he oben\":\"" (csv:json-escape hoehe) "\""
|
||||
",\"H\\u00f6he unten\":\"" (csv:json-escape hoehe) "\""
|
||||
",\"Drehung\":" drehung
|
||||
",\"SivasNummer\":\"\"}")
|
||||
)
|
||||
|
||||
|
||||
;; --- Omniflo Sum Zeile bauen ---
|
||||
(defun csv:omni-sum-zeile (elem-nr cnt-boegen cnt-wk cnt-einzel cnt-delta cnt-doppel cnt-stern len-ap110 len-ap60)
|
||||
(strcat (itoa elem-nr)
|
||||
";\"Omniflo Sum\";\"autogenerated_of_json\";\"Omniflo sum\";1;"
|
||||
"{\"Anzahl B\\u00f6gen\":" (itoa cnt-boegen)
|
||||
",\"Anzahl Weichengrundk\\u00f6rper\":" (itoa cnt-wk)
|
||||
",\"Anzahl Einwegweichen\":" (itoa cnt-einzel)
|
||||
",\"Anzahl Deltaweichen\":" (itoa cnt-delta)
|
||||
",\"Anzahl Doppelweichen und Dreiwegeweichen\":" (itoa cnt-doppel)
|
||||
",\"Anzahl Sternweichen\":" (itoa cnt-stern)
|
||||
",\"Gesamtl\\u00e4nge AP110\":" (rtos len-ap110 2 1)
|
||||
",\"Gesamtl\\u00e4nge AP60\":" (rtos len-ap60 2 1)
|
||||
"}")
|
||||
)
|
||||
|
||||
|
||||
|
||||
;; ============================================================
|
||||
;; C:OMNI_EXPORT - Omniflo-Elemente zaehlen und als CSV exportieren
|
||||
;; Rein LISP-basiert, kein Python noetig.
|
||||
;; ============================================================
|
||||
(defun c:OMNI_EXPORT ( / ss-all i ename ed bname rotation typ
|
||||
elem-nr cnt-boegen cnt-weichen cnt-geraden
|
||||
cnt-wk cnt-einzel cnt-delta cnt-doppel cnt-stern
|
||||
len-ap110 len-ap60 laenge-str
|
||||
fh out-pfad lines subtyp merkmale)
|
||||
|
||||
;; Vor dem Export: IDs aller Bloecke sicherstellen und Duplikate korrigieren
|
||||
(princ "\n[OMNI_EXPORT] Pruefe und vergebe IDs...")
|
||||
(ssg-id-check-all)
|
||||
;; Vor dem Export HOEHE/DREHUNG aller Omniflo-Elemente aktualisieren
|
||||
(princ "\n[OMNI_EXPORT] Aktualisiere Hoehe/Drehung...")
|
||||
(omni:update-all-attribs)
|
||||
(princ "\n[OMNI_EXPORT] Sammle Omniflo-Elemente...")
|
||||
(setq ss-all (ssget "X" (list (cons 0 "INSERT"))))
|
||||
(if (null ss-all)
|
||||
(progn (princ "\n[OMNI_EXPORT] Keine INSERT-Bloecke in der Zeichnung.") (princ))
|
||||
(progn
|
||||
(setq elem-nr 0)
|
||||
(setq cnt-boegen 0 cnt-weichen 0 cnt-geraden 0)
|
||||
(setq cnt-wk 0 cnt-einzel 0 cnt-delta 0 cnt-doppel 0 cnt-stern 0)
|
||||
(setq len-ap110 0.0 len-ap60 0.0)
|
||||
(setq lines '())
|
||||
|
||||
(setq i 0)
|
||||
(while (setq ename (ssname ss-all i))
|
||||
(setq ed (entget ename))
|
||||
(setq bname (cdr (assoc 2 ed)))
|
||||
(setq rotation (cdr (assoc 50 ed)))
|
||||
(if (null rotation) (setq rotation 0.0))
|
||||
(setq rotation (* (/ rotation pi) 180.0))
|
||||
|
||||
(setq typ (csv:omni-classify ename bname))
|
||||
|
||||
(cond
|
||||
((= typ "bogen")
|
||||
(setq elem-nr (1+ elem-nr))
|
||||
(setq cnt-boegen (1+ cnt-boegen))
|
||||
(setq merkmale (csv:omni-bogen-merkmale ename rotation))
|
||||
(setq lines (cons
|
||||
(strcat (itoa elem-nr)
|
||||
";\"Omniflo Kurve\""
|
||||
";\"" (or (csv:get-attrib ename "ID") "0000") "\""
|
||||
";\"" (strcat "OFBogen :" (itoa cnt-boegen)) "\""
|
||||
";1"
|
||||
";" merkmale)
|
||||
lines))
|
||||
)
|
||||
|
||||
((= typ "weiche")
|
||||
(setq elem-nr (1+ elem-nr))
|
||||
(setq cnt-weichen (1+ cnt-weichen))
|
||||
(setq subtyp (csv:omni-weichensubtyp ename))
|
||||
(cond
|
||||
((= subtyp "weichenkoerper") (setq cnt-wk (1+ cnt-wk)))
|
||||
((= subtyp "einzelweiche") (setq cnt-einzel (1+ cnt-einzel)))
|
||||
((= subtyp "deltaweiche") (setq cnt-delta (1+ cnt-delta)))
|
||||
((= subtyp "sternweiche") (setq cnt-stern (1+ cnt-stern)))
|
||||
(t (setq cnt-doppel (1+ cnt-doppel)))
|
||||
)
|
||||
(setq merkmale (csv:omni-weiche-merkmale ename rotation))
|
||||
(setq lines (cons
|
||||
(strcat (itoa elem-nr)
|
||||
";\"Omniflo Weiche\""
|
||||
";\"" (or (csv:get-attrib ename "ID") "0000") "\""
|
||||
";\"" (strcat "OFWeiche :" (itoa cnt-weichen)) "\""
|
||||
";1"
|
||||
";" merkmale)
|
||||
lines))
|
||||
)
|
||||
|
||||
((= typ "gerade")
|
||||
(setq elem-nr (1+ elem-nr))
|
||||
(setq cnt-geraden (1+ cnt-geraden))
|
||||
(setq laenge-str (csv:get-attrib ename "LAENGE"))
|
||||
(if (null laenge-str) (setq laenge-str (csv:get-attrib ename "A")))
|
||||
(if (and laenge-str (> (strlen laenge-str) 0))
|
||||
(setq len-ap110 (+ len-ap110 (atof laenge-str)))
|
||||
(setq len-ap110 (+ len-ap110 (float (atoi (ssg-cfg-or "omniflo" "default_gerade_laenge" "2000")))))
|
||||
)
|
||||
(setq merkmale (csv:omni-gerade-merkmale ename rotation))
|
||||
(setq lines (cons
|
||||
(strcat (itoa elem-nr)
|
||||
";\"Omniflo Gerade\""
|
||||
";\"" (or (csv:get-attrib ename "ID") "0000") "\""
|
||||
";\"" (strcat "OFGerade :" (itoa cnt-geraden)) "\""
|
||||
";1"
|
||||
";" merkmale)
|
||||
lines))
|
||||
)
|
||||
)
|
||||
|
||||
(setq i (1+ i))
|
||||
)
|
||||
|
||||
(princ (strcat "\n[OMNI_EXPORT] Gefunden: "
|
||||
(itoa cnt-boegen) " Boegen, "
|
||||
(itoa cnt-weichen) " Weichen, "
|
||||
(itoa cnt-geraden) " Geraden"))
|
||||
|
||||
(if (= elem-nr 0)
|
||||
(princ "\n[OMNI_EXPORT] Keine Omniflo-Elemente gefunden.")
|
||||
(progn
|
||||
(setq out-pfad (strcat (getenv "DXFM_RESULTS") "/omniflo_export.csv"))
|
||||
(setq fh (open out-pfad "w"))
|
||||
(if (null fh)
|
||||
(princ (strcat "\n[OMNI_EXPORT] FEHLER: Kann Datei nicht oeffnen: " out-pfad))
|
||||
(progn
|
||||
(write-line "Elementnummer;TeileArt;TeileId;Bezeichnung;Anzahl;Merkmale" fh)
|
||||
(foreach ln (reverse lines)
|
||||
(write-line ln fh)
|
||||
)
|
||||
(write-line
|
||||
(csv:omni-sum-zeile (1+ elem-nr)
|
||||
cnt-boegen cnt-wk cnt-einzel cnt-delta cnt-doppel cnt-stern
|
||||
len-ap110 len-ap60)
|
||||
fh)
|
||||
(close fh)
|
||||
(princ (strcat "\n[OMNI_EXPORT] CSV geschrieben: " out-pfad))
|
||||
(princ (strcat "\n[OMNI_EXPORT] Omniflo Sum: "
|
||||
(itoa cnt-boegen) " Boegen, "
|
||||
(itoa (+ cnt-einzel cnt-doppel cnt-delta cnt-stern cnt-wk)) " Weichen, "
|
||||
(rtos len-ap110 2 0) " mm AP110"))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(princ)
|
||||
)
|
||||
|
||||
|
||||
;; ============================================================
|
||||
;; OMNIFLO HOEHE/DREHUNG AKTUALISIEREN
|
||||
;; ============================================================
|
||||
|
||||
;; --- Hoehe eines Omniflo-Blocks aus der Zeichnung ermitteln ---
|
||||
;; Liest z-Koordinate des Einfuegepunkts.
|
||||
;; Falls der Block KS_EIN, KS_AUS oder K1-K4 enthaelt,
|
||||
@@ -512,7 +322,8 @@
|
||||
(if (= (cdr (assoc 0 sub-ed)) "INSERT")
|
||||
(progn
|
||||
(setq sub-bname (strcase (cdr (assoc 2 sub-ed))))
|
||||
(if (wcmatch sub-bname "KS_EIN,KS_AUS,K1,K2,K3,K4")
|
||||
(if (wcmatch sub-bname
|
||||
(export:pattern "pattern_ks_subblocks" "KS_EIN,KS_AUS,K1,K2,K3,K4"))
|
||||
(progn
|
||||
(setq sub-pt (cdr (assoc 10 sub-ed)))
|
||||
(setq z-list (cons (if (caddr sub-pt) (caddr sub-pt) 0.0) z-list))
|
||||
@@ -576,7 +387,7 @@
|
||||
;; Nur Omniflo-Elemente (erkennbar an TEILEART-Attribut oder AP110-Blockname)
|
||||
(setq teileart (csv:get-attrib ename "TEILEART"))
|
||||
(if (or teileart
|
||||
(wcmatch (strcase bname) "AP110*,AP_110*"))
|
||||
(wcmatch (strcase bname) (export:pattern "pattern_gerade" "AP110*,AP_110*")))
|
||||
(progn
|
||||
(omni:set-hoehe-attrib ename)
|
||||
(omni:set-drehung-attrib ename)
|
||||
|
||||
@@ -750,5 +750,85 @@
|
||||
)
|
||||
|
||||
|
||||
;; ============================================================
|
||||
;; INI-STYLE CONFIG-PARSER (Standard fuer alle cfg/*.cfg-Dateien)
|
||||
;;
|
||||
;; Format:
|
||||
;; [Abschnitt]
|
||||
;; Key = Wert (einzelner Wert)
|
||||
;; Key = Wert1, Wert2, Wert3 (kommagetrennte Liste, z.B. fuer wcmatch)
|
||||
;; # Kommentar / Leerzeile werden ignoriert
|
||||
;;
|
||||
;; Verwendung:
|
||||
;; (setq cfg (ssg-load-ini pfad))
|
||||
;; (ssg-ini-get cfg "sektion" "key" "default")
|
||||
;; (ssg-ini-list cfg "sektion" "key" "a,b,c") ; normalisiert zu "a,b,c" (wcmatch-tauglich)
|
||||
;; ============================================================
|
||||
|
||||
;; Zeilen einer .cfg-Datei in verschachtelte Alist parsen.
|
||||
;; Rueckgabe: (("sektion" ("key" . "wert") ...) ...)
|
||||
(defun ssg-ini-parse-lines (zeilen / ergebnis section sec-data trimmed pos key val)
|
||||
(setq ergebnis nil section nil sec-data nil)
|
||||
(foreach zeile zeilen
|
||||
(setq trimmed (vl-string-trim " \t\r\n" zeile))
|
||||
(cond
|
||||
;; Leerzeile oder Kommentar
|
||||
((or (= trimmed "") (= (substr trimmed 1 1) "#")) nil)
|
||||
;; Abschnitt: [name]
|
||||
((and (= (substr trimmed 1 1) "[")
|
||||
(= (substr trimmed (strlen trimmed) 1) "]"))
|
||||
(if section
|
||||
(setq ergebnis (cons (cons section (reverse sec-data)) ergebnis)))
|
||||
(setq section (substr trimmed 2 (- (strlen trimmed) 2)))
|
||||
(setq sec-data nil)
|
||||
)
|
||||
;; Key=Wert
|
||||
((and section (setq pos (vl-string-search "=" trimmed)))
|
||||
(setq key (vl-string-trim " \t" (substr trimmed 1 pos)))
|
||||
(setq val (vl-string-trim " \t" (substr trimmed (+ pos 2))))
|
||||
(setq sec-data (cons (cons key val) sec-data))
|
||||
)
|
||||
)
|
||||
)
|
||||
(if section
|
||||
(setq ergebnis (cons (cons section (reverse sec-data)) ergebnis)))
|
||||
(reverse ergebnis)
|
||||
)
|
||||
|
||||
;; .cfg-Datei laden und als verschachtelte Alist zurueckgeben, oder nil.
|
||||
(defun ssg-load-ini (cfg-datei / zeilen)
|
||||
(setq zeilen (ssg-read-file-lines cfg-datei))
|
||||
(if zeilen (ssg-ini-parse-lines zeilen))
|
||||
)
|
||||
|
||||
;; Einzelwert aus geladener INI-Alist lesen, mit Fallback.
|
||||
;; ini-data = Rueckgabe von ssg-load-ini
|
||||
(defun ssg-ini-get (ini-data section key default / sec-data val)
|
||||
(setq sec-data (cdr (assoc section ini-data)))
|
||||
(setq val (if sec-data (cdr (assoc key sec-data))))
|
||||
(if (and val (> (strlen val) 0)) val default)
|
||||
)
|
||||
|
||||
;; Kommagetrennten Wert aus geladener INI-Alist lesen und normalisieren
|
||||
;; (Leerzeichen um Kommas entfernen, leere Teile verwerfen).
|
||||
;; default-str = Fallback als kommagetrennter String, falls Config/Key fehlt.
|
||||
;; Rueckgabe: kommagetrennter String, z.B. "KR_*,KREISEL_*" (wcmatch-tauglich)
|
||||
(defun ssg-ini-list (ini-data section key default-str / raw teile ergebnis item first)
|
||||
(setq raw (ssg-ini-get ini-data section key default-str))
|
||||
(setq teile (ssg-cfg-split-comma raw))
|
||||
(setq ergebnis "" first T)
|
||||
(foreach item teile
|
||||
(setq item (vl-string-trim " \t" item))
|
||||
(if (> (strlen item) 0)
|
||||
(progn
|
||||
(setq ergebnis (if first item (strcat ergebnis "," item)))
|
||||
(setq first nil)
|
||||
)
|
||||
)
|
||||
)
|
||||
ergebnis
|
||||
)
|
||||
|
||||
|
||||
(prompt "\nssg_core.lsp geladen.")
|
||||
(princ)
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@
|
||||
(if (or
|
||||
(wcmatch bname "KR_*,KREISEL_*,ECKRAD_*")
|
||||
(wcmatch (strcase bname) "AP110*,AP_110*,AP60*,AP_60*,APG110*")
|
||||
(wcmatch bname "Vario*,Staustrecke*,AUS_Element*,EIN_Element*")
|
||||
(wcmatch bname "Vario*,Staustrecke*,AUS_Element*,EIN_Element*,VF_*,GF_*")
|
||||
(wcmatch bname "#*")
|
||||
)
|
||||
(ssadd ename ss-out)
|
||||
|
||||
+2
-1
@@ -114,7 +114,8 @@
|
||||
("VF_Bogen_A_30" "0")
|
||||
("VF_Bogen_I_90" "0")
|
||||
("VF_Bogen_I_60" "0")
|
||||
("VF_Bogen_I_30" "0")))
|
||||
("VF_Bogen_I_30" "0")
|
||||
("ID" "")))
|
||||
|
||||
;; ============================================================
|
||||
;; TEIL 4: HILFSFUNKTIONEN
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# ============================================================
|
||||
# export.cfg - Konfiguration fuer Lisp/export.lsp UND lib/export_csv.py,
|
||||
# lib/export_sivas.py (gemeinsame Blockname-Muster, siehe
|
||||
# lib/export_blockpatterns.py)
|
||||
#
|
||||
# Format: INI-Style
|
||||
# [ABSCHNITT]
|
||||
# Key = Wert (einzelner Wert)
|
||||
# Key = Wert1, Wert2, Wert3 (Musterliste fuer wcmatch/fnmatch, kommagetrennt)
|
||||
#
|
||||
# Zeilen mit # sind Kommentare. Leerzeilen werden ignoriert.
|
||||
#
|
||||
# pattern_kreisel/pattern_strecke sind fuer Lisp's Block-SAMMLUNG grob
|
||||
# gehalten (nur "gehoert das ueberhaupt zum Export"). Die uebrigen
|
||||
# pattern_eckrad/pattern_variofoerderer/pattern_gefaellestrecke/
|
||||
# pattern_strecke_module-Keys sind Teilmengen davon fuer die feinere
|
||||
# TeileArt-KLASSIFIKATION in Python (eigene Zeile je Teileart) -
|
||||
# Aenderungen an Blocknamen bitte in beiden Stellen nachziehen.
|
||||
# ============================================================
|
||||
|
||||
[blockpattern]
|
||||
pattern_kreisel= KR_*, KREISEL_*, ECKRAD_*
|
||||
pattern_eckrad= ECKRAD_*
|
||||
pattern_gerade= AP110*, AP_110*
|
||||
pattern_strecke= Vario*, Staustrecke*, AUS_Element*, EIN_Element*, VF_*, GF_*
|
||||
pattern_variofoerderer= VF_*
|
||||
pattern_gefaellestrecke= GF_*
|
||||
pattern_strecke_module= Vario*, Staustrecke*, AUS_Element*, EIN_Element*
|
||||
pattern_ks_subblocks= KS_EIN, KS_AUS, K1, K2, K3, K4
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
-143002
File diff suppressed because it is too large
Load Diff
+28
-9
@@ -5,7 +5,12 @@
|
||||
Die SSG_LIB AutoLISP-Makros werden ueber einen zweistufigen Hybrid-Ansatz getestet:
|
||||
|
||||
1. **AutoLISP** (in BricsCAD): Liest JSON-Testfaelle, fuehrt die Funktionen aus,
|
||||
schreibt Ergebnisse als JSON und DXF/DWG.
|
||||
schreibt Ergebnisse als JSON und DXF/DWG. Jedes Testmodul laeuft dabei in einer
|
||||
eigenen, neuen Zeichnung (kein Wiederverwenden/Leeren der aktuellen).
|
||||
`SSG_RUN_ALL_TESTS` fragt beim Start interaktiv, ob die Zeichnung nach dem
|
||||
Speichern (Zoom Extents) als Tab offen bleibt (Kontroll-Modus, Default) oder
|
||||
ob sie zusaetzlich per `c:EXPORTCSV`/`c:EXPORTSIVAS` exportiert und danach
|
||||
sofort geschlossen wird (Automatik-Modus).
|
||||
2. **Python** (pytest + ezdxf): Validiert die Ergebnisse automatisch gegen
|
||||
erwartete Werte und optionale Referenzdaten.
|
||||
|
||||
@@ -26,9 +31,10 @@ tests/
|
||||
├── test_run_all.lsp # C:SSG_RUN_ALL_TESTS - fuehrt alle Tests aus
|
||||
├── test_kreisel.lsp # C:TEST_KREISEL - Kreisel-Testrunner
|
||||
├── test_foerderer.lsp # C:TEST_FOERDERER - Variofoerderer-Testrunner
|
||||
├── test_kseinaus.lsp # C:TEST_KSEINAUS - KreiselEinAus-Testrunner
|
||||
├── test_omniflo.lsp # C:TEST_OMNIFLO - Omniflo-Testrunner
|
||||
├── conftest.py # pytest Fixtures
|
||||
├── test_gefaellestrecke.lsp # C:TEST_GEFAELLESTRECKE - Gefaellestrecke-Testrunner
|
||||
├── test_export_all.lsp # C:TEST_EXPORT_ALL - manuell: oeffnet alle DXF/DWG aus output/ und exportiert sie (nicht in alltests.json)
|
||||
├── conftest.py # pytest Fixtures
|
||||
├── test_kreisel.py # pytest Kreisel-Validierung
|
||||
├── test_omniflo.py # pytest Omniflo-Validierung
|
||||
├── create_testbase.py # Hilfsskript (optional, i.d.R. nicht benoetigt)
|
||||
@@ -44,10 +50,14 @@ tests/
|
||||
├── kreisel_tests.dxf
|
||||
├── foerderer_results.json
|
||||
├── foerderer_tests.dwg
|
||||
├── kseinaus_results.json
|
||||
├── omniflo_results.json
|
||||
├── omniflo_tests.dxf
|
||||
└── omniflo_export.csv
|
||||
├── gefaellestrecke_results.json
|
||||
├── gefaellestrecke_tests.dwg
|
||||
├── export_all_results.json # nur nach manuellem TEST_EXPORT_ALL-Lauf
|
||||
├── kreisel_tests_export.csv # EXPORTCSV/EXPORTSIVAS je Testmodul - manuell via
|
||||
├── kreisel_tests_sivas.csv # TEST_EXPORT_ALL, oder automatisch im Automatik-Modus
|
||||
└── ... # von SSG_RUN_ALL_TESTS
|
||||
```
|
||||
|
||||
## Testregistry (alltests.json)
|
||||
@@ -57,13 +67,22 @@ Die Datei `tests/alltests.json` ist die zentrale Registry aller Testmodule.
|
||||
|
||||
```json
|
||||
[
|
||||
{ "name": "kreisel", "save": "dxf", "module": "KreiselInsert" },
|
||||
{ "name": "foerderer", "save": "dwg", "module": "VarioFoerderer" },
|
||||
{ "name": "kseinaus", "save": null, "module": "VarioFoerderer" },
|
||||
{ "name": "omniflo", "save": "dxf", "module": "OmniModulInsert" }
|
||||
{ "name": "kreisel", "save": "dxf", "module": "KreiselInsert" },
|
||||
{ "name": "foerderer", "save": "dwg", "module": "VarioFoerderer" },
|
||||
{ "name": "omniflo", "save": "dxf", "module": "OmniModulInsert" },
|
||||
{ "name": "gefaellestrecke", "save": "dwg", "module": "Gefaellestrecke" }
|
||||
]
|
||||
```
|
||||
|
||||
`TEST_EXPORT_ALL` (`tests/test_export_all.lsp`) ist **nicht** in `alltests.json`
|
||||
registriert und laeuft daher nicht automatisch mit `SSG_RUN_ALL_TESTS`. Es wird
|
||||
manuell aufgerufen (Menuepunkt "Export CSV/Sivas ALL"), oeffnet dann jede zu diesem
|
||||
Zeitpunkt in `tests/output/` vorhandene DXF/DWG-Datei und exportiert sie per
|
||||
`c:EXPORTCSV`/`c:EXPORTSIVAS` nach `<basis>_export.csv`/`<basis>_sivas.csv`.
|
||||
Alternativ: `SSG_RUN_ALL_TESTS` im Automatik-Modus (interaktive Abfrage beim
|
||||
Start) erledigt Speichern, Export und Schliessen direkt je Testmodul, ohne dass
|
||||
`TEST_EXPORT_ALL` danach noch manuell aufgerufen werden muss.
|
||||
|
||||
| Feld | Typ | Bedeutung |
|
||||
| --- | --- | --- |
|
||||
| `name` | String | Basisname des Testmoduls (Pflicht) |
|
||||
|
||||
Binary file not shown.
+1
-1
@@ -372,7 +372,7 @@ Quellwerte koennen sein:
|
||||
|
||||
### CSV-Export-Felder (Merkmale)
|
||||
|
||||
Die Attribute werden beim CSV-Export (`export_csv.py` / `OMNI_EXPORT`) wie folgt in CSV-Merkmale ueberfuehrt.
|
||||
Die Attribute werden beim CSV-Export (`export_csv.py`, aufgerufen ueber `c:EXPORTCSV`) wie folgt in CSV-Merkmale ueberfuehrt.
|
||||
CSV-Format: `Elementnummer;TeileArt;TeileId;Bezeichnung;Anzahl;Merkmale`
|
||||
|
||||
**Omniflo Kurve:**
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
export_blockpatterns.py - Liest Blockname-Muster aus cfg/export.cfg.
|
||||
|
||||
Einzige Quelle fuer die Blockname-Klassifikation, die sowohl von
|
||||
Lisp/export.lsp (csv:collect-export-blocks, ueber export:pattern) als auch
|
||||
von den Python-Export-Skripten (export_csv.py, export_sivas.py) genutzt
|
||||
wird. Vermeidet, dass Blockname-Praefixe/-Wildcards in mehreren Dateien
|
||||
unabhaengig voneinander gepflegt werden und dabei auseinanderlaufen.
|
||||
|
||||
cfg/export.cfg deklariert fuer Lisp kombinierte Kategorien (z.B.
|
||||
pattern_kreisel deckt Kreisel UND Eckrad ab, weil Lisp beim Sammeln der
|
||||
Bloecke nur "gehoert das ueberhaupt zum Export" wissen muss). Python
|
||||
braucht dagegen feinere Kategorien (Kreisel und Eckrad sind unterschied-
|
||||
liche TeileArt-Zeilen) - dafuer gibt es zusaetzliche, feinere Keys
|
||||
(pattern_eckrad, pattern_strecke_compound, pattern_strecke_module), die
|
||||
inhaltlich Teilmengen der groben Lisp-Kategorien sind.
|
||||
"""
|
||||
|
||||
import configparser
|
||||
import fnmatch
|
||||
import os
|
||||
|
||||
|
||||
def cfg_path_from_env(default_dir=None):
|
||||
"""Pfad zu export.cfg ermitteln: DXFM_CFG env var, sonst <repo>/cfg."""
|
||||
dxfm_cfg = os.environ.get("DXFM_CFG")
|
||||
if dxfm_cfg:
|
||||
return os.path.join(dxfm_cfg, "export.cfg")
|
||||
base = default_dir or os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
return os.path.join(base, "cfg", "export.cfg")
|
||||
|
||||
|
||||
def load_patterns(cfg_path=None):
|
||||
"""Liest [blockpattern] aus export.cfg.
|
||||
|
||||
Rueckgabe: dict key -> list[str] (Wildcard-Muster, wcmatch-kompatibel:
|
||||
"*" = beliebig viele Zeichen). Bei fehlender Datei/Sektion: leeres dict.
|
||||
"""
|
||||
if cfg_path is None:
|
||||
cfg_path = cfg_path_from_env()
|
||||
parser = configparser.ConfigParser()
|
||||
parser.read(cfg_path, encoding="utf-8")
|
||||
patterns = {}
|
||||
if parser.has_section("blockpattern"):
|
||||
for key, value in parser.items("blockpattern"):
|
||||
patterns[key] = [p.strip() for p in value.split(",") if p.strip()]
|
||||
return patterns
|
||||
|
||||
|
||||
def matches_any(bname, patterns):
|
||||
"""True, wenn bname (case-insensitiv) zu mindestens einem Muster passt."""
|
||||
upper = bname.upper()
|
||||
return any(fnmatch.fnmatchcase(upper, p.upper()) for p in patterns)
|
||||
+192
-11
@@ -1,7 +1,11 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
export_csv.py - Erzeugt eine einfache Item-Liste als CSV (ohne Summierung).
|
||||
export_csv.py - Erzeugt eine einfache Item-Liste als CSV.
|
||||
|
||||
Fuer Omniflo-Elemente (Bogen/Weiche/Gerade) wird am Ende zusaetzlich eine
|
||||
"Omniflo Sum"-Zeile mit Anzahl Boegen/Weichentypen und Gesamtlaenge AP110/AP60
|
||||
angehaengt (siehe build_omni_sum_merkmale).
|
||||
|
||||
Aufruf:
|
||||
python export_csv.py <export_raw.json> <data_dir> <output.csv>
|
||||
@@ -14,14 +18,28 @@ import json
|
||||
import sys
|
||||
import os
|
||||
|
||||
from export_blockpatterns import load_patterns, matches_any
|
||||
|
||||
BLOCKPATTERNS = load_patterns()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Gemeinsame Hilfsfunktionen (auch in export_sivas.py verwendet)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def load_json(path):
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
"""Laedt JSON, tolerant gegenueber BricsCAD-Ausgaben.
|
||||
|
||||
AutoLISP schreibt export_raw.json (write-line) im ANSI-Codepage des
|
||||
Systems, nicht in UTF-8 - Sonderzeichen (z.B. Gradzeichen) koennen
|
||||
daher nicht als UTF-8 dekodierbar sein. Fallback auf cp1252.
|
||||
"""
|
||||
try:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
except UnicodeDecodeError:
|
||||
with open(path, "r", encoding="cp1252") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def build_lookup(boegen, weichen):
|
||||
@@ -79,19 +97,93 @@ def build_weiche_merkmale(block, eintrag):
|
||||
}
|
||||
|
||||
|
||||
def build_variofoerderer_merkmale(block):
|
||||
attribs = block.get("attribs", {})
|
||||
return {
|
||||
"Typ": attribs.get("TYP", ""),
|
||||
"Seite": attribs.get("SEITE", ""),
|
||||
"Winkel": attribs.get("WINKEL", ""),
|
||||
"DeltaH_mm": attribs.get("DELTA_H", ""),
|
||||
"DeltaL_mm": attribs.get("DELTA_L", ""),
|
||||
"L_VF_m": attribs.get("L_VF_m", ""),
|
||||
"L_GF_m": attribs.get("L_GF_m", ""),
|
||||
"Montagehoehe_m": attribs.get("MONTAGEHOEHE_m", ""),
|
||||
"Hoehe_Von_mm": attribs.get("HOEHE_VON", ""),
|
||||
"Hoehe_Bis_mm": attribs.get("HOEHE_BIS", ""),
|
||||
"Antriebfahrtrichtung": attribs.get("ANTRIEBFAHRTRICHTUNG", ""),
|
||||
"Anzahl_Separator": attribs.get("ANZAHL_SEPARATOR", ""),
|
||||
"Anzahl_Scanner": attribs.get("ANZAHL_SCANNER", ""),
|
||||
}
|
||||
|
||||
|
||||
def get_laenge_mm(block):
|
||||
"""Liest LAENGE (oder A) aus Block-Attributen. Fallback: 2000mm."""
|
||||
attribs = block.get("attribs", {})
|
||||
laenge = attribs.get("LAENGE") or attribs.get("A")
|
||||
try:
|
||||
return float(laenge)
|
||||
except (TypeError, ValueError):
|
||||
return 2000.0
|
||||
|
||||
|
||||
def build_gerade_merkmale(block):
|
||||
laenge_mm = get_laenge_mm(block)
|
||||
hoehe = get_hoehe(block)
|
||||
return {
|
||||
"Anzahl der Separatoren": "0",
|
||||
"Länge in Meter": "2",
|
||||
"Länge in Meter": f"{laenge_mm / 1000.0:.2f}",
|
||||
"Winkel": "0",
|
||||
"Anzahl der Scanner": 0,
|
||||
"Höhe oben": "2000",
|
||||
"Höhe unten": "2000",
|
||||
"Drehung": block.get("rotation", 0.0),
|
||||
"Höhe oben": hoehe,
|
||||
"Höhe unten": hoehe,
|
||||
"Drehung": get_drehung(block),
|
||||
"SivasNummer": ""
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Omniflo Sum-Zeile (Anzahl Boegen/Weichentypen, Gesamtlaenge AP110/AP60)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
WEICHE_SUBTYP_MAP = {
|
||||
"Einzelweiche": "einzelweiche",
|
||||
"Doppelweiche": "doppelweiche",
|
||||
"Dreiwegeweiche": "doppelweiche",
|
||||
"Deltaweiche": "deltaweiche",
|
||||
"Dreifachweiche": "deltaweiche",
|
||||
"Sternweiche": "sternweiche",
|
||||
}
|
||||
|
||||
|
||||
def weichensubtyp(block, eintrag):
|
||||
"""Bestimmt den Weichen-Subtyp fuer die Omniflo-Sum-Zaehlung."""
|
||||
beschr = (block.get("attribs", {}).get("BESCHR") or "").upper()
|
||||
if "WEICHENK" in beschr:
|
||||
return "weichenkoerper"
|
||||
subtyp = WEICHE_SUBTYP_MAP.get(eintrag.get("WeichenTyp", ""))
|
||||
if subtyp:
|
||||
return subtyp
|
||||
if "DELTA" in beschr:
|
||||
return "deltaweiche"
|
||||
if "STAR" in beschr:
|
||||
return "sternweiche"
|
||||
return "einzelweiche"
|
||||
|
||||
|
||||
def build_omni_sum_merkmale(cnt_boegen, cnt_wk, cnt_einzel, cnt_delta, cnt_doppel, cnt_stern,
|
||||
len_ap110_mm, len_ap60_mm):
|
||||
return {
|
||||
"Anzahl Bögen": cnt_boegen,
|
||||
"Anzahl Weichengrundkörper": cnt_wk,
|
||||
"Anzahl Einwegweichen": cnt_einzel,
|
||||
"Anzahl Deltaweichen": cnt_delta,
|
||||
"Anzahl Doppelweichen und Dreiwegeweichen": cnt_doppel,
|
||||
"Anzahl Sternweichen": cnt_stern,
|
||||
"Gesamtlänge AP110": round(len_ap110_mm, 1),
|
||||
"Gesamtlänge AP60": round(len_ap60_mm, 1),
|
||||
}
|
||||
|
||||
|
||||
def build_kreisel_merkmale(block):
|
||||
attribs = block.get("attribs", {})
|
||||
abstand_mm = attribs.get("ABSTAND", "2300")
|
||||
@@ -121,7 +213,7 @@ def build_kreisel_merkmale(block):
|
||||
# Bekannte Blocknamen
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
SKIP_BLOCKS = {"K1", "K2", "K3", "K4", "KS_EIN", "KS_AUS"}
|
||||
SKIP_BLOCKS = set(BLOCKPATTERNS.get("pattern_ks_subblocks", ["K1", "K2", "K3", "K4", "KS_EIN", "KS_AUS"]))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -135,6 +227,16 @@ def process_blocks(blocks, lookup):
|
||||
weiche_count = {}
|
||||
gerade_count = 0
|
||||
kreisel_count = 0
|
||||
eckrad_count = 0
|
||||
vf_count = 0
|
||||
gf_count = 0
|
||||
strecke_modul_count = 0
|
||||
|
||||
# Zaehlung fuer die Omniflo-Sum-Zeile (nur Bogen/Weiche/Gerade)
|
||||
cnt_wk = cnt_einzel = cnt_delta = cnt_doppel = cnt_stern = 0
|
||||
len_ap110_mm = 0.0
|
||||
len_ap60_mm = 0.0
|
||||
has_omniflo = False
|
||||
|
||||
for block in blocks:
|
||||
bname = block.get("block_name", "")
|
||||
@@ -147,6 +249,7 @@ def process_blocks(blocks, lookup):
|
||||
typ, eintrag = lookup[bname]
|
||||
elem_nr += 1
|
||||
shape_id = block.get("attribs", {}).get("ID", "0000")
|
||||
has_omniflo = True
|
||||
|
||||
if typ == "bogen":
|
||||
bogen_count += 1
|
||||
@@ -168,12 +271,24 @@ def process_blocks(blocks, lookup):
|
||||
"bezeichnung": f"OFWeiche :{weiche_count[wt]}",
|
||||
"merkmale": build_weiche_merkmale(block, eintrag),
|
||||
})
|
||||
subtyp = weichensubtyp(block, eintrag)
|
||||
if subtyp == "weichenkoerper":
|
||||
cnt_wk += 1
|
||||
elif subtyp == "deltaweiche":
|
||||
cnt_delta += 1
|
||||
elif subtyp == "sternweiche":
|
||||
cnt_stern += 1
|
||||
elif subtyp == "doppelweiche":
|
||||
cnt_doppel += 1
|
||||
else:
|
||||
cnt_einzel += 1
|
||||
continue
|
||||
|
||||
# Omniflo Gerade (AP110)
|
||||
if "AP110" in bname.upper() or "AP_110" in bname.upper():
|
||||
if matches_any(bname, BLOCKPATTERNS.get("pattern_gerade", [])):
|
||||
elem_nr += 1
|
||||
gerade_count += 1
|
||||
has_omniflo = True
|
||||
items.append({
|
||||
"nr": elem_nr,
|
||||
"teileart": "Omniflo Gerade",
|
||||
@@ -181,10 +296,51 @@ def process_blocks(blocks, lookup):
|
||||
"bezeichnung": f"OFGerade :{gerade_count}",
|
||||
"merkmale": build_gerade_merkmale(block),
|
||||
})
|
||||
len_ap110_mm += get_laenge_mm(block)
|
||||
continue
|
||||
|
||||
# ILS Kreisel (Compound-Bloecke mit Praefix KR_)
|
||||
if bname.startswith("KR_"):
|
||||
# VarioFoerderer Compound-Block (VF_N)
|
||||
if matches_any(bname, BLOCKPATTERNS.get("pattern_variofoerderer", [])):
|
||||
elem_nr += 1
|
||||
vf_count += 1
|
||||
items.append({
|
||||
"nr": elem_nr,
|
||||
"teileart": "VarioFoerderer",
|
||||
"teileid": block.get("attribs", {}).get("ID", "0000"),
|
||||
"bezeichnung": f"VarioFoerderer :{vf_count}",
|
||||
"merkmale": build_variofoerderer_merkmale(block),
|
||||
})
|
||||
continue
|
||||
|
||||
# Gefaellestrecke Compound-Block (GF_N)
|
||||
if matches_any(bname, BLOCKPATTERNS.get("pattern_gefaellestrecke", [])):
|
||||
elem_nr += 1
|
||||
gf_count += 1
|
||||
items.append({
|
||||
"nr": elem_nr,
|
||||
"teileart": "Gefaellestrecke",
|
||||
"teileid": block.get("attribs", {}).get("ID", "0000"),
|
||||
"bezeichnung": f"Gefaellestrecke :{gf_count}",
|
||||
"merkmale": build_variofoerderer_merkmale(block),
|
||||
})
|
||||
continue
|
||||
|
||||
# ILS Eckrad (vor Kreisel pruefen: pattern_kreisel schliesst
|
||||
# ECKRAD_* mit ein, pattern_eckrad ist die praezisere Teilmenge)
|
||||
if matches_any(bname, BLOCKPATTERNS.get("pattern_eckrad", [])):
|
||||
elem_nr += 1
|
||||
eckrad_count += 1
|
||||
items.append({
|
||||
"nr": elem_nr,
|
||||
"teileart": "ILS 2.0 Eckrad",
|
||||
"teileid": block.get("attribs", {}).get("ID", "0000"),
|
||||
"bezeichnung": f"Eckrad :{eckrad_count}",
|
||||
"merkmale": build_kreisel_merkmale(block),
|
||||
})
|
||||
continue
|
||||
|
||||
# ILS Kreisel (Compound-Bloecke mit Praefix KR_ oder KREISEL_)
|
||||
if matches_any(bname, BLOCKPATTERNS.get("pattern_kreisel", [])):
|
||||
elem_nr += 1
|
||||
kreisel_count += 1
|
||||
items.append({
|
||||
@@ -196,6 +352,31 @@ def process_blocks(blocks, lookup):
|
||||
})
|
||||
continue
|
||||
|
||||
# ILS Strecke-Module (Vario/Staustrecke-Einzelkomponenten)
|
||||
if matches_any(bname, BLOCKPATTERNS.get("pattern_strecke_module", [])):
|
||||
elem_nr += 1
|
||||
strecke_modul_count += 1
|
||||
items.append({
|
||||
"nr": elem_nr,
|
||||
"teileart": "ILS 2.0 Strecke - Modul",
|
||||
"teileid": block.get("attribs", {}).get("ID", "0000"),
|
||||
"bezeichnung": f"Streckenmodul :{strecke_modul_count}",
|
||||
"merkmale": {"Blockname": bname},
|
||||
})
|
||||
continue
|
||||
|
||||
if has_omniflo:
|
||||
elem_nr += 1
|
||||
items.append({
|
||||
"nr": elem_nr,
|
||||
"teileart": "Omniflo Sum",
|
||||
"teileid": "autogenerated_of_json",
|
||||
"bezeichnung": "Omniflo sum",
|
||||
"merkmale": build_omni_sum_merkmale(
|
||||
bogen_count, cnt_wk, cnt_einzel, cnt_delta, cnt_doppel, cnt_stern,
|
||||
len_ap110_mm, len_ap60_mm),
|
||||
})
|
||||
|
||||
return items
|
||||
|
||||
|
||||
|
||||
+95
-31
@@ -24,10 +24,24 @@ import json
|
||||
import sys
|
||||
import os
|
||||
|
||||
from export_blockpatterns import load_patterns, matches_any
|
||||
|
||||
BLOCKPATTERNS = load_patterns()
|
||||
|
||||
|
||||
def load_json(path):
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
"""Laedt JSON, tolerant gegenueber BricsCAD-Ausgaben.
|
||||
|
||||
AutoLISP schreibt export_raw.json (write-line) im ANSI-Codepage des
|
||||
Systems, nicht in UTF-8 - Sonderzeichen (z.B. Gradzeichen) koennen
|
||||
daher nicht als UTF-8 dekodierbar sein. Fallback auf cp1252.
|
||||
"""
|
||||
try:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
except UnicodeDecodeError:
|
||||
with open(path, "r", encoding="cp1252") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def build_lookup(boegen, weichen):
|
||||
@@ -64,6 +78,11 @@ def get_hoehe_mm(block):
|
||||
return v
|
||||
|
||||
|
||||
def get_id(block):
|
||||
"""Liest das eindeutige ID-Attribut eines Blocks (von ssg-id-check-all vergeben)."""
|
||||
return block.get("attribs", {}).get("ID", "")
|
||||
|
||||
|
||||
def format_hoehe_m(hoehe_mm_str):
|
||||
"""Konvertiert mm-String nach m mit deutschem Dezimaltrennzeichen."""
|
||||
try:
|
||||
@@ -101,7 +120,7 @@ def build_kreisel_details(block):
|
||||
|
||||
|
||||
# --- Bekannte Bloecke ignorieren ---
|
||||
SKIP_BLOCKS = {"K1", "K2", "K3", "K4", "KS_EIN", "KS_AUS"}
|
||||
SKIP_BLOCKS = set(BLOCKPATTERNS.get("pattern_ks_subblocks", ["K1", "K2", "K3", "K4", "KS_EIN", "KS_AUS"]))
|
||||
|
||||
# Sivas-Nummern und Bezeichnungen je ILS-Elementtyp
|
||||
ILS_SIVASNR = {
|
||||
@@ -113,13 +132,6 @@ ILS_SIVASNR = {
|
||||
"automation": ("6269", "ILS 2.0 Automation und Pneumatik - Gesamtanlage"),
|
||||
}
|
||||
|
||||
# Strecken-Module: Blocknamen-Fragmente fuer Vario/Staustrecke-Elemente
|
||||
STRECKE_BLOCKS = {
|
||||
"AUS_Element_links", "EIN_Element_links",
|
||||
"Staustrecke_SP_1000_mm", "Staustrecke_Separator_SP_300_mm",
|
||||
"Vario_Spannstation_SP_500mm", "Vario_Motorstation_SP_500mm",
|
||||
}
|
||||
|
||||
|
||||
def process_blocks(blocks, lookup):
|
||||
"""
|
||||
@@ -129,12 +141,14 @@ def process_blocks(blocks, lookup):
|
||||
items - Liste der CSV-Zeilen als Dicts
|
||||
counters - Zaehler fuer ILS-Automation-Summierungszeile
|
||||
"""
|
||||
# Gruppen: sivasnr_str -> {anzahl, hoehe_mm}
|
||||
# Gruppen: sivasnr_str -> {anzahl, hoehe_mm, ids}
|
||||
bogen_groups = {} # sivasnr -> dict
|
||||
weiche_groups = {} # sivasnr -> dict
|
||||
# Gerade: artinr_str -> {anzahl, laenge_mm_total, hoehe_mm}
|
||||
# Gerade: artinr_str -> {anzahl, laenge_mm_total, hoehe_mm, ids}
|
||||
gerade_groups = {}
|
||||
kreisel_list = [] # Liste von blocks
|
||||
eckrad_ids = [] # IDs aller Eckrad-Bloecke
|
||||
strecke_ids = [] # IDs aller Strecke-Module (VF_*, GF_*, Vario/Staustrecke)
|
||||
|
||||
counters = {
|
||||
"anzahl_boegen": 0,
|
||||
@@ -181,14 +195,16 @@ def process_blocks(blocks, lookup):
|
||||
|
||||
if typ == "bogen":
|
||||
if sivasnr not in bogen_groups:
|
||||
bogen_groups[sivasnr] = {"anzahl": 0, "hoehe_mm": hoehe}
|
||||
bogen_groups[sivasnr] = {"anzahl": 0, "hoehe_mm": hoehe, "ids": []}
|
||||
bogen_groups[sivasnr]["anzahl"] += 1
|
||||
bogen_groups[sivasnr]["ids"].append(get_id(block))
|
||||
counters["anzahl_boegen"] += 1
|
||||
|
||||
elif typ == "weiche":
|
||||
if sivasnr not in weiche_groups:
|
||||
weiche_groups[sivasnr] = {"anzahl": 0, "hoehe_mm": hoehe}
|
||||
weiche_groups[sivasnr] = {"anzahl": 0, "hoehe_mm": hoehe, "ids": []}
|
||||
weiche_groups[sivasnr]["anzahl"] += 1
|
||||
weiche_groups[sivasnr]["ids"].append(get_id(block))
|
||||
subtyp = classify_weiche(eintrag)
|
||||
if subtyp == "einzelweiche":
|
||||
counters["anzahl_einzelweichen"] += 1
|
||||
@@ -203,7 +219,7 @@ def process_blocks(blocks, lookup):
|
||||
continue
|
||||
|
||||
# --- Omniflo Gerade (AP110) ---
|
||||
if "AP110" in bname.upper() or "AP_110" in bname.upper():
|
||||
if matches_any(bname, BLOCKPATTERNS.get("pattern_gerade", [])):
|
||||
attribs = block.get("attribs", {})
|
||||
artinr = attribs.get("ARTINR", "")
|
||||
key = artinr if artinr else "AP110"
|
||||
@@ -217,15 +233,24 @@ def process_blocks(blocks, lookup):
|
||||
pass
|
||||
if key not in gerade_groups:
|
||||
gerade_groups[key] = {
|
||||
"anzahl": 0, "laenge_mm": 0.0, "hoehe_mm": hoehe, "artinr": artinr
|
||||
"anzahl": 0, "laenge_mm": 0.0, "hoehe_mm": hoehe,
|
||||
"artinr": artinr, "ids": [],
|
||||
}
|
||||
gerade_groups[key]["anzahl"] += 1
|
||||
gerade_groups[key]["laenge_mm"] += laenge
|
||||
gerade_groups[key]["ids"].append(get_id(block))
|
||||
counters["gesamtlaenge_ap110"] += laenge
|
||||
continue
|
||||
|
||||
# --- ILS Eckrad (vor Kreisel pruefen: pattern_kreisel schliesst
|
||||
# ECKRAD_* mit ein, pattern_eckrad ist die praezisere Teilmenge) ---
|
||||
if matches_any(bname, BLOCKPATTERNS.get("pattern_eckrad", [])):
|
||||
counters["anzahl_eckraeder"] += 1
|
||||
eckrad_ids.append(get_id(block))
|
||||
continue
|
||||
|
||||
# --- ILS Kreisel ---
|
||||
if bname.startswith("KR_") or bname.startswith("KREISEL_"):
|
||||
if matches_any(bname, BLOCKPATTERNS.get("pattern_kreisel", [])):
|
||||
kreisel_list.append(block)
|
||||
attribs = block.get("attribs", {})
|
||||
counters["anzahl_kreisel"] += 1
|
||||
@@ -244,16 +269,53 @@ def process_blocks(blocks, lookup):
|
||||
counters["anzahl_streckengruppen"] = 1
|
||||
continue
|
||||
|
||||
# --- ILS Eckrad ---
|
||||
if bname.startswith("ECKRAD_"):
|
||||
counters["anzahl_eckraeder"] += 1
|
||||
# --- VarioFoerderer Compound-Block (VF_N) ---
|
||||
if matches_any(bname, BLOCKPATTERNS.get("pattern_variofoerderer", [])):
|
||||
attribs = block.get("attribs", {})
|
||||
counters["anzahl_variofoerderer"] += 1
|
||||
strecke_ids.append(get_id(block))
|
||||
try:
|
||||
counters["laengen_variofoerderer"] += float(attribs.get("L_VF_m", "0") or "0")
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
try:
|
||||
counters["anzahl_separatoren"] += int(attribs.get("ANZAHL_SEPARATOR", "0") or "0")
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
try:
|
||||
counters["anzahl_scanner"] += int(attribs.get("ANZAHL_SCANNER", "0") or "0")
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
if counters["anzahl_streckengruppen"] == 0:
|
||||
counters["anzahl_streckengruppen"] = 1
|
||||
continue
|
||||
|
||||
# --- ILS Strecke-Module (Vario/Staustrecke) ---
|
||||
for sb in STRECKE_BLOCKS:
|
||||
if sb in bname:
|
||||
counters["anzahl_variofoerderer"] += 1
|
||||
break
|
||||
# --- Gefaellestrecke Compound-Block (GF_N) ---
|
||||
if matches_any(bname, BLOCKPATTERNS.get("pattern_gefaellestrecke", [])):
|
||||
attribs = block.get("attribs", {})
|
||||
counters["anzahl_variofoerderer"] += 1
|
||||
counters["anzahl_gefaellestrecken"] += 1
|
||||
strecke_ids.append(get_id(block))
|
||||
try:
|
||||
counters["laengen_gefaellestrecken"] += float(attribs.get("L_GF_m", "0") or "0")
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
try:
|
||||
counters["anzahl_separatoren"] += int(attribs.get("ANZAHL_SEPARATOR", "0") or "0")
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
try:
|
||||
counters["anzahl_scanner"] += int(attribs.get("ANZAHL_SCANNER", "0") or "0")
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
if counters["anzahl_streckengruppen"] == 0:
|
||||
counters["anzahl_streckengruppen"] = 1
|
||||
continue
|
||||
|
||||
# --- ILS Strecke-Module (Vario/Staustrecke, einzelne Komponenten in Modelspace) ---
|
||||
if matches_any(bname, BLOCKPATTERNS.get("pattern_strecke_module", [])):
|
||||
counters["anzahl_variofoerderer"] += 1
|
||||
strecke_ids.append(get_id(block))
|
||||
|
||||
# --- Zeilen aufbauen ---
|
||||
items = []
|
||||
@@ -269,7 +331,7 @@ def process_blocks(blocks, lookup):
|
||||
"anzahl": g["anzahl"],
|
||||
"laenge_mm": "",
|
||||
"hoehe_m": format_hoehe_m(g["hoehe_mm"]),
|
||||
"details": {},
|
||||
"details": {"IDs": g["ids"]},
|
||||
})
|
||||
nr += 1
|
||||
|
||||
@@ -283,7 +345,7 @@ def process_blocks(blocks, lookup):
|
||||
"anzahl": g["anzahl"],
|
||||
"laenge_mm": "",
|
||||
"hoehe_m": format_hoehe_m(g["hoehe_mm"]),
|
||||
"details": {},
|
||||
"details": {"IDs": g["ids"]},
|
||||
})
|
||||
nr += 1
|
||||
|
||||
@@ -297,12 +359,14 @@ def process_blocks(blocks, lookup):
|
||||
"anzahl": g["anzahl"],
|
||||
"laenge_mm": int(g["laenge_mm"]),
|
||||
"hoehe_m": format_hoehe_m(g["hoehe_mm"]),
|
||||
"details": {},
|
||||
"details": {"IDs": g["ids"]},
|
||||
})
|
||||
nr += 1
|
||||
|
||||
for block in kreisel_list:
|
||||
sivasnr, teileart = ILS_SIVASNR["kreisel"]
|
||||
details = build_kreisel_details(block)
|
||||
details["ID"] = get_id(block)
|
||||
items.append({
|
||||
"nr": nr,
|
||||
"teileart": teileart,
|
||||
@@ -312,7 +376,7 @@ def process_blocks(blocks, lookup):
|
||||
"anzahl": 1,
|
||||
"laenge_mm": "",
|
||||
"hoehe_m": "",
|
||||
"details": build_kreisel_details(block),
|
||||
"details": details,
|
||||
})
|
||||
nr += 1
|
||||
|
||||
@@ -327,7 +391,7 @@ def process_blocks(blocks, lookup):
|
||||
"anzahl": counters["anzahl_eckraeder"],
|
||||
"laenge_mm": "",
|
||||
"hoehe_m": "",
|
||||
"details": {},
|
||||
"details": {"IDs": eckrad_ids},
|
||||
})
|
||||
nr += 1
|
||||
|
||||
@@ -342,7 +406,7 @@ def process_blocks(blocks, lookup):
|
||||
"anzahl": counters["anzahl_variofoerderer"],
|
||||
"laenge_mm": "",
|
||||
"hoehe_m": "",
|
||||
"details": {},
|
||||
"details": {"IDs": strecke_ids},
|
||||
})
|
||||
nr += 1
|
||||
|
||||
|
||||
+2
-2
@@ -123,9 +123,9 @@
|
||||
[--]
|
||||
[Kreisel]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_kreisel.lsp")) TEST_KREISEL
|
||||
[Foerderer]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_foerderer.lsp")) TEST_FOERDERER
|
||||
[KS EIN/AUS]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_ks.lsp")) TEST_KSEINAUS
|
||||
[Omniflo]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_omniflo.lsp")) TEST_OMNIFLO
|
||||
[<-Gefaellestrecke]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_gefaellestrecke.lsp")) TEST_GEFAELLESTRECKE
|
||||
[Gefaellestrecke]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_gefaellestrecke.lsp")) TEST_GEFAELLESTRECKE
|
||||
[<-Export CSV/Sivas ALL]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_export_all.lsp")) TEST_EXPORT_ALL
|
||||
|
||||
***DOUBLECLICK
|
||||
[INSERT]^C^C(ssg-ensure "SSG_LIB_Commands") SSG_BLOCKEDIT
|
||||
|
||||
+41
-18
@@ -16,8 +16,8 @@ BricsCAD Kommandozeile
|
||||
| SSG_RUN_ALL_TESTS |
|
||||
| (TEST_KREISEL, |
|
||||
| TEST_FOERDERER, |
|
||||
| TEST_KSEINAUS, |
|
||||
| TEST_OMNIFLO) |
|
||||
| TEST_OMNIFLO, |
|
||||
| TEST_GEFAELLESTRECKE) |
|
||||
| | |
|
||||
v v |
|
||||
tests/output/ |
|
||||
@@ -25,11 +25,10 @@ tests/output/ |
|
||||
kreisel_tests.dxf (save: dxf) |
|
||||
foerderer_results.json (immer) |
|
||||
foerderer_tests.dwg (save: dwg) |
|
||||
kseinaus_results.json (immer) |
|
||||
(keine Zeichnung) (save: null) |
|
||||
omniflo_results.json (immer) |
|
||||
omniflo_tests.dxf (save: dxf) |
|
||||
omniflo_export.csv |
|
||||
gefaellestrecke_results.json |
|
||||
gefaellestrecke_tests.dwg |
|
||||
|
|
||||
bin\run_tests.bat --runall
|
||||
|
|
||||
@@ -37,8 +36,29 @@ tests/output/ |
|
||||
|
|
||||
test_kreisel.py
|
||||
test_omniflo.py
|
||||
test_foerderer.py
|
||||
```
|
||||
|
||||
**Jedes Testmodul laeuft in einer eigenen, neuen Zeichnung** (statt die aktuelle zu
|
||||
leeren/wiederzuverwenden). `SSG_RUN_ALL_TESTS` fragt beim Start interaktiv nach
|
||||
dem gewuenschten Modus:
|
||||
|
||||
- **Kontroll-Modus (Default, Enter/Nein)**: Zeichnung wird gespeichert, per
|
||||
`_.ZOOM _Extents` auf den Inhalt gezoomt und bleibt als eigener Tab offen —
|
||||
der naechste Test startet in einer weiteren neuen Zeichnung. Am Ende des Laufs
|
||||
sind alle Testzeichnungen gleichzeitig offen und direkt sichtbar. Sinnvoll,
|
||||
wenn neue/geaenderte Testfaelle visuell geprueft werden sollen.
|
||||
`c:EXPORTCSV`/`c:EXPORTSIVAS` laufen **nicht** automatisch mit — CSV/Sivas-
|
||||
Export danach bei Bedarf manuell ueber `TEST_EXPORT_ALL`
|
||||
(`tests/test_export_all.lsp`, Menuepunkt "Export CSV/Sivas ALL"), das alle
|
||||
vorhandenen DXF/DWG-Dateien aus `tests/output/` einliest und je Datei
|
||||
`<basis>_export.csv`/`<basis>_sivas.csv` erzeugt.
|
||||
- **Automatik-Modus (Ja)**: Zeichnung wird gespeichert, direkt per
|
||||
`c:EXPORTCSV`/`c:EXPORTSIVAS` exportiert (`<name>_tests_export.csv`/
|
||||
`<name>_tests_sivas.csv`) und danach sofort wieder geschlossen. Kompletter
|
||||
Lauf ohne manuellen Zusatzschritt, aber ohne gleichzeitige visuelle Kontrolle
|
||||
aller Zeichnungen am Ende. Sinnvoll fuer reine CI-artige Validierung.
|
||||
|
||||
## alltests.json — Testregistry
|
||||
|
||||
Die Datei `tests/alltests.json` ist die zentrale Registry aller Test-Module.
|
||||
@@ -46,10 +66,10 @@ Die Datei `tests/alltests.json` ist die zentrale Registry aller Test-Module.
|
||||
|
||||
```json
|
||||
[
|
||||
{ "name": "kreisel", "save": "dxf" },
|
||||
{ "name": "foerderer", "save": "dwg" },
|
||||
{ "name": "kseinaus", "save": null },
|
||||
{ "name": "omniflo", "save": "dxf" }
|
||||
{ "name": "kreisel", "save": "dxf" },
|
||||
{ "name": "foerderer", "save": "dwg" },
|
||||
{ "name": "omniflo", "save": "dxf" },
|
||||
{ "name": "gefaellestrecke", "save": "dwg" }
|
||||
]
|
||||
```
|
||||
|
||||
@@ -73,7 +93,7 @@ wird in `tests/output/` geschrieben, sobald der Test abgeschlossen ist.
|
||||
|
||||
1. **BricsCAD starten** (ueber `bin\start_briscad.bat`) und die LISP-Testrunner ausfuehren (siehe Abschnitt "LISP-Tests in BricsCAD ausfuehren"). Die Testrunner erzeugen Bloecke in der Zeichnung und speichern Ergebnisse nach `tests/output/`.
|
||||
|
||||
2. **Optische Kontrolle** in BricsCAD — pruefen ob die erzeugten Bloecke korrekt aussehen (Positionen, Drehungen, Beschriftungen). Die Zeichnung bleibt offen und kann visuell inspiziert werden.
|
||||
2. **Optische Kontrolle** in BricsCAD — pruefen ob die erzeugten Bloecke korrekt aussehen (Positionen, Drehungen, Beschriftungen). Jede Testzeichnung bleibt als eigener Tab offen (auf den Inhalt gezoomt) und kann direkt visuell inspiziert werden.
|
||||
|
||||
3. **Kommandozeile**: Ergebnisse validieren oder als neue Referenz speichern:
|
||||
- `bin\run_tests.bat --runall` — pytest prueft die Ergebnisse aus `output/` gegen Testdefinitionen und Referenzdaten
|
||||
@@ -90,9 +110,9 @@ tests/
|
||||
test_kreisel.lsp # LISP-Testrunner: C:TEST_KREISEL
|
||||
test_kreisel.py # pytest-Validierung der Kreisel-Ergebnisse
|
||||
test_foerderer.lsp # LISP-Testrunner: C:TEST_FOERDERER
|
||||
test_kseinaus.lsp # LISP-Testrunner: C:TEST_KSEINAUS
|
||||
test_omniflo.lsp # LISP-Testrunner: C:TEST_OMNIFLO
|
||||
test_omniflo.py # pytest-Validierung der Omniflo-Ergebnisse
|
||||
test_export_all.lsp # LISP-Testrunner: C:TEST_EXPORT_ALL (manuell, nicht in alltests.json)
|
||||
testdata/
|
||||
kreisel_tests.json # Testfall-Definitionen fuer Kreisel
|
||||
omniflo_tests.json # Testfall-Definitionen fuer Omniflo
|
||||
@@ -100,12 +120,14 @@ tests/
|
||||
kreisel_results.json # JSON-Ergebnisse pro Testmodul
|
||||
kreisel_tests.dxf # DXF-Zeichnung pro Testmodul
|
||||
foerderer_results.json
|
||||
foerderer_tests.dxf
|
||||
kseinaus_results.json
|
||||
kseinaus_tests.dxf
|
||||
foerderer_tests.dwg
|
||||
omniflo_results.json
|
||||
omniflo_tests.dxf
|
||||
omniflo_export.csv # CSV-Export (Omniflo-spezifisch)
|
||||
gefaellestrecke_results.json
|
||||
gefaellestrecke_tests.dwg
|
||||
export_all_results.json # nur nach manuellem TEST_EXPORT_ALL-Lauf
|
||||
<basis>_export.csv # EXPORTCSV je Quelldatei, nur nach TEST_EXPORT_ALL
|
||||
<basis>_sivas.csv # EXPORTSIVAS je Quelldatei, nur nach TEST_EXPORT_ALL
|
||||
reference/ # Abgenommene Referenzdaten (in Git)
|
||||
kreisel_ref.dxf
|
||||
...
|
||||
@@ -173,7 +195,7 @@ Die Python-Tests pruefen:
|
||||
- **Attribut-Werte** gegen die Testdefinitionen (JSON)
|
||||
- **DXF-Geometrie** mit ezdxf (Bloecke, Positionen, Radien)
|
||||
- **Referenz-Vergleich** gegen abgenommene Referenz-DXF
|
||||
- **CSV-Export** (Omniflo: Spalten, Merkmale, Datentypen)
|
||||
- **CSV-Export** ueber `lib/export_csv.py` (Omniflo: Spalten, Merkmale, Sum-Zeile, Datentypen)
|
||||
|
||||
Tests, deren Eingabedaten fehlen (z.B. kein BricsCAD-Lauf), werden automatisch uebersprungen (`pytest.skip`).
|
||||
|
||||
@@ -381,8 +403,9 @@ def <modul>_ref_dxf():
|
||||
|
||||
| Klasse | Prueft |
|
||||
|---|---|
|
||||
| `TestOmnifloExportUnit` | Mock-Export: Merkmale, Datentypen, Vollstaendigkeit (ohne BricsCAD) |
|
||||
| `TestOmnifloExportCSV` | CSV aus BricsCAD: Header, Spalten, JSON-Merkmale |
|
||||
| `TestOmnifloExportUnit` | Mock-Export ueber `lib/export_csv.py`: Merkmale, Datentypen, Sum-Zeile, Vollstaendigkeit (ohne BricsCAD) |
|
||||
| `TestOmnifloResults` | omniflo_results.json aus BricsCAD: Testfaelle vollstaendig, Status OK, Hoehe |
|
||||
| `TestOmnifloReferenceCSV` | CSV-Export aus `lib/export_csv.py` gegen abgenommene Referenz-CSV |
|
||||
|
||||
## Umgebungsvariablen
|
||||
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
[
|
||||
{ "name": "kreisel", "save": "dxf", "module": "KreiselInsert" },
|
||||
{ "name": "foerderer", "save": "dwg", "module": "VarioFoerderer" },
|
||||
{ "name": "kseinaus", "save": null, "module": "VarioFoerderer" },
|
||||
{ "name": "omniflo", "save": "dxf", "module": "OmniModulInsert" },
|
||||
{ "name": "gefaellestrecke", "save": "dxf", "module": "Gefaellestrecke" }
|
||||
{ "name": "gefaellestrecke", "save": "dwg", "module": "Gefaellestrecke" }
|
||||
]
|
||||
|
||||
+51
-17
@@ -32,8 +32,18 @@ def _reference_dir():
|
||||
|
||||
|
||||
def _load_json(path):
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
"""Laedt JSON, tolerant gegenueber BricsCAD-Ausgaben.
|
||||
|
||||
AutoLISP schreibt Dateien (write-line) im ANSI-Codepage des Systems,
|
||||
nicht in UTF-8 - Sonderzeichen (z.B. Grad-Zeichen in Beschreibungen)
|
||||
koennen daher nicht als UTF-8 dekodierbar sein. Fallback auf cp1252.
|
||||
"""
|
||||
try:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
except UnicodeDecodeError:
|
||||
with open(path, "r", encoding="cp1252") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
# --- Foerderer Fixtures ---
|
||||
@@ -68,22 +78,15 @@ def omniflo_testdata():
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def omniflo_csv():
|
||||
"""Laedt den Inhalt der omniflo_export.csv aus output/."""
|
||||
import csv as _csv
|
||||
path = os.path.join(_output_dir(), "omniflo_export.csv")
|
||||
def omniflo_results():
|
||||
"""Laedt die Omniflo-Ergebnisse aus output/omniflo_results.json."""
|
||||
path = os.path.join(_output_dir(), "omniflo_results.json")
|
||||
if not os.path.exists(path):
|
||||
pytest.skip(
|
||||
"omniflo_export.csv nicht vorhanden - "
|
||||
"TEST_OMNIFLO_EXPORT in BricsCAD ausfuehren"
|
||||
)
|
||||
rows = []
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
reader = _csv.reader(f, delimiter=";")
|
||||
header = next(reader)
|
||||
for row in reader:
|
||||
rows.append(row)
|
||||
return {"header": header, "rows": rows}
|
||||
pytest.skip("omniflo_results.json nicht vorhanden - TEST_OMNIFLO in BricsCAD ausfuehren")
|
||||
data = _load_json(path)
|
||||
if not data:
|
||||
pytest.skip("omniflo_results.json ist leer")
|
||||
return data
|
||||
|
||||
|
||||
# --- Kreisel Fixtures ---
|
||||
@@ -123,3 +126,34 @@ def kreisel_ref_dxf():
|
||||
if not os.path.exists(path):
|
||||
pytest.skip("kreisel_ref.dxf nicht vorhanden - Referenz noch nicht erstellt")
|
||||
return ezdxf.readfile(path)
|
||||
|
||||
|
||||
# --- Gefaellestrecke Fixtures ---
|
||||
|
||||
@pytest.fixture
|
||||
def gefaellestrecke_testdata():
|
||||
"""Laedt die Gefaellestrecke-Testdefinitionen aus testdata/gefaellestrecke_tests.json."""
|
||||
path = os.path.join(_testdata_dir(), "gefaellestrecke_tests.json")
|
||||
data = _load_json(path)
|
||||
return [t for t in data if "test_id" in t]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def gefaellestrecke_results():
|
||||
"""Laedt die Gefaellestrecke-Ergebnisse aus output/gefaellestrecke_results.json."""
|
||||
path = os.path.join(_output_dir(), "gefaellestrecke_results.json")
|
||||
if not os.path.exists(path):
|
||||
pytest.skip("gefaellestrecke_results.json nicht vorhanden - TEST_GEFAELLESTRECKE in BricsCAD ausfuehren")
|
||||
data = _load_json(path)
|
||||
if not data:
|
||||
pytest.skip("gefaellestrecke_results.json ist leer")
|
||||
return data
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def gefaellestrecke_dxf():
|
||||
"""Laedt die erzeugte Gefaellestrecke-DXF aus output/gefaellestrecke_tests.dxf."""
|
||||
path = os.path.join(_output_dir(), "gefaellestrecke_tests.dxf")
|
||||
if not os.path.exists(path):
|
||||
pytest.skip("gefaellestrecke_tests.dxf nicht vorhanden - TESTRUN in BricsCAD ausfuehren")
|
||||
return ezdxf.readfile(path)
|
||||
|
||||
@@ -106,3 +106,4 @@ Elementnummer;TeileArt;TeileId;Bezeichnung;Anzahl;Merkmale
|
||||
105;"Omniflo Weiche";"0000";"OFWeiche :6";1;{"Weichentyp": "Dreifachweiche", "Richtung": "3", "Weichenwinkel": 90.0, "Höhe": "2000", "Drehung": 0.0, "Antrieb Kurve": true, "SivasNummer": "834372404+0_BG071090"}
|
||||
106;"Omniflo Weiche";"0000";"OFWeiche :1";1;{"Weichentyp": "Deltaweiche", "Richtung": "7", "Weichenwinkel": 90.0, "Höhe": "2000", "Drehung": 0.0, "Antrieb Kurve": false, "SivasNummer": "834372401"}
|
||||
107;"Omniflo Weiche";"0000";"OFWeiche :1";1;{"Weichentyp": "Sternweiche", "Richtung": "3", "Weichenwinkel": 0.0, "Höhe": "2000", "Drehung": 0.0, "Antrieb Kurve": false, "SivasNummer": "834372420"}
|
||||
108;"Omniflo Sum";"autogenerated_of_json";"Omniflo sum";1;{"Anzahl Bögen": 27, "Anzahl Weichengrundkörper": 0, "Anzahl Einwegweichen": 30, "Anzahl Deltaweichen": 7, "Anzahl Doppelweichen und Dreiwegeweichen": 42, "Anzahl Sternweichen": 1, "Gesamtlänge AP110": 0.0, "Gesamtlänge AP60": 0.0}
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 42.
|
@@ -0,0 +1,197 @@
|
||||
;; ============================================================
|
||||
;; test_export_all.lsp - Batch-Export aller Testzeichnungen
|
||||
;;
|
||||
;; Oeffnet jede vorhandene DXF/DWG-Datei aus tests/output/ und fuehrt
|
||||
;; darauf c:EXPORTCSV und c:EXPORTSIVAS aus (Lisp/export.lsp).
|
||||
;;
|
||||
;; Muss als LETZTES Modul in tests/alltests.json stehen: Es liest
|
||||
;; die Ausgabedateien aller anderen Testmodule, die zu diesem
|
||||
;; Zeitpunkt bereits geschrieben sein muessen.
|
||||
;;
|
||||
;; Ausgabe je Quelldatei <basis>.dxf/.dwg:
|
||||
;; tests/output/<basis>_export.csv
|
||||
;; tests/output/<basis>_sivas.csv
|
||||
;; tests/output/export_all_results.json (Zusammenfassung, immer)
|
||||
;;
|
||||
;; Voraussetzungen:
|
||||
;; - SSG_LIB geladen (export.lsp, ssg_core.lsp)
|
||||
;; - Umgebungsvariable DXFMAKRO gesetzt
|
||||
;;
|
||||
;; Aufruf in BricsCAD:
|
||||
;; (load (strcat (getenv "DXFMAKRO") "/tests/test_export_all.lsp"))
|
||||
;; TEST_EXPORT_ALL
|
||||
;; ============================================================
|
||||
|
||||
(vl-load-com)
|
||||
|
||||
|
||||
;; --- Auf Fertigstellung einer asynchron geschriebenen Datei warten ---
|
||||
;; csv:run-export startet Python per startapp (fire-and-forget). Pollt
|
||||
;; bis die Datei existiert und ihre Groesse ueber ein Intervall stabil
|
||||
;; bleibt, oder bis timeout-sec erreicht ist. Rueckgabe: T bei Erfolg.
|
||||
(defun export_all:wait-for-file (path timeout-sec / elapsed size1 size2 done)
|
||||
(setq elapsed 0.0 done nil)
|
||||
(while (and (not done) (< elapsed timeout-sec))
|
||||
(if (findfile path)
|
||||
(progn
|
||||
(setq size1 (vl-file-size path))
|
||||
(command "_.DELAY" 400)
|
||||
(setq size2 (vl-file-size path))
|
||||
(if (and size1 size2 (= size1 size2) (> size1 0))
|
||||
(setq done T)
|
||||
)
|
||||
)
|
||||
(command "_.DELAY" 400)
|
||||
)
|
||||
(setq elapsed (+ elapsed 0.4))
|
||||
)
|
||||
done
|
||||
)
|
||||
|
||||
|
||||
;; --- Zeichnung zusaetzlich oeffnen, ohne die aktuelle zu schliessen ---
|
||||
;; Nutzt reines ActiveX Documents.Open statt (command "_.OPEN" ...):
|
||||
;; Damit entfaellt sowohl die interaktive "Speichern?"-Abfrage als auch
|
||||
;; das vorherige Schliessen des aktiven Dokuments. Letzteres fuehrte dazu,
|
||||
;; dass BricsCAD (weil kein Dokument mehr offen war) automatisch ein neues
|
||||
;; Blank-Dokument erzeugt und dabei den kompletten Modul-/Menue-Ladezyklus
|
||||
;; (inkl. Fremd-Addons) erneut durchlaeuft - pro verarbeiteter Datei.
|
||||
(defun export_all:open-drawing (filepath / app doc)
|
||||
(setq app (vlax-get-acad-object))
|
||||
(setq doc (vla-open (vla-get-documents app) filepath))
|
||||
(vla-put-activedocument app doc)
|
||||
doc
|
||||
)
|
||||
|
||||
|
||||
;; --- Basisname (ohne Dateiendung) aus einem Dateinamen extrahieren ---
|
||||
(defun export_all:basisname (dateiname / i punkt-pos)
|
||||
(setq i 0 punkt-pos nil)
|
||||
(while (setq i (vl-string-search "." dateiname i))
|
||||
(setq punkt-pos i)
|
||||
(setq i (1+ i))
|
||||
)
|
||||
(if punkt-pos (substr dateiname 1 punkt-pos) dateiname)
|
||||
)
|
||||
|
||||
|
||||
;; --- Ergebnis eines Datei-Exports als JSON-String erzeugen ---
|
||||
(defun export_all:result-json (basis csv-ok sivas-ok / json)
|
||||
(strcat " {\n"
|
||||
" \"quelldatei\": \"" basis "\",\n"
|
||||
" \"csv_ok\": " (if csv-ok "true" "false") ",\n"
|
||||
" \"sivas_ok\": " (if sivas-ok "true" "false") "\n"
|
||||
" }")
|
||||
)
|
||||
|
||||
|
||||
;; --- JSON-Export der Ergebnisse ---
|
||||
(defun export_all:export-results (tests-out-dir / out-json f first)
|
||||
(if (null *export-all-results*)
|
||||
(princ "\n Keine Export-All-Ergebnisse vorhanden.")
|
||||
(progn
|
||||
(vl-mkdir tests-out-dir)
|
||||
(setq out-json (strcat tests-out-dir "/export_all_results.json"))
|
||||
(setq f (open out-json "w"))
|
||||
(if f
|
||||
(progn
|
||||
(write-line "[" f)
|
||||
(setq first T)
|
||||
(foreach r *export-all-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_EXPORT_ALL
|
||||
;;
|
||||
;; Oeffnet jede DXF/DWG-Datei aus tests/output/ und exportiert sie
|
||||
;; per EXPORTCSV/EXPORTSIVAS in eigene, nach der Quelldatei benannte
|
||||
;; CSV-Dateien.
|
||||
;; ============================================================
|
||||
(defun c:TEST_EXPORT_ALL (/ tests-out-dir dateien datei basis doc
|
||||
csv-name sivas-name csv-pfad sivas-pfad
|
||||
csv-ok sivas-ok results-list old-filenames)
|
||||
|
||||
(setq tests-out-dir (strcat (getenv "DXFMAKRO") "/tests/output"))
|
||||
(vl-mkdir tests-out-dir)
|
||||
(setenv "DXFM_RESULTS" tests-out-dir)
|
||||
|
||||
(setq dateien (append
|
||||
(vl-directory-files tests-out-dir "*.dxf" 1)
|
||||
(vl-directory-files tests-out-dir "*.dwg" 1)
|
||||
))
|
||||
(setq old-filenames *export-filenames*)
|
||||
(setq results-list nil)
|
||||
|
||||
(princ "\n================================================================")
|
||||
(princ "\n TEST_EXPORT_ALL")
|
||||
(princ (strcat "\n " (itoa (length dateien)) " Zeichnung(en) gefunden"))
|
||||
(princ "\n================================================================")
|
||||
|
||||
(foreach datei dateien
|
||||
(setq basis (export_all:basisname datei))
|
||||
(princ (strcat "\n\n>>> " datei " <<<"))
|
||||
|
||||
(setq doc (export_all:open-drawing (strcat tests-out-dir "/" datei)))
|
||||
|
||||
;; Datei-/Skriptnamen fuer diesen Durchlauf ueberschreiben, damit
|
||||
;; jede Quelldatei ihre eigenen CSV-Ergebnisse bekommt (statt der
|
||||
;; fixen Standardnamen "export.csv"/"export_sivas.csv").
|
||||
(setq csv-name (strcat basis "_export.csv"))
|
||||
(setq sivas-name (strcat basis "_sivas.csv"))
|
||||
(setq csv-pfad (strcat tests-out-dir "/" csv-name))
|
||||
(setq sivas-pfad (strcat tests-out-dir "/" sivas-name))
|
||||
(if (findfile csv-pfad) (vl-file-delete csv-pfad))
|
||||
(if (findfile sivas-pfad) (vl-file-delete sivas-pfad))
|
||||
|
||||
(setq *export-filenames*
|
||||
(list
|
||||
(cons "raw_json_datei" (cdr (assoc "raw_json_datei" old-filenames)))
|
||||
(cons "sivas_script" (cdr (assoc "sivas_script" old-filenames)))
|
||||
(cons "sivas_csv" sivas-name)
|
||||
(cons "csv_script" (cdr (assoc "csv_script" old-filenames)))
|
||||
(cons "csv_csv" csv-name)
|
||||
)
|
||||
)
|
||||
|
||||
(princ (strcat "\n[TEST_EXPORT_ALL] CSV-Export -> " csv-name))
|
||||
(c:EXPORTCSV)
|
||||
(setq csv-ok (export_all:wait-for-file csv-pfad 30.0))
|
||||
(princ (if csv-ok "\n[TEST_EXPORT_ALL] CSV OK" "\n[TEST_EXPORT_ALL] CSV TIMEOUT/FEHLER"))
|
||||
|
||||
(princ (strcat "\n[TEST_EXPORT_ALL] Sivas-Export -> " sivas-name))
|
||||
(c:EXPORTSIVAS)
|
||||
(setq sivas-ok (export_all:wait-for-file sivas-pfad 30.0))
|
||||
(princ (if sivas-ok "\n[TEST_EXPORT_ALL] Sivas OK" "\n[TEST_EXPORT_ALL] Sivas TIMEOUT/FEHLER"))
|
||||
|
||||
(setq results-list (cons
|
||||
(export_all:result-json basis csv-ok sivas-ok)
|
||||
results-list))
|
||||
|
||||
;; Diese Zeichnung wieder schliessen (verwirft ID/Hoehe/Drehung-Updates
|
||||
;; aus dem Export). Das urspruengliche Dokument (vor dieser Schleife
|
||||
;; aktiv) bleibt dabei durchgehend offen, BricsCAD erzeugt also kein
|
||||
;; neues Blank-Dokument und der teure Modul-/Menue-Reload entfaellt.
|
||||
(vla-close doc :vlax-false)
|
||||
)
|
||||
|
||||
(setq *export-filenames* old-filenames)
|
||||
(setq *export-all-results* (reverse results-list))
|
||||
|
||||
(princ "\n\n================================================================")
|
||||
(princ "\n TEST_EXPORT_ALL abgeschlossen.")
|
||||
(princ "\n================================================================")
|
||||
(princ)
|
||||
)
|
||||
+15
-17
@@ -75,7 +75,7 @@
|
||||
y-offset y-abstand idx
|
||||
anz-gebaut anz-nicht-gebaut results-list
|
||||
startpunkt vf-nummer hoehe-von hoehe-bis lastEnt
|
||||
tests-out-dir old-dxfm-results)
|
||||
sub-ent sub-typ)
|
||||
|
||||
;; Bibliothek initialisieren (laedt Block-Library und extrahiert Masse)
|
||||
(if (not *lib-initialized*)
|
||||
@@ -103,7 +103,7 @@
|
||||
;; Konfiguration aus erstem Eintrag (ohne test_id)
|
||||
(setq cfg (car testdaten))
|
||||
(setq y-abstand (ssg-val cfg "y_abstand"))
|
||||
(if (or (null y-abstand) (= y-abstand 0)) (setq y-abstand 400))
|
||||
(if (or (null y-abstand) (= y-abstand 0)) (setq y-abstand 800))
|
||||
|
||||
;; Nur Eintraege mit test_id sind Testfaelle
|
||||
(setq tests
|
||||
@@ -175,6 +175,19 @@
|
||||
(/ L_GF 2.0) (/ L_GF 2.0)
|
||||
richtung best-winkel startpunkt lastEnt 0.0)
|
||||
|
||||
;; lastEnt hinter den SEQEND des VF-Blocks schieben.
|
||||
;; Ohne diesen Schritt wuerde entnext vom INSERT-Entity
|
||||
;; in die ATTRIB-Kette des gerade erstellten VF-Blocks
|
||||
;; springen und diese beim naechsten _.-BLOCK-Aufruf
|
||||
;; in den neuen Block verschieben (Datenverlust).
|
||||
(setq lastEnt (entlast))
|
||||
(setq sub-ent (entnext lastEnt))
|
||||
(while (and sub-ent
|
||||
(not (equal (cdr (assoc 0 (entget sub-ent))) "SEQEND")))
|
||||
(setq lastEnt sub-ent)
|
||||
(setq sub-ent (entnext lastEnt)))
|
||||
(if sub-ent (setq lastEnt sub-ent))
|
||||
|
||||
(setq results-list (cons
|
||||
(foerderer:test-result-json test-id richtung deltaL deltaH
|
||||
"GEBAUT" best-winkel L_GF L_VF)
|
||||
@@ -205,21 +218,6 @@
|
||||
;; Ergebnisse in globaler Variable speichern
|
||||
(setq *foerderer-test-results* (reverse results-list))
|
||||
|
||||
;; CSV- und Sivas-Export nach tests/output/
|
||||
(setq tests-out-dir (strcat (getenv "DXFMAKRO") "/tests/output"))
|
||||
(vl-mkdir tests-out-dir)
|
||||
(setq old-dxfm-results (getenv "DXFM_RESULTS"))
|
||||
(setenv "DXFM_RESULTS" tests-out-dir)
|
||||
|
||||
(princ "\n================================================================")
|
||||
(princ "\n[TEST_FOERDERER] CSV-Export -> foerderer_export.csv")
|
||||
(csv:run-export "FOERDERER_CSV" "export_csv.py" "foerderer_export.csv")
|
||||
|
||||
(princ "\n[TEST_FOERDERER] Sivas-Export -> foerderer_sivas.csv")
|
||||
(csv:run-export "FOERDERER_SIVAS" "export_sivas.py" "foerderer_sivas.csv")
|
||||
|
||||
(setenv "DXFM_RESULTS" (if old-dxfm-results old-dxfm-results ""))
|
||||
|
||||
(ssg-end)
|
||||
(princ "\n================================================================")
|
||||
(princ "\n TEST_FOERDERER abgeschlossen.")
|
||||
|
||||
+14
-14
@@ -7,8 +7,8 @@ Prueft:
|
||||
1. Status-Werte und Vollstaendigkeit (foerderer_results.json)
|
||||
2. Mathematische Invarianten (Laengen, Winkel, Richtung)
|
||||
3. Geometrisch unmoegliche Faelle
|
||||
4. CSV-Export-Struktur (foerderer_export.csv, falls vorhanden)
|
||||
5. Sivas-Export-Struktur (foerderer_sivas.csv, falls vorhanden)
|
||||
4. CSV-Export-Struktur (foerderer_tests_export.csv, aus TEST_EXPORT_ALL)
|
||||
5. Sivas-Export-Struktur (foerderer_tests_sivas.csv, aus TEST_EXPORT_ALL)
|
||||
"""
|
||||
|
||||
import csv
|
||||
@@ -179,19 +179,19 @@ class TestFoerdererMathematik:
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Klasse 3: CSV-Export-Pruefung (foerderer_export.csv)
|
||||
# Klasse 3: CSV-Export-Pruefung (foerderer_tests_export.csv)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestFoerdererExportCSV:
|
||||
"""Prueft den CSV-Export aus BricsCAD (foerderer_export.csv)."""
|
||||
"""Prueft den CSV-Export aus BricsCAD (foerderer_tests_export.csv, aus TEST_EXPORT_ALL)."""
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup(self):
|
||||
csv_path = os.path.join(_output_dir(), "foerderer_export.csv")
|
||||
csv_path = os.path.join(_output_dir(), "foerderer_tests_export.csv")
|
||||
if not os.path.exists(csv_path):
|
||||
pytest.skip(
|
||||
"foerderer_export.csv nicht vorhanden - "
|
||||
"TEST_FOERDERER in BricsCAD ausfuehren (exportiert automatisch)"
|
||||
"foerderer_tests_export.csv nicht vorhanden - "
|
||||
"TEST_FOERDERER und anschliessend TEST_EXPORT_ALL in BricsCAD ausfuehren"
|
||||
)
|
||||
self.rows = []
|
||||
with open(csv_path, "r", encoding="utf-8") as f:
|
||||
@@ -211,7 +211,7 @@ class TestFoerdererExportCSV:
|
||||
|
||||
def test_hat_eintraege(self):
|
||||
"""CSV muss mindestens einen Dateneintrag enthalten."""
|
||||
assert len(self.rows) > 0, "foerderer_export.csv enthaelt keine Daten"
|
||||
assert len(self.rows) > 0, "foerderer_tests_export.csv enthaelt keine Daten"
|
||||
|
||||
def test_vario_teileart(self):
|
||||
"""Alle Eintraege muessen TeileArt 'VarioFoerderer' haben."""
|
||||
@@ -265,19 +265,19 @@ class TestFoerdererExportCSV:
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Klasse 4: Sivas-Export-Pruefung (foerderer_sivas.csv)
|
||||
# Klasse 4: Sivas-Export-Pruefung (foerderer_tests_sivas.csv)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestFoerdererExportSivas:
|
||||
"""Prueft den Sivas-Export aus BricsCAD (foerderer_sivas.csv)."""
|
||||
"""Prueft den Sivas-Export aus BricsCAD (foerderer_tests_sivas.csv, aus TEST_EXPORT_ALL)."""
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup(self):
|
||||
csv_path = os.path.join(_output_dir(), "foerderer_sivas.csv")
|
||||
csv_path = os.path.join(_output_dir(), "foerderer_tests_sivas.csv")
|
||||
if not os.path.exists(csv_path):
|
||||
pytest.skip(
|
||||
"foerderer_sivas.csv nicht vorhanden - "
|
||||
"TEST_FOERDERER in BricsCAD ausfuehren (exportiert automatisch)"
|
||||
"foerderer_tests_sivas.csv nicht vorhanden - "
|
||||
"TEST_FOERDERER und anschliessend TEST_EXPORT_ALL in BricsCAD ausfuehren"
|
||||
)
|
||||
self.rows = []
|
||||
with open(csv_path, "r", encoding="utf-8") as f:
|
||||
@@ -297,7 +297,7 @@ class TestFoerdererExportSivas:
|
||||
|
||||
def test_hat_eintraege(self):
|
||||
"""Sivas-CSV muss mindestens einen Dateneintrag enthalten."""
|
||||
assert len(self.rows) > 0, "foerderer_sivas.csv enthaelt keine Daten"
|
||||
assert len(self.rows) > 0, "foerderer_tests_sivas.csv enthaelt keine Daten"
|
||||
|
||||
def test_strecke_zeile_vorhanden(self):
|
||||
"""Eine Zeile mit TeileArt 'ILS 2.0 Strecke - Module' muss vorhanden sein."""
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
test_gefaellestrecke.py - Validiert GEFAELLESTRECKE Testergebnisse.
|
||||
|
||||
Prueft:
|
||||
1. Status-Werte und Vollstaendigkeit (gefaellestrecke_results.json)
|
||||
2. Mathematische Invarianten (Hoehen, Winkel, deltaH)
|
||||
3. Konsistenz zwischen den Modi (Modus 1 Richtungsunabhaengigkeit,
|
||||
Modus 3 uebernimmt deltaH/deltaL aus Modus 2)
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import pytest
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Hilfsfunktionen
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _testdata_dir():
|
||||
return os.getenv("DXFM_TESTDATA",
|
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), "testdata"))
|
||||
|
||||
|
||||
def _output_dir():
|
||||
return os.getenv("DXFM_TESTOUT",
|
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), "output"))
|
||||
|
||||
|
||||
def _load_json(path):
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def _load_gefaellestrecke_results():
|
||||
path = os.path.join(_output_dir(), "gefaellestrecke_results.json")
|
||||
if not os.path.exists(path):
|
||||
pytest.skip(
|
||||
"gefaellestrecke_results.json nicht vorhanden - "
|
||||
"TEST_GEFAELLESTRECKE in BricsCAD ausfuehren"
|
||||
)
|
||||
data = _load_json(path)
|
||||
if not data:
|
||||
pytest.skip("gefaellestrecke_results.json ist leer")
|
||||
return data
|
||||
|
||||
|
||||
def _load_gefaellestrecke_testdata():
|
||||
path = os.path.join(_testdata_dir(), "gefaellestrecke_tests.json")
|
||||
return _load_json(path)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Klasse 1: Status und Vollstaendigkeit
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestGefaellestreckeStatus:
|
||||
"""Prueft Status-Werte und Vollstaendigkeit der Ergebnisse."""
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup(self):
|
||||
self.results = _load_gefaellestrecke_results()
|
||||
testdata = _load_gefaellestrecke_testdata()
|
||||
self.tests = [t for t in testdata if "test_id" in t]
|
||||
self.results_by_id = {r["test_id"]: r for r in self.results}
|
||||
|
||||
def test_alle_testfaelle_vorhanden(self):
|
||||
"""Alle Testfaelle muessen in den Ergebnissen vorhanden sein."""
|
||||
expected_ids = {t["test_id"] for t in self.tests}
|
||||
result_ids = {r["test_id"] for r in self.results}
|
||||
missing = expected_ids - result_ids
|
||||
assert not missing, f"Fehlende Testfaelle in Ergebnissen: {missing}"
|
||||
|
||||
def test_status_werte_gueltig(self):
|
||||
"""Status muss GEBAUT oder FEHLER (bzw. FEHLER_LINIE) sein."""
|
||||
gueltige_status = {"GEBAUT", "FEHLER", "FEHLER_LINIE"}
|
||||
for r in self.results:
|
||||
assert r["status"] in gueltige_status, (
|
||||
f'{r["test_id"]}: Unbekannter Status "{r["status"]}"'
|
||||
)
|
||||
|
||||
def test_alle_modus1_gebaut(self):
|
||||
"""Alle vier Modus-1-Richtungen (hz=0/90/180/270) muessen GEBAUT sein."""
|
||||
modus1 = [r for r in self.results if r["modus"] == 1]
|
||||
assert len(modus1) == 4, f"Erwartet 4 Modus-1-Testfaelle, gefunden {len(modus1)}"
|
||||
for r in modus1:
|
||||
assert r["status"] == "GEBAUT", (
|
||||
f'{r["test_id"]}: erwartet GEBAUT, ist "{r["status"]}"'
|
||||
)
|
||||
|
||||
def test_modus2_und_modus3_gebaut(self):
|
||||
"""Modus 2 und Modus 3 muessen GEBAUT sein."""
|
||||
for test_id in ("M2_hz000", "M3_hz270"):
|
||||
r = self.results_by_id.get(test_id)
|
||||
assert r is not None, f"{test_id} fehlt in den Ergebnissen"
|
||||
assert r["status"] == "GEBAUT", (
|
||||
f'{test_id}: erwartet GEBAUT, ist "{r["status"]}"'
|
||||
)
|
||||
|
||||
def test_mindestens_ein_gebaut(self):
|
||||
"""Mindestens ein Testfall muss GEBAUT sein."""
|
||||
gebaut = [r for r in self.results if r["status"] == "GEBAUT"]
|
||||
assert len(gebaut) > 0, "Keine einzige Gefaellestrecke konnte gebaut werden"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Klasse 2: Mathematische Invarianten
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestGefaellestreckeMathematik:
|
||||
"""Prueft mathematische Korrektheit der berechneten Werte."""
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup(self):
|
||||
self.results = _load_gefaellestrecke_results()
|
||||
self.gebaut = [r for r in self.results if r["status"] == "GEBAUT"]
|
||||
self.results_by_id = {r["test_id"]: r for r in self.results}
|
||||
|
||||
def test_gebaut_hat_pflichtfelder(self):
|
||||
"""GEBAUT-Ergebnisse muessen alle Kernfelder enthalten."""
|
||||
pflichtfelder = {
|
||||
"modus", "hz_grad", "deltaL_mm", "winkel_grad",
|
||||
"hoehe_von_mm", "hoehe_bis_mm", "delta_h_mm",
|
||||
}
|
||||
for r in self.gebaut:
|
||||
missing = pflichtfelder - set(r.keys())
|
||||
assert not missing, f'{r["test_id"]}: fehlende Felder {missing}'
|
||||
|
||||
def test_deltaL_positiv(self):
|
||||
"""deltaL_mm muss fuer alle GEBAUT-Faelle groesser als 0 sein."""
|
||||
for r in self.gebaut:
|
||||
assert r["deltaL_mm"] > 0, (
|
||||
f'{r["test_id"]}: deltaL_mm={r["deltaL_mm"]} muss > 0 sein'
|
||||
)
|
||||
|
||||
def test_winkel_positiv(self):
|
||||
"""Der berechnete Gefaellewinkel muss groesser als 0 Grad sein."""
|
||||
for r in self.gebaut:
|
||||
assert r["winkel_grad"] > 0, (
|
||||
f'{r["test_id"]}: winkel_grad={r["winkel_grad"]} muss > 0 sein'
|
||||
)
|
||||
|
||||
def test_delta_h_konsistent_mit_hoehen(self):
|
||||
"""delta_h_mm muss der absoluten Differenz von hoehe_von/hoehe_bis entsprechen."""
|
||||
for r in self.gebaut:
|
||||
erwartet = abs(r["hoehe_von_mm"] - r["hoehe_bis_mm"])
|
||||
assert erwartet == r["delta_h_mm"], (
|
||||
f'{r["test_id"]}: delta_h_mm={r["delta_h_mm"]}, '
|
||||
f'erwartet |{r["hoehe_von_mm"]}-{r["hoehe_bis_mm"]}|={erwartet}'
|
||||
)
|
||||
|
||||
def test_hoehe_bis_kleiner_hoehe_von(self):
|
||||
"""Bei einer Gefaellestrecke muss die Endhoehe unter der Starthoehe liegen."""
|
||||
for r in self.gebaut:
|
||||
assert r["hoehe_bis_mm"] < r["hoehe_von_mm"], (
|
||||
f'{r["test_id"]}: hoehe_bis_mm={r["hoehe_bis_mm"]} '
|
||||
f'nicht < hoehe_von_mm={r["hoehe_von_mm"]}'
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Klasse 3: Konsistenz zwischen den Modi
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestGefaellestreckeModusKonsistenz:
|
||||
"""Prueft Zusammenhaenge zwischen Modus 1/2/3 (Richtungsunabhaengigkeit,
|
||||
Uebernahme von deltaH/deltaL aus Modus 2 in Modus 3)."""
|
||||
|
||||
# Toleranz fuer die 2-Pass-Winkelkorrektur in Modus 3 (mm)
|
||||
DELTA_H_TOLERANZ_MM = 5
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup(self):
|
||||
self.results = _load_gefaellestrecke_results()
|
||||
self.results_by_id = {r["test_id"]: r for r in self.results}
|
||||
self.testdata = _load_gefaellestrecke_testdata()
|
||||
self.tests_by_id = {t["test_id"]: t for t in self.testdata if "test_id" in t}
|
||||
|
||||
def test_modus1_richtungsunabhaengig(self):
|
||||
"""Alle vier Modus-1-Richtungen muessen dieselbe Hoehe/Winkel liefern,
|
||||
da nur die Fahrtrichtung (hz), nicht die Geometrie variiert."""
|
||||
modus1 = [r for r in self.results
|
||||
if r["modus"] == 1 and r["status"] == "GEBAUT"]
|
||||
assert len(modus1) >= 2, "Zu wenige Modus-1-Ergebnisse fuer Vergleich"
|
||||
referenz = modus1[0]
|
||||
for r in modus1[1:]:
|
||||
assert r["delta_h_mm"] == referenz["delta_h_mm"], (
|
||||
f'{r["test_id"]}: delta_h_mm={r["delta_h_mm"]} weicht von '
|
||||
f'{referenz["test_id"]}={referenz["delta_h_mm"]} ab (Richtungsabhaengigkeit!)'
|
||||
)
|
||||
assert r["winkel_grad"] == referenz["winkel_grad"], (
|
||||
f'{r["test_id"]}: winkel_grad={r["winkel_grad"]} weicht von '
|
||||
f'{referenz["test_id"]}={referenz["winkel_grad"]} ab'
|
||||
)
|
||||
|
||||
def test_modus3_uebernimmt_deltaH_aus_modus2(self):
|
||||
"""Modus 3 (Linienzug mit 2-Pass-Korrektur) muss innerhalb einer kleinen
|
||||
Toleranz dieselbe Hoehendifferenz wie die Quelle aus Modus 2 erreichen."""
|
||||
m3_test = self.tests_by_id.get("M3_hz270")
|
||||
if m3_test is None:
|
||||
pytest.skip("M3_hz270 nicht in Testdaten definiert")
|
||||
quelle_id = m3_test.get("deltaH_quelle")
|
||||
m2 = self.results_by_id.get(quelle_id)
|
||||
m3 = self.results_by_id.get("M3_hz270")
|
||||
if m2 is None or m3 is None:
|
||||
pytest.skip("M2- oder M3-Ergebnis fehlt")
|
||||
if m2["status"] != "GEBAUT" or m3["status"] != "GEBAUT":
|
||||
pytest.skip("M2 oder M3 nicht GEBAUT")
|
||||
abweichung = abs(m3["delta_h_mm"] - m2["delta_h_mm"])
|
||||
assert abweichung <= self.DELTA_H_TOLERANZ_MM, (
|
||||
f'M3_hz270: delta_h_mm={m3["delta_h_mm"]} weicht um {abweichung} mm '
|
||||
f'von {quelle_id}={m2["delta_h_mm"]} ab '
|
||||
f'(Toleranz {self.DELTA_H_TOLERANZ_MM} mm)'
|
||||
)
|
||||
@@ -1,261 +0,0 @@
|
||||
;; ============================================================
|
||||
;; test_kseinaus.lsp - Automatischer Integrationstest fuer KS_EIN/KS_AUS
|
||||
;;
|
||||
;; Fuegt alle Vario-Bloecke in 3 Gruppen ein, jeweils 2 Reihen:
|
||||
;; Gruppe 1: Stationaere Elemente (AUS, EIN, Separator, Umlenk, Motor)
|
||||
;; Gruppe 2: Alle Boegen aufwaerts (in Z verschoben)
|
||||
;; Gruppe 3: Alle Boegen abwaerts (in Z verschoben)
|
||||
;;
|
||||
;; Pro Gruppe:
|
||||
;; Reihe A: nach KS_EIN ausgerichtet
|
||||
;; Reihe B: nach KS_AUS ausgerichtet
|
||||
;; Luecke von 400mm zwischen den Reihen
|
||||
;;
|
||||
;; Speichert:
|
||||
;; - tests/output/kseinaus_results.json (Ergebnisse)
|
||||
;; - tests/output/kseinaus_tests.dxf (Zeichnung)
|
||||
;;
|
||||
;; Voraussetzungen:
|
||||
;; - SSG_LIB geladen (VarioFoerderer.lsp, ssg_core.lsp)
|
||||
;; - Umgebungsvariable DXFMAKRO gesetzt
|
||||
;;
|
||||
;; Aufruf in BricsCAD:
|
||||
;; (load (strcat (getenv "DXFMAKRO") "/tests/test_kseinaus.lsp"))
|
||||
;; TEST_KSEINAUS
|
||||
;; ============================================================
|
||||
|
||||
|
||||
;; --- Ergebnis eines Testfalls als JSON-String erzeugen ---
|
||||
(defun kseinaus:test-result-json (test-id block-name gruppe ks-type status /
|
||||
json)
|
||||
(setq json (strcat " {\n"
|
||||
" \"test_id\": \"" test-id "\",\n"
|
||||
" \"block_name\": \"" (if block-name block-name "") "\",\n"
|
||||
" \"gruppe\": \"" gruppe "\",\n"
|
||||
" \"ks_type\": \"" ks-type "\",\n"
|
||||
" \"status\": \"" status "\"\n"
|
||||
" }"))
|
||||
json
|
||||
)
|
||||
|
||||
|
||||
;; --- Eine Reihe Bloecke einfuegen und Ergebnisse sammeln ---
|
||||
;; Rueckgabe: (naechste-y-pos . results-list)
|
||||
(defun kseinaus:test-reihe (namen x-start y-start z-offset y-abstand y-luecke
|
||||
gruppe results-in /
|
||||
bname block-obj ks-data ks-ein ks-aus
|
||||
insert-pt offset-vec y-pos idx
|
||||
test-id results-out)
|
||||
|
||||
(setq results-out results-in)
|
||||
|
||||
;; --- Reihe A: nach KS_EIN ---
|
||||
(princ "\n Reihe KS_EIN:")
|
||||
(setq y-pos y-start)
|
||||
(setq idx 0)
|
||||
|
||||
(foreach bname namen
|
||||
(setq idx (1+ idx))
|
||||
(setq test-id (strcat gruppe "_KS_EIN_" (itoa idx)))
|
||||
(setq insert-pt (list x-start y-pos z-offset))
|
||||
(setq block-obj (vla-InsertBlock modelspace
|
||||
(vlax-3D-point insert-pt)
|
||||
bname 1.0 1.0 1.0 0))
|
||||
(setq ks-data (extract-ks-from-block block-obj))
|
||||
(setq ks-ein (cadr (assoc "KS_EIN" ks-data)))
|
||||
|
||||
(if ks-ein
|
||||
(progn
|
||||
(setq offset-vec (list (- x-start (car (car ks-ein)))
|
||||
(- y-pos (cadr (car ks-ein)))
|
||||
(- z-offset (caddr (car ks-ein)))))
|
||||
(vla-Move block-obj
|
||||
(vlax-3D-point '(0 0 0))
|
||||
(vlax-3D-point offset-vec))
|
||||
(princ (strcat "\n " (itoa idx) ". " bname " OK"))
|
||||
(setq results-out (cons
|
||||
(kseinaus:test-result-json test-id bname gruppe "KS_EIN" "OK")
|
||||
results-out))
|
||||
)
|
||||
(progn
|
||||
(princ (strcat "\n " (itoa idx) ". " bname " WARNUNG: KS_EIN fehlt!"))
|
||||
(setq results-out (cons
|
||||
(kseinaus:test-result-json test-id bname gruppe "KS_EIN" "KS_FEHLT")
|
||||
results-out))
|
||||
)
|
||||
)
|
||||
(setq y-pos (- y-pos y-abstand))
|
||||
)
|
||||
|
||||
;; --- Luecke ---
|
||||
(setq y-pos (- y-pos y-luecke))
|
||||
|
||||
;; --- Reihe B: nach KS_AUS ---
|
||||
(princ "\n Reihe KS_AUS:")
|
||||
(setq idx 0)
|
||||
|
||||
(foreach bname namen
|
||||
(setq idx (1+ idx))
|
||||
(setq test-id (strcat gruppe "_KS_AUS_" (itoa idx)))
|
||||
(setq insert-pt (list x-start y-pos z-offset))
|
||||
(setq block-obj (vla-InsertBlock modelspace
|
||||
(vlax-3D-point insert-pt)
|
||||
bname 1.0 1.0 1.0 0))
|
||||
(setq ks-data (extract-ks-from-block block-obj))
|
||||
(setq ks-aus (cadr (assoc "KS_AUS" ks-data)))
|
||||
|
||||
(if ks-aus
|
||||
(progn
|
||||
(setq offset-vec (list (- x-start (car (car ks-aus)))
|
||||
(- y-pos (cadr (car ks-aus)))
|
||||
(- z-offset (caddr (car ks-aus)))))
|
||||
(vla-Move block-obj
|
||||
(vlax-3D-point '(0 0 0))
|
||||
(vlax-3D-point offset-vec))
|
||||
(princ (strcat "\n " (itoa idx) ". " bname " OK"))
|
||||
(setq results-out (cons
|
||||
(kseinaus:test-result-json test-id bname gruppe "KS_AUS" "OK")
|
||||
results-out))
|
||||
)
|
||||
(progn
|
||||
(princ (strcat "\n " (itoa idx) ". " bname " WARNUNG: KS_AUS fehlt!"))
|
||||
(setq results-out (cons
|
||||
(kseinaus:test-result-json test-id bname gruppe "KS_AUS" "KS_FEHLT")
|
||||
results-out))
|
||||
)
|
||||
)
|
||||
(setq y-pos (- y-pos y-abstand))
|
||||
)
|
||||
|
||||
;; Rueckgabe: (naechste-y-pos . results-list)
|
||||
(cons y-pos results-out)
|
||||
)
|
||||
|
||||
|
||||
;; --- JSON-Export der KS-Testergebnisse ---
|
||||
(defun kseinaus:export-results (tests-out-dir / out-json f first)
|
||||
(if (null *kseinaus-test-results*)
|
||||
(princ "\n Keine KS-Ergebnisse vorhanden.")
|
||||
(progn
|
||||
(vl-mkdir tests-out-dir)
|
||||
(setq out-json (strcat tests-out-dir "/kseinaus_results.json"))
|
||||
(setq f (open out-json "w"))
|
||||
(if f
|
||||
(progn
|
||||
(write-line "[" f)
|
||||
(setq first T)
|
||||
(foreach r *kseinaus-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_KSEINAUS - Hauptbefehl
|
||||
;; ============================================================
|
||||
(defun c:TEST_KSEINAUS ( / stationen boegen-auf-liste boegen-ab-liste
|
||||
bname y-pos y-abstand y-luecke z-bogen
|
||||
results-list reihe-result
|
||||
anz-ok anz-fehlt)
|
||||
|
||||
;; Bibliothek initialisieren
|
||||
(if (not *lib-initialized*)
|
||||
(init-bibliothek)
|
||||
)
|
||||
|
||||
(ssg-start "TEST_KSEINAUS" '(("OSMODE") ("CECOLOR") ("ATTREQ") ("ATTDIA")))
|
||||
(setvar "OSMODE" 0)
|
||||
(setvar "ATTREQ" 0)
|
||||
(setvar "ATTDIA" 0)
|
||||
|
||||
(setq y-abstand 200)
|
||||
(setq y-luecke 400)
|
||||
(setq z-bogen 2500)
|
||||
(setq results-list '())
|
||||
|
||||
;; Gruppe 1: Stationaere Elemente
|
||||
(setq stationen (list
|
||||
"_3D_AS_90_links"
|
||||
"Staustrecke_Separator_SP_300_mm"
|
||||
"Vario_Umlenkstation_500mm"
|
||||
"Vario_Motorstation_500mm"
|
||||
"_3D_ES_90_links"
|
||||
))
|
||||
|
||||
;; Gruppe 2+3: Boegen sammeln
|
||||
(setq boegen-auf-liste '())
|
||||
(setq boegen-ab-liste '())
|
||||
(foreach w '(3 6 9 12 15 18 21 27 33 39 45 51)
|
||||
(setq bname (strcat "Vario_Bogen_auf_" (itoa w) grad-zeichen))
|
||||
(if (tblsearch "BLOCK" bname)
|
||||
(setq boegen-auf-liste (append boegen-auf-liste (list bname)))
|
||||
)
|
||||
(setq bname (strcat "Vario_Bogen_ab_" (itoa w) grad-zeichen))
|
||||
(if (tblsearch "BLOCK" bname)
|
||||
(setq boegen-ab-liste (append boegen-ab-liste (list bname)))
|
||||
)
|
||||
)
|
||||
|
||||
(princ "\n================================================================")
|
||||
(princ "\n TEST_KSEINAUS - KS-Ausrichtungstest")
|
||||
(princ (strcat "\n Stationen: " (itoa (length stationen))))
|
||||
(princ (strcat "\n Boegen auf: " (itoa (length boegen-auf-liste))))
|
||||
(princ (strcat "\n Boegen ab: " (itoa (length boegen-ab-liste))))
|
||||
(princ (strcat "\n Z-Offset Boegen: " (rtos z-bogen 2 0) " mm"))
|
||||
(princ "\n================================================================")
|
||||
|
||||
;; Gruppe 1: Stationaere Elemente bei X=0, Z=0
|
||||
(princ "\n\n GRUPPE 1: Stationaere Elemente (Z=0)")
|
||||
(setq reihe-result (kseinaus:test-reihe stationen 0 0 0 y-abstand y-luecke
|
||||
"Stationen" results-list))
|
||||
(setq y-pos (car reihe-result))
|
||||
(setq results-list (cdr reihe-result))
|
||||
|
||||
;; Gruppe 2: Boegen aufwaerts bei X=3000, Z=z-bogen
|
||||
(princ (strcat "\n\n GRUPPE 2: Boegen aufwaerts (Z=" (rtos z-bogen 2 0) ")"))
|
||||
(setq reihe-result (kseinaus:test-reihe boegen-auf-liste 3000 0 z-bogen y-abstand y-luecke
|
||||
"Boegen_Auf" results-list))
|
||||
(setq y-pos (car reihe-result))
|
||||
(setq results-list (cdr reihe-result))
|
||||
|
||||
;; Gruppe 3: Boegen abwaerts bei X=6000, Z=z-bogen
|
||||
(princ (strcat "\n\n GRUPPE 3: Boegen abwaerts (Z=" (rtos z-bogen 2 0) ")"))
|
||||
(setq reihe-result (kseinaus:test-reihe boegen-ab-liste 6000 0 z-bogen y-abstand y-luecke
|
||||
"Boegen_Ab" results-list))
|
||||
(setq y-pos (car reihe-result))
|
||||
(setq results-list (cdr reihe-result))
|
||||
|
||||
;; Zusammenfassung
|
||||
(setq anz-ok 0)
|
||||
(setq anz-fehlt 0)
|
||||
(foreach r results-list
|
||||
(if (vl-string-search "\"OK\"" r)
|
||||
(setq anz-ok (1+ anz-ok))
|
||||
(setq anz-fehlt (1+ anz-fehlt))
|
||||
)
|
||||
)
|
||||
|
||||
(princ "\n================================================================")
|
||||
(princ (strcat "\n Ergebnis: " (itoa anz-ok) " OK, "
|
||||
(itoa anz-fehlt) " KS fehlt"))
|
||||
(princ "\n================================================================")
|
||||
|
||||
;; Ergebnisse in globaler Variable speichern
|
||||
(setq *kseinaus-test-results* (reverse results-list))
|
||||
|
||||
(ssg-end)
|
||||
(princ "\n================================================================")
|
||||
(princ "\n TEST_KSEINAUS abgeschlossen.")
|
||||
(princ "\n================================================================")
|
||||
(princ)
|
||||
)
|
||||
+6
-20
@@ -1,10 +1,11 @@
|
||||
;; ============================================================
|
||||
;; test_omniflo.lsp - Automatischer Integrationstest fuer Omniflo
|
||||
;;
|
||||
;; Laedt Testfaelle aus tests/testdata/omniflo_tests.json,
|
||||
;; Laedt Testfaelle aus tests/testdata/omniflo_tests.json und
|
||||
;; fuegt je einen Bogen und Weiche jeden Typs in verschiedenen
|
||||
;; Hoehen ein (Versatz in x-Richtung je 200mm) und exportiert
|
||||
;; die CSV-Datei nach tests/output/omniflo_export.csv.
|
||||
;; Hoehen ein (Versatz in x-Richtung je 200mm). Ergebnisse werden
|
||||
;; ueber omniflo:export-results nach tests/output/omniflo_results.json
|
||||
;; geschrieben (Aufruf durch SSG_RUN_ALL_TESTS oder manuell).
|
||||
;;
|
||||
;; Voraussetzungen:
|
||||
;; - SSG_LIB geladen (OmniModulInsert.lsp, export.lsp)
|
||||
@@ -104,14 +105,13 @@
|
||||
;; ============================================================
|
||||
;; C:TEST_OMNIFLO
|
||||
;;
|
||||
;; Liest omniflo_tests.json, fuegt alle Testbloecke ein,
|
||||
;; exportiert CSV nach tests/output/.
|
||||
;; Liest omniflo_tests.json und fuegt alle Testbloecke ein.
|
||||
;; ============================================================
|
||||
(defun c:TEST_OMNIFLO (/ tests-json-pfad testfaelle
|
||||
x-val y-val
|
||||
idx eintrag sivasnr hoehe-str drehung-str
|
||||
description insert-pt anzahl-ok anzahl-fehler
|
||||
results-list tests-out-dir old-results)
|
||||
results-list)
|
||||
|
||||
(ssg-start "TEST_OMNIFLO"
|
||||
'(("OSMODE") ("ATTREQ") ("ATTDIA")))
|
||||
@@ -217,24 +217,10 @@
|
||||
;; Ergebnisse in globaler Variable speichern
|
||||
(setq *omniflo-test-results* (reverse results-list))
|
||||
|
||||
;; CSV-Export nach tests/output/
|
||||
(setq tests-out-dir
|
||||
(strcat (getenv "DXFMAKRO") "/tests/output"))
|
||||
(vl-mkdir tests-out-dir)
|
||||
|
||||
(setq old-results (getenv "DXFM_RESULTS"))
|
||||
(setenv "DXFM_RESULTS" tests-out-dir)
|
||||
|
||||
(princ "\n[TEST_OMNIFLO] Starte OMNI_EXPORT -> tests/output/omniflo_export.csv")
|
||||
(c:OMNI_EXPORT)
|
||||
|
||||
(setenv "DXFM_RESULTS" (if old-results old-results ""))
|
||||
|
||||
(ssg-end)
|
||||
|
||||
(princ "\n================================================================")
|
||||
(princ "\n TEST_OMNIFLO abgeschlossen.")
|
||||
(princ (strcat "\n CSV: " tests-out-dir "/omniflo_export.csv"))
|
||||
(princ "\n================================================================")
|
||||
(princ)
|
||||
)
|
||||
|
||||
+58
-95
@@ -6,11 +6,11 @@ test_omniflo.py - Validiert Omniflo Bogen/Weiche Export.
|
||||
Prueft:
|
||||
1. CSV-Inhalt gegen Testdefinitionen (Unit-Test, ohne BricsCAD)
|
||||
2. Merkmale-Werte gegen JSON-Katalog
|
||||
3. Korrekte Anzahl und Reihenfolge der Elemente
|
||||
4. Integrationstest: liest CSV aus BricsCAD-Lauf (falls vorhanden)
|
||||
3. Korrekte Anzahl und Reihenfolge der Elemente inkl. Omniflo-Sum-Zeile
|
||||
4. Integrationstest: liest omniflo_results.json aus BricsCAD-Lauf (falls vorhanden)
|
||||
5. Referenz-Vergleich: CSV-Export aus export_csv.py gegen abgenommene Referenz-CSV
|
||||
"""
|
||||
|
||||
import csv
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
@@ -99,6 +99,9 @@ class TestOmnifloExportUnit:
|
||||
drehung=float(tc["drehung"]),
|
||||
))
|
||||
self.items = process_blocks(self.blocks, self.lookup)
|
||||
# Item-Zeilen ohne die abschliessende "Omniflo Sum"-Zeile
|
||||
self.element_items = [it for it in self.items if it["teileart"] != "Omniflo Sum"]
|
||||
self.sum_item = next((it for it in self.items if it["teileart"] == "Omniflo Sum"), None)
|
||||
|
||||
# CSV in output speichern
|
||||
out_dir = _output_dir()
|
||||
@@ -112,27 +115,40 @@ class TestOmnifloExportUnit:
|
||||
|
||||
def test_item_count_matches_testcases(self):
|
||||
"""Anzahl exportierter Elemente muss Anzahl Testfaelle entsprechen."""
|
||||
assert len(self.items) == len(self.tests), (
|
||||
f"Erwartet {len(self.tests)} Elemente, erhalten {len(self.items)}"
|
||||
assert len(self.element_items) == len(self.tests), (
|
||||
f"Erwartet {len(self.tests)} Elemente, erhalten {len(self.element_items)}"
|
||||
)
|
||||
|
||||
def test_numbering_starts_at_one(self):
|
||||
"""Element-Nummerierung muss bei 1 beginnen."""
|
||||
assert self.items[0]["nr"] == 1, (
|
||||
f"Erstes Element: nr={self.items[0]['nr']}, erwartet 1"
|
||||
assert self.element_items[0]["nr"] == 1, (
|
||||
f"Erstes Element: nr={self.element_items[0]['nr']}, erwartet 1"
|
||||
)
|
||||
|
||||
def test_numbering_is_consecutive(self):
|
||||
"""Element-Nummern muessen lueckenlos aufsteigen."""
|
||||
"""Element-Nummern muessen lueckenlos aufsteigen (inkl. abschliessender Sum-Zeile)."""
|
||||
for i, item in enumerate(self.items):
|
||||
assert item["nr"] == i + 1, (
|
||||
f"Element {i}: nr={item['nr']}, erwartet {i + 1}"
|
||||
)
|
||||
|
||||
def test_sum_zeile_angehaengt(self):
|
||||
"""Nach allen Omniflo-Elementen muss eine 'Omniflo Sum'-Zeile folgen."""
|
||||
assert self.sum_item is not None, "Keine 'Omniflo Sum'-Zeile erzeugt"
|
||||
assert self.sum_item["nr"] == len(self.items)
|
||||
assert self.sum_item["teileid"] == "autogenerated_of_json"
|
||||
m = self.sum_item["merkmale"]
|
||||
bogen_tests = [tc for tc in self.tests if tc["type"] == "bogen"]
|
||||
weiche_tests = [tc for tc in self.tests if tc["type"] == "weiche"]
|
||||
assert m["Anzahl Bögen"] == len(bogen_tests)
|
||||
assert (m["Anzahl Weichengrundkörper"] + m["Anzahl Einwegweichen"]
|
||||
+ m["Anzahl Deltaweichen"] + m["Anzahl Doppelweichen und Dreiwegeweichen"]
|
||||
+ m["Anzahl Sternweichen"]) == len(weiche_tests)
|
||||
|
||||
def test_bogen_teileart(self):
|
||||
"""Boegen muessen TeileArt 'Omniflo Kurve' haben."""
|
||||
bogen_tests = [tc for tc in self.tests if tc["type"] == "bogen"]
|
||||
bogen_items = [it for it in self.items if it["teileart"] == "Omniflo Kurve"]
|
||||
bogen_items = [it for it in self.element_items if it["teileart"] == "Omniflo Kurve"]
|
||||
assert len(bogen_items) == len(bogen_tests), (
|
||||
f"Erwartet {len(bogen_tests)} Boegen, erhalten {len(bogen_items)}"
|
||||
)
|
||||
@@ -140,7 +156,7 @@ class TestOmnifloExportUnit:
|
||||
def test_weiche_teileart(self):
|
||||
"""Weichen muessen TeileArt 'Omniflo Weiche' haben."""
|
||||
weiche_tests = [tc for tc in self.tests if tc["type"] == "weiche"]
|
||||
weiche_items = [it for it in self.items if it["teileart"] == "Omniflo Weiche"]
|
||||
weiche_items = [it for it in self.element_items if it["teileart"] == "Omniflo Weiche"]
|
||||
assert len(weiche_items) == len(weiche_tests), (
|
||||
f"Erwartet {len(weiche_tests)} Weichen, erhalten {len(weiche_items)}"
|
||||
)
|
||||
@@ -148,7 +164,7 @@ class TestOmnifloExportUnit:
|
||||
def test_bogen_merkmale_felder(self):
|
||||
"""Boegen-Merkmale muessen alle Pflichtfelder enthalten."""
|
||||
required = {"Kurvenwinkel", "Radius", "Höhe", "Drehung", "SivasNummer"}
|
||||
for item in self.items:
|
||||
for item in self.element_items:
|
||||
if item["teileart"] != "Omniflo Kurve":
|
||||
continue
|
||||
merkmale = item["merkmale"]
|
||||
@@ -161,7 +177,7 @@ class TestOmnifloExportUnit:
|
||||
"""Weichen-Merkmale muessen alle Pflichtfelder enthalten."""
|
||||
required = {"Weichentyp", "Richtung", "Weichenwinkel",
|
||||
"Höhe", "Drehung", "Antrieb Kurve", "SivasNummer"}
|
||||
for item in self.items:
|
||||
for item in self.element_items:
|
||||
if item["teileart"] != "Omniflo Weiche":
|
||||
continue
|
||||
merkmale = item["merkmale"]
|
||||
@@ -174,7 +190,7 @@ class TestOmnifloExportUnit:
|
||||
"""Bogen-Merkmale muessen mit JSON-Katalog uebereinstimmen."""
|
||||
for tc, item in zip(
|
||||
[t for t in self.tests if t["type"] == "bogen"],
|
||||
[it for it in self.items if it["teileart"] == "Omniflo Kurve"]
|
||||
[it for it in self.element_items if it["teileart"] == "Omniflo Kurve"]
|
||||
):
|
||||
sivasnr = tc["sivasnr"]
|
||||
assert sivasnr in self.lookup, f"SivasNr {sivasnr} nicht im Katalog"
|
||||
@@ -200,7 +216,7 @@ class TestOmnifloExportUnit:
|
||||
"""Weichen-Merkmale muessen mit JSON-Katalog uebereinstimmen."""
|
||||
for tc, item in zip(
|
||||
[t for t in self.tests if t["type"] == "weiche"],
|
||||
[it for it in self.items if it["teileart"] == "Omniflo Weiche"]
|
||||
[it for it in self.element_items if it["teileart"] == "Omniflo Weiche"]
|
||||
):
|
||||
sivasnr = tc["sivasnr"]
|
||||
assert sivasnr in self.lookup, f"SivasNr {sivasnr} nicht im Katalog"
|
||||
@@ -229,7 +245,7 @@ class TestOmnifloExportUnit:
|
||||
|
||||
def test_bogen_hoehe_als_string(self):
|
||||
"""Bogen-Hoehe muss als String vorliegen (z.B. '2000', nicht 2000)."""
|
||||
for item in self.items:
|
||||
for item in self.element_items:
|
||||
if item["teileart"] != "Omniflo Kurve":
|
||||
continue
|
||||
assert isinstance(item["merkmale"]["Höhe"], str), (
|
||||
@@ -239,7 +255,7 @@ class TestOmnifloExportUnit:
|
||||
|
||||
def test_weiche_hoehe_als_string(self):
|
||||
"""Weichen-Hoehe muss als String vorliegen."""
|
||||
for item in self.items:
|
||||
for item in self.element_items:
|
||||
if item["teileart"] != "Omniflo Weiche":
|
||||
continue
|
||||
assert isinstance(item["merkmale"]["Höhe"], str), (
|
||||
@@ -248,7 +264,7 @@ class TestOmnifloExportUnit:
|
||||
|
||||
def test_kurvenwinkel_als_float(self):
|
||||
"""Kurvenwinkel muss als Float vorliegen (90.0, nicht 90)."""
|
||||
for item in self.items:
|
||||
for item in self.element_items:
|
||||
if item["teileart"] == "Omniflo Kurve":
|
||||
assert isinstance(item["merkmale"]["Kurvenwinkel"], float), (
|
||||
f"{item['bezeichnung']}: Kurvenwinkel muss float sein"
|
||||
@@ -273,90 +289,37 @@ class TestOmnifloExportUnit:
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Integrations-Tests: CSV aus BricsCAD-Lauf validieren
|
||||
# Integrations-Tests: Omniflo-Ergebnisse aus BricsCAD-Lauf validieren
|
||||
# (tests/output/omniflo_results.json, geschrieben von omniflo:export-results)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestOmnifloExportCSV:
|
||||
"""Validiert CSV-Datei aus dem BricsCAD-Lauf (c:TEST_OMNIFLO_EXPORT)."""
|
||||
class TestOmnifloResults:
|
||||
"""Validiert omniflo_results.json aus dem BricsCAD-Lauf (c:TEST_OMNIFLO)."""
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup(self):
|
||||
csv_path = os.path.join(_output_dir(), "omniflo_export.csv")
|
||||
if not os.path.exists(csv_path):
|
||||
pytest.skip(
|
||||
"omniflo_export.csv nicht vorhanden - "
|
||||
"TEST_OMNIFLO_EXPORT in BricsCAD ausfuehren"
|
||||
)
|
||||
self.csv_path = csv_path
|
||||
self.rows = []
|
||||
with open(csv_path, "r", encoding="utf-8") as f:
|
||||
reader = csv.reader(f, delimiter=";")
|
||||
self.header = next(reader)
|
||||
for row in reader:
|
||||
self.rows.append(row)
|
||||
def test_all_tests_executed(self, omniflo_testdata, omniflo_results):
|
||||
"""Alle definierten Testfaelle muessen ausgefuehrt worden sein."""
|
||||
expected_sivas = {t["sivasnr"] for t in omniflo_testdata}
|
||||
result_sivas = {r["sivasnr"] for r in omniflo_results}
|
||||
missing = expected_sivas - result_sivas
|
||||
assert not missing, f"Nicht ausgefuehrte Tests: {missing}"
|
||||
|
||||
def test_header_felder(self):
|
||||
"""CSV-Header muss korrekte Spaltenbezeichnungen haben."""
|
||||
assert self.header[0] == "Elementnummer"
|
||||
assert self.header[1] == "TeileArt"
|
||||
assert self.header[2] == "TeileId"
|
||||
assert self.header[3] == "Bezeichnung"
|
||||
assert self.header[4] == "Anzahl"
|
||||
assert self.header[5] == "Merkmale"
|
||||
|
||||
def test_mindest_anzahl_zeilen(self):
|
||||
"""CSV muss mindestens so viele Zeilen haben wie Testfaelle."""
|
||||
tests = _load_omniflo_tests()
|
||||
assert len(self.rows) >= len(tests), (
|
||||
f"CSV hat {len(self.rows)} Zeilen, "
|
||||
f"erwartet mind. {len(tests)}"
|
||||
)
|
||||
|
||||
def test_anzahl_spalte_ist_eins(self):
|
||||
"""Anzahl-Spalte muss fuer alle Zeilen '1' sein."""
|
||||
for i, row in enumerate(self.rows):
|
||||
if len(row) < 5:
|
||||
continue
|
||||
assert row[4] == "1", (
|
||||
f"Zeile {i + 2}: Anzahl={row[4]}, erwartet 1"
|
||||
def test_all_executed_successfully(self, omniflo_results):
|
||||
"""Alle Ergebnisse muessen Status 'OK' haben."""
|
||||
for result in omniflo_results:
|
||||
assert result["status"] == "OK", (
|
||||
f'{result["test_id"]} ({result["sivasnr"]}): Status={result["status"]}'
|
||||
)
|
||||
|
||||
def test_merkmale_sind_valides_json(self):
|
||||
"""Merkmale-Spalte muss valides JSON enthalten."""
|
||||
for i, row in enumerate(self.rows):
|
||||
if len(row) < 6:
|
||||
def test_hoehe_matches(self, omniflo_testdata, omniflo_results):
|
||||
"""Eingefuegte Hoehe muss dem Testfall entsprechen."""
|
||||
results_by_sivas = {r["sivasnr"]: r for r in omniflo_results}
|
||||
for test in omniflo_testdata:
|
||||
result = results_by_sivas.get(test["sivasnr"])
|
||||
if not result:
|
||||
continue
|
||||
try:
|
||||
json.loads(row[5])
|
||||
except json.JSONDecodeError as e:
|
||||
pytest.fail(
|
||||
f"Zeile {i + 2}: Merkmale ist kein valides JSON: {e}\n"
|
||||
f" Inhalt: {row[5][:100]}"
|
||||
)
|
||||
|
||||
def test_bogen_merkmale_felder_in_csv(self):
|
||||
"""Bogen-Zeilen muessen Pflichtfelder in Merkmale haben."""
|
||||
required = {"Kurvenwinkel", "Radius", "Höhe", "Drehung", "SivasNummer"}
|
||||
for i, row in enumerate(self.rows):
|
||||
if len(row) < 6 or row[1].strip('"') != "Omniflo Kurve":
|
||||
continue
|
||||
merkmale = json.loads(row[5])
|
||||
missing = required - set(merkmale.keys())
|
||||
assert not missing, (
|
||||
f"Zeile {i + 2}: Fehlende Bogen-Felder: {missing}"
|
||||
)
|
||||
|
||||
def test_weiche_merkmale_felder_in_csv(self):
|
||||
"""Weichen-Zeilen muessen Pflichtfelder in Merkmale haben."""
|
||||
required = {"Weichentyp", "Richtung", "Weichenwinkel",
|
||||
"Höhe", "Drehung", "Antrieb Kurve", "SivasNummer"}
|
||||
for i, row in enumerate(self.rows):
|
||||
if len(row) < 6 or row[1].strip('"') != "Omniflo Weiche":
|
||||
continue
|
||||
merkmale = json.loads(row[5])
|
||||
missing = required - set(merkmale.keys())
|
||||
assert not missing, (
|
||||
f"Zeile {i + 2}: Fehlende Weichen-Felder: {missing}"
|
||||
assert result["hoehe"] == str(int(test["hoehe"])), (
|
||||
f'{test["sivasnr"]}: Hoehe erwartet={test["hoehe"]}, '
|
||||
f'ist={result["hoehe"]}'
|
||||
)
|
||||
|
||||
|
||||
@@ -377,7 +340,7 @@ def _normalize_row(line):
|
||||
|
||||
|
||||
class TestOmnifloReferenceCSV:
|
||||
"""Vergleicht den erzeugten Unit-Test-CSV-Export mit einer Referenzdatei."""
|
||||
"""Vergleicht den von export_csv.py erzeugten CSV-Export mit einer Referenzdatei."""
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup(self):
|
||||
|
||||
+188
-29
@@ -20,8 +20,50 @@
|
||||
;; Aufruf in BricsCAD:
|
||||
;; (load (strcat (getenv "DXFMAKRO") "/tests/test_run_all.lsp"))
|
||||
;; SSG_RUN_ALL_TESTS
|
||||
;;
|
||||
;; Jedes Testmodul laeuft in einer eigenen, neuen Zeichnung (statt die
|
||||
;; aktuelle zu leeren/wiederzuverwenden). SSG_RUN_ALL_TESTS fragt zu
|
||||
;; Beginn interaktiv nach dem gewuenschten Modus:
|
||||
;; Kontroll-Modus (Default): Zeichnung wird gespeichert, auf den
|
||||
;; Inhalt gezoomt und bleibt als Tab offen - kein CSV/Sivas-Export.
|
||||
;; Am Ende sind alle Testzeichnungen gleichzeitig offen und koennen
|
||||
;; direkt begutachtet werden. CSV/Sivas-Export danach bei Bedarf
|
||||
;; manuell ueber TEST_EXPORT_ALL (tests/test_export_all.lsp).
|
||||
;; Automatik-Modus: Zeichnung wird gespeichert, per EXPORTCSV/
|
||||
;; EXPORTSIVAS exportiert und danach sofort wieder geschlossen -
|
||||
;; kompletter Lauf ohne manuellen Zusatzschritt, aber ohne
|
||||
;; gleichzeitige visuelle Kontrolle aller Zeichnungen am Ende.
|
||||
;; ============================================================
|
||||
|
||||
(vl-load-com)
|
||||
|
||||
|
||||
;; --- Auf Fertigstellung einer asynchron geschriebenen Datei warten ---
|
||||
;; csv:run-export startet Python per startapp (fire-and-forget). Pollt
|
||||
;; bis die Datei existiert und ihre Groesse ueber ein Intervall stabil
|
||||
;; bleibt, oder bis timeout-sec erreicht ist. Rueckgabe: T bei Erfolg.
|
||||
;; (Analog zu export_all:wait-for-file aus tests/test_export_all.lsp,
|
||||
;; hier lokal, da hier direkt auf der offenen Zeichnung exportiert wird
|
||||
;; statt auf einer bereits gespeicherten Datei.)
|
||||
(defun testrun:wait-for-file (path timeout-sec / elapsed size1 size2 done)
|
||||
(setq elapsed 0.0 done nil)
|
||||
(while (and (not done) (< elapsed timeout-sec))
|
||||
(if (findfile path)
|
||||
(progn
|
||||
(setq size1 (vl-file-size path))
|
||||
(command "_.DELAY" 400)
|
||||
(setq size2 (vl-file-size path))
|
||||
(if (and size1 size2 (= size1 size2) (> size1 0))
|
||||
(setq done T)
|
||||
)
|
||||
)
|
||||
(command "_.DELAY" 400)
|
||||
)
|
||||
(setq elapsed (+ elapsed 0.4))
|
||||
)
|
||||
done
|
||||
)
|
||||
|
||||
|
||||
;; --- Hilfsfunktion: String-Wert aus JSON-Objekt-Zeile lesen ---
|
||||
;; Sucht "key": "value" in obj-str und gibt value zurueck.
|
||||
@@ -99,14 +141,17 @@
|
||||
)
|
||||
|
||||
|
||||
(defun c:SSG_RUN_ALL_TESTS ( / tests-pfad tests-out-dir
|
||||
(defun c:SSG_RUN_ALL_TESTS ( / tests-pfad tests-out-dir app auto-export
|
||||
idx anz entry name save-fmt module
|
||||
namen befehl datei export-fn
|
||||
out-path old-json test-result)
|
||||
namen befehl datei export-fn new-doc
|
||||
out-path dwg-pfad dxf-pfad old-json test-result
|
||||
old-filenames csv-name sivas-name csv-pfad
|
||||
sivas-pfad csv-ok sivas-ok)
|
||||
|
||||
(setq tests-pfad (strcat (getenv "DXFMAKRO") "/tests"))
|
||||
(setq tests-out-dir (strcat tests-pfad "/output"))
|
||||
(vl-mkdir tests-out-dir)
|
||||
(setq app (vlax-get-acad-object))
|
||||
|
||||
;; Testliste aus alltests.json laden
|
||||
(setq *test-module* (alltests:load tests-pfad))
|
||||
@@ -120,15 +165,29 @@
|
||||
(setq anz (length *test-module*))
|
||||
(setq idx 0)
|
||||
|
||||
;; Modus abfragen: Kontrolle (Zeichnungen bleiben offen, kein Export)
|
||||
;; oder Automatik (Speichern + CSV/Sivas-Export + Schliessen je Test).
|
||||
;; ssg-ques gibt T zurueck wenn "No" gewaehlt wurde - deshalb "not".
|
||||
;; Default = Nein -> Kontroll-Modus (nicht-destruktiver Default).
|
||||
(setq auto-export
|
||||
(not (ssg-ques
|
||||
"Zeichnungen automatisch speichern, exportieren (CSV/Sivas) und schliessen"
|
||||
nil))
|
||||
)
|
||||
|
||||
(princ "\n")
|
||||
(princ "\n================================================================")
|
||||
(princ "\n SSG_RUN_ALL_TESTS")
|
||||
(princ (strcat "\n " (itoa anz) " Testmodule (aus alltests.json)"))
|
||||
(princ (if auto-export
|
||||
"\n Modus: Automatik (speichern + CSV/Sivas-Export + schliessen)"
|
||||
"\n Modus: Kontrolle (Zeichnungen bleiben offen, kein Export)"
|
||||
))
|
||||
(princ "\n================================================================")
|
||||
|
||||
;; Tests laden und ausfuehren
|
||||
;; Vor jedem Test werden alle Entities geloescht, damit jeder
|
||||
;; Testlauf eine eigene saubere Zeichnung erzeugt.
|
||||
;; Jeder Test bekommt eine eigene neue Zeichnung (statt die aktuelle
|
||||
;; zu leeren/wiederzuverwenden) und bleibt danach als Tab offen.
|
||||
(foreach entry *test-module*
|
||||
(setq idx (1+ idx))
|
||||
(setq name (cdr (assoc "name" entry)))
|
||||
@@ -158,19 +217,18 @@
|
||||
(princ " [kein Speichern]")
|
||||
)
|
||||
|
||||
;; Zeichnung leeren fuer sauberen Testlauf
|
||||
;; Frozen Layer werden von _.ERASE _ALL ignoriert - deshalb
|
||||
;; erst alle Layer auftauen, entsperren und einschalten.
|
||||
(command "_.LAYER" "_Thaw" "*" "_Unlock" "*" "_On" "*" "")
|
||||
(if (ssget "_X")
|
||||
(command "_.ERASE" "_ALL" "")
|
||||
)
|
||||
;; PURGE mehrfach, da verschachtelte Block-Referenzen
|
||||
;; erst nach mehreren Durchlaeufen entfernt werden
|
||||
(repeat 5
|
||||
(command "_.PURGE" "_ALL" "" "_N")
|
||||
)
|
||||
;; Block-Library-Flag zuruecksetzen
|
||||
;; Neue, leere Zeichnung fuer diesen Test anlegen und aktivieren
|
||||
(setq new-doc (vla-add (vla-get-documents app)))
|
||||
(vla-put-activedocument app new-doc)
|
||||
;; vf_core.lsp/Gefaellestrecke.lsp setzen die globalen Variablen "doc"
|
||||
;; und "modelspace" nur EINMAL, beim ersten Laden des Moduls (Top-Level-
|
||||
;; Code, kein erneuter Aufruf durch ssg-ensure). Ohne diese Aktualisierung
|
||||
;; wuerden VarioFoerderer/Gefaellestrecke weiterhin in das allererste
|
||||
;; Dokument der Sitzung zeichnen statt in die hier neu angelegte
|
||||
;; Zeichnung - die neuen Tabs blieben leer.
|
||||
(setq doc new-doc)
|
||||
(setq modelspace (vla-get-modelspace new-doc))
|
||||
;; Block-Library-Flag zuruecksetzen (Bloecke sind pro Zeichnung neu zu laden)
|
||||
(setq *lib-initialized* nil)
|
||||
|
||||
(if (findfile datei)
|
||||
@@ -193,19 +251,117 @@
|
||||
;; JSON-Export (immer)
|
||||
(apply export-fn (list tests-out-dir))
|
||||
|
||||
;; Datei speichern je nach save-fmt aus alltests.json
|
||||
(setq out-path (strcat tests-out-dir "/" name "_tests"))
|
||||
(cond
|
||||
((and save-fmt (= (strcase save-fmt) "DXF"))
|
||||
(command "_.DXFOUT" out-path 6)
|
||||
(princ (strcat "\n DXF gespeichert: " out-path ".dxf"))
|
||||
|
||||
(if auto-export
|
||||
|
||||
;; ==========================================================
|
||||
;; AUTOMATIK-MODUS: speichern (vereinfacht, kein Tab-Naming-
|
||||
;; Trick noetig) + CSV/Sivas-Export + Zeichnung schliessen.
|
||||
;; ==========================================================
|
||||
(progn
|
||||
(cond
|
||||
((and save-fmt (= (strcase save-fmt) "DWG"))
|
||||
(setq dwg-pfad (strcat out-path ".dwg"))
|
||||
(vla-saveas new-doc dwg-pfad)
|
||||
(princ (strcat "\n DWG gespeichert: " dwg-pfad))
|
||||
)
|
||||
((and save-fmt (= (strcase save-fmt) "DXF"))
|
||||
(command "_.DXFOUT" out-path 6)
|
||||
(princ (strcat "\n DXF gespeichert: " out-path ".dxf"))
|
||||
)
|
||||
(T
|
||||
(princ "\n (kein Speichern der Zeichnung)")
|
||||
)
|
||||
)
|
||||
|
||||
;; CSV/Sivas-Export direkt auf der offenen Zeichnung.
|
||||
;; *export-filenames* temporaer umbiegen, damit jeder Test
|
||||
;; seine eigenen CSV-Ergebnisse bekommt (statt der fixen
|
||||
;; Standardnamen "export.csv"/"export_sivas.csv").
|
||||
(setq csv-name (strcat name "_tests_export.csv"))
|
||||
(setq sivas-name (strcat name "_tests_sivas.csv"))
|
||||
(setq csv-pfad (strcat tests-out-dir "/" csv-name))
|
||||
(setq sivas-pfad (strcat tests-out-dir "/" sivas-name))
|
||||
(if (findfile csv-pfad) (vl-file-delete csv-pfad))
|
||||
(if (findfile sivas-pfad) (vl-file-delete sivas-pfad))
|
||||
|
||||
(setq old-filenames *export-filenames*)
|
||||
(setq *export-filenames*
|
||||
(list
|
||||
(cons "raw_json_datei" (cdr (assoc "raw_json_datei" old-filenames)))
|
||||
(cons "sivas_script" (cdr (assoc "sivas_script" old-filenames)))
|
||||
(cons "sivas_csv" sivas-name)
|
||||
(cons "csv_script" (cdr (assoc "csv_script" old-filenames)))
|
||||
(cons "csv_csv" csv-name)
|
||||
)
|
||||
)
|
||||
|
||||
(princ (strcat "\n CSV-Export -> " csv-name))
|
||||
(c:EXPORTCSV)
|
||||
(setq csv-ok (testrun:wait-for-file csv-pfad 30.0))
|
||||
(princ (if csv-ok "\n CSV OK" "\n CSV TIMEOUT/FEHLER"))
|
||||
|
||||
(princ (strcat "\n Sivas-Export -> " sivas-name))
|
||||
(c:EXPORTSIVAS)
|
||||
(setq sivas-ok (testrun:wait-for-file sivas-pfad 30.0))
|
||||
(princ (if sivas-ok "\n Sivas OK" "\n Sivas TIMEOUT/FEHLER"))
|
||||
|
||||
(setq *export-filenames* old-filenames)
|
||||
|
||||
;; Zeichnung schliessen: der urspruenglich schon offene Tab
|
||||
;; bleibt unangetastet, BricsCAD erzeugt daher kein neues
|
||||
;; Blank-Dokument samt Modul-/Menue-Reload.
|
||||
(vla-close new-doc :vlax-false)
|
||||
)
|
||||
((and save-fmt (= (strcase save-fmt) "DWG"))
|
||||
(command "_.-SAVEAS" "R2013" (strcat out-path ".dwg"))
|
||||
(princ (strcat "\n DWG gespeichert: " out-path ".dwg"))
|
||||
)
|
||||
(T
|
||||
(princ "\n (kein Speichern der Zeichnung)")
|
||||
|
||||
;; ==========================================================
|
||||
;; KONTROLL-MODUS: speichern, auf Inhalt zoomen, Tab offen
|
||||
;; lassen (bisheriges Verhalten).
|
||||
;; ==========================================================
|
||||
(progn
|
||||
(cond
|
||||
((and save-fmt (= (strcase save-fmt) "DWG"))
|
||||
;; Direkt als DWG speichern - Tab bleibt offen und benannt.
|
||||
;; Ueber ActiveX statt "_.-SAVEAS" "R2013" ...: Das Versions-
|
||||
;; Keyword "R2013" wird von BricsCADs Kommandozeilen-SAVEAS
|
||||
;; nicht zuverlaessig erkannt, wodurch der Befehl auf eine
|
||||
;; ungueltige Eingabe wartet und nie eine Datei schreibt
|
||||
;; (nachfolgende LISP-Eingaben landen dann im haengenden
|
||||
;; Prompt). vla-saveas ist deterministisch.
|
||||
(setq dwg-pfad (strcat out-path ".dwg"))
|
||||
(vla-saveas new-doc dwg-pfad)
|
||||
(princ (strcat "\n DWG gespeichert: " dwg-pfad))
|
||||
)
|
||||
((and save-fmt (= (strcase save-fmt) "DXF"))
|
||||
;; Erst als DWG speichern (nur fuer den Tab-Namen) und DXF
|
||||
;; exportieren. Danach die Zeichnung schliessen (entsperrt die
|
||||
;; DWG-Datei), die temporaere DWG loeschen und stattdessen die
|
||||
;; eigentlich angeforderte DXF-Datei erneut oeffnen - der Tab
|
||||
;; ist dann korrekt an die DXF gebunden, keine ungewollte DWG
|
||||
;; bleibt liegen.
|
||||
(setq dwg-pfad (strcat out-path ".dwg"))
|
||||
(setq dxf-pfad (strcat out-path ".dxf"))
|
||||
(vla-saveas new-doc dwg-pfad)
|
||||
(command "_.DXFOUT" out-path 6)
|
||||
(vla-close new-doc :vlax-false)
|
||||
(if (findfile dwg-pfad) (vl-file-delete dwg-pfad))
|
||||
(setq new-doc (vla-open (vla-get-documents app) dxf-pfad))
|
||||
(vla-put-activedocument app new-doc)
|
||||
(setq doc new-doc)
|
||||
(setq modelspace (vla-get-modelspace new-doc))
|
||||
(princ (strcat "\n DXF gespeichert: " dxf-pfad))
|
||||
)
|
||||
(T
|
||||
;; Kein Speichern angefordert: keine Datei erzeugen, Tab
|
||||
;; bleibt beim generischen Namen ("ZeichnungN").
|
||||
(princ "\n (kein Speichern der Zeichnung)")
|
||||
)
|
||||
)
|
||||
|
||||
;; Ansicht auf den gesamten Inhalt zoomen, Tab bleibt offen
|
||||
;; (naechster Test startet in einer neuen Zeichnung).
|
||||
(command "_.ZOOM" "_Extents")
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -216,6 +372,9 @@
|
||||
(princ "\n\n================================================================")
|
||||
(princ "\n SSG_RUN_ALL_TESTS abgeschlossen.")
|
||||
(princ (strcat "\n Ergebnisse in: " (getenv "DXFMAKRO") "/tests/output/"))
|
||||
(if (not auto-export)
|
||||
(princ "\n CSV/Sivas-Export: TEST_EXPORT_ALL manuell aufrufen (Menue: Tests -> Export CSV/Sivas ALL)")
|
||||
)
|
||||
(princ "\n================================================================")
|
||||
(princ)
|
||||
)
|
||||
|
||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"y_abstand": 400
|
||||
"y_abstand": 600
|
||||
},
|
||||
{
|
||||
"test_id": "VF_Auf_0",
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
[
|
||||
{
|
||||
"deltaL_mm": 10000,
|
||||
"winkel_grad": 3.0,
|
||||
"z_start_mm": 5000
|
||||
},
|
||||
{
|
||||
"test_id": "M1_hz000",
|
||||
"modus": 1,
|
||||
"hz_grad": 0.0,
|
||||
"start_mm": [0, 0, 5000],
|
||||
"as_seite": "links",
|
||||
"es_seite": "links"
|
||||
},
|
||||
{
|
||||
"test_id": "M1_hz090",
|
||||
"modus": 1,
|
||||
"hz_grad": 90.0,
|
||||
"start_mm": [15000, 0, 5000],
|
||||
"as_seite": "links",
|
||||
"es_seite": "links"
|
||||
},
|
||||
{
|
||||
"test_id": "M1_hz180",
|
||||
"modus": 1,
|
||||
"hz_grad": 180.0,
|
||||
"start_mm": [0, -15000, 5000],
|
||||
"as_seite": "links",
|
||||
"es_seite": "links"
|
||||
},
|
||||
{
|
||||
"test_id": "M1_hz270",
|
||||
"modus": 1,
|
||||
"hz_grad": 270.0,
|
||||
"start_mm": [15000, -15000, 5000],
|
||||
"as_seite": "links",
|
||||
"es_seite": "links"
|
||||
},
|
||||
{
|
||||
"test_id": "M2_hz000",
|
||||
"modus": 2,
|
||||
"hz_grad": 0.0,
|
||||
"start_mm": [0, -35000, 5000],
|
||||
"linie_start_mm": [0, -35000, 0],
|
||||
"linie_ende_mm": [10000, -35000, 0],
|
||||
"as_seite": "links",
|
||||
"es_seite": "links"
|
||||
},
|
||||
{
|
||||
"test_id": "M3_hz270",
|
||||
"modus": 3,
|
||||
"richtung": "Sued",
|
||||
"start_mm": [0, -50000, 5000],
|
||||
"deltaH_quelle": "M2_hz000",
|
||||
"deltaL_quelle": "M2_hz000",
|
||||
"as_seite": "links",
|
||||
"es_seite": "links"
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user