This commit is contained in:
2026-06-25 12:57:33 +02:00
19 changed files with 1483 additions and 534951 deletions
+27 -12
View File
@@ -133,18 +133,17 @@
;; ============================================================ ;; ============================================================
(defun c:SSG_BLOCKEDIT ( / ss ent ed bname attribs teileart) (defun c:SSG_BLOCKEDIT ( / ss ent ed bname attribs teileart)
;; Implied Selection pruefen (Block muss vorselektiert sein) ;; Block ermitteln: zuerst Implied Selection, dann Picking
(setq ss (ssget "I")) (setq ss (ssget "I"))
(if (or (null ss) (/= (sslength ss) 1))
(progn
(princ "\nBlock auswaehlen: ")
(setq ss (ssget ":S" '((0 . "INSERT"))))
)
)
(if (null ss) (if (null ss)
(progn (progn
(princ "\nBitte zuerst einen Block auswaehlen.") (princ "\nKein Block ausgewaehlt.")
(princ)
(exit)
)
)
(if (/= (sslength ss) 1)
(progn
(princ "\nBitte genau einen Block auswaehlen.")
(princ) (princ)
(exit) (exit)
) )
@@ -191,14 +190,30 @@
(c:OMNI_EDIT) (c:OMNI_EDIT)
) )
;; Unbekannter Blocktyp -> Standard BEDIT ;; Unbekannter Blocktyp -> natives EATTEDIT (Attribut-Editor)
(t (t
(princ (strcat "\nBlock bearbeiten (BEDIT): " bname)) (princ (strcat "\nAttribute bearbeiten: " bname))
(command "_.BEDIT" bname) (command "_.EATTEDIT")
) )
) )
(princ) (princ)
) )
;; ============================================================
;; C:EATTEDIT - Abfangen des eingebauten Doppelklick-Befehls
;; UNDEFINE zwingt BricsCAD, auch interne Aufrufe ueber LISP
;; zu leiten. Fallback auf _.EATTEDIT (Dot-Prefix = nativ,
;; kein Loop) fuer unbekannte Bloecke.
;; ============================================================
(defun c:EATTEDIT ()
(c:SSG_BLOCKEDIT)
)
(if (not *ssg-eattedit-undefined*)
(progn
(command "_.UNDEFINE" "EATTEDIT")
(setq *ssg-eattedit-undefined* t)
)
)
(princ "\nSSG_LIB_Commands.lsp geladen - Dummy-Befehle registriert.") (princ "\nSSG_LIB_Commands.lsp geladen - Dummy-Befehle registriert.")
(princ) (princ)
+23 -23
View File
@@ -140,13 +140,9 @@
;; csv-name = Ergebnis-CSV-Name (z.B. "export.csv") ;; csv-name = Ergebnis-CSV-Name (z.B. "export.csv")
(defun csv:run-export (label py-name csv-name / ss i ename fh out-pfad (defun csv:run-export (label py-name csv-name / ss i ename fh out-pfad
py-skript ergebnis-pfad cmd first-block count) py-skript ergebnis-pfad cmd first-block count)
;; Vor dem Export: Doppelte IDs pruefen und korrigieren ;; Vor dem Export: IDs aller Bloecke sicherstellen und Duplikate korrigieren
(if (atoms-family 1 '("ssg-id-check-all")) (princ (strcat "\n[" label "] Pruefe und vergebe IDs..."))
(progn
(princ (strcat "\n[" label "] Pruefe IDs auf Duplikate..."))
(ssg-id-check-all) (ssg-id-check-all)
)
)
;; Vor dem Export HOEHE/DREHUNG aller Omniflo-Elemente aktualisieren ;; Vor dem Export HOEHE/DREHUNG aller Omniflo-Elemente aktualisieren
(princ (strcat "\n[" label "] Aktualisiere Hoehe/Drehung...")) (princ (strcat "\n[" label "] Aktualisiere Hoehe/Drehung..."))
(omni:update-all-attribs) (omni:update-all-attribs)
@@ -351,17 +347,6 @@
) )
;; --- CSV-Zeile fuer ein Omniflo-Element bauen ---
(defun csv:omni-csv-line (elem-nr teileart bezeichnung merkmale-json)
(strcat (itoa elem-nr)
";\"" teileart "\""
";\"shape_" (substr (rtos (* (getvar "CDATE") 1000000.0) 2 0) 1 8)
(substr (rtos (* (getvar "TDUSRTIMER") 1000000.0) 2 0) 1 8) "\""
";\"" bezeichnung "\""
";1"
";" merkmale-json)
)
;; ============================================================ ;; ============================================================
;; C:OMNI_EXPORT - Omniflo-Elemente zaehlen und als CSV exportieren ;; C:OMNI_EXPORT - Omniflo-Elemente zaehlen und als CSV exportieren
@@ -373,6 +358,9 @@
len-ap110 len-ap60 laenge-str len-ap110 len-ap60 laenge-str
fh out-pfad lines subtyp merkmale) 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 ;; Vor dem Export HOEHE/DREHUNG aller Omniflo-Elemente aktualisieren
(princ "\n[OMNI_EXPORT] Aktualisiere Hoehe/Drehung...") (princ "\n[OMNI_EXPORT] Aktualisiere Hoehe/Drehung...")
(omni:update-all-attribs) (omni:update-all-attribs)
@@ -403,8 +391,12 @@
(setq cnt-boegen (1+ cnt-boegen)) (setq cnt-boegen (1+ cnt-boegen))
(setq merkmale (csv:omni-bogen-merkmale ename rotation)) (setq merkmale (csv:omni-bogen-merkmale ename rotation))
(setq lines (cons (setq lines (cons
(csv:omni-csv-line elem-nr "Omniflo Kurve" (strcat (itoa elem-nr)
(strcat "OFBogen :" (itoa cnt-boegen)) merkmale) ";\"Omniflo Kurve\""
";\"" (or (csv:get-attrib ename "ID") "0000") "\""
";\"" (strcat "OFBogen :" (itoa cnt-boegen)) "\""
";1"
";" merkmale)
lines)) lines))
) )
@@ -421,8 +413,12 @@
) )
(setq merkmale (csv:omni-weiche-merkmale ename rotation)) (setq merkmale (csv:omni-weiche-merkmale ename rotation))
(setq lines (cons (setq lines (cons
(csv:omni-csv-line elem-nr "Omniflo Weiche" (strcat (itoa elem-nr)
(strcat "OFWeiche :" (itoa cnt-weichen)) merkmale) ";\"Omniflo Weiche\""
";\"" (or (csv:get-attrib ename "ID") "0000") "\""
";\"" (strcat "OFWeiche :" (itoa cnt-weichen)) "\""
";1"
";" merkmale)
lines)) lines))
) )
@@ -437,8 +433,12 @@
) )
(setq merkmale (csv:omni-gerade-merkmale ename rotation)) (setq merkmale (csv:omni-gerade-merkmale ename rotation))
(setq lines (cons (setq lines (cons
(csv:omni-csv-line elem-nr "Omniflo Gerade" (strcat (itoa elem-nr)
(strcat "OFGerade :" (itoa cnt-geraden)) merkmale) ";\"Omniflo Gerade\""
";\"" (or (csv:get-attrib ename "ID") "0000") "\""
";\"" (strcat "OFGerade :" (itoa cnt-geraden)) "\""
";1"
";" merkmale)
lines)) lines))
) )
) )
+35
View File
@@ -620,6 +620,41 @@
) )
) )
;; --- JSON-Array von flachen Objekten parsen ---
;; Verwendet ssg-cfg-trim und ssg-cfg-parse-kv aus ssg_core.
;; Rueckgabe: Liste von Alists (("key" . wert) ...)
(defun ssg-parse-json-array (zeilen / in-obj obj ergebnis zeile kv trimmed)
(setq in-obj nil obj nil ergebnis nil)
(foreach zeile zeilen
(setq trimmed (ssg-cfg-trim zeile))
(cond
((= trimmed "{")
(setq in-obj T obj nil))
((or (= trimmed "}") (= trimmed "},"))
(if (and in-obj obj)
(setq ergebnis (cons (reverse obj) ergebnis)))
(setq in-obj nil))
(in-obj
(setq kv (ssg-cfg-parse-kv trimmed))
(if kv (setq obj (cons kv obj))))
)
)
(reverse ergebnis)
)
;; JSON-Datei als flaches Array laden
;; Rueckgabe: Liste von Alists oder nil bei Fehler
(defun ssg-load-json (datei / zeilen)
(setq zeilen (ssg-read-file-lines datei))
(if zeilen (ssg-parse-json-array zeilen))
)
;; Wert aus Alist lesen: (ssg-val alist "key")
(defun ssg-val (alist key)
(cdr (assoc key alist))
)
;; Verschachteltes JSON-Objekt parsen (1 Ebene tief) ;; Verschachteltes JSON-Objekt parsen (1 Ebene tief)
;; {"section": {"key": val, ...}, ...} ;; {"section": {"key": val, ...}, ...}
;; -> (("section" ("key" . val) ...) ...) ;; -> (("section" ("key" . val) ...) ...)
+2 -2
View File
@@ -103,8 +103,8 @@
(princ "\n[SSG_LIB] WARNUNG: DXFMAKRO nicht gesetzt, Menue nicht geladen!") (princ "\n[SSG_LIB] WARNUNG: DXFMAKRO nicht gesetzt, Menue nicht geladen!")
) )
;; DBLCLKEDIT deaktivieren (Block-Editor nicht bei Doppelklick oeffnen) ;; DBLCLKEDIT=1: Doppelklick aktiv, ***DOUBLECLICK-Eintraege steuern Block-Verhalten
(setvar "DBLCLKEDIT" 0) (setvar "DBLCLKEDIT" 1)
(dbgflush) (dbgflush)
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
+3 -8
View File
@@ -13,7 +13,6 @@ CSV-Format:
import json import json
import sys import sys
import os import os
import uuid
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -35,10 +34,6 @@ def build_lookup(boegen, weichen):
return lookup return lookup
def generate_shape_id():
return "shape_" + str(uuid.uuid4())
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Merkmale-Builder # Merkmale-Builder
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -151,7 +146,7 @@ def process_blocks(blocks, lookup):
if bname in lookup: if bname in lookup:
typ, eintrag = lookup[bname] typ, eintrag = lookup[bname]
elem_nr += 1 elem_nr += 1
shape_id = generate_shape_id() shape_id = block.get("attribs", {}).get("ID", "0000")
if typ == "bogen": if typ == "bogen":
bogen_count += 1 bogen_count += 1
@@ -182,7 +177,7 @@ def process_blocks(blocks, lookup):
items.append({ items.append({
"nr": elem_nr, "nr": elem_nr,
"teileart": "Omniflo Gerade", "teileart": "Omniflo Gerade",
"teileid": generate_shape_id(), "teileid": block.get("attribs", {}).get("ID", "0000"),
"bezeichnung": f"OFGerade :{gerade_count}", "bezeichnung": f"OFGerade :{gerade_count}",
"merkmale": build_gerade_merkmale(block), "merkmale": build_gerade_merkmale(block),
}) })
@@ -195,7 +190,7 @@ def process_blocks(blocks, lookup):
items.append({ items.append({
"nr": elem_nr, "nr": elem_nr,
"teileart": "ILS 2.0 Kreisel", "teileart": "ILS 2.0 Kreisel",
"teileid": generate_shape_id(), "teileid": block.get("attribs", {}).get("ID", "0000"),
"bezeichnung": f"Kreisel :{kreisel_count}", "bezeichnung": f"Kreisel :{kreisel_count}",
"merkmale": build_kreisel_merkmale(block), "merkmale": build_kreisel_merkmale(block),
}) })
+3 -8
View File
@@ -15,7 +15,6 @@ Argumente:
import json import json
import sys import sys
import os import os
import uuid
def load_json(path): def load_json(path):
@@ -35,10 +34,6 @@ def build_lookup(boegen, weichen):
return lookup return lookup
def generate_shape_id():
return "shape_" + str(uuid.uuid4())
def classify_weiche(eintrag): def classify_weiche(eintrag):
"""Bestimmt den Weichen-Subtyp fuer die Omniflo Sum Zaehlung.""" """Bestimmt den Weichen-Subtyp fuer die Omniflo Sum Zaehlung."""
wt = eintrag.get("WeichenTyp", "") wt = eintrag.get("WeichenTyp", "")
@@ -213,7 +208,7 @@ def process_blocks(blocks, lookup):
if bname in lookup: if bname in lookup:
typ, eintrag = lookup[bname] typ, eintrag = lookup[bname]
elem_nr += 1 elem_nr += 1
shape_id = generate_shape_id() shape_id = block.get("attribs", {}).get("ID", "0000")
if typ == "bogen": if typ == "bogen":
bogen_count += 1 bogen_count += 1
@@ -261,7 +256,7 @@ def process_blocks(blocks, lookup):
items.append({ items.append({
"nr": elem_nr, "nr": elem_nr,
"teileart": "Omniflo Gerade", "teileart": "Omniflo Gerade",
"teileid": generate_shape_id(), "teileid": block.get("attribs", {}).get("ID", "0000"),
"bezeichnung": f"OFGerade :{gerade_count}", "bezeichnung": f"OFGerade :{gerade_count}",
"anzahl": 1, "anzahl": 1,
"merkmale": build_gerade_merkmale(block) "merkmale": build_gerade_merkmale(block)
@@ -276,7 +271,7 @@ def process_blocks(blocks, lookup):
items.append({ items.append({
"nr": elem_nr, "nr": elem_nr,
"teileart": "ILS 2.0 Kreisel", "teileart": "ILS 2.0 Kreisel",
"teileid": generate_shape_id(), "teileid": block.get("attribs", {}).get("ID", "0000"),
"bezeichnung": f"Kreisel :{kreisel_count}", "bezeichnung": f"Kreisel :{kreisel_count}",
"anzahl": 1, "anzahl": 1,
"merkmale": build_kreisel_merkmale(block) "merkmale": build_kreisel_merkmale(block)
+1 -1
View File
@@ -149,7 +149,7 @@
;; Pulldown-Menue in die Menueleiste einhaengen ;; Pulldown-Menue in die Menueleiste einhaengen
(menucmd "P13=+SSG_LIB.POP1") (menucmd "P13=+SSG_LIB.POP1")
(setvar "DBLCLKEDIT" 0) (setvar "DBLCLKEDIT" 1)
(princ "\n[SSG_LIB] Alle Module geladen. Menue aktiv.") (princ "\n[SSG_LIB] Alle Module geladen. Menue aktiv.")
) )
(princ "\n[SSG_LIB] WARNUNG: DXFM_LISP nicht gesetzt - Befehle nicht verfuegbar.") (princ "\n[SSG_LIB] WARNUNG: DXFM_LISP nicht gesetzt - Befehle nicht verfuegbar.")
+5
View File
@@ -126,6 +126,11 @@
***DOUBLECLICK ***DOUBLECLICK
[INSERT]^C^C(ssg-ensure "SSG_LIB_Commands") SSG_BLOCKEDIT [INSERT]^C^C(ssg-ensure "SSG_LIB_Commands") SSG_BLOCKEDIT
[MTEXT]^C^CDDEDIT
[TEXT]^C^CDDEDIT
[ATTDEF]^C^CDDEDIT
[HATCH]^C^CHATCHEDIT
[DIMENSION]^C^CDDEDIT
***POP501 ***POP501
[SSG_LIB Edit Context] [SSG_LIB Edit Context]
+83 -20
View File
@@ -8,6 +8,9 @@ Die Tests folgen einer **zweistufigen Architektur**:
2. **Python-Validierung** (`.py`) — pruefen die Ergebnisse mit pytest + ezdxf 2. **Python-Validierung** (`.py`) — pruefen die Ergebnisse mit pytest + ezdxf
``` ```
tests/alltests.json
|
v
BricsCAD Kommandozeile BricsCAD Kommandozeile
| | | |
| SSG_RUN_ALL_TESTS | | SSG_RUN_ALL_TESTS |
@@ -18,14 +21,14 @@ BricsCAD Kommandozeile
| | | | | |
v v | v v |
tests/output/ | tests/output/ |
kreisel_results.json | kreisel_results.json (immer) |
kreisel_tests.dxf | kreisel_tests.dxf (save: dxf) |
foerderer_results.json | foerderer_results.json (immer) |
foerderer_tests.dxf | foerderer_tests.dwg (save: dwg) |
kseinaus_results.json | kseinaus_results.json (immer) |
kseinaus_tests.dxf | (keine Zeichnung) (save: null) |
omniflo_results.json | omniflo_results.json (immer) |
omniflo_tests.dxf | omniflo_tests.dxf (save: dxf) |
omniflo_export.csv | omniflo_export.csv |
| |
bin\run_tests.bat --runall bin\run_tests.bat --runall
@@ -36,6 +39,36 @@ tests/output/ |
test_omniflo.py test_omniflo.py
``` ```
## alltests.json — Testregistry
Die Datei `tests/alltests.json` ist die zentrale Registry aller Test-Module.
`SSG_RUN_ALL_TESTS` liest sie beim Start — die Testliste ist **nicht** hartcodiert in `test_run_all.lsp`.
```json
[
{ "name": "kreisel", "save": "dxf" },
{ "name": "foerderer", "save": "dwg" },
{ "name": "kseinaus", "save": null },
{ "name": "omniflo", "save": "dxf" }
]
```
Felder pro Eintrag:
| Feld | Typ | Bedeutung |
| --- | --- | --- |
| `name` | String | Basisname des Testmoduls (Pflicht) |
| `save` | `"dxf"` / `"dwg"` / `null` | Ob und als welches Format die Zeichnung gespeichert wird |
**JSON-Export laeuft immer** — unabhaengig vom `save`-Feld. Die Datei `<name>_results.json`
wird in `tests/output/` geschrieben, sobald der Test abgeschlossen ist.
**Wann welches `save` verwenden:**
- `"dxf"` — wenn ein Python-Test (ezdxf) die Geometrie der Zeichnung pruefen soll
- `"dwg"` — wenn die Zeichnung zur manuellen Kontrolle oder als DWG-Referenz erhalten bleiben soll
- `null` (oder Feld weglassen) — wenn nur der JSON-Export benoetigt wird
## Ablauf fuer den Anwender ## Ablauf fuer den Anwender
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/`. 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/`.
@@ -96,22 +129,33 @@ Dann alle Tests auf einmal ausfuehren:
SSG_RUN_ALL_TESTS SSG_RUN_ALL_TESTS
``` ```
Oder einen einzelnen Test: `SSG_RUN_ALL_TESTS` liest die Testliste aus `tests/alltests.json` und fuehrt fuer jedes Modul aus:
1. Zeichnung leeren (ERASE ALL + PURGE x5) — saubere Basis fuer jeden Test
2. `test_<name>.lsp` laden und `TEST_<NAME>` ausfuehren
3. JSON-Export aufrufen: `<name>:export-results`**immer**, unabhaengig von `save`
4. Zeichnung speichern — nur wenn `"save"` in `alltests.json` gesetzt ist (`"dxf"` oder `"dwg"`)
5. Bei Fehler: weiter mit naechstem Test (`vl-catch-all-apply`)
**Einzeltest** (z.B. nur Kreisel pruefen):
```lisp ```lisp
;; Einzeltest (z.B. Kreisel) ;; Test-Datei laden und Befehl ausfuehren
(load (strcat (getenv "DXFMAKRO") "/tests/test_kreisel.lsp")) (load (strcat (getenv "DXFMAKRO") "/tests/test_kreisel.lsp"))
TEST_KREISEL TEST_KREISEL
;; Ergebnisse manuell exportieren: ;; JSON-Export manuell aufrufen:
(kreisel:export-results (strcat (getenv "DXFMAKRO") "/tests/output")) (kreisel:export-results (strcat (getenv "DXFMAKRO") "/tests/output"))
``` ```
`SSG_RUN_ALL_TESTS` fuehrt fuer jedes Modul in `*test-module*` folgendes aus: **Unterschied SSG_RUN_ALL_TESTS vs. Einzeltest:**
- Zeichnung leeren (ERASE + PURGE)
- Testdatei laden und Befehl ausfuehren | | SSG_RUN_ALL_TESTS | Einzeltest |
- JSON-Export aufrufen (`<name>:export-results`) | --- | --- | --- |
- DXF speichern (`<name>_tests.dxf`) | Testliste | aus `alltests.json` | manuell gewaehlter Test |
- Bei Fehler: weiter mit naechstem Test (vl-catch-all-apply) | Zeichnung leeren | automatisch vor jedem Test | nein (Zeichnung bleibt) |
| JSON-Export | automatisch nach jedem Test | manuell aufrufen |
| DXF/DWG speichern | gemaess `save` in `alltests.json` | manuell (SAVEAS / DXFOUT) |
| Fehlerisolation | ja (Fehler unterbricht nicht weitere Tests) | nein |
### 2. Python-Validierung ausfuehren ### 2. Python-Validierung ausfuehren
@@ -229,13 +273,32 @@ Fuer ein neues Modul eine neue `.lsp`-Datei erstellen nach dem Muster:
) )
``` ```
Dann den Basisnamen in `*test-module*` (in `test_run_all.lsp`) eintragen: Dann den Basisnamen in `tests/alltests.json` eintragen:
```lisp ```json
(setq *test-module* '("kreisel" "foerderer" "kseinaus" "omniflo" "<modul>")) { "name": "<modul>", "save": "dxf" }
``` ```
`"save"` waehlen je nach Bedarf (`"dxf"`, `"dwg"` oder `null`).
`test_run_all.lsp` leitet alle Namen aus dem `"name"`-Feld ab — **nichts** in `test_run_all.lsp` aendern.
### Namenskonventionen (werden von test_run_all.lsp automatisch abgeleitet)
Aus dem Basisnamen `<name>` in `alltests.json` entstehen durch Konvention:
| Element | Schema | Beispiel fuer `"name": "kreisel"` |
| --- | --- | --- |
| LISP-Datei | `tests/test_<name>.lsp` | `tests/test_kreisel.lsp` |
| BricsCAD-Befehl | `TEST_<NAME>` (Grossbuchstaben) | `TEST_KREISEL` |
| Export-Funktion | `<name>:export-results` | `kreisel:export-results` |
| JSON-Ergebnis | `output/<name>_results.json` | `output/kreisel_results.json` |
| DXF/DWG-Datei | `output/<name>_tests.dxf/.dwg` | `output/kreisel_tests.dxf` |
Die Export-Funktion **muss** exakt so heissen: `<name>:export-results` (Namespace-Notation mit Doppelpunkt).
Sie nimmt genau einen Parameter entgegen: den Pfad zum Output-Verzeichnis.
Wichtig: Wichtig:
- `omni:load-json` erwartet ein **flaches JSON-Array** mit flachen Objekten (kein Nesting) - `omni:load-json` erwartet ein **flaches JSON-Array** mit flachen Objekten (kein Nesting)
- `omni:val` liest Werte aus den geladenen Eintraegen - `omni:val` liest Werte aus den geladenen Eintraegen
- Script-Funktionen (z.B. `kreisel-insert-script`) muessen **ohne User-Interaktion** funktionieren - Script-Funktionen (z.B. `kreisel-insert-script`) muessen **ohne User-Interaktion** funktionieren
+1 -1
View File
@@ -4,7 +4,7 @@
;; Erzeugt 12 Variofoerderer (6x Auf, 6x Ab) mit deltaL=7000 ;; Erzeugt 12 Variofoerderer (6x Auf, 6x Ab) mit deltaL=7000
;; und verschiedenen Hoehenstufen. Speichert: ;; und verschiedenen Hoehenstufen. Speichert:
;; - tests/output/foerderer_results.json (Ergebnisse) ;; - tests/output/foerderer_results.json (Ergebnisse)
;; - tests/output/foerderer_tests.dxf (Zeichnung) ;; - tests/output/foerderer_tests.dwg (Zeichnung)
;; ;;
;; Voraussetzungen: ;; Voraussetzungen:
;; - SSG_LIB geladen (VarioFoerderer.lsp, ssg_core.lsp) ;; - SSG_LIB geladen (VarioFoerderer.lsp, ssg_core.lsp)
+17 -17
View File
@@ -54,13 +54,13 @@
(defun kreisel:run-insert-test (eintrag / id x y z abstand rotation typ (defun kreisel:run-insert-test (eintrag / id x y z abstand rotation typ
pt blockEnt ed attribs block-name pt blockEnt ed attribs block-name
block-handle insert-point) block-handle insert-point)
(setq id (omni:val eintrag "id")) (setq id (ssg-val eintrag "id"))
(setq x (omni:val eintrag "x")) (setq x (ssg-val eintrag "x"))
(setq y (omni:val eintrag "y")) (setq y (ssg-val eintrag "y"))
(setq z (omni:val eintrag "z")) (setq z (ssg-val eintrag "z"))
(setq abstand (omni:val eintrag "abstand")) (setq abstand (ssg-val eintrag "abstand"))
(setq rotation (omni:val eintrag "rotation")) (setq rotation (ssg-val eintrag "rotation"))
(setq typ (omni:val eintrag "typ")) (setq typ (ssg-val eintrag "typ"))
(if (numberp x) (setq x (float x))) (if (numberp x) (setq x (float x)))
(if (numberp y) (setq y (float y))) (if (numberp y) (setq y (float y)))
@@ -98,14 +98,14 @@
(defun kreisel:run-connect-test (eintrag / id sx sy sz ex ey ez typ (defun kreisel:run-connect-test (eintrag / id sx sy sz ex ey ez typ
ptStart ptEnd blockEnt ed attribs ptStart ptEnd blockEnt ed attribs
block-name block-handle insert-point) block-name block-handle insert-point)
(setq id (omni:val eintrag "id")) (setq id (ssg-val eintrag "id"))
(setq sx (omni:val eintrag "start_x")) (setq sx (ssg-val eintrag "start_x"))
(setq sy (omni:val eintrag "start_y")) (setq sy (ssg-val eintrag "start_y"))
(setq sz (omni:val eintrag "start_z")) (setq sz (ssg-val eintrag "start_z"))
(setq ex (omni:val eintrag "end_x")) (setq ex (ssg-val eintrag "end_x"))
(setq ey (omni:val eintrag "end_y")) (setq ey (ssg-val eintrag "end_y"))
(setq ez (omni:val eintrag "end_z")) (setq ez (ssg-val eintrag "end_z"))
(setq typ (omni:val eintrag "typ")) (setq typ (ssg-val eintrag "typ"))
(if (numberp sx) (setq sx (float sx))) (if (numberp sx) (setq sx (float sx)))
(if (numberp sy) (setq sy (float sy))) (if (numberp sy) (setq sy (float sy)))
@@ -194,7 +194,7 @@
(princ) (princ)
) )
(progn (progn
(setq testfaelle (omni:load-json tests-json-pfad)) (setq testfaelle (ssg-load-json tests-json-pfad))
(if (null testfaelle) (if (null testfaelle)
(progn (progn
(princ "\n[TEST_KREISEL] FEHLER: Testdaten konnten nicht geladen werden.") (princ "\n[TEST_KREISEL] FEHLER: Testdaten konnten nicht geladen werden.")
@@ -213,7 +213,7 @@
;; Testfaelle ausfuehren ;; Testfaelle ausfuehren
(foreach eintrag testfaelle (foreach eintrag testfaelle
(setq func (omni:val eintrag "function")) (setq func (ssg-val eintrag "function"))
(setq result-json (setq result-json
(cond (cond
((= func "insert") ((= func "insert")
+57 -15
View File
@@ -100,6 +100,47 @@
) )
;; --- Post-Clean: Omniflo-Testbloecke aus der Zeichnung loeschen ---
;; Entfernt alle INSERT-Entities mit rein numerischen Blocknamen (Omniflo SivasNr)
;; sowie AP110-Geraden. Wird vom Test-Runner nach dem Export aufgerufen.
;; Rueckgabe: Anzahl geloeschter Bloecke
(defun omniflo:post-clean ( / ss-all i ename ed bname count)
(setq count 0)
(setq ss-all (ssget "X" (list (cons 0 "INSERT"))))
(if ss-all
(progn
(setq i 0)
(while (setq ename (ssname ss-all i))
(setq ed (entget ename))
(setq bname (cdr (assoc 2 ed)))
(if (or
;; Omniflo SivasNr: rein numerisch (beginnt mit Ziffer, kein Unterstrich)
(and (wcmatch bname "#*") (null (vl-string-search "_" bname)))
;; Omniflo Geraden
(wcmatch (strcase bname) "AP110*,AP_110*")
)
(progn
(entdel ename)
(setq count (1+ count))
)
)
(setq i (1+ i))
)
)
)
(princ (strcat "\n[OMNIFLO] Post-Clean: " (itoa count) " Bloecke entfernt."))
count
)
;; --- BricsCAD-Befehl: Omniflo-Ergebnisse exportieren ---
(defun c:TEST_OMNIFLO_EXPORT ( / tests-out-dir)
(setq tests-out-dir (strcat (getenv "DXFMAKRO") "/tests/output"))
(omniflo:export-results tests-out-dir)
(princ)
)
;; ============================================================ ;; ============================================================
;; C:TEST_OMNIFLO ;; C:TEST_OMNIFLO
;; ;;
@@ -107,15 +148,11 @@
;; exportiert CSV nach tests/output/. ;; exportiert CSV nach tests/output/.
;; ============================================================ ;; ============================================================
(defun c:TEST_OMNIFLO (/ tests-json-pfad testfaelle (defun c:TEST_OMNIFLO (/ tests-json-pfad testfaelle
x-pos x-schritt x-val y-val
idx eintrag sivasnr hoehe-str drehung-str idx eintrag sivasnr hoehe-str drehung-str
description insert-pt anzahl-ok anzahl-fehler description insert-pt anzahl-ok anzahl-fehler
results-list tests-out-dir old-results) results-list tests-out-dir old-results)
(if (not *lib-initialized*)
(init-bibliothek)
)
(ssg-start "TEST_OMNIFLO" (ssg-start "TEST_OMNIFLO"
'(("OSMODE") ("ATTREQ") ("ATTDIA"))) '(("OSMODE") ("ATTREQ") ("ATTDIA")))
(setvar "OSMODE" 0) (setvar "OSMODE" 0)
@@ -134,7 +171,7 @@
(princ) (princ)
) )
(progn (progn
(setq testfaelle (omni:load-json tests-json-pfad)) (setq testfaelle (ssg-load-json tests-json-pfad))
(if (null testfaelle) (if (null testfaelle)
(progn (progn
(princ "\n[TEST_OMNIFLO] FEHLER: Testdaten konnten nicht geladen werden.") (princ "\n[TEST_OMNIFLO] FEHLER: Testdaten konnten nicht geladen werden.")
@@ -148,8 +185,6 @@
(princ "\n================================================================") (princ "\n================================================================")
;; Alle Bloecke einfuegen ;; Alle Bloecke einfuegen
(setq x-pos 0.0)
(setq x-schritt 200.0)
(setq anzahl-ok 0) (setq anzahl-ok 0)
(setq anzahl-fehler 0) (setq anzahl-fehler 0)
(setq results-list nil) (setq results-list nil)
@@ -157,24 +192,31 @@
(foreach eintrag testfaelle (foreach eintrag testfaelle
(setq idx (1+ idx)) (setq idx (1+ idx))
(setq sivasnr (omni:val eintrag "sivasnr")) (setq sivasnr (ssg-val eintrag "sivasnr"))
(setq hoehe-str (omni:val eintrag "hoehe")) (setq hoehe-str (ssg-val eintrag "hoehe"))
(if (null hoehe-str) (setq hoehe-str "2000")) (if (null hoehe-str) (setq hoehe-str "2000"))
(if (numberp hoehe-str) (if (numberp hoehe-str)
(setq hoehe-str (rtos hoehe-str 2 0)) (setq hoehe-str (rtos hoehe-str 2 0))
) )
(setq drehung-str (omni:val eintrag "drehung")) (setq drehung-str (ssg-val eintrag "drehung"))
(if (null drehung-str) (setq drehung-str "0")) (if (null drehung-str) (setq drehung-str "0"))
(if (numberp drehung-str) (if (numberp drehung-str)
(setq drehung-str (rtos drehung-str 2 1)) (setq drehung-str (rtos drehung-str 2 1))
) )
(setq description (omni:val eintrag "description")) (setq description (ssg-val eintrag "description"))
(setq insert-pt (list x-pos 0.0 (atof hoehe-str))) ;; x,y-Koordinaten aus JSON lesen (wie in omniflo_uebersicht.dxf)
(setq x-val (ssg-val eintrag "x"))
(setq y-val (ssg-val eintrag "y"))
(if (null x-val) (setq x-val 0.0))
(if (null y-val) (setq y-val 0.0))
(if (not (numberp x-val)) (setq x-val (atof x-val)))
(if (not (numberp y-val)) (setq y-val (atof y-val)))
(setq insert-pt (list x-val y-val (atof hoehe-str)))
(princ (strcat "\n " (itoa idx) ". " (princ (strcat "\n " (itoa idx) ". "
(if description description (if sivasnr sivasnr "?")) (if description description (if sivasnr sivasnr "?"))
" -> (" (rtos x-pos 2 0) ", 0, " hoehe-str ")")) " -> (" (rtos x-val 2 0) ", " (rtos y-val 2 0) ", " hoehe-str ")"))
(if (omni:test-insert sivasnr insert-pt hoehe-str drehung-str) (if (omni:test-insert sivasnr insert-pt hoehe-str drehung-str)
(progn (progn
@@ -193,9 +235,9 @@
) )
) )
(setq x-pos (+ x-pos x-schritt))
) )
;; Zusammenfassung ;; Zusammenfassung
(princ "\n================================================================") (princ "\n================================================================")
(princ (strcat "\n Eingefuegt: " (itoa anzahl-ok) (princ (strcat "\n Eingefuegt: " (itoa anzahl-ok)
+79 -1
View File
@@ -22,7 +22,7 @@ sys.path.insert(0, _LIB_DIR)
from export_csv import ( from export_csv import (
build_lookup, process_blocks, build_bogen_merkmale, build_lookup, process_blocks, build_bogen_merkmale,
build_weiche_merkmale, load_json build_weiche_merkmale, load_json, format_csv_line
) )
@@ -100,6 +100,16 @@ class TestOmnifloExportUnit:
)) ))
self.items = process_blocks(self.blocks, self.lookup) self.items = process_blocks(self.blocks, self.lookup)
# CSV in output speichern
out_dir = _output_dir()
os.makedirs(out_dir, exist_ok=True)
csv_path = os.path.join(out_dir, "omniflo_export.csv")
header = "Elementnummer;TeileArt;TeileId;Bezeichnung;Anzahl;Merkmale"
with open(csv_path, "w", encoding="utf-8") as f:
f.write(header + "\n")
for item in self.items:
f.write(format_csv_line(item) + "\n")
def test_item_count_matches_testcases(self): def test_item_count_matches_testcases(self):
"""Anzahl exportierter Elemente muss Anzahl Testfaelle entsprechen.""" """Anzahl exportierter Elemente muss Anzahl Testfaelle entsprechen."""
assert len(self.items) == len(self.tests), ( assert len(self.items) == len(self.tests), (
@@ -348,3 +358,71 @@ class TestOmnifloExportCSV:
assert not missing, ( assert not missing, (
f"Zeile {i + 2}: Fehlende Weichen-Felder: {missing}" f"Zeile {i + 2}: Fehlende Weichen-Felder: {missing}"
) )
# ---------------------------------------------------------------------------
# Referenz-Tests: CSV gegen Referenzdatei vergleichen
# ---------------------------------------------------------------------------
def _reference_dir():
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "reference")
def _normalize_row(line):
"""Splittet eine CSV-Zeile und ersetzt die TeileId (UUID) durch Platzhalter."""
cols = line.strip().split(";", 5)
if len(cols) >= 3:
cols[2] = "<ID>"
return cols
class TestOmnifloReferenceCSV:
"""Vergleicht den erzeugten Unit-Test-CSV-Export mit einer Referenzdatei."""
@pytest.fixture(autouse=True)
def setup(self):
self.out_csv = os.path.join(_output_dir(), "omniflo_export.csv")
self.ref_csv = os.path.join(_reference_dir(), "omniflo_export_reference.csv")
if not os.path.exists(self.out_csv):
pytest.skip(
"omniflo_export.csv nicht vorhanden - "
"zuerst TestOmnifloExportUnit ausfuehren"
)
if not os.path.exists(self.ref_csv):
pytest.skip("Referenzdatei nicht vorhanden: " + self.ref_csv)
with open(self.out_csv, "r", encoding="utf-8") as f:
self.out_lines = f.readlines()
with open(self.ref_csv, "r", encoding="utf-8") as f:
self.ref_lines = f.readlines()
def test_header_matches_reference(self):
"""Header muss mit Referenz uebereinstimmen."""
assert self.out_lines[0].strip() == self.ref_lines[0].strip(), (
f"Header abweichend:\n Ist: {self.out_lines[0].strip()}\n"
f" Soll: {self.ref_lines[0].strip()}"
)
def test_zeilenanzahl_matches_reference(self):
"""Zeilenanzahl muss mit Referenz uebereinstimmen."""
assert len(self.out_lines) == len(self.ref_lines), (
f"Zeilenanzahl abweichend: Ist={len(self.out_lines)}, "
f"Soll={len(self.ref_lines)}"
)
def test_zeilen_matches_reference(self):
"""Alle Zeilen muessen mit Referenz uebereinstimmen (TeileId wird ignoriert)."""
min_lines = min(len(self.out_lines), len(self.ref_lines))
diffs = []
for i in range(1, min_lines):
out_cols = _normalize_row(self.out_lines[i])
ref_cols = _normalize_row(self.ref_lines[i])
if out_cols != ref_cols:
diffs.append(
f"Zeile {i + 1}:\n"
f" Ist: {self.out_lines[i].strip()[:120]}\n"
f" Soll: {self.ref_lines[i].strip()[:120]}"
)
assert not diffs, (
f"{len(diffs)} abweichende Zeile(n) (erste 5):\n" +
"\n".join(diffs[:5])
)
+3
View File
@@ -146,6 +146,9 @@
) )
;; Zeichnung leeren fuer sauberen Testlauf ;; 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") (if (ssget "_X")
(command "_.ERASE" "_ALL" "") (command "_.ERASE" "_ALL" "")
) )
+1145 -80
View File
File diff suppressed because it is too large Load Diff