02303ad7fc
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>
770 lines
44 KiB
Markdown
770 lines
44 KiB
Markdown
# TODO/Plan: VF-Linienzug ohne GUI/Konsole aufrufbar machen
|
|
|
|
**Stand:** 2026-09-02 · **Status:** Stufe 0 fertig (im CAD noch ungetestet)
|
|
**Herkunft:** Planungslauf (Plan-Modus) vom 2026-09-02, hier eingecheckt zur Nachverfolgung.
|
|
|
|
## Fortschritt
|
|
|
|
| Schritt | Beschreibung | Status |
|
|
|---|---|---|
|
|
| P0 | `vfl-journal-reset` im Dispatcher hochziehen | **erledigt** |
|
|
| P0b | `member`-Shadowing in `vf-linienzug-modus2` | **erledigt** |
|
|
| P1 | `vfl-meldung` statt `alert` in den Bau-Pfaden | **erledigt** |
|
|
| 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-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
|
|
|
|
Neue Symbole in `Lisp/vf_linienzug.lsp` (Abschnitte "TEIL 0a-2" und "TEIL 0a-3"):
|
|
|
|
| Symbol | Zweck |
|
|
|---|---|
|
|
| `*vfl-meldungen*` / `vfl-meldung` | Bau-Meldungen sammeln statt blockierend zu alerten (`alert` nur bei `ssg-gui-p`, sonst `princ`) |
|
|
| `*vfl-headless*` / `vfl-headless-p` | Schalter "keine Live-Eingabe erlaubt" |
|
|
| `vfl-headless-abbruch` / `-notausgang` / `-ort` | harter Abbruch mit Fundstelle bzw. Hook-Variante |
|
|
| `*vfl-headless-fehler*` | Diagnose-Alist (`was`/`glied`/`eingabe`/`ort`) |
|
|
| `*vfl-headless-antwort-fn*` | optionaler Hook fuer nicht vorhersagbare Rueckfragen |
|
|
|
|
Eingebaut an den vier Eingabe-Engstellen (`vfl-in-value`, `vfl-in-value-p`,
|
|
`vfl-in-selection` 2x, `vfl-in-abstand` 2x). `vfl-journal-reset` loescht
|
|
Meldungen und Diagnose mit; `vfl-view-refresh` ueberspringt headless
|
|
`_PLAN`/`_ZOOM`. `tests/test_hundm05.lsp` schaltet `*vfl-headless*` ein
|
|
(Save/Restore in `hundm05:stubs-an`/`-aus`), zaehlt erreichte Live-Eingaben
|
|
(`*hundm05-prompts*`) und schreibt `prompts`, `headless_fehler`, `meldungen`
|
|
ins Ergebnis-JSON; `tests/test_hundm05.py` prueft alle drei.
|
|
|
|
Bereits abgesichert, ohne CAD lauffaehig: `tests/test_vf_headless_statisch.py`
|
|
(10 Tests) haelt das Design gegen Verrottung - eingechecktes Inventar aller
|
|
`alert`/`get*`/`ssget`/`new_dialog`-Fundstellen je Funktion (ein neues
|
|
`getreal` in einer Bau-Funktion faellt auf, auch wenn sein Zweig im Test nie
|
|
erreicht wird), Praesenz des Riegels in allen vier Wrappern, Diagnose-Reset
|
|
in `vfl-journal-reset` und die P0-Reihenfolge im Dispatcher. Der Waechter
|
|
wurde gegengeprobt: ein injizierter `alert` im Bau-Pfad wird erkannt, ein
|
|
`alert` in einem Kommentar nicht.
|
|
|
|
**CAD-Lauf 1 (2026-09-03)**: die erste Kette wurde headless gebaut
|
|
(">>> VF chain inserted! <<<", kein Prompt, kein Dialog) - der Riegel und der
|
|
Replay funktionieren also. Danach brach der Treiber ab:
|
|
|
|
```
|
|
[TEST_HUNDM05] FEHLER in der Kettenschleife:
|
|
no function definition <ENTPREV>
|
|
```
|
|
|
|
`entprev` gibt es in AutoLISP nicht (nur `entnext` und `entlast`) - mein
|
|
Fehler in `hundm05:last-insert-prefix`, der Suche nach dem fertigen
|
|
`VF_`-Block. Erreicht wurde sie erst, weil `entlast` nach dem Bau nicht der
|
|
Block ist (danach entstehen noch Beschriftungstexte). Behoben:
|
|
|
|
- Suche laeuft **vorwaerts** (`hundm05:suche-vorwaerts`, letzter Treffer
|
|
gewinnt), und zwar ab dem Zeichnungsstand VOR dem Bau (`vor-ent`, in
|
|
`hundm05:build-linienzug` gemerkt). Das ist schnell und kann nicht den
|
|
Block eines FRUEHEREN Baus liefern - deshalb dort bewusst kein
|
|
`entlast`-Kurzschluss.
|
|
- **Dieselbe Falle in `tests/test_mubea.lsp`** (`mubea:last-insert-prefix`)
|
|
mitbehoben. Sie schlug dort nie zu, weil `entlast` bisher immer sofort
|
|
traf; `entprev` war nur der nie erreichte Schleifenschritt.
|
|
- Die Kettenschleife fangt jetzt **je Kette** (`vl-catch-all-apply` um
|
|
`hundm05:build-linienzug`): ein Fehler NACH dem Bau nimmt nicht mehr die
|
|
restlichen vier Ketten mit ("0 OK, 0 Fehler" ohne jede Auskunft).
|
|
- Neuer Waechter `test_keine_nicht_existierenden_funktionen` in
|
|
`tests/test_vf_headless_statisch.py` prueft alle `.lsp` in `Lisp/` und
|
|
`tests/` gegen eine Liste erfundener Funktionen (`entprev` u.a.) - genau
|
|
diese Fehlerklasse (falscher Name in einem selten erreichten Zweig) kostet
|
|
sonst jedes Mal einen CAD-Lauf. Gegengeprobt: ein wieder eingebautes
|
|
`entprev` wird gefunden, das Wort im Kommentar nicht.
|
|
- `vfl-meldung` verkraftet jetzt `txt` = nil (fehlender i18n-Schluessel
|
|
wuerde `strcat` genau dann abbrechen, wenn schon etwas schiefging).
|
|
|
|
**Noch offen** (braucht BricsCAD): `TEST_HUNDM05` muss 5x
|
|
`"status": "executed"` mit `prompts` 0 liefern, und eine interaktive
|
|
Gegenprobe (`FOERDERANLAGE` -> Linienzug Modus 1, Doppelklick-Edit,
|
|
`SSG_DIM_SWITCH`) muss unveraendert aussehen. Erst danach kann in
|
|
`tests/alltests.json` das `"disabled": true` der hundm05-Zeile weg.
|
|
|
|
**Bewusst nicht umgestellt**: `vfl-konvertiere-ent` (Batch-2D/3D) laeuft
|
|
weiter ohne `*vfl-headless*` - eine Live-Rueckfrage ist dort bisher gewolltes
|
|
Verhalten. Kandidat fuer Stufe 1, wenn der Ergebnis-Record steht.
|
|
|
|
## Kontext
|
|
|
|
`VF_n`-Ketten entstehen heute nur interaktiv: `vf-linienzug-modus` (Modus 1,
|
|
freies Zeichnen), `vf-linienzug-modus2` (Modus 2, Pfad + Zielhoehe) und
|
|
`vf-linienzug-modus3` (Modus 3, Vorwaerts-Nachbau, Legacy) fragen jeden Wert
|
|
einzeln per `getpoint`/`getstring`/`getint`/`getreal` bzw. per DCL-Wizard ab.
|
|
`tests/testdata/object_data.md` Abschnitt 3.3 fuehrt den Linienzug darum als
|
|
"nicht scriptbar".
|
|
|
|
Ziel: eine Kette soll **aus einem Stapel Eingabedaten plus einer Handvoll
|
|
LISP-Funktionen** gebaut werden koennen - ohne Dialog, ohne Konsolenfrage.
|
|
Damit werden Regressionstests (siehe `tests/test_hundm05.lsp`, 5 echte Ketten
|
|
aus `data/polylines.dxf`), Batch-Nachbauten und der 2D/3D-Umbau reproduzierbar,
|
|
und die Bau-Logik wird pro Abschnitt einzeln aufrufbar.
|
|
|
|
Entscheidungen des Auftraggebers (fix):
|
|
- **Zweistufig**: erst Daten-API (Spec -> Journal -> Replay), dann echte
|
|
Trennung der Misch-Funktionen in Frage-Schale + reinen Bauteil.
|
|
- **Modus 1 + 2 vollstaendig**, Modus 3 nur duenn gewrappt (ist als "wird
|
|
umgebaut" markiert).
|
|
- **Eingabeformat**: benannte LISP-Spec (Alist je Abschnitt) + JSON-Loader.
|
|
- **Fehlerverhalten**: fail fast mit Ergebnis-Record, nichts raten, kein Dialog.
|
|
|
|
Zeilennummern in diesem Dokument beziehen sich auf den Stand bei der Planung
|
|
(`Lisp/vf_linienzug.lsp`, `Lisp/vf_core.lsp`) und verschieben sich mit jeder
|
|
Aenderung - im Zweifel per Funktionsname suchen.
|
|
|
|
---
|
|
|
|
## Teil 1 - Befund: was ist heute schon headless moeglich?
|
|
|
|
| Modus | Headless heute? | Mechanismus | Blocker |
|
|
|---|---|---|---|
|
|
| 1 (frei zeichnend, `vf-linienzug-modus` :3211) | **Ja, in der Praxis** | Journal-Replay: `vfl-journal-replay-start` :1545 fuellt `*vfl-replay-queue*`, alle `vfl-in-*`-Wrapper lesen zuerst daraus. Produktiv genutzt von `vfl-edit-ent` :4322, `vfl-edit-glied` :4190, `vfl-konvertiere-ent` :4292 und von `tests/test_hundm05.lsp` | Alerts nicht abschaltbar (:3523, :3550, :3559, :3650, :3772); **leere Queue faellt still auf Live-Eingabe zurueck** (:1186-1191); Daten muessen in exakter Frage-Reihenfolge vorliegen |
|
|
| 2 (Pfad + Zielhoehe, `vf-linienzug-modus2` :5278) | **Ja, gleicher Mechanismus** | Marker `"linienzug2"`, Replay-Einstieg `vfl-edit-ent2` :4397 | Pfad-Objekte (LINE/ARC) muessen in der Zeichnung liegen - das Journal speichert nur Handles (`OBJS`, `handent` :1060); **keine STEP-Marker** -> kein Sektions-Edit; Dispatcher `vf_core.lsp:1139` ruft `vfl-journal-reset` NICHT (Journal des Vorlaufs wird als Praefix mitgeschrieben); Alerts :5418, :5650; lokale Variable `member` :5289/:5496 verdeckt die Builtin-Nutzung :5686/:5759 |
|
|
| 3 (Vorwaerts-Nachbau, `vf-linienzug-modus3` :4806) | **Nein** | Alle Eingaben laufen zwar durch die `vfl-in-*`-Wrapper (im Prinzip replaybar), aber: kein `vfl-journal-reset`, **kein** `vfl-journal-xdata-schreiben`, kein Marker, `*error*`-Hook wickelt keine Teil-Geometrie (:4835-4844) | bewusst so, Kommentar :4825-4827; fehlt: Reset + XDATA-Schreiben + Marker + Edit-Zweig |
|
|
|
|
Modus-uebergreifende Blocker:
|
|
- **40 `alert`-Aufrufe** im Cluster, keiner durch `ssg-gui-p` gesichert; im
|
|
Bau-Pfad erreichbar: :3523/:3550/:3559/:3650/:3772 (M1), :5418/:5650 (M2),
|
|
:4901/:4921/:5029 (M3), :1067 (Replay: Handles fehlen), :123 (GF-Modul
|
|
fehlt). Mehrere mit anschliessendem `(exit)`.
|
|
- **`(ssg-gui-aus)` deckt nur zwei Stellen ab**: `vf_core.lsp:632`
|
|
(`vf-frage-element-winkel`) und `vf_linienzug.lsp:447` (`vfl-wizard-mode-p`
|
|
-> alle `vflw-*`-Dialoge). Die Nicht-Wizard-DCLs (`variofoerderer_basis`,
|
|
`variofoerderer_winkel`, `gefaellestrecke`, `vfl_edit_modus`, `vfl_edit` und
|
|
die drei wiederverwendeten `vflw_*`-Edit-Dialoge :3962/:4000/:4044) bleiben
|
|
modal.
|
|
- **Der Dispatcher selbst fragt**: `c:VarioFoerderer` (`vf_core.lsp:1073`) holt
|
|
Anlagentyp (`getint` :1108) und Untermodus (`getint` :1130) roh ab. Ein
|
|
headless Aufruf muss darum immer direkt `vf-linienzug-modus*` aufrufen.
|
|
- Rohe Eingaben ohne Journal in Nebenpfaden: `vfl-praeambel-erfragen` :4445,
|
|
`c:VF_SEKTION_RESTORE` :4496, `c:VFL_KS_DIAG` :4622, `c:Vario_Kette_Merge`
|
|
:6347.
|
|
- **`ATTREQ`/`ATTDIA`**: ohne `0` fragt das `(command "_.INSERT" ...)` in den
|
|
Einfuegeprimitiven nach Attributwerten - der wahrscheinlichste Haenger.
|
|
|
|
Namensfalle: die `vfl3-*`-Helfer (:5093-5276) gehoeren zu **Modus 2**, die
|
|
`vfl2-*`-Helfer (:4703-4804) zu **Modus 3**.
|
|
|
|
---
|
|
|
|
## Teil 2 - Invarianten
|
|
|
|
| # | Invariante | Sicherung |
|
|
|---|---|---|
|
|
| I1 | Jeder fertige `VF_n`-Block traegt ein gueltiges `SSG_VF_EDIT`-Journal (Marker `"linienzug"`/`"linienzug2"`) und bleibt per Doppelklick editier- und 2D/3D-konvertierbar | Stufe 1 baut ueber die unveraenderte Modus-Funktion (schreibt :3867 selbst). Stufe 2: **kein Bauteil beruehrt das Journal** - im Frage-Pfad erzeugen es die `vfl-in-*`-Wrapper, im Daten-Pfad wird es einmal aus der Spec erzeugt und vor dem Bau gesetzt |
|
|
| I2 | Kein Prompt, kein Dialog | GUI aus + Wizard aus + zaehlende Eingabe-Stubs + `*vfl-headless*` (harter Fehler statt Frage) |
|
|
| I3 | Nichts geraten | Leere Queue = Fehler mit Fundstelle; jede abgewiesene Sektion erzeugt eine gesammelte Meldung; Spec-Validierung bricht vor dem ersten Insert ab |
|
|
| I4 | Interaktives Verhalten unveraendert | Jede Produktionsaenderung ist ein No-Op, solange `*ssg-gui-aus*` und `*vfl-headless*` nil sind |
|
|
|
|
---
|
|
|
|
## Teil 3 - Stufe 0: Vorarbeiten in der Produktion
|
|
|
|
Fuenf kleine Aenderungen, alle No-Ops im interaktiven Betrieb. Reihenfolge =
|
|
Empfehlung; P0 und P1 sind eigenstaendige Bugfixes und koennen sofort raus.
|
|
|
|
**P0 - `vfl-journal-reset` hochziehen** (`Lisp/vf_core.lsp:1139`). Heute nur im
|
|
Modus-1-Zweig. Folge: ein frischer Modus-2-Lauf erbt das Journal des
|
|
Vorlaufs und schreibt es in die XDATA - ein spaeterer Doppelklick spielt
|
|
Muell vor. Reset vor die `cond` ziehen. Reiner Bugfix (die Edit-Pfade laufen
|
|
nie hier durch), behebt zugleich ein veraltetes `*vfl-seg-glied-letzte*`.
|
|
|
|
**P0b - `member`-Shadowing** in `vf-linienzug-modus2`: lokale Variable
|
|
`member` (:5289, gesetzt :5496) gegen Builtin-Nutzung (:5686, :5759) im
|
|
selben Scope. Lokale umbenennen (z.B. `vf-run-member`).
|
|
|
|
**P1 - `vfl-meldung` statt `alert` in den Bau-Pfaden.** Neuer Helfer neben
|
|
`vfl-journal-record` (~:170): Text in `*vfl-meldungen*` sammeln, `dbgmsg`
|
|
schreiben, dann `alert` (GUI an) **oder** `princ` (GUI aus). `alert` ->
|
|
`vfl-meldung` an genau diesen Stellen: :123, :1067, :3523, :3550, :3559,
|
|
:3650, :3772, :5418, :5650, :4901, :4921, :5029. Die reinen
|
|
Interaktiv-Alerts (:3968, :4006, :4050, :4266, :4325, :4566, :4593) bleiben.
|
|
Ohne P1 blockiert ein Alert den Lauf und der Text - die einzige Auskunft,
|
|
*welche* Sektion abgewiesen wurde - ist verloren.
|
|
|
|
**P2 - `*vfl-headless*`: leere Queue wird zum Fehler.** Neuer Notausgang
|
|
`vfl-headless-notausgang (was)` neben `vfl-replay-pop` (~:210): setzt
|
|
`*vfl-headless-fehler*` samt Glied-Index und Eingabe-Nummer (aus
|
|
`vfl-steps-zaehlen` :1612 und `(length *vfl-journal*)`), meldet und bricht ab.
|
|
Optionaler Hook `*vfl-headless-antwort-fn*` darf einen Wert liefern (wird
|
|
journalisiert und als Warnung protokolliert). Vier Einbaustellen, je ein
|
|
neuer `cond`-Zweig vor der Live-Eingabe:
|
|
- `vfl-in-value` :1185 und `vfl-in-value-p` :1196 - deckt `vfl-in-point`,
|
|
`-string`, `-real`, `-int`, `vfl-menu`, `vfl-menu-int`, `vfl-menu-winkel`
|
|
und die AS/ES-Winkelfrage ab (rund 90 % aller Eingaben)
|
|
- `vfl-in-selection` :1055 (vor dem `ssget` :1069) **und** der
|
|
`fehlt > 0`-Zweig :1066: unter headless abbrechen statt mit einer
|
|
Teilauswahl weiterzubauen (aendert sonst still die Modus-2-Fragenzahl)
|
|
- `vfl-in-abstand` :2113 (eigene Replay-Logik)
|
|
|
|
**P3 - Kleinkram**: `*vfl-meldungen*` in `vfl-journal-reset` :157 mitloeschen;
|
|
`vfl-view-refresh` :2028 unter headless ueberspringen (`_.PLAN`/`_.ZOOM` vor
|
|
*jeder* Laengeneingabe, im Batch nur Kosten).
|
|
|
|
---
|
|
|
|
## Teil 4 - Stufe 1: Daten-API (Spec -> Journal -> Replay)
|
|
|
|
Neue Datei **`Lisp/vf_spec.lsp`**, geladen aus `vf_core.lsp` direkt nach
|
|
`vf_linienzug.lsp` (:1062) - deckt beide Ladewege ab (MNL ueber
|
|
`VarioFoerderer.lsp` und `Lisp/ssg_load.lsp`), also **keine** Aenderung an
|
|
`menu/SSG_LIB.mnl`. Guarded Nachlade-`load` am Kopf fuers isolierte Testen
|
|
(Muster `ssg_ks_insert` in `vf_core.lsp:66`).
|
|
|
|
### 4.1 Spec-Form
|
|
|
|
```lisp
|
|
'(("version" . 1) ("modus" . 1) ("name" . "VF_LZ_01") ("dim" . "3D")
|
|
("start" ("punkt" 4912.0 1231.0 2100.0) ("hoehe" . 2154.0)
|
|
("as" . T) ("as-winkel" . "90") ("as-seite" . "links"))
|
|
("sektionen"
|
|
(("glied" . "Linie-GF") ("dl" . 5500.0) ("hz" . 0.0)
|
|
("gefaelle" . "winkel") ("winkel" . 3.0) ("ende" . "nein"))
|
|
(("glied" . "Horizontal-VF") ("dl" . 4566.7)
|
|
("gf-verteilung" . "einlauf") ("sep-vor" . nil) ("sep-nach" . nil)
|
|
("weiter" (("sub" . "vario-kurve") ("winkel" . 90)
|
|
("seite" . "links") ("variante" . "aussen"))
|
|
(("sub" . "horizontal") ("dl" . 250.0))
|
|
(("sub" . "kettenende") ("es" . T) ("dl" . 6215.0)
|
|
("hoehe" . 4500.0)))
|
|
("ende" ("es-winkel" . "90") ("es-seite" . "rechts")))))
|
|
```
|
|
|
|
Durchgaengig **Domaenenwerte** (`"links"`, `"winkel"`, `"aussen"`, `T`/`nil`),
|
|
keine Menue-Codes; rohe Codes (`"1"`/`"2"`) werden als Durchreiche fuer
|
|
zurueckgelesene Journale akzeptiert. Glied-Typen: `GF-Bogen`, `Linie-GF`,
|
|
`Linie-VF`, `Horizontal-VF`, `Linie`, plus das automatische `ES`-Glied.
|
|
|
|
Die variable VF-Einheit (heute bewusst **nicht** im Schema, Kommentar :260-292)
|
|
wird als `("weiter" ...)`-Liste modelliert, immer terminiert mit `motorstation`
|
|
oder `kettenende`; Sub-Typen `horizontal`, `vario-kurve`, `auf-ab`.
|
|
|
|
**Drei Fallen, die der Uebersetzer besitzen muss:**
|
|
1. **Menue-Code haengt am Frame**: nur die *erste* Sektion laeuft ohne Frame
|
|
(4 Optionen :3396), alle spaeteren mit (5 Optionen :3378). Also
|
|
`Linie-GF` = `"1"` in Sektion 1, `"2"` ab Sektion 2 usw.; `GF-Bogen` in
|
|
Sektion 1 = Validierungsfehler (nicht angeboten).
|
|
2. **`hz` genau einmal**: `vfl-in-abstand` journalisiert die Richtung nur bei
|
|
`hz-vorgabe = nil` (:2130) - das ist ausschliesslich das erste Segment der
|
|
Kette. `hz` an spaeterer Stelle verschiebt die ganze Queue -> harter
|
|
Fehler; `hz` fehlt in Sektion 1 -> harter Fehler.
|
|
3. **`gf-verteilung` ist zwischen den Modi invertiert**: Modus 1 :3118
|
|
(`"1"` = haelfte), Modus 2 :5729 (`"2"` = haelfte). Code-Tabelle pro Modus.
|
|
|
|
Nicht vorhersagbar bleibt die **Winkelwahl** (`vfl-waehle-winkel` :1656 fragt
|
|
nur, wenn mehrere Kandidaten gueltig sind - abhaengig von der real gemessenen
|
|
Restlaenge). Loesung: optionales Feld `("winkel-idx" . n)`; fehlt es und der
|
|
Lauf braucht es, greift P2 mit einer Fehlermeldung, die Sektion und
|
|
Eingabe-Nummer nennt. Kein Raten (I3).
|
|
|
|
### 4.2 Uebersetzer
|
|
|
|
`vfl-spec-pruefen` (nur Fehlerliste, fragt und baut nie) und
|
|
`vfl-spec->journal` / `-m2` / `-m3` (Vorwaerts-Journal). Rueckgabe-Alist
|
|
`(("journal" . ...) ("fehler" . ...) ("warnungen" . ...))`.
|
|
|
|
Wiederverwendung statt Neubau - diese Bausteine existieren schon:
|
|
`vfl-schema-slice-bauen` :326, `vfl-schema-feld-pruefen` :303 (mit den
|
|
erlaubten Werten aus `*vfl-glied-schema*` :243, `*vfk-gf-bogen-winkel*`,
|
|
`*vfk-as-es-winkel*`), `vfl-gf-bogen-slice-bauen` :3941, `vfl-es-slice-bauen`
|
|
:4093, `vfl-vario-slice-bauen` :4110, `vfl-journal-list->string` :1258.
|
|
Neu sind nur `Linie-GF`/`Linie-VF`/`Horizontal-VF`/`Linie` und der VF-Fluss.
|
|
|
|
Wichtig: `vfl-schema-slice-bauen` loggt Schemaverstoesse und schreibt den Wert
|
|
**trotzdem** ("Sicherung geht vor", :324) - richtig fuer den Recovery-Pfad,
|
|
falsch hier. Darum prueft `vfl-spec-*` mit `vfl-schema-feld-pruefen` selbst
|
|
vorab und ruft die Slice-Bauer erst mit sauberen Werten. `vfl-schema-slice-bauen`
|
|
bleibt unangetastet.
|
|
|
|
Emitter-Regel fuer den VF-Fluss (macht den Uebersetzer trivial): die
|
|
`ist-Endpunkt`-Antwort wird zwar an zwei Stellen gestellt (Schleifenkopf :2560
|
|
und innerhalb `vfl-baue-horizontal-koerper` im Ziel-Modus :2308, von dort als
|
|
`vor-antwort` zurueckgegeben), landet in der flachen Queue aber **immer an
|
|
derselben linearen Position** - direkt nach den Werten des Vorgaenger-Knotens.
|
|
Also je Knoten: `STR ende-code` (`"1"` Motor / `"3"` Kettenende / sonst `"2"`),
|
|
bei `"2"` zusaetzlich `STR naechstes-vf` plus die Nutzlast des Sub-Typs.
|
|
|
|
### 4.3 Headless-Rahmen und Ergebnis-Record
|
|
|
|
```lisp
|
|
(vfl-headless-an opt) / (vfl-headless-aus) ; Save/Restore, MUSS immer laufen
|
|
(vfl-bau-aus-spec spec opt) ; -> Ergebnis-Record
|
|
(vfl-bau-aus-spec-liste specs opt) / (vfl-bau-aus-json datei opt)
|
|
(vfl-result->json rec) / (vfl-results-schreiben recs datei)
|
|
(c:VF_SPEC_BAU) ; Datei aus DXFM_VF_SPEC, fragt nichts
|
|
```
|
|
|
|
`vfl-headless-an`: `*vfl-headless*` T, `*vfl-wizard-mode*` nil, `(ssg-gui-aus)`,
|
|
zaehlende Stubs fuer `vfl-getpoint`/`getstring`/`getint`/`getreal`/`getkword`/
|
|
`alert`/`new_dialog` (jeder erhoeht `*vfl-spec-prompts*` und liefert nil),
|
|
`ssg-start` mit `OSMODE`/`ATTREQ`/`ATTDIA` = 0, `init-bibliothek` falls noetig.
|
|
**`ssget`/`entsel` bewusst NICHT stubben** - `vf-next-number`
|
|
(`vf_core.lsp:322`) braucht `(ssget "X" ...)`; die eine Bau-Pfad-`ssget` sitzt
|
|
in `vfl-in-selection` und ist ueber P2 abgedeckt.
|
|
|
|
Pro Kette: validieren (Fehler -> Status `"spec-fehler"`, nichts gebaut) ->
|
|
`*ssg-ils-dim*` **je Kette** neu setzen (der Abbruch-Handler :3286 nullt es) ->
|
|
`lastEnt` merken -> `vfl-journal-reset` + `vfl-journal-replay-start` ->
|
|
`*error*` sichern, `(vl-catch-all-apply 'vf-linienzug-modus '())`, `*error*`
|
|
zurueck (genau die Klammer aus `hundm05:build-linienzug`) -> Record aus den
|
|
Globals sammeln -> aufraeumen.
|
|
|
|
Bewusster Kompromiss (wie beim Batch-Konverter `vfl-konvertiere-ent` :4292):
|
|
weil gefangen wird, laeuft der `*error*`-Handler der Modus-Funktion nicht, die
|
|
Teil-Geometrie wird also **nicht** zu einem Block gewickelt. Option
|
|
`("bei-fehler" . "behalten"|"loeschen")` entscheidet, ob sie stehenbleibt
|
|
(per `c:VF_SEKTION_RESTORE` rettbar, weil `vfl-segment-xdata-sichern` :3855
|
|
pro Iteration schon `SSG_VF_EDIT_SEG`/`_PRE` geschrieben hat) oder ab
|
|
`lastEnt` weggeraeumt wird.
|
|
|
|
Ergebnis-Record: Feldreihenfolge von `hundm05:result-json` beibehalten
|
|
(`test_id`, `kind`, `status`, `dimension`, `block_name`, `block_handle`,
|
|
`insert_point`, `actual_attributes`), dazu `eingaben_gesamt`,
|
|
`eingaben_offen`, `sektionen_soll`/`sektionen_ist`, `glieder`, `prompts`,
|
|
`warnungen`, `fehler_sektion`, `fehler_text`, `ziel_abweichung`, `journal`.
|
|
Status: `executed` | `warnung` | `desync` | `abbruch` | `spec-fehler`.
|
|
|
|
**Drei unabhaengige Desync-Detektoren** (eine geometrisch abgewiesene Sektion
|
|
wirft keinen Fehler, sie verbraucht nur weniger Eintraege): offene
|
|
Queue-Werte, Vergleich der erzeugten STEP-Labels (`*vfl-journal*`) gegen die
|
|
Glied-Folge der Spec (nennt die erste abweichende Sektion), und die
|
|
gesammelten Meldungen aus P1.
|
|
|
|
### 4.4 Modus 2 und Modus 3
|
|
|
|
Modus 2: Spec traegt `("pfad-handles" ...)` (Entity-Handles der LINE/ARC-Trasse)
|
|
plus `("segmente" ...)` in Pfadreihenfolge. Vor dem Bau wird die Segmentzahl
|
|
gegen die aufgeloesten Handles geprueft und bei Fehlbestand abgebrochen - die
|
|
Fragenzahl kommt aus `gf-sortiere-objekte`/`gf-analysiere-kette` auf der
|
|
*realen* Geometrie. Eine Gerade direkt nach einer Vario-Kurve wird nicht
|
|
gefragt (:5449); der Uebersetzer leitet das selbst ab.
|
|
|
|
Modus 3: nur `vfl-bau-aus-spec` mit Modus-3-Uebersetzer. **Dokumentierte
|
|
Einschraenkung**: I1 gilt nicht (kein Journal-XDATA, also kein Doppelklick-Edit,
|
|
keine 2D/3D-Konvertierung). Die volle Nachruestung braeuchte vier gekoppelte
|
|
Eingriffe (Reset, XDATA-Schreiben, Marker `"linienzug3"` in der Whitelist
|
|
:1340, `vfl-edit-ent3`) in dem Modus, der laut Code ohnehin ersetzt wird -
|
|
darum bewusst zurueckgestellt.
|
|
|
|
### 4.5 JSON-Form
|
|
|
|
`ssg-load-json`/`ssg-parse-json-array` (`ssg_core.lsp:1146/1167`) liest
|
|
**zeilenweise und flach**: jedes `{` beginnt ein Objekt, Verschachtelung ist
|
|
unmoeglich, Zahlen-Arrays muessen in einer Zeile stehen. Also dieselbe Form wie
|
|
`tests/testdata/hundm05.json`: ein Kopf-Objekt (`spec_id`) plus flache
|
|
Sektions-Objekte (`glied`), Sub-Objekte (`sub`) und Abschluss (`ende`),
|
|
gruppiert wie `hundm05:gruppiere`. Wahrheitswerte als `1`/`0`
|
|
(`ssg-cfg-parse-value` kennt kein `true`/`false`). Ein `nr`-Feld pro Sektion
|
|
macht handgeschriebene Dateien selbstpruefend.
|
|
|
|
### 4.6 Umsetzungsstand Stufe 1 (2026-09-03)
|
|
|
|
**Fertig fuer Modus 1.** Was entstanden ist:
|
|
|
|
| Datei | Inhalt |
|
|
|---|---|
|
|
| `Lisp/vf_spec.lsp` | Code-Tabellen, Emitter `vsp-journal`, Validierung `vsp-pruefen`, flacher JSON-Loader `vsp-json-laden`/`vsp-gruppieren`, Headless-Rahmen `vsp-headless-an`/`-aus`, Ergebnis-Record, `vsp-bau-aus-spec`/`-liste`, `c:VF_SPEC_BAU` |
|
|
| `lib/vf_spec_export.py` | derselbe Emitter in Python PLUS die Umkehrung (`spec_aus_journal`), erzeugt die Spec-Testdaten |
|
|
| `tests/testdata/vf_spec_hundm05.json` | die 5 echten Ketten als Spec (57 flache Objekte), **erzeugt** statt handgeschrieben |
|
|
| `tests/test_vf_spec.lsp` | `C:TEST_VF_SPEC`: 7 handgebaute Faelle je Glied-Typ, 7 Validierungsfaelle, Gruppieren, und die 5 echten Ketten gegen ihr aufgezeichnetes Journal |
|
|
| `tests/test_vf_spec.py` | 27 Tests ohne CAD + 10 fuer die `VF_SPEC_BAU`-Ergebnisse |
|
|
|
|
Abweichungen von der Planung, jeweils mit Grund:
|
|
|
|
1. **`vsp-pruefen` ruft den Emitter** statt eine zweite Regelmenge zu
|
|
pflegen. Zwei getrennte Regelwerke laufen auseinander; der Emitter
|
|
validiert ohnehin jedes Feld, das er anfasst.
|
|
2. **Das vollstaendige Journal steht NICHT im Ergebnis-Record.** Es ist schon
|
|
in der Spec und in der XDATA des gebauten Blocks - eine dritte Kopie waere
|
|
nur Ballast. Statt dessen `glieder_soll` und `glieder_ist`: das ist der
|
|
Desync-Detektor, der auch dann greift, wenn die Queue zufaellig aufgeht.
|
|
3. **Zusatzfeld `segment_typ`** am Glied `Linie`: dort waehlt
|
|
`vfl-segment-entscheidung` selbst zwischen reiner Gefaellestrecke und
|
|
VF-Einheit. Am Journal ist es erkennbar (VF-Einheit beginnt mit der
|
|
GF-Verteilungsfrage), aus der Spec allein nicht - und Geometrie zu raten
|
|
verstoesst gegen I3.
|
|
4. **`vf_ende`-Werte `automatisch` und `zielpunkt-ohne-es`** neben
|
|
`ja-mit-es`/`nein`: in beiden Faellen stellt `vfl-vf-einheit-abschluss`
|
|
KEINE Frage (Glied `Linie` bzw. Kettenende am Zielpunkt ohne ES-Wunsch).
|
|
Ohne diese Werte waere die Tokenzahl nicht bestimmbar.
|
|
5. **Praefix `vsp-`**, nicht `vfl-`: `vfs-` ist schon von `vf_standard.lsp`
|
|
belegt, und die Spec-Ebene ist bewusst von `vf_linienzug.lsp` getrennt
|
|
(einseitige Abhaengigkeit).
|
|
|
|
**Wie die Richtigkeit belegt ist** - drei Implementierungen derselben
|
|
Grammatik, die einander pruefen:
|
|
|
|
- **Rundlauf gegen echte Daten**: fuer alle 5 HundM-Ketten gilt
|
|
`journal_aus_spec(spec_aus_journal(tok)) == tok`, tokenweise numerisch
|
|
verglichen. Damit ist bewiesen, dass die Spec das Journal verlustfrei
|
|
abbildet - sonst waere ein spec-gebauter Nachbau eine andere Kette.
|
|
- **Der Weg, den LISP nimmt**, ist separat geprueft: flaches JSON -> Spec ->
|
|
Journal ergibt dieselben Journale wie das aufgezeichnete Protokoll.
|
|
- **Der unabhaengige Dekoder** (`lib/vf_journal_export.py`, gegen die echten
|
|
Ketten verifiziert) verdaut alle 7 synthetischen Emitter-Ausgaben restlos -
|
|
auch die zwei Zweige, die in den echten Daten fehlen (`Linie-VF`,
|
|
gewinkelter Erstkoerper).
|
|
- **Die Erwartungswerte im LISP-Test sind nicht von Hand abgeleitet**: sie
|
|
kommen aus dem geprueften Python-Emitter, und
|
|
`TestLispErwartungen` in `tests/test_vf_spec.py` liest sie aus
|
|
`tests/test_vf_spec.lsp` zurueck und vergleicht sie erneut. Sonst koennte
|
|
ein Uebertragungsfehler einen falschen Erwartungswert festschreiben und der
|
|
CAD-Lauf waere gruen, obwohl der Emitter falsch liegt. Gegengeprobt: eine
|
|
verfaelschte Zahl im LISP-Test wird erkannt.
|
|
|
|
**CAD-Lauf `VF_SPEC_BAU` (2026-09-03): 5 OK, 0 Fehler.** Alle 5 Ketten aus
|
|
der Spec gebaut, je Kette `status: executed`, `prompts: 0` (keine Live-Eingabe
|
|
erreicht - der positive Beweis), `eingaben_offen: 0`, `spec_fehler: []`,
|
|
`meldungen: []`, `glieder_ist == glieder_soll`, Einfuegepunkt gleich dem
|
|
Kettenstart der Spec. Damit ist der Daten-Pfad in der Praxis belegt; die
|
|
9 Ergebnis-Tests in `tests/test_vf_spec.py` laufen gruen gegen diesen Lauf.
|
|
|
|
Zwei Nacharbeiten aus dem Lauf:
|
|
- Die Ergebnisdatei landete in `results/`, weil `c:VF_SPEC_BAU` `DXFM_RESULTS`
|
|
bevorzugte. Das ist das Verzeichnis der Sivas-/CSV-Exporte; Testergebnisse
|
|
gehoeren nach `tests/output` (Konvention `test_run_all.lsp`, von den
|
|
pytest-Fixtures gelesen, per `.gitignore` ausgeschlossen). Default jetzt
|
|
`tests/output`, Override ueber `DXFM_VF_SPEC_OUT`. Die Fixture prueft beide
|
|
Orte, damit ein alter Lauf nicht stumm uebersprungen wird.
|
|
- `TEST_VF_SPEC` ist ein Testbefehl und muss vorher geladen werden:
|
|
`(load (strcat (getenv "DXFMAKRO") "/tests/test_vf_spec.lsp"))` - oder
|
|
ueber `SSG_RUN_ALL_TESTS`, wo `vf_spec` inzwischen in `alltests.json` steht.
|
|
|
|
### 4.7 Aufteilung der Testfaelle (2026-09-03)
|
|
|
|
Nach dem gruenen `VF_SPEC_BAU`-Lauf gab es zwei Tests, die dieselbe Geometrie
|
|
bauen. Aufgeloest:
|
|
|
|
| | Aufzeichnungsformat | Spec |
|
|
|---|---|---|
|
|
| Daten | `tests/testdata/hm_recformat.json` (281 Objekte) | `tests/testdata/hundm05.json` (52 Objekte) |
|
|
| Treiber | `tests/test_hm_recformat.lsp`, `TEST_HM_RECFORMAT` | `tests/test_hundm05.lsp`, `TEST_HUNDM05` |
|
|
| pytest | `tests/test_hm_recformat.py` | `tests/test_hundm05.py` |
|
|
| In `alltests.json` | **abgeschaltet** | aktiv |
|
|
|
|
Der Spec-Test ist damit der routinemaessige Bau-Test; die Spec ist auch die
|
|
Datei, in der weitere Ketten der Anlage nachgetragen werden.
|
|
|
|
**Warum die Aufzeichnung bleibt** (und nicht durch die Spec ersetzt wird):
|
|
|
|
1. Sie ist die **einzige eingecheckte Kopie der Originaldaten** -
|
|
`data/polylines.dxf` ist mit 124 MB per `.gitignore` ausgeschlossen. Die
|
|
Spec ist daraus abgeleitet; ohne die Aufzeichnung faellt die rechte Seite
|
|
des Rundlauf-Beweises weg und die Spec belegt nur noch, dass der Emitter
|
|
mit sich selbst uebereinstimmt.
|
|
2. Sie prueft eine **andere Invariante**: ihr Journal kommt roh aus der XDATA
|
|
einer Kundenzeichnung. Damit ist `TEST_HM_RECFORMAT` der einzige Test
|
|
dafuer, dass ein BESTEHENDER `VF_n`-Block weiter abspielbar ist - also
|
|
dass Doppelklick-Edit und 2D/3D-Konvertierung an Altbestand funktionieren
|
|
(I1). Ein spec-gebautes Journal kann das nicht zeigen, es kommt aus dem
|
|
Uebersetzer. Bei Aenderungen am Journal-Format, an den
|
|
`vfl-in-*`-Wrappern oder am XDATA-Layout also bewusst einschalten.
|
|
3. Sie **dokumentiert die Frage-Reihenfolge**: jeder Eintrag traegt einen
|
|
`kommentar`, der die zugehoerige Frage nennt. Die Spec verbirgt den Dialog -
|
|
das ist ihr Zweck - und kann diese Rolle nicht uebernehmen.
|
|
|
|
Damit es nur EINEN Code-Pfad gibt, der aus einer Spec Geometrie erzeugt, hat
|
|
`tests/test_hundm05.lsp` keine eigene Bau-Logik: es ruft `vsp-bau-datei` aus
|
|
`Lisp/vf_spec.lsp` - dieselbe Funktion, die auch `c:VF_SPEC_BAU` nutzt. Die
|
|
Ergebnisdatei heisst jetzt nach der Spec-Datei
|
|
(`<basisname>_results.json`), damit Befehl und Testrunner in dieselbe Datei
|
|
schreiben und keine zweite Kopie derselben Ergebnisse entsteht.
|
|
|
|
Nebenbei behoben: das Anlagenkuerzel der `test_id` in
|
|
`lib/vf_journal_export.py` kam aus dem Namen der Ziel-JSON. Nach der
|
|
Umbenennung haette eine Regenerierung die ids stillschweigend von
|
|
`VF_hundm05_LZ_*` auf `VF_hm_recformat_LZ_*` geaendert - und damit die
|
|
Paarung zwischen den beiden Testlaeufen zerlegt. Jetzt feste Konstante
|
|
`TEST_ID_ANLAGE`.
|
|
|
|
**Noch offen in Stufe 1**: die Uebersetzer fuer Modus 2 (`pfad-handles` +
|
|
Segmentliste, Abschnitt 4.4) und Modus 3 sind NICHT gebaut - Modus 2 braucht
|
|
laut Reihenfolge (Punkt 11) erst 2-3 echte `"linienzug2"`-Journale als
|
|
Referenz, und `tests/testdata/hundm05.json` enthaelt ausschliesslich
|
|
Modus-1-Ketten. Ebenfalls offen: der CAD-Lauf `TEST_VF_SPEC` (Uebersetzer,
|
|
ohne Zeichnung) und `VF_SPEC_BAU` (baut die 5 Ketten aus der Spec; danach
|
|
prueft `tests/test_vf_spec.py` das Ergebnis gegen
|
|
`tests/output/hundm05_results.json` - Attribut fuer Attribut, das ist der
|
|
Beweis "Spec-Pfad == Journal-Pfad" auf Geometrie-Ebene).
|
|
|
|
---
|
|
|
|
## Teil 5 - Stufe 2: echte Funktionstrennung
|
|
|
|
Konvention: reiner Bauteil bekommt Suffix `-bauen` (Geometrie aus Parametern)
|
|
bzw. `-setzen` (nur Globals); **die Frage-Schale behaelt den bestehenden
|
|
Namen** - dadurch aendert sich keine einzige Aufrufstelle in Modus 1/2/3.
|
|
|
|
| heute gemischt | reiner Bauteil (neu) |
|
|
|---|---|
|
|
| `vfl-baue-horizontal-koerper` :2270 | `vfl-hor-koerper-bauen (frame hz dL sep-vor sep-nach ende-code gf2-laenge)` |
|
|
| `vfl-body-abschluss` :2409 | `vfl-body-abschluss-bauen (frame letzt-hz p-umlenk dL hz-neu hoehe-ziel es-gewuenscht)` |
|
|
| `vfl-vf-einheit` :2497 | `vfl-vf-eingang-bauen` / `vfl-vf-sub-bauen` / `vfl-vf-ausgang-bauen` / `vfl-vf-einheit-bauen (frame vf-spec)` |
|
|
| `vfl-vf-einheit-abschluss` :3112 | `vfl-vf-abschluss-bauen (...)` + `vfl-ziel-report (frame)` |
|
|
| `vfl-frage-es-seite` :2956 | `vfl-es-masse-setzen (es-winkel es-seite)` |
|
|
| `vfl-waehle-winkel` :1656 | `vfl-winkel-gueltige` + `vfl-winkel-nach-index` |
|
|
|
|
`ende-code` ersetzt das Flag `ziel-modus`; der Bauteil gibt dann immer nur
|
|
`frame` zurueck (die Doppelrueckgabe `(frame ist-ende-antwort)` existiert nur,
|
|
um die *Antwort* nach oben zu geben).
|
|
|
|
**Die Winkelfrage sitzt im Solver, nicht in einer Schale.**
|
|
`vfl-body-zerlegung` :2357 (also nicht rein, anders als zunaechst
|
|
angenommen), `vfl-vf-winkel` :1692, `vfl-vf-entscheidung` :1707 und
|
|
`vfl-segment-entscheidung` :1972 rufen alle `vfl-waehle-winkel`. Ein
|
|
Parameter wuerde vier Signaturen und alle Aufrufer aendern; stattdessen der
|
|
Kanal, den die Datei schon fuer `vfl-menu` nutzt (:1118, dynamisch gebundene
|
|
`*vflw-menu-*`): `*vfl-winkel-idx-vorgabe*`.
|
|
|
|
**"Alles fragen, dann alles bauen" ist unmoeglich** - und das ist der
|
|
zentrale Punkt: `vfl-neue-linie-messen` braucht `(car frame)`, die
|
|
Hoehenvorschlaege brauchen `(caddr (car frame))`, und die dL-Reservierung
|
|
braucht den *gemessenen* auf_3-Fussabdruck. Deshalb behaelt der interaktive
|
|
Treiber seine `while`-Schleife, fragt aber pro Runde **einen** Knoten und baut
|
|
ihn sofort ueber denselben reinen Knoten-Bauteil; der Daten-Treiber ist
|
|
dieselbe Kette mit `foreach` statt `while`.
|
|
|
|
Knotenform: **die rohen Antworten in Frage-Reihenfolge** (nil = nicht gefragt)
|
|
plus die abgeleiteten Bauwerte. Dann ist der Serializer ein dummer Flattener.
|
|
Dazu gehoert das Feld `dl-verworfen`: `vfl-in-abstand` journalisiert
|
|
bedingungslos (:2129), abgewiesene 25-m-Eingaben (:2165) stehen also **im
|
|
Journal** - ohne dieses Feld ist Journal -> Spec verlustbehaftet und jeder
|
|
Round-Trip-Test scheitert an einer solchen Kette.
|
|
|
|
Journal-Invariante im Daten-Pfad: die Spec erzeugt das Journal einmal
|
|
(`vfl-spec->journal`), es wird vor dem Bau gesetzt, kein Bauteil ruehrt es an.
|
|
Als Dauerbeweis eine `dbgmsg`-Assertion dort, wo beide Erzeuger sich treffen -
|
|
in `vf-linienzug-modus` unmittelbar vor `vfl-journal-xdata-schreiben` (:3867)
|
|
das aus der Spec serialisierte Journal gegen `(vfl-journal->string)` stellen.
|
|
Ohne offene `.dbg`-Datei kostet das nichts, mit Logging beweist es die Treue
|
|
bei jedem Bau.
|
|
|
|
### Reihenfolge (billig/risikoarm zuerst)
|
|
|
|
0. `vfl-ziel-report` aus den zwei byte-identischen Bloecken :3150/:3176 ziehen
|
|
1. Winkelwahl (`vfl-winkel-gueltige`/`-nach-index` + `*vfl-winkel-idx-vorgabe*`)
|
|
2. ES/AS-Masse (`vfl-es-masse-setzen`)
|
|
3. `vfl-hor-koerper-bauen`
|
|
4. `vfl-body-abschluss-bauen`
|
|
5. VF-Einheit: Eingang/Sub/Ausgang + Schleifenrumpf
|
|
6. Daten-Executor fuer die VF-Einheit
|
|
7. **Spec + Serializer + Assertion** - dieser Schritt *ist* die Invariante,
|
|
vorher nichts ausliefern
|
|
8. Einheit-Abschluss
|
|
9. Kettenebene, ein Glied-Zweig pro Commit: GF-Bogen -> ES -> Horizontal-VF ->
|
|
Linie-GF -> Linie-VF -> Linie
|
|
10. `vfl-spec-ausfuehren` (Daten-Pfad ohne Replay-Umweg)
|
|
11. Modus 2 - **erst** wenn 2-3 echte `"linienzug2"`-Journale als Referenz
|
|
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,
|
|
:2301, :2319/:2323): jeder Schritt mit `(max *vfk-restlaenge-min-clamp* ...)`
|
|
geklammert, also **reihenfolgeabhaengig**, und der auf_3-**Insert** sitzt
|
|
zwischen Subtraktion 1 und 2 (Fussabdruck gemessen, nicht geschaetzt). Der
|
|
Bauteil ist der heutige Rumpf **ohne** die drei `vfl-menu`-Bloecke und
|
|
**ohne jede Umsortierung**. Die drei Fragen duerfen nach vorn in die Schale
|
|
(keine haengt an `pt`/`dL`), die Subtraktionen nicht.
|
|
- `vfl-nach-3grad` :2415/:2643 darf von Schale *und* Bauteil gerufen werden -
|
|
es ist idempotent (:2236 prueft `vfl-frame-flach-p`).
|
|
- `ein-dx`/`ein-dz`-Nullen (:2588-2593) wandert vom Aufrufer in den Bauteil und
|
|
aendert damit das Budget in `berechne-alle-winkel`, also Geometrie. Nur der
|
|
"ES nein"-Pfad fuehrt dort durch.
|
|
- `ziel-gf2` vs. `L_GF2-bau` im Ausgang (:2706): geht es verloren, entsteht
|
|
still die falsche GF2 hinter dem Motor.
|
|
- Die `vfl-acc-*`-Aufrufe muessen an derselben Stelle in derselben Reihenfolge
|
|
bleiben, sonst vertauschen sich die Komma-Listen `L_VF_m`/`L_GF_m`/`GF_WINKEL`
|
|
- in der Zeichnung unsichtbar, im Sivas-Export falsch.
|
|
- `*vfl-as-winkel*`/`*vfl-es-winkel*` werden **lazy** von den Blocknamen-Bauern
|
|
gelesen (:111/:112) - spaeter gesetzt als heute heisst falscher Block.
|
|
- Im Daten-Pfad muss `hz` eines Fortsetzungsknotens "vom Frame erben" (nil)
|
|
bedeuten, nie ein gespeicherter Absolutwert: `vfl-insert-vario-kurve-block`
|
|
snappt den gemessenen Wert neu (:2838-2841), ein Absolutwert laesst die Kette
|
|
driften.
|
|
- Die `aus-dx`/`ein-dx`-Save/Restore-Fenster (:1693, :2362, :5125, :5236) sind
|
|
heute nie verschachtelt; zwei ineinander und das innere Restore ueberschreibt
|
|
das aeussere Save.
|
|
|
|
### Ausdruecklich nicht anfassen
|
|
|
|
`vf-linienzug-modus3` + alle `vfl2-*`; die `vfl-in-*`-Wrapper und `vfl-menu*`;
|
|
`vfl-entry->string` und das XDATA-Layout/die Marker; **alle Journal-Konsumenten**
|
|
(brauchen sie Aenderungen, ist die Invariante gebrochen - das ist der
|
|
Lackmustest); die reinen `vfs-*`/`berechne-*` in `vf_standard.lsp` (via
|
|
`*vf-typ-registry*` mit Standard/Etage geteilt); die `vflw-*`-Wizard-Schicht
|
|
und die DCLs; das Buendeln der `*vfl-acc-*` zu einem Record; der
|
|
Abbruch-/`*error*`-Pfad.
|
|
|
|
---
|
|
|
|
## Teil 6 - Nebenarbeit: Python-Grammatik vervollstaendigen
|
|
|
|
`lib/vf_journal_export.py` enthaelt die Journal-Grammatik schon als
|
|
Recursive-Descent-Parser (`Journal.dekodiere`), inklusive `vorantwort`-
|
|
Mechanik, bedingtem Winkel-`INT` und der `erster_dl`/`REAL hz`-Regel - aber
|
|
unvollstaendig: **`Linie-VF` fehlt im Dispatch** (`lauf`, sonst `raise`) und
|
|
`vf_einheit` ruft `horizontal_koerper()` unbedingt als ersten Koerper (nur fuer
|
|
`Horizontal-VF` richtig, falsch bei gewinkeltem Erstkoerper). Die 5
|
|
HundM-Ketten dekodieren nur, weil ihre Daten diesen Fall nicht enthalten. Beide
|
|
Luecken zusammen mit dem LISP-Serializer schliessen: dann pruefen zwei
|
|
unabhaengige Implementierungen der gleichen Grammatik einander.
|
|
|
|
**Erledigt** (2026-09-02): `Linie-VF` ist im Dispatch (`glied_linie_vf`: DL,
|
|
Zielhoehe, optionale Winkelwahl, dann VF-Einheit), und `vf_einheit` bekommt
|
|
`erster_horizontal` - das spiegelt die `winkel1`-Verzweigung in
|
|
`vfl-vf-einheit`: nur der horizontale Erstkoerper laeuft durch
|
|
`vfl-baue-horizontal-koerper` und fragt Separator vor/nach + Endpunkt vorab,
|
|
ein gewinkelter wird ohne Frage gebaut. `glied_linie_kettenende` uebergibt
|
|
`False` (fuer `typ="VF"` liefert `vfl-segment-entscheidung` immer einen Winkel
|
|
> 0).
|
|
|
|
Abgesichert von `tests/test_vf_journal_grammatik.py` (9 Tests, ohne CAD und
|
|
ohne die 118-MB-DXF: Journale stehen als Token-Listen im Test). Gegenprobe
|
|
gemacht: mit dem alten `erster_horizontal`-Verhalten scheitert die Linie-VF-
|
|
Tokenfolge (`Token 11: STR erwartet, STEP:ES gefunden`), der Fix ist also
|
|
tragend. `tests/testdata/hundm05.json` bleibt nach der Regenerierung
|
|
**byte-identisch** - die 5 echten Ketten enthalten keinen der beiden Faelle.
|
|
|
|
Offen bleibt der Gegenpart: der LISP-Serializer (`vfl-spec->journal`, Stufe 1).
|
|
Erst mit ihm pruefen zwei unabhaengige Implementierungen einander.
|
|
|
|
---
|
|
|
|
## Verifikation
|
|
|
|
**Stufe 0** (nach P0-P3, interaktiv unveraendert):
|
|
- `TEST_HUNDM05` in BricsCAD: 5 Ketten, alle `"status": "executed"`,
|
|
`eingaben_offen` 0 - beweist, dass Replay und Journal-XDATA unveraendert sind.
|
|
- `python -m pytest tests/test_hundm05.py -q` (Daten-Checks laufen ohne CAD).
|
|
- Interaktive Gegenprobe: `FOERDERANLAGE` -> Linienzug Modus 1 mit Wizard,
|
|
eine kurze Kette bauen, Doppelklick-Edit, danach `SSG_DIM_SWITCH` - Alert-
|
|
Texte und Dialoge muessen identisch aussehen.
|
|
|
|
**Stufe 1**:
|
|
- Neu `tests/test_vf_spec.lsp` (`C:TEST_VF_SPEC`, Muster `test_vfl_schema.lsp`
|
|
mit `tvs-eq`/`tvs-truthy`/`tvs-nil`, in `alltests.json` ohne `"save"`):
|
|
Uebersetzer-Unit-Tests ohne Zeichnung - je Glied-Typ das erwartete flache
|
|
Journal, die Menue-Code-Tabelle, die `hz`-Regel, die invertierte
|
|
`gf-verteilung`, Validierungsfehler.
|
|
**Der entscheidende Test**: `tests/testdata/linienzug_tests.json` (eine
|
|
vollstaendig kommentierte Kette mit Linie-VF, Horizontal-VF, zwei
|
|
Vario-Kurven, Option-3-Kettenende und ES-Glied) in eine Spec uebersetzen und
|
|
pruefen, dass `vfl-spec->journal` **dieselbe** Liste liefert.
|
|
- Neu `tests/test_vf_spec_bau.lsp` (`C:TEST_VF_SPEC_BAU`, `"save": "dxf"`):
|
|
die 5 HundM-Ketten als Spec (`tests/testdata/vf_spec_hundm05.json`) bauen und
|
|
das Ergebnis **gegen `tests/output/hundm05_results.json`** stellen -
|
|
Attribut fuer Attribut, Einfuegepunkt fuer Einfuegepunkt. Das beweist
|
|
Spec -> Journal -> Bau == aufgezeichnetes Journal -> Bau.
|
|
- Neu `tests/test_vf_spec.py` + Fixtures in `conftest.py` (Muster der
|
|
bestehenden `hundm05_*`-Fixtures mit `pytest.skip`): pro Kette `status`
|
|
`executed`, `eingaben_offen` 0, `sektionen_soll` == `sektionen_ist`,
|
|
`glieder` == Spec-Glied-Folge, `warnungen` leer, **`prompts` == 0**.
|
|
- "Kein Prompt, kein Dialog" in vier Schichten: statischer Waechter
|
|
(pytest ohne CAD, vergleicht die Fundstellen von `alert`/`get*`/`ssget`/
|
|
`new_dialog` gegen eine eingecheckte Liste - haelt das Design gegen
|
|
Verrottung), zaehlende Stubs (`prompts` im Record als positiver Beweis),
|
|
Schalterzustand (`*ssg-gui-aus*` T, `*vfl-wizard-mode*` nil mit
|
|
Save/Restore in einer `vl-catch-all-apply`-Klammer), und ein
|
|
`tests/test_vf_spec.scr`-Lauf (Muster `test_unit.scr`) mit Sentinel in der
|
|
letzten Zeile: fehlt der Sentinel im Log, hat doch ein Prompt geoeffnet.
|
|
|
|
**Stufe 2**: nach jedem Schritt `TEST_HUNDM05` + `TEST_VF_SPEC` + `TEST_LINIENZUG`;
|
|
nach den Geometrie-Schritten (3, 4, 5, 9) zusaetzlich DXF-Diff Frage-Pfad gegen
|
|
Daten-Pfad ueber `lib/dxf_abbild.py`, und Sivas-Export-Vergleich
|
|
(`EXPORTSIVAS`) wegen der Reihenfolge in `L_VF_m`/`L_GF_m`.
|
|
|
|
---
|
|
|
|
## Getroffene Entscheidungen (aenderbar)
|
|
|
|
1. **Modus-2-Pfadobjekte**: die Spec traegt die Entity-Handles. Damit bleibt I1
|
|
exakt und es braucht keinen neuen Marker. Alternative waere ein Marker
|
|
`"linienzug2s"` plus Zweig in `vfl-edit-ent2` - erweitert das Markerschema
|
|
fuer jeden Leser, daher nicht gewaehlt.
|
|
2. **Segment-XDATA im Daten-Pfad**: bleibt erhalten (Paritaet mit dem
|
|
interaktiven Pfad), damit `c:VF_SEKTION_RESTORE` auch bei spec-gebauten
|
|
Ketten greift. In Stufe 1 automatisch (Bau laeuft durch die Modus-Funktion);
|
|
in Stufe 2 braucht der Executor denselben Per-Glied-Hook.
|
|
3. **Modus 3**: nur Wrapper, kein Journal-XDATA (zurueckgestellt).
|
|
4. **`vf_spec.lsp` als neue Datei** statt Anbau an `vf_linienzug.lsp` (schon
|
|
~6500 Zeilen, groesste Datei im Repo); Abhaengigkeit ist einseitig.
|
|
|
|
## Betroffene Dateien
|
|
|
|
- `Lisp/vf_linienzug.lsp` - P1/P2/P3-Hooks (:123, :157, :1055, :1066, :1185,
|
|
:1196, :2028, :2113, :3523, :3550, :3559, :3650, :3772, :5418, :5650) und
|
|
die ganze Stufe 2
|
|
- `Lisp/vf_spec.lsp` - **neu**: Spec, Uebersetzer, Headless-Rahmen, Record
|
|
- `Lisp/vf_core.lsp` - P0 (:1139), Ladezeile nach :1062
|
|
- `lib/vf_journal_export.py` - Grammatik vervollstaendigen (`Linie-VF`,
|
|
erster Koerper)
|
|
- `tests/test_hundm05.lsp` - Prototyp zum Verallgemeinern (`hundm05:gruppiere`,
|
|
`-eintrag->journal`, `-result-json`, `-stubs-an`/`-aus`, `-queue-rest`)
|
|
- `tests/testdata/linienzug_tests.json` - fixiert den VF-Fluss im Unit-Test
|
|
- `tests/testdata/hundm05.json` + `tests/output/hundm05_results.json` -
|
|
Referenz fuer die Aequivalenz von Spec-Pfad und Journal-Pfad
|