diff --git a/Lisp/vf_linienzug.lsp b/Lisp/vf_linienzug.lsp index d8c227f..2b3fb25 100644 --- a/Lisp/vf_linienzug.lsp +++ b/Lisp/vf_linienzug.lsp @@ -190,6 +190,208 @@ (list (cdr e))) nil)) +;; ============================================================ +;; TEIL 0b-1b: GLIED-SCHEMA (single source of truth) +;; ============================================================ +;; Das Journal ist und bleibt eine flache (kind . value)-Liste - das Format auf +;; der XDATA aendert sich NICHT (keine Migration noetig, Replay unveraendert). +;; Neu ist nur eine zentrale, DEKLARATIVE Beschreibung, welche Felder in welcher +;; Reihenfolge und mit welchem Typ ein Glied ausmachen. Damit gibt es genau EINE +;; Stelle, die die Struktur eines Glieds kennt - Bauen, Lesen, Validieren und +;; das Debug-Log leiten sich alle daraus ab (kein Auseinanderlaufen von je-Typ- +;; slice-bauen/slice-werte mehr, aus dem der Winkel-Bug entstand). +;; +;; Ein Feld ist: (name kind werte). name = Feldname (fuers Log), kind = Journal- +;; kind ("STR"/"INT"/"REAL"/"PT"), werte = Liste erlaubter Werte oder nil (frei). +;; Das fuehrende STEP (Glied-Checkpoint) ist NICHT Teil der Feldliste - es wird +;; von den Slice-Helfern separat vorangestellt bzw. beim Lesen uebersprungen. +;; +;; Nur die FESTEN, kurzen Glieder sind hier erfasst (AS, ES, GF-Bogen, Vario- +;; Kurve). Die VF-Einheit/Horizontal-VF ist ein variabler Dialog-Fluss mit +;; optionsabhaengiger Feldzahl - sie ist bewusst KEIN Feld-Record und wird +;; weiter direkt behandelt (nicht einzeln editierbar, siehe vfl-edit-glied). +;; +;; Feldkonventionen (wie vom bestehenden Code erzeugt): +;; seite "1"=links / "2"=rechts +;; menue fuehrende "Naechstes Element waehlen"-Antwort (nur GF-Bogen: "1") +;; winkel GF-Bogen/Vario-Kurve als INT (30/60/90), AS/ES als STR ("30"/"90") +(setq *vfl-glied-schema* + (list + (cons "AS" + (list '("winkel" "STR" ("30" "90")) + '("seite" "STR" ("1" "2")))) + (cons "ES" + (list '("winkel" "STR" ("30" "90")) + '("seite" "STR" ("1" "2")))) + (cons "GF-Bogen" + (list '("menue" "STR" ("1")) + '("winkel" "INT" (30 60 90)) + '("seite" "STR" ("1" "2")))) + (cons "Vario-Kurve" + (list '("winkel" "INT" (90 60 30)) + '("seite" "STR" ("1" "2")) + '("variante" "STR" ("1" "2")))))) + +;; --- VF-Einheit: Sub-Step-Referenz (variable Laenge) --------------------- +;; Die VF-Einheit (Linie-VF / Horizontal-VF) ist KEIN Glied mit fester Feldzahl, +;; sondern eine SCHLEIFE, die beliebig viele Sub-Segmente aneinanderhaengt, bis +;; der Nutzer "Ende" waehlt. Sie kann ausserdem Vario-Kurven ENTHALTEN. Ein +;; starres Feld-Schema wie oben passt daher nicht. +;; +;; Diese Referenz beschreibt die EINZELNEN Teilfragen, die im VF-Fluss auftreten +;; koennen - jede fuer sich ein kleiner benannter Record. Anders als bei den +;; festen Gliedern ist die REIHENFOLGE hier verzweigungsabhaengig; die Benennung +;; im Log erfolgt deshalb ueber das Wert-Muster (nicht ueber eine feste +;; Position). Zweck: lesbares Log ("VF.sep-vor = ja") + Wert-Validierung, OHNE +;; den fragilen Bau-Fluss nachzubauen. min/max = wie oft die Teilfrage je +;; VF-Einheit auftreten kann (Doku-Wert; die Schleife macht max offen = nil). +;; +;; Teilfrage kind werte min max Bedeutung +;; menue STR "3"/"4" 1 1 Hauptmenue-Wahl Linie-VF/Horizontal-VF +;; laenge DL - 1 n Reststrecke (Punktwahl -> dL) +;; zielhoehe REAL - 0 n Zielhoehe (nur Auf/Ab bzw. Linie-VF) +;; sep-vor STR "1"/"2" 0 n Separator vor (horizontaler Koerper) +;; sep-nach STR "1"/"2" 0 n Separator nach (horizontaler Koerper) +;; ist-ende STR "1"/"2"/"3" 1 n "Ist Endpunkt der Foerderer?" (Schleife) +;; naechstes-vf STR "1"/"2"/"3" 0 n Fortsetzung Horiz./Vario-Kurve/Auf-Ab +;; es-gewuenscht STR "1"/"2" 0 1 ES setzen? (nur Kettenende-Option 3) +;; variokurve.* siehe Glied "Vario-Kurve" (eingebettet moeglich) +;; +;; Fuer die Log-Benennung (vfl-schema-log-vf) reicht die Unterscheidung nach +;; Wert-Muster: eine STR mit Wert "3" ist ein 1-3-Menue-Schritt, "1"/"2" eine +;; Ja/Nein-Frage; REAL/DL sind Laenge bzw. Hoehe. +(setq *vfl-vf-substeps* + (list + '("STR-menue" "STR" ("3" "4")) ; fuehrende Menue-Wahl + '("STR-janein" "STR" ("1" "2")) ; sep-vor/-nach, es-gewuenscht + '("STR-menue3" "STR" ("1" "2" "3")))) ; ist-ende, naechstes-vf + +;; Feldliste eines Glied-Typs (nil, wenn nicht im Schema - z.B. VF-Einheit). +(defun vfl-schema-felder (label) (cdr (assoc label *vfl-glied-schema*))) +(defun vfl-schema-feld-name (feld) (car feld)) +(defun vfl-schema-feld-kind (feld) (cadr feld)) +(defun vfl-schema-feld-werte (feld) (caddr feld)) + +;; Prueft EINEN Wert gegen eine Felddefinition. Rueckgabe: nil = ok, sonst ein +;; Fehlertext (fuer Log/Alert). Prueft (a) den Typ passend zum kind und (b) die +;; erlaubten Werte, falls im Feld angegeben. +(defun vfl-schema-feld-pruefen (feld val / kind werte typ-ok) + (setq kind (vfl-schema-feld-kind feld) werte (vfl-schema-feld-werte feld)) + (setq typ-ok + (cond ((= kind "STR") (= (type val) 'STR)) + ((= kind "INT") (= (type val) 'INT)) + ((= kind "REAL") (or (= (type val) 'REAL) (= (type val) 'INT))) + ((= kind "PT") (and (listp val) (= (length val) 3))) + (t t))) + (cond + ((not typ-ok) + (strcat (vfl-schema-feld-name feld) ": erwartet " kind ", ist " + (vl-princ-to-string (type val)))) + ((and werte (not (member val werte))) + (strcat (vfl-schema-feld-name feld) "=" (vl-princ-to-string val) + " nicht in erlaubten Werten " (vl-princ-to-string werte))) + (t nil))) + +;; Baut eine Journal-Slice aus einem Glied-Label und einer Werteliste (in +;; Feldreihenfolge). Stellt das STEP voran. AS ist ein Sonderfall (kein STEP - +;; Praeambel), daher hier NICHT erlaubt (nil-Rueckgabe). Validiert jeden Wert +;; vor dem Bauen; bei einem Fehler wird er als dbgmsg protokolliert, der Wert +;; aber trotzdem geschrieben (Sicherung geht vor - lieber ein warnendes Log als +;; ein verworfener Bau). Rueckgabe: forward-order Slice (Liste (kind . value)). +(defun vfl-schema-slice-bauen (label werte / felder out feld val fehler) + (setq felder (vfl-schema-felder label)) + (if (or (null felder) (= label "AS")) + nil + (progn + (setq out (list (cons "STEP" label))) + (while felder + (setq feld (car felder) val (car werte)) + (setq fehler (vfl-schema-feld-pruefen feld val)) + (if fehler (dbgmsg (strcat ">> SCHEMA-WARN " label "." fehler))) + (setq out (append out (list (cons (vfl-schema-feld-kind feld) val)))) + (setq felder (cdr felder) werte (cdr werte))) + out))) + +;; Liest die Feldwerte aus einer Glied-Slice (STEP + Felder) in Feldreihenfolge. +;; Nimmt der Reihe nach den naechsten Eintrag, dessen kind zum jeweiligen Feld +;; passt (ueberspringt das fuehrende STEP automatisch). Fehlt ein Wert oder +;; stimmt der Typ nicht, wird der (optionale) Default aus defaults an dieser +;; Position eingesetzt. Rueckgabe: Werteliste in Feldreihenfolge. +(defun vfl-schema-slice-werte (label slice defaults / felder rest out feld val gefunden) + (setq felder (vfl-schema-felder label) rest slice out '()) + (while felder + (setq feld (car felder) gefunden nil) + ;; naechsten passenden Eintrag suchen (STEP und Fremdtypen ueberspringen) + (while (and rest (not gefunden)) + (if (= (car (car rest)) (vfl-schema-feld-kind feld)) + (setq val (cdr (car rest)) gefunden t)) + (setq rest (cdr rest))) + (setq out (append out (list (if gefunden val (car defaults))))) + (setq felder (cdr felder) defaults (cdr defaults))) + out) + +;; Benanntes Debug-Log fuer eine ganze Glied-Slice: statt roher [INT] value=90- +;; Zeilen die Felder als "GF-Bogen.winkel = 90". No-Op ohne offene .dbg-Datei. +;; Wird beim Sichern (vfl-segment-xdata-sichern) genutzt. +(defun vfl-schema-log-slice (label slice / felder werte feld fehler) + (setq felder (vfl-schema-felder label)) + (if felder + (progn + (setq werte (vfl-schema-slice-werte label slice + (mapcar '(lambda (f) nil) felder))) + (dbgmsg (strcat ">> GLIED " label ":")) + (while felder + (setq feld (car felder)) + (setq fehler (vfl-schema-feld-pruefen feld (car werte))) + (dbgmsg (strcat " " (vfl-schema-feld-name feld) " = " + (vl-princ-to-string (car werte)) + (if fehler (strcat " Reststrecke + ((= kind "REAL") (cons "hoehe" nil)) ; Zielhoehe (Auf/Ab bzw. Linie-VF) + ((= kind "PT") (cons "punkt" nil)) + ((= kind "INT") ; eingebettete Vario-Kurve + (setq fehler (vfl-schema-feld-pruefen '("vk-winkel" "INT" (90 60 30)) val)) + (cons "variokurve.winkel" fehler)) + ((= kind "STR") + (cond + ((= pos-str 0) ; 1. STR = fuehrende Menue-Wahl + (setq fehler (vfl-schema-feld-pruefen '("menue" "STR" ("3" "4")) val)) + (cons "menue" fehler)) + ((member val '("3")) ; nur 1-3-Menue kann "3" sein + (cons "ist-ende/naechstes-vf" nil)) + ((member val '("1" "2")) ; ja/nein ODER 1-3 (1/2) + (cons "wahl(1/2)" nil)) + (t (cons "str" (strcat "unerwarteter STR-Wert " (vl-princ-to-string val)))))) + (t (cons kind nil)))) + +;; Benanntes Debug-Log fuer eine ganze VF-Einheit-Slice (variable Laenge). Statt +;; roher [STR]/[REAL]-Zeilen jede Teilfrage benannt ("VF.menue = 4", +;; "VF.laenge = 4566.7", "VF.wahl(1/2) = 2"). Validiert dabei, was validierbar +;; ist (menue-Wert, eingebettete Vario-Kurven-Winkel). No-Op ohne offene .dbg. +(defun vfl-schema-log-vf (label slice / rest e paar name fehler pos-str) + (dbgmsg (strcat ">> GLIED " label " (VF-Einheit, variable Laenge):")) + (setq rest slice pos-str 0) + (foreach e rest + (if (/= (car e) "STEP") + (progn + (setq paar (vfl-vf-substep-benennen e pos-str)) + (setq name (car paar) fehler (cdr paar)) + (dbgmsg (strcat " " name " = " (vl-princ-to-string (cdr e)) + (if fehler (strcat " = dat 0)) (unload_dialog dat)) @@ -439,14 +644,15 @@ (if (= ergebnis 1) (vflw-pending-push-all (list - (if (= winkel-kind "INT") (1+ (atoi gwinkel)) (nth (atoi gwinkel) '("90" "30"))) + (nth (atoi gwinkel) winkel-werte) (if (= gseite "1") "2" "1")))))) (princ)) ;; ------------------------------------------------------------ ;; Gruppe "Vario-Kurve": Winkel(90/60/30) + Seite + Variante(aussen/innen). -;; Original-Reihenfolge/-Journal: vfl-in-int (Winkel), vfl-in-string -;; (Seite), vfl-in-string (Variante). +;; Reihenfolge/Journal: vfl-menu-winkel (Winkel als ECHTER Wert 90/60/30, als +;; INT journalisiert - kein Index mehr), vfl-in-string (Seite), vfl-in-string +;; (Variante). (defun vflw-gruppe-variokurve-impl ( / dat dcl-pfad ergebnis gwinkel gseite gvariante) (setq dat (load_dialog (vflw-dcl-pfad))) (if (not (new_dialog "vflw_variokurve" dat)) @@ -479,7 +685,7 @@ (unload_dialog dat) (if (= ergebnis 1) (vflw-pending-push-all - (list (1+ (atoi gwinkel)) + (list (nth (atoi gwinkel) '(90 60 30)) (if (= gseite "1") "2" "1") (if (= gvariante "1") "2" "1")))))) (princ)) @@ -754,6 +960,50 @@ (setq *vflw-menu-frage* frage-key) (vfl-in-int prompt)) +;; Winkel-Menue: fragt einen WINKELWERT (30/60/90) ab und journalisiert ihn +;; als echten Winkel (INT-Eintrag = 30/60/90), NICHT als Auswahl-Index 1/2/3. +;; Damit ist jedes Journal/Log selbsterklaerend und alle GF-Bogen-/Vario-Kurve- +;; Stellen (Live-Bau, Slice, Edit) teilen denselben Wert. winkel-werte ist die +;; Liste der zur Optionsreihenfolge gehoerenden Winkel (z.B. (30 60 90) oder +;; (90 60 30)); default-winkel der vorbelegte Winkelwert. Bei Konsolen-Eingabe +;; tippt der Nutzer den Winkel direkt (30/60/90); ungueltige Eingaben fallen auf +;; den naechstliegenden gueltigen Wert bzw. den Default. Rueckgabe: Winkel (Int). +(defun vfl-menu-winkel (prompt option-labels winkel-werte default-winkel frage-key / + *vflw-menu-optionen* *vflw-menu-default* *vflw-menu-frage* v) + (setq *vflw-menu-optionen* option-labels) + (setq *vflw-menu-frage* frage-key) + ;; Default-Index = Position des Default-Winkels in winkel-werte (1-basiert). + (setq *vflw-menu-default* + (+ 1 (- (length winkel-werte) (length (member default-winkel winkel-werte))))) + (setq v (vfl-in-value "INT" + (function (lambda () (vfl-winkel-live prompt winkel-werte default-winkel))))) + ;; Absicherung: nur gueltige Winkel zulassen (auch bei Alt-Journals mit Index). + (vfl-winkel-normieren v winkel-werte default-winkel)) + +;; Live-Abfrage eines Winkelwerts. Wizard: Auswahl aus den Labels, Position -> +;; winkel-werte. Konsole: direkte Zahleneingabe (ENTER = Default). +(defun vfl-winkel-live (prompt winkel-werte default-winkel / vs idx r) + (if (and *vfl-wizard-mode* *vflw-menu-optionen*) + (progn + (setq vs (vflw-wahl (if *vflw-menu-frage* (ssg-text *vflw-menu-frage*) prompt) + *vflw-menu-optionen* *vflw-menu-default*)) + (setq idx (if (and vs (> (strlen vs) 0)) (atoi vs) 0)) + (if (and (>= idx 1) (<= idx (length winkel-werte))) + (nth (1- idx) winkel-werte) + default-winkel)) + (progn + (setq r (getint prompt)) + (if r r default-winkel)))) + +;; Normiert einen (moeglicherweise aus Alt-Journals stammenden Index oder einen +;; frei getippten Wert) auf einen gueltigen Winkel aus winkel-werte. Ein Wert, +;; der bereits gueltig ist, bleibt unveraendert. Ansonsten Default. +(defun vfl-winkel-normieren (v winkel-werte default-winkel / n) + (setq n (cond ((null v) default-winkel) + ((= (type v) 'STR) (atoi v)) + (T v))) + (if (member n winkel-werte) n default-winkel)) + ;; Generischer Wrapper fuer eine Eingabe, die nicht direkt ein getXXX ist, ;; sondern das Ergebnis einer fragenden Hilfsfunktion (z.B. die gemeinsame ;; vf-frage-element-winkel). livefn ist ein aufrufbares Objekt ohne Argumente; @@ -950,20 +1200,48 @@ ;; ergaenzt) und schreibt sie auf JEDES seit seg-lastEnt neu erzeugte Entity ;; (entnext-Wanderung, gleiches Muster wie vfl-block-erstellen/ ;; vfl-modus-abbruch-sichern). -(defun vfl-segment-xdata-sichern (seg-lastEnt wahl / forward-journal seg-idx seg-journal e) +(defun vfl-segment-xdata-sichern (seg-lastEnt wahl / forward-journal seg-idx seg-journal e cnt slice-label) (setq forward-journal (reverse *vfl-journal*)) (setq seg-idx (length (vfl-journal-glieder forward-journal))) + (setq cnt 0) (if (> seg-idx 0) (progn (setq seg-journal (vfl-journal-slice forward-journal seg-idx)) + ;; Tatsaechliches Label der geschnittenen Slice (der STEP-Wert am Anfang). + ;; Kann von wahl (dem Hauptschleifen-Label) ABWEICHEN: enthaelt die + ;; VF-Einheit eine Vario-Kurve, ist DEREN STEP das letzte Glied, seg-idx + ;; zeigt darauf, und die Slice ist die Vario-Kurve-Slice - waehrend wahl + ;; noch "Horizontal-VF"/"Linie-VF" ist. Fuers Log/die Validierung zaehlt + ;; das echte Slice-Label, nicht wahl (sonst falsch etikettiert). + (setq slice-label + (if (and seg-journal (= (car (car seg-journal)) "STEP")) + (cdr (car seg-journal)) wahl)) (setq e (if seg-lastEnt (entnext seg-lastEnt) (entnext))) (while e (vfl-journal-xdata-schreiben-seg e seg-idx seg-journal) + (setq cnt (1+ cnt)) (setq e (entnext e)) ) + ;; Diagnose: den tatsaechlichen Segment-Journal-Inhalt als JSON loggen + ;; (analog zum Ketten-XDATA-JOURNAL-JSON in vfl-journal-xdata-schreiben) + ;; plus die Anzahl der beschriebenen Entities. No-Op ohne offene .dbg. + (dbgmsg (strcat "SEGMENT-XDATA-JSON Glied " (itoa seg-idx) " (" + (if slice-label slice-label "?") ") auf " (itoa cnt) " Entity(s): " + (vfl-journal->json seg-journal))) + ;; Zusaetzlich benannt loggen (statt roher [INT]/[STR]-Zeilen). Feste + ;; Glieder (AS/ES/GF-Bogen/Vario-Kurve) ueber ihr Feld-Schema + ;; (GF-Bogen.winkel = 90), die VF-Einheit (Linie-VF/Horizontal-VF, variable + ;; Laenge) ueber den Sub-Step-Logger (VF.menue = 4, VF.laenge = ...). Beide + ;; validieren, was validierbar ist, und markieren Abweichungen (). + ;; Ueber slice-label (echtes Glied), nicht wahl. + (if (vfl-schema-felder slice-label) + (vfl-schema-log-slice slice-label seg-journal) + (if (member slice-label '("Linie-VF" "Horizontal-VF")) + (vfl-schema-log-vf slice-label seg-journal))) ) + (dbgmsg (strcat "SEGMENT-XDATA: kein Glied vorhanden (" (if wahl wahl "?") ")")) ) - (dbgmsg (strcat "SEGMENT-XDATA: Glied " (itoa seg-idx) " (" (if wahl wahl "?") ") gesichert")) + (dbgflush) (princ)) ;; --- Abbruch-Sicherung --- @@ -1078,6 +1356,9 @@ ((= w "Linie-VF") (ssg-text "vfl-glied-vf")) ((= w "Horizontal-VF") (ssg-text "vfl-glied-horizontal-vf")) ((= w "Linie") (ssg-text "vfl-glied-linie")) + ((= w "AS") (ssg-text "vfl-glied-as")) + ((= w "ES") (ssg-text "vfl-glied-es")) + ((= w "Vario-Kurve") (ssg-text "vfl-glied-vario")) ((= w "?") (ssg-text "vfl-glied-offen")) (t w))) @@ -2219,16 +2500,15 @@ (vl-catch-all-apply 'vflw-gruppe-winkel-seite-impl (list "vfl-gf-bogen-winkel-header" (list (ssg-text "vfl-winkel-30") (ssg-text "vfl-winkel-60") (ssg-text "vfl-winkel-90")) - "INT"))) + '(30 60 90)))) (princ (ssg-text "vfl-gf-bogen-winkel-header")) (princ (ssg-text "vfl-winkel-30")) (princ (ssg-text "vfl-winkel-60")) (princ (ssg-text "vfl-winkel-90")) - (setq antwort (vfl-menu-int (ssg-text "vfl-prompt-wahl-1-3-def3") + (setq bwinkel (vfl-menu-winkel (ssg-text "vfl-prompt-wahl-winkel-30-60-90") (list (ssg-text "vfl-winkel-30") (ssg-text "vfl-winkel-60") - (ssg-text "vfl-winkel-90")) 3 "vfl-gf-bogen-winkel-header")) - (if (null antwort) (setq antwort 3)) - (setq bwinkel (cond ((= antwort 1) 30) ((= antwort 2) 60) (t 90))) + (ssg-text "vfl-winkel-90")) + '(30 60 90) 90 "vfl-gf-bogen-winkel-header")) (princ (ssg-text "vfl-gf-bogen-seite-header")) (princ (ssg-text "gf-seite-links")) (princ (ssg-text "gf-seite-rechts")) @@ -2283,17 +2563,27 @@ ;; KSYS_EIN/KSYS_AUS fuehren - extract-ks-from-block-raw normalisiert das ;; (siehe ks-normalize-name). ;; Interaktiv (i18n): Vario-Kurve-Winkel + Seite + Variante abfragen, dann Kern. +;; Die Vario-Kurve bekommt - wie das ES-Element (vfl-frage-es-seite) - einen +;; EIGENEN STEP-Marker "Vario-Kurve", obwohl sie INNERHALB der VF-Einheit- +;; Schleife gebaut wird und keinen eigenen Hauptschleifen-Durchlauf hat. Damit +;; wird sie zu einem regulaeren, waehlbaren Glied und ist ueber die bestehende +;; Glied-Maschinerie (vfl-journal-glieder/-slice/-splice) editierbar. Der Marker +;; steht VOR den drei Eingaben (Winkel INT, Seite STR, Variante STR), sodass +;; diese als Glied-Inhalt darauf folgen. Beim Replay ueberspringt vfl-replay-pop +;; den Marker automatisch; die drei Werte poppen danach regulaer. Der Glied-Edit +;; liest allein aus dem Ketten-Journal (Segment-XDATA ist nur additive Deko), +;; daher ist KEIN Umbau der Segment-XDATA-Sicherung noetig. (defun vfl-insert-vario-kurve (frame / kwinkel kseite kvariante antwort) + (vfl-journal-mark "Vario-Kurve") (if (vfl-wizard-aktiv) (vl-catch-all-apply 'vflw-gruppe-variokurve-impl nil)) (princ (ssg-text "vfl-variokurve-winkel-header")) (princ (ssg-text "vfl-opt1-90grad")) (princ (ssg-text "vfl-opt2-60grad")) (princ (ssg-text "vfl-opt3-30grad")) - (setq antwort (vfl-menu-int (ssg-text "vfl-prompt-wahl-1-3-def1") + (setq kwinkel (vfl-menu-winkel (ssg-text "vfl-prompt-wahl-winkel-30-60-90") (list (ssg-text "vfl-opt1-90grad") (ssg-text "vfl-opt2-60grad") - (ssg-text "vfl-opt3-30grad")) 1 "vfl-variokurve-winkel-header")) - (if (null antwort) (setq antwort 1)) - (setq kwinkel (cond ((= antwort 1) 90) ((= antwort 2) 60) (t 30))) + (ssg-text "vfl-opt3-30grad")) + '(90 60 30) 90 "vfl-variokurve-winkel-header")) (princ (ssg-text "vfl-variokurve-seite-header")) (princ (ssg-text "gf-seite-links")) (princ (ssg-text "gf-seite-rechts")) @@ -2369,12 +2659,23 @@ ;; ES-Winkel (30/90) + Seite erst am Kettenende abfragen. Setzt *vfl-es-winkel* ;; und die ES-Masse (ein-dx/dz) fuer die Variante. Rueckgabe: Seite (links/rechts). +;; +;; Das ES-Element bekommt einen EIGENEN STEP-Marker "ES" (vfl-journal-mark), +;; obwohl es nicht in einem eigenen Hauptschleifen-Durchlauf, sondern am Ende +;; des letzten Segments gebaut wird. Damit wird ES zu einem regulaeren "Glied" +;; und ist ueber die bestehende Glied-Maschinerie (vfl-journal-glieder/-slice/ +;; -splice) genauso editierbar wie ein GF-Bogen - ohne fehleranfaelligen +;; Tail-Sonderfall. Der Marker steht VOR den beiden ES-Eingaben (Winkel, Seite), +;; sodass diese als Glied-Inhalt darauf folgen. Beim Replay wird der Marker in +;; vfl-replay-pop automatisch uebersprungen; die beiden STR-Werte poppen danach +;; regulaer. (defun vfl-frage-es-seite ( / antwort es-seite) + (vfl-journal-mark "ES") (if (vfl-wizard-aktiv) (vl-catch-all-apply 'vflw-gruppe-winkel-seite-impl (list "vf-winkel-ein-header" (list (ssg-text "vf-winkel-90") (ssg-text "vf-winkel-30")) - "STR"))) + (list "90" "30")))) (setq *vfl-es-winkel* (vfl-in-value "STR" (function (lambda () (vf-frage-element-winkel "vf-winkel-ein-header"))))) ; 30/90 vor Seite @@ -3318,12 +3619,14 @@ ;; bestehende Replay-Schleife von allein (jedes Glied baut relativ zum Frame ;; des Vorgaengers), ohne Zusatzlogik. ;; -;; Umfang dieser ersten Version: sauber editierbar ist der GF-Bogen (genau eine -;; Winkel- + eine Seiten-Entscheidung, ein eigenstaendiges Top-Level-Glied). -;; Andere Glieder (Linie-GF/-VF, Horizontal-VF, Linie-bis-Ende) enthalten -;; Punktwahl + Hoehen und teils VF-interne Vario-Kurven - fuer die ist der -;; Sektions-Ruecksprung ("rueck") der passende Weg. Sie werden hier mit einem -;; Hinweis abgewiesen (statt eine halbfertige In-Place-Bearbeitung anzubieten). +;; Umfang: sauber einzeln editierbar sind Glieder mit reiner Winkel-/Seiten- +;; Entscheidung ohne Punkt-/Hoehen-Eingabe: GF-Bogen (Winkel 30/60/90 + Seite), +;; AS-Element (Winkel 30/90 + Seite, liegt in der Praeambel) und ES-Element +;; (Winkel 30/90 + Seite, eigenes STEP-Glied seit vfl-frage-es-seite). Andere +;; Glieder (Linie-GF/-VF, Horizontal-VF, Linie-bis-Ende) enthalten Punktwahl + +;; Hoehen und teils VF-interne Vario-Kurven - fuer die ist der Sektions- +;; Ruecksprung ("rueck") der passende Weg. Sie werden hier mit einem Hinweis +;; abgewiesen (statt eine halbfertige In-Place-Bearbeitung anzubieten). ;; Neue Journal-Teilsequenz fuer einen GF-Bogen aus den im Dialog gewaehlten ;; Werten bauen. Aufbau exakt wie vom Live-Bau erzeugt: STEP-Eintrag, dann @@ -3332,40 +3635,37 @@ ;; den STEP-Checkpoint VOR dieser Menuefrage, vfl-journal-steplabel schreibt ;; danach nur das Label um, fuegt aber KEINEN neuen Eintrag ein; die ;; Menueantwort bleibt also als eigener Journal-Eintrag direkt nach dem STEP -;; stehen), dann ("INT" . winkel-idx) + ("STR" . seite-code) aus -;; vfl-insert-gf-bogen (vfl-menu-int Winkel, vfl-menu Seite). winkel-idx -;; 1/2/3 = 30/60/90, seite-code "1"=links, "2"=rechts. Ein GF-Bogen kann nie -;; das erste Kettenglied sein (am Kettenanfang bietet das Menue "GF-Bogen" -;; gar nicht erst an, siehe Hauptschleife) - die Menueantwort ist daher immer -;; aus dem 5er-Menue (frame vorhanden) und immer "1". Rueckgabe: forward-order -;; Journal-Teilsequenz. -(defun vfl-gf-bogen-slice-bauen (winkel-idx seite-code) - (list (cons "STEP" "GF-Bogen") - (cons "STR" "1") - (cons "INT" winkel-idx) - (cons "STR" seite-code))) +;; stehen), dann ("INT" . winkel) + ("STR" . seite-code) aus +;; vfl-insert-gf-bogen (vfl-menu-winkel Winkel, vfl-menu Seite). winkel ist der +;; ECHTE Winkelwert 30/60/90 (kein Index mehr), seite-code "1"=links, +;; "2"=rechts. Ein GF-Bogen kann nie das erste Kettenglied sein (am +;; Kettenanfang bietet das Menue "GF-Bogen" gar nicht erst an, siehe +;; Hauptschleife) - die Menueantwort ist daher immer aus dem 5er-Menue (frame +;; vorhanden) und immer "1". Rueckgabe: forward-order Journal-Teilsequenz. +;; Struktur kommt aus *vfl-glied-schema* ("GF-Bogen": menue winkel seite) - +;; hier nur der Adapter (Aufrufsignatur bleibt winkel/seite-code). +(defun vfl-gf-bogen-slice-bauen (winkel seite-code) + (vfl-schema-slice-bauen "GF-Bogen" (list "1" winkel seite-code))) -;; Aktuelle (Winkel-Index, Seiten-Code) aus einer bestehenden GF-Bogen-Slice -;; lesen (fuer die Dialog-Vorbelegung). Die Slice traegt ZWEI STR-Eintraege -;; (die fuehrende "Naechstes Element waehlen"-Menueantwort, danach - NACH dem -;; INT-Winkel - der Seiten-Code) - nur der STR NACH dem INT ist der Seiten- -;; Code, daher explizit per Positions-Flag statt "letzter gesehener STR -;; gewinnt" (siehe vfl-gf-bogen-slice-bauen). Faellt bei unerwartetem Aufbau -;; auf die Live-Bau-Defaults zurueck (Winkel 90 = idx 3, Seite links = "1"). -(defun vfl-gf-bogen-slice-werte (slice / w s nach-int) - (setq w 3 s "1" nach-int nil) - (foreach e slice - (cond ((= (car e) "INT") (setq w (cdr e) nach-int t)) - ((and (= (car e) "STR") nach-int) (setq s (cdr e) nach-int nil)))) - (list w s)) +;; Aktuellen (Winkel, Seiten-Code) aus einer bestehenden GF-Bogen-Slice lesen +;; (fuer die Dialog-Vorbelegung). Liest ueber das Schema (menue/winkel/seite) +;; und wirft das menue-Feld weg. Der Winkel wird zusaetzlich ueber +;; vfl-winkel-normieren auf 30/60/90 gebracht (faengt auch Alt-Journals mit +;; Index 1/2/3 ab: idx-Werte werden zum Default 90 normiert - solche Altbloecke +;; sind ohnehin selten und der Nutzer sieht/korrigiert den Winkel im +;; vorbelegten Dialog). Defaults: menue "1", Winkel 90, Seite links "1". +(defun vfl-gf-bogen-slice-werte (slice / werte w s) + (setq werte (vfl-schema-slice-werte "GF-Bogen" slice (list "1" 90 "1"))) + (setq w (cadr werte) s (caddr werte)) + (list (vfl-winkel-normieren w '(30 60 90) 90) s)) ;; Pre-fill-faehiger Winkel+Seite-Dialog fuer den GF-Bogen (nutzt das ;; bestehende vflw_winkel_seite-Tile aus dem Wizard-DCL, aber mit Vorbelegung ;; auf die aktuellen Werte - anders als vflw-gruppe-winkel-seite-impl, das ;; immer bei 0 startet und daher nicht wiederverwendet werden kann, ohne den -;; Live-Bau zu beruehren). Rueckgabe: (winkel-idx seite-code) oder nil bei -;; Abbruch. winkel-idx 1/2/3, seite-code "1"/"2". -(defun vfl-glied-gf-bogen-dialog (vor-winkel-idx vor-seite-code / +;; Live-Bau zu beruehren). Rueckgabe: (winkel seite-code) oder nil bei +;; Abbruch. winkel 30/60/90 (echter Wert), seite-code "1"/"2". +(defun vfl-glied-gf-bogen-dialog (vor-winkel vor-seite-code / dat dcl-pfad ergebnis gwinkel gseite) (setq dcl-pfad (strcat (getenv "DXFM_DCL") "/vf_linienzug_wizard.dcl")) (setq dat (load_dialog dcl-pfad)) @@ -3379,7 +3679,9 @@ (add_list (ssg-text "vfl-winkel-60")) (add_list (ssg-text "vfl-winkel-90")) (end_list) - (set_tile "winkel" (itoa (1- vor-winkel-idx))) ; idx 1/2/3 -> 0-basiert 0/1/2 + ;; Winkel 30/60/90 -> Listenposition 0/1/2 + (set_tile "winkel" (itoa (- (length '(30 60 90)) + (length (member vor-winkel '(30 60 90)))))) (start_list "seite") (add_list (ssg-text "gf-seite-links")) (add_list (ssg-text "gf-seite-rechts")) @@ -3391,31 +3693,253 @@ (setq ergebnis (start_dialog)) (unload_dialog dat) (if (= ergebnis 1) - (list (1+ (atoi gwinkel)) ; 0-basiert -> idx 1/2/3 + (list (nth (atoi gwinkel) '(30 60 90)) ; Listenposition 0/1/2 -> 30/60/90 (if (= gseite "1") "2" "1")) ; Liste 0=links/1=rechts -> code "1"/"2" nil)))) -;; Einzelnes Glied bearbeiten. glieder = Glied-Label-Liste (Bau-Reihenfolge). -;; journal = volles Ketten-Journal. Waehlt per Liste ein Glied, laesst dessen -;; Eigenschaften aendern (aktuell nur GF-Bogen), ersetzt die Teilsequenz und -;; baut die Kette komplett neu auf (stummer Voll-Replay des gespliceten -;; Journals). Bricht der Neuaufbau ab, wickelt der bestehende *error*-Handler -;; in vf-linienzug-modus den Zwischenstand - kein Sonderfall noetig. -(defun vfl-edit-glied (ent journal glieder / pos label alt-slice vorwerte - neuwerte neu-slice spliced) - (setq pos (vfl-dlg-position glieder)) +;; Pre-fill-faehiger Winkel+Seite+Variante-Dialog fuer die Vario-Kurve (nutzt +;; das bestehende vflw_variokurve-Tile mit Vorbelegung). Wizard-Optionsreihen- +;; folge: Winkel (90 60 30), Seite (links rechts), Variante (aussen innen) - +;; genau wie vflw-gruppe-variokurve-impl. vor-* sind die aktuellen Werte: +;; vor-winkel echter Wert 90/60/30, vor-seite-code/vor-variante-code je "1"/"2". +;; Rueckgabe: (winkel seite-code variante-code) oder nil bei Abbruch. +(defun vfl-glied-vario-dialog (vor-winkel vor-seite-code vor-variante-code / + dat dcl-pfad ergebnis gwinkel gseite gvariante) + (setq dcl-pfad (strcat (getenv "DXFM_DCL") "/vf_linienzug_wizard.dcl")) + (setq dat (load_dialog dcl-pfad)) + (if (not (new_dialog "vflw_variokurve" dat)) + (progn (if (and dat (>= dat 0)) (unload_dialog dat)) + (alert (ssg-textf "vfl-edit-dialog-fehlt" (list dcl-pfad))) nil) + (progn + (set_tile "kopf" (ssg-text "vfl-variokurve-winkel-header")) + (start_list "winkel") + (add_list (ssg-text "vfl-opt1-90grad")) + (add_list (ssg-text "vfl-opt2-60grad")) + (add_list (ssg-text "vfl-opt3-30grad")) + (end_list) + ;; Winkel 90/60/30 -> Listenposition 0/1/2 + (set_tile "winkel" (itoa (- (length '(90 60 30)) + (length (member vor-winkel '(90 60 30)))))) + (start_list "seite") + (add_list (ssg-text "gf-seite-links")) + (add_list (ssg-text "gf-seite-rechts")) + (end_list) + (set_tile "seite" (if (= vor-seite-code "2") "1" "0")) + (start_list "variante") + (add_list (ssg-text "vfl-variante-aussen")) + (add_list (ssg-text "vfl-variante-innen")) + (end_list) + (set_tile "variante" (if (= vor-variante-code "2") "1" "0")) + (action_tile "accept" + "(setq gwinkel (get_tile \"winkel\")) (setq gseite (get_tile \"seite\")) (setq gvariante (get_tile \"variante\")) (done_dialog 1)") + (action_tile "cancel" "(done_dialog 0)") + (setq ergebnis (start_dialog)) + (unload_dialog dat) + (if (= ergebnis 1) + (list (nth (atoi gwinkel) '(90 60 30)) ; Listenposition 0/1/2 -> 90/60/30 + (if (= gseite "1") "2" "1") ; Liste 0=links/1=rechts -> "1"/"2" + (if (= gvariante "1") "2" "1")) ; Liste 0=aussen/1=innen -> "1"/"2" + nil)))) + +;; Pre-fill-faehiger Winkel+Seite-Dialog fuer AS/ES-Element: nur ZWEI +;; Winkel-Optionen (90/30, in genau dieser Listen-Reihenfolge - wie +;; vf-frage-element-winkel/vflw-gruppe-winkel-seite-impl), Winkel als STRING +;; "90"/"30" (nicht Index). kopf-key waehlt die Kopfzeile (AS: vf-winkel-aus- +;; header, ES: vf-winkel-ein-header). Rueckgabe: (winkel-str seite-code) oder +;; nil bei Abbruch. seite-code "1"=links / "2"=rechts. +(defun vfl-glied-as-es-dialog (kopf-key vor-winkel-str vor-seite-code / + dat dcl-pfad ergebnis gwinkel gseite) + (setq dcl-pfad (strcat (getenv "DXFM_DCL") "/vf_linienzug_wizard.dcl")) + (setq dat (load_dialog dcl-pfad)) + (if (not (new_dialog "vflw_winkel_seite" dat)) + (progn (if (and dat (>= dat 0)) (unload_dialog dat)) + (alert (ssg-textf "vfl-edit-dialog-fehlt" (list dcl-pfad))) nil) + (progn + (set_tile "kopf" (ssg-text kopf-key)) + (start_list "winkel") + (add_list (ssg-text "vf-winkel-90")) + (add_list (ssg-text "vf-winkel-30")) + (end_list) + (set_tile "winkel" (if (= vor-winkel-str "30") "1" "0")) ; Liste 0=90/1=30 + (start_list "seite") + (add_list (ssg-text "gf-seite-links")) + (add_list (ssg-text "gf-seite-rechts")) + (end_list) + (set_tile "seite" (if (= vor-seite-code "2") "1" "0")) + (action_tile "accept" + "(setq gwinkel (get_tile \"winkel\")) (setq gseite (get_tile \"seite\")) (done_dialog 1)") + (action_tile "cancel" "(done_dialog 0)") + (setq ergebnis (start_dialog)) + (unload_dialog dat) + (if (= ergebnis 1) + (list (if (= gwinkel "1") "30" "90") ; Liste 0=90/1=30 -> "90"/"30" + (if (= gseite "1") "2" "1")) ; Liste 0=links/1=rechts -> "1"/"2" + nil)))) + +;; --- AS-/ES-Element (Winkel 30/90 + Seite links/rechts) --- +;; AS und ES sind KEINE ueber das Hauptmenue gewaehlten Glieder, ihre Slice +;; traegt daher KEINE fuehrende Menue-Antwort (anders als der GF-Bogen). Aufbau: +;; AS: gehoert in die PRAEAMBEL (vor dem 1. STEP), Eintraege: STR(winkel) +;; STR(seite) - siehe vf-linienzug-modus Kettenanfang. AS ist damit KEIN +;; STEP-Glied und wird ueber die Praeambel-Splice (vfl-journal-splice-as) +;; bearbeitet. +;; ES: hat seit vfl-frage-es-seite einen EIGENEN STEP-Marker "ES" und ist +;; damit ein regulaeres Glied. Slice: STEP("ES") STR(winkel) STR(seite). +;; winkel-str ist bei beiden "30" oder "90" (Rueckgabe vf-frage-element-winkel, +;; via vfl-in-value "STR" journalisiert), seite-code "1"=links / "2"=rechts. + +;; Aktuelle (winkel-str seite-code) aus einer ES-Glied-Slice lesen (2 STR nach +;; dem STEP: erst Winkel, dann Seite). Struktur/Defaults aus *vfl-glied-schema* +;; ("ES": winkel seite). Defaults 90/links. +(defun vfl-es-slice-werte (slice) + (vfl-schema-slice-werte "ES" slice (list "90" "1"))) + +;; Neue ES-Glied-Slice bauen: STEP("ES") + STR(winkel) + STR(seite), Struktur +;; aus dem Schema. +(defun vfl-es-slice-bauen (winkel-str seite-code) + (vfl-schema-slice-bauen "ES" (list winkel-str seite-code))) + +;; --- Vario-Kurve (Winkel 30/60/90 + Seite + Variante) -------------------- +;; Slice: STEP("Vario-Kurve") INT(winkel) STR(seite) STR(variante), Struktur aus +;; *vfl-glied-schema* ("Vario-Kurve"). winkel echter Wert 90/60/30 (kein Index), +;; seite/variante als Menue-Code "1"/"2" (seite: 1=links/2=rechts, variante: +;; 1=aussen/2=innen - genau wie im Live-Bau vfl-insert-vario-kurve +;; journalisiert). Aktuelle Werte aus einer bestehenden Slice lesen; Defaults +;; 90/links/innen (innen = Live-Bau-Default, vfl-menu-Vorgabe 2). +(defun vfl-vario-slice-werte (slice / werte) + (setq werte (vfl-schema-slice-werte "Vario-Kurve" slice (list 90 "1" "2"))) + ;; Winkel gegen Fremdwerte absichern (analog GF-Bogen). + (list (vfl-winkel-normieren (car werte) '(90 60 30) 90) + (cadr werte) (caddr werte))) + +;; Neue Vario-Kurve-Slice bauen. +(defun vfl-vario-slice-bauen (winkel seite-code variante-code) + (vfl-schema-slice-bauen "Vario-Kurve" (list winkel seite-code variante-code))) + +;; Neue Vario-Kurve-Slice bauen, die den SCHWANZ der alten Slice erhaelt. +;; WICHTIG - Sonderfall gegenueber GF-Bogen/ES: Die Vario-Kurve ist das EINZIGE +;; STEP-Glied, das MITTEN in einem umschliessenden Konstrukt (der VF-Einheit) +;; steht. Nach ihren drei eigenen Werten (INT winkel, STR seite, STR variante) +;; folgen im Journal - noch VOR dem naechsten STEP - weitere Antworten der +;; VF-Einheit-Schleife ("Ist Endpunkt?", "Naechstes VF-Element?" usw.). Ein +;; naiver vfl-journal-splice (der die GANZE Slice bis zum naechsten STEP +;; ersetzt) wuerde diese nachfolgenden VF-Antworten VERWERFEN und beim Replay +;; die restliche Kette verschieben (Bug, adversarial verifiziert). Daher hier: +;; nur die drei Vario-Felder direkt nach dem STEP ersetzen, ALLE weiteren +;; Eintraege der alten Slice unveraendert anhaengen. alt-slice = die vom +;; vfl-journal-slice gelieferte Original-Slice dieses Vario-Glieds. +;; Rueckgabe: neue Slice (STEP INT STR STR + originaler Schwanz). +(defun vfl-vario-slice-splicen (alt-slice winkel seite-code variante-code / + schwanz rest n) + ;; Schwanz = alt-slice OHNE das fuehrende STEP und die naechsten drei + ;; Eintraege (winkel/seite/variante). Robust ueber die feste Feldzahl (3) + ;; des Vario-Schemas statt ueber kind-Rateraten. + (setq rest (cdr alt-slice)) ; STEP weg + (setq n 0) + (while (and rest (< n 3)) ; die drei Vario-Felder weg + (setq rest (cdr rest) n (1+ n))) + (setq schwanz rest) ; alles Weitere (VF-Antworten) bleibt + (append (vfl-vario-slice-bauen winkel seite-code variante-code) schwanz)) + +;; AS-Werte aus der PRAEAMBEL lesen: die beiden STR direkt VOR dem 1. STEP, die +;; auf den AS-ja/nein-STR folgen (Reihenfolge im Journal: PT REAL STR(ja/nein) +;; [STR(winkel) STR(seite)]). Nur gueltig, wenn AS vorhanden (as-ja /= "2"). +;; Rueckgabe: (winkel-str seite-code) oder nil, wenn kein AS. +(defun vfl-as-werte-lesen (journal / praeambel str-liste) + (setq praeambel (vfl-journal-praeambel journal)) + (setq str-liste '()) + (foreach e praeambel + (if (= (car e) "STR") (setq str-liste (cons (cdr e) str-liste)))) + (setq str-liste (reverse str-liste)) ; [ja/nein, winkel, seite] in Reihenfolge + (if (and (>= (length str-liste) 3) (/= (car str-liste) "2")) + (list (cadr str-liste) (caddr str-liste)) + nil)) + +;; Praeambel (alle Eintraege vor dem 1. STEP) als Liste in Reihenfolge. +(defun vfl-journal-praeambel (journal / out done) + (setq out '() done nil) + (foreach e journal + (if (and (not done) (= (car e) "STEP")) (setq done t)) + (if (not done) (setq out (cons e out)))) + (reverse out)) + +;; AS-Winkel/Seite in der Praeambel ersetzen (die beiden STR nach dem +;; ja/nein-STR), Rest des Journals unveraendert. Erwartet, dass AS vorhanden +;; ist (3 STR in der Praeambel). Gibt das komplette neue Journal zurueck. +(defun vfl-journal-splice-as (journal winkel-str seite-code / out str-idx done) + (setq out '() str-idx 0 done nil) + (foreach e journal + (cond + (done (setq out (cons e out))) ; ab 1. STEP: unveraendert + ((= (car e) "STEP") (setq done t) (setq out (cons e out))) + ((= (car e) "STR") + (setq str-idx (1+ str-idx)) + (cond + ((= str-idx 2) (setq out (cons (cons "STR" winkel-str) out))) ; AS-Winkel + ((= str-idx 3) (setq out (cons (cons "STR" seite-code) out))) ; AS-Seite + (t (setq out (cons e out))))) ; ja/nein bleibt + (t (setq out (cons e out))))) ; PT/REAL bleiben + (reverse out)) + +;; Einzelnes Glied bearbeiten. glieder = Glied-Label-Liste (Bau-Reihenfolge, +;; enthaelt jetzt ggf. das synthetische "AS" ganz vorne und "ES" als regulaeres +;; Glied). journal = volles Ketten-Journal. Waehlt per Liste ein Glied, laesst +;; dessen Eigenschaften aendern (GF-Bogen, AS, ES), ersetzt die Teilsequenz und +;; baut die Kette komplett neu auf (stummer Voll-Replay). Bricht der Neuaufbau +;; ab, wickelt der bestehende *error*-Handler in vf-linienzug-modus den +;; Zwischenstand - kein Sonderfall noetig. +;; +;; glieder-alle = die dem Picker angezeigte Liste (inkl. evtl. "AS" an Position +;; 1); pos ist 1-basiert darauf. STEP-Glieder (GF-Bogen, ES, ...) werden ueber +;; ihren STEP-Index angesprochen - dieser ist pos MINUS 1, falls "AS" als +;; synthetischer erster Listeneintrag vorhanden ist (AS ist kein STEP). +(defun vfl-edit-glied (ent journal glieder-alle has-as / pos label + step-idx alt-slice vorwerte neuwerte neu-slice spliced) + (setq pos (vfl-dlg-position glieder-alle)) (if (null pos) (progn (princ (ssg-text "vfl-edit-abgebrochen")) (exit))) - (setq label (nth (1- pos) glieder)) + (setq label (nth (1- pos) glieder-alle)) + ;; STEP-Index eines gewaehlten STEP-Glieds: bei vorhandenem "AS"-Listenkopf + ;; um 1 nach vorne verschoben (AS belegt Listenposition 1, ist aber kein STEP). + (setq step-idx (if has-as (1- pos) pos)) (cond + ;; --- AS-Element (Praeambel, kein STEP) --- + ((= label "AS") + (setq vorwerte (vfl-as-werte-lesen journal)) + (if (null vorwerte) + (progn (princ (ssg-text "vfl-edit-abgebrochen")) (exit))) + (setq neuwerte (vfl-glied-as-es-dialog "vf-winkel-aus-header" + (car vorwerte) (cadr vorwerte))) + (if (null neuwerte) + (progn (princ (ssg-text "vfl-edit-abgebrochen")) (exit))) + (setq spliced (vfl-journal-splice-as journal (car neuwerte) (cadr neuwerte))) + (entdel ent) + (princ (ssg-textf "vfl-glied-geaendert" (list (itoa pos)))) + (vfl-journal-replay-start spliced) + (vf-linienzug-modus)) + ;; --- ES-Element (eigenes STEP-Glied) --- + ((= label "ES") + (setq alt-slice (vfl-journal-slice journal step-idx)) + (setq vorwerte (vfl-es-slice-werte alt-slice)) + (setq neuwerte (vfl-glied-as-es-dialog "vf-winkel-ein-header" + (car vorwerte) (cadr vorwerte))) + (if (null neuwerte) + (progn (princ (ssg-text "vfl-edit-abgebrochen")) (exit))) + (setq neu-slice (vfl-es-slice-bauen (car neuwerte) (cadr neuwerte))) + (setq spliced (vfl-journal-splice journal step-idx neu-slice)) + (entdel ent) + (princ (ssg-textf "vfl-glied-geaendert" (list (itoa pos)))) + (vfl-journal-replay-start spliced) + (vf-linienzug-modus)) + ;; --- GF-Bogen (STEP-Glied mit fuehrender Menue-Antwort) --- ((= label "GF-Bogen") - (setq alt-slice (vfl-journal-slice journal pos)) + (setq alt-slice (vfl-journal-slice journal step-idx)) (setq vorwerte (vfl-gf-bogen-slice-werte alt-slice)) (setq neuwerte (vfl-glied-gf-bogen-dialog (car vorwerte) (cadr vorwerte))) (if (null neuwerte) (progn (princ (ssg-text "vfl-edit-abgebrochen")) (exit))) (setq neu-slice (vfl-gf-bogen-slice-bauen (car neuwerte) (cadr neuwerte))) - (setq spliced (vfl-journal-splice journal pos neu-slice)) + (setq spliced (vfl-journal-splice journal step-idx neu-slice)) (entdel ent) (princ (ssg-textf "vfl-glied-geaendert" (list (itoa pos)))) ;; Voller stummer Replay des gespliceten Journals: die Queue beschreibt @@ -3423,13 +3947,34 @@ ;; Live-Modus, sondern baut 1:1 mit dem geaenderten Glied fertig. (vfl-journal-replay-start spliced) (vf-linienzug-modus)) + ;; --- Vario-Kurve (STEP-Glied INNERHALB einer VF-Einheit) --- + ;; Gleiche Slice/Splice/Replay-Maschinerie wie GF-Bogen; die Vario-Kurve hat + ;; seit vfl-insert-vario-kurve einen eigenen STEP-Marker und ist damit ein + ;; regulaeres Glied. Aendert Winkel/Seite/Variante, Rest der Kette bleibt. + ((= label "Vario-Kurve") + (setq alt-slice (vfl-journal-slice journal step-idx)) + (setq vorwerte (vfl-vario-slice-werte alt-slice)) + (setq neuwerte (vfl-glied-vario-dialog (car vorwerte) (cadr vorwerte) (caddr vorwerte))) + (if (null neuwerte) + (progn (princ (ssg-text "vfl-edit-abgebrochen")) (exit))) + ;; Schwanz-erhaltende Slice: die Vario-Kurve steht MITTEN in der VF-Einheit, + ;; nach ihren 3 Werten folgen weitere VF-Antworten in derselben Slice. Nur + ;; die 3 Vario-Felder ersetzen, den Rest behalten (siehe + ;; vfl-vario-slice-splicen) - sonst verschiebt sich beim Replay die Kette. + (setq neu-slice (vfl-vario-slice-splicen alt-slice + (car neuwerte) (cadr neuwerte) (caddr neuwerte))) + (setq spliced (vfl-journal-splice journal step-idx neu-slice)) + (entdel ent) + (princ (ssg-textf "vfl-glied-geaendert" (list (itoa pos)))) + (vfl-journal-replay-start spliced) + (vf-linienzug-modus)) (t (alert (ssg-textf "vfl-glied-nicht-editierbar" (list (vfl-glied-label-text label)))) (princ (ssg-text "vfl-edit-abgebrochen")))) (princ)) -(defun vfl-edit-ent (ent / xd marker journal glieder n i pos trunc edit-modus) +(defun vfl-edit-ent (ent / xd marker journal glieder glieder-alle has-as n i pos trunc edit-modus) (setq xd (vfl-journal-xdata-lesen ent)) (if (null xd) (progn (alert (ssg-text "vfl-edit-kein-journal")) (exit))) @@ -3439,6 +3984,15 @@ (progn (setq glieder (vfl-journal-glieder journal)) (setq n (length glieder)) + ;; AS ist kein STEP-Glied (steht in der Praeambel). Fuer den Glied-Edit + ;; wird es als synthetischer erster Listeneintrag "AS" vorangestellt, + ;; falls in der Kette ein AS vorhanden ist (vfl-as-werte-lesen liefert + ;; dann nicht nil). has-as steuert in vfl-edit-glied den STEP-Index- + ;; Versatz (AS belegt Listenposition 1, ist aber kein STEP). Der Sektions- + ;; Ruecksetz-Modus nutzt weiterhin die reine STEP-Liste (glieder), ES ist + ;; darin bereits als eigenes Glied enthalten. + (setq has-as (and (vfl-as-werte-lesen journal) t)) + (setq glieder-alle (if has-as (cons "AS" glieder) glieder)) ;; NEU: zuerst Modus waehlen (Sektion zuruecksetzen ODER ein einzelnes ;; Glied bearbeiten). Alte Bloecke ohne Segment-XDATA funktionieren in ;; beiden Zweigen, da beide allein aus dem Ketten-Journal arbeiten. @@ -3447,7 +4001,7 @@ ((null edit-modus) (princ (ssg-text "vfl-edit-abgebrochen"))) ((= edit-modus "glied") - (vfl-edit-glied ent journal glieder)) + (vfl-edit-glied ent journal glieder-alle has-as)) (t ;; --- bestehender Ablauf: Sektion zuruecksetzen (unveraendert) --- (princ (ssg-textf "vfl-edit-kette-header" (list (itoa n)))) diff --git a/lang/de_DE.json b/lang/de_DE.json index 4a84beb..6c2392e 100644 --- a/lang/de_DE.json +++ b/lang/de_DE.json @@ -53,6 +53,9 @@ "vfl-glied-vf": "VarioFoerderer-Einheit (Ab/Auf)", "vfl-glied-horizontal-vf": "VF mit horizontalem Anfang", "vfl-glied-linie": "Linie bis Kettenende", + "vfl-glied-as": "AS-Element (Kettenanfang)", + "vfl-glied-es": "ES-Element (Kettenende)", + "vfl-glied-vario": "Vario-Kurve (horizontale Kurve im VF)", "vfl-glied-offen": "(offen)", "vfl-edit-kein-journal": "Dieser Linienzug-Block traegt kein Eingabe-Journal (Altbestand) und kann nicht schrittweise editiert werden.", "vfl-edit-kette-header": "\n\nLinienzug mit %1 Glied(ern):", @@ -75,13 +78,14 @@ "vf-winkel-30": "\n 2 - 30 Grad", "vfl-gf-bogen-winkel-header": "\n\nGF-Bogen - Winkel waehlen:", "vfl-gf-bogen-seite-header": "\nGF-Bogen - Seite waehlen:", - "vfl-winkel-30": "\n 1 - 30 Grad", - "vfl-winkel-60": "\n 2 - 60 Grad", - "vfl-winkel-90": "\n 3 - 90 Grad", + "vfl-winkel-30": "\n 30 Grad", + "vfl-winkel-60": "\n 60 Grad", + "vfl-winkel-90": "\n 90 Grad", + "vfl-prompt-wahl-winkel-30-60-90": "\nWinkel eingeben (30/60/90) [90]: ", "vfl-variokurve-winkel-header": "\n\nVario-Kurve - Winkel waehlen:", - "vfl-opt1-90grad": "\n 1 - 90 Grad", - "vfl-opt2-60grad": "\n 2 - 60 Grad", - "vfl-opt3-30grad": "\n 3 - 30 Grad", + "vfl-opt1-90grad": "\n 90 Grad", + "vfl-opt2-60grad": "\n 60 Grad", + "vfl-opt3-30grad": "\n 30 Grad", "vfl-prompt-wahl-1-3-def1": "\nIhre Wahl (1/2/3) [1]: ", "vfl-variokurve-seite-header": "\nVario-Kurve - Seite waehlen:", "vfl-variokurve-variante-header": "\nVario-Kurve - Variante waehlen:", diff --git a/lang/en_GB.json b/lang/en_GB.json index de5316e..82d7fc0 100644 --- a/lang/en_GB.json +++ b/lang/en_GB.json @@ -53,6 +53,9 @@ "vfl-glied-vf": "VarioFoerderer unit (down/up)", "vfl-glied-horizontal-vf": "VF with horizontal start", "vfl-glied-linie": "Line to chain end", + "vfl-glied-as": "AS element (chain start)", + "vfl-glied-es": "ES element (chain end)", + "vfl-glied-vario": "Vario curve (horizontal curve in VF)", "vfl-glied-offen": "(open)", "vfl-edit-kein-journal": "This chain block carries no input journal (legacy) and cannot be edited step by step.", "vfl-edit-kette-header": "\n\nChain with %1 segment(s):", @@ -75,13 +78,14 @@ "vf-winkel-30": "\n 2 - 30 deg", "vfl-gf-bogen-winkel-header": "\n\nGF arc - choose angle:", "vfl-gf-bogen-seite-header": "\nGF arc - choose side:", - "vfl-winkel-30": "\n 1 - 30 deg", - "vfl-winkel-60": "\n 2 - 60 deg", - "vfl-winkel-90": "\n 3 - 90 deg", + "vfl-winkel-30": "\n 30 deg", + "vfl-winkel-60": "\n 60 deg", + "vfl-winkel-90": "\n 90 deg", + "vfl-prompt-wahl-winkel-30-60-90": "\nEnter angle (30/60/90) [90]: ", "vfl-variokurve-winkel-header": "\n\nVario curve - choose angle:", - "vfl-opt1-90grad": "\n 1 - 90 deg", - "vfl-opt2-60grad": "\n 2 - 60 deg", - "vfl-opt3-30grad": "\n 3 - 30 deg", + "vfl-opt1-90grad": "\n 90 deg", + "vfl-opt2-60grad": "\n 60 deg", + "vfl-opt3-30grad": "\n 30 deg", "vfl-prompt-wahl-1-3-def1": "\nYour choice (1/2/3) [1]: ", "vfl-variokurve-seite-header": "\nVario curve - choose side:", "vfl-variokurve-variante-header": "\nVario curve - choose variant:", diff --git a/tests/alltests.json b/tests/alltests.json index 10b83ef..6d54e87 100644 --- a/tests/alltests.json +++ b/tests/alltests.json @@ -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" }, diff --git a/tests/test_vfl_schema.lsp b/tests/test_vfl_schema.lsp new file mode 100644 index 0000000..70d2939 --- /dev/null +++ b/tests/test_vfl_schema.lsp @@ -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 :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)