[CHANGE] VF-Linienzug: Winkel-Standardisierung + Glied-Schema + Vario-Kurve editierbar

Winkel im Linienzug durchgaengig als echter Wert (30/60/90) statt Auswahl-
Index (1/2/3) journalisiert - behebt den Bug, bei dem ein verrutschter
String-Wert zum falschen Bogen fuehrte (vfl-menu-winkel/-winkel-live/
-winkel-normieren, GF-Bogen und Vario-Kurve).

Zentrales Glied-Schema (*vfl-glied-schema*) als single source of truth fuer
AS/ES/GF-Bogen/Vario-Kurve: generisches bauen/lesen/validieren, benanntes
Debug-Log (GF-Bogen.winkel = 90 statt roher [INT]-Zeilen). VF-Einheit ueber
Sub-Step-Referenz (*vfl-vf-substeps*) benannt geloggt, ohne sie in ein starres
Feld-Schema zu zwingen.

Vario-Kurve als eigenes Sub-Glied editierbar (eigener STEP-Marker wie ES):
vfl-vario-slice-bauen/-werte/-splicen, vfl-glied-vario-dialog, Dispatcher-Zweig,
Label. Schwanz-erhaltender Splice (vfl-vario-slice-splicen), da die Vario-Kurve
mitten in der VF-Einheit steht und ihr Slice nachfolgende VF-Antworten enthaelt
- naiver Splice haette diese verworfen (adversarial verifiziert). Uebrige
VF-Sub-Segmente bleiben bewusst nur ueber Sektions-Ruecksprung editierbar.

Neuer Unittest tests/test_vfl_schema.lsp (TEST_VFL_SCHEMA, in alltests.json):
Roundtrip + Validierung + Vario-Schwanz-Splice ohne Geometrie.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-28 23:12:30 +02:00
parent cd31380a91
commit c36d2774bf
5 changed files with 879 additions and 82 deletions
+1
View File
@@ -2,6 +2,7 @@
{ "name": "kreisel", "save": "dxf", "module": "KreiselInsert" },
{ "name": "foerderer", "save": "dwg", "module": "VarioFoerderer" },
{ "name": "linienzug", "save": "dwg", "module": "VarioFoerderer" },
{ "name": "vfl_schema", "module": "VarioFoerderer" },
{ "name": "omniflo", "save": "dxf", "module": "OmniModulInsert" },
{ "name": "omniflo_strecke", "save": "dxf", "module": "OmniModulInsert" },
{ "name": "gefaellestrecke", "save": "dwg", "module": "Gefaellestrecke" },
+234
View File
@@ -0,0 +1,234 @@
;; ============================================================
;; test_vfl_schema.lsp - Unittest fuer das Glied-Schema (vf_linienzug.lsp)
;;
;; Prueft die reinen Schema-Helfer OHNE Geometrie/BricsCAD-Bau:
;; - vfl-schema-slice-bauen / vfl-schema-slice-werte (Roundtrip)
;; - vfl-schema-feld-pruefen (Validierung Typ + erlaubte Werte)
;; - die Adapter vfl-gf-bogen-slice-bauen/-werte, vfl-es-slice-bauen/-werte
;; (Kompatibilitaet zum bisherigen flachen Journal-Format)
;;
;; Voraussetzung: VarioFoerderer.lsp (inkl. vf_linienzug.lsp) geladen.
;;
;; Aufruf in BricsCAD:
;; (load (strcat (getenv "DXFMAKRO") "/tests/test_vfl_schema.lsp"))
;; TEST_VFL_SCHEMA
;;
;; Speichert tests/output/vfl_schema_results.json (Konvention test_run_all).
;; ============================================================
(setq *tvs-pass* 0 *tvs-fail* 0 *tvs-fails* '())
;; Ein Assert: erwartet-Wert gleich ist-Wert (equal). Loggt Pass/Fail.
(defun tvs-eq (name erwartet ist)
(if (equal erwartet ist)
(progn (setq *tvs-pass* (1+ *tvs-pass*))
(princ (strcat "\n [PASS] " name)))
(progn (setq *tvs-fail* (1+ *tvs-fail*))
(setq *tvs-fails* (cons name *tvs-fails*))
(princ (strcat "\n [FAIL] " name
"\n erwartet: " (vl-princ-to-string erwartet)
"\n ist: " (vl-princ-to-string ist))))))
;; Assert: Ausdruck ist nicht-nil (z.B. ein Fehlertext von der Validierung).
(defun tvs-truthy (name ist)
(if ist
(progn (setq *tvs-pass* (1+ *tvs-pass*))
(princ (strcat "\n [PASS] " name)))
(progn (setq *tvs-fail* (1+ *tvs-fail*))
(setq *tvs-fails* (cons name *tvs-fails*))
(princ (strcat "\n [FAIL] " name " (erwartet nicht-nil, war nil)")))))
;; Assert: Ausdruck ist nil (z.B. kein Validierungsfehler).
(defun tvs-nil (name ist)
(if (null ist)
(progn (setq *tvs-pass* (1+ *tvs-pass*))
(princ (strcat "\n [PASS] " name)))
(progn (setq *tvs-fail* (1+ *tvs-fail*))
(setq *tvs-fails* (cons name *tvs-fails*))
(princ (strcat "\n [FAIL] " name
" (erwartet nil, war " (vl-princ-to-string ist) ")")))))
(defun vfl-schema:tests ( / slice werte)
;; --- GF-Bogen: bauen erzeugt exakt die bisherige flache Struktur ---
;; STEP("GF-Bogen") STR("1") INT(90) STR("1")
(setq slice (vfl-gf-bogen-slice-bauen 90 "1"))
(tvs-eq "gf-bogen bauen: struktur"
(list (cons "STEP" "GF-Bogen") (cons "STR" "1")
(cons "INT" 90) (cons "STR" "1"))
slice)
;; --- GF-Bogen: Roundtrip bauen -> lesen ergibt (winkel seite) ---
(tvs-eq "gf-bogen roundtrip 60/rechts"
(list 60 "2")
(vfl-gf-bogen-slice-werte (vfl-gf-bogen-slice-bauen 60 "2")))
(tvs-eq "gf-bogen roundtrip 30/links"
(list 30 "1")
(vfl-gf-bogen-slice-werte (vfl-gf-bogen-slice-bauen 30 "1")))
;; --- GF-Bogen: die Seite ist der STR NACH dem INT (nicht der menue-STR) ---
;; Genau der Fall, an dem das alte "nach-int"-Flag haengt.
(tvs-eq "gf-bogen lesen: seite = STR nach INT"
(list 90 "2")
(vfl-gf-bogen-slice-werte
(list (cons "STEP" "GF-Bogen") (cons "STR" "1")
(cons "INT" 90) (cons "STR" "2"))))
;; --- GF-Bogen: Alt-Journal mit Index (1/2/3) statt Winkel wird auf den
;; Default 90 normiert (harte Umstellung, dokumentiertes Verhalten). ---
(tvs-eq "gf-bogen lesen: alt-index -> default 90"
(list 90 "1")
(vfl-gf-bogen-slice-werte
(list (cons "STEP" "GF-Bogen") (cons "STR" "1")
(cons "INT" 2) (cons "STR" "1"))))
;; --- ES: bauen/lesen Roundtrip (Winkel als STR "30"/"90") ---
(setq slice (vfl-es-slice-bauen "30" "2"))
(tvs-eq "es bauen: struktur"
(list (cons "STEP" "ES") (cons "STR" "30") (cons "STR" "2"))
slice)
(tvs-eq "es roundtrip 90/links"
(list "90" "1")
(vfl-es-slice-werte (vfl-es-slice-bauen "90" "1")))
;; --- Vario-Kurve: generisch bauen/lesen (winkel INT, seite/variante STR) ---
(setq slice (vfl-schema-slice-bauen "Vario-Kurve" (list 60 "2" "1")))
(tvs-eq "vario bauen: struktur"
(list (cons "STEP" "Vario-Kurve") (cons "INT" 60)
(cons "STR" "2") (cons "STR" "1"))
slice)
(tvs-eq "vario roundtrip"
(list 90 "1" "2")
(vfl-schema-slice-werte "Vario-Kurve"
(vfl-schema-slice-bauen "Vario-Kurve" (list 90 "1" "2"))
(list 90 "1" "1")))
;; --- Vario-Kurve Edit-Adapter (vfl-vario-slice-bauen/-werte) ---
;; Slice: STEP INT(winkel) STR(seite) STR(variante). Roundtrip inkl. Winkel-
;; Normierung (analog GF-Bogen).
(setq slice (vfl-vario-slice-bauen 60 "2" "1"))
(tvs-eq "vario-adapter bauen: struktur"
(list (cons "STEP" "Vario-Kurve") (cons "INT" 60)
(cons "STR" "2") (cons "STR" "1"))
slice)
(tvs-eq "vario-adapter roundtrip 30/rechts/innen"
(list 30 "2" "2")
(vfl-vario-slice-werte (vfl-vario-slice-bauen 30 "2" "2")))
;; Alt-Journal/Fremdwert im Winkel -> Default 90 (Normierung)
(tvs-eq "vario-adapter: fremdwinkel -> default 90"
(list 90 "1" "2")
(vfl-vario-slice-werte
(list (cons "STEP" "Vario-Kurve") (cons "INT" 2)
(cons "STR" "1") (cons "STR" "2"))))
;; Vario-Kurve ist jetzt im Schema (eigenes STEP-Glied)
(tvs-truthy "vario-kurve hat schema (eigenes glied)"
(vfl-schema-felder "Vario-Kurve"))
;; --- Vario-Kurve schwanz-erhaltende Splice (Regressionstest) ---
;; Die Vario-Kurve steht MITTEN in der VF-Einheit; nach ihren 3 Werten folgen
;; VF-Antworten in derselben Slice. Beim Editieren duerfen NUR die 3 Vario-
;; Felder ersetzt werden, der Schwanz muss erhalten bleiben (sonst verschiebt
;; sich beim Replay die Kette). alt-slice mit Schwanz STR("2") STR("1"):
(setq slice (list (cons "STEP" "Vario-Kurve") (cons "INT" 90)
(cons "STR" "1") (cons "STR" "2")
(cons "STR" "2") (cons "STR" "1"))) ; <- Schwanz (VF-Antworten)
(tvs-eq "vario splice: 3 felder ersetzt, schwanz erhalten"
(list (cons "STEP" "Vario-Kurve") (cons "INT" 60)
(cons "STR" "2") (cons "STR" "1")
(cons "STR" "2") (cons "STR" "1")) ; Schwanz unveraendert
(vfl-vario-slice-splicen slice 60 "2" "1"))
;; Reader liest trotz Schwanz korrekt die ersten 3 Felder (Vorbelegung):
(tvs-eq "vario werte-lesen ignoriert schwanz"
(list 90 "1" "2")
(vfl-vario-slice-werte slice))
;; --- Validierung: gueltige Werte -> kein Fehler ---
(tvs-nil "validierung gf-bogen winkel 90 ok"
(vfl-schema-feld-pruefen '("winkel" "INT" (30 60 90)) 90))
(tvs-nil "validierung seite '2' ok"
(vfl-schema-feld-pruefen '("seite" "STR" ("1" "2")) "2"))
;; --- Validierung: falscher Typ -> Fehlertext (nicht-nil) ---
;; Genau der Bug: ein STRING "30" in einem INT-Winkelfeld.
(tvs-truthy "validierung winkel als STRING -> fehler"
(vfl-schema-feld-pruefen '("winkel" "INT" (30 60 90)) "30"))
;; --- Validierung: Winkel ausserhalb der erlaubten Werte -> Fehlertext ---
(tvs-truthy "validierung winkel 45 nicht erlaubt -> fehler"
(vfl-schema-feld-pruefen '("winkel" "INT" (30 60 90)) 45))
;; --- Validierung: Seite ausserhalb ("3") -> Fehlertext ---
(tvs-truthy "validierung seite '3' nicht erlaubt -> fehler"
(vfl-schema-feld-pruefen '("seite" "STR" ("1" "2")) "3"))
;; --- Schema-Zugriff: unbekanntes Glied (VF-Einheit) -> keine Felder ---
(tvs-nil "vf-einheit hat kein schema"
(vfl-schema-felder "Horizontal-VF"))
;; --- AS ist bewusst kein STEP-Glied -> slice-bauen liefert nil ---
(tvs-nil "as slice-bauen ist nil (praeambel-sonderfall)"
(vfl-schema-slice-bauen "AS" (list "90" "1")))
;; --- VF-Einheit: Sub-Step-Benennung nach Wert-Muster ---
;; 1. STR einer VF-Einheit = fuehrende Menue-Wahl "4" (Horizontal-VF)
(tvs-eq "vf-substep: 1. STR = menue"
"menue"
(car (vfl-vf-substep-benennen (cons "STR" "4") 0)))
(tvs-nil "vf-substep: menue '4' ist gueltig"
(cdr (vfl-vf-substep-benennen (cons "STR" "4") 0)))
;; menue-Wert ausserhalb ("2") -> Fehlertext
(tvs-truthy "vf-substep: menue '2' ungueltig -> fehler"
(cdr (vfl-vf-substep-benennen (cons "STR" "2") 0)))
;; spaetere STR "3" = 1-3-Menue-Schritt (ist-ende/naechstes-vf)
(tvs-eq "vf-substep: spaetere STR '3' = menue3"
"ist-ende/naechstes-vf"
(car (vfl-vf-substep-benennen (cons "STR" "3") 1)))
;; spaetere STR "2" = ja/nein bzw. 1-3 (1/2)
(tvs-eq "vf-substep: spaetere STR '2' = wahl(1/2)"
"wahl(1/2)"
(car (vfl-vf-substep-benennen (cons "STR" "2") 1)))
;; DL/REAL = Laenge
(tvs-eq "vf-substep: DL = laenge"
"laenge"
(car (vfl-vf-substep-benennen (cons "DL" 4566.7) 0)))
;; eingebettete Vario-Kurve: INT-Winkel gueltig
(tvs-nil "vf-substep: eingebetteter vario-winkel 60 gueltig"
(cdr (vfl-vf-substep-benennen (cons "INT" 60) 1)))
(tvs-truthy "vf-substep: eingebetteter vario-winkel 45 ungueltig"
(cdr (vfl-vf-substep-benennen (cons "INT" 45) 1)))
(princ))
;; Ergebnis-Export (Konvention <name>:export-results fuer test_run_all.lsp)
(defun vfl_schema:export-results (out-dir / f pfad)
(setq pfad (strcat out-dir "/vfl_schema_results.json"))
(setq f (open pfad "w"))
(if f
(progn
(write-line "{" f)
(write-line (strcat " \"pass\": " (itoa *tvs-pass*) ",") f)
(write-line (strcat " \"fail\": " (itoa *tvs-fail*) ",") f)
(write-line (strcat " \"status\": \""
(if (= *tvs-fail* 0) "OK" "FEHLER") "\"") f)
(write-line "}" f)
(close f)
(princ (strcat "\n Ergebnis: " pfad))))
(princ))
(defun c:TEST_VFL_SCHEMA ( / )
(setq *tvs-pass* 0 *tvs-fail* 0 *tvs-fails* '())
(princ "\n================================================================")
(princ "\n TEST_VFL_SCHEMA - Glied-Schema (vf_linienzug.lsp)")
(princ "\n================================================================")
(vfl-schema:tests)
(princ "\n\n----------------------------------------------------------------")
(princ (strcat "\n Ergebnis: " (itoa *tvs-pass*) " PASS, "
(itoa *tvs-fail*) " FAIL"))
(if (> *tvs-fail* 0)
(progn
(princ "\n Fehlgeschlagen:")
(foreach n (reverse *tvs-fails*) (princ (strcat "\n - " n)))))
(princ "\n================================================================")
(princ))
(princ "\nTEST_VFL_SCHEMA geladen. Aufruf: TEST_VFL_SCHEMA")
(princ)