diff --git a/Lisp/export.lsp b/Lisp/export.lsp index 8f55ae6..21d7af3 100644 --- a/Lisp/export.lsp +++ b/Lisp/export.lsp @@ -68,6 +68,115 @@ ) +;; ============================================================ +;; PYTHON-INTERPRETER ERMITTELN +;; ============================================================ +;; BricsCAD erbt seine Umgebung von start_briscad.bat/setenv.bat - dort wird +;; das Projekt-venv NICHT aktiviert. Ein blankes "python" trifft unter Windows +;; 11 daher den WindowsApps-Platzhalter +;; (%LOCALAPPDATA%\Microsoft\WindowsApps\python.exe), der kein Interpreter ist, +;; sondern nur auf den Microsoft Store verweist und mit Exitcode 49 abbricht. +;; Der Export schrieb dann zwar die JSON, aber nie eine CSV. +;; +;; Aufloesungsreihenfolge (erster Treffer gewinnt): +;; 1. cfg/export.cfg [python] interpreter= (explizite Vorgabe) +;; 2. Umgebungsvariable DXFM_PYTHON (von setenv.bat gesetzt) +;; 3. /.venv/Scripts/python.exe (Projekt-venv, siehe install_py.bat) +;; 4. "py" (Python-Launcher, C:\Windows\py.exe) - kein Store-Platzhalter +;; 5. "python" (PATH) als letzter Ausweg +(defun export:python-store-stub-p (pfad / p) + (setq p (strcase (vl-string-translate "\\" "/" pfad))) + (and (vl-string-search "/WINDOWSAPPS/" p) T) +) + +;; --- Kandidat pruefen: existierende Datei und kein Store-Platzhalter --- +(defun export:python-kandidat-ok-p (pfad) + (and pfad + (/= pfad "") + (findfile pfad) + (not (export:python-store-stub-p pfad))) +) + +(defun export:python-exe ( / cfg-wert env-wert venv-pfad launcher-pfad) + (cond + ;; 1. Explizite Vorgabe aus der Config + ((export:python-kandidat-ok-p + (setq cfg-wert (export:cfg "python" "interpreter" ""))) + cfg-wert) + ;; 2. Umgebungsvariable + ((export:python-kandidat-ok-p (setq env-wert (getenv "DXFM_PYTHON"))) + env-wert) + ;; 3. Projekt-venv + ((export:python-kandidat-ok-p + (setq venv-pfad (strcat (cond ((getenv "DXFMAKRO")) (".")) + "/.venv/Scripts/python.exe"))) + venv-pfad) + ;; 4. Python-Launcher (liegt in C:\Windows, daher kein Store-Platzhalter). + ;; Voller Pfad statt nur "py.exe": findfile durchsucht die BricsCAD- + ;; Suchpfade, nicht zwangslaeufig den System-PATH. + ((export:python-kandidat-ok-p + (setq launcher-pfad (strcat (cond ((getenv "SystemRoot")) ("C:/Windows")) + "/py.exe"))) + launcher-pfad) + ;; 5. Letzter Ausweg - kann der Store-Platzhalter sein, deshalb wird der + ;; Exitcode nach dem Aufruf geprueft (siehe export:run-python). + (t "python") + ) +) + + +;; --- Python-Skript synchron ausfuehren, Ausgabe in Logdatei --- +;; Ersetzt das frueherer "fire and forget" (startapp "cmd" "/c ..."): das +;; cmd-Fenster schloss sich sofort wieder, jede Python-Fehlermeldung (fehlender +;; Interpreter, Traceback) war unsichtbar und der Befehl meldete trotzdem +;; "Export gestartet". Jetzt wird ueber WScript.Shell.Run mit bWaitOnReturn=T +;; gewartet, stdout/stderr nach DXFM_LOG/export_python.log umgeleitet und der +;; Exitcode ausgewertet. +;; Rueckgabe: Exitcode (0 = ok) oder nil, wenn der Aufruf selbst scheiterte. +(defun export:run-python (py-exe py-skript args log-pfad / cmd sh rc code) + ;; Aeussere Anfuehrungszeichen um die gesamte /c-Zeile: sonst verschluckt + ;; cmd.exe die Quotes um den Interpreterpfad (Leerzeichen in "Program Files"). + (setq cmd (strcat "cmd.exe /c \"\"" py-exe "\"")) + (setq cmd (strcat cmd " \"" py-skript "\"")) + (foreach a args (setq cmd (strcat cmd " \"" a "\""))) + (setq cmd (strcat cmd " > \"" log-pfad "\" 2>&1\"")) + ;; Run(cmd, 0, T): 0 = kein Fenster, T = warten und Exitcode liefern. + (setq rc + (vl-catch-all-apply + '(lambda () + (setq sh (vlax-create-object "WScript.Shell")) + (setq code (vlax-invoke sh 'Run cmd 0 :vlax-true)) + (vlax-release-object sh) + (setq sh nil) + code))) + (if (vl-catch-all-error-p rc) + (progn + (if sh (vl-catch-all-apply 'vlax-release-object (list sh))) + nil) + rc + ) +) + + +;; --- Erste Zeilen der Python-Logdatei in die Kommandozeile spiegeln --- +;; Damit steht die eigentliche Fehlerursache direkt in BricsCAD und nicht nur +;; in der Logdatei. max-zeilen begrenzt die Ausgabe (Tracebacks koennen lang sein). +(defun export:log-ausgeben (log-pfad max-zeilen / fh zeile n) + (if (setq fh (open log-pfad "r")) + (progn + (setq n 0) + (while (and (setq zeile (read-line fh)) (< n max-zeilen)) + (if (/= zeile "") + (progn (princ (strcat "\n | " zeile)) (setq n (1+ n))) + ) + ) + (close fh) + ) + ) + (princ) +) + + ;; --- String fuer JSON escapen (Anfuehrungszeichen/Backslash) --- ;; Konvertiert nicht-String-Werte (z.B. T, nil, Zahlen) defensiv in String. (defun csv:json-escape (s) @@ -458,7 +567,8 @@ ;; 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 cmd first-block count out-dir) + py-skript ergebnis-pfad py-exe log-pfad rc first-block + count out-dir) ;; Vor dem Export: IDs aller Bloecke sicherstellen und Duplikate korrigieren (princ (ssg-textf "exp-check-ids" (list label))) (ssg-id-check-all) @@ -521,13 +631,36 @@ (if (findfile py-skript) (progn (setq ergebnis-pfad (strcat out-dir "/" csv-name)) - (setq cmd (strcat "python \"" py-skript "\"" - " \"" out-pfad "\"" - " \"" (getenv "DXFM_DATA") "\"" - " \"" ergebnis-pfad "\"")) + (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))) - (startapp "cmd" (strcat "/c " cmd)) - (princ (ssg-textf "exp-export-started" (list label ergebnis-pfad))) + ;; 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))) ) diff --git a/bin/setenv.bat b/bin/setenv.bat index 15405f0..885ac65 100644 --- a/bin/setenv.bat +++ b/bin/setenv.bat @@ -29,6 +29,23 @@ set "DXFM_RESULTS=%DXFMAKRO%\results" REM 2D/3D-Modus: Standard ist 3D (alle Module nutzen data\ils\3D\) set "DXFM_DIM=3D" +REM Python-Interpreter fuer die Export-Skripte (EXPORTCSV/EXPORTSIVAS). +REM WICHTIG: BricsCAD erbt diese Umgebung, aktiviert aber kein venv. Ein blankes +REM "python" trifft unter Windows 11 den WindowsApps-Platzhalter (kein echter +REM Interpreter, bricht mit Exitcode 49 ab) - deshalb hier explizit setzen. +set "DXFM_PYTHON=" +if exist "%DXFMAKRO%\.venv\Scripts\python.exe" ( + set "DXFM_PYTHON=%DXFMAKRO%\.venv\Scripts\python.exe" +) else ( + for /f "delims=" %%P in ('py -c "import sys;print(sys.executable)" 2^>nul') do ( + if not defined DXFM_PYTHON set "DXFM_PYTHON=%%P" + ) +) +if not defined DXFM_PYTHON ( + echo HINWEIS: Kein Python gefunden - bin/install_py.bat ausfuehren, sonst + echo liefern EXPORTCSV/EXPORTSIVAS keine CSV-Datei. +) + set BRICSCAD="C:\Program Files\Bricsys\BricsCAD V25 de_DE\bricscad.exe" if exist "C:\Program Files\Bricsys\BricsCAD V25 de_DE\bricscad.exe" ( @@ -110,6 +127,7 @@ echo DXFM_DIM = %DXFM_DIM% echo DXFM_BLOCKS = %DXFM_BLOCKS% echo DXFM_OMNIFLO = %DXFM_OMNIFLO% echo DXFM_DIFFTOOL = %DXFM_DIFFTOOL% +echo DXFM_PYTHON = %DXFM_PYTHON% echo ================================================================ echo. diff --git a/cfg/export.cfg b/cfg/export.cfg index fb8e7c0..435c0f2 100644 --- a/cfg/export.cfg +++ b/cfg/export.cfg @@ -42,6 +42,16 @@ pattern_separator= Separator_SP*, S-LP* pattern_scanner= Scanner* pattern_ks_subblocks= KS_EIN, KS_AUS, K1, K2, K3, K4 +[python] +# Interpreter fuer die Export-Skripte (lib/export_csv.py, lib/export_sivas.py). +# Leer lassen = automatisch: DXFM_PYTHON (aus bin/setenv.bat) -> Projekt-venv +# /.venv/Scripts/python.exe -> py.exe -> python (PATH). +# Nur setzen, wenn ein bestimmter Interpreter erzwungen werden muss. +# Wichtig: NICHT auf %LOCALAPPDATA%\Microsoft\WindowsApps\python.exe zeigen - +# das ist nur der Microsoft-Store-Platzhalter (bricht mit Exitcode 49 ab und +# der Export erzeugt dann keine CSV). Solche Pfade werden ignoriert. +interpreter= + [Planquadrate] section=Planquadrate_AN diff --git a/lang/de_DE.json b/lang/de_DE.json index 51967d6..59c80f4 100644 --- a/lang/de_DE.json +++ b/lang/de_DE.json @@ -500,6 +500,12 @@ "exp-calling-python": "\n[%1] Rufe Python auf...", "exp-export-started": "\n[%1] Export gestartet: %2", "exp-python-not-found": "\n[%1] FEHLER: Python-Skript nicht gefunden: %2", + "exp-python-exe": "\n[%1] Interpreter: %2", + "exp-export-done": "\n[%1] Export fertig: %2", + "exp-python-call-failed": "\n[%1] FEHLER: Python konnte nicht gestartet werden (WScript.Shell nicht verfuegbar).", + "exp-python-error": "\n[%1] FEHLER: Python endete mit Exitcode %2. Log: %3", + "exp-python-store-hint": "\n HINWEIS: Es wurde kein echter Python-Interpreter gefunden ('python' zeigt\n unter Windows 11 auf den WindowsApps-Platzhalter). Loesung: bin/install_py.bat\n ausfuehren (legt .venv an) und BricsCAD ueber bin/start_briscad.bat neu\n starten, oder in cfg/export.cfg unter [python] interpreter= den vollen Pfad\n zur python.exe setzen.", + "exp-csv-missing": "\n[%1] FEHLER: Python meldete Erfolg, aber die CSV fehlt: %2. Log: %3", "exp-omni-no-inserts": "\n[OMNI_UPDATE] Keine INSERT-Bloecke in der Zeichnung.", "exp-omni-updated": "\n[OMNI_UPDATE] %1 Elemente aktualisiert (HOEHE, DREHUNG).", "id-new-assigned": "\n[ID] Neue ID zugewiesen: %1", diff --git a/lang/en_GB.json b/lang/en_GB.json index e0306d8..ad41f24 100644 --- a/lang/en_GB.json +++ b/lang/en_GB.json @@ -500,6 +500,12 @@ "exp-calling-python": "\n[%1] Calling Python...", "exp-export-started": "\n[%1] Export started: %2", "exp-python-not-found": "\n[%1] ERROR: Python script not found: %2", + "exp-python-exe": "\n[%1] Interpreter: %2", + "exp-export-done": "\n[%1] Export finished: %2", + "exp-python-call-failed": "\n[%1] ERROR: Could not start Python (WScript.Shell unavailable).", + "exp-python-error": "\n[%1] ERROR: Python exited with code %2. Log: %3", + "exp-python-store-hint": "\n NOTE: No real Python interpreter was found (on Windows 11 'python' points\n to the WindowsApps placeholder). Fix: run bin/install_py.bat (creates .venv)\n and restart BricsCAD via bin/start_briscad.bat, or set the full path to\n python.exe in cfg/export.cfg under [python] interpreter=.", + "exp-csv-missing": "\n[%1] ERROR: Python reported success but the CSV is missing: %2. Log: %3", "exp-omni-no-inserts": "\n[OMNI_UPDATE] No INSERT blocks in the drawing.", "exp-omni-updated": "\n[OMNI_UPDATE] %1 elements updated (HEIGHT, ROTATION).", "id-new-assigned": "\n[ID] New ID assigned: %1",