[REFACTOR] Stufe 2, Schritte 0-4: Frage-Schale und reinen Bauteil trennen
Alle Aenderungen sind verhaltensgleich: die Frage-Schalen behalten ihre Namen, keine Aufrufstelle in Modus 1/2/3 wurde angefasst. Schritt 0 - vfl-ziel-report: der Soll-Ist-Vergleich am Kettenende stand zweimal WORTGLEICH in vfl-vf-einheit-abschluss (einmal fuer "Kettenende ohne ES", einmal "mit ES"); die Faelle unterscheiden sich nur darin, wann sie melden, nicht was. Schritt 1 - Winkelwahl aufgeteilt: vfl-winkel-gueltige (Kandidaten filtern) und vfl-winkel-nach-index (Kandidat waehlen) sind rein; die Frage-Schale vfl-waehle-winkel setzt sie zusammen. Neuer Vorgabe-Kanal *vfl-winkel-idx-vorgabe*: ist er gesetzt, wird nicht gefragt. Kanal statt Parameter, weil vfl-waehle-winkel aus vier Solvern gerufen wird (vfl-vf-winkel, vfl-vf-entscheidung, vfl-body-zerlegung, vfl-segment-entscheidung) - ein Parameter wuerde vier Signaturen und alle deren Aufrufer aendern. Default nil, also heute ohne Wirkung. Schritt 2 - vfl-es-masse-setzen: reines Globals-Setzen (ES-Winkel merken + vf-set-es-masse) aus vfl-frage-es-seite gezogen. *vfl-es-winkel* wird LAZY von den Blocknamen-Bauern gelesen und muss vor dem ersten ES-Insert stehen. Schritt 3 - vfl-hor-koerper-bauen: die drei Fragen (Separator vor/nach, Endpunkt) haengen an keinem berechneten Wert und stehen jetzt alle vor dem Bau. Journal-Reihenfolge UND Entity-Reihenfolge bleiben identisch; nur interaktiv erscheint der auf_3-Uebergangsblock erst nach den drei Fragen statt zwischen Frage 1 und 2. Die dL-Subtraktionskette wurde Zeile fuer Zeile uebernommen - jeder Schritt ist mit (max *vfk-restlaenge-min-clamp* ...) geklammert, also nicht kommutativ, und der auf_3-Insert sitzt bewusst zwischen Subtraktion 1 und 2 (sein Fussabdruck wird GEMESSEN). Nachweis: Zeilenvergleich alt/neu, dieselben 15 geometrie-relevanten Zeilen in derselben Reihenfolge. Schritt 4 - vfl-body-abschluss-bauen: hier laesst sich NICHT alles vorab fragen (vfl-neue-linie-messen braucht den Frame nach vfl-nach-3grad, die Hoehenvorschlaege brauchen dL und die Kettenhoehe). Die Schale macht darum 3-Grad-Abschluss -> messen -> Vorschlaege -> Zielhoehe fragen, der reine Teil beginnt bei der Zerlegung. Nachweis: Multiset-Vergleich der Zeilen, ausser Kopf, Klammer und Aufruf ist keine Zeile veraendert. Schritt 7 (Assertion-Teil) - vsp-journal-diff in Lisp/vf_spec.lsp: stellt das aus der Spec erzeugte Journal Token fuer Token gegen das beim Bau neu aufgezeichnete. Schaerfster Desync-Detektor - offene Queue-Werte und die Glied-Folge zeigen nur grobe Abweichungen, ein einzelner Wert, der an der falschen Stelle verbraucht wird, kann beide passieren lassen und trotzdem andere Geometrie erzeugen. Neues Record-Feld journal_diff, neuer Test test_journal_token_gleich. Zwei bewusste Abweichungen von der Planung (begruendet in doc/TODO-plan-vf-interactive.md, Abschnitt "Umsetzungsstand Stufe 2"): vfl-hor-koerper-bauen behaelt ziel-modus als eigenen Parameter neben ende-code (ende-code darf nil sein, die Separator-Subtraktionen haengen aber allein am Ziel-Modus); vfl-body-abschluss-bauen braucht letzt-hz und es-gewuenscht nicht. Offen: Schritte 5, 6, 8, 9, 10. Das sind die, bei denen die Reihenfolge selbst Geometrie ist - der Fahrplan verlangt nach jedem einen CAD-Lauf (TEST_HUNDM05 + TEST_VF_SPEC + TEST_LINIENZUG), nach den Geometrie-Schritten zusaetzlich DXF-Diff und Sivas-Export-Vergleich. Ohne Lauf zwischen den Schritten waere ein Fehler nicht mehr einem Schritt zuzuordnen. Sie bringen keine neue Faehigkeit: der Bau aus Daten laeuft seit Stufe 1 und ist mit VF_SPEC_BAU belegt (5 OK, 0 Fehler). Verifiziert: 109 pytest-Tests gruen, beide .lsp lint-sauber, kein neuer alert/get*/ssget im Bau-Pfad (statischer Waechter). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+195
-111
@@ -1772,16 +1772,48 @@
|
||||
;; Aus der Ergebnisliste von berechne-alle-winkel die GUELTIGEN Winkel filtern
|
||||
;; und - bei mehreren - den Nutzer waehlen lassen. Rueckgabe: (winkel L_GF L_VF)
|
||||
;; oder nil, wenn kein gueltiger Winkel existiert.
|
||||
(defun vfl-waehle-winkel (ergebnis-liste / gueltige idx antwort e labels)
|
||||
;; --- Winkelwahl: reine Teile + Frage-Schale ---
|
||||
;; Die gueltigen Kandidaten aus einer berechne-alle-winkel-Ergebnisliste
|
||||
;; filtern. Gueltig heisst: baubar (cadddr) UND beide Laengen positiv.
|
||||
;; Reine Funktion, keine Frage, keine Globals.
|
||||
(defun vfl-winkel-gueltige (ergebnis-liste / gueltige)
|
||||
(setq gueltige '())
|
||||
(foreach e ergebnis-liste
|
||||
(if (and (cadddr e) (numberp (cadr e)) (numberp (caddr e))
|
||||
(> (cadr e) 0) (> (caddr e) 0))
|
||||
(setq gueltige (append gueltige (list e)))))
|
||||
gueltige)
|
||||
|
||||
;; Kandidat Nummer idx (1-basiert) als (winkel L_GF L_VF). Ein Index
|
||||
;; ausserhalb der Liste faellt auf den ersten Kandidaten zurueck - so
|
||||
;; verhielt sich die Frage-Schale schon immer bei ungueltiger Eingabe.
|
||||
(defun vfl-winkel-nach-index (gueltige idx / e)
|
||||
(if (or (null idx) (< idx 1) (> idx (length gueltige))) (setq idx 1))
|
||||
(setq e (nth (1- idx) gueltige))
|
||||
(if e (list (nth 0 e) (nth 1 e) (nth 2 e)) nil))
|
||||
|
||||
;; Vorgabe-Kanal: ist *vfl-winkel-idx-vorgabe* gesetzt, wird NICHT gefragt,
|
||||
;; sondern dieser Kandidat genommen. Derselbe Mechanismus, den die Datei
|
||||
;; schon fuer die Wizard-Menues nutzt (dynamisch gebundene *vflw-menu-*).
|
||||
;; Warum ein Kanal und kein Parameter: vfl-waehle-winkel wird aus vier
|
||||
;; Solvern gerufen (vfl-vf-winkel, vfl-vf-entscheidung, vfl-body-zerlegung,
|
||||
;; vfl-segment-entscheidung) - ein zusaetzlicher Parameter wuerde vier
|
||||
;; Signaturen und alle deren Aufrufer aendern.
|
||||
;; ACHTUNG: die Vorgabe gilt fuer JEDE folgende Winkelfrage, bis sie
|
||||
;; zurueckgesetzt wird. Setzer muessen sie darum wieder auf nil stellen.
|
||||
(if (not (boundp '*vfl-winkel-idx-vorgabe*))
|
||||
(setq *vfl-winkel-idx-vorgabe* nil))
|
||||
|
||||
;; Frage-Schale (Name unveraendert, alle Aufrufstellen bleiben): gueltige
|
||||
;; Kandidaten bestimmen, bei genau einem ohne Frage nehmen, sonst fragen -
|
||||
;; bzw. die Vorgabe verwenden.
|
||||
(defun vfl-waehle-winkel (ergebnis-liste / gueltige idx antwort e labels)
|
||||
(setq gueltige (vfl-winkel-gueltige ergebnis-liste))
|
||||
(cond
|
||||
((null gueltige) nil)
|
||||
((= (length gueltige) 1)
|
||||
(setq e (car gueltige)) (list (nth 0 e) (nth 1 e) (nth 2 e)))
|
||||
((= (length gueltige) 1) (vfl-winkel-nach-index gueltige 1))
|
||||
(*vfl-winkel-idx-vorgabe*
|
||||
(vfl-winkel-nach-index gueltige *vfl-winkel-idx-vorgabe*))
|
||||
(t
|
||||
(princ (ssg-text "vfl-mehrere-winkel-header"))
|
||||
(setq idx 1 labels '())
|
||||
@@ -1793,9 +1825,7 @@
|
||||
(setq idx (1+ idx)))
|
||||
(setq antwort (vfl-menu-int (ssg-textf "vfl-prompt-wahl-bis-n" (list (length gueltige)))
|
||||
labels 1 "vfl-mehrere-winkel-header"))
|
||||
(if (or (null antwort) (< antwort 1) (> antwort (length gueltige))) (setq antwort 1))
|
||||
(setq e (nth (1- antwort) gueltige))
|
||||
(list (nth 0 e) (nth 1 e) (nth 2 e)))))
|
||||
(vfl-winkel-nach-index gueltige antwort))))
|
||||
|
||||
;; berechne-alle-winkel ausfuehren (mit Linienzug-FESTE_HORIZONTAL = 1300) und
|
||||
;; den Winkel waehlen lassen. Rueckgabe: (winkel L_GF L_VF) oder nil.
|
||||
@@ -2397,16 +2427,24 @@
|
||||
;; (vfl-vf-einheit) muss die Frage dann NICHT erneut stellen. Bei ziel-modus=
|
||||
;; nil (Default/mid-chain-Fortsetzung): unveraendertes Verhalten, Rueckgabe
|
||||
;; nur frame.
|
||||
(defun vfl-baue-horizontal-koerper (frame hz dL ziel-modus gf2-laenge /
|
||||
pt m1 sep-vor pt-vor-bogen sep-nach ist-ende-antwort)
|
||||
;; --- Horizontaler Koerper: reiner Bauteil ---
|
||||
;; Baut das horizontale Stueck aus FERTIGEN Antworten - keine Frage, kein
|
||||
;; Dialog. Rueckgabe: neuer Frame (das Stueck endet flach, 0 Grad).
|
||||
;;
|
||||
;; ziel-modus bleibt ein EIGENER Parameter neben ende-code (der Fahrplan
|
||||
;; wollte ihn ersetzen): die Separator-Subtraktionen haengen allein am
|
||||
;; Ziel-Modus, nicht an der Endpunkt-Antwort - und ende-code darf nil sein
|
||||
;; (abgebrochene Menuefrage). Mit nur einem Parameter waere genau dieser Fall
|
||||
;; eine stille Verhaltensaenderung.
|
||||
;;
|
||||
;; REIHENFOLGE IST GEOMETRIE: jede dL-Subtraktion ist mit
|
||||
;; (max *vfk-restlaenge-min-clamp* ...) geklammert, also nicht kommutativ,
|
||||
;; und der auf_3-Insert sitzt BEWUSST zwischen Subtraktion 1 und 2 - sein
|
||||
;; Fussabdruck wird GEMESSEN (vfl-projiziere-distanz), nicht geschaetzt.
|
||||
;; Hier darf nichts umsortiert werden.
|
||||
(defun vfl-hor-koerper-bauen (frame hz dL ziel-modus ende-code sep-vor sep-nach
|
||||
gf2-laenge / pt m1 pt-vor-bogen)
|
||||
(setq pt (car frame))
|
||||
(if (vfl-wizard-aktiv)
|
||||
(vl-catch-all-apply 'vflw-gruppe-horizontal-impl (list ziel-modus)))
|
||||
(princ (ssg-text "vfl-sep-vor-frage"))
|
||||
(princ (ssg-text "vfl-ja"))
|
||||
(princ (ssg-text "vfl-nein"))
|
||||
(setq sep-vor (= (vfl-menu (ssg-text "vfl-prompt-wahl-1-2-def2")
|
||||
(list (ssg-text "vfl-ja") (ssg-text "vfl-nein")) 2 "vfl-sep-vor-frage") "1"))
|
||||
(if (and ziel-modus sep-vor) (setq dL (max *vfk-restlaenge-min-clamp* (- dL *vfk-separator-laenge*))))
|
||||
;; auf_3-Eintritt nur, wenn noch NICHT flach (sonst flache Zone fortsetzen).
|
||||
;; dL ist die gewuenschte Reststrecke AB HIER (pt) - der reale Bogen-
|
||||
@@ -2421,18 +2459,53 @@
|
||||
(setq pt (insert-rotated-block-with-ks "Vario_Bogen_auf_3_TEF_rechts" pt
|
||||
(ssg-cfg-or "vario" "gefaelle_winkel" 3) (car m1) (caddr m1) hz))
|
||||
(setq dL (max *vfk-restlaenge-min-clamp* (- dL (vfl-projiziere-distanz pt-vor-bogen pt hz))))))
|
||||
;; Separator NACH abfragen (noch nicht bauen) - im Ziel-Modus VOR der
|
||||
;; Laengenberechnung, damit der Fussabdruck feststeht.
|
||||
(if (and ziel-modus sep-nach) (setq dL (max *vfk-restlaenge-min-clamp* (- dL *vfk-separator-laenge*))))
|
||||
;; Ausgangs-Fussabdruck (Vario_Bogen_ab_3 + Motorstation) nur reservieren,
|
||||
;; wenn die Einheit tatsaechlich sofort schliesst (Antwort 1 oder 3); bei
|
||||
;; Antwort 1 zusaetzlich die GF2 hinter dem Motor.
|
||||
(if ziel-modus
|
||||
(cond
|
||||
((= ende-code "1")
|
||||
(setq dL (max *vfk-restlaenge-min-clamp* (- dL (car (get-bogen-mass bogen-ab (fix *vfk-gefaelle-winkel*))) *vfk-stations-laenge*
|
||||
(* (if gf2-laenge gf2-laenge 0.0)
|
||||
(cos (* *vfk-gefaelle-winkel* (/ pi 180.0))))))))
|
||||
((= ende-code "3")
|
||||
(setq dL (max *vfk-restlaenge-min-clamp* (- dL (car (get-bogen-mass bogen-ab (fix *vfk-gefaelle-winkel*))) *vfk-stations-laenge*))))))
|
||||
;; optionaler Separator VOR - in der horizontalen Ebene (0 Grad)
|
||||
(if sep-vor (setq pt (vfl-sep-hz pt hz)))
|
||||
;; horizontale Zwischenstrecke (0 Grad)
|
||||
(princ (ssg-textf "vfl-horizontale-zwischenstrecke" (list (rtos dL 2 2))))
|
||||
(setq pt (insert-inclined-scaled-block "Staustrecke_SP_1000_mm" pt dL 0 hz))
|
||||
(vfl-acc-vf-seg "horizontal" 0 dL)
|
||||
;; optionaler Separator NACH (jetzt tatsaechlich bauen)
|
||||
(if sep-nach (setq pt (vfl-sep-hz pt hz)))
|
||||
;; KEIN ab_3 mehr -> das Stueck ENDET FLACH (0 Grad)
|
||||
(make-frame-from-dir pt (hz-winkel->xu hz 0.0))
|
||||
)
|
||||
|
||||
;; --- Horizontaler Koerper: Frage-Schale (Name unveraendert) ---
|
||||
;; Fragt Separator vor/nach und - im Ziel-Modus - "Ist der Endpunkt der
|
||||
;; Foerderer?", danach baut vfl-hor-koerper-bauen. Keine der drei Fragen
|
||||
;; haengt an einem berechneten Wert, sie stehen darum alle vor dem Bau; die
|
||||
;; Journal-Reihenfolge ist dieselbe wie zuvor.
|
||||
;; Die Endpunkt-Antwort wird im Ziel-Modus mit zurueckgegeben (vor-antwort),
|
||||
;; damit die Fortsetzungsschleife des Aufrufers sie nicht erneut erfragt.
|
||||
(defun vfl-baue-horizontal-koerper (frame hz dL ziel-modus gf2-laenge /
|
||||
sep-vor sep-nach ist-ende-antwort neu)
|
||||
(if (vfl-wizard-aktiv)
|
||||
(vl-catch-all-apply 'vflw-gruppe-horizontal-impl (list ziel-modus)))
|
||||
(princ (ssg-text "vfl-sep-vor-frage"))
|
||||
(princ (ssg-text "vfl-ja"))
|
||||
(princ (ssg-text "vfl-nein"))
|
||||
(setq sep-vor (= (vfl-menu (ssg-text "vfl-prompt-wahl-1-2-def2")
|
||||
(list (ssg-text "vfl-ja") (ssg-text "vfl-nein")) 2 "vfl-sep-vor-frage") "1"))
|
||||
;; Separator NACH abfragen (noch nicht bauen) - im Ziel-Modus muss der
|
||||
;; Fussabdruck vor der Laengenberechnung feststehen.
|
||||
(princ (ssg-text "vfl-sep-nach-frage"))
|
||||
(princ (ssg-text "vfl-ja"))
|
||||
(princ (ssg-text "vfl-nein"))
|
||||
(setq sep-nach (= (vfl-menu (ssg-text "vfl-prompt-wahl-1-2-def2")
|
||||
(list (ssg-text "vfl-ja") (ssg-text "vfl-nein")) 2 "vfl-sep-nach-frage") "1"))
|
||||
(if (and ziel-modus sep-nach) (setq dL (max *vfk-restlaenge-min-clamp* (- dL *vfk-separator-laenge*))))
|
||||
;; Im Ziel-Modus: "Ist der Endpunkt der Foerderer?" JETZT abfragen (statt
|
||||
;; erst danach in der Aufruferschleife) - der Ausgangs-Fussabdruck
|
||||
;; (Vario_Bogen_ab_3 + Motorstation) wird nur reserviert, wenn tatsaechlich
|
||||
;; sofort geschlossen wird (Antwort 1 oder 3).
|
||||
(if ziel-modus
|
||||
(progn
|
||||
(princ (ssg-text "vfl-ist-endpunkt-frage"))
|
||||
@@ -2443,28 +2516,10 @@
|
||||
(list (ssg-text "vfl-ja-nur-motorstation")
|
||||
(ssg-text "vfl-nein-weiterbauen")
|
||||
(ssg-text "vfl-ja-motorstation-kettenende")) 2
|
||||
"vfl-ist-endpunkt-frage"))
|
||||
(cond
|
||||
((= ist-ende-antwort "1")
|
||||
(setq dL (max *vfk-restlaenge-min-clamp* (- dL (car (get-bogen-mass bogen-ab (fix *vfk-gefaelle-winkel*))) *vfk-stations-laenge*
|
||||
(* (if gf2-laenge gf2-laenge 0.0)
|
||||
(cos (* *vfk-gefaelle-winkel* (/ pi 180.0))))))))
|
||||
((= ist-ende-antwort "3")
|
||||
(setq dL (max *vfk-restlaenge-min-clamp* (- dL (car (get-bogen-mass bogen-ab (fix *vfk-gefaelle-winkel*))) *vfk-stations-laenge*))))
|
||||
)
|
||||
)
|
||||
)
|
||||
;; optionaler Separator VOR - in der horizontalen Ebene (0 Grad)
|
||||
(if sep-vor (setq pt (vfl-sep-hz pt hz)))
|
||||
;; horizontale Zwischenstrecke (0 Grad)
|
||||
(princ (ssg-textf "vfl-horizontale-zwischenstrecke" (list (rtos dL 2 2))))
|
||||
(setq pt (insert-inclined-scaled-block "Staustrecke_SP_1000_mm" pt dL 0 hz))
|
||||
(vfl-acc-vf-seg "horizontal" 0 dL)
|
||||
;; optionaler Separator NACH (jetzt tatsaechlich bauen)
|
||||
(if sep-nach (setq pt (vfl-sep-hz pt hz)))
|
||||
;; KEIN ab_3 mehr -> das Stueck ENDET FLACH (0 Grad)
|
||||
(setq frame (make-frame-from-dir pt (hz-winkel->xu hz 0.0)))
|
||||
(if ziel-modus (list frame ist-ende-antwort) frame)
|
||||
"vfl-ist-endpunkt-frage"))))
|
||||
(setq neu (vfl-hor-koerper-bauen frame hz dL ziel-modus ist-ende-antwort
|
||||
sep-vor sep-nach gf2-laenge))
|
||||
(if ziel-modus (list neu ist-ende-antwort) neu)
|
||||
)
|
||||
|
||||
;; ============================================================
|
||||
@@ -2536,36 +2591,21 @@
|
||||
;; mit Motor -> GF2 -> Separator -> ES ab). Speichert den Soll-Zielpunkt in
|
||||
;; *vfl-ziel-punkt* fuer den Ist-Ziel-Report.
|
||||
;; Rueckgabe: (frame anzahl-koerper letzt-hz gf2-laenge) oder nil.
|
||||
(defun vfl-body-abschluss (frame letzt-hz p-umlenk /
|
||||
linie-mess dL hzn hn dH richtn rad3 dec typ w lgf lvf
|
||||
gf2 cnt p0 tx ty
|
||||
hoehe-vorschlaege hoehe-ab hoehe-auf hoehe-fallback)
|
||||
;; Der Abschluss-Koerper (vfs-vf-koerper) und der Motor gehen von 3-Grad-Basis
|
||||
;; aus - eine gerade laufende flache Zone hier mit ab_3 abschliessen.
|
||||
(setq frame (vfl-nach-3grad frame))
|
||||
(setq linie-mess (vfl-neue-linie-messen (car frame) letzt-hz))
|
||||
(if (null linie-mess)
|
||||
(progn (princ (ssg-text "vfl-abgebrochen-kein-ziel")) nil)
|
||||
(progn
|
||||
(setq dL (car linie-mess) hzn (cadr linie-mess))
|
||||
;; Baubare Vorschlaege (Ab/Auf) statt der unveraenderten Kettenhoehe als
|
||||
;; Default - sonst waere dH=0 ("Auf", da (>= 0.0 0.0)) und der Rest wird
|
||||
;; von vfl-body-zerlegung sofort als "nicht baubar" abgelehnt (kleineres
|
||||
;; Budget als vfl-vf-entscheidung: nur Motor+Separator, 800mm statt
|
||||
;; 1300mm) - siehe vfl-body-hoehe-vorschlaege/vfl-body-machbar-p.
|
||||
(setq hoehe-vorschlaege (vfl-body-hoehe-vorschlaege (caddr (car frame)) dL))
|
||||
(setq hoehe-ab (nth 0 hoehe-vorschlaege) hoehe-auf (nth 1 hoehe-vorschlaege))
|
||||
(setq hoehe-fallback (if hoehe-ab hoehe-ab (caddr (car frame))))
|
||||
(vfl-hoehe-hinweis-falls-noetig hoehe-ab hoehe-auf)
|
||||
(if (vfl-wizard-aktiv)
|
||||
(vl-catch-all-apply 'vflw-gruppe-ziel-hoehe-impl
|
||||
(list (list hoehe-ab hoehe-auf (caddr (car frame))))))
|
||||
(setq hn (vfl-in-real
|
||||
(if (and hoehe-ab hoehe-auf)
|
||||
(ssg-textf "vfl-prompt-hoehe-kettenende-2"
|
||||
(list (rtos hoehe-ab 2 1) (rtos hoehe-auf 2 1)))
|
||||
(ssg-textf "vfl-prompt-hoehe-kettenende" (list (rtos hoehe-fallback 2 1))))))
|
||||
(if (null hn) (setq hn hoehe-fallback))
|
||||
;; --- Kettenende-Abschluss: reiner Bauteil ---
|
||||
;; Zerlegt die Reststrecke und baut Koerper + Motor-Vorbereitung. Fragt
|
||||
;; nichts mehr - die einzige Rueckfrage steckt in vfl-body-zerlegung
|
||||
;; (vfl-waehle-winkel, nur wenn mehrere Winkel gueltig sind) und ist ueber
|
||||
;; *vfl-winkel-idx-vorgabe* vorab beantwortbar.
|
||||
;; Rueckgabe: (frame anzahl-koerper hz gf2-laenge) oder nil, wenn der Rest
|
||||
;; nicht baubar ist.
|
||||
;;
|
||||
;; Signatur schlanker als im Fahrplan skizziert: letzt-hz und es-gewuenscht
|
||||
;; werden hier nicht gebraucht. es-gewuenscht wirkt beim Aufrufer, der
|
||||
;; ein-dx/ein-dz um den Aufruf herum nullt (kein ES-Fussabdruck reservieren);
|
||||
;; letzt-hz wurde nur einer wirkungslosen Zuweisung zugefuehrt (siehe unten).
|
||||
(defun vfl-body-abschluss-bauen (frame p-umlenk dL hzn hn /
|
||||
dH richtn rad3 dec typ w lgf lvf gf2 cnt
|
||||
p0 tx ty letzt-hz)
|
||||
(setq dH (- hn (caddr (car frame))))
|
||||
(setq richtn (if (>= dH 0.0) "Auf" "Ab"))
|
||||
(setq dH (abs dH))
|
||||
@@ -2600,6 +2640,9 @@
|
||||
(setq frame (vfl-frame-3grad (vfs-vf-koerper (car frame) richtn w lvf hzn) hzn))
|
||||
(vfl-acc-vf-seg richtn w lvf)
|
||||
(setq cnt (1+ cnt))))
|
||||
;; ohne Wirkung: letzt-hz ist lokal und wird nicht mehr gelesen -
|
||||
;; die Fahrtrichtung verlaesst die Funktion ueber die Rueckgabe.
|
||||
;; Beim Umzug bewusst wortwoertlich uebernommen.
|
||||
(setq letzt-hz hzn)
|
||||
(vfl-20m-check p-umlenk (car frame))
|
||||
;; GF2-Laenge (hinter dem Motor): aus der Zerlegung (variiert mit Winkel),
|
||||
@@ -2619,6 +2662,44 @@
|
||||
(list frame cnt hzn gf2)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; --- Kettenende-Abschluss: Frage-Schale (Name unveraendert) ---
|
||||
;; "Alles fragen, dann alles bauen" geht hier NICHT: vfl-neue-linie-messen
|
||||
;; braucht den Frame NACH vfl-nach-3grad, und die Hoehenvorschlaege brauchen
|
||||
;; die gemessene Laenge und die aktuelle Kettenhoehe. Darum in dieser
|
||||
;; Reihenfolge: flache Zone abschliessen -> Laenge messen -> Vorschlaege
|
||||
;; berechnen -> Zielhoehe fragen -> bauen.
|
||||
(defun vfl-body-abschluss (frame letzt-hz p-umlenk /
|
||||
linie-mess dL hzn hn
|
||||
hoehe-vorschlaege hoehe-ab hoehe-auf hoehe-fallback)
|
||||
;; Der Abschluss-Koerper (vfs-vf-koerper) und der Motor gehen von 3-Grad-Basis
|
||||
;; aus - eine gerade laufende flache Zone hier mit ab_3 abschliessen.
|
||||
(setq frame (vfl-nach-3grad frame))
|
||||
(setq linie-mess (vfl-neue-linie-messen (car frame) letzt-hz))
|
||||
(if (null linie-mess)
|
||||
(progn (princ (ssg-text "vfl-abgebrochen-kein-ziel")) nil)
|
||||
(progn
|
||||
(setq dL (car linie-mess) hzn (cadr linie-mess))
|
||||
;; Baubare Vorschlaege (Ab/Auf) statt der unveraenderten Kettenhoehe als
|
||||
;; Default - sonst waere dH=0 ("Auf", da (>= 0.0 0.0)) und der Rest wird
|
||||
;; von vfl-body-zerlegung sofort als "nicht baubar" abgelehnt (kleineres
|
||||
;; Budget als vfl-vf-entscheidung: nur Motor+Separator, 800mm statt
|
||||
;; 1300mm) - siehe vfl-body-hoehe-vorschlaege/vfl-body-machbar-p.
|
||||
(setq hoehe-vorschlaege (vfl-body-hoehe-vorschlaege (caddr (car frame)) dL))
|
||||
(setq hoehe-ab (nth 0 hoehe-vorschlaege) hoehe-auf (nth 1 hoehe-vorschlaege))
|
||||
(setq hoehe-fallback (if hoehe-ab hoehe-ab (caddr (car frame))))
|
||||
(vfl-hoehe-hinweis-falls-noetig hoehe-ab hoehe-auf)
|
||||
(if (vfl-wizard-aktiv)
|
||||
(vl-catch-all-apply 'vflw-gruppe-ziel-hoehe-impl
|
||||
(list (list hoehe-ab hoehe-auf (caddr (car frame))))))
|
||||
(setq hn (vfl-in-real
|
||||
(if (and hoehe-ab hoehe-auf)
|
||||
(ssg-textf "vfl-prompt-hoehe-kettenende-2"
|
||||
(list (rtos hoehe-ab 2 1) (rtos hoehe-auf 2 1)))
|
||||
(ssg-textf "vfl-prompt-hoehe-kettenende" (list (rtos hoehe-fallback 2 1))))))
|
||||
(if (null hn) (setq hn hoehe-fallback))
|
||||
(vfl-body-abschluss-bauen frame p-umlenk dL hzn hn)
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -3083,6 +3164,17 @@
|
||||
;; 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.
|
||||
;; ES-Masse setzen (kein Bau, nur Globals): Winkel merken und die
|
||||
;; ein-dx/ein-dz-Masse des ES-Elements laden. Aus der Frage-Schale gezogen,
|
||||
;; damit der Daten-Pfad sie ohne Frage setzen kann.
|
||||
;; *vfl-es-winkel* wird LAZY von den Blocknamen-Bauern gelesen (vfl-es-winkel
|
||||
;; weiter oben) - es MUSS also vor dem ersten ES-Insert stehen, sonst waehlt
|
||||
;; der Bau den falschen Block.
|
||||
(defun vfl-es-masse-setzen (winkel seite)
|
||||
(setq *vfl-es-winkel* winkel)
|
||||
(vf-set-es-masse winkel seite)
|
||||
seite)
|
||||
|
||||
(defun vfl-frage-es-seite ( / antwort es-seite)
|
||||
(vfl-journal-mark "ES")
|
||||
(if (vfl-wizard-aktiv)
|
||||
@@ -3100,7 +3192,7 @@
|
||||
(list (ssg-text "gf-seite-links") (ssg-text "gf-seite-rechts")) 1
|
||||
"gf-seite-ein-header"))
|
||||
(setq es-seite (if (= antwort "2") "rechts" "links"))
|
||||
(vf-set-es-masse *vfl-es-winkel* es-seite)
|
||||
(vfl-es-masse-setzen *vfl-es-winkel* es-seite)
|
||||
es-seite
|
||||
)
|
||||
|
||||
@@ -3239,6 +3331,32 @@
|
||||
;; auto-ende: T => keine Kettenende-Frage, es wird direkt Separator + ES
|
||||
;; gesetzt (Option "Neue Linie BIS Kettenende").
|
||||
;; Rueckgabe: (frame anzahl-koerper ende-flag es-seite-oder-nil).
|
||||
;; Soll-Ist-Vergleich am Kettenende ausgeben (Zielpunkt aus dem
|
||||
;; Modus-1-Zielmodus, gesetzt in vfl-body-abschluss). Stand vorher zweimal
|
||||
;; wortgleich im Abschluss - einmal fuer "Kettenende ohne ES", einmal fuer
|
||||
;; "Kettenende mit ES"; die Faelle unterscheiden sich nur darin, WANN sie
|
||||
;; gemeldet werden, nicht WAS.
|
||||
;; Loescht *vfl-ziel-punkt* danach: der Report gehoert zu genau einem
|
||||
;; Kettenende, ein zweiter Aufruf soll nichts mehr melden.
|
||||
(defun vfl-ziel-report (frame)
|
||||
(if *vfl-ziel-punkt*
|
||||
(progn
|
||||
(princ (ssg-text "vfl-ist-ziel-vergleich-header"))
|
||||
(princ (ssg-textf "vfl-soll-xyz"
|
||||
(list (rtos (car *vfl-ziel-punkt*) 2 1)
|
||||
(rtos (cadr *vfl-ziel-punkt*) 2 1)
|
||||
(rtos (caddr *vfl-ziel-punkt*) 2 1))))
|
||||
(princ (ssg-textf "vfl-ist-xyz"
|
||||
(list (rtos (car (car frame)) 2 1)
|
||||
(rtos (cadr (car frame)) 2 1)
|
||||
(rtos (caddr (car frame)) 2 1))))
|
||||
(princ (ssg-textf "vfl-abweichung-xyz"
|
||||
(list (rtos (- (car (car frame)) (car *vfl-ziel-punkt*)) 2 1)
|
||||
(rtos (- (cadr (car frame)) (cadr *vfl-ziel-punkt*)) 2 1)
|
||||
(rtos (- (caddr (car frame)) (caddr *vfl-ziel-punkt*)) 2 1))))
|
||||
(setq *vfl-ziel-punkt* nil)))
|
||||
(princ))
|
||||
|
||||
(defun vfl-vf-einheit-abschluss (frame hz richtung winkel L_GF L_VF auto-ende /
|
||||
antwort res es-s ende es-gewuenscht)
|
||||
;; GF-Verteilung: halbe Staustrecke am Ausgang (GF2) oder alles am Einlauf.
|
||||
@@ -3277,24 +3395,7 @@
|
||||
(setq ende t)
|
||||
;; Ist-Ziel-Report auch ohne ES (Vergleich Soll-Zielpunkt vs. tatsaechliches
|
||||
;; Kettenende nach GF2/Motor).
|
||||
(if *vfl-ziel-punkt*
|
||||
(progn
|
||||
(princ (ssg-text "vfl-ist-ziel-vergleich-header"))
|
||||
(princ (ssg-textf "vfl-soll-xyz"
|
||||
(list (rtos (car *vfl-ziel-punkt*) 2 1)
|
||||
(rtos (cadr *vfl-ziel-punkt*) 2 1)
|
||||
(rtos (caddr *vfl-ziel-punkt*) 2 1))))
|
||||
(princ (ssg-textf "vfl-ist-xyz"
|
||||
(list (rtos (car (car frame)) 2 1)
|
||||
(rtos (cadr (car frame)) 2 1)
|
||||
(rtos (caddr (car frame)) 2 1))))
|
||||
(princ (ssg-textf "vfl-abweichung-xyz"
|
||||
(list (rtos (- (car (car frame)) (car *vfl-ziel-punkt*)) 2 1)
|
||||
(rtos (- (cadr (car frame)) (cadr *vfl-ziel-punkt*)) 2 1)
|
||||
(rtos (- (caddr (car frame)) (caddr *vfl-ziel-punkt*)) 2 1))))
|
||||
(setq *vfl-ziel-punkt* nil)
|
||||
)
|
||||
)
|
||||
(vfl-ziel-report frame)
|
||||
)
|
||||
((= antwort "1")
|
||||
(setq es-s (vfl-frage-es-seite))
|
||||
@@ -3303,24 +3404,7 @@
|
||||
(caddr (car frame)) es-s))
|
||||
(setq ende t)
|
||||
;; Ist-Ziel-Report (Option 3): Soll-ES (aus vfl-body-abschluss) vs. Ist-ES.
|
||||
(if *vfl-ziel-punkt*
|
||||
(progn
|
||||
(princ (ssg-text "vfl-ist-ziel-vergleich-header"))
|
||||
(princ (ssg-textf "vfl-soll-xyz"
|
||||
(list (rtos (car *vfl-ziel-punkt*) 2 1)
|
||||
(rtos (cadr *vfl-ziel-punkt*) 2 1)
|
||||
(rtos (caddr *vfl-ziel-punkt*) 2 1))))
|
||||
(princ (ssg-textf "vfl-ist-xyz"
|
||||
(list (rtos (car (car frame)) 2 1)
|
||||
(rtos (cadr (car frame)) 2 1)
|
||||
(rtos (caddr (car frame)) 2 1))))
|
||||
(princ (ssg-textf "vfl-abweichung-xyz"
|
||||
(list (rtos (- (car (car frame)) (car *vfl-ziel-punkt*)) 2 1)
|
||||
(rtos (- (cadr (car frame)) (cadr *vfl-ziel-punkt*)) 2 1)
|
||||
(rtos (- (caddr (car frame)) (caddr *vfl-ziel-punkt*)) 2 1))))
|
||||
(setq *vfl-ziel-punkt* nil)
|
||||
)
|
||||
)
|
||||
(vfl-ziel-report frame)
|
||||
)
|
||||
(t
|
||||
(princ (ssg-text "vfl-sep-an-stelle-frage"))
|
||||
|
||||
+40
-2
@@ -529,6 +529,7 @@
|
||||
(cons "eingaben_offen" 0)
|
||||
(cons "glieder_soll" nil)
|
||||
(cons "glieder_ist" nil)
|
||||
(cons "journal_diff" nil)
|
||||
(cons "prompts" 0)
|
||||
(cons "meldungen" nil)
|
||||
(cons "spec_fehler" nil)
|
||||
@@ -600,6 +601,8 @@
|
||||
",\n"
|
||||
" \"glieder_ist\": " (vsp-json-liste (ssg-val rec "glieder_ist"))
|
||||
",\n"
|
||||
" \"journal_diff\": " (vsp-json-str (ssg-val rec "journal_diff"))
|
||||
",\n"
|
||||
" \"prompts\": " (vsp-json-int (ssg-val rec "prompts")) ",\n"
|
||||
" \"meldungen\": " (vsp-json-liste (ssg-val rec "meldungen"))
|
||||
",\n"
|
||||
@@ -687,6 +690,32 @@
|
||||
(setq soll (cdr soll) ist (cdr ist))))
|
||||
diff)
|
||||
|
||||
;; Das aus der Spec erzeugte Journal gegen das beim Bau NEU aufgezeichnete
|
||||
;; stellen. Beide muessen Token fuer Token gleich sein: der Replay gibt jeden
|
||||
;; Wert an denselben Wrapper, der ihn wieder journalisiert - passt der Ablauf
|
||||
;; zur Spec, entsteht dasselbe Journal.
|
||||
;;
|
||||
;; Das ist der SCHAERFSTE Desync-Detektor. Offene Queue-Werte und die
|
||||
;; Glied-Folge zeigen nur grobe Abweichungen; ein einzelner Wert, der an der
|
||||
;; falschen Stelle verbraucht wird, kann beide passieren lassen und trotzdem
|
||||
;; andere Geometrie erzeugen. Rueckgabe: nil bei Gleichheit, sonst ein Text
|
||||
;; mit der ersten Abweichung.
|
||||
(defun vsp-journal-diff (soll ist / i a b diff)
|
||||
(setq i 0 diff nil)
|
||||
(while (and (null diff) (or soll ist))
|
||||
(setq i (1+ i) a (car soll) b (car ist))
|
||||
(cond
|
||||
((null a) (setq diff (strcat "Token " (itoa i) ": Spec zu Ende, gebaut "
|
||||
(vl-princ-to-string b))))
|
||||
((null b) (setq diff (strcat "Token " (itoa i) ": erwartet "
|
||||
(vl-princ-to-string a) ", gebaut nichts")))
|
||||
((not (equal a b))
|
||||
(setq diff (strcat "Token " (itoa i) ": erwartet "
|
||||
(vl-princ-to-string a) ", gebaut "
|
||||
(vl-princ-to-string b))))
|
||||
(T (setq soll (cdr soll) ist (cdr ist)))))
|
||||
diff)
|
||||
|
||||
;; Eine Kette aus einer Spec bauen. Rueckgabe: Ergebnis-Record (Alist).
|
||||
;; Die Klammer um vf-linienzug-modus ist dieselbe wie in
|
||||
;; tests/test_hm_recformat.lsp: *error* sichern, fangen, *error* zurueck. Weil
|
||||
@@ -696,7 +725,7 @@
|
||||
;; schon SSG_VF_EDIT_SEG/_PRE geschrieben).
|
||||
(defun vsp-bau-aus-spec (spec / kopf sid journal rec vor-ent alt-error err
|
||||
offen hl meld ist-glieder soll-glieder diff
|
||||
ent ed dim)
|
||||
ist-journal jdiff ent ed dim)
|
||||
(setq kopf (vsp-kopf spec))
|
||||
(setq sid (vsp-spec-id kopf))
|
||||
(setq journal (vsp-journal spec))
|
||||
@@ -739,7 +768,8 @@
|
||||
(setq err (vl-catch-all-apply 'vf-linienzug-modus '()))
|
||||
(setq offen (vsp-queue-rest))
|
||||
;; Diagnose VOR dem Reset sichern (vfl-journal-reset loescht sie).
|
||||
(setq ist-glieder (vfl-journal-glieder (reverse *vfl-journal*)))
|
||||
(setq ist-journal (reverse *vfl-journal*))
|
||||
(setq ist-glieder (vfl-journal-glieder ist-journal))
|
||||
(setq hl (vsp-headless-fehler-text))
|
||||
(setq meld (if (boundp '*vfl-meldungen*) *vfl-meldungen* nil))
|
||||
(setq *error* alt-error)
|
||||
@@ -760,6 +790,8 @@
|
||||
(setq rec (vsp-set rec "attribs" (ssg-attrib-read ent)))))
|
||||
|
||||
(setq diff (vsp-glieder-diff soll-glieder ist-glieder))
|
||||
(setq jdiff (vsp-journal-diff journal ist-journal))
|
||||
(setq rec (vsp-set rec "journal_diff" jdiff))
|
||||
(cond
|
||||
(hl
|
||||
(princ (strcat "\n DESYNC: " hl))
|
||||
@@ -782,6 +814,12 @@
|
||||
(princ (strcat "\n DESYNC: " diff))
|
||||
(setq rec (vsp-set rec "status" "desync"))
|
||||
(setq rec (vsp-set rec "fehler_text" diff)))
|
||||
;; Feinster Detektor zuletzt: Queue geht auf, Glied-Folge stimmt -
|
||||
;; aber ein Wert wurde an anderer Stelle verbraucht als geplant.
|
||||
(jdiff
|
||||
(princ (strcat "\n DESYNC (Journal): " jdiff))
|
||||
(setq rec (vsp-set rec "status" "desync"))
|
||||
(setq rec (vsp-set rec "fehler_text" jdiff)))
|
||||
((null ent)
|
||||
(princ "\n FEHLER: kein VF_-Block entstanden")
|
||||
(setq rec (vsp-set rec "status" "abbruch"))
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
| P2 | `*vfl-headless*`: leere Replay-Queue wird zum Fehler | **erledigt** |
|
||||
| P3 | Kleinkram (`*vfl-meldungen*`-Reset, `vfl-view-refresh`) | **erledigt** |
|
||||
| S1 | Stufe 1: `Lisp/vf_spec.lsp` (Spec -> Journal -> Replay) | **Modus 1 fertig**, im CAD ungetestet |
|
||||
| S2 | Stufe 2: echte Funktionstrennung (Schritte 0-11) | offen |
|
||||
| S2 | Stufe 2: echte Funktionstrennung | **Schritte 0-4 + 7-Assertion fertig**, 5/6/8/9/10 offen, 11 zurueckgestellt |
|
||||
| PY | `lib/vf_journal_export.py`: Grammatik vervollstaendigen | **erledigt** |
|
||||
|
||||
### Was Stufe 0 konkret gebracht hat
|
||||
@@ -562,6 +562,64 @@ bei jedem Bau.
|
||||
vorliegen (`tests/testdata/hundm05.json` enthaelt ausschliesslich Modus-1-
|
||||
Ketten); sonst zurueckstellen
|
||||
|
||||
### Umsetzungsstand Stufe 2 (2026-09-03)
|
||||
|
||||
**Fertig: Schritte 0-4 und der Assertion-Teil von Schritt 7.** Alle fuenf
|
||||
sind verhaltensgleich - die Frage-Schalen behalten ihre Namen, keine
|
||||
Aufrufstelle in Modus 1/2/3 wurde angefasst.
|
||||
|
||||
| Schritt | Neu | Nachweis |
|
||||
|---|---|---|
|
||||
| 0 | `vfl-ziel-report` | zwei byte-identische Bloecke ersetzt, Diff zeigt nur die Entfernung |
|
||||
| 1 | `vfl-winkel-gueltige`, `vfl-winkel-nach-index`, Kanal `*vfl-winkel-idx-vorgabe*` | Verzweigungen 1:1; ungueltiger Index faellt wie zuvor auf den ersten Kandidaten |
|
||||
| 2 | `vfl-es-masse-setzen` | reines Globals-Setzen aus `vfl-frage-es-seite` gezogen |
|
||||
| 3 | `vfl-hor-koerper-bauen` | Zeilenvergleich alt/neu: dieselben 15 geometrie-relevanten Zeilen in derselben Reihenfolge |
|
||||
| 4 | `vfl-body-abschluss-bauen` | Multiset-Vergleich: ausser Kopf, Klammer und Aufruf keine Zeile veraendert |
|
||||
| 7 (Teil) | `vsp-journal-diff` + Record-Feld `journal_diff` | neuer Test `test_journal_token_gleich` |
|
||||
|
||||
Zu Schritt 3 im Detail: die drei Fragen (Separator vor/nach, Endpunkt)
|
||||
haengen an keinem berechneten Wert und stehen jetzt alle vor dem Bau. Die
|
||||
**Journal-Reihenfolge und die Entity-Reihenfolge bleiben identisch** - nur
|
||||
interaktiv erscheint der auf_3-Uebergangsblock erst nach den drei Fragen
|
||||
statt zwischen Frage 1 und 2. Die dL-Subtraktionskette wurde Zeile fuer
|
||||
Zeile uebernommen, mit dem auf_3-Insert an derselben Stelle dazwischen.
|
||||
|
||||
Zwei bewusste Abweichungen von der Planung:
|
||||
- `vfl-hor-koerper-bauen` behaelt `ziel-modus` als EIGENEN Parameter neben
|
||||
`ende-code` (der Fahrplan wollte ihn ersetzen). Die Separator-Subtraktionen
|
||||
haengen allein am Ziel-Modus, und `ende-code` darf nil sein (abgebrochene
|
||||
Menuefrage) - mit nur einem Parameter waere genau dieser Fall eine stille
|
||||
Verhaltensaenderung.
|
||||
- `vfl-body-abschluss-bauen` hat eine schlankere Signatur als skizziert:
|
||||
`letzt-hz` und `es-gewuenscht` braucht es nicht. `es-gewuenscht` wirkt beim
|
||||
Aufrufer (der `ein-dx`/`ein-dz` um den Aufruf herum nullt), `letzt-hz` ging
|
||||
nur in eine wirkungslose Zuweisung - beim Umzug wortwoertlich uebernommen
|
||||
und als solche markiert.
|
||||
|
||||
Schritt 7 ist nur zur Haelfte erledigt: die Spec und der Serializer stehen
|
||||
schon aus Stufe 1, neu ist die **Assertion** - `vsp-journal-diff` stellt das
|
||||
aus der Spec erzeugte Journal Token fuer Token gegen das beim Bau neu
|
||||
aufgezeichnete. Das ist der schaerfste Desync-Detektor: offene Queue-Werte
|
||||
und die Glied-Folge zeigen nur grobe Abweichungen, ein einzelner Wert, der an
|
||||
der falschen Stelle verbraucht wird, kann beide passieren lassen und trotzdem
|
||||
andere Geometrie erzeugen. Sitzt im Daten-Pfad (`vf_spec.lsp`), nicht als
|
||||
`dbgmsg` im Bau-Pfad - dort waere er nur bei offener `.dbg`-Datei sichtbar.
|
||||
|
||||
**Offen: 5, 6, 8, 9, 10** (VF-Einheit aufteilen, Daten-Executor,
|
||||
Einheit-Abschluss, Kettenebene je Glied-Zweig, `vfl-spec-ausfuehren`).
|
||||
Bewusst NICHT vorab erledigt: das sind die Schritte, bei denen laut Abschnitt
|
||||
"Wo Geometrie still kippen kann" die Reihenfolge selbst Geometrie ist. Der
|
||||
Fahrplan verlangt nach jedem Schritt einen CAD-Lauf (`TEST_HUNDM05` +
|
||||
`TEST_VF_SPEC` + `TEST_LINIENZUG`), nach den Geometrie-Schritten zusaetzlich
|
||||
einen DXF-Diff und einen Sivas-Export-Vergleich. Ohne diese Laeufe zwischen
|
||||
den Schritten waere ein Fehler nicht mehr einem Schritt zuzuordnen.
|
||||
|
||||
Zur Erwartungshaltung: die Schritte 5-10 bringen **keine neue Faehigkeit**.
|
||||
Der Bau aus Daten funktioniert seit Stufe 1 (ueber den Replay) und ist mit
|
||||
`VF_SPEC_BAU` in der Praxis belegt. Was 5-10 bringen, ist der direkte
|
||||
Daten-Pfad ohne den Replay-Umweg und einzeln aufrufbare Knoten-Bauteile -
|
||||
Architektur, nicht Funktion.
|
||||
|
||||
### Wo Geometrie still kippen kann
|
||||
|
||||
- **Die dL-Subtraktionskette** in `vfl-baue-horizontal-koerper` (:2280, :2293,
|
||||
|
||||
@@ -86,6 +86,18 @@ class TestSpecBau:
|
||||
f'{r["spec_id"]}: gebaut {r["glieder_ist"]}, ' \
|
||||
f'geplant {r["glieder_soll"]}'
|
||||
|
||||
def test_journal_token_gleich(self, spec_ergebnisse):
|
||||
"""Der schaerfste Desync-Detektor: das aus der Spec erzeugte Journal
|
||||
und das beim Bau neu aufgezeichnete muessen Token fuer Token gleich
|
||||
sein. Queue-Rest und Glied-Folge zeigen nur grobe Abweichungen - ein
|
||||
einzelner Wert, der an der falschen Stelle verbraucht wird, kann
|
||||
beide passieren lassen und trotzdem andere Geometrie erzeugen."""
|
||||
for r in spec_ergebnisse:
|
||||
if "journal_diff" not in r:
|
||||
pytest.skip("Ergebnis aus einem Lauf vor Stufe 2")
|
||||
assert not r["journal_diff"], \
|
||||
f'{r["spec_id"]}: {r["journal_diff"]}'
|
||||
|
||||
def test_keine_meldungen(self, spec_ergebnisse):
|
||||
for r in spec_ergebnisse:
|
||||
assert not r["meldungen"], \
|
||||
|
||||
Reference in New Issue
Block a user