fix: VF-Linienzug Journal-Reset, Spec-Grammatik (auf-ab/Linie-GF) und hz-Bug in HundM05

- vfl-journal-reset-lauf ans Funktionsanfang verschoben (Modus 1/2/3,
  Lisp/vf_linienzug.lsp): stand bisher nach der Startpunkt/Hoehe/AS-
  Praeambel und loeschte diese beim Reset selbst wieder - Journal begann
  mit dem ersten STEP-Marker statt PT, betraf jede Kette.
- Spec-Grammatik erweitert (Lisp/vf_spec.lsp, lib/vf_linienzug_grammatik.py,
  lib/vf_spec_export.py): "auf-ab" bei bereits flach ankommender Kette
  (Richtungswahl statt Zielhoehe) und neue Linie-GF-Fragereihenfolge
  (Kettenende direkt nach laenge_modus, inkl. vorgezogenem ES). Tests in
  tests/test_vf_spec.lsp/.py angepasst.
- lib/vf_linienzug_grammatik.py: _linie("kettenende_ziel") erwartete
  faelschlich ein hz-REAL nach der Distanz - der reale Bauablauf
  (vfl-body-abschluss) leitet die Richtung dort aus dem Frame ab und
  fragt sie nie. Fuehrte zu einem Token-Desync beim Dekodieren echter
  Kettenende-Journale.
- tests/testdata/hundm05.json (VF_hundm05_LZ_01): 5 ueberfluessige
  hz-Felder entfernt. vsp-linie injiziert bei JEDEM vorhandenen hz-Feld
  einen Richtungs-Token, unabhaengig vom tatsaechlichen Bauablauf - die
  Felder desyncten den Replay und liessen die Kette stumm mit der
  falschen (verkuerzten) Geometrie fertig bauen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Samer Ayadi
2026-09-15 15:13:07 +02:00
co-authored by Claude Sonnet 5
parent 6f12cab86f
commit 4a057862a8
7 changed files with 402 additions and 119 deletions
+46 -14
View File
@@ -60,6 +60,9 @@
("auf-ab" . "3")))
(setq *vsp-tab-endpunkt* '(("motorstation" . "1") ("weiter" . "2")
("kettenende" . "3")))
;; Richtungswahl fuer "auf-ab", wenn die Kette bereits FLACH ankommt
;; (vfl-frage-auf-ab-richtung: "1"=Auf, "2"=Ab) - siehe vsp-vf-einheit.
(setq *vsp-tab-richtung* '(("auf" . "1") ("ab" . "2")))
(setq *vsp-tab-ende3* '(("ja-mit-es" . "1") ("ja-ohne-es" . "2")
("nein" . "3")))
(setq *vsp-tab-ende2* '(("ja-mit-es" . "1") ("nein" . "2")))
@@ -200,22 +203,22 @@
(setq v (ssg-val kopf "spec_id"))
(if v v "(ohne spec_id)"))
;; Segmentlaenge, und NUR beim allerersten Segment der Kette die gesnappte
;; Fahrtrichtung: vfl-in-abstand journalisiert die Richtung nur bei freier
;; Richtungswahl (hz-vorgabe nil), jedes weitere Segment erbt sie vom
;; Vorgaenger. Ein hz an spaeterer Stelle wuerde die ganze Replay-Queue um
;; einen Eintrag verschieben - darum hier ein harter Fehler.
;; Segmentlaenge und optional die gesnappte Fahrtrichtung. Feldpraesenz
;; entscheidet, NICHT *vsp-erster-dl*: die eingefrorene Aufzeichnung
;; (hm_recformat.json) journalisiert die Richtung nur beim allerersten
;; Segment der ganzen Kette, der heutige Ablauf (linienzug_tests.json/
;; -stufe2.json, 2026-09-12) dagegen bei JEDEM Segment neu, auch nach einer
;; Vario-Kurve oder einem neuen Glied. Beim ALLERERSTEN Segment MUSS die
;; Richtung dabeistehen (die Kette hat noch keine geerbte).
(defun vsp-linie (quelle / hz)
(vsp-zahl "DL" (ssg-val quelle "dl") "dl")
(setq hz (ssg-val quelle "hz"))
(cond
(*vsp-erster-dl*
(vsp-zahl "REAL" hz "hz (Pflicht beim ersten Segment der Kette)")
(setq *vsp-erster-dl* nil))
((and *vsp-erster-dl* (null hz))
(vsp-fehler "Feld \"hz\" fehlt (Pflicht beim ersten Segment der Kette)"))
(hz
(vsp-fehler (strcat "Feld \"hz\" ist nur beim ERSTEN Segment der Kette"
" erlaubt - jedes weitere erbt die Fahrtrichtung"
" vom Vorgaenger und hat keinen Journal-Eintrag")))))
(vsp-zahl "REAL" hz "hz")))
(setq *vsp-erster-dl* nil))
;; Reine Richtungswahl OHNE Laenge (vfl-in-richtung: journalisiert NUR REAL,
;; kein DL-Token) - fuer den Kettenanfang-Fall der Zielhoehe-zuerst-Zweige
@@ -234,6 +237,16 @@
(setq v (ssg-val quelle "winkel_idx"))
(if v (vsp-ganz v "winkel_idx")))
;; Stufe-2-Fallback (vfl-frage-gf2-minimum in vf_linienzug.lsp): steht nur im
;; Journal, wenn die am Kettenende automatisch berechnete GF2-Laenge UNTER
;; der ueblichen Mindestlaenge liegt (Stufe 1 - Winkeltabelle - hat dann
;; keinen gueltigen Kandidaten geliefert). "ja" = mit der exakt berechneten
;; (kuerzeren) Laenge bauen, "nein" = auf die Mindestlaenge auffuellen. Faellt
;; die Frage nicht an, kein Journal-Eintrag - wie vsp-winkel-idx.
(defun vsp-gf2-bestaetigung (quelle / v)
(setq v (ssg-val quelle "gf2_exakt_bauen"))
(if v (vsp-raus "STR" (vsp-ja-code v))))
;; Ziel-Hoehe fuer den GF1-Einlauf einer mit "Horizontal-VF" eroeffneten
;; VF-Einheit (vfl-prompt-hoehe-eingang-gf1). Optional, weil die Frage NUR
;; kommt, wenn der Eingang komplett neu gebaut wird - das ist der Fall, den
@@ -470,7 +483,8 @@
(vsp-code-raus *vsp-tab-seite* (ssg-val sub "es_seite") "es_seite")))
(vsp-linie sub)
(vsp-zahl "REAL" (ssg-val sub "hoehe") "hoehe")
(vsp-winkel-idx sub))
(vsp-winkel-idx sub)
(vsp-gf2-bestaetigung sub))
(T
(if letzter
@@ -491,8 +505,26 @@
"variante"))
((= wahl "auf-ab")
(vsp-linie sub)
(vsp-zahl "REAL" (ssg-val sub "hoehe") "hoehe")
(vsp-winkel-idx sub))))))
;; Zwei Auspraegungen, unterschieden per Feldpraesenz (kein
;; separates Modus-Flag noetig - "richtung" IST die Aussage):
;; kommt die Kette schon FLACH an (vfl-frame-flach-p), fragt
;; vf-linienzug-modus NICHT nach einer Zielhoehe, sondern nach der
;; Richtung (Auf/Ab) und waehlt den Winkel aus einer Kandidaten-
;; tabelle (vfl-waehle-winkel-hoehen-tabelle) - winkel_idx dabei
;; wie bei GF-Bogen/Vario-Kurve nur, wenn mehrere Kandidaten
;; gueltig waren. Kommt sie NICHT flach an (die urspruengliche,
;; einzige Auspraegung), wird weiterhin direkt eine Zielhoehe
;; erfragt. Welcher Fall ansteht, haengt am Zustand der Zeichnung
;; und wird hier nicht geraten - die Spec sagt es ueber die
;; Anwesenheit von "richtung".
(if (ssg-val sub "richtung")
(progn
(vsp-code-raus *vsp-tab-richtung* (ssg-val sub "richtung")
"richtung")
(vsp-winkel-idx sub))
(progn
(vsp-zahl "REAL" (ssg-val sub "hoehe") "hoehe")
(vsp-winkel-idx sub))))))))
;; --- Abschluss (vfl-vf-einheit-abschluss) ---
;; "zielpunkt-ohne-es": Kette endet direkt am Zielpunkt, keine Frage.
;; "automatisch": Glied "Linie" bzw. erreichter Zielpunkt - das