Merge gemacht. Versuch eines Tests der lisp Routinen erstellt

This commit is contained in:
2026-05-20 16:31:51 +02:00
parent c2c31898e8
commit 4eb5fc92fc
27 changed files with 3948 additions and 4659 deletions
+99
View File
@@ -0,0 +1,99 @@
;; ============================================================
;; ILS_test.lsp - Automatisierter ILS Kreisel-Test
;;
;; Oeffnet ILStest.dxf, erzeugt 5 Kreisel per draw-module
;; (gleiche Kernfunktion wie KreiselConnect/KreiselInsert),
;; exportiert CSV und speichert.
;;
;; Voraussetzung: SSG_LIB muss geladen sein (ssg_load.lsp).
;; ============================================================
(defun c:ILSTEST ( / test-pfad dxf-pfad abstand z-hoehe)
(princ "\n========== ILS TEST START ==========")
(setq test-pfad (getenv "DXFM_TESTS"))
(if (null test-pfad)
(progn (princ "\nFEHLER: DXFM_TESTS nicht gesetzt!") (exit))
)
;; DXF oeffnen (erzeugt durch create_ILStest_dxf.py)
(setq dxf-pfad (strcat test-pfad "/ILStest.dxf"))
(if (not (findfile dxf-pfad))
(progn (princ (strcat "\nFEHLER: " dxf-pfad " nicht gefunden!")) (exit))
)
(command "_.OPEN" dxf-pfad)
;; Konstanten fuer die Geometrie
;; Linien: H1 bei Y=0, H2 bei Y=5000, V1 bei X=0, V2 bei X=5000
;; Alle auf Z=2500, je 2000mm lang
;; Kreiseldurchmesser=800 -> Abstand = Linienabstand - Durchmesser
(setq abstand (- 5000.0 *kreisel-durchmesser*)) ;; 4200mm
(setq z-hoehe 2500.0)
(ssg-start "ILS TEST" '(("OSMODE") ("CECOLOR") ("ATTREQ") ("ATTDIA")))
(setvar "OSMODE" 0)
(setvar "ATTREQ" 0)
(setvar "ATTDIA" 0)
;; ---- Kreisel 1: Horizontal, oben->unten (90°), STANDARD ----
;; AN-Punkt auf H2 (Y=5000), SP-Richtung nach unten (Y=0)
;; Rotation 270° = von oben nach unten
(princ "\n[TEST] Kreisel 1: Horizontal oben->unten, STANDARD")
(draw-module (list 500.0 5000.0 z-hoehe)
abstand 270.0
(list (cons "KREISELART" "STANDARD")
(cons "NAME" "TEST_H_ObenUnten")))
;; ---- Kreisel 2: Horizontal, unten->oben (270°), PIN ----
;; AN-Punkt auf H1 (Y=0), SP-Richtung nach oben (Y=5000)
;; Rotation 90° = von unten nach oben
(princ "\n[TEST] Kreisel 2: Horizontal unten->oben, PIN")
(draw-module (list 1500.0 0.0 z-hoehe)
abstand 90.0
(list (cons "KREISELART" "PIN")
(cons "NAME" "TEST_H_UntenOben")))
;; ---- Kreisel 3: Vertikal, links->rechts (0°), PIN ----
;; AN-Punkt auf V1 (X=0), SP-Richtung nach rechts (X=5000)
;; Rotation 0° = von links nach rechts
(princ "\n[TEST] Kreisel 3: Vertikal links->rechts, PIN")
(draw-module (list 0.0 500.0 z-hoehe)
abstand 0.0
(list (cons "KREISELART" "PIN")
(cons "NAME" "TEST_V_LinksRechts")))
;; ---- Kreisel 4: Vertikal, rechts->links (180°), STANDARD ----
;; AN-Punkt auf V2 (X=5000), SP-Richtung nach links (X=0)
;; Rotation 180° = von rechts nach links
(princ "\n[TEST] Kreisel 4: Vertikal rechts->links, STANDARD")
(draw-module (list 5000.0 1500.0 z-hoehe)
abstand 180.0
(list (cons "KREISELART" "STANDARD")
(cons "NAME" "TEST_V_RechtsLinks")))
;; ---- Kreisel 5: KreiselInsert-Equivalent ----
;; Position 3000/3000/2000, 6000mm lang, PIN, 10 Grad
(princ "\n[TEST] Kreisel 5: Insert 3000/3000/2000, 6000mm, PIN, 10°")
(draw-module (list 3000.0 3000.0 2000.0)
6000.0 10.0
(list (cons "KREISELART" "PIN")
(cons "NAME" "TEST_Insert")))
(ssg-end)
;; Speichern als DXF
(command "_.SAVEAS" "DXF" (strcat test-pfad "/ILStest.dxf") "_Y")
(princ "\n[TEST] DXF gespeichert.")
;; Export CSV (nutzt csv:run-export aus tests.lsp)
;; Ergebnisse landen in DXFM_RESULTS
(princ "\n[TEST] Starte CSV-Export...")
(csv:run-export "ILSTEST" "export_csv.py" "ILStest_export.csv")
(princ "\n========== ILS TEST ENDE ==========")
(princ)
)
(princ "\nILS_test.lsp geladen. Befehl: ILSTEST")
(princ)
+5
View File
@@ -0,0 +1,5 @@
(load (strcat (getenv "DXFM_LISP") "/on_start.lsp"))
_.OPEN
(strcat (getenv "DXFM_TESTS") "/ILStest.dxf")
(load (strcat (getenv "DXFM_TESTS") "/ILS_test.lsp"))
ILSTEST
+3174
View File
File diff suppressed because it is too large Load Diff
+44
View File
@@ -0,0 +1,44 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
create_ILStest_dxf.py - Erzeugt die Basis-DXF fuer den ILS-Test.
Erstellt 4 Linien als Grundlage fuer KreiselConnect:
- 2 horizontale Linien, 5000mm Abstand, je 2000mm lang, ab (0,0,2500)
- 2 vertikale Linien, 5000mm Abstand, je 2000mm lang, ab (0,0,2500)
"""
import ezdxf
import sys
import os
def create_ils_test_dxf(output_path):
doc = ezdxf.new("R2018")
msp = doc.modelspace()
z = 2500.0
# Horizontale Linienpaare (Abstand 5000mm in Y-Richtung)
# H1: (0,0,2500) -> (2000,0,2500)
msp.add_line((0, 0, z), (2000, 0, z), dxfattribs={"layer": "S_LP", "color": 1})
# H2: (0,5000,2500) -> (2000,5000,2500)
msp.add_line((0, 5000, z), (2000, 5000, z), dxfattribs={"layer": "S_LP", "color": 1})
# Vertikale Linienpaare (Abstand 5000mm in X-Richtung)
# V1: (0,0,2500) -> (0,2000,2500)
msp.add_line((0, 0, z), (0, 2000, z), dxfattribs={"layer": "S_LP", "color": 1})
# V2: (5000,0,2500) -> (5000,2000,2500)
msp.add_line((5000, 0, z), (5000, 2000, z), dxfattribs={"layer": "S_LP", "color": 1})
doc.saveas(output_path)
print(f"[ILStest] DXF erstellt: {output_path}")
return output_path
if __name__ == "__main__":
if len(sys.argv) > 1:
out = sys.argv[1]
else:
out = os.path.join(os.getenv("DXFM_TESTS", "tests"), "ILStest.dxf")
create_ils_test_dxf(out)
+6
View File
@@ -0,0 +1,6 @@
Elementnummer;TeileArt;TeileId;NachbarIds;Bezeichnung;Planquadrat;rotation;Merkmale
0;"ILS 2.0 Kreisel";"REF_PLACEHOLDER";;"Kreisel :1";"X:500.00 Y:5000.00";;{"Abstand (Kreiselachse A - Kreiselachse) in Meter": "4.2", "Anzahl der Separatoren": "2", "Kreiselart": "STANDARD", "Anzahl der Scanner": "0", "Anzahl der Rampen": "0", "Höhe in m": "2.5", "Drehrichtung": "UZS", "Drehung": 270, "Name": "TEST_H_ObenUnten"}
1;"ILS 2.0 Kreisel";"REF_PLACEHOLDER";;"Kreisel :2";"X:1500.00 Y:0.00";;{"Abstand (Kreiselachse A - Kreiselachse) in Meter": "4.2", "Anzahl der Separatoren": "2", "Kreiselart": "PIN", "Anzahl der Scanner": "0", "Anzahl der Rampen": "0", "Höhe in m": "2.5", "Drehrichtung": "UZS", "Drehung": 90, "Name": "TEST_H_UntenOben"}
2;"ILS 2.0 Kreisel";"REF_PLACEHOLDER";;"Kreisel :3";"X:0.00 Y:500.00";;{"Abstand (Kreiselachse A - Kreiselachse) in Meter": "4.2", "Anzahl der Separatoren": "2", "Kreiselart": "PIN", "Anzahl der Scanner": "0", "Anzahl der Rampen": "0", "Höhe in m": "2.5", "Drehrichtung": "UZS", "Drehung": 0, "Name": "TEST_V_LinksRechts"}
3;"ILS 2.0 Kreisel";"REF_PLACEHOLDER";;"Kreisel :4";"X:5000.00 Y:1500.00";;{"Abstand (Kreiselachse A - Kreiselachse) in Meter": "4.2", "Anzahl der Separatoren": "2", "Kreiselart": "STANDARD", "Anzahl der Scanner": "0", "Anzahl der Rampen": "0", "Höhe in m": "2.5", "Drehrichtung": "UZS", "Drehung": 180, "Name": "TEST_V_RechtsLinks"}
4;"ILS 2.0 Kreisel";"REF_PLACEHOLDER";;"Kreisel :5";"X:3000.00 Y:3000.00";;{"Abstand (Kreiselachse A - Kreiselachse) in Meter": "6.0", "Anzahl der Separatoren": "2", "Kreiselart": "PIN", "Anzahl der Scanner": "0", "Anzahl der Rampen": "0", "Höhe in m": "2.0", "Drehrichtung": "UZS", "Drehung": 10, "Name": "TEST_Insert"}
Can't render this file because it contains an unexpected character in line 2 and column 76.
+232
View File
@@ -0,0 +1,232 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
validate_tests.py - Validiert ILS-Test Ergebnisse gegen Referenzdateien.
Aufruf:
python validate_tests.py <results_dir> <references_dir>
Prueft:
1. export_raw.json: Anzahl und Typ der Bloecke
2. ILStest_export.csv: Zeilenanzahl und Inhalte gegen Referenz
3. ILStest.dxf: Existenz und KR_-Block-Anzahl
"""
import json
import sys
import os
import csv
def load_json(path):
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def load_csv_lines(path):
"""Laedt CSV und gibt Liste von Dicts zurueck (ohne Header)."""
with open(path, "r", encoding="utf-8") as f:
return f.read().strip().split("\n")
def validate_raw_json(results_dir):
"""Prueft export_raw.json auf erwartete Kreisel-Bloecke."""
raw_path = os.path.join(results_dir, "export_raw.json")
if not os.path.exists(raw_path):
return False, "export_raw.json nicht gefunden"
blocks = load_json(raw_path)
kr_blocks = [b for b in blocks if b["block_name"].startswith("KR_")]
errors = []
if len(kr_blocks) != 5:
errors.append(f"Erwartet 5 KR_-Bloecke, gefunden: {len(kr_blocks)}")
# Pruefen ob alle Attribute haben
empty_attribs = [b for b in kr_blocks if not b.get("attribs")]
if empty_attribs:
errors.append(f"{len(empty_attribs)} Kreisel ohne Attribute: "
+ ", ".join(b["block_name"] for b in empty_attribs))
# Pruefen ob erwartete Namen vorhanden
expected_names = {
"TEST_H_ObenUnten", "TEST_H_UntenOben",
"TEST_V_LinksRechts", "TEST_V_RechtsLinks",
"TEST_Insert"
}
found_names = set()
for b in kr_blocks:
attribs = b.get("attribs", {})
name = attribs.get("NAME", "")
if name:
found_names.add(name)
missing = expected_names - found_names
if missing:
errors.append(f"Fehlende Kreisel-Namen: {missing}")
# Pruefen der spezifischen Werte
for b in kr_blocks:
attribs = b.get("attribs", {})
name = attribs.get("NAME", "")
if name == "TEST_Insert":
if attribs.get("ABSTAND") != "6000":
errors.append(f"TEST_Insert: ABSTAND={attribs.get('ABSTAND')}, erwartet 6000")
if attribs.get("KREISELART") != "PIN":
errors.append(f"TEST_Insert: KREISELART={attribs.get('KREISELART')}, erwartet PIN")
if abs(b.get("rotation", 0) - 10.0) > 0.1:
errors.append(f"TEST_Insert: rotation={b.get('rotation')}, erwartet 10")
if abs(b.get("z", 0) - 2000.0) > 0.1:
errors.append(f"TEST_Insert: z={b.get('z')}, erwartet 2000")
if name == "TEST_H_ObenUnten":
if attribs.get("KREISELART") != "STANDARD":
errors.append(f"TEST_H_ObenUnten: KREISELART={attribs.get('KREISELART')}, erwartet STANDARD")
if abs(b.get("rotation", 0) - 270.0) > 0.1:
errors.append(f"TEST_H_ObenUnten: rotation={b.get('rotation')}, erwartet 270")
if name == "TEST_H_UntenOben":
if attribs.get("KREISELART") != "PIN":
errors.append(f"TEST_H_UntenOben: KREISELART={attribs.get('KREISELART')}, erwartet PIN")
if name == "TEST_V_LinksRechts":
if attribs.get("KREISELART") != "PIN":
errors.append(f"TEST_V_LinksRechts: KREISELART={attribs.get('KREISELART')}, erwartet PIN")
if abs(b.get("rotation", 0) - 0.0) > 0.1:
errors.append(f"TEST_V_LinksRechts: rotation={b.get('rotation')}, erwartet 0")
if name == "TEST_V_RechtsLinks":
if attribs.get("KREISELART") != "STANDARD":
errors.append(f"TEST_V_RechtsLinks: KREISELART={attribs.get('KREISELART')}, erwartet STANDARD")
if errors:
return False, "export_raw.json Fehler:\n " + "\n ".join(errors)
return True, f"export_raw.json OK: {len(kr_blocks)} Kreisel mit Attributen"
def validate_csv(results_dir, references_dir):
"""Prueft CSV gegen Referenz (Zeilenanzahl, Kreisel-Anzahl)."""
csv_path = os.path.join(results_dir, "ILStest_export.csv")
ref_path = os.path.join(references_dir, "ILStest_export_ref.csv")
if not os.path.exists(csv_path):
return False, "ILStest_export.csv nicht gefunden"
csv_lines = load_csv_lines(csv_path)
kreisel_lines = [l for l in csv_lines[1:] if "ILS 2.0 Kreisel" in l]
errors = []
if len(kreisel_lines) != 5:
errors.append(f"Erwartet 5 Kreisel-Zeilen in CSV, gefunden: {len(kreisel_lines)}")
# Gegen Referenz vergleichen (falls vorhanden)
if os.path.exists(ref_path):
ref_lines = load_csv_lines(ref_path)
ref_kreisel = [l for l in ref_lines[1:] if "ILS 2.0 Kreisel" in l]
if len(kreisel_lines) != len(ref_kreisel):
errors.append(f"CSV hat {len(kreisel_lines)} Kreisel, Referenz hat {len(ref_kreisel)}")
# Vergleiche Merkmale (ohne TeileId, da UUID)
for i, (act, ref) in enumerate(zip(kreisel_lines, ref_kreisel)):
# Merkmale extrahieren (letztes Feld)
act_parts = act.split(";")
ref_parts = ref.split(";")
if len(act_parts) >= 8 and len(ref_parts) >= 8:
act_merkmale = act_parts[-1]
ref_merkmale = ref_parts[-1]
if act_merkmale != ref_merkmale:
errors.append(f"Zeile {i+1} Merkmale unterschiedlich:\n"
f" IST: {act_merkmale}\n"
f" SOLL: {ref_merkmale}")
else:
errors.append(f"Referenz-CSV nicht gefunden: {ref_path} (erster Lauf?)")
if errors:
return False, "CSV Fehler:\n " + "\n ".join(errors)
return True, f"CSV OK: {len(kreisel_lines)} Kreisel-Zeilen"
def validate_dxf(test_dir):
"""Prueft ob ILStest.dxf existiert und KR_-Bloecke enthaelt."""
dxf_path = os.path.join(test_dir, "ILStest.dxf")
if not os.path.exists(dxf_path):
return False, "ILStest.dxf nicht gefunden"
# Einfache Textsuche nach KR_-Blocknamen im DXF
with open(dxf_path, "r", encoding="utf-8", errors="ignore") as f:
content = f.read()
# Zaehle KR_-Blockreferenzen im ENTITIES-Bereich
entities_start = content.find("ENTITIES")
if entities_start < 0:
return False, "Kein ENTITIES-Bereich in DXF"
entities_section = content[entities_start:]
kr_count = 0
pos = 0
while True:
pos = entities_section.find("\nKR_", pos)
if pos < 0:
break
kr_count += 1
pos += 1
if kr_count < 5:
return False, f"Nur {kr_count} KR_-Referenzen in ENTITIES (erwartet mind. 5)"
return True, f"DXF OK: {kr_count} KR_-Referenzen gefunden"
def main():
if len(sys.argv) < 3:
print("Aufruf: python validate_tests.py <results_dir> <references_dir>")
print(" results_dir = Ordner mit Testergebnissen (z.B. results/)")
print(" references_dir = Ordner mit Referenzdateien (z.B. tests/references/)")
sys.exit(1)
results_dir = sys.argv[1]
references_dir = sys.argv[2]
test_dir = os.path.dirname(os.path.abspath(__file__))
print("=" * 60)
print("ILS Test Validierung")
print("=" * 60)
all_ok = True
# Test 1: export_raw.json
ok, msg = validate_raw_json(results_dir)
status = "PASS" if ok else "FAIL"
print(f"\n[{status}] {msg}")
if not ok:
all_ok = False
# Test 2: CSV Export
ok, msg = validate_csv(results_dir, references_dir)
status = "PASS" if ok else "FAIL"
print(f"\n[{status}] {msg}")
if not ok:
all_ok = False
# Test 3: DXF Datei
ok, msg = validate_dxf(test_dir)
status = "PASS" if ok else "FAIL"
print(f"\n[{status}] {msg}")
if not ok:
all_ok = False
print("\n" + "=" * 60)
if all_ok:
print("ERGEBNIS: ALLE TESTS BESTANDEN")
else:
print("ERGEBNIS: TESTS FEHLGESCHLAGEN")
print("=" * 60)
sys.exit(0 if all_ok else 1)
if __name__ == "__main__":
main()