From 37fdedd996116ff45944c239910f52559c453943 Mon Sep 17 00:00:00 2001 From: Michael Stangl Date: Fri, 18 Sep 2026 14:51:42 +0200 Subject: [PATCH] VF-Linienzug AS/ES-Praeambel + csv:run-export entduplizieren vf_linienzug.lsp: die 5-6x wortgleich ausgeschriebene AS-/ES-Winkel+Seite- Praeambel (Winkel 30/90, Seite links/rechts, Masse setzen) in zwei parametrische Helfer ausgelagert (vfl-frage-as-winkel-seite, vfl-frage-es-winkel-seite - Seiten-Headerkey als Parameter, symmetrisch zum bestehenden vfl-frage-es-seite). Ersetzt in modus1-AS, modus2-AS, modus2-ES. modus3-AS und die QC-Nachfrage bleiben (kein echtes Duplikat, abweichende Prompt-/Wizard-Semantik). export.lsp: csv:run-export (181 Z.) in Phasen zerlegt - csv:export-vorbereiten (Proxies/IDs/Zuordnung/Attribut-Update/Sep-Scan/REGENALL/bbox-Reset) und csv:python-aufrufen (Python-Skript-Aufruf + Exitcode-Auswertung). Runner liest sich jetzt als klare Sequenz. Verhaltensneutrale Extraktionen; Klammerbalance unveraendert, Python-Testsuite ohne neue Fehler (16 failed baseline vor/nach identisch). Co-Authored-By: Claude Opus 4.8 (1M context) @ --- Lisp/export.lsp | 137 +++++++++++++++++++++++++----------------- Lisp/vf_linienzug.lsp | 105 +++++++++++++++++--------------- 2 files changed, 139 insertions(+), 103 deletions(-) diff --git a/Lisp/export.lsp b/Lisp/export.lsp index eed4dce..1bcc0e6 100644 --- a/Lisp/export.lsp +++ b/Lisp/export.lsp @@ -1222,16 +1222,18 @@ ;; --- Gemeinsame Export-Funktion --- ;; Sammelt relevante Bloecke, schreibt JSON, ruft Python auf. ;; label = Anzeigename (z.B. "EXPORTCSV") -;; py-name = Python-Skript-Name (z.B. "export_csv.py") -;; csv-name = Ergebnis-CSV-Name (z.B. "export.csv") -;; include-bbox = T -> Bounding-Box je Block ermitteln und mit exportieren -;; (siehe csv:block-to-json); fuer EXPORTSIVAS nil. -(defun csv:run-export (label py-name csv-name include-bbox / ss i ename fh out-pfad - py-skript ergebnis-pfad py-exe log-pfad rc first-block - count out-dir sep-proxies) +;; --- Export-Vorbereitung: Zeichnungszustand fuer den Export herrichten --- +;; Aus csv:run-export ausgelagert: alle Schritte, die VOR dem Sammeln/Schreiben +;; den Zeichnungszustand herrichten - Separator-Proxies einpacken, IDs +;; sicherstellen, Proxy-Zuordnung setzen, Omniflo-Attribute (HOEHE/DREHUNG) +;; aktualisieren, Separator-/Scanner-Zuordnung pruefen, global regenerieren und +;; die bbox-Fehlerdiagnose zuruecksetzen. Rueckgabe: die Liste der erzeugten +;; Separator-Proxies (der Aufrufer entfernt sie am Ende wieder). label dient +;; nur den Meldungen. +(defun csv:export-vorbereiten (label / sep-proxies) ;; Verpackte Separator_SP-Symbole (siehe csv:sep-proxies-erzeugen) VOR der ;; ID-Vergabe durch temporaere, echte Kopien ersetzen - sie werden am Ende - ;; dieser Funktion wieder entfernt (csv:sep-proxies-loeschen). + ;; von csv:run-export wieder entfernt (csv:sep-proxies-loeschen). ;; ;; HINWEIS: csv:sepliste-proxies-erzeugen (echte Kopien fuer die nur noch in ;; der sepliste-XDATA gefuehrten SEP/AS/ES-Eintraege der VF_n/GF_n-Wrapper) @@ -1291,6 +1293,76 @@ ;; bbox-Fehlerdiagnose fuer diesen Lauf zuruecksetzen (siehe csv:get-bbox). (setq *csv-bbox-fail-count* 0) (setq *csv-bbox-fail-first* nil) + sep-proxies) + +;; --- Python-Export-Skript aufrufen und Ergebnis auswerten --------------- +;; Aus csv:run-export ausgelagert: das fertige raw-JSON (out-pfad) an das +;; Python-Skript py-name uebergeben, synchron ausfuehren und den Exitcode +;; auswerten (kein stiller Fehlschlag). Arbeitet ausschliesslich ueber die +;; uebergebenen Pfade/Namen und die Umgebung (DXFM_LIB/DXFM_DATA/DXFM_LOG) - +;; keine Zeichnungs-/Sammel-Logik mehr, daher gut fuer sich testbar. +;; label/out-dir dienen nur den Meldungen bzw. dem Ergebnis-Pfad. +(defun csv:python-aufrufen (label out-dir out-pfad py-name csv-name + / py-skript ergebnis-pfad py-exe log-pfad rc) + ;; Python-Skript aufrufen. DXFM_LIB muss gesetzt sein - fehlt es + ;; (z.B. weil BricsCAD nicht ueber bin/start_briscad.bat gestartet + ;; wurde und die Umgebungsvariablen daher nie in diesen Prozess + ;; vererbt wurden), wuerde das folgende strcat sonst mit einem + ;; kryptischen Lisp-Fehler ("bad argument type ") abbrechen. + (if (null (getenv "DXFM_LIB")) + (princ (ssg-textf "exp-dxfm-lib-missing" (list label))) + (progn + (setq py-skript (strcat (getenv "DXFM_LIB") "/" py-name)) + (if (findfile py-skript) + (progn + (setq ergebnis-pfad (strcat out-dir "/" (export:dwg-praefix) csv-name)) + (setq py-exe (export:python-exe)) + (setq log-pfad (strcat (cond ((getenv "DXFM_LOG")) (out-dir)) + "/export_python.log")) + (princ (ssg-textf "exp-python-exe" (list label py-exe))) + (princ (ssg-textf "exp-calling-python" (list label))) + ;; Synchron ausfuehren und Exitcode auswerten - ein stiller + ;; Fehlschlag (z.B. WindowsApps-Platzhalter statt Interpreter) + ;; darf nicht mehr als "Export gestartet" durchgehen. + (setq rc (export:run-python py-exe py-skript + (list out-pfad (getenv "DXFM_DATA") ergebnis-pfad) + log-pfad)) + (cond + ((null rc) + (princ (ssg-textf "exp-python-call-failed" (list label)))) + ((/= rc 0) + (princ (ssg-textf "exp-python-error" (list label rc log-pfad))) + (export:log-ausgeben log-pfad 12) + ;; Exitcode 49 (Store-Platzhalter direkt) bzw. 9009 (cmd.exe: + ;; "Befehl nicht gefunden") = kein brauchbarer Interpreter. + ;; Gezielter Hinweis, wie er korrekt gesetzt wird. + (if (or (= rc 49) (= rc 9009) + (export:python-store-stub-p py-exe)) + (princ (ssg-text "exp-python-store-hint")) + )) + ((not (findfile ergebnis-pfad)) + (princ (ssg-textf "exp-csv-missing" (list label ergebnis-pfad log-pfad))) + (export:log-ausgeben log-pfad 12)) + (t + (princ (ssg-textf "exp-export-done" (list label ergebnis-pfad)))) + ) + ) + (princ (ssg-textf "exp-python-not-found" (list label py-skript))) + ) + ) + ) +) + +;; py-name = Python-Skript-Name (z.B. "export_csv.py") +;; csv-name = Ergebnis-CSV-Name (z.B. "export.csv") +;; include-bbox = T -> Bounding-Box je Block ermitteln und mit exportieren +;; (siehe csv:block-to-json); fuer EXPORTSIVAS nil. +(defun csv:run-export (label py-name csv-name include-bbox / ss i ename fh out-pfad + first-block count out-dir sep-proxies) + ;; Zeichnungszustand fuer den Export herrichten (Proxies, IDs, Zuordnung, + ;; Attribut-Update, Sep/Scan-Zaehlung, REGENALL, bbox-Reset) - ausgelagert. + ;; Rueckgabe: die erzeugten Separator-Proxies (unten wieder zu entfernen). + (setq sep-proxies (csv:export-vorbereiten label)) (princ (ssg-textf "exp-collect-blocks" (list label))) (setq ss (csv:collect-export-blocks)) @@ -1350,53 +1422,8 @@ (list (itoa *csv-bbox-fail-count*) (cond (*csv-bbox-fail-first*) ("?")))))) - ;; Python-Skript aufrufen. DXFM_LIB muss gesetzt sein - fehlt es - ;; (z.B. weil BricsCAD nicht ueber bin/start_briscad.bat gestartet - ;; wurde und die Umgebungsvariablen daher nie in diesen Prozess - ;; vererbt wurden), wuerde das folgende strcat sonst mit einem - ;; kryptischen Lisp-Fehler ("bad argument type ") abbrechen. - (if (null (getenv "DXFM_LIB")) - (princ (ssg-textf "exp-dxfm-lib-missing" (list label))) - (progn - (setq py-skript (strcat (getenv "DXFM_LIB") "/" py-name)) - (if (findfile py-skript) - (progn - (setq ergebnis-pfad (strcat out-dir "/" (export:dwg-praefix) csv-name)) - (setq py-exe (export:python-exe)) - (setq log-pfad (strcat (cond ((getenv "DXFM_LOG")) (out-dir)) - "/export_python.log")) - (princ (ssg-textf "exp-python-exe" (list label py-exe))) - (princ (ssg-textf "exp-calling-python" (list label))) - ;; Synchron ausfuehren und Exitcode auswerten - ein stiller - ;; Fehlschlag (z.B. WindowsApps-Platzhalter statt Interpreter) - ;; darf nicht mehr als "Export gestartet" durchgehen. - (setq rc (export:run-python py-exe py-skript - (list out-pfad (getenv "DXFM_DATA") ergebnis-pfad) - log-pfad)) - (cond - ((null rc) - (princ (ssg-textf "exp-python-call-failed" (list label)))) - ((/= rc 0) - (princ (ssg-textf "exp-python-error" (list label rc log-pfad))) - (export:log-ausgeben log-pfad 12) - ;; Exitcode 49 (Store-Platzhalter direkt) bzw. 9009 (cmd.exe: - ;; "Befehl nicht gefunden") = kein brauchbarer Interpreter. - ;; Gezielter Hinweis, wie er korrekt gesetzt wird. - (if (or (= rc 49) (= rc 9009) - (export:python-store-stub-p py-exe)) - (princ (ssg-text "exp-python-store-hint")) - )) - ((not (findfile ergebnis-pfad)) - (princ (ssg-textf "exp-csv-missing" (list label ergebnis-pfad log-pfad))) - (export:log-ausgeben log-pfad 12)) - (t - (princ (ssg-textf "exp-export-done" (list label ergebnis-pfad)))) - ) - ) - (princ (ssg-textf "exp-python-not-found" (list label py-skript))) - ) - ) - ) + ;; Python-Export-Skript aufrufen (ausgelagert, siehe oben). + (csv:python-aufrufen label out-dir out-pfad py-name csv-name) ) ) ) diff --git a/Lisp/vf_linienzug.lsp b/Lisp/vf_linienzug.lsp index 525b97c..4846534 100644 --- a/Lisp/vf_linienzug.lsp +++ b/Lisp/vf_linienzug.lsp @@ -5925,6 +5925,60 @@ (vf-set-es-masse winkel seite) seite) +;; AS-Winkel (30/90) + Seite abfragen. Gemeinsame Schale fuer die frueher +;; wortgleich in vf-linienzug-modus/-modus2/-modus3 UND vfl-qc-linie-mit-gf +;; ausgeschriebene AS-Praeambel (Winkel-Frage 30/90, Seiten-Frage links/rechts, +;; vf-set-as-masse) - Gegenstueck zu vfl-frage-es-seite. Setzt *vfl-as-winkel* +;; und die AS-Masse (aus-dx/dz) fuer die Variante. Rueckgabe: Seite (links/rechts). +;; +;; seiten-headerkey ist der EINZIGE Unterschied zwischen den Aufrufstellen +;; (modus1: "vfl-aus-seite-header", modus2: "gf-seite-aus-header") und daher +;; Parameter. Die je Modus unterschiedliche VORANGEHENDE Wizard-Vorbereitung +;; (vflw-gruppe-as-impl "AS setzen?" bzw. vflw-gruppe-winkel-seite-impl) bleibt +;; bewusst an der Aufrufstelle - die abzudeckende Winkel/Seite-Frage selbst ist +;; ueberall identisch. vf-frage-element-winkel/vflw-wahl zeigen den Wizard- +;; Winkeldialog bei aktivem Wizard-Modus schon selbst. +(defun vfl-frage-as-winkel-seite (seiten-headerkey / antwort as-seite) + (princ (ssg-text "vf-winkel-aus-header")) + (princ (ssg-text "vf-winkel-90")) + (princ (ssg-text "vf-winkel-30")) + (setq *vfl-as-winkel* + (vfl-in-value "STR" + (function (lambda () (vf-frage-element-winkel "vf-winkel-aus-header"))))) ; 30/90 vor Seite + (princ (ssg-text seiten-headerkey)) + (princ (ssg-text "gf-seite-links")) + (princ (ssg-text "gf-seite-rechts")) + (setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2") + (list (ssg-text "gf-seite-links") (ssg-text "gf-seite-rechts")) 1 + seiten-headerkey)) + (setq as-seite (if (= antwort "2") "rechts" "links")) + (vf-set-as-masse *vfl-as-winkel* as-seite) ; Masse fuer Variante + as-seite) + +;; ES-Winkel (30/90) + Seite abfragen - MARKER-FREIE Schale fuer die Modi 2/3, +;; die das ES-Element als eigene Praeambel (Start/Ziel bereits fest) abfragen +;; und ihr eigenes Journal-/Masse-Schema fuehren. Unterscheidet sich bewusst +;; von vfl-frage-es-seite (Modus 1), das zusaetzlich einen Journal-Marker "ES" +;; setzt und ueber vfl-es-masse-setzen laeuft - das ist Teil des Modus-1- +;; Kettenglied-Replays und darf hier NICHT feuern. Setzt *vfl-es-winkel* und +;; die ES-Masse (ein-dx/dz). Rueckgabe: Seite (links/rechts). +(defun vfl-frage-es-winkel-seite (seiten-headerkey / antwort es-seite) + (princ (ssg-text "vf-winkel-ein-header")) + (princ (ssg-text "vf-winkel-90")) + (princ (ssg-text "vf-winkel-30")) + (setq *vfl-es-winkel* + (vfl-in-value "STR" + (function (lambda () (vf-frage-element-winkel "vf-winkel-ein-header"))))) ; 30/90 vor Seite + (princ (ssg-text seiten-headerkey)) + (princ (ssg-text "gf-seite-links")) + (princ (ssg-text "gf-seite-rechts")) + (setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2") + (list (ssg-text "gf-seite-links") (ssg-text "gf-seite-rechts")) 1 + seiten-headerkey)) + (setq es-seite (if (= antwort "2") "rechts" "links")) + (vf-set-es-masse *vfl-es-winkel* es-seite) ; Masse fuer Variante + es-seite) + ;; dialog-schon-gezeigt: T, wenn Winkel+Seite BEREITS ueber einen vorherigen ;; kombinierten Dialog (vflw-gruppe-as-impl, "ES-Element setzen?" + Winkel + ;; Seite in einem Bildschirm) abgefragt und in die Pending-Queue gelegt @@ -6372,22 +6426,7 @@ ;; tragen, wenn diesmal kein AS gewaehlt wird. (setq *vfl-as-winkel* nil *vfl-es-winkel* nil) (if as-vorhanden - (progn - (princ (ssg-text "vf-winkel-aus-header")) - (princ (ssg-text "vf-winkel-90")) - (princ (ssg-text "vf-winkel-30")) - (setq *vfl-as-winkel* - (vfl-in-value "STR" - (function (lambda () (vf-frage-element-winkel "vf-winkel-aus-header"))))) ; 30/90 vor Seite - (princ (ssg-text "vfl-aus-seite-header")) - (princ (ssg-text "gf-seite-links")) - (princ (ssg-text "gf-seite-rechts")) - (setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2") - (list (ssg-text "gf-seite-links") (ssg-text "gf-seite-rechts")) 1 - "vfl-aus-seite-header")) - (setq as-seite (if (= antwort "2") "rechts" "links")) - (vf-set-as-masse *vfl-as-winkel* as-seite) ; Masse fuer Variante - ) + (setq as-seite (vfl-frage-as-winkel-seite "vfl-aus-seite-header")) ) ;; ES-Seite wird erst am Kettenende abgefragt (siehe vfl-frage-es-seite), ;; da das ES-Element erst dort eingefuegt wird. @@ -9231,22 +9270,7 @@ ;; Globals pro Lauf zuruecksetzen (siehe Kommentar in vf-linienzug-modus). (setq *vfl-as-winkel* nil *vfl-es-winkel* nil) (if as-vorhanden - (progn - (princ (ssg-text "vf-winkel-aus-header")) - (princ (ssg-text "vf-winkel-90")) - (princ (ssg-text "vf-winkel-30")) - (setq *vfl-as-winkel* - (vfl-in-value "STR" - (function (lambda () (vf-frage-element-winkel "vf-winkel-aus-header"))))) ; 30/90 vor Seite - (princ (ssg-text "gf-seite-aus-header")) - (princ (ssg-text "gf-seite-links")) - (princ (ssg-text "gf-seite-rechts")) - (setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2") - (list (ssg-text "gf-seite-links") (ssg-text "gf-seite-rechts")) 1 - "gf-seite-aus-header")) - (setq as-seite (if (= antwort "2") "rechts" "links")) - (vf-set-as-masse *vfl-as-winkel* as-seite) ; Masse fuer Variante - ) + (setq as-seite (vfl-frage-as-winkel-seite "gf-seite-aus-header")) ) ;; --- 3. Endpunkt + Z + ES-Seite (NEU in Modus 3) --- @@ -9269,22 +9293,7 @@ "vfl-es-setzen-frage")) (setq es-vorhanden (/= antwort "2")) (if es-vorhanden - (progn - (princ (ssg-text "vf-winkel-ein-header")) - (princ (ssg-text "vf-winkel-90")) - (princ (ssg-text "vf-winkel-30")) - (setq *vfl-es-winkel* - (vfl-in-value "STR" - (function (lambda () (vf-frage-element-winkel "vf-winkel-ein-header"))))) ; 30/90 vor Seite - (princ (ssg-text "gf-seite-ein-header")) - (princ (ssg-text "gf-seite-links")) - (princ (ssg-text "gf-seite-rechts")) - (setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2") - (list (ssg-text "gf-seite-links") (ssg-text "gf-seite-rechts")) 1 - "gf-seite-ein-header")) - (setq es-seite (if (= antwort "2") "rechts" "links")) - (vf-set-es-masse *vfl-es-winkel* es-seite) ; Masse fuer Variante - ) + (setq es-seite (vfl-frage-es-winkel-seite "gf-seite-ein-header")) ) ;; --- 4. Sortieren + Analysieren + Eck-Vorpruefung ---