[CHANGE] Nur noch EIN Test baut die HundM-Geometrie: Spec wird TEST_HUNDM05
Nach dem gruenen VF_SPEC_BAU-Lauf bauten zwei Tests dieselben 5 Ketten. Der Spec-Pfad ist jetzt der routinemaessige Bau-Test, der Aufzeichnungs-Pfad der abgeschaltete Regressionstest fuer das Journal-Format. Umbenennungen: - tests/testdata/vf_spec_hundm05.json -> tests/testdata/hundm05.json Die Spec ist das Eingabeformat und die Datei, in der weitere Ketten der Anlage nachgetragen werden. spec_ids jetzt VF_hundm05_LZ_* (wie im Aufzeichnungs-Protokoll), damit sich die Ergebnisse beider Laeufe paaren lassen. - tests/testdata/hundm05.json -> tests/testdata/hm_recformat.json tests/test_hundm05.lsp -> tests/test_hm_recformat.lsp (TEST_HM_RECFORMAT, Praefix hmrec:, Export hm_recformat:export-results) tests/test_hundm05.py -> tests/test_hm_recformat.py In alltests.json auf "disabled": true. Neu: tests/test_hundm05.lsp (TEST_HUNDM05) baut die Spec - OHNE eigene Bau-Logik, es ruft vsp-bau-datei aus Lisp/vf_spec.lsp. Damit gibt es genau einen Code-Pfad, der aus einer Spec Geometrie erzeugt; c:VF_SPEC_BAU nutzt denselben. tests/test_hundm05.py prueft das Ergebnis (die 9 Tests, die vorher in test_vf_spec.py standen); test_vf_spec.py ist jetzt reiner Uebersetzer-Test ohne CAD. Warum das Aufzeichnungsformat bleibt (Details in doc/TODO-plan-vf-interactive.md Abschnitt 4.7): - hm_recformat.json 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. - Es prueft eine ANDERE Invariante: das Journal kommt roh aus der XDATA einer Kundenzeichnung. TEST_HM_RECFORMAT ist damit der einzige Test dafuer, dass ein BESTEHENDER VF_n-Block weiter abspielbar ist - also dass Doppelklick-Edit und 2D/3D-Konvertierung an Altbestand funktionieren. Ein spec-gebautes Journal kann das nicht zeigen, es kommt aus dem Uebersetzer. - Es dokumentiert die Frage-Reihenfolge (kommentar je Eintrag); die Spec verbirgt den Dialog, das ist ihr Zweck. Weitere Anpassungen: - Ergebnisdatei heisst nach der Spec-Datei (<basisname>_results.json), damit Befehl und Testrunner in dieselbe Datei schreiben. Verzeichnis: tests/output (Override DXFM_VF_SPEC_OUT); NICHT DXFM_RESULTS - das sind die Sivas-/ CSV-Exporte, dort landete die Datei ausserhalb des Testbaums. - Neu *vsp-dim-override* fuer TEST_HUNDM05_2D/_3D: wird je Kette angewandt, weil der Abbruch-Handler in vf-linienzug-modus *ssg-ils-dim* zurueck setzt. - 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 auf VF_hm_recformat_LZ_* geaendert und die Paarung der beiden Laeufe zerlegt - jetzt feste Konstante TEST_ID_ANLAGE. - conftest: hundm05_* Fixtures -> hmrec_* (die Spec-Fixtures stehen in tests/test_hundm05.py). Verifiziert: 98 pytest-Tests gruen (die 9 Ergebnis-Tests warten auf einen neuen TEST_HUNDM05-Lauf), alle .lsp lint-sauber, Spec-Regenerierung idempotent, TEST_VF_SPEC in BricsCAD 28 PASS / 0 FAIL. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+59
-27
@@ -77,6 +77,11 @@
|
|||||||
(if (not (boundp '*vsp-erster-dl*)) (setq *vsp-erster-dl* T))
|
(if (not (boundp '*vsp-erster-dl*)) (setq *vsp-erster-dl* T))
|
||||||
(if (not (boundp '*vsp-wo*)) (setq *vsp-wo* "Spec"))
|
(if (not (boundp '*vsp-wo*)) (setq *vsp-wo* "Spec"))
|
||||||
(if (not (boundp '*vsp-prompts*)) (setq *vsp-prompts* 0))
|
(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
|
;; Fehler sammeln (aeltester zuerst) statt beim ersten abzubrechen: eine
|
||||||
;; handgeschriebene Spec hat selten genau einen Tippfehler.
|
;; handgeschriebene Spec hat selten genau einen Tippfehler.
|
||||||
@@ -684,7 +689,7 @@
|
|||||||
|
|
||||||
;; Eine Kette aus einer Spec bauen. Rueckgabe: Ergebnis-Record (Alist).
|
;; Eine Kette aus einer Spec bauen. Rueckgabe: Ergebnis-Record (Alist).
|
||||||
;; Die Klammer um vf-linienzug-modus ist dieselbe wie in
|
;; 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
|
;; gefangen wird, laeuft der Abbruch-Handler der Modus-Funktion NICHT - eine
|
||||||
;; abgebrochene Kette bleibt also als lose Geometrie liegen und ist per
|
;; abgebrochene Kette bleibt also als lose Geometrie liegen und ist per
|
||||||
;; c:VF_SEKTION_RESTORE rettbar (vfl-segment-xdata-sichern hat pro Iteration
|
;; 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
|
;; Dimension je Kette neu setzen: der Abbruch-Handler in
|
||||||
;; vf-linienzug-modus setzt *ssg-ils-dim* zurueck, eine abgebrochene
|
;; vf-linienzug-modus setzt *ssg-ils-dim* zurueck, eine abgebrochene
|
||||||
;; Kette wuerde die folgenden sonst in der falschen Dimension bauen.
|
;; 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))
|
(if dim (setq *ssg-ils-dim* dim))
|
||||||
(setq rec (vsp-set rec "dimension" (vsp-dim-text)))
|
(setq rec (vsp-set rec "dimension" (vsp-dim-text)))
|
||||||
(princ (strcat " -> " (itoa (length journal)) " Journal-Eintraege, "
|
(princ (strcat " -> " (itoa (length journal)) " Journal-Eintraege, "
|
||||||
@@ -811,7 +820,7 @@
|
|||||||
;; TEIL 8: BEFEHL
|
;; TEIL 8: BEFEHL
|
||||||
;; ============================================================
|
;; ============================================================
|
||||||
;; Fragt nichts. Spec-Datei aus DXFM_VF_SPEC, sonst
|
;; 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.
|
;; tests/output.
|
||||||
|
|
||||||
;; ssg-ensure gibt es nur, wenn das Menue geladen wurde (SSG_LIB.mnl). Im
|
;; 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 Ergebnis: " (itoa ok) " OK, " (itoa rest) " Fehler"
|
||||||
"\n================================================")))
|
"\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
|
||||||
|
;; <name>: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")
|
(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))
|
(setq specs (vsp-json-laden datei))
|
||||||
(if (null specs)
|
(cond
|
||||||
(princ "\n[VF_SPEC] Keine Specs gelesen - Abbruch.")
|
((null specs)
|
||||||
(progn
|
(princ "\n[VF_SPEC] Keine Specs gelesen - Abbruch.")
|
||||||
|
nil)
|
||||||
|
(T
|
||||||
(ssg-start "VF_SPEC_BAU" '(("OSMODE") ("CECOLOR") ("ATTREQ") ("ATTDIA")))
|
(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 "OSMODE" 0)
|
||||||
(setvar "ATTREQ" 0)
|
(setvar "ATTREQ" 0)
|
||||||
(setvar "ATTDIA" 0)
|
(setvar "ATTDIA" 0)
|
||||||
@@ -873,10 +875,40 @@
|
|||||||
(princ (strcat "\n[VF_SPEC] FEHLER in der Spec-Schleife: "
|
(princ (strcat "\n[VF_SPEC] FEHLER in der Spec-Schleife: "
|
||||||
(vl-catch-all-error-message records)))
|
(vl-catch-all-error-message records)))
|
||||||
(setq records '())))
|
(setq records '())))
|
||||||
(vl-mkdir (vl-filename-directory ziel))
|
|
||||||
(vsp-results-schreiben records ziel)
|
|
||||||
(vsp-bilanz records)
|
(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))
|
||||||
|
|
||||||
(princ "\n[vf_spec] geladen (VF_SPEC_BAU)")
|
(princ "\n[vf_spec] geladen (VF_SPEC_BAU)")
|
||||||
|
|||||||
@@ -434,6 +434,53 @@ Zwei Nacharbeiten aus dem Lauf:
|
|||||||
`(load (strcat (getenv "DXFMAKRO") "/tests/test_vf_spec.lsp"))` - oder
|
`(load (strcat (getenv "DXFMAKRO") "/tests/test_vf_spec.lsp"))` - oder
|
||||||
ueber `SSG_RUN_ALL_TESTS`, wo `vf_spec` inzwischen in `alltests.json` steht.
|
ueber `SSG_RUN_ALL_TESTS`, wo `vf_spec` inzwischen in `alltests.json` steht.
|
||||||
|
|
||||||
|
### 4.7 Aufteilung der Testfaelle (2026-09-03)
|
||||||
|
|
||||||
|
Nach dem gruenen `VF_SPEC_BAU`-Lauf gab es zwei Tests, die dieselbe Geometrie
|
||||||
|
bauen. Aufgeloest:
|
||||||
|
|
||||||
|
| | Aufzeichnungsformat | Spec |
|
||||||
|
|---|---|---|
|
||||||
|
| Daten | `tests/testdata/hm_recformat.json` (281 Objekte) | `tests/testdata/hundm05.json` (52 Objekte) |
|
||||||
|
| Treiber | `tests/test_hm_recformat.lsp`, `TEST_HM_RECFORMAT` | `tests/test_hundm05.lsp`, `TEST_HUNDM05` |
|
||||||
|
| pytest | `tests/test_hm_recformat.py` | `tests/test_hundm05.py` |
|
||||||
|
| In `alltests.json` | **abgeschaltet** | aktiv |
|
||||||
|
|
||||||
|
Der Spec-Test ist damit der routinemaessige Bau-Test; die Spec ist auch die
|
||||||
|
Datei, in der weitere Ketten der Anlage nachgetragen werden.
|
||||||
|
|
||||||
|
**Warum die Aufzeichnung bleibt** (und nicht durch die Spec ersetzt wird):
|
||||||
|
|
||||||
|
1. Sie ist die **einzige eingecheckte Kopie der Originaldaten** -
|
||||||
|
`data/polylines.dxf` ist mit 124 MB per `.gitignore` ausgeschlossen. Die
|
||||||
|
Spec ist daraus abgeleitet; ohne die Aufzeichnung faellt die rechte Seite
|
||||||
|
des Rundlauf-Beweises weg und die Spec belegt nur noch, dass der Emitter
|
||||||
|
mit sich selbst uebereinstimmt.
|
||||||
|
2. Sie prueft eine **andere Invariante**: ihr Journal kommt roh aus der XDATA
|
||||||
|
einer Kundenzeichnung. Damit ist `TEST_HM_RECFORMAT` der einzige Test
|
||||||
|
dafuer, dass ein BESTEHENDER `VF_n`-Block weiter abspielbar ist - also
|
||||||
|
dass Doppelklick-Edit und 2D/3D-Konvertierung an Altbestand funktionieren
|
||||||
|
(I1). Ein spec-gebautes Journal kann das nicht zeigen, es kommt aus dem
|
||||||
|
Uebersetzer. Bei Aenderungen am Journal-Format, an den
|
||||||
|
`vfl-in-*`-Wrappern oder am XDATA-Layout also bewusst einschalten.
|
||||||
|
3. Sie **dokumentiert die Frage-Reihenfolge**: jeder Eintrag traegt einen
|
||||||
|
`kommentar`, der die zugehoerige Frage nennt. Die Spec verbirgt den Dialog -
|
||||||
|
das ist ihr Zweck - und kann diese Rolle nicht uebernehmen.
|
||||||
|
|
||||||
|
Damit es nur EINEN Code-Pfad gibt, der aus einer Spec Geometrie erzeugt, hat
|
||||||
|
`tests/test_hundm05.lsp` keine eigene Bau-Logik: es ruft `vsp-bau-datei` aus
|
||||||
|
`Lisp/vf_spec.lsp` - dieselbe Funktion, die auch `c:VF_SPEC_BAU` nutzt. Die
|
||||||
|
Ergebnisdatei heisst jetzt nach der Spec-Datei
|
||||||
|
(`<basisname>_results.json`), damit Befehl und Testrunner in dieselbe Datei
|
||||||
|
schreiben und keine zweite Kopie derselben Ergebnisse entsteht.
|
||||||
|
|
||||||
|
Nebenbei behoben: das Anlagenkuerzel der `test_id` in
|
||||||
|
`lib/vf_journal_export.py` kam aus dem Namen der Ziel-JSON. Nach der
|
||||||
|
Umbenennung haette eine Regenerierung die ids stillschweigend von
|
||||||
|
`VF_hundm05_LZ_*` auf `VF_hm_recformat_LZ_*` geaendert - und damit die
|
||||||
|
Paarung zwischen den beiden Testlaeufen zerlegt. Jetzt feste Konstante
|
||||||
|
`TEST_ID_ANLAGE`.
|
||||||
|
|
||||||
**Noch offen in Stufe 1**: die Uebersetzer fuer Modus 2 (`pfad-handles` +
|
**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
|
Segmentliste, Abschnitt 4.4) und Modus 3 sind NICHT gebaut - Modus 2 braucht
|
||||||
laut Reihenfolge (Punkt 11) erst 2-3 echte `"linienzug2"`-Journale als
|
laut Reihenfolge (Punkt 11) erst 2-3 echte `"linienzug2"`-Journale als
|
||||||
|
|||||||
@@ -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
|
abbildet. Modus 2 und 3 haben noch keinen Uebersetzer (Modus 2 braucht erst
|
||||||
echte `"linienzug2"`-Journale als Referenz).
|
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`/
|
setzt alle drei Schalter mit Save/Restore, ersetzt `getpoint`/`getstring`/
|
||||||
`getint`/`getreal`/`alert` zusaetzlich durch **zaehlende** Stubs (Netz fuer
|
`getint`/`getreal`/`alert` zusaetzlich durch **zaehlende** Stubs (Netz fuer
|
||||||
Prompts ausserhalb der Wrapper) und schreibt `prompts`, `headless_fehler` und
|
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-konvertiere-ent` (Batch-2D/3D-Konvertierung) laeuft weiter ohne
|
||||||
`*vfl-headless*` - dort ist eine Live-Rueckfrage bisher gewolltes Verhalten.
|
`*vfl-headless*` - dort ist eine Live-Rueckfrage bisher gewolltes Verhalten.
|
||||||
|
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ Aufruf:
|
|||||||
python vf_journal_export.py <dxf> <output.json> [--csv <sivas_export.csv>]
|
python vf_journal_export.py <dxf> <output.json> [--csv <sivas_export.csv>]
|
||||||
[--kein-startpunkt-fix]
|
[--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 \
|
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
|
import argparse
|
||||||
@@ -57,6 +57,9 @@ TRENNER = "^J"
|
|||||||
|
|
||||||
RUND = 6
|
RUND = 6
|
||||||
|
|
||||||
|
# Anlagenkuerzel in den test_ids (VF_<kuerzel>_LZ_01 ...).
|
||||||
|
TEST_ID_ANLAGE = "hundm05"
|
||||||
|
|
||||||
# Antwort-Klartexte der Menues aus vf_linienzug.lsp (nur zur Annotation).
|
# Antwort-Klartexte der Menues aus vf_linienzug.lsp (nur zur Annotation).
|
||||||
JA_NEIN = {"1": "Ja", "2": "Nein"}
|
JA_NEIN = {"1": "Ja", "2": "Nein"}
|
||||||
SEITE = {"1": "links", "2": "rechts"}
|
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
|
ssg-parse-json-array (Lisp/ssg_core.lsp) liest die Datei ZEILENWEISE und
|
||||||
behandelt jedes "{" als Objektanfang. Ein verschachteltes Array wuerde die
|
behandelt jedes "{" als Objektanfang. Ein verschachteltes Array wuerde die
|
||||||
Kopf-Objekte verschlucken. Der Treiber gruppiert wieder ueber "test_id"
|
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
|
# Anlagenkuerzel der test_id: FEST, nicht aus dem Dateinamen
|
||||||
# Testfall-Sammlung), nicht aus dem Zeichnungsnamen: tests/testdata/
|
# abgeleitet. Die id benennt die ANLAGE, nicht die Datei - und sie
|
||||||
# hundm05.json -> VF_hundm05_LZ_01 .. _nn.
|
# muss stabil bleiben, weil dieselbe Kette in beiden Testlaeufen
|
||||||
praefix = os.path.splitext(os.path.basename(ziel_datei))[0]
|
# (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 = []
|
faelle = []
|
||||||
for nr, e in enumerate(sorted(inserts, key=lambda i: i["x"]), start=1):
|
for nr, e in enumerate(sorted(inserts, key=lambda i: i["x"]), start=1):
|
||||||
eintraege = Journal(e["journal_roh"]).dekodiere()
|
eintraege = Journal(e["journal_roh"]).dekodiere()
|
||||||
|
|||||||
+13
-10
@@ -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):
|
Aufruf (erzeugt die Spec-Testdaten aus dem Protokoll der echten Ketten):
|
||||||
|
|
||||||
python lib/vf_spec_export.py tests/testdata/hundm05.json \
|
python lib/vf_spec_export.py tests/testdata/hm_recformat.json \
|
||||||
tests/testdata/vf_spec_hundm05.json
|
tests/testdata/hundm05.json
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@@ -111,7 +111,7 @@ def _ja_code(wert):
|
|||||||
# STR Menue-/Wertantwort STEP Glied-Marker (Label = Segmenttyp)
|
# STR Menue-/Wertantwort STEP Glied-Marker (Label = Segmenttyp)
|
||||||
|
|
||||||
def tokens_aus_protokoll(eintraege):
|
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
|
zurueck in Journal-Tokens uebersetzen - die Umkehrung von
|
||||||
vf_journal_export.Journal.add."""
|
vf_journal_export.Journal.add."""
|
||||||
tok = []
|
tok = []
|
||||||
@@ -577,8 +577,8 @@ def journal_aus_spec(spec):
|
|||||||
# Flaches JSON (vom LISP-Parser lesbar)
|
# Flaches JSON (vom LISP-Parser lesbar)
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# ssg-load-json/ssg-parse-json-array liest ZEILENWEISE und FLACH: jedes "{"
|
# ssg-load-json/ssg-parse-json-array liest ZEILENWEISE und FLACH: jedes "{"
|
||||||
# beginnt ein Objekt, Verschachtelung ist unmoeglich. Also dieselbe Form wie
|
# beginnt ein Objekt, Verschachtelung ist unmoeglich. Also dieselbe flache
|
||||||
# tests/testdata/hundm05.json - Kopf-Objekt ("spec_id"), dann Sektionen
|
# Form wie die Aufzeichnung: ein Kopf-Objekt ("spec_id"), dann Sektionen
|
||||||
# ("glied") und deren Sub-Knoten ("sub") in Reihenfolge.
|
# ("glied") und deren Sub-Knoten ("sub") in Reihenfolge.
|
||||||
|
|
||||||
def flache_objekte(spec_id, spec, kopf_extra=None):
|
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):
|
def spec_aus_flachen_objekten(objekte):
|
||||||
"""Umkehrung von flache_objekte - dieselbe Gruppierregel wie
|
"""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 = []
|
specs = []
|
||||||
for obj in objekte:
|
for obj in objekte:
|
||||||
if "spec_id" in obj:
|
if "spec_id" in obj:
|
||||||
@@ -680,9 +680,9 @@ def spec_aus_flachen_objekten(objekte):
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
p = argparse.ArgumentParser(
|
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.")
|
"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)")
|
p.add_argument("ziel", help="Ausgabe (flaches Spec-JSON)")
|
||||||
args = p.parse_args()
|
args = p.parse_args()
|
||||||
|
|
||||||
@@ -720,8 +720,11 @@ def main():
|
|||||||
"planquadrat", "block", "dxf_handle", "quelle"):
|
"planquadrat", "block", "dxf_handle", "quelle"):
|
||||||
if feld in kopf:
|
if feld in kopf:
|
||||||
extra[feld] = kopf[feld]
|
extra[feld] = kopf[feld]
|
||||||
alle.extend(flache_objekte(tid.replace("hundm05_LZ", "spec_LZ"),
|
# Die spec_id bleibt die Kettenkennung aus dem Protokoll: dieselbe
|
||||||
spec, extra))
|
# 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"
|
print("%s %-20s %2d Sektionen, %3d Tokens - Rundlauf ok"
|
||||||
% (PREFIX, tid, len(spec["sektionen"]), len(tok)))
|
% (PREFIX, tid, len(spec["sektionen"]), len(tok)))
|
||||||
|
|
||||||
|
|||||||
+14
-3
@@ -102,7 +102,7 @@ Die Datei `tests/alltests.json` ist die zentrale Registry aller Test-Module.
|
|||||||
{ "name": "omniflo_strecke", "save": "dxf", "module": "OmniModulInsert" },
|
{ "name": "omniflo_strecke", "save": "dxf", "module": "OmniModulInsert" },
|
||||||
{ "name": "gefaellestrecke", "save": "dwg", "module": "Gefaellestrecke" },
|
{ "name": "gefaellestrecke", "save": "dwg", "module": "Gefaellestrecke" },
|
||||||
{ "name": "mubea", "save": "dxf", "module": "KreiselInsert" },
|
{ "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_foerderer.py # pytest-Validierung Foerderer (Status/Mathematik/CSV/Sivas)
|
||||||
test_gefaellestrecke.py # pytest-Validierung Gefaellestrecke
|
test_gefaellestrecke.py # pytest-Validierung Gefaellestrecke
|
||||||
test_mubea.py # pytest-Validierung Mubea-Gesamtmodell
|
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),
|
test_vf_spec.lsp # LISP-Unittest: Spec-Uebersetzer (vsp-journal),
|
||||||
# je Glied-Typ + die 5 echten HundM-Ketten
|
# je Glied-Typ + die 5 echten HundM-Ketten
|
||||||
# gegen ihr aufgezeichnetes Journal
|
# gegen ihr aufgezeichnetes Journal
|
||||||
@@ -179,7 +188,9 @@ tests/
|
|||||||
kreisel_tests.json # Testfall-Definitionen fuer Kreisel
|
kreisel_tests.json # Testfall-Definitionen fuer Kreisel
|
||||||
omniflo_tests.json # Testfall-Definitionen fuer Omniflo
|
omniflo_tests.json # Testfall-Definitionen fuer Omniflo
|
||||||
omniflo_strecke_tests.json # Streckenzug: 4 Geraden + 2 Boegen + 1 Weiche (7 Elemente)
|
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)
|
output/ # Ergebnisse aus BricsCAD (nicht in Git)
|
||||||
<name>_results.json # JSON-Ergebnisse pro Testmodul (immer)
|
<name>_results.json # JSON-Ergebnisse pro Testmodul (immer)
|
||||||
<name>_tests.dxf/.dwg # Zeichnung pro Testmodul (gemaess save in alltests.json)
|
<name>_tests.dxf/.dwg # Zeichnung pro Testmodul (gemaess save in alltests.json)
|
||||||
|
|||||||
+2
-1
@@ -8,5 +8,6 @@
|
|||||||
{ "name": "omniflo_strecke", "save": "dxf", "module": "OmniModulInsert" },
|
{ "name": "omniflo_strecke", "save": "dxf", "module": "OmniModulInsert" },
|
||||||
{ "name": "gefaellestrecke", "save": "dwg", "module": "Gefaellestrecke" },
|
{ "name": "gefaellestrecke", "save": "dwg", "module": "Gefaellestrecke" },
|
||||||
{ "name": "mubea", "save": "dxf", "module": "KreiselInsert" },
|
{ "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 }
|
||||||
]
|
]
|
||||||
|
|||||||
+19
-17
@@ -189,35 +189,37 @@ def mubea_dxf():
|
|||||||
return ezdxf.readfile(path)
|
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
|
@pytest.fixture
|
||||||
def hundm05_testdata():
|
def hmrec_testdata():
|
||||||
"""Laedt die HundM05-Testdefinitionen aus testdata/hundm05.json.
|
"""Laedt das Frage-Antwort-Protokoll aus testdata/hm_recformat.json."""
|
||||||
|
path = os.path.join(_testdata_dir(), "hm_recformat.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")
|
|
||||||
return _load_json(path)
|
return _load_json(path)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def hundm05_results():
|
def hmrec_results():
|
||||||
"""Laedt die HundM05-Ergebnisse aus output/hundm05_results.json."""
|
"""Laedt die Ergebnisse aus output/hm_recformat_results.json."""
|
||||||
path = os.path.join(_output_dir(), "hundm05_results.json")
|
path = os.path.join(_output_dir(), "hm_recformat_results.json")
|
||||||
if not os.path.exists(path):
|
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)
|
data = _load_json(path)
|
||||||
if not data:
|
if not data:
|
||||||
pytest.skip("hundm05_results.json ist leer")
|
pytest.skip("hm_recformat_results.json ist leer")
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def hundm05_dxf():
|
def hmrec_dxf():
|
||||||
"""Laedt die erzeugte HundM05-DXF aus output/hundm05_tests.dxf."""
|
"""Laedt die erzeugte DXF aus output/hm_recformat_tests.dxf."""
|
||||||
path = os.path.join(_output_dir(), "hundm05_tests.dxf")
|
path = os.path.join(_output_dir(), "hm_recformat_tests.dxf")
|
||||||
if not os.path.exists(path):
|
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)
|
return ezdxf.readfile(path)
|
||||||
|
|||||||
@@ -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"))
|
||||||
@@ -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'
|
||||||
+79
-636
@@ -1,41 +1,32 @@
|
|||||||
;; ============================================================
|
;; ============================================================
|
||||||
;; test_hundm05.lsp - Integrationstest: baut die 5 VarioFoerderer-Linienzuege
|
;; 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
|
;; Datenquelle: tests/testdata/hundm05.json - die Ketten im Spec-Format
|
||||||
;; python lib/vf_journal_export.py data/polylines.dxf \
|
;; (Domaenenwerte statt Menue-Codes, siehe Lisp/vf_spec.lsp und
|
||||||
;; tests/testdata/hundm05.json --csv results/HundM_export.csv
|
;; doc/variofoerderer.md Abschnitt 8b). Erzeugt aus den echten
|
||||||
;; Das sind die EINGABE-JOURNALE der echten Ketten - ausgelesen aus der XDATA
|
;; Aufzeichnungen mit
|
||||||
;; (App SSG_VF_EDIT, Marker "linienzug") der VF_n-Bloecke in data/polylines.dxf,
|
;; python lib/vf_spec_export.py tests/testdata/hm_recformat.json \
|
||||||
;; also genau die Werte, mit denen die Ketten in BricsCAD gebaut wurden.
|
;; 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
|
;; Dieser Test hat KEINE eigene Bau-Logik: er ruft vsp-bau-datei aus
|
||||||
;; tests/testdata/object_data.md Abschnitt 8):
|
;; Lisp/vf_spec.lsp - denselben Weg, den auch der Befehl VF_SPEC_BAU nimmt.
|
||||||
;; Kopf-Objekt (traegt "test_id"): test_id, anlagetyp, modus,
|
;; Es gibt also genau einen Code-Pfad, der aus einer Spec Geometrie erzeugt.
|
||||||
;; 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).
|
|
||||||
;;
|
;;
|
||||||
;; WARUM REPLAY UND KEINE EINGABE-MOCKS (Unterschied zu test_linienzug.lsp):
|
;; ABGRENZUNG zu TEST_HM_RECFORMAT (tests/test_hm_recformat.lsp): dort werden
|
||||||
;; test_linienzug.lsp ersetzt getpoint/getstring/getint/getreal durch Mocks und
|
;; dieselben Ketten aus dem ROHEN Journal der Kundenzeichnung gebaut. Das ist
|
||||||
;; rechnet Punkte aus "dL"+"hz" selbst aus. Das geht nur, weil
|
;; der Regressionstest fuer das Aufzeichnungsformat (bleibt ein bestehender
|
||||||
;; linienzug_tests.json zu JEDEM Segment ein "hz" mitfuehrt. Ein echtes Journal
|
;; VF_n-Block abspielbar - Doppelklick-Edit, 2D/3D-Konvertierung?) und
|
||||||
;; tut das nicht: vfl-in-abstand journalisiert die Fahrtrichtung NUR beim
|
;; deshalb dort auf "disabled": true, um nicht zweimal dieselbe Geometrie zu
|
||||||
;; allerersten Segment der Kette, jedes weitere erbt sie vom Vorgaenger. Ein
|
;; bauen.
|
||||||
;; 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:
|
;; Voraussetzung:
|
||||||
;; - SSG_LIB geladen (VarioFoerderer inkl. vf_linienzug, Gefaellestrecke,
|
;; - SSG_LIB geladen (VarioFoerderer inkl. vf_linienzug + vf_spec,
|
||||||
;; ssg_core, ssg_dbg)
|
;; Gefaellestrecke, ssg_core, ssg_dbg)
|
||||||
;; - Umgebungsvariable DXFMAKRO gesetzt
|
;; - Umgebungsvariable DXFMAKRO gesetzt
|
||||||
;;
|
;;
|
||||||
;; Speichert (via SSG_RUN_ALL_TESTS bei "save":"dxf"):
|
;; 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 ; in der aktuellen Dimension (ssg-ils-dim-aktuell)
|
||||||
;; TEST_HUNDM05_2D ; erzwungen 2D
|
;; TEST_HUNDM05_2D ; erzwungen 2D
|
||||||
;; TEST_HUNDM05_3D ; erzwungen 3D
|
;; 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) ---
|
(defun c:TEST_HUNDM05 ( / datei)
|
||||||
;; Die extra-Felder in hundm05:result-json werden ROH in das JSON gesetzt,
|
(setq datei (hundm05:spec-datei))
|
||||||
;; 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)))
|
|
||||||
|
|
||||||
(princ "\n\n================================================================")
|
(princ "\n\n================================================================")
|
||||||
(princ "\n TEST_HUNDM05 - 5 Linienzuege der Anlage HundM (polylines.dxf)")
|
(princ "\n TEST_HUNDM05 - 5 Linienzuege der Anlage HundM (aus der Spec)")
|
||||||
(princ (strcat "\n " (itoa (length daten)) " JSON-Objekte, "
|
(princ (strcat "\n " datei))
|
||||||
(itoa (length ketten)) " Ketten"))
|
|
||||||
(princ "\n================================================================")
|
(princ "\n================================================================")
|
||||||
|
(cond
|
||||||
(setq anz-ok 0 anz-fehler 0)
|
((null (car (atoms-family 1 '("VSP-BAU-DATEI"))))
|
||||||
(hundm05:stubs-an)
|
(princ "\n[TEST_HUNDM05] FEHLER: vf_spec.lsp nicht geladen"
|
||||||
;; Stubs MUESSEN auch nach einem unerwarteten Fehler zurueckgenommen werden.
|
" (VarioFoerderer laden).")
|
||||||
(setq results-list (vl-catch-all-apply 'hundm05:ketten-bauen (list ketten)))
|
(setq *hundm05-records* nil))
|
||||||
(hundm05:stubs-aus)
|
((null (findfile datei))
|
||||||
(if (vl-catch-all-error-p results-list)
|
(princ (strcat "\n[TEST_HUNDM05] FEHLER: " datei " nicht gefunden!"))
|
||||||
(progn
|
(setq *hundm05-records* nil))
|
||||||
(princ (strcat "\n[TEST_HUNDM05] FEHLER in der Kettenschleife: "
|
(T
|
||||||
(vl-catch-all-error-message results-list)))
|
(setq *hundm05-records* (vsp-bau-datei datei))))
|
||||||
(setq results-list '())))
|
(princ))
|
||||||
|
|
||||||
(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)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
;; ============================================================
|
;; ============================================================
|
||||||
;; Dieselbe Anlage in einer bestimmten Dimension bauen
|
;; Dieselbe Anlage in einer bestimmten Dimension bauen
|
||||||
;; ============================================================
|
;; ============================================================
|
||||||
;; Die Eingabedaten (hundm05.json) sind dimensionsfrei - sie beschreiben die
|
;; Die Spec ist dimensionsfrei - sie beschreibt die Eingaben, mit denen die
|
||||||
;; Eingaben, mit denen die Ketten gebaut wurden. Ob daraus flache 2D-Symbole
|
;; Ketten gebaut wurden. Ob daraus flache 2D-Symbole oder 3D-Modelle werden,
|
||||||
;; oder 3D-Modelle werden, entscheidet allein die Blockvariante (_2D/_3D), die
|
;; entscheidet allein die Blockvariante (_2D/_3D), die ssg-ils-dim-aktuell
|
||||||
;; ssg-ils-dim-aktuell aufloest. Unsere Bibliothek hat beide, also laesst sich
|
;; aufloest. Unsere Bibliothek hat beide, also laesst sich dieselbe Anlage
|
||||||
;; dieselbe Anlage ohne Datenaenderung in 2D ODER 3D aufbauen.
|
;; ohne Datenaenderung in 2D ODER 3D aufbauen.
|
||||||
;; *hundm05-dim-override* wird in der Kettenschleife je Kette neu gesetzt
|
;; *vsp-dim-override* wird von vsp-bau-aus-spec JE KETTE angewandt (der
|
||||||
;; (siehe dort).
|
;; Abbruch-Handler in vf-linienzug-modus setzt *ssg-ils-dim* zurueck).
|
||||||
(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
|
|
||||||
)
|
|
||||||
|
|
||||||
(defun c:TEST_HUNDM05_2D ( / ) (hundm05:mit-dimension "2D"))
|
(defun hundm05:mit-dimension (dim / alt)
|
||||||
(defun c:TEST_HUNDM05_3D ( / ) (hundm05:mit-dimension "3D"))
|
(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 <name>: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)
|
||||||
|
|||||||
+100
-348
@@ -1,18 +1,17 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- 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:
|
Eingabe: tests/testdata/hundm05.json (die 5 Ketten im Spec-Format).
|
||||||
1. Die Testdaten selbst (tests/testdata/hundm05.json): Struktur der
|
Gebaut von TEST_HUNDM05 bzw. VF_SPEC_BAU in BricsCAD, Ergebnis in
|
||||||
Eingabe-Journale und Lesbarkeit durch den LISP-Parser.
|
tests/output/hundm05_results.json.
|
||||||
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.
|
|
||||||
|
|
||||||
Die Testdaten stammen aus lib/vf_journal_export.py (Journale der VF_n-Bloecke
|
Diese Tests brauchen also einen CAD-Lauf und werden sonst uebersprungen.
|
||||||
aus data/polylines.dxf, XDATA-App SSG_VF_EDIT) und nicht aus Handarbeit -
|
Den UEBERSETZER (Spec -> Journal) prueft tests/test_vf_spec.py ohne CAD;
|
||||||
Schema siehe tests/testdata/object_data.md Abschnitt 8.
|
das Aufzeichnungsformat prueft tests/test_hm_recformat.py.
|
||||||
|
|
||||||
|
Der Ergebnis-Record je Kette kommt aus Lisp/vf_spec.lsp (vsp-result-json).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
@@ -20,361 +19,114 @@ import os
|
|||||||
|
|
||||||
import pytest
|
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")]
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
@pytest.fixture(scope="module")
|
||||||
# Hilfsfunktionen: flaches JSON in Ketten gruppieren
|
def spec_objekte():
|
||||||
# ============================================================
|
if not os.path.isfile(SPEC_JSON):
|
||||||
# Gleiche Regel wie hundm05:gruppiere in test_hundm05.lsp: ein Objekt mit
|
pytest.skip("%s fehlt (lib/vf_spec_export.py erzeugt es)" % SPEC_JSON)
|
||||||
# "test_id" ist ein Kopf und beginnt eine Kette, die folgenden Objekte mit
|
with open(SPEC_JSON, encoding="utf-8") as fh:
|
||||||
# "typ" sind ihre Eingaben.
|
return json.load(fh)
|
||||||
|
|
||||||
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):
|
@pytest.fixture(scope="module")
|
||||||
"""Anzahl der Journal-Eintraege, die der Treiber daraus baut: ein
|
def spec_ergebnisse():
|
||||||
point_rel mit "hz" wird zu ZWEI Eintraegen (DL + REAL)."""
|
for pfad in ERGEBNIS_ORTE:
|
||||||
return sum(2 if (e["typ"] == "point_rel" and "hz" in e) else 1
|
if os.path.isfile(pfad):
|
||||||
for e in eingaben)
|
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):
|
class TestSpecBau:
|
||||||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), *teile)
|
|
||||||
|
|
||||||
|
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):
|
||||||
# Testdaten-Pruefung (hundm05.json selbst)
|
for r in spec_ergebnisse:
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
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:
|
|
||||||
assert r["status"] == "executed", \
|
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,
|
def test_keine_spec_fehler(self, spec_ergebnisse):
|
||||||
hundm05_results):
|
for r in spec_ergebnisse:
|
||||||
"""Alle aufgezeichneten Werte muessen im Bau abgerufen worden sein.
|
assert not r["spec_fehler"], \
|
||||||
Bleibt etwas offen, ist der Ablauf von der Aufzeichnung abgewichen und
|
f'{r["spec_id"]}: {r["spec_fehler"]}'
|
||||||
die Geometrie kann der Vorlage nicht entsprechen."""
|
|
||||||
soll = {k["test_id"]: _journal_laenge(e)
|
def test_kein_prompt(self, spec_ergebnisse):
|
||||||
for k, e in _ketten(hundm05_testdata)}
|
"""Der positive Beweis fuer "kein Dialog, keine Konsolenfrage": die
|
||||||
for r in hundm05_results:
|
zaehlenden Stubs wurden nie erreicht."""
|
||||||
assert "eingaben_offen" in r, (
|
for r in spec_ergebnisse:
|
||||||
f'{r["test_id"]}: kein eingaben_offen im Ergebnis - die Kette '
|
assert r["prompts"] == 0, \
|
||||||
f'ist vor der Auswertung abgebrochen (status='
|
f'{r["spec_id"]}: {r["prompts"]} Live-Eingabe(n) erreicht'
|
||||||
f'{r.get("status")!r}, {r.get("fehler_text")})')
|
|
||||||
|
def test_journal_vollstaendig_verbraucht(self, spec_ergebnisse):
|
||||||
|
for r in spec_ergebnisse:
|
||||||
assert r["eingaben_offen"] == 0, \
|
assert r["eingaben_offen"] == 0, \
|
||||||
f'{r["test_id"]}: {r["eingaben_offen"]} Journal-Werte offen'
|
f'{r["spec_id"]}: {r["eingaben_offen"]} Werte offen'
|
||||||
assert r["eingaben_gesamt"] == soll[r["test_id"]], \
|
assert r["eingaben_gesamt"] > 0
|
||||||
f'{r["test_id"]}: {r["eingaben_gesamt"]} Journal-Eintraege ' \
|
|
||||||
f'abgespielt, erwartet {soll[r["test_id"]]}'
|
|
||||||
|
|
||||||
def test_keine_live_eingabe(self, hundm05_results):
|
def test_glieder_wie_geplant(self, spec_ergebnisse):
|
||||||
"""Kein Prompt, kein Dialog: der Treiber schaltet *vfl-headless* ein
|
"""Der Desync-Detektor, der auch dann greift, wenn die Queue aufgeht:
|
||||||
(harter Abbruch statt stillem Rueckfall auf Live-Eingabe, siehe
|
eine geometrisch abgewiesene Sektion verbraucht nur weniger
|
||||||
vfl-headless-abbruch in Lisp/vf_linienzug.lsp) UND ersetzt zusaetzlich
|
Eintraege, die Glied-Folge weicht dann aber ab."""
|
||||||
getpoint/getstring/getint/getreal durch zaehlende Stubs. Beide Wege
|
for r in spec_ergebnisse:
|
||||||
muessen leer bleiben - "prompts" ist der positive Beweis, dass keine
|
assert r["glieder_ist"] == r["glieder_soll"], \
|
||||||
Eingabe ausserhalb der vfl-in-*-Wrapper erreicht wurde."""
|
f'{r["spec_id"]}: gebaut {r["glieder_ist"]}, ' \
|
||||||
for r in hundm05_results:
|
f'geplant {r["glieder_soll"]}'
|
||||||
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, hundm05_results):
|
def test_keine_meldungen(self, spec_ergebnisse):
|
||||||
"""*vfl-meldungen* sammelt die abgewiesenen Sektionen (frueher
|
for r in spec_ergebnisse:
|
||||||
blockierende alert-Fenster, jetzt vfl-meldung). Eine Meldung heisst:
|
assert not r["meldungen"], \
|
||||||
ein Glied liess sich geometrisch nicht bauen - der Nachbau weicht dann
|
f'{r["spec_id"]}: Meldungen aus dem Bau: {r["meldungen"]}'
|
||||||
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_vf_block_erzeugt(self, hundm05_results):
|
def test_vf_block_erzeugt(self, spec_ergebnisse):
|
||||||
for r in hundm05_results:
|
for r in spec_ergebnisse:
|
||||||
assert r["block_name"].startswith("VF_"), \
|
assert r["block_name"].startswith("VF_"), \
|
||||||
f'{r["test_id"]}: Block "{r["block_name"]}" ist kein VF_-Block'
|
f'{r["spec_id"]}: Block {r["block_name"]!r}'
|
||||||
assert r["block_handle"], f'{r["test_id"]}: kein Handle'
|
assert r["block_handle"]
|
||||||
|
|
||||||
def test_dimension_einheitlich(self, hundm05_results):
|
def test_startpunkt_getroffen(self, spec_objekte, spec_ergebnisse):
|
||||||
"""Alle Ergebnisse eines Laufs stammen aus EINER Bau-Dimension.
|
"""Der Einfuegepunkt des VF_-Blocks ist der Kettenstart aus der
|
||||||
|
Spec - er ist der einzige absolute Anker der ganzen Kette."""
|
||||||
Die Eingabedaten sind dimensionsfrei; ob _2D- oder _3D-Blockvarianten
|
soll = dict((o["spec_id"], o["start_punkt"]) for o in spec_objekte
|
||||||
verwendet werden, loest ssg-ils-dim-aktuell auf. Ein Lauf, in dem beide
|
if "spec_id" in o)
|
||||||
vorkommen, waere ein halb umgeschalteter Zustand (z.B. weil ein
|
for r in spec_ergebnisse:
|
||||||
vorheriger Test *ssg-ils-dim* nicht zurueckgesetzt hat).
|
erwartet = soll[r["spec_id"]]
|
||||||
"""
|
|
||||||
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"]]
|
|
||||||
ist = r["insert_point"]
|
ist = r["insert_point"]
|
||||||
for achse, i in (("X", 0), ("Y", 1), ("Z", 2)):
|
for achse, (e, i) in enumerate(zip(erwartet, ist)):
|
||||||
assert abs(ist[i] - p[i]) < 1.0, \
|
assert abs(e - i) < 1.0, \
|
||||||
f'{r["test_id"]}: Einfuegepunkt {achse} {ist[i]} != ' \
|
f'{r["spec_id"]}: Achse {achse} ist {i}, erwartet {e}'
|
||||||
f'{p[i]}'
|
|
||||||
|
|
||||||
def test_as_es_attribute_aus_eingaben(self, hundm05_testdata,
|
def test_gleiche_ergebnisse_wie_journal_pfad(self, spec_ergebnisse):
|
||||||
hundm05_results):
|
"""Spec-Pfad == Journal-Pfad: dieselben Ketten, gebaut aus der Spec
|
||||||
"""WINKEL_AS/SEITE_AS und WINKEL_ES/SEITE_ES muessen die Antworten aus
|
statt aus dem aufgezeichneten Journal, muessen dieselben Bloecke mit
|
||||||
dem Journal wiedergeben (AS steht in der Praeambel, ES im ES-Glied)."""
|
denselben Attributen ergeben. Vergleich gegen die Ergebnisse von
|
||||||
seiten = {"1": "links", "2": "rechts"}
|
TEST_HUNDM05, falls beide Laeufe vorliegen."""
|
||||||
nach_id = {r["test_id"]: r for r in hundm05_results}
|
pfad = os.path.join(HIER, "output", "hm_recformat_results.json")
|
||||||
for kopf, eingaben in _ketten(hundm05_testdata):
|
if not os.path.isfile(pfad):
|
||||||
r = nach_id[kopf["test_id"]]
|
pytest.skip("tests/output/hm_recformat_results.json fehlt "
|
||||||
attrs = r["actual_attributes"]
|
"(TEST_HM_RECFORMAT nicht gelaufen)")
|
||||||
# Praeambel: [0] Punkt, [1] Hoehe, [2] AS setzen, [3] Winkel,
|
with open(pfad, encoding="utf-8") as fh:
|
||||||
# [4] Seite
|
journal_ergebnisse = json.load(fh)
|
||||||
if eingaben[2]["wert"] == "1":
|
if len(journal_ergebnisse) != len(spec_ergebnisse):
|
||||||
assert attrs.get("WINKEL_AS") == eingaben[3]["wert"], \
|
pytest.skip("unterschiedlich viele Ketten in den beiden Laeufen")
|
||||||
f'{kopf["test_id"]}: WINKEL_AS=' \
|
for spec_r, journal_r in zip(spec_ergebnisse, journal_ergebnisse):
|
||||||
f'{attrs.get("WINKEL_AS")}, erwartet {eingaben[3]["wert"]}'
|
if journal_r["status"] != "executed":
|
||||||
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:
|
|
||||||
continue
|
continue
|
||||||
assert abs(int(attrs["HOEHE_VON_mm"]) - eingaben[1]["wert"]) <= 1, \
|
for tag, wert in journal_r["actual_attributes"].items():
|
||||||
f'{kopf["test_id"]}: HOEHE_VON_mm={attrs["HOEHE_VON_mm"]}, ' \
|
assert spec_r["actual_attributes"].get(tag) == wert, \
|
||||||
f'Starthoehe {eingaben[1]["wert"]}'
|
f'{spec_r["spec_id"]}: Attribut {tag} ist ' \
|
||||||
|
f'{spec_r["actual_attributes"].get(tag)!r}, ' \
|
||||||
|
f'Journal-Pfad hatte {wert!r}'
|
||||||
# ============================================================
|
|
||||||
# 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'
|
|
||||||
|
|||||||
@@ -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
|
(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
|
dieselbe Frage-Reihenfolge kennen, laesst sich ein Journal aus einer echten
|
||||||
Zeichnung verlustfrei in das Testdatenschema zuruecklesen
|
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
|
Die 5 echten HundM-Ketten deckten zwei Zweige NICHT ab, weil ihre Daten sie
|
||||||
nicht enthalten:
|
nicht enthalten:
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
;; (lib/vf_journal_export.py) - drei Implementierungen derselben
|
;; (lib/vf_journal_export.py) - drei Implementierungen derselben
|
||||||
;; Grammatik, die einander pruefen.
|
;; Grammatik, die einander pruefen.
|
||||||
;; 2. Die 5 echten HundM-Ketten: die Spec-Datei
|
;; 2. Die 5 echten HundM-Ketten: die Spec-Datei
|
||||||
;; tests/testdata/vf_spec_hundm05.json muss dieselben Journale ergeben
|
;; tests/testdata/hundm05.json muss dieselben Journale ergeben
|
||||||
;; wie das aufgezeichnete Protokoll tests/testdata/hundm05.json.
|
;; wie das aufgezeichnete Protokoll tests/testdata/hm_recformat.json.
|
||||||
;; Das ist der Beweis "Spec-Pfad == Journal-Pfad" auf Datenebene; der
|
;; 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).
|
;; 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"))
|
;; (load (strcat (getenv "DXFMAKRO") "/tests/test_vf_spec.lsp"))
|
||||||
;; TEST_VF_SPEC
|
;; TEST_VF_SPEC
|
||||||
;;
|
;;
|
||||||
;; Speichert tests/output/vf_spec_results.json ist NICHT dieser Test -
|
;; Dieser Test baut KEINE Geometrie - er prueft nur den Uebersetzer.
|
||||||
;; das schreibt VF_SPEC_BAU. Hier: tests/output/vf_spec_unit_results.json.
|
;; 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* '())
|
(setq *tsp-pass* 0 *tsp-fail* 0 *tsp-fails* '())
|
||||||
@@ -362,8 +363,8 @@
|
|||||||
;; ============================================================
|
;; ============================================================
|
||||||
;; 4. Die 5 echten HundM-Ketten: Spec == aufgezeichnetes Journal
|
;; 4. Die 5 echten HundM-Ketten: Spec == aufgezeichnetes Journal
|
||||||
;; ============================================================
|
;; ============================================================
|
||||||
;; Das aufgezeichnete Protokoll (hundm05.json) in Journal-Tokens
|
;; Das aufgezeichnete Protokoll (hm_recformat.json) in Journal-Tokens
|
||||||
;; zurueckuebersetzen - dieselbe Umrechnung wie in test_hundm05.lsp, hier
|
;; zurueckuebersetzen - dieselbe Umrechnung wie in test_hm_recformat.lsp, hier
|
||||||
;; bewusst dupliziert, damit der Test ohne den anderen Treiber laeuft.
|
;; bewusst dupliziert, damit der Test ohne den anderen Treiber laeuft.
|
||||||
|
|
||||||
(defun tsp-als-string (v)
|
(defun tsp-als-string (v)
|
||||||
@@ -410,9 +411,9 @@
|
|||||||
|
|
||||||
(defun tsp-tests-echte-ketten ( / p-datei s-datei p-daten specs journale i n
|
(defun tsp-tests-echte-ketten ( / p-datei s-datei p-daten specs journale i n
|
||||||
spec soll ist sid)
|
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")
|
(setq s-datei (strcat (getenv "DXFMAKRO")
|
||||||
"/tests/testdata/vf_spec_hundm05.json"))
|
"/tests/testdata/hundm05.json"))
|
||||||
(cond
|
(cond
|
||||||
((null (findfile p-datei))
|
((null (findfile p-datei))
|
||||||
(princ (strcat "\n [SKIP] " p-datei " fehlt")))
|
(princ (strcat "\n [SKIP] " p-datei " fehlt")))
|
||||||
|
|||||||
+16
-120
@@ -1,20 +1,21 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
test_vf_spec.py - Validiert die Spec-Ebene des VF-Linienzugs.
|
test_vf_spec.py - Validiert den Spec-UEBERSETZER des VF-Linienzugs.
|
||||||
|
Laeuft komplett ohne BricsCAD.
|
||||||
Drei Ebenen, die ersten zwei laufen ohne BricsCAD:
|
|
||||||
|
|
||||||
1. Rundlauf: fuer jede der 5 echten HundM-Ketten muss
|
1. Rundlauf: fuer jede der 5 echten HundM-Ketten muss
|
||||||
journal_aus_spec(spec_aus_journal(tokens)) == tokens gelten. Das
|
journal_aus_spec(spec_aus_journal(tokens)) == tokens gelten. Das
|
||||||
beweist, dass die Spec das Eingabe-Journal VERLUSTFREI abbildet - sonst
|
beweist, dass die Spec das Eingabe-Journal VERLUSTFREI abbildet - sonst
|
||||||
waere ein spec-gebauter Nachbau eine andere Kette als das Original.
|
waere ein spec-gebauter Nachbau eine andere Kette als das Original.
|
||||||
2. Die Spec-Testdaten (tests/testdata/vf_spec_hundm05.json): Struktur,
|
2. Die Spec-Testdaten (tests/testdata/hundm05.json): Struktur, Lesbarkeit
|
||||||
Lesbarkeit durch den LISP-Parser, und dass sie dieselben Journale
|
durch den LISP-Parser, und dass sie dieselben Journale ergeben wie die
|
||||||
ergeben wie tests/testdata/hundm05.json.
|
Aufzeichnung tests/testdata/hm_recformat.json.
|
||||||
3. Die Ergebnisse von VF_SPEC_BAU (tests/output/vf_spec_results.json):
|
3. Validierung: was die Spec nicht hergibt, muss ein Fehler sein.
|
||||||
jede Kette gebaut, kein Prompt, kein offener Journal-Wert, Glied-Folge
|
4. Die Erwartungswerte in tests/test_vf_spec.lsp stimmen mit dem hier
|
||||||
wie geplant.
|
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
|
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
|
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
|
import vf_spec_export as vse # noqa: E402
|
||||||
|
|
||||||
HIER = os.path.dirname(__file__)
|
HIER = os.path.dirname(__file__)
|
||||||
PROTOKOLL = os.path.join(HIER, "testdata", "hundm05.json")
|
# Die AUFZEICHNUNG (rohes Journal aus der Kundenzeichnung, Frage-Antwort-
|
||||||
SPEC_JSON = os.path.join(HIER, "testdata", "vf_spec_hundm05.json")
|
# Protokoll) und die daraus abgeleitete SPEC. Erstere ist die Vorlage, an
|
||||||
|
# der die zweite gemessen wird.
|
||||||
# VF_SPEC_BAU schreibt nach tests/output. Aeltere Laeufe (und ein Lauf mit
|
PROTOKOLL = os.path.join(HIER, "testdata", "hm_recformat.json")
|
||||||
# gesetztem DXFM_VF_SPEC_OUT) koennen die Datei woanders haben - darum beide
|
SPEC_JSON = os.path.join(HIER, "testdata", "hundm05.json")
|
||||||
# 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")]
|
|
||||||
|
|
||||||
|
|
||||||
def _lade(pfad, was):
|
def _lade(pfad, was):
|
||||||
@@ -49,14 +47,6 @@ def _lade(pfad, was):
|
|||||||
return json.load(fh)
|
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")
|
@pytest.fixture(scope="module")
|
||||||
def protokoll():
|
def protokoll():
|
||||||
return _lade(PROTOKOLL, "lib/vf_journal_export.py erzeugt es")
|
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")
|
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):
|
def _ketten(protokoll):
|
||||||
"""Gleiche Gruppierregel wie hundm05:gruppiere in LISP."""
|
"""Gleiche Gruppierregel wie hmrec:gruppiere in LISP."""
|
||||||
ketten = []
|
ketten = []
|
||||||
for obj in protokoll:
|
for obj in protokoll:
|
||||||
if "test_id" in obj:
|
if "test_id" in obj:
|
||||||
@@ -458,92 +443,3 @@ class TestLispErwartungen:
|
|||||||
f" LISP: {ist}\n Python: {soll}\n"
|
f" LISP: {ist}\n Python: {soll}\n"
|
||||||
f"Entweder ist die Tokenfolge im LISP-Test falsch uebertragen, "
|
f"Entweder ist die Tokenfolge im LISP-Test falsch uebertragen, "
|
||||||
f"oder die Spec hier und dort beschreibt nicht dasselbe.")
|
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}'
|
|
||||||
|
|||||||
Vendored
+1492
File diff suppressed because it is too large
Load Diff
Vendored
+267
-1341
File diff suppressed because it is too large
Load Diff
Vendored
+18
-9
@@ -407,7 +407,7 @@ Dasselbe Protokoll steht produktionsseitig als **XDATA am fertigen
|
|||||||
wiederabspielbar (`vfl-journal-replay-start` + `vf-linienzug-modus`, genutzt
|
wiederabspielbar (`vfl-journal-replay-start` + `vf-linienzug-modus`, genutzt
|
||||||
vom Doppelklick-Editieren). Ein Journal aus einer echten Zeichnung laesst
|
vom Doppelklick-Editieren). Ein Journal aus einer echten Zeichnung laesst
|
||||||
sich darum 1:1 in dieses Schema zuruecklesen — genau das tut
|
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`:
|
Unterschiede zum handgeschriebenen `linienzug_tests.json`:
|
||||||
|
|
||||||
- Zusaetzlicher Eintragstyp `step` (Glied-Marker aus `vfl-journal-mark`,
|
- 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*`
|
harter Abbruch mit Fundstelle (`vfl-headless-abbruch` -> `*vfl-headless-fehler*`
|
||||||
nennt Art der Eingabe, Glied-Nummer und Eingabe-Nummer). Die Meldungen der
|
nennt Art der Eingabe, Glied-Nummer und Eingabe-Nummer). Die Meldungen der
|
||||||
geometrisch abgewiesenen Sektionen laufen ueber `vfl-meldung` statt `alert` und
|
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
|
schaltet beides ein; die Diagnose landet im Ergebnis-JSON
|
||||||
(`headless_fehler`, `meldungen`, `prompts`).
|
(`headless_fehler`, `meldungen`, `prompts`).
|
||||||
|
|
||||||
@@ -615,7 +615,7 @@ Vollstaendiges JSON:
|
|||||||
"endpunkt": [220499.01, 154926.53, 0.0]
|
"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
|
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.
|
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) |
|
| Omniflo TV_*/APBW_* | - | **nein** (nicht implementiert, DUMMY) |
|
||||||
| Separator/Scanner | `ils-insert-sensor` | **nein** (Schleife mit `getpoint`/`pause`) |
|
| 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
|
VarioFoerderer-Linienzuege** aus `data/polylines.dxf` (die Anlage HundM, ein
|
||||||
Kreisel mit fuenf daran haengenden Ketten). Quelle ist nicht die gezeichnete
|
Kreisel mit fuenf daran haengenden Ketten). Quelle ist nicht die gezeichnete
|
||||||
Geometrie, sondern die XDATA `SSG_VF_EDIT` (Marker `"linienzug"`) an den
|
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 \
|
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
|
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
|
`linienzug_tests.json`. Der Anlagetyp im Kopf heisst `anlagetyp` und NICHT
|
||||||
`typ`, damit Kopf und Eingabe unterscheidbar bleiben.
|
`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
|
`TEST_HUNDM05_2D`/`_3D`): der Treiber gruppiert die Objekte
|
||||||
(`hundm05:gruppiere`), baut daraus das Vorwaerts-Journal
|
(`hmrec:gruppiere`), baut daraus das Vorwaerts-Journal
|
||||||
(`hundm05:eintrag->journal`) und spielt es je Kette ueber
|
(`hmrec:eintrag->journal`) und spielt es je Kette ueber
|
||||||
`vfl-journal-replay-start` + `vf-linienzug-modus` ab — denselben Weg nimmt die
|
`vfl-journal-replay-start` + `vf-linienzug-modus` ab — denselben Weg nimmt die
|
||||||
Produktion beim Editieren/Konvertieren (`vfl-konvertiere-ent`). Bewusst KEINE
|
Produktion beim Editieren/Konvertieren (`vfl-konvertiere-ent`). Bewusst KEINE
|
||||||
Eingabe-Mocks wie in `test_linienzug.lsp`: ein Mock muesste zu jedem Segment
|
Eingabe-Mocks wie in `test_linienzug.lsp`: ein Mock muesste zu jedem Segment
|
||||||
|
|||||||
Vendored
-418
@@ -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"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
Reference in New Issue
Block a user