diff --git a/CLAUDE.md b/CLAUDE.md index 0ba7380..ddef2fc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -98,7 +98,7 @@ Detaillierte Dokumentation aller Funktionen und Befehle: siehe `Lisp/README.md`. | `ssg_dialog.lsp` | - | DCL-Dialog-Vorlagen und Hilfsfunktionen | | `ssg_layer.lsp` | - | Layer-Verwaltung | | `KreiselInsert.lsp` | KreiselInsert, KreiselConnect, KreiselRedraw, KreiselQuick, KreiselEdit, KreiselParams, KreiselLabelSetup, KreiselLabelPos, KreiselLabelHoehe, ILS_Eckrad | ILS Kreisel und Eckrad komplett in AutoLISP | -| `VarioFoerderer.lsp` (Wrapper fuer `vf_core.lsp`/`vf_standard.lsp`/`vf_etage.lsp`/`vf_linienzug.lsp`) | FOERDERANLAGE, VARIOFOERDERER_EDIT, VARIOFOERDERER_NACHRUESTEN, Vario_Kette_Merge, VF_SEKTION_RESTORE, VF_WIZARD_AN, VF_WIZARD_AUS | Vario-Foerderanlagen Generator (Typen Standard/Etage/Linienzug); Standard- UND Etage-Typ: Werteingabe per DCL-Dialog. Der Dialog-Ablauf ist zentral in vf_core (`vf-dialog-ablauf`/`vf-dialog-berechnen-einfuegen`, parametrisiert ueber Typ-Registry `*vf-typ-registry*` + Flag `horizontal-p`); `vfs-standard-dialog-ablauf`/`vfe-etage-dialog-ablauf` sind nur noch duenne Wrapper darauf. Beide nutzen dieselben Dialoge `vfs-dialog-eingabe-basis`/`vfs-dialog-winkel-verteilung` und den gemeinsamen L_GF/L_VF-Solve `vf-winkel-solve` (vf_core; berechne-alle-winkel/berechne-winkel-etage teilen ihn); Doppelklick auf VF_n-Block ruft VARIOFOERDERER_EDIT, das per SSG_VF_EDIT-XDATA-Marker ("standard"/"etage"/"linienzug") in den passenden Neuaufbau-Zweig dispatcht (`vfe-edit-ent` fuer Etage, `vfl-edit-ent` fuer Linienzug); Altbestand-Bloecke ohne Marker werden abgewiesen. Linienzug (Modus 1, `vf-linienzug-modus`) zeichnet jede interaktive Eingabe in einem Journal auf (`vfl-in-point`/`-string`/`-real`/`-int`) und schreibt es als XDATA auf den fertigen `VF_n`-Block; ein Abbruch (ESC) loescht dadurch nichts mehr, sondern wickelt die bis dahin gebaute Teil-Geometrie ebenso zu einem Block (`vfl-modus-abbruch-sichern`) - sie bleibt stehen und ist sofort per Doppelklick weiter editierbar/fortsetzbar. Der komplette Linienzug-Hauptstrang (Menue-Fragen von AS-Element bis Kettenende, inkl. GF-Bogen/Vario-Kurve/VF-Einheit-Fortsetzung) laeuft per Default ueber DCL-Dialoge statt Konsole (`*vfl-wizard-mode*`, Befehle VF_WIZARD_AN/VF_WIZARD_AUS): `vfl-in-string`/`-int`/`-real` zeigen bei aktivem Wizard-Modus generische Dialoge aus `dcl/vf_linienzug_wizard.dcl` (Zahl-Eingabe bzw. Mehrfachauswahl aus `vfl-menu`/`vfl-menu-int`) statt `getstring`/`getint`/`getreal` aufzurufen; die ORIGINALEN Konsolen-Fragen (princ + get*, de/en ueber ssg-text) bleiben unveraendert im Code (Fallback bei Fehlern via `vl-catch-all-apply`, siehe `doc/variofoerderer.md`). `vfl-edit-ent` liest das Journal, zeigt die Sektionen ueber einen DCL-Dialog (`vfl-dlg-position`, `dcl/vfl_edit.dcl`) an, kuerzt bei Bedarf auf eine gewaehlte Sektion (stummer Replay der behaltenen Eingaben, umgeht den Wizard-Zweig komplett) und baut danach interaktiv weiter. Linienzug Modus 2 (Pfad+Zielhoehe-Solver, `vf-linienzug-modus2`) nutzt dieselben Wrapper PLUS die neue Objektauswahl-Wrapper-Funktion `vfl-in-selection` (Pfad-Objekte als stabile Entity-Handles journalisiert, neue Journal-Art `"OBJS"`) und schreibt sein Journal unter dem eigenen XDATA-Marker `"linienzug2"`; Doppelklick baut hier NICHT abschnittsweise, sondern per vollem Reset 1:1 aus dem Journal neu auf (`vfl-edit-ent2`, dispatcht aus `vfl-edit-ent` per Marker) - Modus 3 (Vorwaerts-Nachbau) bleibt bewusst reine Konsole (im Code selbst als Legacy/"wird umgebaut" markiert). `Vario_Kette_Merge` (in vf_linienzug.lsp) fuehrt mehrere einzeln gebaute VF_n-Bloecke ab einem gewaehlten Start-Baustein ueber die reale KS_AUS->KS_EIN-Nachbarschaft zu einem Gesamt-Block zusammen (aggregierte Attribute, Luecken-Erkennung). Die interaktiven Editier-Pfade (`vfl-edit-ent` Sektions-Zweig, `vfl-edit-ent2`) rufen den Neuaufbau BEWUSST direkt auf, NICHT ueber `vl-catch-all-apply`: der alte Block ist beim Neuaufbau schon per `entdel` weg, und ein Catch faengt den Abbruch VOR `*error*` ab - der Abbruch-Wickler `vfl-modus-abbruch-sichern` kaeme nie zum Zug und die ganze Kette bliebe als lose Einzelteile ohne Block liegen (Fehlerbild "VF-Block aufgebrochen"). Nur der nicht-interaktive Batch-Konverter `vfl-konvertiere-ent` behaelt den Catch (die Batch-Schleife muss weiterlaufen) und meldet den betroffenen Block. Die Abbruch-Handler setzen zusaetzlich `*ssg-ils-dim*` zurueck, weil der Aufrufer, der das sonst tut, nach einem Abbruch nicht mehr erreicht wird. Recovery-Pfad fuer bereits aufgebrochene Ketten: `VF_SEKTION_RESTORE` (vf_linienzug.lsp) setzt das Ketten-Journal aus der XDATA der losen Objekte zusammen - `SSG_VF_EDIT_SEG` traegt pro Entity den Journal-Abschnitt DER ITERATION, in der es entstand (indiziert mit dem ersten Glied-Index darin; eine VF-Einheit mit eingebetteten Vario-Kurven erzeugt mehrere Glieder in einer Iteration), `SSG_VF_EDIT_PRE` auf den Entities des ersten Gliedes die Praeambel (Startpunkt/Starthoehe/AS). Die `.dbg`-Dateien sind reine AUSGABE und werden von keinem Befehl gelesen (dbgopen legt sie bei jedem Lauf neu an) - Wiederherstellungs-Daten gehoeren in die XDATA. `vf_konstanten.lsp` (von vf_core.lsp vor den drei Submodulen geladen) sammelt alle Laengen-/Winkel-/Toleranz-Magic-Numbers aus dem VarioFoerderer-Cluster als dokumentierte `*vfk-*`-Konstanten. Die Migration ist abgeschlossen: vf_core/vf_standard/vf_etage/vf_linienzug referenzieren durchgaengig die zentralen Konstanten statt roher Literale (Winkel, Stations-/Separator-Laengen, Mindestlaengen, AS/ES-Fallback-Versaetze, Text-/Label-Masse) - Ausnahmen sind in `vf_konstanten.lsp` selbst dokumentiert (Werte mit fuehrender Config-Anbindung via `ssg-cfg-or`, itoa-/Integer-Kontexte, Blocknamen-Strings, die 2000.0-mm/m-Umrechnung). `*vfk-gf-bogen-winkel*` (30/60/90, GF-Bogen + Vario-Kurve) und `*vfk-as-es-winkel*` ("30"/"90", AS/ES; bewusst STRING, da 2-wertig und im Journal als STR gefuehrt) sind Beispiele; vf_linienzug hat fuer beide einen Fallback, falls vf_konstanten nicht geladen wurde. **Nicht-interaktiver Betrieb**: `*vfl-headless*` (+ `vfl-headless-p`/`-abbruch`/`-notausgang`, Diagnose `*vfl-headless-fehler*`, optionaler Antwort-Hook `*vfl-headless-antwort-fn*`) verbietet jede Live-Eingabe - eine erschoepfte Replay-Queue ist damit ein harter Abbruch MIT Fundstelle (Art der Eingabe, Glied- und Eingabe-Nummer) statt eines stillen Rueckfalls auf `getpoint`/`getreal`; eingebaut in `vfl-in-value`/`-value-p`/`-selection`/`-abstand`. Die Bau-Pfade melden abgewiesene Sektionen ueber `vfl-meldung` (sammelt nach `*vfl-meldungen*` + `dbgmsg`, `alert` nur bei erlaubter GUI, sonst `princ`) statt per blockierendem `alert`; reine Interaktiv-Alerts (fehlendes DCL, "nicht editierbar", "kein Journal") bleiben `alert`. Genutzt von `tests/test_hundm05.lsp`; Fahrplan zur vollen Daten-API in `doc/TODO-plan-vf-interactive.md`. Details/Entscheidungsbaum: `doc/variofoerderer.md` | +| `VarioFoerderer.lsp` (Wrapper fuer `vf_core.lsp`/`vf_standard.lsp`/`vf_etage.lsp`/`vf_linienzug.lsp`) | FOERDERANLAGE, VARIOFOERDERER_EDIT, VARIOFOERDERER_NACHRUESTEN, Vario_Kette_Merge, VF_SEKTION_RESTORE, VF_WIZARD_AN, VF_WIZARD_AUS | Vario-Foerderanlagen Generator (Typen Standard/Etage/Linienzug); Standard- UND Etage-Typ: Werteingabe per DCL-Dialog. Der Dialog-Ablauf ist zentral in vf_core (`vf-dialog-ablauf`/`vf-dialog-berechnen-einfuegen`, parametrisiert ueber Typ-Registry `*vf-typ-registry*` + Flag `horizontal-p`); `vfs-standard-dialog-ablauf`/`vfe-etage-dialog-ablauf` sind nur noch duenne Wrapper darauf. Beide nutzen dieselben Dialoge `vfs-dialog-eingabe-basis`/`vfs-dialog-winkel-verteilung` und den gemeinsamen L_GF/L_VF-Solve `vf-winkel-solve` (vf_core; berechne-alle-winkel/berechne-winkel-etage teilen ihn); Doppelklick auf VF_n-Block ruft VARIOFOERDERER_EDIT, das per SSG_VF_EDIT-XDATA-Marker ("standard"/"etage"/"linienzug") in den passenden Neuaufbau-Zweig dispatcht (`vfe-edit-ent` fuer Etage, `vfl-edit-ent` fuer Linienzug); Altbestand-Bloecke ohne Marker werden abgewiesen. Linienzug (Modus 1, `vf-linienzug-modus`) zeichnet jede interaktive Eingabe in einem Journal auf (`vfl-in-point`/`-string`/`-real`/`-int`) und schreibt es als XDATA auf den fertigen `VF_n`-Block; ein Abbruch (ESC) loescht dadurch nichts mehr, sondern wickelt die bis dahin gebaute Teil-Geometrie ebenso zu einem Block (`vfl-modus-abbruch-sichern`) - sie bleibt stehen und ist sofort per Doppelklick weiter editierbar/fortsetzbar. Der komplette Linienzug-Hauptstrang (Menue-Fragen von AS-Element bis Kettenende, inkl. GF-Bogen/Vario-Kurve/VF-Einheit-Fortsetzung) laeuft per Default ueber DCL-Dialoge statt Konsole (`*vfl-wizard-mode*`, Befehle VF_WIZARD_AN/VF_WIZARD_AUS): `vfl-in-string`/`-int`/`-real` zeigen bei aktivem Wizard-Modus generische Dialoge aus `dcl/vf_linienzug_wizard.dcl` (Zahl-Eingabe bzw. Mehrfachauswahl aus `vfl-menu`/`vfl-menu-int`) statt `getstring`/`getint`/`getreal` aufzurufen; die ORIGINALEN Konsolen-Fragen (princ + get*, de/en ueber ssg-text) bleiben unveraendert im Code (Fallback bei Fehlern via `vl-catch-all-apply`, siehe `doc/variofoerderer.md`). `vfl-edit-ent` liest das Journal, zeigt die Sektionen ueber einen DCL-Dialog (`vfl-dlg-position`, `dcl/vfl_edit.dcl`) an, kuerzt bei Bedarf auf eine gewaehlte Sektion (stummer Replay der behaltenen Eingaben, umgeht den Wizard-Zweig komplett) und baut danach interaktiv weiter. Linienzug Modus 2 (Pfad+Zielhoehe-Solver, `vf-linienzug-modus2`) nutzt dieselben Wrapper PLUS die neue Objektauswahl-Wrapper-Funktion `vfl-in-selection` (Pfad-Objekte als stabile Entity-Handles journalisiert, neue Journal-Art `"OBJS"`) und schreibt sein Journal unter dem eigenen XDATA-Marker `"linienzug2"`; Doppelklick baut hier NICHT abschnittsweise, sondern per vollem Reset 1:1 aus dem Journal neu auf (`vfl-edit-ent2`, dispatcht aus `vfl-edit-ent` per Marker) - Modus 3 (Vorwaerts-Nachbau) bleibt bewusst reine Konsole (im Code selbst als Legacy/"wird umgebaut" markiert). `Vario_Kette_Merge` (in vf_linienzug.lsp) fuehrt mehrere einzeln gebaute VF_n-Bloecke ab einem gewaehlten Start-Baustein ueber die reale KS_AUS->KS_EIN-Nachbarschaft zu einem Gesamt-Block zusammen (aggregierte Attribute, Luecken-Erkennung). Die interaktiven Editier-Pfade (`vfl-edit-ent` Sektions-Zweig, `vfl-edit-ent2`) rufen den Neuaufbau BEWUSST direkt auf, NICHT ueber `vl-catch-all-apply`: der alte Block ist beim Neuaufbau schon per `entdel` weg, und ein Catch faengt den Abbruch VOR `*error*` ab - der Abbruch-Wickler `vfl-modus-abbruch-sichern` kaeme nie zum Zug und die ganze Kette bliebe als lose Einzelteile ohne Block liegen (Fehlerbild "VF-Block aufgebrochen"). Nur der nicht-interaktive Batch-Konverter `vfl-konvertiere-ent` behaelt den Catch (die Batch-Schleife muss weiterlaufen) und meldet den betroffenen Block. Die Abbruch-Handler setzen zusaetzlich `*ssg-ils-dim*` zurueck, weil der Aufrufer, der das sonst tut, nach einem Abbruch nicht mehr erreicht wird. Recovery-Pfad fuer bereits aufgebrochene Ketten: `VF_SEKTION_RESTORE` (vf_linienzug.lsp) setzt das Ketten-Journal aus der XDATA der losen Objekte zusammen - `SSG_VF_EDIT_SEG` traegt pro Entity den Journal-Abschnitt DER ITERATION, in der es entstand (indiziert mit dem ersten Glied-Index darin; eine VF-Einheit mit eingebetteten Vario-Kurven erzeugt mehrere Glieder in einer Iteration), `SSG_VF_EDIT_PRE` auf den Entities des ersten Gliedes die Praeambel (Startpunkt/Starthoehe/AS). Die `.dbg`-Dateien sind reine AUSGABE und werden von keinem Befehl gelesen (dbgopen legt sie bei jedem Lauf neu an) - Wiederherstellungs-Daten gehoeren in die XDATA. `vf_konstanten.lsp` (von vf_core.lsp vor den drei Submodulen geladen) sammelt alle Laengen-/Winkel-/Toleranz-Magic-Numbers aus dem VarioFoerderer-Cluster als dokumentierte `*vfk-*`-Konstanten. Die Migration ist abgeschlossen: vf_core/vf_standard/vf_etage/vf_linienzug referenzieren durchgaengig die zentralen Konstanten statt roher Literale (Winkel, Stations-/Separator-Laengen, Mindestlaengen, AS/ES-Fallback-Versaetze, Text-/Label-Masse) - Ausnahmen sind in `vf_konstanten.lsp` selbst dokumentiert (Werte mit fuehrender Config-Anbindung via `ssg-cfg-or`, itoa-/Integer-Kontexte, Blocknamen-Strings, die 2000.0-mm/m-Umrechnung). `*vfk-gf-bogen-winkel*` (30/60/90, GF-Bogen + Vario-Kurve) und `*vfk-as-es-winkel*` ("30"/"90", AS/ES; bewusst STRING, da 2-wertig und im Journal als STR gefuehrt) sind Beispiele; vf_linienzug hat fuer beide einen Fallback, falls vf_konstanten nicht geladen wurde. **Nicht-interaktiver Betrieb**: `*vfl-headless*` (+ `vfl-headless-p`/`-abbruch`/`-notausgang`, Diagnose `*vfl-headless-fehler*`, optionaler Antwort-Hook `*vfl-headless-antwort-fn*`) verbietet jede Live-Eingabe - eine erschoepfte Replay-Queue ist damit ein harter Abbruch MIT Fundstelle (Art der Eingabe, Glied- und Eingabe-Nummer) statt eines stillen Rueckfalls auf `getpoint`/`getreal`; eingebaut in `vfl-in-value`/`-value-p`/`-selection`/`-abstand`. Die Bau-Pfade melden abgewiesene Sektionen ueber `vfl-meldung` (sammelt nach `*vfl-meldungen*` + `dbgmsg`, `alert` nur bei erlaubter GUI, sonst `princ`) statt per blockierendem `alert`; reine Interaktiv-Alerts (fehlendes DCL, "nicht editierbar", "kein Journal") bleiben `alert`. Genutzt von `tests/test_hm_recformat.lsp` (Replay der echten Aufzeichnungen) und von `vf_spec.lsp`; Fahrplan in `doc/TODO-plan-vf-interactive.md`. Details/Entscheidungsbaum: `doc/variofoerderer.md` | | `vf_spec.lsp` (von vf_core.lsp NACH vf_linienzug.lsp geladen) | VF_SPEC_BAU | Linienzug-Ketten aus **Eingabedaten** bauen, ohne Dialog und ohne Konsolenfrage (Stufe 1 aus `doc/TODO-plan-vf-interactive.md`). Eine **Spec** beschreibt eine Kette in Domaenenwerten (`"links"`, `"winkel"`, `"aussen"`, ja/nein) statt in Menue-Codes; `vsp-journal` uebersetzt sie in ein Eingabe-Journal, `vsp-bau-aus-spec` spielt es per `vfl-journal-replay-start` durch den UNVERAENDERTEN `vf-linienzug-modus`. Damit traegt der fertige Block dasselbe `SSG_VF_EDIT`-Journal wie eine handgebaute Kette und bleibt per Doppelklick editier- und 2D/3D-konvertierbar - kein Journal-Konsument muss etwas von Specs wissen. Wichtige Regeln, die der Uebersetzer kennt: der Menue-Code haengt am Frame (erste Sektion 4 Optionen ohne GF-Bogen, danach 5), `hz` steht nur beim ERSTEN Segment im Journal, und `erst-hor` unterscheidet den horizontalen Erstkoerper einer VF-Einheit (Separator-/Endpunktfragen vorab) vom gewinkelten (keine Frage). Fehlerverhalten: fail fast - `vsp-pruefen` sammelt ALLE Spec-Fehler, gebaut wird dann nichts (nichts wird geraten). Ergebnis-Record je Kette mit `status` (`executed`/`warnung`/`desync`/`abbruch`/`spec-fehler`), Block/Handle/Einfuegepunkt/Attributen, `prompts` (muss 0 sein), `glieder_soll` vs. `glieder_ist` (Desync-Detektor, der auch bei aufgehender Queue greift) und den gesammelten `meldungen`. Headless-Rahmen `vsp-headless-an`/`-aus` (Save/Restore von `*vfl-headless*`, `*vfl-wizard-mode*`, `*ssg-gui-aus*` + zaehlende Eingabe-Stubs). Spec-Datei flach als JSON (`ssg-load-json` liest zeilenweise), erzeugt von `lib/vf_spec_export.py`. Gegenstueck/Referenz in Python: derselbe Emitter, gegen die 5 echten HundM-Ketten rundlaufgeprueft | | `Gefaellestrecke.lsp` | GEFAELLESTRECKE, GEFAELLESTRECKE_EDIT | Gefaelle-Foerderanlage (AUS -> Staustrecke skaliert -> Separator -> EIN); Modus 1 per DCL-Dialog (Einfuegehoehe, Element-Winkel 30/90); Doppelklick auf GF_n-Block ruft GEFAELLESTRECKE_EDIT (Dialog vorbelegt, Neuaufbau) | | `export.lsp` | EXPORTSIVAS, EXPORTCSV, OMNI_UPDATE_ATTRIBS | JSON-Sammlung und Python-Export (Omniflo-Merkmale/Sum-Zeile via lib/export_csv.py) | @@ -142,7 +142,7 @@ Werden via `bin/setenv.bat` gesetzt. Wichtigste: - `DXFM_DCL` - Pfad zu DCL-Dialogdateien - `DXFM_CFG` - Konfigurationsverzeichnis - `DXFM_LANG` - Aktive Sprache (`de_DE`/`en_GB`), persistiert von `SSG_SPRACHE`; Default `de_DE` -- `DXFM_VF_SPEC` - Spec-Datei fuer `VF_SPEC_BAU` (optional; Default `tests/testdata/vf_spec_hundm05.json`) +- `DXFM_VF_SPEC` - Spec-Datei fuer `VF_SPEC_BAU` (optional; Default `tests/testdata/hundm05.json`) ## Konventionen @@ -166,9 +166,9 @@ Abhaengigkeiten: `ezdxf`, Standard-Library. Benoetigen Umgebungsvariablen (`DXFM | `export_sivas.py` | Erzeugt Sivas-Export-CSV mit Summierungszeilen (ILS Automation, Omniflo Sum). Aufruf: `python export_sivas.py ` | | `export_csv.py` | Erzeugt einfache Item-Liste als CSV (ohne Summierung). Aufruf: `python export_csv.py ` | | `split_block_library.py` | Teilt `ils_library.dxf` in einzelne DXF-Dateien pro Block auf (mit Abhaengigkeiten). Export-Prefixe: Vario_, Staustrecke_, _3D_AS_, _3D_ES_ | -| `vf_journal_export.py` | Liest die Eingabe-Journale der `VF_n`-Bloecke (XDATA-App `SSG_VF_EDIT`, Marker `"linienzug"`) aus einer Zeichnung und schreibt sie als Frage-Antwort-Protokoll im Schema von `tests/testdata/linienzug_tests.json` (Gegenstueck zu `vfl-entry->string`; kein ezdxf, reine Gruppencode-Lesung). Optional `--csv ` fuer Strecken-ID und Erwartungswerte je Kette. Aufruf: `python vf_journal_export.py [--csv ...] [--kein-startpunkt-fix]` — erzeugt `tests/testdata/hundm05.json` aus `data/polylines.dxf` | -| `vf_spec_export.py` | Uebersetzt ein Eingabe-Journal in eine **Spec** und zurueck (`spec_aus_journal` / `journal_aus_spec`) - Gegenstueck zu `vsp-journal` in `Lisp/vf_spec.lsp`, beide muessen dieselbe Tokenfolge liefern. Der Rundlauf `journal_aus_spec(spec_aus_journal(tok)) == tok` laeuft in `tests/test_vf_spec.py` gegen die 5 echten HundM-Ketten und beweist, dass die Spec das Journal verlustfrei abbildet. Schreibt das flache Spec-JSON (vom LISP-Parser lesbar). Aufruf: `python vf_spec_export.py ` — erzeugt `tests/testdata/vf_spec_hundm05.json` aus `tests/testdata/hundm05.json` | -| `dxf_scan_components.py` | Erkennt ILS-/Omniflo-Komponenten in einer **fremden** Projektzeichnung (ohne unsere Wrapper-Bloecke) und schreibt sie im JSON-Schema aus `tests/testdata/object_data.md` — Grundlage fuer Nachbau-Testfaelle wie `tests/testdata/hundm05.json`. Liest binaere DXF im GB-Bereich streamend per mmap (ezdxf-DOM waere zu gross), loest INSERTs inkl. OCS/Extrusionsrichtung rekursiv in Weltkoordinaten auf, paart AN8/SP8 zu Kreiseln und Antrieb/Umlenkung zu Foerderern. Aufruf: `python dxf_scan_components.py [--fenster X0 Y0 X1 Y1]` — ohne `--fenster` liefert eine Anlagenzeichnung leicht ueber 1300 Eintraege, zuviel fuer einen Testlauf | +| `vf_journal_export.py` | Liest die Eingabe-Journale der `VF_n`-Bloecke (XDATA-App `SSG_VF_EDIT`, Marker `"linienzug"`) aus einer Zeichnung und schreibt sie als Frage-Antwort-Protokoll im Schema von `tests/testdata/linienzug_tests.json` (Gegenstueck zu `vfl-entry->string`; kein ezdxf, reine Gruppencode-Lesung). Optional `--csv ` fuer Strecken-ID und Erwartungswerte je Kette. Aufruf: `python vf_journal_export.py [--csv ...] [--kein-startpunkt-fix]` — erzeugt `tests/testdata/hm_recformat.json` aus `data/polylines.dxf` (die Aufzeichnung, aus der `tests/testdata/hundm05.json` als Spec abgeleitet wird) | +| `vf_spec_export.py` | Uebersetzt ein Eingabe-Journal in eine **Spec** und zurueck (`spec_aus_journal` / `journal_aus_spec`) - Gegenstueck zu `vsp-journal` in `Lisp/vf_spec.lsp`, beide muessen dieselbe Tokenfolge liefern. Der Rundlauf `journal_aus_spec(spec_aus_journal(tok)) == tok` laeuft in `tests/test_vf_spec.py` gegen die 5 echten HundM-Ketten und beweist, dass die Spec das Journal verlustfrei abbildet. Schreibt das flache Spec-JSON (vom LISP-Parser lesbar). Aufruf: `python vf_spec_export.py ` — erzeugt `tests/testdata/hundm05.json` (Spec, Eingabe fuer `TEST_HUNDM05`/`VF_SPEC_BAU`) aus `tests/testdata/hm_recformat.json` (Aufzeichnung) | +| `dxf_scan_components.py` | Erkennt ILS-/Omniflo-Komponenten in einer **fremden** Projektzeichnung (ohne unsere Wrapper-Bloecke) und schreibt sie im JSON-Schema aus `tests/testdata/object_data.md` — Grundlage fuer Nachbau-Testfaelle wie `tests/testdata/mubea.json`. Liest binaere DXF im GB-Bereich streamend per mmap (ezdxf-DOM waere zu gross), loest INSERTs inkl. OCS/Extrusionsrichtung rekursiv in Weltkoordinaten auf, paart AN8/SP8 zu Kreiseln und Antrieb/Umlenkung zu Foerderern. Aufruf: `python dxf_scan_components.py [--fenster X0 Y0 X1 Y1]` — ohne `--fenster` liefert eine Anlagenzeichnung leicht ueber 1300 Eintraege, zuviel fuer einen Testlauf | ### Entwickler-Tools (`lib/`) - kein ezdxf, reine Text-/AutoLISP-Verarbeitung diff --git a/Lisp/vf_spec.lsp b/Lisp/vf_spec.lsp index f4818fb..a302c16 100644 --- a/Lisp/vf_spec.lsp +++ b/Lisp/vf_spec.lsp @@ -77,6 +77,11 @@ (if (not (boundp '*vsp-erster-dl*)) (setq *vsp-erster-dl* T)) (if (not (boundp '*vsp-wo*)) (setq *vsp-wo* "Spec")) (if (not (boundp '*vsp-prompts*)) (setq *vsp-prompts* 0)) +;; Erzwungene Bau-Dimension ("2D"/"3D") fuer einen ganzen Lauf. nil = die +;; Spec bzw. die Sitzung entscheidet. Muss JE KETTE neu gesetzt werden, +;; darum in vsp-bau-aus-spec und nicht einmal aussen: der Abbruch-Handler in +;; vf-linienzug-modus setzt *ssg-ils-dim* zurueck. +(if (not (boundp '*vsp-dim-override*)) (setq *vsp-dim-override* nil)) ;; Fehler sammeln (aeltester zuerst) statt beim ersten abzubrechen: eine ;; handgeschriebene Spec hat selten genau einen Tippfehler. @@ -684,7 +689,7 @@ ;; Eine Kette aus einer Spec bauen. Rueckgabe: Ergebnis-Record (Alist). ;; Die Klammer um vf-linienzug-modus ist dieselbe wie in -;; tests/test_hundm05.lsp: *error* sichern, fangen, *error* zurueck. Weil +;; tests/test_hm_recformat.lsp: *error* sichern, fangen, *error* zurueck. Weil ;; gefangen wird, laeuft der Abbruch-Handler der Modus-Funktion NICHT - eine ;; abgebrochene Kette bleibt also als lose Geometrie liegen und ist per ;; c:VF_SEKTION_RESTORE rettbar (vfl-segment-xdata-sichern hat pro Iteration @@ -716,7 +721,11 @@ ;; Dimension je Kette neu setzen: der Abbruch-Handler in ;; vf-linienzug-modus setzt *ssg-ils-dim* zurueck, eine abgebrochene ;; Kette wuerde die folgenden sonst in der falschen Dimension bauen. - (setq dim (ssg-val kopf "dim")) + ;; Vorrang: expliziter Lauf-Override (Testbefehle _2D/_3D) vor dem + ;; Feld in der Spec. Ist keines gesetzt, gilt die Sitzung. + (setq dim (if (and (boundp '*vsp-dim-override*) *vsp-dim-override*) + *vsp-dim-override* + (ssg-val kopf "dim"))) (if dim (setq *ssg-ils-dim* dim)) (setq rec (vsp-set rec "dimension" (vsp-dim-text))) (princ (strcat " -> " (itoa (length journal)) " Journal-Eintraege, " @@ -811,7 +820,7 @@ ;; TEIL 8: BEFEHL ;; ============================================================ ;; Fragt nichts. Spec-Datei aus DXFM_VF_SPEC, sonst -;; tests/testdata/vf_spec_hundm05.json; Ergebnisse nach DXFM_RESULTS bzw. +;; tests/testdata/hundm05.json (die Ketten der Anlage HundM); ;; tests/output. ;; ssg-ensure gibt es nur, wenn das Menue geladen wurde (SSG_LIB.mnl). Im @@ -831,30 +840,23 @@ "\n Ergebnis: " (itoa ok) " OK, " (itoa rest) " Fehler" "\n================================================"))) -(defun c:VF_SPEC_BAU ( / datei ziel specs records alt-dim) +;; Eine Spec-Datei bauen. Rueckgabe: Liste der Ergebnis-Records (nil, wenn +;; die Datei nicht lesbar war). Schreibt NICHTS - das entscheidet der +;; Aufrufer (der Befehl legt die Datei selbst ab, der Testrunner ueber +;; :export-results). Genutzt von c:VF_SPEC_BAU UND von +;; tests/test_hundm05.lsp, damit es nur EINEN Weg gibt, der Geometrie aus +;; einer Spec erzeugt. +(defun vsp-bau-datei (datei / specs records alt-dim) (vsp-modul-sichern "Gefaellestrecke") - (setq datei (getenv "DXFM_VF_SPEC")) - (if (or (null datei) (= datei "")) - (setq datei (strcat (getenv "DXFMAKRO") - "/tests/testdata/vf_spec_hundm05.json"))) - ;; Ergebnis-JSON gehoert nach tests/output - dort liegen alle - ;; Testergebnisse (Konvention test_run_all.lsp, von den - ;; pytest-Fixtures gelesen, per .gitignore ausgeschlossen). - ;; NICHT DXFM_RESULTS: das ist das Verzeichnis der Sivas-/CSV- - ;; Exporte, dort wuerde die Datei ausserhalb des Testbaums landen. - (setq ziel (getenv "DXFM_VF_SPEC_OUT")) - (if (or (null ziel) (= ziel "")) - (setq ziel (strcat (getenv "DXFMAKRO") "/tests/output"))) - (setq ziel (strcat ziel "/vf_spec_results.json")) - - (princ (strcat "\n================================================" - "\n VF_SPEC_BAU - " datei - "\n================================================")) (setq specs (vsp-json-laden datei)) - (if (null specs) - (princ "\n[VF_SPEC] Keine Specs gelesen - Abbruch.") - (progn + (cond + ((null specs) + (princ "\n[VF_SPEC] Keine Specs gelesen - Abbruch.") + nil) + (T (ssg-start "VF_SPEC_BAU" '(("OSMODE") ("CECOLOR") ("ATTREQ") ("ATTDIA"))) + ;; Ohne ATTREQ/ATTDIA 0 fragt jedes (command "_.INSERT" ...) nach + ;; Attributwerten - der wahrscheinlichste Haenger im Batch. (setvar "OSMODE" 0) (setvar "ATTREQ" 0) (setvar "ATTDIA" 0) @@ -873,10 +875,40 @@ (princ (strcat "\n[VF_SPEC] FEHLER in der Spec-Schleife: " (vl-catch-all-error-message records))) (setq records '()))) - (vl-mkdir (vl-filename-directory ziel)) - (vsp-results-schreiben records ziel) (vsp-bilanz records) - (ssg-end))) + (ssg-end) + records))) + +;; Standard-Spec-Datei: DXFM_VF_SPEC, sonst die HundM-Ketten. +(defun vsp-standard-datei ( / datei) + (setq datei (getenv "DXFM_VF_SPEC")) + (if (or (null datei) (= datei "")) + (setq datei (strcat (getenv "DXFMAKRO") "/tests/testdata/hundm05.json"))) + datei) + +;; Ergebnis-Datei zur Spec-Datei: gleicher Basisname, Endung _results.json, +;; im Testausgabe-Verzeichnis. Damit schreibt ein Lauf mit hundm05.json nach +;; hundm05_results.json - dieselbe Datei, die der Testrunner erwartet, also +;; keine zweite Kopie derselben Ergebnisse. +;; NICHT DXFM_RESULTS als Verzeichnis: das sind die Sivas-/CSV-Exporte, dort +;; wuerde die Datei ausserhalb des Testbaums landen. +(defun vsp-ziel-datei (spec-datei / verz) + (setq verz (getenv "DXFM_VF_SPEC_OUT")) + (if (or (null verz) (= verz "")) + (setq verz (strcat (getenv "DXFMAKRO") "/tests/output"))) + (strcat verz "/" (vl-filename-base spec-datei) "_results.json")) + +(defun c:VF_SPEC_BAU ( / datei ziel records) + (setq datei (vsp-standard-datei)) + (setq ziel (vsp-ziel-datei datei)) + (princ (strcat "\n================================================" + "\n VF_SPEC_BAU - " datei + "\n================================================")) + (setq records (vsp-bau-datei datei)) + (if records + (progn + (vl-mkdir (vl-filename-directory ziel)) + (vsp-results-schreiben records ziel))) (princ)) (princ "\n[vf_spec] geladen (VF_SPEC_BAU)") diff --git a/doc/TODO-plan-vf-interactive.md b/doc/TODO-plan-vf-interactive.md index 56da214..5ab3451 100644 --- a/doc/TODO-plan-vf-interactive.md +++ b/doc/TODO-plan-vf-interactive.md @@ -434,6 +434,53 @@ Zwei Nacharbeiten aus dem Lauf: `(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 +(`_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 diff --git a/doc/variofoerderer.md b/doc/variofoerderer.md index 0dca796..fd97fca 100644 --- a/doc/variofoerderer.md +++ b/doc/variofoerderer.md @@ -550,11 +550,11 @@ echten HundM-Ketten - er beweist, dass die Spec das Journal verlustfrei abbildet. Modus 2 und 3 haben noch keinen Uebersetzer (Modus 2 braucht erst echte `"linienzug2"`-Journale als Referenz). -Beispiel: `tests/test_hundm05.lsp` (5 echte Ketten aus `data/polylines.dxf`) +Beispiel: `tests/test_hm_recformat.lsp` (5 echte Ketten aus `data/polylines.dxf`) setzt alle drei Schalter mit Save/Restore, ersetzt `getpoint`/`getstring`/ `getint`/`getreal`/`alert` zusaetzlich durch **zaehlende** Stubs (Netz fuer Prompts ausserhalb der Wrapper) und schreibt `prompts`, `headless_fehler` und -`meldungen` in `tests/output/hundm05_results.json`. Noch nicht umgestellt: +`meldungen` in `tests/output/hm_recformat_results.json`. Noch nicht umgestellt: `vfl-konvertiere-ent` (Batch-2D/3D-Konvertierung) laeuft weiter ohne `*vfl-headless*` - dort ist eine Live-Rueckfrage bisher gewolltes Verhalten. diff --git a/lib/vf_journal_export.py b/lib/vf_journal_export.py index b10921d..03fe87b 100644 --- a/lib/vf_journal_export.py +++ b/lib/vf_journal_export.py @@ -35,9 +35,9 @@ Aufruf: python vf_journal_export.py [--csv ] [--kein-startpunkt-fix] -Beispiel (erzeugt tests/testdata/hundm05.json): +Beispiel (erzeugt tests/testdata/hm_recformat.json): python lib/vf_journal_export.py data/polylines.dxf \ - tests/testdata/hundm05.json --csv results/HundM_export.csv + tests/testdata/hm_recformat.json --csv results/HundM_export.csv """ import argparse @@ -57,6 +57,9 @@ TRENNER = "^J" RUND = 6 +# Anlagenkuerzel in den test_ids (VF__LZ_01 ...). +TEST_ID_ANLAGE = "hundm05" + # Antwort-Klartexte der Menues aus vf_linienzug.lsp (nur zur Annotation). JA_NEIN = {"1": "Ja", "2": "Nein"} SEITE = {"1": "links", "2": "rechts"} @@ -531,12 +534,17 @@ def baue_testfaelle(inserts, strecken, startpunkt_fix, quelle_datei, ssg-parse-json-array (Lisp/ssg_core.lsp) liest die Datei ZEILENWEISE und behandelt jedes "{" als Objektanfang. Ein verschachteltes Array wuerde die Kopf-Objekte verschlucken. Der Treiber gruppiert wieder ueber "test_id" - (siehe hundm05:gruppiere in tests/test_hundm05.lsp). + (siehe hmrec:gruppiere in tests/test_hm_recformat.lsp). """ - # Anlagenkuerzel der test_id kommt aus dem Namen der Ziel-JSON (die - # Testfall-Sammlung), nicht aus dem Zeichnungsnamen: tests/testdata/ - # hundm05.json -> VF_hundm05_LZ_01 .. _nn. - praefix = os.path.splitext(os.path.basename(ziel_datei))[0] + # Anlagenkuerzel der test_id: FEST, nicht aus dem Dateinamen + # abgeleitet. Die id benennt die ANLAGE, nicht die Datei - und sie + # muss stabil bleiben, weil dieselbe Kette in beiden Testlaeufen + # (Aufzeichnungsformat und Spec) denselben Namen tragen soll: + # tests/testdata/hundm05.json (die Spec) wird aus dieser Datei + # erzeugt und uebernimmt die ids. Frueher kam das Kuerzel aus dem + # Namen der Ziel-JSON; nach der Umbenennung in hm_recformat.json + # haette eine Regenerierung die ids stillschweigend geaendert. + praefix = TEST_ID_ANLAGE faelle = [] for nr, e in enumerate(sorted(inserts, key=lambda i: i["x"]), start=1): eintraege = Journal(e["journal_roh"]).dekodiere() diff --git a/lib/vf_spec_export.py b/lib/vf_spec_export.py index 607c82b..9801f83 100644 --- a/lib/vf_spec_export.py +++ b/lib/vf_spec_export.py @@ -29,8 +29,8 @@ lib/vf_journal_export.py (Journal.dekodiere) und ist dort kommentiert. Aufruf (erzeugt die Spec-Testdaten aus dem Protokoll der echten Ketten): - python lib/vf_spec_export.py tests/testdata/hundm05.json \ - tests/testdata/vf_spec_hundm05.json + python lib/vf_spec_export.py tests/testdata/hm_recformat.json \ + tests/testdata/hundm05.json """ import argparse @@ -111,7 +111,7 @@ def _ja_code(wert): # STR Menue-/Wertantwort STEP Glied-Marker (Label = Segmenttyp) def tokens_aus_protokoll(eintraege): - """Das Frage-Antwort-Protokoll (Schema tests/testdata/hundm05.json) + """Das Frage-Antwort-Protokoll (Schema tests/testdata/hm_recformat.json) zurueck in Journal-Tokens uebersetzen - die Umkehrung von vf_journal_export.Journal.add.""" tok = [] @@ -577,8 +577,8 @@ def journal_aus_spec(spec): # Flaches JSON (vom LISP-Parser lesbar) # ============================================================ # ssg-load-json/ssg-parse-json-array liest ZEILENWEISE und FLACH: jedes "{" -# beginnt ein Objekt, Verschachtelung ist unmoeglich. Also dieselbe Form wie -# tests/testdata/hundm05.json - Kopf-Objekt ("spec_id"), dann Sektionen +# beginnt ein Objekt, Verschachtelung ist unmoeglich. Also dieselbe flache +# Form wie die Aufzeichnung: ein Kopf-Objekt ("spec_id"), dann Sektionen # ("glied") und deren Sub-Knoten ("sub") in Reihenfolge. def flache_objekte(spec_id, spec, kopf_extra=None): @@ -626,7 +626,7 @@ def flache_objekte(spec_id, spec, kopf_extra=None): def spec_aus_flachen_objekten(objekte): """Umkehrung von flache_objekte - dieselbe Gruppierregel wie - hundm05:gruppiere in LISP (Kopf, dann glied/sub in Reihenfolge).""" + hmrec:gruppiere in LISP (Kopf, dann glied/sub in Reihenfolge).""" specs = [] for obj in objekte: if "spec_id" in obj: @@ -680,9 +680,9 @@ def spec_aus_flachen_objekten(objekte): def main(): p = argparse.ArgumentParser( - description="Frage-Antwort-Protokoll (hundm05.json) in Specs " + description="Frage-Antwort-Protokoll (hm_recformat.json) in Specs " "uebersetzen und den Rundlauf pruefen.") - p.add_argument("protokoll", help="Eingabe (Schema hundm05.json)") + p.add_argument("protokoll", help="Eingabe (Schema hm_recformat.json)") p.add_argument("ziel", help="Ausgabe (flaches Spec-JSON)") args = p.parse_args() @@ -720,8 +720,11 @@ def main(): "planquadrat", "block", "dxf_handle", "quelle"): if feld in kopf: extra[feld] = kopf[feld] - alle.extend(flache_objekte(tid.replace("hundm05_LZ", "spec_LZ"), - spec, extra)) + # Die spec_id bleibt die Kettenkennung aus dem Protokoll: dieselbe + # Kette soll in beiden Testlaeufen (Spec-Pfad und Aufzeichnungs- + # Pfad) denselben Namen tragen, sonst laesst sich das Ergebnis + # nicht paaren. + alle.extend(flache_objekte(tid, spec, extra)) print("%s %-20s %2d Sektionen, %3d Tokens - Rundlauf ok" % (PREFIX, tid, len(spec["sektionen"]), len(tok))) diff --git a/tests/README.md b/tests/README.md index 6f81bc4..eabfd4e 100644 --- a/tests/README.md +++ b/tests/README.md @@ -102,7 +102,7 @@ Die Datei `tests/alltests.json` ist die zentrale Registry aller Test-Module. { "name": "omniflo_strecke", "save": "dxf", "module": "OmniModulInsert" }, { "name": "gefaellestrecke", "save": "dwg", "module": "Gefaellestrecke" }, { "name": "mubea", "save": "dxf", "module": "KreiselInsert" }, - { "name": "hundm05", "save": "dxf", "module": "VarioFoerderer", "disabled": true } + { "name": "hundm05", "save": "dxf", "module": "VarioFoerderer" } ] ``` @@ -160,7 +160,16 @@ tests/ test_foerderer.py # pytest-Validierung Foerderer (Status/Mathematik/CSV/Sivas) test_gefaellestrecke.py # pytest-Validierung Gefaellestrecke test_mubea.py # pytest-Validierung Mubea-Gesamtmodell - test_hundm05.py # pytest-Validierung HundM05 (5 Linienzuege) + test_hundm05.lsp # LISP: baut die 5 HundM-Ketten aus ihrer + # Spec (testdata/hundm05.json) - ruft + # vsp-bau-datei, keine eigene Bau-Logik + test_hundm05.py # pytest: die so gebaute Anlage pruefen + test_hm_recformat.lsp # LISP: dieselben Ketten aus dem ROHEN + # Journal der Kundenzeichnung - der + # Regressionstest fuers Aufzeichnungs- + # format (in alltests.json abgeschaltet, + # baut sonst dieselbe Geometrie zweimal) + test_hm_recformat.py # pytest-Validierung dazu test_vf_spec.lsp # LISP-Unittest: Spec-Uebersetzer (vsp-journal), # je Glied-Typ + die 5 echten HundM-Ketten # gegen ihr aufgezeichnetes Journal @@ -179,7 +188,9 @@ tests/ kreisel_tests.json # Testfall-Definitionen fuer Kreisel omniflo_tests.json # Testfall-Definitionen fuer Omniflo omniflo_strecke_tests.json # Streckenzug: 4 Geraden + 2 Boegen + 1 Weiche (7 Elemente) - foerderer_tests.json / mubea.json / hundm05.json / ... + hm_recformat.json # Aufzeichnung: rohes Journal der 5 Ketten + hundm05.json # daraus abgeleitete Spec (wird erweitert) + foerderer_tests.json / mubea.json / ... output/ # Ergebnisse aus BricsCAD (nicht in Git) _results.json # JSON-Ergebnisse pro Testmodul (immer) _tests.dxf/.dwg # Zeichnung pro Testmodul (gemaess save in alltests.json) diff --git a/tests/alltests.json b/tests/alltests.json index ef4437b..2ff6624 100644 --- a/tests/alltests.json +++ b/tests/alltests.json @@ -8,5 +8,6 @@ { "name": "omniflo_strecke", "save": "dxf", "module": "OmniModulInsert" }, { "name": "gefaellestrecke", "save": "dwg", "module": "Gefaellestrecke" }, { "name": "mubea", "save": "dxf", "module": "KreiselInsert" }, - { "name": "hundm05", "save": "dxf", "module": "VarioFoerderer", "disabled": true } + { "name": "hundm05", "save": "dxf", "module": "VarioFoerderer" }, + { "name": "hm_recformat", "save": "dxf", "module": "VarioFoerderer", "disabled": true } ] diff --git a/tests/conftest.py b/tests/conftest.py index 1d8e8c6..8f5f53a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -189,35 +189,37 @@ def mubea_dxf(): return ezdxf.readfile(path) -# --- HundM05 Fixtures --- +# --- Aufzeichnungsformat-Fixtures (TEST_HM_RECFORMAT) --- +# hm_recformat.json ist das ROHE Eingabe-Journal der 5 HundM-Ketten, +# ausgelesen aus der XDATA der Kundenzeichnung (lib/vf_journal_export.py). +# Die daraus abgeleitete Spec liegt als testdata/hundm05.json - die Fixtures +# dafuer stehen in tests/test_hundm05.py. @pytest.fixture -def hundm05_testdata(): - """Laedt die HundM05-Testdefinitionen aus testdata/hundm05.json. - - Erzeugt von lib/dxf_scan_components.py aus results/ST500592_05.dxf - (Fenster 216000 144000 228000 156000, Kreisel-Abschnitt). - """ - path = os.path.join(_testdata_dir(), "hundm05.json") +def hmrec_testdata(): + """Laedt das Frage-Antwort-Protokoll aus testdata/hm_recformat.json.""" + path = os.path.join(_testdata_dir(), "hm_recformat.json") return _load_json(path) @pytest.fixture -def hundm05_results(): - """Laedt die HundM05-Ergebnisse aus output/hundm05_results.json.""" - path = os.path.join(_output_dir(), "hundm05_results.json") +def hmrec_results(): + """Laedt die Ergebnisse aus output/hm_recformat_results.json.""" + path = os.path.join(_output_dir(), "hm_recformat_results.json") if not os.path.exists(path): - pytest.skip("hundm05_results.json nicht vorhanden - TEST_HUNDM05 in BricsCAD ausfuehren") + pytest.skip("hm_recformat_results.json nicht vorhanden - " + "TEST_HM_RECFORMAT in BricsCAD ausfuehren") data = _load_json(path) if not data: - pytest.skip("hundm05_results.json ist leer") + pytest.skip("hm_recformat_results.json ist leer") return data @pytest.fixture -def hundm05_dxf(): - """Laedt die erzeugte HundM05-DXF aus output/hundm05_tests.dxf.""" - path = os.path.join(_output_dir(), "hundm05_tests.dxf") +def hmrec_dxf(): + """Laedt die erzeugte DXF aus output/hm_recformat_tests.dxf.""" + path = os.path.join(_output_dir(), "hm_recformat_tests.dxf") if not os.path.exists(path): - pytest.skip("hundm05_tests.dxf nicht vorhanden - TESTRUN (save:dxf) in BricsCAD ausfuehren") + pytest.skip("hm_recformat_tests.dxf nicht vorhanden - TESTRUN " + "(save:dxf) in BricsCAD ausfuehren") return ezdxf.readfile(path) diff --git a/tests/test_hm_recformat.lsp b/tests/test_hm_recformat.lsp new file mode 100644 index 0000000..bbd5972 --- /dev/null +++ b/tests/test_hm_recformat.lsp @@ -0,0 +1,683 @@ +;; ============================================================ +;; test_hm_recformat.lsp - Regressionstest fuer das AUFZEICHNUNGSFORMAT +;; (rec = recorded): baut die 5 VarioFoerderer-Linienzuege der Anlage HundM +;; aus den Journalen nach, die in der Kundenzeichnung stehen. +;; +;; ABGRENZUNG zu TEST_HUNDM05 (tests/test_hundm05.lsp): dort werden dieselben +;; 5 Ketten aus einer SPEC gebaut (tests/testdata/hundm05.json, das neue +;; Eingabeformat) - das ist der Test fuer den Bau-Pfad und der, der +;; routinemaessig laeuft. HIER geht es um etwas anderes: das Journal kommt +;; roh aus der XDATA einer echten Zeichnung. Damit ist dies der einzige Test, +;; der belegt, dass ein BESTEHENDER VF_n-Block weiter abspielbar bleibt - +;; also dass Doppelklick-Editieren und 2D/3D-Konvertierung an Altbestand +;; funktionieren (Invariante I1 in doc/TODO-plan-vf-interactive.md). Ein +;; spec-gebautes Journal kann das nicht zeigen, es kommt aus dem Uebersetzer. +;; +;; Darum in tests/alltests.json auf "disabled": true - er baut dieselbe +;; Geometrie wie TEST_HUNDM05 und kostet doppelte CAD-Zeit. Bei Aenderungen +;; am Journal-Format, an den vfl-in-*-Wrappern oder am XDATA-Layout aber +;; bewusst einschalten: dann ist er der Test, der zaehlt. +;; +;; Datenquelle: tests/testdata/hm_recformat.json, erzeugt von +;; python lib/vf_journal_export.py data/polylines.dxf \ +;; tests/testdata/hm_recformat.json --csv results/HundM_export.csv +;; Das sind die EINGABE-JOURNALE der echten Ketten - ausgelesen aus der XDATA +;; (App SSG_VF_EDIT, Marker "linienzug") der VF_n-Bloecke in data/polylines.dxf, +;; also genau die Werte, mit denen die Ketten in BricsCAD gebaut wurden. +;; +;; JSON-Aufbau (FLACH, weil ssg-load-json zeilenweise liest - Details in +;; tests/testdata/object_data.md Abschnitt 8): +;; Kopf-Objekt (traegt "test_id"): test_id, anlagetyp, modus, +;; anzahl_eingaben, block, dxf_handle, startpunkt_mm, +;; rotation_grad, beschreibung, sivas_* , erwartung_hinweis +;; Eingabe-Objekt (traegt "typ"): point_abs | point_rel | real | int | +;; string | step - in exakter Frage-Reihenfolge +;; Ein Kopf-Objekt beginnt eine Kette, die folgenden Eingabe-Objekte gehoeren +;; dazu (hmrec:gruppiere). +;; +;; WARUM REPLAY UND KEINE EINGABE-MOCKS (Unterschied zu test_linienzug.lsp): +;; test_linienzug.lsp ersetzt getpoint/getstring/getint/getreal durch Mocks und +;; rechnet Punkte aus "dL"+"hz" selbst aus. Das geht nur, weil +;; linienzug_tests.json zu JEDEM Segment ein "hz" mitfuehrt. Ein echtes Journal +;; tut das nicht: vfl-in-abstand journalisiert die Fahrtrichtung NUR beim +;; allerersten Segment der Kette, jedes weitere erbt sie vom Vorgaenger. Ein +;; Mock kann diese geerbte Richtung nicht kennen - er wuerde einen Punkt in der +;; falschen Richtung liefern und die Projektion in vfl-neue-linie-messen ergaebe +;; eine falsche (bis auf 0 zusammenfallende) Laenge. Darum wird hier derselbe +;; Weg genommen, den die Produktion beim Editieren/Konvertieren nutzt: +;; vfl-journal-replay-start + vf-linienzug-modus (siehe vfl-konvertiere-ent in +;; Lisp/vf_linienzug.lsp). Die vfl-in-*-Wrapper ziehen ihre Werte dann direkt +;; aus der Replay-Queue - inklusive der Sonderregel fuer "hz". +;; +;; Voraussetzungen: +;; - SSG_LIB geladen (VarioFoerderer inkl. vf_linienzug, Gefaellestrecke, +;; ssg_core, ssg_dbg) +;; - Umgebungsvariable DXFMAKRO gesetzt +;; +;; Speichert (via SSG_RUN_ALL_TESTS bei "save":"dxf"): +;; tests/output/hm_recformat_tests.dxf +;; tests/output/hm_recformat_results.json (via hm_recformat:export-results) +;; +;; Aufruf in BricsCAD: +;; (load (strcat (getenv "DXFMAKRO") "/tests/test_hm_recformat.lsp")) +;; TEST_HM_RECFORMAT ; in der aktuellen Dimension (ssg-ils-dim-aktuell) +;; TEST_HM_RECFORMAT_2D ; erzwungen 2D +;; TEST_HM_RECFORMAT_3D ; erzwungen 3D +;; +;; Debug-Datei (nur wenn eingeschaltet): (dbg-schalter-on "hm_recformat") +;; schreibt hm_recformat.dbg ins DXFM_LOG-Verzeichnis (Journal je Kette als JSON). +;; ============================================================ + + +;; ============================================================ +;; Ergebnis-JSON +;; ============================================================ + +;; --- JSON-Hilfen fuer Diagnosefelder (Strings/String-Listen) --- +;; Die extra-Felder in hmrec:result-json werden ROH in das JSON gesetzt, +;; ein Text muss also selbst seine Anfuehrungszeichen mitbringen und die +;; JSON-Sonderzeichen maskieren. +(defun hmrec:json-escape (s / out i c) + (setq out "" i 1) + (while (<= i (strlen s)) + (setq c (substr s i 1)) + (setq out (cond ((= c "\"") (strcat out "\\\"")) + ((= c "\\") (strcat out "\\\\")) + ((= c "\n") (strcat out " ")) + (T (strcat out c)))) + (setq i (1+ i))) + out) + +(defun hmrec:json-str (s) + (if s (strcat "\"" (hmrec:json-escape s) "\"") "null")) + +(defun hmrec:json-liste (lst / s first) + (setq s "[" first T) + (foreach x lst + (if (not first) (setq s (strcat s ", "))) + (setq s (strcat s (hmrec:json-str x)) first nil)) + (strcat s "]")) + +;; --- Headless-Diagnose der Produktion lesen --- +;; vfl-headless-abbruch (Lisp/vf_linienzug.lsp) legt bei erschoepfter +;; Replay-Queue Art der Eingabe, Glied-Nummer und Eingabe-Nummer ab. Das ist +;; die eigentliche Desync-Auskunft - ohne sie weiss man nur, DASS Werte offen +;; blieben, nicht WO der Ablauf abgewichen ist. +(defun hmrec:headless-fehler-text ( / f) + (if (and (boundp '*vfl-headless-fehler*) *vfl-headless-fehler*) + (progn + (setq f *vfl-headless-fehler*) + (strcat (cdr (assoc "was" f)) " @ " (cdr (assoc "ort" f)))) + nil)) + +;; Zaehler der trotz allem erreichten Live-Eingaben (muss 0 bleiben). +(defun hmrec:prompts () + (if (and (boundp '*hmrec-prompts*) *hmrec-prompts*) *hmrec-prompts* 0)) + + +;; "dimension" haelt fest, in welcher Dimension (2D/3D) gebaut wurde. Das ist +;; KEINE Eigenschaft der Eingabedaten: hm_recformat.json beschreibt die Ketten +;; dimensionsfrei. Womit unsere Makros bauen, entscheidet ssg-ils-dim-aktuell - +;; unsere Bibliothek hat von jedem Block eine _2D- und eine _3D-Variante. +;; extra: Alist zusaetzlicher Felder ((name . wert-string)), wird als +;; JSON-Zahlen/Strings so uebernommen, wie sie uebergeben werden. +(defun hmrec:result-json (test-id kind status block-name block-handle + insert-point attribs extra / + json tag val first) + (setq json (strcat " {\n" + " \"test_id\": \"" test-id "\",\n" + " \"kind\": \"" kind "\",\n" + " \"status\": \"" status "\",\n" + " \"dimension\": \"" + (if (and (boundp '*hmrec-dimension*) *hmrec-dimension*) + *hmrec-dimension* "?") "\",\n" + " \"block_name\": \"" (if block-name block-name "") "\",\n" + " \"block_handle\": \"" (if block-handle block-handle "") "\",\n" + " \"insert_point\": [" + (if insert-point + (strcat (rtos (car insert-point) 2 1) ", " + (rtos (cadr insert-point) 2 1) ", " + (rtos (if (caddr insert-point) (caddr insert-point) 0.0) 2 1)) + "0.0, 0.0, 0.0") + "],\n")) + (foreach f extra + (setq json (strcat json " \"" (car f) "\": " (cdr f) ",\n"))) + (setq json (strcat json " \"actual_attributes\": {")) + (setq first T) + (foreach att attribs + (setq tag (car att) val (cdr att)) + (if (not first) (setq json (strcat json ","))) + (setq json (strcat json "\n \"" tag "\": \"" (if val val "") "\"")) + (setq first nil)) + (setq json (strcat json "\n }\n }")) + json +) + + +;; --- Aus einer Block-ENAME ein Ergebnis-JSON erzeugen --- +;; status: "executed" bei sauberem Lauf; ein abweichender Status (z.B. bei +;; einem Queue-Desync) wird durchgereicht, die Block-Angaben bleiben dabei +;; erhalten - sonst waere der betroffene Block im Ergebnis nicht auffindbar. +(defun hmrec:ent-json-status (test-id kind status ent extra / ed) + (if ent + (progn + (setq ed (entget ent)) + (hmrec:result-json test-id kind status + (cdr (assoc 2 ed)) (cdr (assoc 5 ed)) (cdr (assoc 10 ed)) + (ssg-attrib-read ent) extra)) + (hmrec:result-json test-id kind "failed" nil nil nil nil extra)) +) + +(defun hmrec:ent-json (test-id kind ent extra) + (hmrec:ent-json-status test-id kind "executed" ent extra) +) + + +;; --- Letzten INSERT mit Blocknamen-Praefix suchen --- +;; AutoLISP kennt KEIN entprev (nur entnext und entlast) - rueckwaerts durch +;; die Zeichnung laufen geht also nicht. Gesucht wird darum VORWAERTS, wobei +;; der letzte Treffer gewinnt. Ein blosses (entlast) genuegt nicht: nach dem +;; Bau koennen weitere Objekte entstehen (z.B. Beschriftungstexte), entlast +;; ist dann nicht der gesuchte Block - genau daran scheiterte der Testlauf. +;; ab-ent = Stand der Zeichnung VOR dem Bau. Damit prueft die Suche nur die +;; neu entstandenen Objekte: schnell, und sie kann nicht versehentlich den +;; Block eines frueheren Baus liefern (siehe Kommentar bei -ab). +(defun hmrec:insert-prefix-p (ent pref / ed bn) + (setq ed (entget ent) bn (cdr (assoc 2 ed))) + (and ed (= (cdr (assoc 0 ed)) "INSERT") bn + (>= (strlen bn) (strlen pref)) + (= (substr bn 1 (strlen pref)) pref))) + +;; Vorwaertslauf ab ab-ent (nil = ganze Zeichnung); der LETZTE Treffer +;; gewinnt. Der entget-Test faengt ein inzwischen ungueltiges ab-ent ab +;; (entget liefert dann nil, entnext wuerde einen Fehler werfen). +(defun hmrec:suche-vorwaerts (pref ab-ent / ent found) + (setq ent (if (and ab-ent (entget ab-ent)) (entnext ab-ent) (entnext))) + (while ent + (if (hmrec:insert-prefix-p ent pref) (setq found ent)) + (setq ent (entnext ent))) + found) + +(defun hmrec:last-insert-prefix-ab (pref ab-ent / ent) + (if ab-ent + ;; MIT Marker nur die neu entstandenen Objekte pruefen - und BEWUSST + ;; ohne entlast-Kurzschluss: hat dieser Bau keinen Block erzeugt, waere + ;; entlast der Block eines FRUEHEREN Baus und das Ergebnis wuerde der + ;; falschen Kette zugeordnet. Kein Treffer = kein Block, das ist die + ;; richtige Antwort. + (hmrec:suche-vorwaerts pref ab-ent) + (progn + ;; Ohne Marker: erst entlast (der haeufige Fall, das gerade + ;; eingefuegte Objekt), sonst die ganze Zeichnung. + (setq ent (entlast)) + (if (and ent (hmrec:insert-prefix-p ent pref)) + ent + (hmrec:suche-vorwaerts pref nil))))) + +(defun hmrec:last-insert-prefix (pref) + (hmrec:last-insert-prefix-ab pref nil)) + + +;; ============================================================ +;; JSON-Eintraege -> Ketten +;; ============================================================ + +;; Das flache JSON-Array in Ketten gruppieren: ein Objekt mit "test_id" ist +;; ein Kopf und beginnt eine neue Kette, die folgenden Objekte mit "typ" sind +;; ihre Eingaben (Reihenfolge = Frage-Reihenfolge, bleibt erhalten). +;; Rueckgabe: Liste von (kopf eingaben-liste). +(defun hmrec:gruppiere (daten / ketten kopf eingaben) + (setq ketten '() kopf nil eingaben '()) + (foreach obj daten + (cond + ((ssg-val obj "test_id") + (if kopf + (setq ketten (cons (list kopf (reverse eingaben)) ketten))) + (setq kopf obj eingaben '())) + ((ssg-val obj "typ") + (if kopf (setq eingaben (cons obj eingaben)))) + ) + ) + (if kopf (setq ketten (cons (list kopf (reverse eingaben)) ketten))) + (reverse ketten) +) + + +;; --- Wert als String lesen (auch wenn er als Zahl im JSON steht) --- +;; Die Menue-Antworten sind Strings ("1"/"2"/"90"/"30"). Steht in einer von +;; Hand gepflegten Datei versehentlich 1 statt "1", wuerde ein Zahlenwert in +;; die Replay-Queue wandern und der Vergleich (= antwort "1") schlagen fehl - +;; darum hier immer nach String wandeln. +(defun hmrec:als-string (v) + (cond + ((null v) "") + ((= (type v) 'STR) v) + ((= (type v) 'INT) (itoa v)) + ((= (type v) 'REAL) (rtos v 2 6)) + (T "") + ) +) + + +;; --- Einen Eingabe-Eintrag in Journal-Eintraege uebersetzen --- +;; Journal-Format (vfl-entry->string in Lisp/vf_linienzug.lsp): +;; ("PT" x y z) / ("REAL" . r) / ("DL" . r) / ("INT" . i) / ("STR" . s) / +;; ("STEP" . label) +;; Ein "point_rel" wird zu EINEM oder ZWEI Eintraegen: immer die Laenge (DL), +;; zusaetzlich die Fahrtrichtung (REAL) nur dort, wo sie im Journal steht +;; (allererstes Segment der Kette, siehe Kopfkommentar). +;; Rueckgabe: Liste von Journal-Eintraegen (kann leer sein). +(defun hmrec:eintrag->journal (e / typ w hz) + (setq typ (ssg-val e "typ")) + (cond + ((= typ "point_abs") + (setq w (ssg-val e "wert")) + (if (and w (listp w) (>= (length w) 3)) + (list (cons "PT" (mapcar 'float w))) + nil)) + ((= typ "point_rel") + (setq w (ssg-val e "dL") hz (ssg-val e "hz")) + (cond + ((not (numberp w)) nil) + ((numberp hz) (list (cons "DL" (float w)) (cons "REAL" (float hz)))) + (T (list (cons "DL" (float w)))))) + ((= typ "real") + (setq w (ssg-val e "wert")) + (if (numberp w) (list (cons "REAL" (float w))) nil)) + ((= typ "int") + (setq w (ssg-val e "wert")) + (if (numberp w) (list (cons "INT" (fix w))) nil)) + ((= typ "string") + (list (cons "STR" (hmrec:als-string (ssg-val e "wert"))))) + ((= typ "step") + (list (cons "STEP" (hmrec:als-string (ssg-val e "wert"))))) + (T nil) + ) +) + + +;; --- Alle Eingaben einer Kette in ein Vorwaerts-Journal uebersetzen --- +;; Vorwaerts = Bau-Reihenfolge, genau so erwartet es vfl-journal-replay-start. +(defun hmrec:journal-bauen (eingaben / out) + (setq out '()) + (foreach e eingaben + (foreach j (hmrec:eintrag->journal e) + (setq out (cons j out)))) + (reverse out) +) + + +;; --- Nicht verbrauchte Werte in der Replay-Queue zaehlen --- +;; STEP-Marker zaehlen nicht mit: vfl-replay-pop ueberspringt sie, sie sind +;; also kein offener Wert. Bleibt hier etwas uebrig, ist der Bau-Ablauf vom +;; aufgezeichneten abgewichen (Desync) - das MUSS auffallen. +(defun hmrec:queue-rest ( / n) + (setq n 0) + (if (and (boundp '*vfl-replay-queue*) *vfl-replay-queue*) + (foreach e *vfl-replay-queue* + (if (/= (car e) "STEP") (setq n (1+ n))))) + n +) + + +;; ============================================================ +;; Eine Kette bauen +;; ============================================================ + +;; Spielt das Journal einer Kette ab. Ablauf wie vfl-konvertiere-ent +;; (Lisp/vf_linienzug.lsp): Queue setzen, vf-linienzug-modus laufen lassen, +;; danach den entstandenen VF_n-Block einsammeln. +;; +;; vl-catch-all-apply ist hier BEWUSST gesetzt (anders als im interaktiven +;; Editier-Pfad vfl-edit-ent): bricht eine Kette ab, muss die Testschleife +;; ueber die restlichen Ketten weiterlaufen. Der Preis ist derselbe wie beim +;; Batch-Konverter - der *error*-Handler in vf-linienzug-modus (der die +;; Teil-Geometrie zu einem Block wickelt) kommt dann nicht zum Zug und die +;; Teilgeometrie bleibt lose in der Zeichnung liegen. Der Testfall wird darum +;; klar als "failed" samt Fehlertext gemeldet. +;; *error* wird um den Aufruf herum gesichert: nach einem abgefangenen Abbruch +;; steht dort noch der Handler-Lambda von vf-linienzug-modus (es kommt nicht +;; mehr zu dessen (setq *error* old-error)) - ohne Restore wuerde die naechste +;; Kette auf einem Handler mit toten dynamischen Bindungen aufsetzen. +(defun hmrec:build-linienzug (kopf eingaben / tid journal anz offen err ent + alt-error extra hl-fehler meldungen vor-ent) + (setq tid (ssg-val kopf "test_id")) + (setq journal (hmrec:journal-bauen eingaben)) + (setq anz (length journal)) + (princ (strcat "\n [LINIENZUG] " tid " -> " (itoa (length eingaben)) + " Eingaben, " (itoa anz) " Journal-Eintraege")) + (if (ssg-val kopf "beschreibung") + (princ (strcat "\n " (ssg-val kopf "beschreibung")))) + (if (and (boundp '*hmrec-dbg*) *hmrec-dbg*) + (progn + (dbgmsg (strcat "KETTE " tid " JOURNAL " (vfl-journal->json journal))) + (dbgflush))) + + (cond + ;; Ohne Journal (oder ohne Startpunkt an erster Stelle) nichts bauen: ein + ;; Replay ohne PT wuerde den Startpunkt interaktiv erfragen und die ganze + ;; Kette an der falschen Stelle aufbauen. + ((null journal) + (princ (strcat "\n FEHLER: " tid " - keine Eingaben lesbar." + " Stehen die Zahlen-Arrays in EINER Zeile?" + " (ssg-cfg-parse-array kann keine umgebrochenen lesen)")) + (hmrec:result-json tid "linienzug" "failed" nil nil nil nil + (list (cons "eingaben_gesamt" (itoa anz)) + (cons "eingaben_offen" (itoa anz))))) + + ((/= (car (car journal)) "PT") + (princ (strcat "\n FEHLER: " tid " - erster Eintrag ist kein" + " Startpunkt (point_abs), nichts gebaut.")) + (hmrec:result-json tid "linienzug" "failed" nil nil nil nil + (list (cons "eingaben_gesamt" (itoa anz)) + (cons "eingaben_offen" (itoa anz))))) + + (T + ;; Stand der Zeichnung VOR dem Bau: der fertige VF_-Block wird danach + ;; ab hier gesucht (siehe hmrec:last-insert-prefix-ab). + (setq vor-ent (entlast)) + (setq alt-error *error*) + (vfl-journal-reset) + (vfl-journal-replay-start journal) + (setq err (vl-catch-all-apply 'vf-linienzug-modus '())) + (setq offen (hmrec:queue-rest)) + ;; Diagnose VOR dem Reset sichern: vfl-journal-reset loescht + ;; *vfl-headless-fehler* und *vfl-meldungen* mit (beide gehoeren zum + ;; einzelnen Lauf, nicht zur Sitzung). + (setq hl-fehler (hmrec:headless-fehler-text)) + (setq meldungen (if (boundp '*vfl-meldungen*) *vfl-meldungen* nil)) + (setq *error* alt-error) + (vfl-journal-reset) + (setq extra (list (cons "eingaben_gesamt" (itoa anz)) + (cons "eingaben_offen" (itoa offen)) + (cons "prompts" (itoa (hmrec:prompts))) + (cons "headless_fehler" (hmrec:json-str hl-fehler)) + (cons "meldungen" (hmrec:json-liste meldungen)))) + (cond + ;; Der Headless-Riegel der Produktion hat gegriffen: die Journal-Daten + ;; passen nicht zum tatsaechlichen Bau-Ablauf. Praeziser als "failed", + ;; weil hl-fehler Glied UND Eingabe-Nummer nennt. + (hl-fehler + (princ (strcat "\n DESYNC: " tid " - " hl-fehler)) + (hmrec:ent-json-status tid "linienzug" "desync" + (hmrec:last-insert-prefix-ab "VF_" vor-ent) extra)) + ((vl-catch-all-error-p err) + (princ (strcat "\n FEHLER: " tid " abgebrochen -> " + (vl-catch-all-error-message err))) + (hmrec:result-json tid "linienzug" "failed" nil nil nil nil extra)) + ;; Kette gebaut, aber der Ablauf hat nicht alle aufgezeichneten Werte + ;; abgerufen: die Geometrie kann nicht der Vorlage entsprechen. + ((> offen 0) + (princ (strcat "\n FEHLER: " tid " - " (itoa offen) + " Journal-Werte NICHT verbraucht (Ablauf weicht" + " von der Aufzeichnung ab).")) + (hmrec:ent-json-status tid "linienzug" "desync" + (hmrec:last-insert-prefix-ab "VF_" vor-ent) extra)) + (T + (setq ent (hmrec:last-insert-prefix-ab "VF_" vor-ent)) + (if ent + (princ (strcat " -> Block " (cdr (assoc 2 (entget ent))))) + (princ "\n FEHLER: kein VF_-Block gefunden")) + (hmrec:ent-json tid "linienzug" ent extra))))) +) + + +;; ============================================================ +;; Headless-Absicherung: Dialoge aus, leere Queue bricht ab +;; ============================================================ +;; Die Journale sind vollstaendig, es darf also keine Live-Eingabe geben. +;; Falls doch (Desync), wuerde ein getpoint/getstring den Testlauf blockieren +;; und ein alert-Fenster ihn haengen lassen. Darum werden die Eingabe- +;; Funktionen fuer die Dauer des Tests durch Abbruch-Stubs ersetzt (liefern +;; nil = Abbruch, was vf-linienzug-modus sauber beendet) und alert auf die +;; Konsole umgeleitet. Alles mit Save/Restore, siehe hmrec:stubs-aus. +;; Die Stubs haben feste 1- bzw. 2-Arity (AutoLISP kennt keine optionalen +;; Parameter) - genau wie die Mocks in test_linienzug.lsp. Ruft der Bau-Ablauf +;; eine der Funktionen mit anderer Argumentzahl auf, gibt es einen +;; Argumentfehler statt einer Warnung; beides landet im vl-catch-all-apply um +;; vf-linienzug-modus und meldet die Kette als "failed". Wichtig ist nur, dass +;; kein Prompt und kein Dialog den Lauf blockiert. +(defun hmrec:stub-warnung (was) + (setq *hmrec-prompts* (1+ (hmrec:prompts))) + (princ (strcat "\n WARNUNG: unerwartete Live-Eingabe (" was + ") - Replay-Queue leer, Kette wird abgebrochen.")) + nil +) +(defun hmrec:stub-getpoint (a b) (hmrec:stub-warnung "getpoint")) +(defun hmrec:stub-getstring (a) (hmrec:stub-warnung "getstring")) +(defun hmrec:stub-getint (a) (hmrec:stub-warnung "getint")) +(defun hmrec:stub-getreal (a) (hmrec:stub-warnung "getreal")) +(defun hmrec:stub-alert (msg) + (princ (strcat "\n ALERT (unterdrueckt): " (if msg msg ""))) + (princ) +) + +(defun hmrec:stubs-an ( / ) + (setq *hmrec-alt-getpoint* vfl-getpoint + *hmrec-alt-getstring* getstring + *hmrec-alt-getint* getint + *hmrec-alt-getreal* getreal + *hmrec-alt-alert* alert) + (setq vfl-getpoint hmrec:stub-getpoint + getstring hmrec:stub-getstring + getint hmrec:stub-getint + getreal hmrec:stub-getreal + alert hmrec:stub-alert) + ;; Wizard-Dialoge aus (waehrend eines Replays greifen sie ohnehin nicht, + ;; siehe vfl-wizard-aktiv - aber nach einem Desync wuerde ein Dialog + ;; aufgehen und warten). + (setq *hmrec-alt-wizard* (if (boundp '*vfl-wizard-mode*) *vfl-wizard-mode*)) + (setq *vfl-wizard-mode* nil) + (if (car (atoms-family 1 '("SSG-GUI-AUS"))) (ssg-gui-aus)) + ;; Headless-Riegel der Produktion einschalten: eine erschoepfte Replay-Queue + ;; ist dann ein harter, lokalisierter Abbruch statt eines stillen Rueckfalls + ;; auf Live-Eingabe (siehe vfl-headless-abbruch). Die Stubs oben bleiben als + ;; zweites Netz - sie fangen die Prompts ausserhalb der vfl-in-*-Wrapper + ;; (Modus 3, Nebenpfade) und zaehlen sie. + (setq *hmrec-alt-headless* (if (boundp '*vfl-headless*) *vfl-headless*)) + (setq *vfl-headless* T) + (setq *hmrec-prompts* 0) + (princ) +) + +(defun hmrec:stubs-aus ( / ) + (setq vfl-getpoint *hmrec-alt-getpoint* + getstring *hmrec-alt-getstring* + getint *hmrec-alt-getint* + getreal *hmrec-alt-getreal* + alert *hmrec-alt-alert*) + (setq *vfl-wizard-mode* *hmrec-alt-wizard*) + (setq *vfl-headless* *hmrec-alt-headless*) + (if (car (atoms-family 1 '("SSG-GUI-AN"))) (ssg-gui-an)) + (princ) +) + + +;; ============================================================ +;; Alle Ketten bauen +;; ============================================================ +;; Eigene Funktion, damit der Aufrufer sie in vl-catch-all-apply klammern und +;; die Eingabe-Stubs GARANTIERT zuruecknehmen kann (ein hier entkommender +;; Fehler wuerde sonst getstring/getpoint/alert der laufenden BricsCAD-Sitzung +;; ersetzt zuruecklassen). +;; Rueckgabe: Ergebnis-Liste in Bau-Reihenfolge. +(defun hmrec:ketten-bauen (ketten / kette res out tid fehlertext) + (setq out '()) + (foreach kette ketten + ;; Dimensions-Override je Kette neu setzen: der Abbruch-Handler in + ;; vf-linienzug-modus setzt *ssg-ils-dim* zurueck, ein Abbruch wuerde die + ;; folgenden Ketten sonst in der falschen Dimension bauen. + (if (and (boundp '*hmrec-dim-override*) *hmrec-dim-override*) + (setq *ssg-ils-dim* *hmrec-dim-override*)) + ;; Je Kette gefangen: ein Fehler NACH dem Bau (z.B. beim Auswerten des + ;; Ergebnisses) darf nicht die restlichen Ketten mitnehmen - sonst steht + ;; am Ende "0 OK, 0 Fehler" und man sieht nicht einmal, welche Kette + ;; gebaut wurde. Der Bau selbst ist in hmrec:build-linienzug bereits + ;; separat gefangen; dieser Riegel hier deckt alles danach ab. + (setq tid (ssg-val (car kette) "test_id")) + (setq res (vl-catch-all-apply 'hmrec:build-linienzug + (list (car kette) (cadr kette)))) + (if (vl-catch-all-error-p res) + (progn + (setq fehlertext (vl-catch-all-error-message res)) + (princ (strcat "\n FEHLER: " tid " - Ausnahme: " fehlertext)) + (setq res (hmrec:result-json tid "linienzug" "failed" + nil nil nil nil + (list (cons "fehler_text" + (hmrec:json-str fehlertext))))))) + (if res (setq out (cons res out)))) + (reverse out) +) + + +;; ============================================================ +;; JSON-Export der Ergebnisse +;; ============================================================ +(defun hm_recformat:export-results (tests-out-dir / out-json f first) + (if (null *hmrec-test-results*) + (princ "\n Keine Ergebnisse vorhanden.") + (progn + (vl-mkdir tests-out-dir) + (setq out-json (strcat tests-out-dir "/hm_recformat_results.json")) + (setq f (open out-json "w")) + (if f + (progn + (write-line "[" f) + (setq first T) + (foreach r *hmrec-test-results* + (if (not first) (write-line "," f)) + (write-line r f) + (setq first nil)) + (write-line "]" f) + (close f) + (princ (strcat "\n Ergebnisse: " out-json))) + (princ (strcat "\n FEHLER: Kann " out-json " nicht schreiben!"))))) +) + + +;; ============================================================ +;; C:TEST_HM_RECFORMAT - Hauptbefehl +;; ============================================================ +(defun c:TEST_HM_RECFORMAT ( / json-datei daten ketten results-list res + anz-ok anz-fehler) + ;; Debug-Datei nur bei eingeschaltetem Schalter: (dbg-schalter-on "hm_recformat") + (setq *hmrec-dbg* + (and (car (atoms-family 1 '("DBG-SCHALTER-OPEN"))) + (dbg-schalter-open "hm_recformat" "hm_recformat.dbg" "DXFM_LOG"))) + + ;; Benoetigte Feature-Module laden (Linienzug braucht Vario + Gefaelle) + (ssg-ensure "VarioFoerderer") + (ssg-ensure "Gefaellestrecke") + + (if (null (car (atoms-family 1 '("VF-LINIENZUG-MODUS")))) + (progn + (princ "\n[TEST_HM_RECFORMAT] FEHLER: vf-linienzug-modus nicht geladen" + " (VarioFoerderer/Gefaellestrecke laden).") + (if *hmrec-dbg* (dbgclose)) + (exit))) + + (ssg-start "TEST_HM_RECFORMAT" '(("OSMODE") ("CECOLOR") ("ATTREQ") ("ATTDIA"))) + (setvar "OSMODE" 0) + (setvar "ATTREQ" 0) + (setvar "ATTDIA" 0) + + ;; Block-Bibliothek (Bogen-/AS-/ES-Masse) initialisieren + (if (or (not *lib-initialized*) (null bogen-auf)) + (init-bibliothek)) + + ;; Bau-Dimension festhalten. Die Eingabedaten sind dimensionsfrei; welche + ;; Blockvariante (_2D/_3D) verwendet wird, loest ssg-ils-dim-aktuell auf + ;; (Override *ssg-ils-dim* -> Umgebungsvariable DXFM_DIM -> Default "3D"). + (setq *hmrec-dimension* (ssg-ils-dim-aktuell)) + (princ (strcat "\n Bau-Dimension: " *hmrec-dimension* + " (umschalten: TEST_HM_RECFORMAT_2D / TEST_HM_RECFORMAT_3D)")) + + ;; Testdaten laden + (setq json-datei (strcat (getenv "DXFMAKRO") "/tests/testdata/hm_recformat.json")) + (if (not (findfile json-datei)) + (progn + (princ (strcat "\n[TEST_HM_RECFORMAT] FEHLER: " json-datei " nicht gefunden!")) + (ssg-end) + (if *hmrec-dbg* (dbgclose)) + (exit))) + (setq daten (ssg-load-json json-datei)) + (if (null daten) + (progn + (princ "\n[TEST_HM_RECFORMAT] FEHLER: Testdaten konnten nicht geladen werden.") + (ssg-end) + (if *hmrec-dbg* (dbgclose)) + (exit))) + + (setq ketten (hmrec:gruppiere daten)) + (if (null ketten) + (progn + (princ "\n[TEST_HM_RECFORMAT] FEHLER: keine Kette gefunden - erwartet werden" + " Kopf-Objekte mit \"test_id\" und Eingaben mit \"typ\".") + (ssg-end) + (if *hmrec-dbg* (dbgclose)) + (exit))) + + (princ "\n\n================================================================") + (princ "\n TEST_HM_RECFORMAT - 5 Linienzuege der Anlage HundM (polylines.dxf)") + (princ (strcat "\n " (itoa (length daten)) " JSON-Objekte, " + (itoa (length ketten)) " Ketten")) + (princ "\n================================================================") + + (setq anz-ok 0 anz-fehler 0) + (hmrec:stubs-an) + ;; Stubs MUESSEN auch nach einem unerwarteten Fehler zurueckgenommen werden. + (setq results-list (vl-catch-all-apply 'hmrec:ketten-bauen (list ketten))) + (hmrec:stubs-aus) + (if (vl-catch-all-error-p results-list) + (progn + (princ (strcat "\n[TEST_HM_RECFORMAT] FEHLER in der Kettenschleife: " + (vl-catch-all-error-message results-list))) + (setq results-list '()))) + + (foreach res results-list + (if (vl-string-search "\"status\": \"executed\"" res) + (setq anz-ok (1+ anz-ok)) + (setq anz-fehler (1+ anz-fehler)))) + + (princ "\n================================================================") + (princ (strcat "\n Ergebnis: " (itoa anz-ok) " OK, " + (itoa anz-fehler) " Fehler")) + (princ "\n================================================================") + + (setq *hmrec-test-results* results-list) + + (ssg-end) + (princ "\n TEST_HM_RECFORMAT abgeschlossen.") + (if *hmrec-dbg* (dbgclose)) + (princ) +) + + +;; ============================================================ +;; Dieselbe Anlage in einer bestimmten Dimension bauen +;; ============================================================ +;; Die Eingabedaten (hm_recformat.json) sind dimensionsfrei - sie beschreiben die +;; Eingaben, mit denen die Ketten gebaut wurden. Ob daraus flache 2D-Symbole +;; oder 3D-Modelle werden, entscheidet allein die Blockvariante (_2D/_3D), die +;; ssg-ils-dim-aktuell aufloest. Unsere Bibliothek hat beide, also laesst sich +;; dieselbe Anlage ohne Datenaenderung in 2D ODER 3D aufbauen. +;; *hmrec-dim-override* wird in der Kettenschleife je Kette neu gesetzt +;; (siehe dort). +(defun hmrec:mit-dimension (dim / alt ergebnis) + (setq alt (if (boundp '*ssg-ils-dim*) *ssg-ils-dim*)) + (setq *ssg-ils-dim* dim) + (setq *hmrec-dim-override* dim) + (princ (strcat "\n[TEST_HM_RECFORMAT] Dimension voruebergehend auf " dim + " gesetzt.")) + (setq ergebnis (c:TEST_HM_RECFORMAT)) + ;; Override wieder auf den vorherigen Stand - sonst baut ein folgender Test + ;; unbemerkt in der hier gewaehlten Dimension weiter. + (setq *ssg-ils-dim* alt) + (setq *hmrec-dim-override* nil) + (princ (strcat "\n[TEST_HM_RECFORMAT] Dimension zurueckgesetzt auf " + (if alt alt "Umgebung/Default") ".")) + ergebnis +) + +(defun c:TEST_HM_RECFORMAT_2D ( / ) (hmrec:mit-dimension "2D")) +(defun c:TEST_HM_RECFORMAT_3D ( / ) (hmrec:mit-dimension "3D")) diff --git a/tests/test_hm_recformat.py b/tests/test_hm_recformat.py new file mode 100644 index 0000000..ddc91ed --- /dev/null +++ b/tests/test_hm_recformat.py @@ -0,0 +1,380 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +test_hm_recformat.py - Validiert die 5 VarioFoerderer-Linienzuege der Anlage HundM. + +Zwei Ebenen: + 1. Die Testdaten selbst (tests/testdata/hm_recformat.json): Struktur der + Eingabe-Journale und Lesbarkeit durch den LISP-Parser. + 2. Die von TEST_HM_RECFORMAT (test_hm_recformat.lsp) erzeugten Ergebnisse + (output/hmrec_results.json + output/hm_recformat_tests.dxf): jede Kette + gebaut, kein Journal-Wert offen, Attribute passen zu den Eingaben. + +Die Testdaten stammen aus lib/vf_journal_export.py (Journale der VF_n-Bloecke +aus data/polylines.dxf, XDATA-App SSG_VF_EDIT) und nicht aus Handarbeit - +Schema siehe tests/testdata/object_data.md Abschnitt 8. +""" + +import json +import os + +import pytest + + +EINGABE_TYPEN = ("point_abs", "point_rel", "real", "int", "string", "step") + + +# ============================================================ +# Hilfsfunktionen: flaches JSON in Ketten gruppieren +# ============================================================ +# Gleiche Regel wie hmrec:gruppiere in test_hm_recformat.lsp: ein Objekt mit +# "test_id" ist ein Kopf und beginnt eine Kette, die folgenden Objekte mit +# "typ" sind ihre Eingaben. + +def _ketten(testdata): + ketten = [] + for obj in testdata: + if "test_id" in obj: + ketten.append((obj, [])) + elif "typ" in obj and ketten: + ketten[-1][1].append(obj) + return ketten + + +def _journal_laenge(eingaben): + """Anzahl der Journal-Eintraege, die der Treiber daraus baut: ein + point_rel mit "hz" wird zu ZWEI Eintraegen (DL + REAL).""" + return sum(2 if (e["typ"] == "point_rel" and "hz" in e) else 1 + for e in eingaben) + + +def _pfad(*teile): + return os.path.join(os.path.dirname(os.path.abspath(__file__)), *teile) + + +# ============================================================ +# Testdaten-Pruefung (hm_recformat.json selbst) +# ============================================================ + +class TestAufzeichnungTestdaten: + + def test_fuenf_ketten(self, hmrec_testdata): + """Die Anlage hat fuenf am Kreisel haengende Linienzuege.""" + ketten = _ketten(hmrec_testdata) + assert len(ketten) == 5, \ + f"{len(ketten)} Ketten in hm_recformat.json, erwartet 5" + + def test_jedes_objekt_zuordenbar(self, hmrec_testdata): + """Kein Objekt darf zwischen Kopf und Eingabe fallen - sonst + verschluckt hmrec:gruppiere es stillschweigend.""" + for i, obj in enumerate(hmrec_testdata): + assert ("test_id" in obj) or ("typ" in obj), \ + f"Objekt {i} ist weder Kopf noch Eingabe: {sorted(obj)}" + + def test_kopf_felder(self, hmrec_testdata): + for kopf, _ in _ketten(hmrec_testdata): + assert kopf["test_id"].startswith("VF_"), kopf["test_id"] + # "typ" wuerde den Kopf zu einer Eingabe machen (siehe + # hmrec:gruppiere) - der Anlagetyp heisst darum "anlagetyp". + assert "typ" not in kopf, \ + f'{kopf["test_id"]}: Kopf darf kein Feld "typ" tragen' + assert kopf["anlagetyp"] == "linienzug", kopf["test_id"] + assert kopf["modus"] == 1, kopf["test_id"] + assert kopf["block"].startswith("VF_"), kopf["block"] + assert len(kopf["startpunkt_mm"]) == 3, kopf["test_id"] + + def test_anzahl_eingaben_stimmt(self, hmrec_testdata): + """anzahl_eingaben ist die Selbstauskunft des Kopfes - weicht sie ab, + fehlen Eingaben (z.B. durch eine Handaenderung).""" + for kopf, eingaben in _ketten(hmrec_testdata): + assert kopf["anzahl_eingaben"] == len(eingaben), \ + f'{kopf["test_id"]}: anzahl_eingaben=' \ + f'{kopf["anzahl_eingaben"]}, gezaehlt {len(eingaben)}' + + def test_eingabe_typen_bekannt(self, hmrec_testdata): + for kopf, eingaben in _ketten(hmrec_testdata): + for i, e in enumerate(eingaben): + assert e["typ"] in EINGABE_TYPEN, \ + f'{kopf["test_id"]} Eingabe {i}: unbekannter Typ ' \ + f'{e["typ"]}' + assert e.get("kommentar"), \ + f'{kopf["test_id"]} Eingabe {i}: kein Kommentar' + + def test_kette_beginnt_mit_startpunkt(self, hmrec_testdata): + """Erste Eingabe = absoluter Startpunkt, zweite = Starthoehe. Fehlt + der Punkt, wuerde das Replay ihn interaktiv erfragen und die Kette an + der falschen Stelle aufbauen (siehe hmrec:build-linienzug).""" + for kopf, eingaben in _ketten(hmrec_testdata): + assert eingaben[0]["typ"] == "point_abs", kopf["test_id"] + assert eingaben[1]["typ"] == "real", kopf["test_id"] + punkt = eingaben[0]["wert"] + assert len(punkt) == 3, kopf["test_id"] + assert punkt == kopf["startpunkt_mm"], \ + f'{kopf["test_id"]}: point_abs {punkt} != startpunkt_mm ' \ + f'{kopf["startpunkt_mm"]}' + assert eingaben[1]["wert"] == punkt[2], \ + f'{kopf["test_id"]}: Starthoehe {eingaben[1]["wert"]} != ' \ + f'Z des Startpunkts {punkt[2]}' + + def test_hz_nur_beim_ersten_segment(self, hmrec_testdata): + """vfl-in-abstand journalisiert die Fahrtrichtung NUR beim ersten + Segment der Kette; jedes weitere erbt sie vom Vorgaenger. Ein "hz" an + spaeterer Stelle waere ein Wert zu viel in der Replay-Queue und + verschiebt alle folgenden Antworten (Desync).""" + for kopf, eingaben in _ketten(hmrec_testdata): + rel = [e for e in eingaben if e["typ"] == "point_rel"] + assert rel, f'{kopf["test_id"]}: kein Segment' + assert "hz" in rel[0], \ + f'{kopf["test_id"]}: erstes Segment ohne hz' + for i, e in enumerate(rel[1:], start=2): + assert "hz" not in e, \ + f'{kopf["test_id"]}: Segment {i} traegt hz - nur das ' \ + f'erste Segment darf eines haben' + + def test_segmentlaengen_positiv(self, hmrec_testdata): + for kopf, eingaben in _ketten(hmrec_testdata): + for e in eingaben: + if e["typ"] == "point_rel": + assert e["dL"] > 0, f'{kopf["test_id"]}: dL={e["dL"]}' + + def test_menue_antworten_sind_strings(self, hmrec_testdata): + """Menue-Antworten muessen Strings bleiben ("1"/"2"/"90"/"30"): der + Produktionscode vergleicht mit (= antwort "1"), eine Zahl wuerde + stumm in den Default-Zweig laufen.""" + for kopf, eingaben in _ketten(hmrec_testdata): + for i, e in enumerate(eingaben): + if e["typ"] in ("string", "step"): + assert isinstance(e["wert"], str), \ + f'{kopf["test_id"]} Eingabe {i}: {e["wert"]!r} ist ' \ + f'kein String' + + def test_glieder_vorhanden(self, hmrec_testdata): + """Jede Kette hat Glied-Marker (STEP) - sie sind der Anker fuer den + Sektions-/Glied-Edit und die Kurzbeschreibung.""" + for kopf, eingaben in _ketten(hmrec_testdata): + glieder = [e["wert"] for e in eingaben if e["typ"] == "step"] + assert glieder, f'{kopf["test_id"]}: kein STEP-Marker' + for g in glieder: + assert g in ("Linie-GF", "Linie-VF", "Linie", "GF-Bogen", + "Horizontal-VF", "Vario-Kurve", "ES"), \ + f'{kopf["test_id"]}: unbekanntes Glied {g}' + + def test_arrays_einzeilig(self): + """Jedes Array muss komplett in EINER Zeile stehen. + + ssg-parse-json-array (Lisp/ssg_core.lsp) liest die Datei zeilenweise, + und ssg-cfg-parse-array braucht das ganze Array in einem String. Ein + von json.dump(indent=2) umgebrochenes Array wird zu nil - der Treiber + baut die Kette dann nicht an ihrer Position. Das ist genau der + Fehler, der die Anlage einmal komplett am Ursprung aufgebaut hat, und + er ist in der Zeichnung nicht als Datenfehler zu erkennen. + """ + with open(_pfad("testdata", "hm_recformat.json"), encoding="utf-8") as fh: + for nr, zeile in enumerate(fh, start=1): + offen = zeile.count("[") - zeile.count("]") + # Die aeusserste Klammer des Dokuments darf allein stehen. + if zeile.strip() in ("[", "]"): + continue + assert offen == 0, \ + f"Zeile {nr}: Array ueber mehrere Zeilen umgebrochen - " \ + f"ssg-cfg-parse-array kann das nicht lesen: {zeile.strip()!r}" + + def test_flach_fuer_lisp_parser(self, hmrec_testdata): + """Die Datei muss FLACH bleiben. + + ssg-load-json / ssg-parse-json-array behandelt jedes "{" als + Objektanfang und jedes "}"/"}," als Objektende - es kennt keine + Verschachtelung. Ein Objekt in einem Objekt (oder ein Array von + Objekten wie "eingaben": [...]) wuerde das umgebende Objekt + verschlucken. Dieser Test zaehlt die Objekte so, wie der LISP-Parser + sie zaehlen wuerde, und vergleicht mit dem echten JSON. + """ + objekte = 0 + with open(_pfad("testdata", "hm_recformat.json"), encoding="utf-8") as fh: + for zeile in fh: + gestrippt = zeile.strip() + if gestrippt == "{": + objekte += 1 + elif gestrippt in ("}", "},"): + continue + elif gestrippt.startswith('"'): + # Ein Wert, der ein Objekt oder eine Objektliste eroeffnet, + # macht die Datei fuer den LISP-Parser unlesbar. + assert not gestrippt.endswith(("{", "[")), \ + f"verschachtelter Wert: {gestrippt!r}" + assert objekte == len(hmrec_testdata), \ + f"LISP-Parser saehe {objekte} Objekte, json.load sieht " \ + f"{len(hmrec_testdata)}" + + +# ============================================================ +# Ergebnis-Pruefung (results.json vs. testdata) +# ============================================================ + +class TestAufzeichnungResults: + + def test_je_kette_ein_ergebnis(self, hmrec_testdata, hmrec_results): + soll = [k["test_id"] for k, _ in _ketten(hmrec_testdata)] + ist = [r["test_id"] for r in hmrec_results] + assert ist == soll, f"Ergebnisse {ist}, erwartet {soll}" + for r in hmrec_results: + assert r["kind"] == "linienzug", f'{r["test_id"]}: {r["kind"]}' + + def test_status_executed(self, hmrec_results): + """"desync" (Journal-Werte nicht verbraucht) und "failed" (Abbruch) + sind beide echte Fehler - siehe hmrec:build-linienzug.""" + for r in hmrec_results: + assert r["status"] == "executed", \ + f'{r["test_id"]}: Status={r["status"]}' + + def test_journal_vollstaendig_verbraucht(self, hmrec_testdata, + hmrec_results): + """Alle aufgezeichneten Werte muessen im Bau abgerufen worden sein. + Bleibt etwas offen, ist der Ablauf von der Aufzeichnung abgewichen und + die Geometrie kann der Vorlage nicht entsprechen.""" + soll = {k["test_id"]: _journal_laenge(e) + for k, e in _ketten(hmrec_testdata)} + for r in hmrec_results: + assert "eingaben_offen" in r, ( + f'{r["test_id"]}: kein eingaben_offen im Ergebnis - die Kette ' + f'ist vor der Auswertung abgebrochen (status=' + f'{r.get("status")!r}, {r.get("fehler_text")})') + assert r["eingaben_offen"] == 0, \ + f'{r["test_id"]}: {r["eingaben_offen"]} Journal-Werte offen' + assert r["eingaben_gesamt"] == soll[r["test_id"]], \ + f'{r["test_id"]}: {r["eingaben_gesamt"]} Journal-Eintraege ' \ + f'abgespielt, erwartet {soll[r["test_id"]]}' + + def test_keine_live_eingabe(self, hmrec_results): + """Kein Prompt, kein Dialog: der Treiber schaltet *vfl-headless* ein + (harter Abbruch statt stillem Rueckfall auf Live-Eingabe, siehe + vfl-headless-abbruch in Lisp/vf_linienzug.lsp) UND ersetzt zusaetzlich + getpoint/getstring/getint/getreal durch zaehlende Stubs. Beide Wege + muessen leer bleiben - "prompts" ist der positive Beweis, dass keine + Eingabe ausserhalb der vfl-in-*-Wrapper erreicht wurde.""" + for r in hmrec_results: + if "prompts" in r: + assert r["prompts"] == 0, \ + f'{r["test_id"]}: {r["prompts"]} Live-Eingabe(n) erreicht' + assert not r.get("headless_fehler"), \ + f'{r["test_id"]}: Headless-Abbruch bei {r["headless_fehler"]}' + + def test_keine_geometrie_meldungen(self, hmrec_results): + """*vfl-meldungen* sammelt die abgewiesenen Sektionen (frueher + blockierende alert-Fenster, jetzt vfl-meldung). Eine Meldung heisst: + ein Glied liess sich geometrisch nicht bauen - der Nachbau weicht dann + von der Vorlage ab, auch wenn das Journal aufgeht.""" + for r in hmrec_results: + assert not r.get("meldungen"), \ + f'{r["test_id"]}: Meldungen aus dem Bau: {r["meldungen"]}' + + def test_vf_block_erzeugt(self, hmrec_results): + for r in hmrec_results: + assert r["block_name"].startswith("VF_"), \ + f'{r["test_id"]}: Block "{r["block_name"]}" ist kein VF_-Block' + assert r["block_handle"], f'{r["test_id"]}: kein Handle' + + def test_dimension_einheitlich(self, hmrec_results): + """Alle Ergebnisse eines Laufs stammen aus EINER Bau-Dimension. + + Die Eingabedaten sind dimensionsfrei; ob _2D- oder _3D-Blockvarianten + verwendet werden, loest ssg-ils-dim-aktuell auf. Ein Lauf, in dem beide + vorkommen, waere ein halb umgeschalteter Zustand (z.B. weil ein + vorheriger Test *ssg-ils-dim* nicht zurueckgesetzt hat). + """ + dims = {r.get("dimension") for r in hmrec_results} + assert len(dims) == 1, f"gemischte Bau-Dimensionen im Lauf: {dims}" + dim = dims.pop() + assert dim in ("2D", "3D"), \ + f'Bau-Dimension "{dim}" ist weder 2D noch 3D' + + def test_startpunkt_getroffen(self, hmrec_testdata, hmrec_results): + """Der VF_n-Block wird mit dem Kettenstartpunkt als Basispunkt + gewickelt (ssg-block-wrap-welt in vfl-block-erstellen) - sein + Einfuegepunkt muss also der Startpunkt aus den Eingaben sein.""" + soll = {k["test_id"]: k["startpunkt_mm"] + for k, _ in _ketten(hmrec_testdata)} + for r in hmrec_results: + p = soll[r["test_id"]] + ist = r["insert_point"] + for achse, i in (("X", 0), ("Y", 1), ("Z", 2)): + assert abs(ist[i] - p[i]) < 1.0, \ + f'{r["test_id"]}: Einfuegepunkt {achse} {ist[i]} != ' \ + f'{p[i]}' + + def test_as_es_attribute_aus_eingaben(self, hmrec_testdata, + hmrec_results): + """WINKEL_AS/SEITE_AS und WINKEL_ES/SEITE_ES muessen die Antworten aus + dem Journal wiedergeben (AS steht in der Praeambel, ES im ES-Glied).""" + seiten = {"1": "links", "2": "rechts"} + nach_id = {r["test_id"]: r for r in hmrec_results} + for kopf, eingaben in _ketten(hmrec_testdata): + r = nach_id[kopf["test_id"]] + attrs = r["actual_attributes"] + # Praeambel: [0] Punkt, [1] Hoehe, [2] AS setzen, [3] Winkel, + # [4] Seite + if eingaben[2]["wert"] == "1": + assert attrs.get("WINKEL_AS") == eingaben[3]["wert"], \ + f'{kopf["test_id"]}: WINKEL_AS=' \ + f'{attrs.get("WINKEL_AS")}, erwartet {eingaben[3]["wert"]}' + assert attrs.get("SEITE_AS") == seiten[eingaben[4]["wert"]], \ + f'{kopf["test_id"]}: SEITE_AS={attrs.get("SEITE_AS")}' + # ES: die beiden Strings direkt nach dem STEP-Marker "ES" + for i, e in enumerate(eingaben): + if e["typ"] == "step" and e["wert"] == "ES": + assert attrs.get("WINKEL_ES") == eingaben[i + 1]["wert"], \ + f'{kopf["test_id"]}: WINKEL_ES=' \ + f'{attrs.get("WINKEL_ES")}' + assert attrs.get("SEITE_ES") == \ + seiten[eingaben[i + 2]["wert"]], \ + f'{kopf["test_id"]}: SEITE_ES={attrs.get("SEITE_ES")}' + break + + def test_vario_kurven_gezaehlt(self, hmrec_testdata, hmrec_results): + """Jede Vario-Kurve im Journal muss in den VF_Bogen_*-Attributen + auftauchen (vfl-insert-vario-kurve-block zaehlt sie mit).""" + nach_id = {r["test_id"]: r for r in hmrec_results} + for kopf, eingaben in _ketten(hmrec_testdata): + soll = sum(1 for e in eingaben + if e["typ"] == "step" and e["wert"] == "Vario-Kurve") + attrs = nach_id[kopf["test_id"]]["actual_attributes"] + ist = sum(int(attrs.get(f"VF_Bogen_{lage}_{w}", 0) or 0) + for lage in ("A", "I") for w in (90, 60, 30)) + assert ist == soll, \ + f'{kopf["test_id"]}: {ist} Vario-Kurven in den Attributen, ' \ + f'{soll} im Journal' + + def test_hoehe_von_ist_starthoehe(self, hmrec_testdata, + hmrec_results): + nach_id = {r["test_id"]: r for r in hmrec_results} + for kopf, eingaben in _ketten(hmrec_testdata): + attrs = nach_id[kopf["test_id"]]["actual_attributes"] + if "HOEHE_VON_mm" not in attrs: + continue + assert abs(int(attrs["HOEHE_VON_mm"]) - eingaben[1]["wert"]) <= 1, \ + f'{kopf["test_id"]}: HOEHE_VON_mm={attrs["HOEHE_VON_mm"]}, ' \ + f'Starthoehe {eingaben[1]["wert"]}' + + +# ============================================================ +# DXF Geometrie-Pruefung (ezdxf) +# ============================================================ + +class TestAufzeichnungGeometry: + + def test_block_anzahl(self, hmrec_dxf, hmrec_testdata): + """Je Kette muss ein VF_-Block im Modelspace stehen.""" + n_vf = sum(1 for e in hmrec_dxf.modelspace() + if e.dxftype() == "INSERT" and e.dxf.name.startswith("VF_")) + soll = len(_ketten(hmrec_testdata)) + assert n_vf >= soll, f"VF_: {n_vf} < {soll}" + + def test_handles_im_modelspace(self, hmrec_dxf, hmrec_results): + """Jedes gebaute Block-Handle (executed) liegt im Modelspace.""" + handles = {e.dxf.handle for e in hmrec_dxf.modelspace() + if e.dxftype() == "INSERT"} + for r in hmrec_results: + if r["status"] == "executed" and r["block_handle"]: + assert r["block_handle"] in handles, \ + f'{r["test_id"]}: Handle {r["block_handle"]} nicht im ' \ + f'Modelspace' diff --git a/tests/test_hundm05.lsp b/tests/test_hundm05.lsp index 8cfaef9..ab613cd 100644 --- a/tests/test_hundm05.lsp +++ b/tests/test_hundm05.lsp @@ -1,41 +1,32 @@ ;; ============================================================ ;; test_hundm05.lsp - Integrationstest: baut die 5 VarioFoerderer-Linienzuege -;; der Anlage HundM (Kreisel mit fuenf daran haengenden Ketten) nach. +;; der Anlage HundM (Kreisel mit fuenf daran haengenden Ketten) aus ihrer +;; SPEC nach. ;; -;; Datenquelle: tests/testdata/hundm05.json, erzeugt von -;; python lib/vf_journal_export.py data/polylines.dxf \ -;; tests/testdata/hundm05.json --csv results/HundM_export.csv -;; Das sind die EINGABE-JOURNALE der echten Ketten - ausgelesen aus der XDATA -;; (App SSG_VF_EDIT, Marker "linienzug") der VF_n-Bloecke in data/polylines.dxf, -;; also genau die Werte, mit denen die Ketten in BricsCAD gebaut wurden. +;; Datenquelle: tests/testdata/hundm05.json - die Ketten im Spec-Format +;; (Domaenenwerte statt Menue-Codes, siehe Lisp/vf_spec.lsp und +;; doc/variofoerderer.md Abschnitt 8b). Erzeugt aus den echten +;; Aufzeichnungen mit +;; python lib/vf_spec_export.py tests/testdata/hm_recformat.json \ +;; tests/testdata/hundm05.json +;; und damit nachweislich dasselbe Journal wie die Vorlage +;; (Rundlauf-Beweis in tests/test_vf_spec.py). Die Datei ist die Stelle, an +;; der weitere Ketten der Anlage nachgetragen werden. ;; -;; JSON-Aufbau (FLACH, weil ssg-load-json zeilenweise liest - Details in -;; tests/testdata/object_data.md Abschnitt 8): -;; Kopf-Objekt (traegt "test_id"): test_id, anlagetyp, modus, -;; anzahl_eingaben, block, dxf_handle, startpunkt_mm, -;; rotation_grad, beschreibung, sivas_* , erwartung_hinweis -;; Eingabe-Objekt (traegt "typ"): point_abs | point_rel | real | int | -;; string | step - in exakter Frage-Reihenfolge -;; Ein Kopf-Objekt beginnt eine Kette, die folgenden Eingabe-Objekte gehoeren -;; dazu (hundm05:gruppiere). +;; Dieser Test hat KEINE eigene Bau-Logik: er ruft vsp-bau-datei aus +;; Lisp/vf_spec.lsp - denselben Weg, den auch der Befehl VF_SPEC_BAU nimmt. +;; Es gibt also genau einen Code-Pfad, der aus einer Spec Geometrie erzeugt. ;; -;; WARUM REPLAY UND KEINE EINGABE-MOCKS (Unterschied zu test_linienzug.lsp): -;; test_linienzug.lsp ersetzt getpoint/getstring/getint/getreal durch Mocks und -;; rechnet Punkte aus "dL"+"hz" selbst aus. Das geht nur, weil -;; linienzug_tests.json zu JEDEM Segment ein "hz" mitfuehrt. Ein echtes Journal -;; tut das nicht: vfl-in-abstand journalisiert die Fahrtrichtung NUR beim -;; allerersten Segment der Kette, jedes weitere erbt sie vom Vorgaenger. Ein -;; Mock kann diese geerbte Richtung nicht kennen - er wuerde einen Punkt in der -;; falschen Richtung liefern und die Projektion in vfl-neue-linie-messen ergaebe -;; eine falsche (bis auf 0 zusammenfallende) Laenge. Darum wird hier derselbe -;; Weg genommen, den die Produktion beim Editieren/Konvertieren nutzt: -;; vfl-journal-replay-start + vf-linienzug-modus (siehe vfl-konvertiere-ent in -;; Lisp/vf_linienzug.lsp). Die vfl-in-*-Wrapper ziehen ihre Werte dann direkt -;; aus der Replay-Queue - inklusive der Sonderregel fuer "hz". +;; ABGRENZUNG zu TEST_HM_RECFORMAT (tests/test_hm_recformat.lsp): dort werden +;; dieselben Ketten aus dem ROHEN Journal der Kundenzeichnung gebaut. Das ist +;; der Regressionstest fuer das Aufzeichnungsformat (bleibt ein bestehender +;; VF_n-Block abspielbar - Doppelklick-Edit, 2D/3D-Konvertierung?) und +;; deshalb dort auf "disabled": true, um nicht zweimal dieselbe Geometrie zu +;; bauen. ;; -;; Voraussetzungen: -;; - SSG_LIB geladen (VarioFoerderer inkl. vf_linienzug, Gefaellestrecke, -;; ssg_core, ssg_dbg) +;; Voraussetzung: +;; - SSG_LIB geladen (VarioFoerderer inkl. vf_linienzug + vf_spec, +;; Gefaellestrecke, ssg_core, ssg_dbg) ;; - Umgebungsvariable DXFMAKRO gesetzt ;; ;; Speichert (via SSG_RUN_ALL_TESTS bei "save":"dxf"): @@ -47,621 +38,73 @@ ;; TEST_HUNDM05 ; in der aktuellen Dimension (ssg-ils-dim-aktuell) ;; TEST_HUNDM05_2D ; erzwungen 2D ;; TEST_HUNDM05_3D ; erzwungen 3D -;; -;; Debug-Datei (nur wenn eingeschaltet): (dbg-schalter-on "hundm05") -;; schreibt hundm05.dbg ins DXFM_LOG-Verzeichnis (Journal je Kette als JSON). ;; ============================================================ +(if (not (boundp '*hundm05-records*)) (setq *hundm05-records* nil)) + +(defun hundm05:spec-datei () + (strcat (getenv "DXFMAKRO") "/tests/testdata/hundm05.json")) + ;; ============================================================ -;; Ergebnis-JSON +;; Testlauf ;; ============================================================ -;; --- JSON-Hilfen fuer Diagnosefelder (Strings/String-Listen) --- -;; Die extra-Felder in hundm05:result-json werden ROH in das JSON gesetzt, -;; ein Text muss also selbst seine Anfuehrungszeichen mitbringen und die -;; JSON-Sonderzeichen maskieren. -(defun hundm05:json-escape (s / out i c) - (setq out "" i 1) - (while (<= i (strlen s)) - (setq c (substr s i 1)) - (setq out (cond ((= c "\"") (strcat out "\\\"")) - ((= c "\\") (strcat out "\\\\")) - ((= c "\n") (strcat out " ")) - (T (strcat out c)))) - (setq i (1+ i))) - out) - -(defun hundm05:json-str (s) - (if s (strcat "\"" (hundm05:json-escape s) "\"") "null")) - -(defun hundm05:json-liste (lst / s first) - (setq s "[" first T) - (foreach x lst - (if (not first) (setq s (strcat s ", "))) - (setq s (strcat s (hundm05:json-str x)) first nil)) - (strcat s "]")) - -;; --- Headless-Diagnose der Produktion lesen --- -;; vfl-headless-abbruch (Lisp/vf_linienzug.lsp) legt bei erschoepfter -;; Replay-Queue Art der Eingabe, Glied-Nummer und Eingabe-Nummer ab. Das ist -;; die eigentliche Desync-Auskunft - ohne sie weiss man nur, DASS Werte offen -;; blieben, nicht WO der Ablauf abgewichen ist. -(defun hundm05:headless-fehler-text ( / f) - (if (and (boundp '*vfl-headless-fehler*) *vfl-headless-fehler*) - (progn - (setq f *vfl-headless-fehler*) - (strcat (cdr (assoc "was" f)) " @ " (cdr (assoc "ort" f)))) - nil)) - -;; Zaehler der trotz allem erreichten Live-Eingaben (muss 0 bleiben). -(defun hundm05:prompts () - (if (and (boundp '*hundm05-prompts*) *hundm05-prompts*) *hundm05-prompts* 0)) - - -;; "dimension" haelt fest, in welcher Dimension (2D/3D) gebaut wurde. Das ist -;; KEINE Eigenschaft der Eingabedaten: hundm05.json beschreibt die Ketten -;; dimensionsfrei. Womit unsere Makros bauen, entscheidet ssg-ils-dim-aktuell - -;; unsere Bibliothek hat von jedem Block eine _2D- und eine _3D-Variante. -;; extra: Alist zusaetzlicher Felder ((name . wert-string)), wird als -;; JSON-Zahlen/Strings so uebernommen, wie sie uebergeben werden. -(defun hundm05:result-json (test-id kind status block-name block-handle - insert-point attribs extra / - json tag val first) - (setq json (strcat " {\n" - " \"test_id\": \"" test-id "\",\n" - " \"kind\": \"" kind "\",\n" - " \"status\": \"" status "\",\n" - " \"dimension\": \"" - (if (and (boundp '*hundm05-dimension*) *hundm05-dimension*) - *hundm05-dimension* "?") "\",\n" - " \"block_name\": \"" (if block-name block-name "") "\",\n" - " \"block_handle\": \"" (if block-handle block-handle "") "\",\n" - " \"insert_point\": [" - (if insert-point - (strcat (rtos (car insert-point) 2 1) ", " - (rtos (cadr insert-point) 2 1) ", " - (rtos (if (caddr insert-point) (caddr insert-point) 0.0) 2 1)) - "0.0, 0.0, 0.0") - "],\n")) - (foreach f extra - (setq json (strcat json " \"" (car f) "\": " (cdr f) ",\n"))) - (setq json (strcat json " \"actual_attributes\": {")) - (setq first T) - (foreach att attribs - (setq tag (car att) val (cdr att)) - (if (not first) (setq json (strcat json ","))) - (setq json (strcat json "\n \"" tag "\": \"" (if val val "") "\"")) - (setq first nil)) - (setq json (strcat json "\n }\n }")) - json -) - - -;; --- Aus einer Block-ENAME ein Ergebnis-JSON erzeugen --- -;; status: "executed" bei sauberem Lauf; ein abweichender Status (z.B. bei -;; einem Queue-Desync) wird durchgereicht, die Block-Angaben bleiben dabei -;; erhalten - sonst waere der betroffene Block im Ergebnis nicht auffindbar. -(defun hundm05:ent-json-status (test-id kind status ent extra / ed) - (if ent - (progn - (setq ed (entget ent)) - (hundm05:result-json test-id kind status - (cdr (assoc 2 ed)) (cdr (assoc 5 ed)) (cdr (assoc 10 ed)) - (ssg-attrib-read ent) extra)) - (hundm05:result-json test-id kind "failed" nil nil nil nil extra)) -) - -(defun hundm05:ent-json (test-id kind ent extra) - (hundm05:ent-json-status test-id kind "executed" ent extra) -) - - -;; --- Letzten INSERT mit Blocknamen-Praefix suchen --- -;; AutoLISP kennt KEIN entprev (nur entnext und entlast) - rueckwaerts durch -;; die Zeichnung laufen geht also nicht. Gesucht wird darum VORWAERTS, wobei -;; der letzte Treffer gewinnt. Ein blosses (entlast) genuegt nicht: nach dem -;; Bau koennen weitere Objekte entstehen (z.B. Beschriftungstexte), entlast -;; ist dann nicht der gesuchte Block - genau daran scheiterte der Testlauf. -;; ab-ent = Stand der Zeichnung VOR dem Bau. Damit prueft die Suche nur die -;; neu entstandenen Objekte: schnell, und sie kann nicht versehentlich den -;; Block eines frueheren Baus liefern (siehe Kommentar bei -ab). -(defun hundm05:insert-prefix-p (ent pref / ed bn) - (setq ed (entget ent) bn (cdr (assoc 2 ed))) - (and ed (= (cdr (assoc 0 ed)) "INSERT") bn - (>= (strlen bn) (strlen pref)) - (= (substr bn 1 (strlen pref)) pref))) - -;; Vorwaertslauf ab ab-ent (nil = ganze Zeichnung); der LETZTE Treffer -;; gewinnt. Der entget-Test faengt ein inzwischen ungueltiges ab-ent ab -;; (entget liefert dann nil, entnext wuerde einen Fehler werfen). -(defun hundm05:suche-vorwaerts (pref ab-ent / ent found) - (setq ent (if (and ab-ent (entget ab-ent)) (entnext ab-ent) (entnext))) - (while ent - (if (hundm05:insert-prefix-p ent pref) (setq found ent)) - (setq ent (entnext ent))) - found) - -(defun hundm05:last-insert-prefix-ab (pref ab-ent / ent) - (if ab-ent - ;; MIT Marker nur die neu entstandenen Objekte pruefen - und BEWUSST - ;; ohne entlast-Kurzschluss: hat dieser Bau keinen Block erzeugt, waere - ;; entlast der Block eines FRUEHEREN Baus und das Ergebnis wuerde der - ;; falschen Kette zugeordnet. Kein Treffer = kein Block, das ist die - ;; richtige Antwort. - (hundm05:suche-vorwaerts pref ab-ent) - (progn - ;; Ohne Marker: erst entlast (der haeufige Fall, das gerade - ;; eingefuegte Objekt), sonst die ganze Zeichnung. - (setq ent (entlast)) - (if (and ent (hundm05:insert-prefix-p ent pref)) - ent - (hundm05:suche-vorwaerts pref nil))))) - -(defun hundm05:last-insert-prefix (pref) - (hundm05:last-insert-prefix-ab pref nil)) - - -;; ============================================================ -;; JSON-Eintraege -> Ketten -;; ============================================================ - -;; Das flache JSON-Array in Ketten gruppieren: ein Objekt mit "test_id" ist -;; ein Kopf und beginnt eine neue Kette, die folgenden Objekte mit "typ" sind -;; ihre Eingaben (Reihenfolge = Frage-Reihenfolge, bleibt erhalten). -;; Rueckgabe: Liste von (kopf eingaben-liste). -(defun hundm05:gruppiere (daten / ketten kopf eingaben) - (setq ketten '() kopf nil eingaben '()) - (foreach obj daten - (cond - ((ssg-val obj "test_id") - (if kopf - (setq ketten (cons (list kopf (reverse eingaben)) ketten))) - (setq kopf obj eingaben '())) - ((ssg-val obj "typ") - (if kopf (setq eingaben (cons obj eingaben)))) - ) - ) - (if kopf (setq ketten (cons (list kopf (reverse eingaben)) ketten))) - (reverse ketten) -) - - -;; --- Wert als String lesen (auch wenn er als Zahl im JSON steht) --- -;; Die Menue-Antworten sind Strings ("1"/"2"/"90"/"30"). Steht in einer von -;; Hand gepflegten Datei versehentlich 1 statt "1", wuerde ein Zahlenwert in -;; die Replay-Queue wandern und der Vergleich (= antwort "1") schlagen fehl - -;; darum hier immer nach String wandeln. -(defun hundm05:als-string (v) - (cond - ((null v) "") - ((= (type v) 'STR) v) - ((= (type v) 'INT) (itoa v)) - ((= (type v) 'REAL) (rtos v 2 6)) - (T "") - ) -) - - -;; --- Einen Eingabe-Eintrag in Journal-Eintraege uebersetzen --- -;; Journal-Format (vfl-entry->string in Lisp/vf_linienzug.lsp): -;; ("PT" x y z) / ("REAL" . r) / ("DL" . r) / ("INT" . i) / ("STR" . s) / -;; ("STEP" . label) -;; Ein "point_rel" wird zu EINEM oder ZWEI Eintraegen: immer die Laenge (DL), -;; zusaetzlich die Fahrtrichtung (REAL) nur dort, wo sie im Journal steht -;; (allererstes Segment der Kette, siehe Kopfkommentar). -;; Rueckgabe: Liste von Journal-Eintraegen (kann leer sein). -(defun hundm05:eintrag->journal (e / typ w hz) - (setq typ (ssg-val e "typ")) - (cond - ((= typ "point_abs") - (setq w (ssg-val e "wert")) - (if (and w (listp w) (>= (length w) 3)) - (list (cons "PT" (mapcar 'float w))) - nil)) - ((= typ "point_rel") - (setq w (ssg-val e "dL") hz (ssg-val e "hz")) - (cond - ((not (numberp w)) nil) - ((numberp hz) (list (cons "DL" (float w)) (cons "REAL" (float hz)))) - (T (list (cons "DL" (float w)))))) - ((= typ "real") - (setq w (ssg-val e "wert")) - (if (numberp w) (list (cons "REAL" (float w))) nil)) - ((= typ "int") - (setq w (ssg-val e "wert")) - (if (numberp w) (list (cons "INT" (fix w))) nil)) - ((= typ "string") - (list (cons "STR" (hundm05:als-string (ssg-val e "wert"))))) - ((= typ "step") - (list (cons "STEP" (hundm05:als-string (ssg-val e "wert"))))) - (T nil) - ) -) - - -;; --- Alle Eingaben einer Kette in ein Vorwaerts-Journal uebersetzen --- -;; Vorwaerts = Bau-Reihenfolge, genau so erwartet es vfl-journal-replay-start. -(defun hundm05:journal-bauen (eingaben / out) - (setq out '()) - (foreach e eingaben - (foreach j (hundm05:eintrag->journal e) - (setq out (cons j out)))) - (reverse out) -) - - -;; --- Nicht verbrauchte Werte in der Replay-Queue zaehlen --- -;; STEP-Marker zaehlen nicht mit: vfl-replay-pop ueberspringt sie, sie sind -;; also kein offener Wert. Bleibt hier etwas uebrig, ist der Bau-Ablauf vom -;; aufgezeichneten abgewichen (Desync) - das MUSS auffallen. -(defun hundm05:queue-rest ( / n) - (setq n 0) - (if (and (boundp '*vfl-replay-queue*) *vfl-replay-queue*) - (foreach e *vfl-replay-queue* - (if (/= (car e) "STEP") (setq n (1+ n))))) - n -) - - -;; ============================================================ -;; Eine Kette bauen -;; ============================================================ - -;; Spielt das Journal einer Kette ab. Ablauf wie vfl-konvertiere-ent -;; (Lisp/vf_linienzug.lsp): Queue setzen, vf-linienzug-modus laufen lassen, -;; danach den entstandenen VF_n-Block einsammeln. -;; -;; vl-catch-all-apply ist hier BEWUSST gesetzt (anders als im interaktiven -;; Editier-Pfad vfl-edit-ent): bricht eine Kette ab, muss die Testschleife -;; ueber die restlichen Ketten weiterlaufen. Der Preis ist derselbe wie beim -;; Batch-Konverter - der *error*-Handler in vf-linienzug-modus (der die -;; Teil-Geometrie zu einem Block wickelt) kommt dann nicht zum Zug und die -;; Teilgeometrie bleibt lose in der Zeichnung liegen. Der Testfall wird darum -;; klar als "failed" samt Fehlertext gemeldet. -;; *error* wird um den Aufruf herum gesichert: nach einem abgefangenen Abbruch -;; steht dort noch der Handler-Lambda von vf-linienzug-modus (es kommt nicht -;; mehr zu dessen (setq *error* old-error)) - ohne Restore wuerde die naechste -;; Kette auf einem Handler mit toten dynamischen Bindungen aufsetzen. -(defun hundm05:build-linienzug (kopf eingaben / tid journal anz offen err ent - alt-error extra hl-fehler meldungen vor-ent) - (setq tid (ssg-val kopf "test_id")) - (setq journal (hundm05:journal-bauen eingaben)) - (setq anz (length journal)) - (princ (strcat "\n [LINIENZUG] " tid " -> " (itoa (length eingaben)) - " Eingaben, " (itoa anz) " Journal-Eintraege")) - (if (ssg-val kopf "beschreibung") - (princ (strcat "\n " (ssg-val kopf "beschreibung")))) - (if (and (boundp '*hundm05-dbg*) *hundm05-dbg*) - (progn - (dbgmsg (strcat "KETTE " tid " JOURNAL " (vfl-journal->json journal))) - (dbgflush))) - - (cond - ;; Ohne Journal (oder ohne Startpunkt an erster Stelle) nichts bauen: ein - ;; Replay ohne PT wuerde den Startpunkt interaktiv erfragen und die ganze - ;; Kette an der falschen Stelle aufbauen. - ((null journal) - (princ (strcat "\n FEHLER: " tid " - keine Eingaben lesbar." - " Stehen die Zahlen-Arrays in EINER Zeile?" - " (ssg-cfg-parse-array kann keine umgebrochenen lesen)")) - (hundm05:result-json tid "linienzug" "failed" nil nil nil nil - (list (cons "eingaben_gesamt" (itoa anz)) - (cons "eingaben_offen" (itoa anz))))) - - ((/= (car (car journal)) "PT") - (princ (strcat "\n FEHLER: " tid " - erster Eintrag ist kein" - " Startpunkt (point_abs), nichts gebaut.")) - (hundm05:result-json tid "linienzug" "failed" nil nil nil nil - (list (cons "eingaben_gesamt" (itoa anz)) - (cons "eingaben_offen" (itoa anz))))) - - (T - ;; Stand der Zeichnung VOR dem Bau: der fertige VF_-Block wird danach - ;; ab hier gesucht (siehe hundm05:last-insert-prefix-ab). - (setq vor-ent (entlast)) - (setq alt-error *error*) - (vfl-journal-reset) - (vfl-journal-replay-start journal) - (setq err (vl-catch-all-apply 'vf-linienzug-modus '())) - (setq offen (hundm05:queue-rest)) - ;; Diagnose VOR dem Reset sichern: vfl-journal-reset loescht - ;; *vfl-headless-fehler* und *vfl-meldungen* mit (beide gehoeren zum - ;; einzelnen Lauf, nicht zur Sitzung). - (setq hl-fehler (hundm05:headless-fehler-text)) - (setq meldungen (if (boundp '*vfl-meldungen*) *vfl-meldungen* nil)) - (setq *error* alt-error) - (vfl-journal-reset) - (setq extra (list (cons "eingaben_gesamt" (itoa anz)) - (cons "eingaben_offen" (itoa offen)) - (cons "prompts" (itoa (hundm05:prompts))) - (cons "headless_fehler" (hundm05:json-str hl-fehler)) - (cons "meldungen" (hundm05:json-liste meldungen)))) - (cond - ;; Der Headless-Riegel der Produktion hat gegriffen: die Journal-Daten - ;; passen nicht zum tatsaechlichen Bau-Ablauf. Praeziser als "failed", - ;; weil hl-fehler Glied UND Eingabe-Nummer nennt. - (hl-fehler - (princ (strcat "\n DESYNC: " tid " - " hl-fehler)) - (hundm05:ent-json-status tid "linienzug" "desync" - (hundm05:last-insert-prefix-ab "VF_" vor-ent) extra)) - ((vl-catch-all-error-p err) - (princ (strcat "\n FEHLER: " tid " abgebrochen -> " - (vl-catch-all-error-message err))) - (hundm05:result-json tid "linienzug" "failed" nil nil nil nil extra)) - ;; Kette gebaut, aber der Ablauf hat nicht alle aufgezeichneten Werte - ;; abgerufen: die Geometrie kann nicht der Vorlage entsprechen. - ((> offen 0) - (princ (strcat "\n FEHLER: " tid " - " (itoa offen) - " Journal-Werte NICHT verbraucht (Ablauf weicht" - " von der Aufzeichnung ab).")) - (hundm05:ent-json-status tid "linienzug" "desync" - (hundm05:last-insert-prefix-ab "VF_" vor-ent) extra)) - (T - (setq ent (hundm05:last-insert-prefix-ab "VF_" vor-ent)) - (if ent - (princ (strcat " -> Block " (cdr (assoc 2 (entget ent))))) - (princ "\n FEHLER: kein VF_-Block gefunden")) - (hundm05:ent-json tid "linienzug" ent extra))))) -) - - -;; ============================================================ -;; Headless-Absicherung: Dialoge aus, leere Queue bricht ab -;; ============================================================ -;; Die Journale sind vollstaendig, es darf also keine Live-Eingabe geben. -;; Falls doch (Desync), wuerde ein getpoint/getstring den Testlauf blockieren -;; und ein alert-Fenster ihn haengen lassen. Darum werden die Eingabe- -;; Funktionen fuer die Dauer des Tests durch Abbruch-Stubs ersetzt (liefern -;; nil = Abbruch, was vf-linienzug-modus sauber beendet) und alert auf die -;; Konsole umgeleitet. Alles mit Save/Restore, siehe hundm05:stubs-aus. -;; Die Stubs haben feste 1- bzw. 2-Arity (AutoLISP kennt keine optionalen -;; Parameter) - genau wie die Mocks in test_linienzug.lsp. Ruft der Bau-Ablauf -;; eine der Funktionen mit anderer Argumentzahl auf, gibt es einen -;; Argumentfehler statt einer Warnung; beides landet im vl-catch-all-apply um -;; vf-linienzug-modus und meldet die Kette als "failed". Wichtig ist nur, dass -;; kein Prompt und kein Dialog den Lauf blockiert. -(defun hundm05:stub-warnung (was) - (setq *hundm05-prompts* (1+ (hundm05:prompts))) - (princ (strcat "\n WARNUNG: unerwartete Live-Eingabe (" was - ") - Replay-Queue leer, Kette wird abgebrochen.")) - nil -) -(defun hundm05:stub-getpoint (a b) (hundm05:stub-warnung "getpoint")) -(defun hundm05:stub-getstring (a) (hundm05:stub-warnung "getstring")) -(defun hundm05:stub-getint (a) (hundm05:stub-warnung "getint")) -(defun hundm05:stub-getreal (a) (hundm05:stub-warnung "getreal")) -(defun hundm05:stub-alert (msg) - (princ (strcat "\n ALERT (unterdrueckt): " (if msg msg ""))) - (princ) -) - -(defun hundm05:stubs-an ( / ) - (setq *hundm05-alt-getpoint* vfl-getpoint - *hundm05-alt-getstring* getstring - *hundm05-alt-getint* getint - *hundm05-alt-getreal* getreal - *hundm05-alt-alert* alert) - (setq vfl-getpoint hundm05:stub-getpoint - getstring hundm05:stub-getstring - getint hundm05:stub-getint - getreal hundm05:stub-getreal - alert hundm05:stub-alert) - ;; Wizard-Dialoge aus (waehrend eines Replays greifen sie ohnehin nicht, - ;; siehe vfl-wizard-aktiv - aber nach einem Desync wuerde ein Dialog - ;; aufgehen und warten). - (setq *hundm05-alt-wizard* (if (boundp '*vfl-wizard-mode*) *vfl-wizard-mode*)) - (setq *vfl-wizard-mode* nil) - (if (car (atoms-family 1 '("SSG-GUI-AUS"))) (ssg-gui-aus)) - ;; Headless-Riegel der Produktion einschalten: eine erschoepfte Replay-Queue - ;; ist dann ein harter, lokalisierter Abbruch statt eines stillen Rueckfalls - ;; auf Live-Eingabe (siehe vfl-headless-abbruch). Die Stubs oben bleiben als - ;; zweites Netz - sie fangen die Prompts ausserhalb der vfl-in-*-Wrapper - ;; (Modus 3, Nebenpfade) und zaehlen sie. - (setq *hundm05-alt-headless* (if (boundp '*vfl-headless*) *vfl-headless*)) - (setq *vfl-headless* T) - (setq *hundm05-prompts* 0) - (princ) -) - -(defun hundm05:stubs-aus ( / ) - (setq vfl-getpoint *hundm05-alt-getpoint* - getstring *hundm05-alt-getstring* - getint *hundm05-alt-getint* - getreal *hundm05-alt-getreal* - alert *hundm05-alt-alert*) - (setq *vfl-wizard-mode* *hundm05-alt-wizard*) - (setq *vfl-headless* *hundm05-alt-headless*) - (if (car (atoms-family 1 '("SSG-GUI-AN"))) (ssg-gui-an)) - (princ) -) - - -;; ============================================================ -;; Alle Ketten bauen -;; ============================================================ -;; Eigene Funktion, damit der Aufrufer sie in vl-catch-all-apply klammern und -;; die Eingabe-Stubs GARANTIERT zuruecknehmen kann (ein hier entkommender -;; Fehler wuerde sonst getstring/getpoint/alert der laufenden BricsCAD-Sitzung -;; ersetzt zuruecklassen). -;; Rueckgabe: Ergebnis-Liste in Bau-Reihenfolge. -(defun hundm05:ketten-bauen (ketten / kette res out tid fehlertext) - (setq out '()) - (foreach kette ketten - ;; Dimensions-Override je Kette neu setzen: der Abbruch-Handler in - ;; vf-linienzug-modus setzt *ssg-ils-dim* zurueck, ein Abbruch wuerde die - ;; folgenden Ketten sonst in der falschen Dimension bauen. - (if (and (boundp '*hundm05-dim-override*) *hundm05-dim-override*) - (setq *ssg-ils-dim* *hundm05-dim-override*)) - ;; Je Kette gefangen: ein Fehler NACH dem Bau (z.B. beim Auswerten des - ;; Ergebnisses) darf nicht die restlichen Ketten mitnehmen - sonst steht - ;; am Ende "0 OK, 0 Fehler" und man sieht nicht einmal, welche Kette - ;; gebaut wurde. Der Bau selbst ist in hundm05:build-linienzug bereits - ;; separat gefangen; dieser Riegel hier deckt alles danach ab. - (setq tid (ssg-val (car kette) "test_id")) - (setq res (vl-catch-all-apply 'hundm05:build-linienzug - (list (car kette) (cadr kette)))) - (if (vl-catch-all-error-p res) - (progn - (setq fehlertext (vl-catch-all-error-message res)) - (princ (strcat "\n FEHLER: " tid " - Ausnahme: " fehlertext)) - (setq res (hundm05:result-json tid "linienzug" "failed" - nil nil nil nil - (list (cons "fehler_text" - (hundm05:json-str fehlertext))))))) - (if res (setq out (cons res out)))) - (reverse out) -) - - -;; ============================================================ -;; JSON-Export der Ergebnisse -;; ============================================================ -(defun hundm05:export-results (tests-out-dir / out-json f first) - (if (null *hundm05-test-results*) - (princ "\n Keine HundM05-Ergebnisse vorhanden.") - (progn - (vl-mkdir tests-out-dir) - (setq out-json (strcat tests-out-dir "/hundm05_results.json")) - (setq f (open out-json "w")) - (if f - (progn - (write-line "[" f) - (setq first T) - (foreach r *hundm05-test-results* - (if (not first) (write-line "," f)) - (write-line r f) - (setq first nil)) - (write-line "]" f) - (close f) - (princ (strcat "\n Ergebnisse: " out-json))) - (princ (strcat "\n FEHLER: Kann " out-json " nicht schreiben!"))))) -) - - -;; ============================================================ -;; C:TEST_HUNDM05 - Hauptbefehl -;; ============================================================ -(defun c:TEST_HUNDM05 ( / json-datei daten ketten results-list res - anz-ok anz-fehler) - ;; Debug-Datei nur bei eingeschaltetem Schalter: (dbg-schalter-on "hundm05") - (setq *hundm05-dbg* - (and (car (atoms-family 1 '("DBG-SCHALTER-OPEN"))) - (dbg-schalter-open "hundm05" "hundm05.dbg" "DXFM_LOG"))) - - ;; Benoetigte Feature-Module laden (Linienzug braucht Vario + Gefaelle) - (ssg-ensure "VarioFoerderer") - (ssg-ensure "Gefaellestrecke") - - (if (null (car (atoms-family 1 '("VF-LINIENZUG-MODUS")))) - (progn - (princ "\n[TEST_HUNDM05] FEHLER: vf-linienzug-modus nicht geladen" - " (VarioFoerderer/Gefaellestrecke laden).") - (if *hundm05-dbg* (dbgclose)) - (exit))) - - (ssg-start "TEST_HUNDM05" '(("OSMODE") ("CECOLOR") ("ATTREQ") ("ATTDIA"))) - (setvar "OSMODE" 0) - (setvar "ATTREQ" 0) - (setvar "ATTDIA" 0) - - ;; Block-Bibliothek (Bogen-/AS-/ES-Masse) initialisieren - (if (or (not *lib-initialized*) (null bogen-auf)) - (init-bibliothek)) - - ;; Bau-Dimension festhalten. Die Eingabedaten sind dimensionsfrei; welche - ;; Blockvariante (_2D/_3D) verwendet wird, loest ssg-ils-dim-aktuell auf - ;; (Override *ssg-ils-dim* -> Umgebungsvariable DXFM_DIM -> Default "3D"). - (setq *hundm05-dimension* (ssg-ils-dim-aktuell)) - (princ (strcat "\n Bau-Dimension: " *hundm05-dimension* - " (umschalten: TEST_HUNDM05_2D / TEST_HUNDM05_3D)")) - - ;; Testdaten laden - (setq json-datei (strcat (getenv "DXFMAKRO") "/tests/testdata/hundm05.json")) - (if (not (findfile json-datei)) - (progn - (princ (strcat "\n[TEST_HUNDM05] FEHLER: " json-datei " nicht gefunden!")) - (ssg-end) - (if *hundm05-dbg* (dbgclose)) - (exit))) - (setq daten (ssg-load-json json-datei)) - (if (null daten) - (progn - (princ "\n[TEST_HUNDM05] FEHLER: Testdaten konnten nicht geladen werden.") - (ssg-end) - (if *hundm05-dbg* (dbgclose)) - (exit))) - - (setq ketten (hundm05:gruppiere daten)) - (if (null ketten) - (progn - (princ "\n[TEST_HUNDM05] FEHLER: keine Kette gefunden - erwartet werden" - " Kopf-Objekte mit \"test_id\" und Eingaben mit \"typ\".") - (ssg-end) - (if *hundm05-dbg* (dbgclose)) - (exit))) - +(defun c:TEST_HUNDM05 ( / datei) + (setq datei (hundm05:spec-datei)) (princ "\n\n================================================================") - (princ "\n TEST_HUNDM05 - 5 Linienzuege der Anlage HundM (polylines.dxf)") - (princ (strcat "\n " (itoa (length daten)) " JSON-Objekte, " - (itoa (length ketten)) " Ketten")) + (princ "\n TEST_HUNDM05 - 5 Linienzuege der Anlage HundM (aus der Spec)") + (princ (strcat "\n " datei)) (princ "\n================================================================") - - (setq anz-ok 0 anz-fehler 0) - (hundm05:stubs-an) - ;; Stubs MUESSEN auch nach einem unerwarteten Fehler zurueckgenommen werden. - (setq results-list (vl-catch-all-apply 'hundm05:ketten-bauen (list ketten))) - (hundm05:stubs-aus) - (if (vl-catch-all-error-p results-list) - (progn - (princ (strcat "\n[TEST_HUNDM05] FEHLER in der Kettenschleife: " - (vl-catch-all-error-message results-list))) - (setq results-list '()))) - - (foreach res results-list - (if (vl-string-search "\"status\": \"executed\"" res) - (setq anz-ok (1+ anz-ok)) - (setq anz-fehler (1+ anz-fehler)))) - - (princ "\n================================================================") - (princ (strcat "\n Ergebnis: " (itoa anz-ok) " OK, " - (itoa anz-fehler) " Fehler")) - (princ "\n================================================================") - - (setq *hundm05-test-results* results-list) - - (ssg-end) - (princ "\n TEST_HUNDM05 abgeschlossen.") - (if *hundm05-dbg* (dbgclose)) - (princ) -) + (cond + ((null (car (atoms-family 1 '("VSP-BAU-DATEI")))) + (princ "\n[TEST_HUNDM05] FEHLER: vf_spec.lsp nicht geladen" + " (VarioFoerderer laden).") + (setq *hundm05-records* nil)) + ((null (findfile datei)) + (princ (strcat "\n[TEST_HUNDM05] FEHLER: " datei " nicht gefunden!")) + (setq *hundm05-records* nil)) + (T + (setq *hundm05-records* (vsp-bau-datei datei)))) + (princ)) ;; ============================================================ ;; Dieselbe Anlage in einer bestimmten Dimension bauen ;; ============================================================ -;; Die Eingabedaten (hundm05.json) sind dimensionsfrei - sie beschreiben die -;; Eingaben, mit denen die Ketten gebaut wurden. Ob daraus flache 2D-Symbole -;; oder 3D-Modelle werden, entscheidet allein die Blockvariante (_2D/_3D), die -;; ssg-ils-dim-aktuell aufloest. Unsere Bibliothek hat beide, also laesst sich -;; dieselbe Anlage ohne Datenaenderung in 2D ODER 3D aufbauen. -;; *hundm05-dim-override* wird in der Kettenschleife je Kette neu gesetzt -;; (siehe dort). -(defun hundm05:mit-dimension (dim / alt ergebnis) - (setq alt (if (boundp '*ssg-ils-dim*) *ssg-ils-dim*)) - (setq *ssg-ils-dim* dim) - (setq *hundm05-dim-override* dim) - (princ (strcat "\n[TEST_HUNDM05] Dimension voruebergehend auf " dim - " gesetzt.")) - (setq ergebnis (c:TEST_HUNDM05)) - ;; Override wieder auf den vorherigen Stand - sonst baut ein folgender Test - ;; unbemerkt in der hier gewaehlten Dimension weiter. - (setq *ssg-ils-dim* alt) - (setq *hundm05-dim-override* nil) - (princ (strcat "\n[TEST_HUNDM05] Dimension zurueckgesetzt auf " - (if alt alt "Umgebung/Default") ".")) - ergebnis -) +;; Die Spec ist dimensionsfrei - sie beschreibt die Eingaben, mit denen die +;; Ketten gebaut wurden. Ob daraus flache 2D-Symbole oder 3D-Modelle werden, +;; entscheidet allein die Blockvariante (_2D/_3D), die ssg-ils-dim-aktuell +;; aufloest. Unsere Bibliothek hat beide, also laesst sich dieselbe Anlage +;; ohne Datenaenderung in 2D ODER 3D aufbauen. +;; *vsp-dim-override* wird von vsp-bau-aus-spec JE KETTE angewandt (der +;; Abbruch-Handler in vf-linienzug-modus setzt *ssg-ils-dim* zurueck). -(defun c:TEST_HUNDM05_2D ( / ) (hundm05:mit-dimension "2D")) -(defun c:TEST_HUNDM05_3D ( / ) (hundm05:mit-dimension "3D")) +(defun hundm05:mit-dimension (dim / alt) + (setq alt (if (boundp '*vsp-dim-override*) *vsp-dim-override*)) + (setq *vsp-dim-override* dim) + (vl-catch-all-apply 'c:TEST_HUNDM05 '()) + (setq *vsp-dim-override* alt) + (princ)) + +(defun c:TEST_HUNDM05_2D () (hundm05:mit-dimension "2D")) +(defun c:TEST_HUNDM05_3D () (hundm05:mit-dimension "3D")) + + +;; ============================================================ +;; Ergebnis-Export (Konvention :export-results fuer test_run_all.lsp) +;; ============================================================ +;; Geschrieben wird hier und nicht in c:TEST_HUNDM05, damit der Testrunner +;; das Zielverzeichnis bestimmt (er uebergibt tests/output). + +(defun hundm05:export-results (out-dir / pfad) + (if (null *hundm05-records*) + (princ "\n Keine HundM05-Ergebnisse vorhanden.") + (progn + (vl-mkdir out-dir) + (setq pfad (strcat out-dir "/hundm05_results.json")) + (vsp-results-schreiben *hundm05-records* pfad))) + (princ)) + +(princ "\nTEST_HUNDM05 geladen. Aufruf: TEST_HUNDM05 (bzw. _2D / _3D)") +(princ) diff --git a/tests/test_hundm05.py b/tests/test_hundm05.py index 08f650e..9da7305 100644 --- a/tests/test_hundm05.py +++ b/tests/test_hundm05.py @@ -1,18 +1,17 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -test_hundm05.py - Validiert die 5 VarioFoerderer-Linienzuege der Anlage HundM. +test_hundm05.py - Validiert die aus der SPEC gebaute Anlage HundM. -Zwei Ebenen: - 1. Die Testdaten selbst (tests/testdata/hundm05.json): Struktur der - Eingabe-Journale und Lesbarkeit durch den LISP-Parser. - 2. Die von TEST_HUNDM05 (test_hundm05.lsp) erzeugten Ergebnisse - (output/hundm05_results.json + output/hundm05_tests.dxf): jede Kette - gebaut, kein Journal-Wert offen, Attribute passen zu den Eingaben. +Eingabe: tests/testdata/hundm05.json (die 5 Ketten im Spec-Format). +Gebaut von TEST_HUNDM05 bzw. VF_SPEC_BAU in BricsCAD, Ergebnis in +tests/output/hundm05_results.json. -Die Testdaten stammen aus lib/vf_journal_export.py (Journale der VF_n-Bloecke -aus data/polylines.dxf, XDATA-App SSG_VF_EDIT) und nicht aus Handarbeit - -Schema siehe tests/testdata/object_data.md Abschnitt 8. +Diese Tests brauchen also einen CAD-Lauf und werden sonst uebersprungen. +Den UEBERSETZER (Spec -> Journal) prueft tests/test_vf_spec.py ohne CAD; +das Aufzeichnungsformat prueft tests/test_hm_recformat.py. + +Der Ergebnis-Record je Kette kommt aus Lisp/vf_spec.lsp (vsp-result-json). """ import json @@ -20,361 +19,114 @@ import os import pytest +HIER = os.path.dirname(__file__) +SPEC_JSON = os.path.join(HIER, "testdata", "hundm05.json") -EINGABE_TYPEN = ("point_abs", "point_rel", "real", "int", "string", "step") +# TEST_HUNDM05 schreibt nach tests/output. Ein Lauf von VF_SPEC_BAU mit +# gesetztem DXFM_VF_SPEC_OUT kann die Datei woanders ablegen - darum beide +# ueblichen Orte pruefen, statt den Test stumm zu ueberspringen. +ERGEBNIS_ORTE = [os.path.join(HIER, "output", "hundm05_results.json"), + os.path.join(HIER, "..", "results", "hundm05_results.json")] -# ============================================================ -# Hilfsfunktionen: flaches JSON in Ketten gruppieren -# ============================================================ -# Gleiche Regel wie hundm05:gruppiere in test_hundm05.lsp: ein Objekt mit -# "test_id" ist ein Kopf und beginnt eine Kette, die folgenden Objekte mit -# "typ" sind ihre Eingaben. - -def _ketten(testdata): - ketten = [] - for obj in testdata: - if "test_id" in obj: - ketten.append((obj, [])) - elif "typ" in obj and ketten: - ketten[-1][1].append(obj) - return ketten +@pytest.fixture(scope="module") +def spec_objekte(): + if not os.path.isfile(SPEC_JSON): + pytest.skip("%s fehlt (lib/vf_spec_export.py erzeugt es)" % SPEC_JSON) + with open(SPEC_JSON, encoding="utf-8") as fh: + return json.load(fh) -def _journal_laenge(eingaben): - """Anzahl der Journal-Eintraege, die der Treiber daraus baut: ein - point_rel mit "hz" wird zu ZWEI Eintraegen (DL + REAL).""" - return sum(2 if (e["typ"] == "point_rel" and "hz" in e) else 1 - for e in eingaben) +@pytest.fixture(scope="module") +def spec_ergebnisse(): + for pfad in ERGEBNIS_ORTE: + if os.path.isfile(pfad): + with open(pfad, encoding="utf-8") as fh: + return json.load(fh) + pytest.skip("hundm05_results.json fehlt - TEST_HUNDM05 in BricsCAD " + "ausfuehren") -def _pfad(*teile): - return os.path.join(os.path.dirname(os.path.abspath(__file__)), *teile) +class TestSpecBau: + def test_je_spec_ein_ergebnis(self, spec_objekte, spec_ergebnisse): + soll = [o["spec_id"] for o in spec_objekte if "spec_id" in o] + ist = [r["spec_id"] for r in spec_ergebnisse] + assert ist == soll -# ============================================================ -# Testdaten-Pruefung (hundm05.json selbst) -# ============================================================ - -class TestHundM05Testdaten: - - def test_fuenf_ketten(self, hundm05_testdata): - """Die Anlage hat fuenf am Kreisel haengende Linienzuege.""" - ketten = _ketten(hundm05_testdata) - assert len(ketten) == 5, \ - f"{len(ketten)} Ketten in hundm05.json, erwartet 5" - - def test_jedes_objekt_zuordenbar(self, hundm05_testdata): - """Kein Objekt darf zwischen Kopf und Eingabe fallen - sonst - verschluckt hundm05:gruppiere es stillschweigend.""" - for i, obj in enumerate(hundm05_testdata): - assert ("test_id" in obj) or ("typ" in obj), \ - f"Objekt {i} ist weder Kopf noch Eingabe: {sorted(obj)}" - - def test_kopf_felder(self, hundm05_testdata): - for kopf, _ in _ketten(hundm05_testdata): - assert kopf["test_id"].startswith("VF_"), kopf["test_id"] - # "typ" wuerde den Kopf zu einer Eingabe machen (siehe - # hundm05:gruppiere) - der Anlagetyp heisst darum "anlagetyp". - assert "typ" not in kopf, \ - f'{kopf["test_id"]}: Kopf darf kein Feld "typ" tragen' - assert kopf["anlagetyp"] == "linienzug", kopf["test_id"] - assert kopf["modus"] == 1, kopf["test_id"] - assert kopf["block"].startswith("VF_"), kopf["block"] - assert len(kopf["startpunkt_mm"]) == 3, kopf["test_id"] - - def test_anzahl_eingaben_stimmt(self, hundm05_testdata): - """anzahl_eingaben ist die Selbstauskunft des Kopfes - weicht sie ab, - fehlen Eingaben (z.B. durch eine Handaenderung).""" - for kopf, eingaben in _ketten(hundm05_testdata): - assert kopf["anzahl_eingaben"] == len(eingaben), \ - f'{kopf["test_id"]}: anzahl_eingaben=' \ - f'{kopf["anzahl_eingaben"]}, gezaehlt {len(eingaben)}' - - def test_eingabe_typen_bekannt(self, hundm05_testdata): - for kopf, eingaben in _ketten(hundm05_testdata): - for i, e in enumerate(eingaben): - assert e["typ"] in EINGABE_TYPEN, \ - f'{kopf["test_id"]} Eingabe {i}: unbekannter Typ ' \ - f'{e["typ"]}' - assert e.get("kommentar"), \ - f'{kopf["test_id"]} Eingabe {i}: kein Kommentar' - - def test_kette_beginnt_mit_startpunkt(self, hundm05_testdata): - """Erste Eingabe = absoluter Startpunkt, zweite = Starthoehe. Fehlt - der Punkt, wuerde das Replay ihn interaktiv erfragen und die Kette an - der falschen Stelle aufbauen (siehe hundm05:build-linienzug).""" - for kopf, eingaben in _ketten(hundm05_testdata): - assert eingaben[0]["typ"] == "point_abs", kopf["test_id"] - assert eingaben[1]["typ"] == "real", kopf["test_id"] - punkt = eingaben[0]["wert"] - assert len(punkt) == 3, kopf["test_id"] - assert punkt == kopf["startpunkt_mm"], \ - f'{kopf["test_id"]}: point_abs {punkt} != startpunkt_mm ' \ - f'{kopf["startpunkt_mm"]}' - assert eingaben[1]["wert"] == punkt[2], \ - f'{kopf["test_id"]}: Starthoehe {eingaben[1]["wert"]} != ' \ - f'Z des Startpunkts {punkt[2]}' - - def test_hz_nur_beim_ersten_segment(self, hundm05_testdata): - """vfl-in-abstand journalisiert die Fahrtrichtung NUR beim ersten - Segment der Kette; jedes weitere erbt sie vom Vorgaenger. Ein "hz" an - spaeterer Stelle waere ein Wert zu viel in der Replay-Queue und - verschiebt alle folgenden Antworten (Desync).""" - for kopf, eingaben in _ketten(hundm05_testdata): - rel = [e for e in eingaben if e["typ"] == "point_rel"] - assert rel, f'{kopf["test_id"]}: kein Segment' - assert "hz" in rel[0], \ - f'{kopf["test_id"]}: erstes Segment ohne hz' - for i, e in enumerate(rel[1:], start=2): - assert "hz" not in e, \ - f'{kopf["test_id"]}: Segment {i} traegt hz - nur das ' \ - f'erste Segment darf eines haben' - - def test_segmentlaengen_positiv(self, hundm05_testdata): - for kopf, eingaben in _ketten(hundm05_testdata): - for e in eingaben: - if e["typ"] == "point_rel": - assert e["dL"] > 0, f'{kopf["test_id"]}: dL={e["dL"]}' - - def test_menue_antworten_sind_strings(self, hundm05_testdata): - """Menue-Antworten muessen Strings bleiben ("1"/"2"/"90"/"30"): der - Produktionscode vergleicht mit (= antwort "1"), eine Zahl wuerde - stumm in den Default-Zweig laufen.""" - for kopf, eingaben in _ketten(hundm05_testdata): - for i, e in enumerate(eingaben): - if e["typ"] in ("string", "step"): - assert isinstance(e["wert"], str), \ - f'{kopf["test_id"]} Eingabe {i}: {e["wert"]!r} ist ' \ - f'kein String' - - def test_glieder_vorhanden(self, hundm05_testdata): - """Jede Kette hat Glied-Marker (STEP) - sie sind der Anker fuer den - Sektions-/Glied-Edit und die Kurzbeschreibung.""" - for kopf, eingaben in _ketten(hundm05_testdata): - glieder = [e["wert"] for e in eingaben if e["typ"] == "step"] - assert glieder, f'{kopf["test_id"]}: kein STEP-Marker' - for g in glieder: - assert g in ("Linie-GF", "Linie-VF", "Linie", "GF-Bogen", - "Horizontal-VF", "Vario-Kurve", "ES"), \ - f'{kopf["test_id"]}: unbekanntes Glied {g}' - - def test_arrays_einzeilig(self): - """Jedes Array muss komplett in EINER Zeile stehen. - - ssg-parse-json-array (Lisp/ssg_core.lsp) liest die Datei zeilenweise, - und ssg-cfg-parse-array braucht das ganze Array in einem String. Ein - von json.dump(indent=2) umgebrochenes Array wird zu nil - der Treiber - baut die Kette dann nicht an ihrer Position. Das ist genau der - Fehler, der die Anlage einmal komplett am Ursprung aufgebaut hat, und - er ist in der Zeichnung nicht als Datenfehler zu erkennen. - """ - with open(_pfad("testdata", "hundm05.json"), encoding="utf-8") as fh: - for nr, zeile in enumerate(fh, start=1): - offen = zeile.count("[") - zeile.count("]") - # Die aeusserste Klammer des Dokuments darf allein stehen. - if zeile.strip() in ("[", "]"): - continue - assert offen == 0, \ - f"Zeile {nr}: Array ueber mehrere Zeilen umgebrochen - " \ - f"ssg-cfg-parse-array kann das nicht lesen: {zeile.strip()!r}" - - def test_flach_fuer_lisp_parser(self, hundm05_testdata): - """Die Datei muss FLACH bleiben. - - ssg-load-json / ssg-parse-json-array behandelt jedes "{" als - Objektanfang und jedes "}"/"}," als Objektende - es kennt keine - Verschachtelung. Ein Objekt in einem Objekt (oder ein Array von - Objekten wie "eingaben": [...]) wuerde das umgebende Objekt - verschlucken. Dieser Test zaehlt die Objekte so, wie der LISP-Parser - sie zaehlen wuerde, und vergleicht mit dem echten JSON. - """ - objekte = 0 - with open(_pfad("testdata", "hundm05.json"), encoding="utf-8") as fh: - for zeile in fh: - gestrippt = zeile.strip() - if gestrippt == "{": - objekte += 1 - elif gestrippt in ("}", "},"): - continue - elif gestrippt.startswith('"'): - # Ein Wert, der ein Objekt oder eine Objektliste eroeffnet, - # macht die Datei fuer den LISP-Parser unlesbar. - assert not gestrippt.endswith(("{", "[")), \ - f"verschachtelter Wert: {gestrippt!r}" - assert objekte == len(hundm05_testdata), \ - f"LISP-Parser saehe {objekte} Objekte, json.load sieht " \ - f"{len(hundm05_testdata)}" - - -# ============================================================ -# Ergebnis-Pruefung (results.json vs. testdata) -# ============================================================ - -class TestHundM05Results: - - def test_je_kette_ein_ergebnis(self, hundm05_testdata, hundm05_results): - soll = [k["test_id"] for k, _ in _ketten(hundm05_testdata)] - ist = [r["test_id"] for r in hundm05_results] - assert ist == soll, f"Ergebnisse {ist}, erwartet {soll}" - for r in hundm05_results: - assert r["kind"] == "linienzug", f'{r["test_id"]}: {r["kind"]}' - - def test_status_executed(self, hundm05_results): - """"desync" (Journal-Werte nicht verbraucht) und "failed" (Abbruch) - sind beide echte Fehler - siehe hundm05:build-linienzug.""" - for r in hundm05_results: + def test_status_executed(self, spec_ergebnisse): + for r in spec_ergebnisse: assert r["status"] == "executed", \ - f'{r["test_id"]}: Status={r["status"]}' + f'{r["spec_id"]}: Status={r["status"]}, {r.get("fehler_text")}' - def test_journal_vollstaendig_verbraucht(self, hundm05_testdata, - hundm05_results): - """Alle aufgezeichneten Werte muessen im Bau abgerufen worden sein. - Bleibt etwas offen, ist der Ablauf von der Aufzeichnung abgewichen und - die Geometrie kann der Vorlage nicht entsprechen.""" - soll = {k["test_id"]: _journal_laenge(e) - for k, e in _ketten(hundm05_testdata)} - for r in hundm05_results: - assert "eingaben_offen" in r, ( - f'{r["test_id"]}: kein eingaben_offen im Ergebnis - die Kette ' - f'ist vor der Auswertung abgebrochen (status=' - f'{r.get("status")!r}, {r.get("fehler_text")})') + def test_keine_spec_fehler(self, spec_ergebnisse): + for r in spec_ergebnisse: + assert not r["spec_fehler"], \ + f'{r["spec_id"]}: {r["spec_fehler"]}' + + def test_kein_prompt(self, spec_ergebnisse): + """Der positive Beweis fuer "kein Dialog, keine Konsolenfrage": die + zaehlenden Stubs wurden nie erreicht.""" + for r in spec_ergebnisse: + assert r["prompts"] == 0, \ + f'{r["spec_id"]}: {r["prompts"]} Live-Eingabe(n) erreicht' + + def test_journal_vollstaendig_verbraucht(self, spec_ergebnisse): + for r in spec_ergebnisse: assert r["eingaben_offen"] == 0, \ - f'{r["test_id"]}: {r["eingaben_offen"]} Journal-Werte offen' - assert r["eingaben_gesamt"] == soll[r["test_id"]], \ - f'{r["test_id"]}: {r["eingaben_gesamt"]} Journal-Eintraege ' \ - f'abgespielt, erwartet {soll[r["test_id"]]}' + f'{r["spec_id"]}: {r["eingaben_offen"]} Werte offen' + assert r["eingaben_gesamt"] > 0 - def test_keine_live_eingabe(self, hundm05_results): - """Kein Prompt, kein Dialog: der Treiber schaltet *vfl-headless* ein - (harter Abbruch statt stillem Rueckfall auf Live-Eingabe, siehe - vfl-headless-abbruch in Lisp/vf_linienzug.lsp) UND ersetzt zusaetzlich - getpoint/getstring/getint/getreal durch zaehlende Stubs. Beide Wege - muessen leer bleiben - "prompts" ist der positive Beweis, dass keine - Eingabe ausserhalb der vfl-in-*-Wrapper erreicht wurde.""" - for r in hundm05_results: - if "prompts" in r: - assert r["prompts"] == 0, \ - f'{r["test_id"]}: {r["prompts"]} Live-Eingabe(n) erreicht' - assert not r.get("headless_fehler"), \ - f'{r["test_id"]}: Headless-Abbruch bei {r["headless_fehler"]}' + def test_glieder_wie_geplant(self, spec_ergebnisse): + """Der Desync-Detektor, der auch dann greift, wenn die Queue aufgeht: + eine geometrisch abgewiesene Sektion verbraucht nur weniger + Eintraege, die Glied-Folge weicht dann aber ab.""" + for r in spec_ergebnisse: + assert r["glieder_ist"] == r["glieder_soll"], \ + f'{r["spec_id"]}: gebaut {r["glieder_ist"]}, ' \ + f'geplant {r["glieder_soll"]}' - def test_keine_geometrie_meldungen(self, hundm05_results): - """*vfl-meldungen* sammelt die abgewiesenen Sektionen (frueher - blockierende alert-Fenster, jetzt vfl-meldung). Eine Meldung heisst: - ein Glied liess sich geometrisch nicht bauen - der Nachbau weicht dann - von der Vorlage ab, auch wenn das Journal aufgeht.""" - for r in hundm05_results: - assert not r.get("meldungen"), \ - f'{r["test_id"]}: Meldungen aus dem Bau: {r["meldungen"]}' + def test_keine_meldungen(self, spec_ergebnisse): + for r in spec_ergebnisse: + assert not r["meldungen"], \ + f'{r["spec_id"]}: Meldungen aus dem Bau: {r["meldungen"]}' - def test_vf_block_erzeugt(self, hundm05_results): - for r in hundm05_results: + def test_vf_block_erzeugt(self, spec_ergebnisse): + for r in spec_ergebnisse: assert r["block_name"].startswith("VF_"), \ - f'{r["test_id"]}: Block "{r["block_name"]}" ist kein VF_-Block' - assert r["block_handle"], f'{r["test_id"]}: kein Handle' + f'{r["spec_id"]}: Block {r["block_name"]!r}' + assert r["block_handle"] - def test_dimension_einheitlich(self, hundm05_results): - """Alle Ergebnisse eines Laufs stammen aus EINER Bau-Dimension. - - Die Eingabedaten sind dimensionsfrei; ob _2D- oder _3D-Blockvarianten - verwendet werden, loest ssg-ils-dim-aktuell auf. Ein Lauf, in dem beide - vorkommen, waere ein halb umgeschalteter Zustand (z.B. weil ein - vorheriger Test *ssg-ils-dim* nicht zurueckgesetzt hat). - """ - dims = {r.get("dimension") for r in hundm05_results} - assert len(dims) == 1, f"gemischte Bau-Dimensionen im Lauf: {dims}" - dim = dims.pop() - assert dim in ("2D", "3D"), \ - f'Bau-Dimension "{dim}" ist weder 2D noch 3D' - - def test_startpunkt_getroffen(self, hundm05_testdata, hundm05_results): - """Der VF_n-Block wird mit dem Kettenstartpunkt als Basispunkt - gewickelt (ssg-block-wrap-welt in vfl-block-erstellen) - sein - Einfuegepunkt muss also der Startpunkt aus den Eingaben sein.""" - soll = {k["test_id"]: k["startpunkt_mm"] - for k, _ in _ketten(hundm05_testdata)} - for r in hundm05_results: - p = soll[r["test_id"]] + def test_startpunkt_getroffen(self, spec_objekte, spec_ergebnisse): + """Der Einfuegepunkt des VF_-Blocks ist der Kettenstart aus der + Spec - er ist der einzige absolute Anker der ganzen Kette.""" + soll = dict((o["spec_id"], o["start_punkt"]) for o in spec_objekte + if "spec_id" in o) + for r in spec_ergebnisse: + erwartet = soll[r["spec_id"]] ist = r["insert_point"] - for achse, i in (("X", 0), ("Y", 1), ("Z", 2)): - assert abs(ist[i] - p[i]) < 1.0, \ - f'{r["test_id"]}: Einfuegepunkt {achse} {ist[i]} != ' \ - f'{p[i]}' + for achse, (e, i) in enumerate(zip(erwartet, ist)): + assert abs(e - i) < 1.0, \ + f'{r["spec_id"]}: Achse {achse} ist {i}, erwartet {e}' - def test_as_es_attribute_aus_eingaben(self, hundm05_testdata, - hundm05_results): - """WINKEL_AS/SEITE_AS und WINKEL_ES/SEITE_ES muessen die Antworten aus - dem Journal wiedergeben (AS steht in der Praeambel, ES im ES-Glied).""" - seiten = {"1": "links", "2": "rechts"} - nach_id = {r["test_id"]: r for r in hundm05_results} - for kopf, eingaben in _ketten(hundm05_testdata): - r = nach_id[kopf["test_id"]] - attrs = r["actual_attributes"] - # Praeambel: [0] Punkt, [1] Hoehe, [2] AS setzen, [3] Winkel, - # [4] Seite - if eingaben[2]["wert"] == "1": - assert attrs.get("WINKEL_AS") == eingaben[3]["wert"], \ - f'{kopf["test_id"]}: WINKEL_AS=' \ - f'{attrs.get("WINKEL_AS")}, erwartet {eingaben[3]["wert"]}' - assert attrs.get("SEITE_AS") == seiten[eingaben[4]["wert"]], \ - f'{kopf["test_id"]}: SEITE_AS={attrs.get("SEITE_AS")}' - # ES: die beiden Strings direkt nach dem STEP-Marker "ES" - for i, e in enumerate(eingaben): - if e["typ"] == "step" and e["wert"] == "ES": - assert attrs.get("WINKEL_ES") == eingaben[i + 1]["wert"], \ - f'{kopf["test_id"]}: WINKEL_ES=' \ - f'{attrs.get("WINKEL_ES")}' - assert attrs.get("SEITE_ES") == \ - seiten[eingaben[i + 2]["wert"]], \ - f'{kopf["test_id"]}: SEITE_ES={attrs.get("SEITE_ES")}' - break - - def test_vario_kurven_gezaehlt(self, hundm05_testdata, hundm05_results): - """Jede Vario-Kurve im Journal muss in den VF_Bogen_*-Attributen - auftauchen (vfl-insert-vario-kurve-block zaehlt sie mit).""" - nach_id = {r["test_id"]: r for r in hundm05_results} - for kopf, eingaben in _ketten(hundm05_testdata): - soll = sum(1 for e in eingaben - if e["typ"] == "step" and e["wert"] == "Vario-Kurve") - attrs = nach_id[kopf["test_id"]]["actual_attributes"] - ist = sum(int(attrs.get(f"VF_Bogen_{lage}_{w}", 0) or 0) - for lage in ("A", "I") for w in (90, 60, 30)) - assert ist == soll, \ - f'{kopf["test_id"]}: {ist} Vario-Kurven in den Attributen, ' \ - f'{soll} im Journal' - - def test_hoehe_von_ist_starthoehe(self, hundm05_testdata, - hundm05_results): - nach_id = {r["test_id"]: r for r in hundm05_results} - for kopf, eingaben in _ketten(hundm05_testdata): - attrs = nach_id[kopf["test_id"]]["actual_attributes"] - if "HOEHE_VON_mm" not in attrs: + def test_gleiche_ergebnisse_wie_journal_pfad(self, spec_ergebnisse): + """Spec-Pfad == Journal-Pfad: dieselben Ketten, gebaut aus der Spec + statt aus dem aufgezeichneten Journal, muessen dieselben Bloecke mit + denselben Attributen ergeben. Vergleich gegen die Ergebnisse von + TEST_HUNDM05, falls beide Laeufe vorliegen.""" + pfad = os.path.join(HIER, "output", "hm_recformat_results.json") + if not os.path.isfile(pfad): + pytest.skip("tests/output/hm_recformat_results.json fehlt " + "(TEST_HM_RECFORMAT nicht gelaufen)") + with open(pfad, encoding="utf-8") as fh: + journal_ergebnisse = json.load(fh) + if len(journal_ergebnisse) != len(spec_ergebnisse): + pytest.skip("unterschiedlich viele Ketten in den beiden Laeufen") + for spec_r, journal_r in zip(spec_ergebnisse, journal_ergebnisse): + if journal_r["status"] != "executed": continue - assert abs(int(attrs["HOEHE_VON_mm"]) - eingaben[1]["wert"]) <= 1, \ - f'{kopf["test_id"]}: HOEHE_VON_mm={attrs["HOEHE_VON_mm"]}, ' \ - f'Starthoehe {eingaben[1]["wert"]}' - - -# ============================================================ -# DXF Geometrie-Pruefung (ezdxf) -# ============================================================ - -class TestHundM05Geometry: - - def test_block_anzahl(self, hundm05_dxf, hundm05_testdata): - """Je Kette muss ein VF_-Block im Modelspace stehen.""" - n_vf = sum(1 for e in hundm05_dxf.modelspace() - if e.dxftype() == "INSERT" and e.dxf.name.startswith("VF_")) - soll = len(_ketten(hundm05_testdata)) - assert n_vf >= soll, f"VF_: {n_vf} < {soll}" - - def test_handles_im_modelspace(self, hundm05_dxf, hundm05_results): - """Jedes gebaute Block-Handle (executed) liegt im Modelspace.""" - handles = {e.dxf.handle for e in hundm05_dxf.modelspace() - if e.dxftype() == "INSERT"} - for r in hundm05_results: - if r["status"] == "executed" and r["block_handle"]: - assert r["block_handle"] in handles, \ - f'{r["test_id"]}: Handle {r["block_handle"]} nicht im ' \ - f'Modelspace' + for tag, wert in journal_r["actual_attributes"].items(): + assert spec_r["actual_attributes"].get(tag) == wert, \ + f'{spec_r["spec_id"]}: Attribut {tag} ist ' \ + f'{spec_r["actual_attributes"].get(tag)!r}, ' \ + f'Journal-Pfad hatte {wert!r}' diff --git a/tests/test_vf_journal_grammatik.py b/tests/test_vf_journal_grammatik.py index 943a7c1..081b35e 100644 --- a/tests/test_vf_journal_grammatik.py +++ b/tests/test_vf_journal_grammatik.py @@ -8,7 +8,7 @@ Warum: der Dekoder ist die ZWEITE Implementierung der Linienzug-Grammatik (erste ist der Bau-Ablauf in Lisp/vf_linienzug.lsp selbst). Nur wenn beide dieselbe Frage-Reihenfolge kennen, laesst sich ein Journal aus einer echten Zeichnung verlustfrei in das Testdatenschema zuruecklesen -(tests/testdata/hundm05.json). +(tests/testdata/hm_recformat.json). Die 5 echten HundM-Ketten deckten zwei Zweige NICHT ab, weil ihre Daten sie nicht enthalten: diff --git a/tests/test_vf_spec.lsp b/tests/test_vf_spec.lsp index 2adf219..4737279 100644 --- a/tests/test_vf_spec.lsp +++ b/tests/test_vf_spec.lsp @@ -15,8 +15,8 @@ ;; (lib/vf_journal_export.py) - drei Implementierungen derselben ;; Grammatik, die einander pruefen. ;; 2. Die 5 echten HundM-Ketten: die Spec-Datei -;; tests/testdata/vf_spec_hundm05.json muss dieselben Journale ergeben -;; wie das aufgezeichnete Protokoll tests/testdata/hundm05.json. +;; tests/testdata/hundm05.json muss dieselben Journale ergeben +;; wie das aufgezeichnete Protokoll tests/testdata/hm_recformat.json. ;; Das ist der Beweis "Spec-Pfad == Journal-Pfad" auf Datenebene; der ;; Beweis auf Geometrie-Ebene ist VF_SPEC_BAU (siehe tests/test_vf_spec.py). ;; @@ -26,8 +26,9 @@ ;; (load (strcat (getenv "DXFMAKRO") "/tests/test_vf_spec.lsp")) ;; TEST_VF_SPEC ;; -;; Speichert tests/output/vf_spec_results.json ist NICHT dieser Test - -;; das schreibt VF_SPEC_BAU. Hier: tests/output/vf_spec_unit_results.json. +;; Dieser Test baut KEINE Geometrie - er prueft nur den Uebersetzer. +;; Ergebnis: tests/output/vf_spec_unit_results.json (Pass/Fail-Zaehler). +;; Die gebaute Anlage prueft TEST_HUNDM05 (tests/test_hundm05.lsp). ;; ============================================================ (setq *tsp-pass* 0 *tsp-fail* 0 *tsp-fails* '()) @@ -362,8 +363,8 @@ ;; ============================================================ ;; 4. Die 5 echten HundM-Ketten: Spec == aufgezeichnetes Journal ;; ============================================================ -;; Das aufgezeichnete Protokoll (hundm05.json) in Journal-Tokens -;; zurueckuebersetzen - dieselbe Umrechnung wie in test_hundm05.lsp, hier +;; Das aufgezeichnete Protokoll (hm_recformat.json) in Journal-Tokens +;; zurueckuebersetzen - dieselbe Umrechnung wie in test_hm_recformat.lsp, hier ;; bewusst dupliziert, damit der Test ohne den anderen Treiber laeuft. (defun tsp-als-string (v) @@ -410,9 +411,9 @@ (defun tsp-tests-echte-ketten ( / p-datei s-datei p-daten specs journale i n spec soll ist sid) - (setq p-datei (strcat (getenv "DXFMAKRO") "/tests/testdata/hundm05.json")) + (setq p-datei (strcat (getenv "DXFMAKRO") "/tests/testdata/hm_recformat.json")) (setq s-datei (strcat (getenv "DXFMAKRO") - "/tests/testdata/vf_spec_hundm05.json")) + "/tests/testdata/hundm05.json")) (cond ((null (findfile p-datei)) (princ (strcat "\n [SKIP] " p-datei " fehlt"))) diff --git a/tests/test_vf_spec.py b/tests/test_vf_spec.py index e82f68b..a957d37 100644 --- a/tests/test_vf_spec.py +++ b/tests/test_vf_spec.py @@ -1,20 +1,21 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ -test_vf_spec.py - Validiert die Spec-Ebene des VF-Linienzugs. - -Drei Ebenen, die ersten zwei laufen ohne BricsCAD: +test_vf_spec.py - Validiert den Spec-UEBERSETZER des VF-Linienzugs. +Laeuft komplett ohne BricsCAD. 1. Rundlauf: fuer jede der 5 echten HundM-Ketten muss journal_aus_spec(spec_aus_journal(tokens)) == tokens gelten. Das beweist, dass die Spec das Eingabe-Journal VERLUSTFREI abbildet - sonst waere ein spec-gebauter Nachbau eine andere Kette als das Original. - 2. Die Spec-Testdaten (tests/testdata/vf_spec_hundm05.json): Struktur, - Lesbarkeit durch den LISP-Parser, und dass sie dieselben Journale - ergeben wie tests/testdata/hundm05.json. - 3. Die Ergebnisse von VF_SPEC_BAU (tests/output/vf_spec_results.json): - jede Kette gebaut, kein Prompt, kein offener Journal-Wert, Glied-Folge - wie geplant. + 2. Die Spec-Testdaten (tests/testdata/hundm05.json): Struktur, Lesbarkeit + durch den LISP-Parser, und dass sie dieselben Journale ergeben wie die + Aufzeichnung tests/testdata/hm_recformat.json. + 3. Validierung: was die Spec nicht hergibt, muss ein Fehler sein. + 4. Die Erwartungswerte in tests/test_vf_spec.lsp stimmen mit dem hier + geprueften Emitter ueberein. + +Die GEBAUTE Geometrie prueft tests/test_hundm05.py (braucht BricsCAD). Der LISP-Emitter (vsp-journal in Lisp/vf_spec.lsp) ist die zweite Implementierung derselben Grammatik; er wird in tests/test_vf_spec.lsp gegen @@ -32,14 +33,11 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "lib")) import vf_spec_export as vse # noqa: E402 HIER = os.path.dirname(__file__) -PROTOKOLL = os.path.join(HIER, "testdata", "hundm05.json") -SPEC_JSON = os.path.join(HIER, "testdata", "vf_spec_hundm05.json") - -# VF_SPEC_BAU schreibt nach tests/output. Aeltere Laeufe (und ein Lauf mit -# gesetztem DXFM_VF_SPEC_OUT) koennen die Datei woanders haben - darum beide -# ueblichen Orte pruefen, statt den Test stumm zu ueberspringen. -ERGEBNIS_ORTE = [os.path.join(HIER, "output", "vf_spec_results.json"), - os.path.join(HIER, "..", "results", "vf_spec_results.json")] +# Die AUFZEICHNUNG (rohes Journal aus der Kundenzeichnung, Frage-Antwort- +# Protokoll) und die daraus abgeleitete SPEC. Erstere ist die Vorlage, an +# der die zweite gemessen wird. +PROTOKOLL = os.path.join(HIER, "testdata", "hm_recformat.json") +SPEC_JSON = os.path.join(HIER, "testdata", "hundm05.json") def _lade(pfad, was): @@ -49,14 +47,6 @@ def _lade(pfad, was): return json.load(fh) -def _lade_erstes(orte, was): - for pfad in orte: - if os.path.isfile(pfad): - with open(pfad, encoding="utf-8") as fh: - return json.load(fh) - pytest.skip("%s fehlt (%s)" % (" bzw. ".join(orte), was)) - - @pytest.fixture(scope="module") def protokoll(): return _lade(PROTOKOLL, "lib/vf_journal_export.py erzeugt es") @@ -67,13 +57,8 @@ def spec_objekte(): return _lade(SPEC_JSON, "lib/vf_spec_export.py erzeugt es") -@pytest.fixture(scope="module") -def spec_ergebnisse(): - return _lade_erstes(ERGEBNIS_ORTE, "VF_SPEC_BAU in BricsCAD erzeugt es") - - def _ketten(protokoll): - """Gleiche Gruppierregel wie hundm05:gruppiere in LISP.""" + """Gleiche Gruppierregel wie hmrec:gruppiere in LISP.""" ketten = [] for obj in protokoll: if "test_id" in obj: @@ -458,92 +443,3 @@ class TestLispErwartungen: f" LISP: {ist}\n Python: {soll}\n" f"Entweder ist die Tokenfolge im LISP-Test falsch uebertragen, " f"oder die Spec hier und dort beschreibt nicht dasselbe.") - - -# ============================================================ -# 5. Ergebnisse von VF_SPEC_BAU (braucht BricsCAD) -# ============================================================ - -class TestSpecBau: - - def test_je_spec_ein_ergebnis(self, spec_objekte, spec_ergebnisse): - soll = [o["spec_id"] for o in spec_objekte if "spec_id" in o] - ist = [r["spec_id"] for r in spec_ergebnisse] - assert ist == soll - - def test_status_executed(self, spec_ergebnisse): - for r in spec_ergebnisse: - assert r["status"] == "executed", \ - f'{r["spec_id"]}: Status={r["status"]}, {r.get("fehler_text")}' - - def test_keine_spec_fehler(self, spec_ergebnisse): - for r in spec_ergebnisse: - assert not r["spec_fehler"], \ - f'{r["spec_id"]}: {r["spec_fehler"]}' - - def test_kein_prompt(self, spec_ergebnisse): - """Der positive Beweis fuer "kein Dialog, keine Konsolenfrage": die - zaehlenden Stubs wurden nie erreicht.""" - for r in spec_ergebnisse: - assert r["prompts"] == 0, \ - f'{r["spec_id"]}: {r["prompts"]} Live-Eingabe(n) erreicht' - - def test_journal_vollstaendig_verbraucht(self, spec_ergebnisse): - for r in spec_ergebnisse: - assert r["eingaben_offen"] == 0, \ - f'{r["spec_id"]}: {r["eingaben_offen"]} Werte offen' - assert r["eingaben_gesamt"] > 0 - - def test_glieder_wie_geplant(self, spec_ergebnisse): - """Der Desync-Detektor, der auch dann greift, wenn die Queue aufgeht: - eine geometrisch abgewiesene Sektion verbraucht nur weniger - Eintraege, die Glied-Folge weicht dann aber ab.""" - for r in spec_ergebnisse: - assert r["glieder_ist"] == r["glieder_soll"], \ - f'{r["spec_id"]}: gebaut {r["glieder_ist"]}, ' \ - f'geplant {r["glieder_soll"]}' - - def test_keine_meldungen(self, spec_ergebnisse): - for r in spec_ergebnisse: - assert not r["meldungen"], \ - f'{r["spec_id"]}: Meldungen aus dem Bau: {r["meldungen"]}' - - def test_vf_block_erzeugt(self, spec_ergebnisse): - for r in spec_ergebnisse: - assert r["block_name"].startswith("VF_"), \ - f'{r["spec_id"]}: Block {r["block_name"]!r}' - assert r["block_handle"] - - def test_startpunkt_getroffen(self, spec_objekte, spec_ergebnisse): - """Der Einfuegepunkt des VF_-Blocks ist der Kettenstart aus der - Spec - er ist der einzige absolute Anker der ganzen Kette.""" - soll = dict((o["spec_id"], o["start_punkt"]) for o in spec_objekte - if "spec_id" in o) - for r in spec_ergebnisse: - erwartet = soll[r["spec_id"]] - ist = r["insert_point"] - for achse, (e, i) in enumerate(zip(erwartet, ist)): - assert abs(e - i) < 1.0, \ - f'{r["spec_id"]}: Achse {achse} ist {i}, erwartet {e}' - - def test_gleiche_ergebnisse_wie_journal_pfad(self, spec_ergebnisse): - """Spec-Pfad == Journal-Pfad: dieselben Ketten, gebaut aus der Spec - statt aus dem aufgezeichneten Journal, muessen dieselben Bloecke mit - denselben Attributen ergeben. Vergleich gegen die Ergebnisse von - TEST_HUNDM05, falls beide Laeufe vorliegen.""" - pfad = os.path.join(HIER, "output", "hundm05_results.json") - if not os.path.isfile(pfad): - pytest.skip("tests/output/hundm05_results.json fehlt " - "(TEST_HUNDM05 nicht gelaufen)") - with open(pfad, encoding="utf-8") as fh: - journal_ergebnisse = json.load(fh) - if len(journal_ergebnisse) != len(spec_ergebnisse): - pytest.skip("unterschiedlich viele Ketten in den beiden Laeufen") - for spec_r, journal_r in zip(spec_ergebnisse, journal_ergebnisse): - if journal_r["status"] != "executed": - continue - for tag, wert in journal_r["actual_attributes"].items(): - assert spec_r["actual_attributes"].get(tag) == wert, \ - f'{spec_r["spec_id"]}: Attribut {tag} ist ' \ - f'{spec_r["actual_attributes"].get(tag)!r}, ' \ - f'Journal-Pfad hatte {wert!r}' diff --git a/tests/testdata/hm_recformat.json b/tests/testdata/hm_recformat.json new file mode 100644 index 0000000..b9ffb11 --- /dev/null +++ b/tests/testdata/hm_recformat.json @@ -0,0 +1,1492 @@ +[ + { + "test_id": "VF_hundm05_LZ_01", + "anlagetyp": "linienzug", + "modus": 1, + "anzahl_eingaben": 65, + "block": "VF_87", + "dxf_handle": "1BEB5", + "startpunkt_mm": [4912.239129, 1230.886425, 2154.0], + "rotation_grad": 0.0, + "beschreibung": "Linienzug (Modus 1), Start Z2154: GF-Linie 2249 mm / 3 Grad -> Horizontal-VF 3257 mm -> Vario-Kurve 90 rechts innen -> Vario-Kurve 90 rechts innen -> Horizontal-VF 1210 mm -> Vario-Kurve 30 rechts innen -> Vario-Kurve 30 links innen -> ES 90 links", + "sivas_id": "0025", + "sivas_bezeichnung": "VarioFoerderer :1", + "planquadrat": "B/1", + "erwartung_hinweis": "Sivas-Export (EXPORTSIVAS), Strecke 0025: DeltaL_mm=1934, DeltaH_mm=463, Hoehe_Von_mm=2154, Hoehe_Bis_mm=1690, L_GF_m=1.826,0.4,0.4, L_VF_m=1.859,1.434,0.1,1.83, VF_Winkel=0,0,0,0, Anzahl_Separator=3", + "quelle": "INSERT 1BEB5 (Block VF_87) in polylines.dxf, XDATA-App SSG_VF_EDIT, Marker linienzug; im Journal aufgezeichneter Kettenstart: [4912.239129, 1230.886425, 0.000000]" + }, + { + "typ": "point_abs", + "wert": [4912.239129, 1230.886425, 2154.0], + "kommentar": "getpoint Startpunkt der Kette (absoluter Weltpunkt, einziger Anker der ganzen Kette)" + }, + { + "typ": "real", + "wert": 2154.0, + "kommentar": "getreal Hoehe (Z) Startpunkt -> 2154.0 mm" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring AS-Element setzen? (1=Ja, 2=Nein) -> Ja" + }, + { + "typ": "string", + "wert": "90", + "kommentar": "getstring AS-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring AS-Seite (1=links, 2=rechts) -> links" + }, + { + "typ": "step", + "wert": "Linie-GF", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Naechstes Element am Kettenanfang (1=Neue Linie GF, 2=Neue Linie Ab/Auf VF, 3=Horizontal-VF, 4=Neue Linie bis Kettenende) -> Neue Linie: GF" + }, + { + "typ": "point_rel", + "dL": 2249.25106, + "hz": 90.0, + "kommentar": "getpoint Endpunkt der GF-Linie: dL=2249.3 mm, gesnappte Fahrtrichtung hz=90 Grad (nur beim ersten Segment der Kette journalisiert)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Gefaelle festlegen (1=ueber Zielhoehe, 2=ueber Neigungswinkel) -> ueber Neigungswinkel" + }, + { + "typ": "real", + "wert": 3.0, + "kommentar": "getreal Neigungswinkel -> 3.0 Grad" + }, + { + "typ": "string", + "wert": "3", + "kommentar": "getstring Ist das Kettenende? (1=Ja mit ES, 2=Ja ohne ES, 3=Nein weiterbauen) -> Nein, weiterbauen" + }, + { + "typ": "step", + "wert": "Horizontal-VF", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "4", + "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Horizontal-VF" + }, + { + "typ": "point_rel", + "dL": 3256.853781, + "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=3256.9 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring GF-Verteilung (1=halbe Staustrecke am Ausgang, 2=alles am Einlauf) -> alles am Einlauf" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" + }, + { + "typ": "step", + "wert": "Vario-Kurve", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "int", + "wert": 90, + "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" + }, + { + "typ": "point_rel", + "dL": 1434.100265, + "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1434.1 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" + }, + { + "typ": "step", + "wert": "Vario-Kurve", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "int", + "wert": 90, + "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Ja, nur Motorstation" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist das Kettenende? (1=Ja, Separator + ES-Element, 2=Nein, weiterbauen) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator an dieser Stelle einfuegen? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "step", + "wert": "Horizontal-VF", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "4", + "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Horizontal-VF" + }, + { + "typ": "point_rel", + "dL": 1210.083164, + "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1210.1 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring GF-Verteilung (1=halbe Staustrecke am Ausgang, 2=alles am Einlauf) -> alles am Einlauf" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" + }, + { + "typ": "step", + "wert": "Vario-Kurve", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "int", + "wert": 30, + "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 30 Grad" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" + }, + { + "typ": "point_rel", + "dL": 1830.286247, + "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1830.3 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" + }, + { + "typ": "step", + "wert": "Vario-Kurve", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "int", + "wert": 30, + "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 30 Grad" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> links" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Ja, nur Motorstation" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Ist das Kettenende? (1=Ja, Separator + ES-Element, 2=Nein, weiterbauen) -> Ja, Separator + ES-Element" + }, + { + "typ": "step", + "wert": "ES", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "90", + "kommentar": "getstring ES-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring ES-Seite (1=links, 2=rechts) -> links" + }, + { + "test_id": "VF_hundm05_LZ_02", + "anlagetyp": "linienzug", + "modus": 1, + "anzahl_eingaben": 54, + "block": "VF_46", + "dxf_handle": "EFD4", + "startpunkt_mm": [8571.95068, 1276.061424, 2494.930831], + "rotation_grad": 0.0, + "beschreibung": "Linienzug (Modus 1), Start Z2495: GF-Linie 3149 mm / 3 Grad -> Horizontal-VF 1575 mm -> Vario-Kurve 90 rechts innen -> Vario-Kurve 90 rechts innen -> Linie bis Kettenende 1153 mm auf Z2154 -> ES 90 links", + "sivas_id": "0019", + "sivas_bezeichnung": "VarioFoerderer :5", + "planquadrat": "D/1", + "erwartung_hinweis": "Sivas-Export (EXPORTSIVAS), Strecke 0019: DeltaL_mm=1788, DeltaH_mm=220, Hoehe_Von_mm=2154, Hoehe_Bis_mm=2374, L_GF_m=2.727,0.4,0.418, L_VF_m=0.177,0.339,0.356,0.548, VF_Winkel=0,0,39,0, Anzahl_Separator=3", + "quelle": "INSERT EFD4 (Block VF_46) in polylines.dxf, XDATA-App SSG_VF_EDIT, Marker linienzug; im Journal aufgezeichneter Kettenstart: [17201.778323, 1205.360995, 2100.000000]" + }, + { + "typ": "point_abs", + "wert": [8571.95068, 1276.061424, 2494.930831], + "kommentar": "getpoint Startpunkt der Kette (absoluter Weltpunkt, einziger Anker der ganzen Kette)" + }, + { + "typ": "real", + "wert": 2494.930831, + "kommentar": "getreal Hoehe (Z) Startpunkt -> 2494.9 mm" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring AS-Element setzen? (1=Ja, 2=Nein) -> Ja" + }, + { + "typ": "string", + "wert": "90", + "kommentar": "getstring AS-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring AS-Seite (1=links, 2=rechts) -> links" + }, + { + "typ": "step", + "wert": "Linie-GF", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Naechstes Element am Kettenanfang (1=Neue Linie GF, 2=Neue Linie Ab/Auf VF, 3=Horizontal-VF, 4=Neue Linie bis Kettenende) -> Neue Linie: GF" + }, + { + "typ": "point_rel", + "dL": 3149.399248, + "hz": 90.0, + "kommentar": "getpoint Endpunkt der GF-Linie: dL=3149.4 mm, gesnappte Fahrtrichtung hz=90 Grad (nur beim ersten Segment der Kette journalisiert)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Gefaelle festlegen (1=ueber Zielhoehe, 2=ueber Neigungswinkel) -> ueber Neigungswinkel" + }, + { + "typ": "real", + "wert": 3.0, + "kommentar": "getreal Neigungswinkel -> 3.0 Grad" + }, + { + "typ": "string", + "wert": "3", + "kommentar": "getstring Ist das Kettenende? (1=Ja mit ES, 2=Ja ohne ES, 3=Nein weiterbauen) -> Nein, weiterbauen" + }, + { + "typ": "step", + "wert": "Horizontal-VF", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "4", + "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Horizontal-VF" + }, + { + "typ": "point_rel", + "dL": 1574.912802, + "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1574.9 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring GF-Verteilung (1=halbe Staustrecke am Ausgang, 2=alles am Einlauf) -> alles am Einlauf" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" + }, + { + "typ": "step", + "wert": "Vario-Kurve", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "int", + "wert": 90, + "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" + }, + { + "typ": "point_rel", + "dL": 338.966171, + "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=339.0 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" + }, + { + "typ": "step", + "wert": "Vario-Kurve", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "int", + "wert": 90, + "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "3", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Auf/Ab-Foerderer" + }, + { + "typ": "point_rel", + "dL": 4528.583638, + "kommentar": "getpoint Endpunkt des Auf/Ab-VF-Segments: dL=4528.6 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "real", + "wert": 2335.0, + "kommentar": "getreal Zielhoehe (Z) am Segmentende -> 2335.0 mm" + }, + { + "typ": "int", + "wert": 4, + "kommentar": "getint Winkelwahl aus den gueltigen Kandidaten (vfl-waehle-winkel) -> Option 4" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" + }, + { + "typ": "point_rel", + "dL": 1443.864872, + "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1443.9 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Ja, nur Motorstation" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist das Kettenende? (1=Ja, Separator + ES-Element, 2=Nein, weiterbauen) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator an dieser Stelle einfuegen? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "step", + "wert": "Linie", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "5", + "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Neue Linie bis Kettenende" + }, + { + "typ": "point_rel", + "dL": 1153.407829, + "kommentar": "getpoint Endpunkt der Linie bis Kettenende (Ziel-XY der Kette): dL=1153.4 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "real", + "wert": 2154.0, + "kommentar": "getreal Zielhoehe (Z) am Kettenende -> 2154.0 mm" + }, + { + "typ": "step", + "wert": "ES", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "90", + "kommentar": "getstring ES-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring ES-Seite (1=links, 2=rechts) -> links" + }, + { + "test_id": "VF_hundm05_LZ_03", + "anlagetyp": "linienzug", + "modus": 1, + "anzahl_eingaben": 54, + "block": "VF_46", + "dxf_handle": "FA5A", + "startpunkt_mm": [12916.465798, 1276.061424, 2494.930831], + "rotation_grad": 0.0, + "beschreibung": "Linienzug (Modus 1), Start Z2495: GF-Linie 3149 mm / 3 Grad -> Horizontal-VF 1575 mm -> Vario-Kurve 90 rechts innen -> Vario-Kurve 90 rechts innen -> Linie bis Kettenende 1153 mm auf Z2154 -> ES 90 links", + "sivas_id": "0018", + "sivas_bezeichnung": "VarioFoerderer :4", + "planquadrat": "F/1", + "erwartung_hinweis": "Sivas-Export (EXPORTSIVAS), Strecke 0018: DeltaL_mm=1788, DeltaH_mm=220, Hoehe_Von_mm=2154, Hoehe_Bis_mm=2374, L_GF_m=2.727,0.4,0.418, L_VF_m=0.177,0.339,0.356,0.548, VF_Winkel=0,0,39,0, Anzahl_Separator=3", + "quelle": "INSERT FA5A (Block VF_46) in polylines.dxf, XDATA-App SSG_VF_EDIT, Marker linienzug; im Journal aufgezeichneter Kettenstart: [17201.778323, 1205.360995, 2100.000000]" + }, + { + "typ": "point_abs", + "wert": [12916.465798, 1276.061424, 2494.930831], + "kommentar": "getpoint Startpunkt der Kette (absoluter Weltpunkt, einziger Anker der ganzen Kette)" + }, + { + "typ": "real", + "wert": 2494.930831, + "kommentar": "getreal Hoehe (Z) Startpunkt -> 2494.9 mm" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring AS-Element setzen? (1=Ja, 2=Nein) -> Ja" + }, + { + "typ": "string", + "wert": "90", + "kommentar": "getstring AS-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring AS-Seite (1=links, 2=rechts) -> links" + }, + { + "typ": "step", + "wert": "Linie-GF", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Naechstes Element am Kettenanfang (1=Neue Linie GF, 2=Neue Linie Ab/Auf VF, 3=Horizontal-VF, 4=Neue Linie bis Kettenende) -> Neue Linie: GF" + }, + { + "typ": "point_rel", + "dL": 3149.399248, + "hz": 90.0, + "kommentar": "getpoint Endpunkt der GF-Linie: dL=3149.4 mm, gesnappte Fahrtrichtung hz=90 Grad (nur beim ersten Segment der Kette journalisiert)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Gefaelle festlegen (1=ueber Zielhoehe, 2=ueber Neigungswinkel) -> ueber Neigungswinkel" + }, + { + "typ": "real", + "wert": 3.0, + "kommentar": "getreal Neigungswinkel -> 3.0 Grad" + }, + { + "typ": "string", + "wert": "3", + "kommentar": "getstring Ist das Kettenende? (1=Ja mit ES, 2=Ja ohne ES, 3=Nein weiterbauen) -> Nein, weiterbauen" + }, + { + "typ": "step", + "wert": "Horizontal-VF", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "4", + "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Horizontal-VF" + }, + { + "typ": "point_rel", + "dL": 1574.912802, + "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1574.9 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring GF-Verteilung (1=halbe Staustrecke am Ausgang, 2=alles am Einlauf) -> alles am Einlauf" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" + }, + { + "typ": "step", + "wert": "Vario-Kurve", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "int", + "wert": 90, + "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" + }, + { + "typ": "point_rel", + "dL": 338.966171, + "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=339.0 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" + }, + { + "typ": "step", + "wert": "Vario-Kurve", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "int", + "wert": 90, + "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "3", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Auf/Ab-Foerderer" + }, + { + "typ": "point_rel", + "dL": 4528.583638, + "kommentar": "getpoint Endpunkt des Auf/Ab-VF-Segments: dL=4528.6 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "real", + "wert": 2335.0, + "kommentar": "getreal Zielhoehe (Z) am Segmentende -> 2335.0 mm" + }, + { + "typ": "int", + "wert": 4, + "kommentar": "getint Winkelwahl aus den gueltigen Kandidaten (vfl-waehle-winkel) -> Option 4" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" + }, + { + "typ": "point_rel", + "dL": 1443.864872, + "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1443.9 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Ja, nur Motorstation" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist das Kettenende? (1=Ja, Separator + ES-Element, 2=Nein, weiterbauen) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator an dieser Stelle einfuegen? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "step", + "wert": "Linie", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "5", + "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Neue Linie bis Kettenende" + }, + { + "typ": "point_rel", + "dL": 1153.407829, + "kommentar": "getpoint Endpunkt der Linie bis Kettenende (Ziel-XY der Kette): dL=1153.4 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "real", + "wert": 2154.0, + "kommentar": "getreal Zielhoehe (Z) am Kettenende -> 2154.0 mm" + }, + { + "typ": "step", + "wert": "ES", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "90", + "kommentar": "getstring ES-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring ES-Seite (1=links, 2=rechts) -> links" + }, + { + "test_id": "VF_hundm05_LZ_04", + "anlagetyp": "linienzug", + "modus": 1, + "anzahl_eingaben": 54, + "block": "VF_46", + "dxf_handle": "E498", + "startpunkt_mm": [17201.778323, 1205.360995, 2154.0], + "rotation_grad": 0.0, + "beschreibung": "Linienzug (Modus 1), Start Z2154: GF-Linie 3149 mm / 3 Grad -> Horizontal-VF 1575 mm -> Vario-Kurve 90 rechts innen -> Vario-Kurve 90 rechts innen -> Linie bis Kettenende 1153 mm auf Z2154 -> ES 90 links", + "sivas_id": "0020", + "sivas_bezeichnung": "VarioFoerderer :6", + "planquadrat": "H/1", + "erwartung_hinweis": "Sivas-Export (EXPORTSIVAS), Strecke 0020: DeltaL_mm=1788, DeltaH_mm=220, Hoehe_Von_mm=2154, Hoehe_Bis_mm=2374, L_GF_m=2.727,0.4,0.418, L_VF_m=0.177,0.339,0.356,0.548, VF_Winkel=0,0,39,0, Anzahl_Separator=3", + "quelle": "INSERT E498 (Block VF_46) in polylines.dxf, XDATA-App SSG_VF_EDIT, Marker linienzug; im Journal aufgezeichneter Kettenstart: [17201.778323, 1205.360995, 2100.000000]" + }, + { + "typ": "point_abs", + "wert": [17201.778323, 1205.360995, 2154.0], + "kommentar": "getpoint Startpunkt der Kette (absoluter Weltpunkt, einziger Anker der ganzen Kette)" + }, + { + "typ": "real", + "wert": 2154.0, + "kommentar": "getreal Hoehe (Z) Startpunkt -> 2154.0 mm" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring AS-Element setzen? (1=Ja, 2=Nein) -> Ja" + }, + { + "typ": "string", + "wert": "90", + "kommentar": "getstring AS-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring AS-Seite (1=links, 2=rechts) -> links" + }, + { + "typ": "step", + "wert": "Linie-GF", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Naechstes Element am Kettenanfang (1=Neue Linie GF, 2=Neue Linie Ab/Auf VF, 3=Horizontal-VF, 4=Neue Linie bis Kettenende) -> Neue Linie: GF" + }, + { + "typ": "point_rel", + "dL": 3149.399248, + "hz": 90.0, + "kommentar": "getpoint Endpunkt der GF-Linie: dL=3149.4 mm, gesnappte Fahrtrichtung hz=90 Grad (nur beim ersten Segment der Kette journalisiert)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Gefaelle festlegen (1=ueber Zielhoehe, 2=ueber Neigungswinkel) -> ueber Neigungswinkel" + }, + { + "typ": "real", + "wert": 3.0, + "kommentar": "getreal Neigungswinkel -> 3.0 Grad" + }, + { + "typ": "string", + "wert": "3", + "kommentar": "getstring Ist das Kettenende? (1=Ja mit ES, 2=Ja ohne ES, 3=Nein weiterbauen) -> Nein, weiterbauen" + }, + { + "typ": "step", + "wert": "Horizontal-VF", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "4", + "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Horizontal-VF" + }, + { + "typ": "point_rel", + "dL": 1574.912802, + "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1574.9 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring GF-Verteilung (1=halbe Staustrecke am Ausgang, 2=alles am Einlauf) -> alles am Einlauf" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" + }, + { + "typ": "step", + "wert": "Vario-Kurve", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "int", + "wert": 90, + "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" + }, + { + "typ": "point_rel", + "dL": 338.966171, + "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=339.0 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" + }, + { + "typ": "step", + "wert": "Vario-Kurve", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "int", + "wert": 90, + "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "3", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Auf/Ab-Foerderer" + }, + { + "typ": "point_rel", + "dL": 4528.583638, + "kommentar": "getpoint Endpunkt des Auf/Ab-VF-Segments: dL=4528.6 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "real", + "wert": 2335.0, + "kommentar": "getreal Zielhoehe (Z) am Segmentende -> 2335.0 mm" + }, + { + "typ": "int", + "wert": 4, + "kommentar": "getint Winkelwahl aus den gueltigen Kandidaten (vfl-waehle-winkel) -> Option 4" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" + }, + { + "typ": "point_rel", + "dL": 1443.864872, + "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1443.9 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Ja, nur Motorstation" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist das Kettenende? (1=Ja, Separator + ES-Element, 2=Nein, weiterbauen) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator an dieser Stelle einfuegen? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "step", + "wert": "Linie", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "5", + "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Neue Linie bis Kettenende" + }, + { + "typ": "point_rel", + "dL": 1153.407829, + "kommentar": "getpoint Endpunkt der Linie bis Kettenende (Ziel-XY der Kette): dL=1153.4 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "real", + "wert": 2154.0, + "kommentar": "getreal Zielhoehe (Z) am Kettenende -> 2154.0 mm" + }, + { + "typ": "step", + "wert": "ES", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "90", + "kommentar": "getstring ES-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring ES-Seite (1=links, 2=rechts) -> links" + }, + { + "test_id": "VF_hundm05_LZ_05", + "anlagetyp": "linienzug", + "modus": 1, + "anzahl_eingaben": 54, + "block": "VF_46", + "dxf_handle": "104E0", + "startpunkt_mm": [21490.273781, 1195.739767, 2494.930831], + "rotation_grad": 0.0, + "beschreibung": "Linienzug (Modus 1), Start Z2495: GF-Linie 3149 mm / 3 Grad -> Horizontal-VF 1575 mm -> Vario-Kurve 90 rechts innen -> Vario-Kurve 90 rechts innen -> Linie bis Kettenende 1153 mm auf Z2154 -> ES 90 links", + "sivas_id": "0008", + "sivas_bezeichnung": "VarioFoerderer :3", + "planquadrat": "J/1", + "erwartung_hinweis": "Sivas-Export (EXPORTSIVAS), Strecke 0008: DeltaL_mm=1788, DeltaH_mm=220, Hoehe_Von_mm=2154, Hoehe_Bis_mm=2374, L_GF_m=2.727,0.4,0.418, L_VF_m=0.177,0.339,0.356,0.548, VF_Winkel=0,0,39,0, Anzahl_Separator=3", + "quelle": "INSERT 104E0 (Block VF_46) in polylines.dxf, XDATA-App SSG_VF_EDIT, Marker linienzug; im Journal aufgezeichneter Kettenstart: [17201.778323, 1205.360995, 2100.000000]" + }, + { + "typ": "point_abs", + "wert": [21490.273781, 1195.739767, 2494.930831], + "kommentar": "getpoint Startpunkt der Kette (absoluter Weltpunkt, einziger Anker der ganzen Kette)" + }, + { + "typ": "real", + "wert": 2494.930831, + "kommentar": "getreal Hoehe (Z) Startpunkt -> 2494.9 mm" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring AS-Element setzen? (1=Ja, 2=Nein) -> Ja" + }, + { + "typ": "string", + "wert": "90", + "kommentar": "getstring AS-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring AS-Seite (1=links, 2=rechts) -> links" + }, + { + "typ": "step", + "wert": "Linie-GF", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Naechstes Element am Kettenanfang (1=Neue Linie GF, 2=Neue Linie Ab/Auf VF, 3=Horizontal-VF, 4=Neue Linie bis Kettenende) -> Neue Linie: GF" + }, + { + "typ": "point_rel", + "dL": 3149.399248, + "hz": 90.0, + "kommentar": "getpoint Endpunkt der GF-Linie: dL=3149.4 mm, gesnappte Fahrtrichtung hz=90 Grad (nur beim ersten Segment der Kette journalisiert)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Gefaelle festlegen (1=ueber Zielhoehe, 2=ueber Neigungswinkel) -> ueber Neigungswinkel" + }, + { + "typ": "real", + "wert": 3.0, + "kommentar": "getreal Neigungswinkel -> 3.0 Grad" + }, + { + "typ": "string", + "wert": "3", + "kommentar": "getstring Ist das Kettenende? (1=Ja mit ES, 2=Ja ohne ES, 3=Nein weiterbauen) -> Nein, weiterbauen" + }, + { + "typ": "step", + "wert": "Horizontal-VF", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "4", + "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Horizontal-VF" + }, + { + "typ": "point_rel", + "dL": 1574.912802, + "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1574.9 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring GF-Verteilung (1=halbe Staustrecke am Ausgang, 2=alles am Einlauf) -> alles am Einlauf" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" + }, + { + "typ": "step", + "wert": "Vario-Kurve", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "int", + "wert": 90, + "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" + }, + { + "typ": "point_rel", + "dL": 338.966171, + "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=339.0 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" + }, + { + "typ": "step", + "wert": "Vario-Kurve", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "int", + "wert": 90, + "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "3", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Auf/Ab-Foerderer" + }, + { + "typ": "point_rel", + "dL": 4528.583638, + "kommentar": "getpoint Endpunkt des Auf/Ab-VF-Segments: dL=4528.6 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "real", + "wert": 2335.0, + "kommentar": "getreal Zielhoehe (Z) am Segmentende -> 2335.0 mm" + }, + { + "typ": "int", + "wert": 4, + "kommentar": "getint Winkelwahl aus den gueltigen Kandidaten (vfl-waehle-winkel) -> Option 4" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" + }, + { + "typ": "point_rel", + "dL": 1443.864872, + "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1443.9 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Ja, nur Motorstation" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Ist das Kettenende? (1=Ja, Separator + ES-Element, 2=Nein, weiterbauen) -> Nein, weiterbauen" + }, + { + "typ": "string", + "wert": "2", + "kommentar": "getstring Separator an dieser Stelle einfuegen? (1=Ja, 2=Nein) -> Nein" + }, + { + "typ": "step", + "wert": "Linie", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "5", + "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Neue Linie bis Kettenende" + }, + { + "typ": "point_rel", + "dL": 1153.407829, + "kommentar": "getpoint Endpunkt der Linie bis Kettenende (Ziel-XY der Kette): dL=1153.4 mm (Fahrtrichtung vom Vorgaenger geerbt)" + }, + { + "typ": "real", + "wert": 2154.0, + "kommentar": "getreal Zielhoehe (Z) am Kettenende -> 2154.0 mm" + }, + { + "typ": "step", + "wert": "ES", + "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + }, + { + "typ": "string", + "wert": "90", + "kommentar": "getstring ES-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" + }, + { + "typ": "string", + "wert": "1", + "kommentar": "getstring ES-Seite (1=links, 2=rechts) -> links" + } +] diff --git a/tests/testdata/hundm05.json b/tests/testdata/hundm05.json index b9ffb11..2240fe3 100644 --- a/tests/testdata/hundm05.json +++ b/tests/testdata/hundm05.json @@ -1,1492 +1,418 @@ [ { - "test_id": "VF_hundm05_LZ_01", - "anlagetyp": "linienzug", + "spec_id": "VF_hundm05_LZ_01", "modus": 1, - "anzahl_eingaben": 65, - "block": "VF_87", - "dxf_handle": "1BEB5", - "startpunkt_mm": [4912.239129, 1230.886425, 2154.0], - "rotation_grad": 0.0, + "start_punkt": [4912.239129, 1230.886425, 2154.0], + "start_hoehe": 2154.0, + "as": 1, + "as_winkel": "90", + "as_seite": "links", "beschreibung": "Linienzug (Modus 1), Start Z2154: GF-Linie 2249 mm / 3 Grad -> Horizontal-VF 3257 mm -> Vario-Kurve 90 rechts innen -> Vario-Kurve 90 rechts innen -> Horizontal-VF 1210 mm -> Vario-Kurve 30 rechts innen -> Vario-Kurve 30 links innen -> ES 90 links", "sivas_id": "0025", "sivas_bezeichnung": "VarioFoerderer :1", "planquadrat": "B/1", - "erwartung_hinweis": "Sivas-Export (EXPORTSIVAS), Strecke 0025: DeltaL_mm=1934, DeltaH_mm=463, Hoehe_Von_mm=2154, Hoehe_Bis_mm=1690, L_GF_m=1.826,0.4,0.4, L_VF_m=1.859,1.434,0.1,1.83, VF_Winkel=0,0,0,0, Anzahl_Separator=3", + "block": "VF_87", + "dxf_handle": "1BEB5", "quelle": "INSERT 1BEB5 (Block VF_87) in polylines.dxf, XDATA-App SSG_VF_EDIT, Marker linienzug; im Journal aufgezeichneter Kettenstart: [4912.239129, 1230.886425, 0.000000]" }, { - "typ": "point_abs", - "wert": [4912.239129, 1230.886425, 2154.0], - "kommentar": "getpoint Startpunkt der Kette (absoluter Weltpunkt, einziger Anker der ganzen Kette)" - }, - { - "typ": "real", - "wert": 2154.0, - "kommentar": "getreal Hoehe (Z) Startpunkt -> 2154.0 mm" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring AS-Element setzen? (1=Ja, 2=Nein) -> Ja" - }, - { - "typ": "string", - "wert": "90", - "kommentar": "getstring AS-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring AS-Seite (1=links, 2=rechts) -> links" - }, - { - "typ": "step", - "wert": "Linie-GF", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Naechstes Element am Kettenanfang (1=Neue Linie GF, 2=Neue Linie Ab/Auf VF, 3=Horizontal-VF, 4=Neue Linie bis Kettenende) -> Neue Linie: GF" - }, - { - "typ": "point_rel", - "dL": 2249.25106, + "glied": "Linie-GF", + "nr": 1, + "dl": 2249.25106, "hz": 90.0, - "kommentar": "getpoint Endpunkt der GF-Linie: dL=2249.3 mm, gesnappte Fahrtrichtung hz=90 Grad (nur beim ersten Segment der Kette journalisiert)" + "gefaelle": "winkel", + "winkel": 3.0, + "ende": "nein" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Gefaelle festlegen (1=ueber Zielhoehe, 2=ueber Neigungswinkel) -> ueber Neigungswinkel" + "glied": "Horizontal-VF", + "nr": 2, + "dl": 3256.853781, + "gf_verteilung": "einlauf", + "vf_sep_vor": 0, + "vf_sep_nach": 0, + "vf_ende": "nein", + "vf_separator": 0 }, { - "typ": "real", - "wert": 3.0, - "kommentar": "getreal Neigungswinkel -> 3.0 Grad" + "sub": "vario-kurve", + "winkel": 90, + "seite": "rechts", + "variante": "innen" }, { - "typ": "string", - "wert": "3", - "kommentar": "getstring Ist das Kettenende? (1=Ja mit ES, 2=Ja ohne ES, 3=Nein weiterbauen) -> Nein, weiterbauen" + "sub": "horizontal", + "dl": 1434.100265, + "sep_vor": 0, + "sep_nach": 0 }, { - "typ": "step", - "wert": "Horizontal-VF", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + "sub": "vario-kurve", + "winkel": 90, + "seite": "rechts", + "variante": "innen" }, { - "typ": "string", - "wert": "4", - "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Horizontal-VF" + "sub": "motorstation" }, { - "typ": "point_rel", - "dL": 3256.853781, - "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=3256.9 mm (Fahrtrichtung vom Vorgaenger geerbt)" + "glied": "Horizontal-VF", + "nr": 3, + "dl": 1210.083164, + "gf_verteilung": "einlauf", + "vf_sep_vor": 0, + "vf_sep_nach": 0, + "vf_ende": "ja-mit-es" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring GF-Verteilung (1=halbe Staustrecke am Ausgang, 2=alles am Einlauf) -> alles am Einlauf" + "sub": "vario-kurve", + "winkel": 30, + "seite": "rechts", + "variante": "innen" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + "sub": "horizontal", + "dl": 1830.286247, + "sep_vor": 0, + "sep_nach": 0 }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + "sub": "vario-kurve", + "winkel": 30, + "seite": "links", + "variante": "innen" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" + "sub": "motorstation" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" + "glied": "ES", + "nr": 4, + "winkel": "90", + "seite": "links" }, { - "typ": "step", - "wert": "Vario-Kurve", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "int", - "wert": 90, - "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" - }, - { - "typ": "point_rel", - "dL": 1434.100265, - "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1434.1 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" - }, - { - "typ": "step", - "wert": "Vario-Kurve", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "int", - "wert": 90, - "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Ja, nur Motorstation" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist das Kettenende? (1=Ja, Separator + ES-Element, 2=Nein, weiterbauen) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator an dieser Stelle einfuegen? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "step", - "wert": "Horizontal-VF", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "string", - "wert": "4", - "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Horizontal-VF" - }, - { - "typ": "point_rel", - "dL": 1210.083164, - "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1210.1 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring GF-Verteilung (1=halbe Staustrecke am Ausgang, 2=alles am Einlauf) -> alles am Einlauf" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" - }, - { - "typ": "step", - "wert": "Vario-Kurve", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "int", - "wert": 30, - "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 30 Grad" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" - }, - { - "typ": "point_rel", - "dL": 1830.286247, - "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1830.3 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" - }, - { - "typ": "step", - "wert": "Vario-Kurve", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "int", - "wert": 30, - "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 30 Grad" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> links" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Ja, nur Motorstation" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Ist das Kettenende? (1=Ja, Separator + ES-Element, 2=Nein, weiterbauen) -> Ja, Separator + ES-Element" - }, - { - "typ": "step", - "wert": "ES", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "string", - "wert": "90", - "kommentar": "getstring ES-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring ES-Seite (1=links, 2=rechts) -> links" - }, - { - "test_id": "VF_hundm05_LZ_02", - "anlagetyp": "linienzug", + "spec_id": "VF_hundm05_LZ_02", "modus": 1, - "anzahl_eingaben": 54, - "block": "VF_46", - "dxf_handle": "EFD4", - "startpunkt_mm": [8571.95068, 1276.061424, 2494.930831], - "rotation_grad": 0.0, + "start_punkt": [8571.95068, 1276.061424, 2494.930831], + "start_hoehe": 2494.930831, + "as": 1, + "as_winkel": "90", + "as_seite": "links", "beschreibung": "Linienzug (Modus 1), Start Z2495: GF-Linie 3149 mm / 3 Grad -> Horizontal-VF 1575 mm -> Vario-Kurve 90 rechts innen -> Vario-Kurve 90 rechts innen -> Linie bis Kettenende 1153 mm auf Z2154 -> ES 90 links", "sivas_id": "0019", "sivas_bezeichnung": "VarioFoerderer :5", "planquadrat": "D/1", - "erwartung_hinweis": "Sivas-Export (EXPORTSIVAS), Strecke 0019: DeltaL_mm=1788, DeltaH_mm=220, Hoehe_Von_mm=2154, Hoehe_Bis_mm=2374, L_GF_m=2.727,0.4,0.418, L_VF_m=0.177,0.339,0.356,0.548, VF_Winkel=0,0,39,0, Anzahl_Separator=3", + "block": "VF_46", + "dxf_handle": "EFD4", "quelle": "INSERT EFD4 (Block VF_46) in polylines.dxf, XDATA-App SSG_VF_EDIT, Marker linienzug; im Journal aufgezeichneter Kettenstart: [17201.778323, 1205.360995, 2100.000000]" }, { - "typ": "point_abs", - "wert": [8571.95068, 1276.061424, 2494.930831], - "kommentar": "getpoint Startpunkt der Kette (absoluter Weltpunkt, einziger Anker der ganzen Kette)" - }, - { - "typ": "real", - "wert": 2494.930831, - "kommentar": "getreal Hoehe (Z) Startpunkt -> 2494.9 mm" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring AS-Element setzen? (1=Ja, 2=Nein) -> Ja" - }, - { - "typ": "string", - "wert": "90", - "kommentar": "getstring AS-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring AS-Seite (1=links, 2=rechts) -> links" - }, - { - "typ": "step", - "wert": "Linie-GF", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Naechstes Element am Kettenanfang (1=Neue Linie GF, 2=Neue Linie Ab/Auf VF, 3=Horizontal-VF, 4=Neue Linie bis Kettenende) -> Neue Linie: GF" - }, - { - "typ": "point_rel", - "dL": 3149.399248, + "glied": "Linie-GF", + "nr": 1, + "dl": 3149.399248, "hz": 90.0, - "kommentar": "getpoint Endpunkt der GF-Linie: dL=3149.4 mm, gesnappte Fahrtrichtung hz=90 Grad (nur beim ersten Segment der Kette journalisiert)" + "gefaelle": "winkel", + "winkel": 3.0, + "ende": "nein" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Gefaelle festlegen (1=ueber Zielhoehe, 2=ueber Neigungswinkel) -> ueber Neigungswinkel" + "glied": "Horizontal-VF", + "nr": 2, + "dl": 1574.912802, + "gf_verteilung": "einlauf", + "vf_sep_vor": 0, + "vf_sep_nach": 0, + "vf_ende": "nein", + "vf_separator": 0 }, { - "typ": "real", - "wert": 3.0, - "kommentar": "getreal Neigungswinkel -> 3.0 Grad" + "sub": "vario-kurve", + "winkel": 90, + "seite": "rechts", + "variante": "innen" }, { - "typ": "string", - "wert": "3", - "kommentar": "getstring Ist das Kettenende? (1=Ja mit ES, 2=Ja ohne ES, 3=Nein weiterbauen) -> Nein, weiterbauen" + "sub": "horizontal", + "dl": 338.966171, + "sep_vor": 0, + "sep_nach": 0 }, { - "typ": "step", - "wert": "Horizontal-VF", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + "sub": "vario-kurve", + "winkel": 90, + "seite": "rechts", + "variante": "innen" }, { - "typ": "string", - "wert": "4", - "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Horizontal-VF" + "sub": "auf-ab", + "dl": 4528.583638, + "hoehe": 2335.0, + "winkel_idx": 4 }, { - "typ": "point_rel", - "dL": 1574.912802, - "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1574.9 mm (Fahrtrichtung vom Vorgaenger geerbt)" + "sub": "horizontal", + "dl": 1443.864872, + "sep_vor": 0, + "sep_nach": 0 }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring GF-Verteilung (1=halbe Staustrecke am Ausgang, 2=alles am Einlauf) -> alles am Einlauf" + "sub": "motorstation" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + "glied": "Linie", + "nr": 3, + "dl": 1153.407829, + "hoehe": 2154.0, + "segment_typ": "GF" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + "glied": "ES", + "nr": 4, + "winkel": "90", + "seite": "links" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" - }, - { - "typ": "step", - "wert": "Vario-Kurve", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "int", - "wert": 90, - "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" - }, - { - "typ": "point_rel", - "dL": 338.966171, - "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=339.0 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" - }, - { - "typ": "step", - "wert": "Vario-Kurve", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "int", - "wert": 90, - "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "3", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Auf/Ab-Foerderer" - }, - { - "typ": "point_rel", - "dL": 4528.583638, - "kommentar": "getpoint Endpunkt des Auf/Ab-VF-Segments: dL=4528.6 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "real", - "wert": 2335.0, - "kommentar": "getreal Zielhoehe (Z) am Segmentende -> 2335.0 mm" - }, - { - "typ": "int", - "wert": 4, - "kommentar": "getint Winkelwahl aus den gueltigen Kandidaten (vfl-waehle-winkel) -> Option 4" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" - }, - { - "typ": "point_rel", - "dL": 1443.864872, - "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1443.9 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Ja, nur Motorstation" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist das Kettenende? (1=Ja, Separator + ES-Element, 2=Nein, weiterbauen) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator an dieser Stelle einfuegen? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "step", - "wert": "Linie", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "string", - "wert": "5", - "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Neue Linie bis Kettenende" - }, - { - "typ": "point_rel", - "dL": 1153.407829, - "kommentar": "getpoint Endpunkt der Linie bis Kettenende (Ziel-XY der Kette): dL=1153.4 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "real", - "wert": 2154.0, - "kommentar": "getreal Zielhoehe (Z) am Kettenende -> 2154.0 mm" - }, - { - "typ": "step", - "wert": "ES", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "string", - "wert": "90", - "kommentar": "getstring ES-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring ES-Seite (1=links, 2=rechts) -> links" - }, - { - "test_id": "VF_hundm05_LZ_03", - "anlagetyp": "linienzug", + "spec_id": "VF_hundm05_LZ_03", "modus": 1, - "anzahl_eingaben": 54, - "block": "VF_46", - "dxf_handle": "FA5A", - "startpunkt_mm": [12916.465798, 1276.061424, 2494.930831], - "rotation_grad": 0.0, + "start_punkt": [12916.465798, 1276.061424, 2494.930831], + "start_hoehe": 2494.930831, + "as": 1, + "as_winkel": "90", + "as_seite": "links", "beschreibung": "Linienzug (Modus 1), Start Z2495: GF-Linie 3149 mm / 3 Grad -> Horizontal-VF 1575 mm -> Vario-Kurve 90 rechts innen -> Vario-Kurve 90 rechts innen -> Linie bis Kettenende 1153 mm auf Z2154 -> ES 90 links", "sivas_id": "0018", "sivas_bezeichnung": "VarioFoerderer :4", "planquadrat": "F/1", - "erwartung_hinweis": "Sivas-Export (EXPORTSIVAS), Strecke 0018: DeltaL_mm=1788, DeltaH_mm=220, Hoehe_Von_mm=2154, Hoehe_Bis_mm=2374, L_GF_m=2.727,0.4,0.418, L_VF_m=0.177,0.339,0.356,0.548, VF_Winkel=0,0,39,0, Anzahl_Separator=3", + "block": "VF_46", + "dxf_handle": "FA5A", "quelle": "INSERT FA5A (Block VF_46) in polylines.dxf, XDATA-App SSG_VF_EDIT, Marker linienzug; im Journal aufgezeichneter Kettenstart: [17201.778323, 1205.360995, 2100.000000]" }, { - "typ": "point_abs", - "wert": [12916.465798, 1276.061424, 2494.930831], - "kommentar": "getpoint Startpunkt der Kette (absoluter Weltpunkt, einziger Anker der ganzen Kette)" - }, - { - "typ": "real", - "wert": 2494.930831, - "kommentar": "getreal Hoehe (Z) Startpunkt -> 2494.9 mm" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring AS-Element setzen? (1=Ja, 2=Nein) -> Ja" - }, - { - "typ": "string", - "wert": "90", - "kommentar": "getstring AS-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring AS-Seite (1=links, 2=rechts) -> links" - }, - { - "typ": "step", - "wert": "Linie-GF", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Naechstes Element am Kettenanfang (1=Neue Linie GF, 2=Neue Linie Ab/Auf VF, 3=Horizontal-VF, 4=Neue Linie bis Kettenende) -> Neue Linie: GF" - }, - { - "typ": "point_rel", - "dL": 3149.399248, + "glied": "Linie-GF", + "nr": 1, + "dl": 3149.399248, "hz": 90.0, - "kommentar": "getpoint Endpunkt der GF-Linie: dL=3149.4 mm, gesnappte Fahrtrichtung hz=90 Grad (nur beim ersten Segment der Kette journalisiert)" + "gefaelle": "winkel", + "winkel": 3.0, + "ende": "nein" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Gefaelle festlegen (1=ueber Zielhoehe, 2=ueber Neigungswinkel) -> ueber Neigungswinkel" + "glied": "Horizontal-VF", + "nr": 2, + "dl": 1574.912802, + "gf_verteilung": "einlauf", + "vf_sep_vor": 0, + "vf_sep_nach": 0, + "vf_ende": "nein", + "vf_separator": 0 }, { - "typ": "real", - "wert": 3.0, - "kommentar": "getreal Neigungswinkel -> 3.0 Grad" + "sub": "vario-kurve", + "winkel": 90, + "seite": "rechts", + "variante": "innen" }, { - "typ": "string", - "wert": "3", - "kommentar": "getstring Ist das Kettenende? (1=Ja mit ES, 2=Ja ohne ES, 3=Nein weiterbauen) -> Nein, weiterbauen" + "sub": "horizontal", + "dl": 338.966171, + "sep_vor": 0, + "sep_nach": 0 }, { - "typ": "step", - "wert": "Horizontal-VF", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + "sub": "vario-kurve", + "winkel": 90, + "seite": "rechts", + "variante": "innen" }, { - "typ": "string", - "wert": "4", - "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Horizontal-VF" + "sub": "auf-ab", + "dl": 4528.583638, + "hoehe": 2335.0, + "winkel_idx": 4 }, { - "typ": "point_rel", - "dL": 1574.912802, - "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1574.9 mm (Fahrtrichtung vom Vorgaenger geerbt)" + "sub": "horizontal", + "dl": 1443.864872, + "sep_vor": 0, + "sep_nach": 0 }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring GF-Verteilung (1=halbe Staustrecke am Ausgang, 2=alles am Einlauf) -> alles am Einlauf" + "sub": "motorstation" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + "glied": "Linie", + "nr": 3, + "dl": 1153.407829, + "hoehe": 2154.0, + "segment_typ": "GF" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + "glied": "ES", + "nr": 4, + "winkel": "90", + "seite": "links" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" - }, - { - "typ": "step", - "wert": "Vario-Kurve", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "int", - "wert": 90, - "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" - }, - { - "typ": "point_rel", - "dL": 338.966171, - "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=339.0 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" - }, - { - "typ": "step", - "wert": "Vario-Kurve", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "int", - "wert": 90, - "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "3", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Auf/Ab-Foerderer" - }, - { - "typ": "point_rel", - "dL": 4528.583638, - "kommentar": "getpoint Endpunkt des Auf/Ab-VF-Segments: dL=4528.6 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "real", - "wert": 2335.0, - "kommentar": "getreal Zielhoehe (Z) am Segmentende -> 2335.0 mm" - }, - { - "typ": "int", - "wert": 4, - "kommentar": "getint Winkelwahl aus den gueltigen Kandidaten (vfl-waehle-winkel) -> Option 4" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" - }, - { - "typ": "point_rel", - "dL": 1443.864872, - "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1443.9 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Ja, nur Motorstation" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist das Kettenende? (1=Ja, Separator + ES-Element, 2=Nein, weiterbauen) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator an dieser Stelle einfuegen? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "step", - "wert": "Linie", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "string", - "wert": "5", - "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Neue Linie bis Kettenende" - }, - { - "typ": "point_rel", - "dL": 1153.407829, - "kommentar": "getpoint Endpunkt der Linie bis Kettenende (Ziel-XY der Kette): dL=1153.4 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "real", - "wert": 2154.0, - "kommentar": "getreal Zielhoehe (Z) am Kettenende -> 2154.0 mm" - }, - { - "typ": "step", - "wert": "ES", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "string", - "wert": "90", - "kommentar": "getstring ES-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring ES-Seite (1=links, 2=rechts) -> links" - }, - { - "test_id": "VF_hundm05_LZ_04", - "anlagetyp": "linienzug", + "spec_id": "VF_hundm05_LZ_04", "modus": 1, - "anzahl_eingaben": 54, - "block": "VF_46", - "dxf_handle": "E498", - "startpunkt_mm": [17201.778323, 1205.360995, 2154.0], - "rotation_grad": 0.0, + "start_punkt": [17201.778323, 1205.360995, 2154.0], + "start_hoehe": 2154.0, + "as": 1, + "as_winkel": "90", + "as_seite": "links", "beschreibung": "Linienzug (Modus 1), Start Z2154: GF-Linie 3149 mm / 3 Grad -> Horizontal-VF 1575 mm -> Vario-Kurve 90 rechts innen -> Vario-Kurve 90 rechts innen -> Linie bis Kettenende 1153 mm auf Z2154 -> ES 90 links", "sivas_id": "0020", "sivas_bezeichnung": "VarioFoerderer :6", "planquadrat": "H/1", - "erwartung_hinweis": "Sivas-Export (EXPORTSIVAS), Strecke 0020: DeltaL_mm=1788, DeltaH_mm=220, Hoehe_Von_mm=2154, Hoehe_Bis_mm=2374, L_GF_m=2.727,0.4,0.418, L_VF_m=0.177,0.339,0.356,0.548, VF_Winkel=0,0,39,0, Anzahl_Separator=3", + "block": "VF_46", + "dxf_handle": "E498", "quelle": "INSERT E498 (Block VF_46) in polylines.dxf, XDATA-App SSG_VF_EDIT, Marker linienzug; im Journal aufgezeichneter Kettenstart: [17201.778323, 1205.360995, 2100.000000]" }, { - "typ": "point_abs", - "wert": [17201.778323, 1205.360995, 2154.0], - "kommentar": "getpoint Startpunkt der Kette (absoluter Weltpunkt, einziger Anker der ganzen Kette)" - }, - { - "typ": "real", - "wert": 2154.0, - "kommentar": "getreal Hoehe (Z) Startpunkt -> 2154.0 mm" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring AS-Element setzen? (1=Ja, 2=Nein) -> Ja" - }, - { - "typ": "string", - "wert": "90", - "kommentar": "getstring AS-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring AS-Seite (1=links, 2=rechts) -> links" - }, - { - "typ": "step", - "wert": "Linie-GF", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Naechstes Element am Kettenanfang (1=Neue Linie GF, 2=Neue Linie Ab/Auf VF, 3=Horizontal-VF, 4=Neue Linie bis Kettenende) -> Neue Linie: GF" - }, - { - "typ": "point_rel", - "dL": 3149.399248, + "glied": "Linie-GF", + "nr": 1, + "dl": 3149.399248, "hz": 90.0, - "kommentar": "getpoint Endpunkt der GF-Linie: dL=3149.4 mm, gesnappte Fahrtrichtung hz=90 Grad (nur beim ersten Segment der Kette journalisiert)" + "gefaelle": "winkel", + "winkel": 3.0, + "ende": "nein" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Gefaelle festlegen (1=ueber Zielhoehe, 2=ueber Neigungswinkel) -> ueber Neigungswinkel" + "glied": "Horizontal-VF", + "nr": 2, + "dl": 1574.912802, + "gf_verteilung": "einlauf", + "vf_sep_vor": 0, + "vf_sep_nach": 0, + "vf_ende": "nein", + "vf_separator": 0 }, { - "typ": "real", - "wert": 3.0, - "kommentar": "getreal Neigungswinkel -> 3.0 Grad" + "sub": "vario-kurve", + "winkel": 90, + "seite": "rechts", + "variante": "innen" }, { - "typ": "string", - "wert": "3", - "kommentar": "getstring Ist das Kettenende? (1=Ja mit ES, 2=Ja ohne ES, 3=Nein weiterbauen) -> Nein, weiterbauen" + "sub": "horizontal", + "dl": 338.966171, + "sep_vor": 0, + "sep_nach": 0 }, { - "typ": "step", - "wert": "Horizontal-VF", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + "sub": "vario-kurve", + "winkel": 90, + "seite": "rechts", + "variante": "innen" }, { - "typ": "string", - "wert": "4", - "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Horizontal-VF" + "sub": "auf-ab", + "dl": 4528.583638, + "hoehe": 2335.0, + "winkel_idx": 4 }, { - "typ": "point_rel", - "dL": 1574.912802, - "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1574.9 mm (Fahrtrichtung vom Vorgaenger geerbt)" + "sub": "horizontal", + "dl": 1443.864872, + "sep_vor": 0, + "sep_nach": 0 }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring GF-Verteilung (1=halbe Staustrecke am Ausgang, 2=alles am Einlauf) -> alles am Einlauf" + "sub": "motorstation" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + "glied": "Linie", + "nr": 3, + "dl": 1153.407829, + "hoehe": 2154.0, + "segment_typ": "GF" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + "glied": "ES", + "nr": 4, + "winkel": "90", + "seite": "links" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" - }, - { - "typ": "step", - "wert": "Vario-Kurve", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "int", - "wert": 90, - "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" - }, - { - "typ": "point_rel", - "dL": 338.966171, - "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=339.0 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" - }, - { - "typ": "step", - "wert": "Vario-Kurve", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "int", - "wert": 90, - "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "3", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Auf/Ab-Foerderer" - }, - { - "typ": "point_rel", - "dL": 4528.583638, - "kommentar": "getpoint Endpunkt des Auf/Ab-VF-Segments: dL=4528.6 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "real", - "wert": 2335.0, - "kommentar": "getreal Zielhoehe (Z) am Segmentende -> 2335.0 mm" - }, - { - "typ": "int", - "wert": 4, - "kommentar": "getint Winkelwahl aus den gueltigen Kandidaten (vfl-waehle-winkel) -> Option 4" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" - }, - { - "typ": "point_rel", - "dL": 1443.864872, - "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1443.9 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Ja, nur Motorstation" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist das Kettenende? (1=Ja, Separator + ES-Element, 2=Nein, weiterbauen) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator an dieser Stelle einfuegen? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "step", - "wert": "Linie", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "string", - "wert": "5", - "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Neue Linie bis Kettenende" - }, - { - "typ": "point_rel", - "dL": 1153.407829, - "kommentar": "getpoint Endpunkt der Linie bis Kettenende (Ziel-XY der Kette): dL=1153.4 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "real", - "wert": 2154.0, - "kommentar": "getreal Zielhoehe (Z) am Kettenende -> 2154.0 mm" - }, - { - "typ": "step", - "wert": "ES", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "string", - "wert": "90", - "kommentar": "getstring ES-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring ES-Seite (1=links, 2=rechts) -> links" - }, - { - "test_id": "VF_hundm05_LZ_05", - "anlagetyp": "linienzug", + "spec_id": "VF_hundm05_LZ_05", "modus": 1, - "anzahl_eingaben": 54, - "block": "VF_46", - "dxf_handle": "104E0", - "startpunkt_mm": [21490.273781, 1195.739767, 2494.930831], - "rotation_grad": 0.0, + "start_punkt": [21490.273781, 1195.739767, 2494.930831], + "start_hoehe": 2494.930831, + "as": 1, + "as_winkel": "90", + "as_seite": "links", "beschreibung": "Linienzug (Modus 1), Start Z2495: GF-Linie 3149 mm / 3 Grad -> Horizontal-VF 1575 mm -> Vario-Kurve 90 rechts innen -> Vario-Kurve 90 rechts innen -> Linie bis Kettenende 1153 mm auf Z2154 -> ES 90 links", "sivas_id": "0008", "sivas_bezeichnung": "VarioFoerderer :3", "planquadrat": "J/1", - "erwartung_hinweis": "Sivas-Export (EXPORTSIVAS), Strecke 0008: DeltaL_mm=1788, DeltaH_mm=220, Hoehe_Von_mm=2154, Hoehe_Bis_mm=2374, L_GF_m=2.727,0.4,0.418, L_VF_m=0.177,0.339,0.356,0.548, VF_Winkel=0,0,39,0, Anzahl_Separator=3", + "block": "VF_46", + "dxf_handle": "104E0", "quelle": "INSERT 104E0 (Block VF_46) in polylines.dxf, XDATA-App SSG_VF_EDIT, Marker linienzug; im Journal aufgezeichneter Kettenstart: [17201.778323, 1205.360995, 2100.000000]" }, { - "typ": "point_abs", - "wert": [21490.273781, 1195.739767, 2494.930831], - "kommentar": "getpoint Startpunkt der Kette (absoluter Weltpunkt, einziger Anker der ganzen Kette)" - }, - { - "typ": "real", - "wert": 2494.930831, - "kommentar": "getreal Hoehe (Z) Startpunkt -> 2494.9 mm" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring AS-Element setzen? (1=Ja, 2=Nein) -> Ja" - }, - { - "typ": "string", - "wert": "90", - "kommentar": "getstring AS-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring AS-Seite (1=links, 2=rechts) -> links" - }, - { - "typ": "step", - "wert": "Linie-GF", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Naechstes Element am Kettenanfang (1=Neue Linie GF, 2=Neue Linie Ab/Auf VF, 3=Horizontal-VF, 4=Neue Linie bis Kettenende) -> Neue Linie: GF" - }, - { - "typ": "point_rel", - "dL": 3149.399248, + "glied": "Linie-GF", + "nr": 1, + "dl": 3149.399248, "hz": 90.0, - "kommentar": "getpoint Endpunkt der GF-Linie: dL=3149.4 mm, gesnappte Fahrtrichtung hz=90 Grad (nur beim ersten Segment der Kette journalisiert)" + "gefaelle": "winkel", + "winkel": 3.0, + "ende": "nein" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Gefaelle festlegen (1=ueber Zielhoehe, 2=ueber Neigungswinkel) -> ueber Neigungswinkel" + "glied": "Horizontal-VF", + "nr": 2, + "dl": 1574.912802, + "gf_verteilung": "einlauf", + "vf_sep_vor": 0, + "vf_sep_nach": 0, + "vf_ende": "nein", + "vf_separator": 0 }, { - "typ": "real", - "wert": 3.0, - "kommentar": "getreal Neigungswinkel -> 3.0 Grad" + "sub": "vario-kurve", + "winkel": 90, + "seite": "rechts", + "variante": "innen" }, { - "typ": "string", - "wert": "3", - "kommentar": "getstring Ist das Kettenende? (1=Ja mit ES, 2=Ja ohne ES, 3=Nein weiterbauen) -> Nein, weiterbauen" + "sub": "horizontal", + "dl": 338.966171, + "sep_vor": 0, + "sep_nach": 0 }, { - "typ": "step", - "wert": "Horizontal-VF", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" + "sub": "vario-kurve", + "winkel": 90, + "seite": "rechts", + "variante": "innen" }, { - "typ": "string", - "wert": "4", - "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Horizontal-VF" + "sub": "auf-ab", + "dl": 4528.583638, + "hoehe": 2335.0, + "winkel_idx": 4 }, { - "typ": "point_rel", - "dL": 1574.912802, - "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1574.9 mm (Fahrtrichtung vom Vorgaenger geerbt)" + "sub": "horizontal", + "dl": 1443.864872, + "sep_vor": 0, + "sep_nach": 0 }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring GF-Verteilung (1=halbe Staustrecke am Ausgang, 2=alles am Einlauf) -> alles am Einlauf" + "sub": "motorstation" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" + "glied": "Linie", + "nr": 3, + "dl": 1153.407829, + "hoehe": 2154.0, + "segment_typ": "GF" }, { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" - }, - { - "typ": "step", - "wert": "Vario-Kurve", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "int", - "wert": 90, - "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" - }, - { - "typ": "point_rel", - "dL": 338.966171, - "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=339.0 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Vario-Kurve" - }, - { - "typ": "step", - "wert": "Vario-Kurve", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "int", - "wert": 90, - "kommentar": "getint Vario-Kurve Winkel (30/60/90 Grad, Wert direkt) -> 90 Grad" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Seite (1=links, 2=rechts) -> rechts" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Vario-Kurve Variante (1=aussen, 2=innen) -> innen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "3", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Auf/Ab-Foerderer" - }, - { - "typ": "point_rel", - "dL": 4528.583638, - "kommentar": "getpoint Endpunkt des Auf/Ab-VF-Segments: dL=4528.6 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "real", - "wert": 2335.0, - "kommentar": "getreal Zielhoehe (Z) am Segmentende -> 2335.0 mm" - }, - { - "typ": "int", - "wert": 4, - "kommentar": "getint Winkelwahl aus den gueltigen Kandidaten (vfl-waehle-winkel) -> Option 4" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Naechstes in der VF-Einheit (1=Horizontaler Foerderer, 2=Vario-Kurve, 3=Auf/Ab-Foerderer) -> Horizontaler Foerderer" - }, - { - "typ": "point_rel", - "dL": 1443.864872, - "kommentar": "getpoint Endpunkt des horizontalen VF-Stuecks: dL=1443.9 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator VOR dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator NACH dem horizontalen Stueck? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring Ist der Endpunkt der Foerderer? (1=Ja nur Motorstation, 2=Nein weiterbauen, 3=Ja Motorstation + Kettenende) -> Ja, nur Motorstation" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Ist das Kettenende? (1=Ja, Separator + ES-Element, 2=Nein, weiterbauen) -> Nein, weiterbauen" - }, - { - "typ": "string", - "wert": "2", - "kommentar": "getstring Separator an dieser Stelle einfuegen? (1=Ja, 2=Nein) -> Nein" - }, - { - "typ": "step", - "wert": "Linie", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "string", - "wert": "5", - "kommentar": "getstring Naechstes Element (1=GF-Bogen, 2=Neue Linie GF, 3=Neue Linie Ab/Auf VF, 4=Horizontal-VF, 5=Neue Linie bis Kettenende) -> Neue Linie bis Kettenende" - }, - { - "typ": "point_rel", - "dL": 1153.407829, - "kommentar": "getpoint Endpunkt der Linie bis Kettenende (Ziel-XY der Kette): dL=1153.4 mm (Fahrtrichtung vom Vorgaenger geerbt)" - }, - { - "typ": "real", - "wert": 2154.0, - "kommentar": "getreal Zielhoehe (Z) am Kettenende -> 2154.0 mm" - }, - { - "typ": "step", - "wert": "ES", - "kommentar": "Glied-Marker (vfl-journal-mark) - beim Replay uebersprungen, Label = gewaehlter Segmenttyp" - }, - { - "typ": "string", - "wert": "90", - "kommentar": "getstring ES-Element Winkel (Wert direkt, 90 oder 30 Grad) -> 90 Grad" - }, - { - "typ": "string", - "wert": "1", - "kommentar": "getstring ES-Seite (1=links, 2=rechts) -> links" + "glied": "ES", + "nr": 4, + "winkel": "90", + "seite": "links" } ] diff --git a/tests/testdata/object_data.md b/tests/testdata/object_data.md index 74b20b6..4edf031 100644 --- a/tests/testdata/object_data.md +++ b/tests/testdata/object_data.md @@ -407,7 +407,7 @@ Dasselbe Protokoll steht produktionsseitig als **XDATA am fertigen wiederabspielbar (`vfl-journal-replay-start` + `vf-linienzug-modus`, genutzt vom Doppelklick-Editieren). Ein Journal aus einer echten Zeichnung laesst sich darum 1:1 in dieses Schema zuruecklesen — genau das tut -`lib/vf_journal_export.py` (siehe Abschnitt 8, `hundm05.json`). Zwei +`lib/vf_journal_export.py` (siehe Abschnitt 8, `hm_recformat.json`). Zwei Unterschiede zum handgeschriebenen `linienzug_tests.json`: - Zusaetzlicher Eintragstyp `step` (Glied-Marker aus `vfl-journal-mark`, @@ -427,7 +427,7 @@ blieb an einem `getpoint` haengen. Mit `*vfl-headless*` = T ist das jetzt ein harter Abbruch mit Fundstelle (`vfl-headless-abbruch` -> `*vfl-headless-fehler*` nennt Art der Eingabe, Glied-Nummer und Eingabe-Nummer). Die Meldungen der geometrisch abgewiesenen Sektionen laufen ueber `vfl-meldung` statt `alert` und -stehen in `*vfl-meldungen*`, blockieren also nichts mehr. `tests/test_hundm05.lsp` +stehen in `*vfl-meldungen*`, blockieren also nichts mehr. `tests/test_hm_recformat.lsp` schaltet beides ein; die Diagnose landet im Ergebnis-JSON (`headless_fehler`, `meldungen`, `prompts`). @@ -615,7 +615,7 @@ Vollstaendiges JSON: "endpunkt": [220499.01, 154926.53, 0.0] } ``` -Einen Treiber dafuer gibt es derzeit nicht mehr: `tests/test_hundm05.lsp` +Einen Treiber dafuer gibt es derzeit nicht mehr: `tests/test_hm_recformat.lsp` spielt seit dem Umbau auf die Linienzug-Journale nur noch Ketten ab (siehe Abschnitt 8); der fruehere `hundm05:build-gerade` steht in der Git-Historie. @@ -640,9 +640,18 @@ Abschnitt 8); der fruehere `hundm05:build-gerade` steht in der Git-Historie. | Omniflo TV_*/APBW_* | - | **nein** (nicht implementiert, DUMMY) | | Separator/Scanner | `ils-insert-sensor` | **nein** (Schleife mit `getpoint`/`pause`) | -## 8. HundM05 — die 5 Linienzuege am Kreisel (`hundm05.json`) +## 8. HundM05 — die 5 Linienzuege am Kreisel -`tests/testdata/hundm05.json` enthaelt die **Eingabe-Journale der 5 +Zwei Dateien beschreiben dieselben 5 Ketten, in zwei Formaten: + +| Datei | Inhalt | Test | +|---|---|---| +| `tests/testdata/hm_recformat.json` | die **Aufzeichnung**: rohes Eingabe-Journal aus der XDATA der Kundenzeichnung, als Frage-Antwort-Protokoll | `TEST_HM_RECFORMAT` (in `alltests.json` abgeschaltet) | +| `tests/testdata/hundm05.json` | die daraus abgeleitete **Spec** (Domaenenwerte statt Menue-Codes, 52 statt 281 Objekte) - hier werden weitere Ketten nachgetragen | `TEST_HUNDM05` | + +Die Spec ist das Eingabeformat, die Aufzeichnung die Vorlage, an der sie gemessen wird (Rundlauf-Beweis in `tests/test_vf_spec.py`). Warum beide bleiben: die Aufzeichnung ist die einzige eingecheckte Kopie der Originaldaten (`data/polylines.dxf` ist zu gross fuer das Repo) UND der einzige Test dafuer, dass ein BESTEHENDER `VF_n`-Block weiter abspielbar ist - also dass Doppelklick-Edit und 2D/3D-Konvertierung an Altbestand funktionieren. Gebaut wird routinemaessig nur noch aus der Spec, damit nicht zweimal dieselbe Geometrie entsteht. + +`tests/testdata/hm_recformat.json` enthaelt die **Eingabe-Journale der 5 VarioFoerderer-Linienzuege** aus `data/polylines.dxf` (die Anlage HundM, ein Kreisel mit fuenf daran haengenden Ketten). Quelle ist nicht die gezeichnete Geometrie, sondern die XDATA `SSG_VF_EDIT` (Marker `"linienzug"`) an den @@ -675,7 +684,7 @@ Neu erzeugen: ``` python lib/vf_journal_export.py data/polylines.dxf \ - tests/testdata/hundm05.json --csv results/HundM_export.csv + tests/testdata/hm_recformat.json --csv results/HundM_export.csv ``` Die frueheren Inhalte (Kreisel/Gefaellestrecke/Sensor-Eintraege aus dem @@ -690,10 +699,10 @@ Kopf-Objekte verschlucken. Darum steht je Kette ein Kopf-Objekt (traegt `linienzug_tests.json`. Der Anlagetyp im Kopf heisst `anlagetyp` und NICHT `typ`, damit Kopf und Eingabe unterscheidbar bleiben. -Abgespielt wird das per `tests/test_hundm05.lsp` (`TEST_HUNDM05`, +Abgespielt wird das per `tests/test_hm_recformat.lsp` (`TEST_HM_RECFORMAT`, `TEST_HUNDM05_2D`/`_3D`): der Treiber gruppiert die Objekte -(`hundm05:gruppiere`), baut daraus das Vorwaerts-Journal -(`hundm05:eintrag->journal`) und spielt es je Kette ueber +(`hmrec:gruppiere`), baut daraus das Vorwaerts-Journal +(`hmrec:eintrag->journal`) und spielt es je Kette ueber `vfl-journal-replay-start` + `vf-linienzug-modus` ab — denselben Weg nimmt die Produktion beim Editieren/Konvertieren (`vfl-konvertiere-ent`). Bewusst KEINE Eingabe-Mocks wie in `test_linienzug.lsp`: ein Mock muesste zu jedem Segment diff --git a/tests/testdata/vf_spec_hundm05.json b/tests/testdata/vf_spec_hundm05.json deleted file mode 100644 index 3b3af9f..0000000 --- a/tests/testdata/vf_spec_hundm05.json +++ /dev/null @@ -1,418 +0,0 @@ -[ - { - "spec_id": "VF_spec_LZ_01", - "modus": 1, - "start_punkt": [4912.239129, 1230.886425, 2154.0], - "start_hoehe": 2154.0, - "as": 1, - "as_winkel": "90", - "as_seite": "links", - "beschreibung": "Linienzug (Modus 1), Start Z2154: GF-Linie 2249 mm / 3 Grad -> Horizontal-VF 3257 mm -> Vario-Kurve 90 rechts innen -> Vario-Kurve 90 rechts innen -> Horizontal-VF 1210 mm -> Vario-Kurve 30 rechts innen -> Vario-Kurve 30 links innen -> ES 90 links", - "sivas_id": "0025", - "sivas_bezeichnung": "VarioFoerderer :1", - "planquadrat": "B/1", - "block": "VF_87", - "dxf_handle": "1BEB5", - "quelle": "INSERT 1BEB5 (Block VF_87) in polylines.dxf, XDATA-App SSG_VF_EDIT, Marker linienzug; im Journal aufgezeichneter Kettenstart: [4912.239129, 1230.886425, 0.000000]" - }, - { - "glied": "Linie-GF", - "nr": 1, - "dl": 2249.25106, - "hz": 90.0, - "gefaelle": "winkel", - "winkel": 3.0, - "ende": "nein" - }, - { - "glied": "Horizontal-VF", - "nr": 2, - "dl": 3256.853781, - "gf_verteilung": "einlauf", - "vf_sep_vor": 0, - "vf_sep_nach": 0, - "vf_ende": "nein", - "vf_separator": 0 - }, - { - "sub": "vario-kurve", - "winkel": 90, - "seite": "rechts", - "variante": "innen" - }, - { - "sub": "horizontal", - "dl": 1434.100265, - "sep_vor": 0, - "sep_nach": 0 - }, - { - "sub": "vario-kurve", - "winkel": 90, - "seite": "rechts", - "variante": "innen" - }, - { - "sub": "motorstation" - }, - { - "glied": "Horizontal-VF", - "nr": 3, - "dl": 1210.083164, - "gf_verteilung": "einlauf", - "vf_sep_vor": 0, - "vf_sep_nach": 0, - "vf_ende": "ja-mit-es" - }, - { - "sub": "vario-kurve", - "winkel": 30, - "seite": "rechts", - "variante": "innen" - }, - { - "sub": "horizontal", - "dl": 1830.286247, - "sep_vor": 0, - "sep_nach": 0 - }, - { - "sub": "vario-kurve", - "winkel": 30, - "seite": "links", - "variante": "innen" - }, - { - "sub": "motorstation" - }, - { - "glied": "ES", - "nr": 4, - "winkel": "90", - "seite": "links" - }, - { - "spec_id": "VF_spec_LZ_02", - "modus": 1, - "start_punkt": [8571.95068, 1276.061424, 2494.930831], - "start_hoehe": 2494.930831, - "as": 1, - "as_winkel": "90", - "as_seite": "links", - "beschreibung": "Linienzug (Modus 1), Start Z2495: GF-Linie 3149 mm / 3 Grad -> Horizontal-VF 1575 mm -> Vario-Kurve 90 rechts innen -> Vario-Kurve 90 rechts innen -> Linie bis Kettenende 1153 mm auf Z2154 -> ES 90 links", - "sivas_id": "0019", - "sivas_bezeichnung": "VarioFoerderer :5", - "planquadrat": "D/1", - "block": "VF_46", - "dxf_handle": "EFD4", - "quelle": "INSERT EFD4 (Block VF_46) in polylines.dxf, XDATA-App SSG_VF_EDIT, Marker linienzug; im Journal aufgezeichneter Kettenstart: [17201.778323, 1205.360995, 2100.000000]" - }, - { - "glied": "Linie-GF", - "nr": 1, - "dl": 3149.399248, - "hz": 90.0, - "gefaelle": "winkel", - "winkel": 3.0, - "ende": "nein" - }, - { - "glied": "Horizontal-VF", - "nr": 2, - "dl": 1574.912802, - "gf_verteilung": "einlauf", - "vf_sep_vor": 0, - "vf_sep_nach": 0, - "vf_ende": "nein", - "vf_separator": 0 - }, - { - "sub": "vario-kurve", - "winkel": 90, - "seite": "rechts", - "variante": "innen" - }, - { - "sub": "horizontal", - "dl": 338.966171, - "sep_vor": 0, - "sep_nach": 0 - }, - { - "sub": "vario-kurve", - "winkel": 90, - "seite": "rechts", - "variante": "innen" - }, - { - "sub": "auf-ab", - "dl": 4528.583638, - "hoehe": 2335.0, - "winkel_idx": 4 - }, - { - "sub": "horizontal", - "dl": 1443.864872, - "sep_vor": 0, - "sep_nach": 0 - }, - { - "sub": "motorstation" - }, - { - "glied": "Linie", - "nr": 3, - "dl": 1153.407829, - "hoehe": 2154.0, - "segment_typ": "GF" - }, - { - "glied": "ES", - "nr": 4, - "winkel": "90", - "seite": "links" - }, - { - "spec_id": "VF_spec_LZ_03", - "modus": 1, - "start_punkt": [12916.465798, 1276.061424, 2494.930831], - "start_hoehe": 2494.930831, - "as": 1, - "as_winkel": "90", - "as_seite": "links", - "beschreibung": "Linienzug (Modus 1), Start Z2495: GF-Linie 3149 mm / 3 Grad -> Horizontal-VF 1575 mm -> Vario-Kurve 90 rechts innen -> Vario-Kurve 90 rechts innen -> Linie bis Kettenende 1153 mm auf Z2154 -> ES 90 links", - "sivas_id": "0018", - "sivas_bezeichnung": "VarioFoerderer :4", - "planquadrat": "F/1", - "block": "VF_46", - "dxf_handle": "FA5A", - "quelle": "INSERT FA5A (Block VF_46) in polylines.dxf, XDATA-App SSG_VF_EDIT, Marker linienzug; im Journal aufgezeichneter Kettenstart: [17201.778323, 1205.360995, 2100.000000]" - }, - { - "glied": "Linie-GF", - "nr": 1, - "dl": 3149.399248, - "hz": 90.0, - "gefaelle": "winkel", - "winkel": 3.0, - "ende": "nein" - }, - { - "glied": "Horizontal-VF", - "nr": 2, - "dl": 1574.912802, - "gf_verteilung": "einlauf", - "vf_sep_vor": 0, - "vf_sep_nach": 0, - "vf_ende": "nein", - "vf_separator": 0 - }, - { - "sub": "vario-kurve", - "winkel": 90, - "seite": "rechts", - "variante": "innen" - }, - { - "sub": "horizontal", - "dl": 338.966171, - "sep_vor": 0, - "sep_nach": 0 - }, - { - "sub": "vario-kurve", - "winkel": 90, - "seite": "rechts", - "variante": "innen" - }, - { - "sub": "auf-ab", - "dl": 4528.583638, - "hoehe": 2335.0, - "winkel_idx": 4 - }, - { - "sub": "horizontal", - "dl": 1443.864872, - "sep_vor": 0, - "sep_nach": 0 - }, - { - "sub": "motorstation" - }, - { - "glied": "Linie", - "nr": 3, - "dl": 1153.407829, - "hoehe": 2154.0, - "segment_typ": "GF" - }, - { - "glied": "ES", - "nr": 4, - "winkel": "90", - "seite": "links" - }, - { - "spec_id": "VF_spec_LZ_04", - "modus": 1, - "start_punkt": [17201.778323, 1205.360995, 2154.0], - "start_hoehe": 2154.0, - "as": 1, - "as_winkel": "90", - "as_seite": "links", - "beschreibung": "Linienzug (Modus 1), Start Z2154: GF-Linie 3149 mm / 3 Grad -> Horizontal-VF 1575 mm -> Vario-Kurve 90 rechts innen -> Vario-Kurve 90 rechts innen -> Linie bis Kettenende 1153 mm auf Z2154 -> ES 90 links", - "sivas_id": "0020", - "sivas_bezeichnung": "VarioFoerderer :6", - "planquadrat": "H/1", - "block": "VF_46", - "dxf_handle": "E498", - "quelle": "INSERT E498 (Block VF_46) in polylines.dxf, XDATA-App SSG_VF_EDIT, Marker linienzug; im Journal aufgezeichneter Kettenstart: [17201.778323, 1205.360995, 2100.000000]" - }, - { - "glied": "Linie-GF", - "nr": 1, - "dl": 3149.399248, - "hz": 90.0, - "gefaelle": "winkel", - "winkel": 3.0, - "ende": "nein" - }, - { - "glied": "Horizontal-VF", - "nr": 2, - "dl": 1574.912802, - "gf_verteilung": "einlauf", - "vf_sep_vor": 0, - "vf_sep_nach": 0, - "vf_ende": "nein", - "vf_separator": 0 - }, - { - "sub": "vario-kurve", - "winkel": 90, - "seite": "rechts", - "variante": "innen" - }, - { - "sub": "horizontal", - "dl": 338.966171, - "sep_vor": 0, - "sep_nach": 0 - }, - { - "sub": "vario-kurve", - "winkel": 90, - "seite": "rechts", - "variante": "innen" - }, - { - "sub": "auf-ab", - "dl": 4528.583638, - "hoehe": 2335.0, - "winkel_idx": 4 - }, - { - "sub": "horizontal", - "dl": 1443.864872, - "sep_vor": 0, - "sep_nach": 0 - }, - { - "sub": "motorstation" - }, - { - "glied": "Linie", - "nr": 3, - "dl": 1153.407829, - "hoehe": 2154.0, - "segment_typ": "GF" - }, - { - "glied": "ES", - "nr": 4, - "winkel": "90", - "seite": "links" - }, - { - "spec_id": "VF_spec_LZ_05", - "modus": 1, - "start_punkt": [21490.273781, 1195.739767, 2494.930831], - "start_hoehe": 2494.930831, - "as": 1, - "as_winkel": "90", - "as_seite": "links", - "beschreibung": "Linienzug (Modus 1), Start Z2495: GF-Linie 3149 mm / 3 Grad -> Horizontal-VF 1575 mm -> Vario-Kurve 90 rechts innen -> Vario-Kurve 90 rechts innen -> Linie bis Kettenende 1153 mm auf Z2154 -> ES 90 links", - "sivas_id": "0008", - "sivas_bezeichnung": "VarioFoerderer :3", - "planquadrat": "J/1", - "block": "VF_46", - "dxf_handle": "104E0", - "quelle": "INSERT 104E0 (Block VF_46) in polylines.dxf, XDATA-App SSG_VF_EDIT, Marker linienzug; im Journal aufgezeichneter Kettenstart: [17201.778323, 1205.360995, 2100.000000]" - }, - { - "glied": "Linie-GF", - "nr": 1, - "dl": 3149.399248, - "hz": 90.0, - "gefaelle": "winkel", - "winkel": 3.0, - "ende": "nein" - }, - { - "glied": "Horizontal-VF", - "nr": 2, - "dl": 1574.912802, - "gf_verteilung": "einlauf", - "vf_sep_vor": 0, - "vf_sep_nach": 0, - "vf_ende": "nein", - "vf_separator": 0 - }, - { - "sub": "vario-kurve", - "winkel": 90, - "seite": "rechts", - "variante": "innen" - }, - { - "sub": "horizontal", - "dl": 338.966171, - "sep_vor": 0, - "sep_nach": 0 - }, - { - "sub": "vario-kurve", - "winkel": 90, - "seite": "rechts", - "variante": "innen" - }, - { - "sub": "auf-ab", - "dl": 4528.583638, - "hoehe": 2335.0, - "winkel_idx": 4 - }, - { - "sub": "horizontal", - "dl": 1443.864872, - "sep_vor": 0, - "sep_nach": 0 - }, - { - "sub": "motorstation" - }, - { - "glied": "Linie", - "nr": 3, - "dl": 1153.407829, - "hoehe": 2154.0, - "segment_typ": "GF" - }, - { - "glied": "ES", - "nr": 4, - "winkel": "90", - "seite": "links" - } -]