refactor: Segment-Antworten als Werte, Modus-Dispatch im Spec-Pfad, Python liest Modus 2/3

Drei herausgeloeste Teile aus doc/todo/spec-modus2-3.md. Der Rest (volle
Spec-Grammatik fuer Modus 2/3, Schritte 3-5 und 7) bleibt bewusst liegen:
Modus-2/3-Journale sind durch ihren SEGS-Eintrag bereits selbst-enthalten
und ohne Zeichnung abspielbar, der Mehrwert einer Spec ist dort also klein.

1. lib/vf_journal_export.py trennt nach Marker. Modus 1 laeuft wie bisher
   durch die Grammatik; "linienzug2"/"linienzug3" werden als Rohtokens
   ausgegeben, im Format, das tests/test_linienzug.lsp einliest. Vorher lief
   die Modus-1-Grammatik auch ueber sie (der startswith-Filter laesst sie
   durch) und brach mit "Token 6: STEP erwartet" ab. Gegen die echte
   Zeichnung geprueft: die Ausgabe ist identisch mit der bisher von Hand
   erzeugten Testdatei.

2. vsp-bau-journal waehlt die Bau-Funktion jetzt anhand des Journals
   (vsp-bau-fn-fuer-journal): kein SEGS = Modus 1, SEGS + Formatmarke
   "m2v2" = Modus 2, SEGS ohne = Modus 3. Bewusst abgeleitet statt als
   zusaetzliches Argument - die Funktion hat zwei Aufrufer, und AutoLISP
   verlangt exakte Argumentzahl. Die Ableitung kann der Datenlage auch nicht
   widersprechen, weil sie genau das Journal liest, das abgespielt wird.

3. Die Segment-Klassifizierung in Modus 2 journalisiert WERTE statt
   Menue-Ziffern: Geradentyp "gf"/"vf", Bogentyp "gf-bogen"/"vario-kurve",
   Variante "aussen"/"innen". Konsole UND Wizard-Dialoge legen dieselben
   Werte ab. Damit deutet ein Umsortieren der Optionen keine gespeicherte
   Kette mehr um - dasselbe Risiko, das beim Lauf-Anfang (833a4b6) real
   wurde. Bestandsketten laufen ueber Alt-Tabellen weiter, ein unbekannter
   Wert bricht mit Segmentnummer ab statt zu raten.

   Statt dreier fast gleicher Normalisierer gibt es einen gemeinsamen
   vfl-wert-norm; vfl-m2-anfang-norm delegiert jetzt dorthin.

   Nebeneffekt: Bogentyp und Variante benutzten BEIDE "1"/"2" und waren im
   Journal nicht zu unterscheiden - deshalb brauchte es die Formatmarke zur
   Erkennung von Alt-Journalen. Mit eigenen Wertevorraeten ist das erledigt;
   ein Test prueft, dass die Mengen disjunkt bleiben.

Geprueft: TEST_UNIT 883/883 (17 neue Tests). TEST_LINIENZUG_MODUS2 beide
Varianten OK - und zwar mit den AUFGEZEICHNETEN Journalen, die bei den
Segment-Fragen noch die alten Ziffern tragen. Genau darum wurden sie nicht
auf die neuen Werte umgeschrieben: sie sind der Nachweis der
Abwaertskompatibilitaet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 12:09:05 +01:00
parent e3c7fb0d25
commit 4a19ac0ae0
6 changed files with 230 additions and 30 deletions
+71 -3
View File
@@ -65,6 +65,11 @@ PREFIX = "[vf_journal_export]"
XDATA_APP = "SSG_VF_EDIT"
MARKER = "linienzug"
# Marker -> Modus-Nummer. "linienzug" (Modus 1) hat ein Frage-Antwort-
# Protokoll (Grammatik in vf_linienzug_grammatik.py); "linienzug2"/"3" werden
# als Rohtokens ausgegeben - fuer sie gibt es bewusst keine Grammatik, ihre
# SEGS-Eintraege machen sie ohnehin selbst-enthalten.
MARKER_MODUS = {"linienzug": 1, "linienzug2": 2, "linienzug3": 3}
# Steuerzeichen-Notation des Eintrags-Trenners im ASCII-DXF (siehe Kopf).
TRENNER = "^J"
@@ -554,6 +559,58 @@ def baue_testfaelle(inserts, strecken, startpunkt_fix, quelle_datei,
return faelle
def baue_tokenfaelle(inserts, quelle_datei):
"""Modus-2/3-Ketten als TOKEN-Records (flache Liste, Kopf + Eintraege).
Warum nicht wie Modus 1 als Frage-Antwort-Protokoll: dafuer braeuchte es
eine Grammatik des Modus-2/3-Fragenflusses, die es (bewusst, siehe
doc/todo/spec-modus2-3.md) nicht gibt. Die Tokens selbst sind aber bereits
aussagekraeftig - vor allem traegt SEGS die komplette Segment-Essenz, womit
die Kette OHNE Zeichnung abspielbar ist.
Ausgabeformat ist genau das, was tests/test_linienzug.lsp
(c:TEST_LINIENZUG_MODUS2) einliest:
{"art": "PT", "x": .., "y": .., "z": ..}
{"art": "REAL", "wert": ..} {"art": "INT", "wert": ..}
{"art": "STR", "wert": ".."} {"art": "SEGS", "wert": ".."}
Die Metadaten-Zeile (beschreibung/erwartung_*) ist bewusst NICHT dabei -
die wird je Testfall von Hand gepflegt und soll beim Neuerzeugen nicht
stillschweigend ueberschrieben werden.
"""
faelle = []
for nr, e in enumerate(sorted(inserts, key=lambda i: i["x"]), start=1):
toks = tokens_aus_journal(e["journal_roh"])
modus = MARKER_MODUS.get(e["marker"], 0)
kopf = {
"test_id": "VF_%s_M%d_%02d" % (TEST_ID_ANLAGE, modus, nr),
"anlagetyp": e["marker"],
"modus": modus,
"anzahl_eingaben": len(toks),
"block": e["block"],
"dxf_handle": e["handle"],
"beschreibung": ("Linienzug Modus %d, %d Journal-Eintraege - "
"Rohtokens, siehe doc/todo/spec-modus2-3.md"
% (modus, len(toks))),
"quelle": ("INSERT %s (Block %s) in %s, XDATA-App %s, Marker %s"
% (e["handle"], e["block"],
os.path.basename(quelle_datei), XDATA_APP,
e["marker"])),
}
faelle.append(kopf)
for art, wert in toks:
if art == "PT":
x, y, z = [float(v) for v in wert.split(",")]
faelle.append({"art": "PT", "x": round(x, RUND),
"y": round(y, RUND), "z": round(z, RUND)})
elif art == "REAL":
faelle.append({"art": "REAL", "wert": float(wert)})
elif art == "INT":
faelle.append({"art": "INT", "wert": int(wert)})
else:
faelle.append({"art": art, "wert": wert})
return faelle
def arrays_einzeilig(text):
"""Rein numerische JSON-Arrays auf eine Zeile ziehen (wie
dxf_scan_components.py) - ssg-parse-json-array (Lisp/ssg_core.lsp) liest
@@ -598,9 +655,20 @@ def main():
print("%s %d verbundene Strecken im Sivas-Export"
% (PREFIX, len(strecken)))
faelle = baue_testfaelle(inserts, strecken,
not args.kein_startpunkt_fix, args.dxf,
args.output)
# Nach Modus trennen: nur Modus 1 hat eine Grammatik. Frueher lief die
# Modus-1-Grammatik auch ueber "linienzug2"/"3" (der startswith-Filter
# laesst sie durch) und brach mit "Token 6: STEP erwartet" ab.
m1 = [e for e in inserts if MARKER_MODUS.get(e["marker"]) == 1]
m23 = [e for e in inserts if MARKER_MODUS.get(e["marker"]) in (2, 3)]
faelle = []
if m1:
faelle.extend(baue_testfaelle(m1, strecken,
not args.kein_startpunkt_fix, args.dxf,
args.output))
if m23:
print("%s %d Modus-2/3-Kette(n) als Rohtokens (keine Grammatik, "
"siehe doc/todo/spec-modus2-3.md)" % (PREFIX, len(m23)))
faelle.extend(baue_tokenfaelle(m23, args.dxf))
koepfe = [f for f in faelle if "test_id" in f]
for f in koepfe:
print("%s %s %s (%s) %d Eingaben"