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)
;; Implied Selection pruefen (Block muss vorselektiert sein)
;; Block ermitteln: zuerst Implied Selection, dann Picking
(setq ss (ssget "I"))
(if (or (null ss) (/= (sslength ss) 1))
(progn
(princ "\nBlock auswaehlen: ")
(setq ss (ssget ":S" '((0 . "INSERT"))))
)
)
(if (null ss)
(progn
(princ "\nBitte zuerst einen Block auswaehlen.")
(princ)
(exit)
)
)
(if (/= (sslength ss) 1)
(progn
(princ "\nBitte genau einen Block auswaehlen.")
(princ "\nKein Block ausgewaehlt.")
(princ)
(exit)
)
@@ -191,14 +190,30 @@
(c:OMNI_EDIT)
)
;; Unbekannter Blocktyp -> Standard BEDIT
;; Unbekannter Blocktyp -> natives EATTEDIT (Attribut-Editor)
(t
(princ (strcat "\nBlock bearbeiten (BEDIT): " bname))
(command "_.BEDIT" bname)
(princ (strcat "\nAttribute bearbeiten: " bname))
(command "_.EATTEDIT")
)
)
(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)
+23 -23
View File
@@ -140,13 +140,9 @@
;; csv-name = Ergebnis-CSV-Name (z.B. "export.csv")
(defun csv:run-export (label py-name csv-name / ss i ename fh out-pfad
py-skript ergebnis-pfad cmd first-block count)
;; Vor dem Export: Doppelte IDs pruefen und korrigieren
(if (atoms-family 1 '("ssg-id-check-all"))
(progn
(princ (strcat "\n[" label "] Pruefe IDs auf Duplikate..."))
;; Vor dem Export: IDs aller Bloecke sicherstellen und Duplikate korrigieren
(princ (strcat "\n[" label "] Pruefe und vergebe IDs..."))
(ssg-id-check-all)
)
)
;; Vor dem Export HOEHE/DREHUNG aller Omniflo-Elemente aktualisieren
(princ (strcat "\n[" label "] Aktualisiere Hoehe/Drehung..."))
(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
@@ -373,6 +358,9 @@
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)
@@ -403,8 +391,12 @@
(setq cnt-boegen (1+ cnt-boegen))
(setq merkmale (csv:omni-bogen-merkmale ename rotation))
(setq lines (cons
(csv:omni-csv-line elem-nr "Omniflo Kurve"
(strcat "OFBogen :" (itoa cnt-boegen)) merkmale)
(strcat (itoa elem-nr)
";\"Omniflo Kurve\""
";\"" (or (csv:get-attrib ename "ID") "0000") "\""
";\"" (strcat "OFBogen :" (itoa cnt-boegen)) "\""
";1"
";" merkmale)
lines))
)
@@ -421,8 +413,12 @@
)
(setq merkmale (csv:omni-weiche-merkmale ename rotation))
(setq lines (cons
(csv:omni-csv-line elem-nr "Omniflo Weiche"
(strcat "OFWeiche :" (itoa cnt-weichen)) merkmale)
(strcat (itoa elem-nr)
";\"Omniflo Weiche\""
";\"" (or (csv:get-attrib ename "ID") "0000") "\""
";\"" (strcat "OFWeiche :" (itoa cnt-weichen)) "\""
";1"
";" merkmale)
lines))
)
@@ -437,8 +433,12 @@
)
(setq merkmale (csv:omni-gerade-merkmale ename rotation))
(setq lines (cons
(csv:omni-csv-line elem-nr "Omniflo Gerade"
(strcat "OFGerade :" (itoa cnt-geraden)) merkmale)
(strcat (itoa elem-nr)
";\"Omniflo Gerade\""
";\"" (or (csv:get-attrib ename "ID") "0000") "\""
";\"" (strcat "OFGerade :" (itoa cnt-geraden)) "\""
";1"
";" merkmale)
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)
;; {"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!")
)
;; DBLCLKEDIT deaktivieren (Block-Editor nicht bei Doppelklick oeffnen)
(setvar "DBLCLKEDIT" 0)
;; DBLCLKEDIT=1: Doppelklick aktiv, ***DOUBLECLICK-Eintraege steuern Block-Verhalten
(setvar "DBLCLKEDIT" 1)
(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 sys
import os
import uuid
# ---------------------------------------------------------------------------
@@ -35,10 +34,6 @@ def build_lookup(boegen, weichen):
return lookup
def generate_shape_id():
return "shape_" + str(uuid.uuid4())
# ---------------------------------------------------------------------------
# Merkmale-Builder
# ---------------------------------------------------------------------------
@@ -151,7 +146,7 @@ def process_blocks(blocks, lookup):
if bname in lookup:
typ, eintrag = lookup[bname]
elem_nr += 1
shape_id = generate_shape_id()
shape_id = block.get("attribs", {}).get("ID", "0000")
if typ == "bogen":
bogen_count += 1
@@ -182,7 +177,7 @@ def process_blocks(blocks, lookup):
items.append({
"nr": elem_nr,
"teileart": "Omniflo Gerade",
"teileid": generate_shape_id(),
"teileid": block.get("attribs", {}).get("ID", "0000"),
"bezeichnung": f"OFGerade :{gerade_count}",
"merkmale": build_gerade_merkmale(block),
})
@@ -195,7 +190,7 @@ def process_blocks(blocks, lookup):
items.append({
"nr": elem_nr,
"teileart": "ILS 2.0 Kreisel",
"teileid": generate_shape_id(),
"teileid": block.get("attribs", {}).get("ID", "0000"),
"bezeichnung": f"Kreisel :{kreisel_count}",
"merkmale": build_kreisel_merkmale(block),
})
+3 -8
View File
@@ -15,7 +15,6 @@ Argumente:
import json
import sys
import os
import uuid
def load_json(path):
@@ -35,10 +34,6 @@ def build_lookup(boegen, weichen):
return lookup
def generate_shape_id():
return "shape_" + str(uuid.uuid4())
def classify_weiche(eintrag):
"""Bestimmt den Weichen-Subtyp fuer die Omniflo Sum Zaehlung."""
wt = eintrag.get("WeichenTyp", "")
@@ -213,7 +208,7 @@ def process_blocks(blocks, lookup):
if bname in lookup:
typ, eintrag = lookup[bname]
elem_nr += 1
shape_id = generate_shape_id()
shape_id = block.get("attribs", {}).get("ID", "0000")
if typ == "bogen":
bogen_count += 1
@@ -261,7 +256,7 @@ def process_blocks(blocks, lookup):
items.append({
"nr": elem_nr,
"teileart": "Omniflo Gerade",
"teileid": generate_shape_id(),
"teileid": block.get("attribs", {}).get("ID", "0000"),
"bezeichnung": f"OFGerade :{gerade_count}",
"anzahl": 1,
"merkmale": build_gerade_merkmale(block)
@@ -276,7 +271,7 @@ def process_blocks(blocks, lookup):
items.append({
"nr": elem_nr,
"teileart": "ILS 2.0 Kreisel",
"teileid": generate_shape_id(),
"teileid": block.get("attribs", {}).get("ID", "0000"),
"bezeichnung": f"Kreisel :{kreisel_count}",
"anzahl": 1,
"merkmale": build_kreisel_merkmale(block)
+1 -1
View File
@@ -149,7 +149,7 @@
;; Pulldown-Menue in die Menueleiste einhaengen
(menucmd "P13=+SSG_LIB.POP1")
(setvar "DBLCLKEDIT" 0)
(setvar "DBLCLKEDIT" 1)
(princ "\n[SSG_LIB] Alle Module geladen. Menue aktiv.")
)
(princ "\n[SSG_LIB] WARNUNG: DXFM_LISP nicht gesetzt - Befehle nicht verfuegbar.")
+5
View File
@@ -126,6 +126,11 @@
***DOUBLECLICK
[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
[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
```
tests/alltests.json
|
v
BricsCAD Kommandozeile
| |
| SSG_RUN_ALL_TESTS |
@@ -18,14 +21,14 @@ BricsCAD Kommandozeile
| | |
v v |
tests/output/ |
kreisel_results.json |
kreisel_tests.dxf |
foerderer_results.json |
foerderer_tests.dxf |
kseinaus_results.json |
kseinaus_tests.dxf |
omniflo_results.json |
omniflo_tests.dxf |
kreisel_results.json (immer) |
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 |
|
bin\run_tests.bat --runall
@@ -36,6 +39,36 @@ tests/output/ |
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
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
```
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
;; Einzeltest (z.B. Kreisel)
;; Test-Datei laden und Befehl ausfuehren
(load (strcat (getenv "DXFMAKRO") "/tests/test_kreisel.lsp"))
TEST_KREISEL
;; Ergebnisse manuell exportieren:
;; JSON-Export manuell aufrufen:
(kreisel:export-results (strcat (getenv "DXFMAKRO") "/tests/output"))
```
`SSG_RUN_ALL_TESTS` fuehrt fuer jedes Modul in `*test-module*` folgendes aus:
- Zeichnung leeren (ERASE + PURGE)
- Testdatei laden und Befehl ausfuehren
- JSON-Export aufrufen (`<name>:export-results`)
- DXF speichern (`<name>_tests.dxf`)
- Bei Fehler: weiter mit naechstem Test (vl-catch-all-apply)
**Unterschied SSG_RUN_ALL_TESTS vs. Einzeltest:**
| | SSG_RUN_ALL_TESTS | Einzeltest |
| --- | --- | --- |
| Testliste | aus `alltests.json` | manuell gewaehlter Test |
| 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
@@ -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
(setq *test-module* '("kreisel" "foerderer" "kseinaus" "omniflo" "<modul>"))
```json
{ "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:
- `omni:load-json` erwartet ein **flaches JSON-Array** mit flachen Objekten (kein Nesting)
- `omni:val` liest Werte aus den geladenen Eintraegen
- 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
;; und verschiedenen Hoehenstufen. Speichert:
;; - tests/output/foerderer_results.json (Ergebnisse)
;; - tests/output/foerderer_tests.dxf (Zeichnung)
;; - tests/output/foerderer_tests.dwg (Zeichnung)
;;
;; Voraussetzungen:
;; - 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
pt blockEnt ed attribs block-name
block-handle insert-point)
(setq id (omni:val eintrag "id"))
(setq x (omni:val eintrag "x"))
(setq y (omni:val eintrag "y"))
(setq z (omni:val eintrag "z"))
(setq abstand (omni:val eintrag "abstand"))
(setq rotation (omni:val eintrag "rotation"))
(setq typ (omni:val eintrag "typ"))
(setq id (ssg-val eintrag "id"))
(setq x (ssg-val eintrag "x"))
(setq y (ssg-val eintrag "y"))
(setq z (ssg-val eintrag "z"))
(setq abstand (ssg-val eintrag "abstand"))
(setq rotation (ssg-val eintrag "rotation"))
(setq typ (ssg-val eintrag "typ"))
(if (numberp x) (setq x (float x)))
(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
ptStart ptEnd blockEnt ed attribs
block-name block-handle insert-point)
(setq id (omni:val eintrag "id"))
(setq sx (omni:val eintrag "start_x"))
(setq sy (omni:val eintrag "start_y"))
(setq sz (omni:val eintrag "start_z"))
(setq ex (omni:val eintrag "end_x"))
(setq ey (omni:val eintrag "end_y"))
(setq ez (omni:val eintrag "end_z"))
(setq typ (omni:val eintrag "typ"))
(setq id (ssg-val eintrag "id"))
(setq sx (ssg-val eintrag "start_x"))
(setq sy (ssg-val eintrag "start_y"))
(setq sz (ssg-val eintrag "start_z"))
(setq ex (ssg-val eintrag "end_x"))
(setq ey (ssg-val eintrag "end_y"))
(setq ez (ssg-val eintrag "end_z"))
(setq typ (ssg-val eintrag "typ"))
(if (numberp sx) (setq sx (float sx)))
(if (numberp sy) (setq sy (float sy)))
@@ -194,7 +194,7 @@
(princ)
)
(progn
(setq testfaelle (omni:load-json tests-json-pfad))
(setq testfaelle (ssg-load-json tests-json-pfad))
(if (null testfaelle)
(progn
(princ "\n[TEST_KREISEL] FEHLER: Testdaten konnten nicht geladen werden.")
@@ -213,7 +213,7 @@
;; Testfaelle ausfuehren
(foreach eintrag testfaelle
(setq func (omni:val eintrag "function"))
(setq func (ssg-val eintrag "function"))
(setq result-json
(cond
((= 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
;;
@@ -107,15 +148,11 @@
;; exportiert CSV nach tests/output/.
;; ============================================================
(defun c:TEST_OMNIFLO (/ tests-json-pfad testfaelle
x-pos x-schritt
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)
(if (not *lib-initialized*)
(init-bibliothek)
)
(ssg-start "TEST_OMNIFLO"
'(("OSMODE") ("ATTREQ") ("ATTDIA")))
(setvar "OSMODE" 0)
@@ -134,7 +171,7 @@
(princ)
)
(progn
(setq testfaelle (omni:load-json tests-json-pfad))
(setq testfaelle (ssg-load-json tests-json-pfad))
(if (null testfaelle)
(progn
(princ "\n[TEST_OMNIFLO] FEHLER: Testdaten konnten nicht geladen werden.")
@@ -148,8 +185,6 @@
(princ "\n================================================================")
;; Alle Bloecke einfuegen
(setq x-pos 0.0)
(setq x-schritt 200.0)
(setq anzahl-ok 0)
(setq anzahl-fehler 0)
(setq results-list nil)
@@ -157,24 +192,31 @@
(foreach eintrag testfaelle
(setq idx (1+ idx))
(setq sivasnr (omni:val eintrag "sivasnr"))
(setq hoehe-str (omni:val eintrag "hoehe"))
(setq sivasnr (ssg-val eintrag "sivasnr"))
(setq hoehe-str (ssg-val eintrag "hoehe"))
(if (null hoehe-str) (setq hoehe-str "2000"))
(if (numberp hoehe-str)
(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 (numberp drehung-str)
(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) ". "
(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)
(progn
@@ -193,9 +235,9 @@
)
)
(setq x-pos (+ x-pos x-schritt))
)
;; Zusammenfassung
(princ "\n================================================================")
(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 (
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)
# 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):
"""Anzahl exportierter Elemente muss Anzahl Testfaelle entsprechen."""
assert len(self.items) == len(self.tests), (
@@ -348,3 +358,71 @@ class TestOmnifloExportCSV:
assert not 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
;; 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" "")
)
+1145 -80
View File
File diff suppressed because it is too large Load Diff