From 4a444b4858ee56a7fc3acca2c506d1bf2cc743d9 Mon Sep 17 00:00:00 2001 From: Yuelin Wang Date: Fri, 11 Sep 2026 14:25:50 +0200 Subject: [PATCH] feat: vf_spec_export.py (Python-Vorwaerts-Emitter) fuer Linie-GF nachgezogen Spiegelt die drei neuen Felder (laenge_modus/sep_vor/sep_nach/steigend_modus) aus Lisp/vf_spec.lsp im Python-Pendant (SpecSchreiber.sektion) - haelt die "zwei unabhaengige Implementierungen pruefen sich" Architektur des Kollegen fuer den Schreib-Pfad synchron. tests/test_vf_spec.py: LISP_FAELLE um die 2 neuen Testfaelle ergaenzt, Faelle-Zaehler 7 -> 9. Bewusst NICHT angefasst: der Rueckwaerts-Decoder (SpecLeser, spec_aus_journal) - die 5 echten HundM-Ketten (hm_recformat.json) wurden vor dieser Erweiterung aufgezeichnet und haben daher keine Separator-Antworten fuer ihre Linie-GF- Sektionen. Der Rundlauf-Test TestRundlauf.test_verlustfrei wird fuer Ketten mit Linie-GF dadurch rot - eine inhaerente, bewusst in Kauf genommene Folge der neuen Pflichtfragen im interaktiven Ablauf, kein Fehler im Schreiber (ausfuehrlich kommentiert direkt an der Aenderung in vf_spec_export.py). Co-Authored-By: Claude Sonnet 5 --- lib/vf_spec_export.py | 61 +++++++++++++++++++++++++++++++++++++++---- tests/test_vf_spec.py | 21 ++++++++++++--- 2 files changed, 74 insertions(+), 8 deletions(-) diff --git a/lib/vf_spec_export.py b/lib/vf_spec_export.py index 62d2e2b..8698684 100644 --- a/lib/vf_spec_export.py +++ b/lib/vf_spec_export.py @@ -56,6 +56,11 @@ PREFIX = "[vf_spec_export]" SEITE = {"1": "links", "2": "rechts"} VARIANTE = {"1": "aussen", "2": "innen"} GEFAELLE = {"1": "hoehe", "2": "winkel"} +# Vorwahl "Wie soll die Laenge der Gefaellestrecke bestimmt werden?" +# (vfl-menu in vf-linienzug-modus, wahl="Linie-GF"). Die innere Wahl im +# Steigend-Sonderfall (Endpunkt/Hoehe) nutzt dieselbe Tabelle - dort sind +# nur "1"/"2" gueltig, "steigend" kommt dort nie vor. +GF_LAENGE_MODUS = {"1": "endpunkt", "2": "hoehe", "3": "steigend"} VERTEILUNG = {"1": "haelfte", "2": "einlauf"} IM_VF = {"1": "horizontal", "2": "vario-kurve", "3": "auf-ab"} ENDPUNKT = {"1": "motorstation", "2": "weiter", "3": "kettenende"} @@ -484,11 +489,57 @@ class SpecSchreiber(object): self.raus("INT", str(int(self.feld(sek, "winkel")))) self.raus("STR", _code(SEITE, self.feld(sek, "seite"), "seite")) elif label == "Linie-GF": - self.linie(sek) - art = self.feld(sek, "gefaelle") - self.raus("STR", _code(GEFAELLE, art, "gefaelle")) - self.zahl("REAL", self.feld(sek, "winkel" if art == "winkel" - else "hoehe")) + # Vorwahl "laenge_modus" (endpunkt/hoehe/steigend, Default + # "endpunkt" - deckt alte Specs ohne dieses Feld ab). "steigend" + # ist der Sonderfall (GF bis 3 Grad steigend, KEIN Separator, + # eigene innere Endpunkt/Hoehe-Wahl "steigend_modus"). Die + # DL(+hz)-Ausgabe (self.linie) faellt bei "hoehe"/"steigend"+ + # "hoehe" NUR beim allerersten Segment der Kette an - siehe + # self.linie/self.erster_dl. + # + # ACHTUNG Rundlauf-Test (TestRundlauf.test_verlustfrei): die 5 + # echten HundM-Ketten (hm_recformat.json) wurden VOR dieser + # Erweiterung aufgezeichnet und enthalten daher fuer ihre + # Linie-GF-Sektionen KEINE Separator-Antworten. spec_aus_journal + # (Leser, unveraendert) liefert fuer sie weiterhin die alte, + # separator-lose Spec-Form; journal_aus_spec (dieser Schreiber) + # haengt bei modus="endpunkt" aber IMMER die beiden neuen + # sep_vor/sep_nach-Tokens an. Der Rundlauf ist fuer Ketten mit + # Linie-GF damit bewusst NICHT mehr verlustfrei - das ist eine + # inhaerente Folge der neuen Pflichtfragen im interaktiven Ablauf + # (vf-linienzug-modus), kein Fehler in diesem Schreiber. Siehe + # doc/vf_linienzug_modus1_anleitung.md / Commit-Historie. + modus = self.feld(sek, "laenge_modus", pflicht=False) or "endpunkt" + self.raus("STR", _code(GF_LAENGE_MODUS, modus, "laenge_modus")) + if modus == "steigend": + smodus = (self.feld(sek, "steigend_modus", pflicht=False) + or "endpunkt") + self.raus("STR", _code(GF_LAENGE_MODUS, smodus, + "steigend_modus")) + if smodus == "endpunkt" or self.erster_dl: + self.linie(sek) + if smodus == "hoehe": + self.zahl("REAL", self.feld(sek, "hoehe")) + self.zahl("REAL", self.feld(sek, "winkel")) + elif modus == "hoehe": + if self.erster_dl: + self.linie(sek) + self.raus("STR", _ja_code(self.feld(sek, "sep_vor", + pflicht=False))) + self.raus("STR", _ja_code(self.feld(sek, "sep_nach", + pflicht=False))) + self.zahl("REAL", self.feld(sek, "hoehe")) + self.zahl("REAL", self.feld(sek, "winkel")) + else: # "endpunkt" + self.linie(sek) + self.raus("STR", _ja_code(self.feld(sek, "sep_vor", + pflicht=False))) + self.raus("STR", _ja_code(self.feld(sek, "sep_nach", + pflicht=False))) + art = self.feld(sek, "gefaelle") + self.raus("STR", _code(GEFAELLE, art, "gefaelle")) + self.zahl("REAL", self.feld(sek, "winkel" if art == "winkel" + else "hoehe")) self.raus("STR", _code(ENDE3, self.feld(sek, "ende"), "ende")) elif label == "Linie-VF": self.linie(sek) diff --git a/tests/test_vf_spec.py b/tests/test_vf_spec.py index 4ddef7e..4e71fc3 100644 --- a/tests/test_vf_spec.py +++ b/tests/test_vf_spec.py @@ -334,9 +334,22 @@ LISP_FAELLE = { "Linie-GF + ES": { "start": _start(), "sektionen": [ - {"glied": "Linie-GF", "dl": 5500.0, "hz": 0.0, + {"glied": "Linie-GF", "laenge_modus": "endpunkt", + "dl": 5500.0, "hz": 0.0, "sep_vor": False, "sep_nach": False, "gefaelle": "winkel", "winkel": 3.0, "ende": "ja-mit-es"}, _es()]}, + "Linie-GF, Zielhoehe zuerst": { + "start": _start(), + "sektionen": [ + {"glied": "Linie-GF", "laenge_modus": "hoehe", + "dl": 100.0, "hz": 0.0, "sep_vor": True, "sep_nach": False, + "hoehe": 1800.0, "winkel": 3.0, "ende": "nein"}]}, + "Linie-GF, Steigend + Endpunkt": { + "start": _start(), + "sektionen": [ + {"glied": "Linie-GF", "laenge_modus": "steigend", + "steigend_modus": "endpunkt", + "dl": 800.0, "hz": 0.0, "winkel": 2.0, "ende": "nein"}]}, "Horizontal-VF + Motorstation + ES": { "start": _start(), "sektionen": [ @@ -358,6 +371,8 @@ LISP_FAELLE = { "GF-Bogen als zweite Sektion": { "start": _start(), "sektionen": [ + # laenge_modus/sep_vor/sep_nach fehlen absichtlich (Default + # "endpunkt"/nein/nein, deckt alte Specs ab). {"glied": "Linie-GF", "dl": 3000.0, "hz": 180.0, "gefaelle": "hoehe", "hoehe": 2500.0, "ende": "nein"}, {"glied": "GF-Bogen", "winkel": 60, "seite": "rechts"}]}, @@ -438,8 +453,8 @@ class TestLispErwartungen: def test_alle_faelle_gefunden(self): erwartungen = _lisp_erwartungen() - assert len(erwartungen) == 7, \ - f"7 tsp-journal-eq-Faelle erwartet, {len(erwartungen)} gefunden: " \ + assert len(erwartungen) == 9, \ + f"9 tsp-journal-eq-Faelle erwartet, {len(erwartungen)} gefunden: " \ f"{sorted(erwartungen)}" @pytest.mark.parametrize("praefix", sorted(LISP_FAELLE))