From 27ab8b4875f9fa9451ccada3ff9f88f6e96da59d Mon Sep 17 00:00:00 2001 From: Michael Stangl Date: Mon, 20 Jul 2026 15:12:52 +0200 Subject: [PATCH 1/3] altes Symbol rausgeworfen --- data/dxf/834372115_symbol.dwg | Bin 42737 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 data/dxf/834372115_symbol.dwg diff --git a/data/dxf/834372115_symbol.dwg b/data/dxf/834372115_symbol.dwg deleted file mode 100644 index 2f563ddb6ebaf31a3b627ed8ec3ec596eca24d5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 42737 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 F{|i)%4XpqG From 5c28db9e913c656bf19743df016769566d3a3f77 Mon Sep 17 00:00:00 2001 From: Michael Stangl Date: Mon, 20 Jul 2026 15:39:17 +0200 Subject: [PATCH 2/3] =?UTF-8?q?Oberfl=C3=A4che=20f=C3=BCr=20Standard=20von?= =?UTF-8?q?=20Variof=C3=B6rder=20und=20Gef=C3=A4llestrecke=20bauen=20lasse?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 12 +- Lisp/Gefaellestrecke.lsp | 500 +++++++++++++++++----- Lisp/README.md | 57 ++- Lisp/SSG_LIB_Commands.lsp | 14 + Lisp/vf_core.lsp | 23 +- Lisp/vf_standard.lsp | 421 ++++++++++++++++++ dcl/gefaellestrecke.dcl | 82 ++++ dcl/variofoerderer.dcl | 99 +++++ lang/de_DE.json | 23 + lang/en_GB.json | 23 + menu/SSG_LIB.mnu | 2 + tests/test_gefaellestrecke.lsp | 105 ++++- tests/test_gefaellestrecke.py | 28 +- tests/testdata/gefaellestrecke_tests.json | 39 +- 14 files changed, 1285 insertions(+), 143 deletions(-) create mode 100644 dcl/gefaellestrecke.dcl create mode 100644 dcl/variofoerderer.dcl diff --git a/CLAUDE.md b/CLAUDE.md index 3491fbd..f4144f9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -67,12 +67,12 @@ Die `start_briscad.bat` ruft `setenv.bat` auf und startet BricsCAD. |---|---|---| | Kreisel-Module | `KreiselInsert` | KreiselInsert, KreiselConnect, KreiselEdit, ILS_Eckrad | | Zusatzmodul/Foerderer/Kurven | `SSG_LIB_Commands` | ILS_BTMT_*, ILS_ETF_*, ILS_K*_*, ILS_GK*_* | -| VarioFoerderer | `VarioFoerderer` | FOERDERANLAGE | -| Gefaellestrecke | `Gefaellestrecke` | GEFAELLESTRECKE | +| VarioFoerderer | `VarioFoerderer` | FOERDERANLAGE, VARIOFOERDERER_EDIT | +| Gefaellestrecke | `Gefaellestrecke` | GEFAELLESTRECKE, GEFAELLESTRECKE_EDIT | | Omniflo (komplett) | `OmniModulInsert` | OMNI_APB_*, OMNI_W*_*, OMNI_AP*, OMNI_TEF_*, OMNI_TV_*, OMNI_EDIT | | Export | `export` | EXPORTSIVAS, EXPORTCSV, CALLPYTHON | | Konfiguration | `SSG_LIB_Commands` | SSG_DIM_SWITCH, SSG_SPRACHE | -| Doppelklick/Kontextmenue | `SSG_LIB_Commands` | SSG_BLOCKEDIT | +| Doppelklick/Kontextmenue | `SSG_LIB_Commands` | SSG_BLOCKEDIT (dispatcht Kreisel->KreiselEdit, Omniflo->OMNI_EDIT, GF_*->GEFAELLESTRECKE_EDIT, VF_*->VARIOFOERDERER_EDIT) | ### Entfernte Legacy-Dateien @@ -92,8 +92,8 @@ Detaillierte Dokumentation aller Funktionen und Befehle: siehe `Lisp/README.md`. | `ssg_dialog.lsp` | - | DCL-Dialog-Vorlagen und Hilfsfunktionen | | `ssg_layer.lsp` | - | Layer-Verwaltung | | `KreiselInsert.lsp` | KreiselInsert, KreiselConnect, KreiselRedraw, KreiselQuick, KreiselEdit, KreiselParams, KreiselLabelSetup, KreiselLabelPos, KreiselLabelHoehe, ILS_Eckrad | ILS Kreisel und Eckrad komplett in AutoLISP | -| `VarioFoerderer.lsp` | FOERDERANLAGE | Vario-Foerderanlagen Generator | -| `Gefaellestrecke.lsp` | GEFAELLESTRECKE | Gefaelle-Foerderanlage (AUS -> Staustrecke skaliert -> Separator -> EIN) | +| `VarioFoerderer.lsp` (Wrapper fuer `vf_core.lsp`/`vf_standard.lsp`/`vf_etage.lsp`/`vf_linienzug.lsp`) | FOERDERANLAGE, VARIOFOERDERER_EDIT | Vario-Foerderanlagen Generator (Typen Standard/Etage/Linienzug); Standardfall + Werteingabe per DCL-Dialog (`vfs-standard-dialog-ablauf` in vf_standard.lsp); Doppelklick auf VF_n-Block ruft VARIOFOERDERER_EDIT (nur fuer per Dialog erstellte Standard-Bloecke, erkannt an XDATA) | +| `Gefaellestrecke.lsp` | GEFAELLESTRECKE, GEFAELLESTRECKE_EDIT | Gefaelle-Foerderanlage (AUS -> Staustrecke skaliert -> Separator -> EIN); Modus 1 per DCL-Dialog (Einfuegehoehe, Element-Winkel 30/90); Doppelklick auf GF_n-Block ruft GEFAELLESTRECKE_EDIT (Dialog vorbelegt, Neuaufbau) | | `export.lsp` | EXPORTSIVAS, EXPORTCSV, OMNI_UPDATE_ATTRIBS | JSON-Sammlung und Python-Export (Omniflo-Merkmale/Sum-Zeile via lib/export_csv.py) | | `OmniModulInsert.lsp` | OMNI_LOAD, OMNI_APB_*, OMNI_W*_*, OMNI_AP60/AP110, OMNI_TEF_*, OMNI_TV_*, OMNI_APBW_*, OMNI_EDIT | Omniflo-Komponenten: Boegen, Weichen, Verbinder, Transferwagen, Edit-Dialog | | `SSG_LIB_Commands.lsp` | ILS_BTMT_*, ILS_ETF_*, ILS_K*_*, ILS_GK*_*, SSG_BLOCKEDIT, SSG_DIM_SWITCH, SSG_SPRACHE | Dummy-Befehle fuer Menue + Doppelklick/Kontextmenue-Dispatcher + Konfiguration (2D/3D, Sprache) | @@ -104,6 +104,8 @@ Detaillierte Dokumentation aller Funktionen und Befehle: siehe `Lisp/README.md`. | Datei | Verwendet von | |---|---| | `kreisel_edit.dcl` | KreiselEdit (KreiselInsert.lsp) | +| `gefaellestrecke.dcl` | GEFAELLESTRECKE Modus 1 (Gefaellestrecke.lsp, `gf-dialog-eingabe`) | +| `variofoerderer.dcl` | FOERDERANLAGE Standardfall + Werteingabe (vf_standard.lsp, `vfs-dialog-eingabe-basis`/`vfs-dialog-winkel-verteilung`) | | `omniflo_boegen.dcl` | OMNI_APB_* (OmniModulInsert.lsp) | | `omniflo_weichen.dcl` | OMNI_W*_* (OmniModulInsert.lsp) | | `sprache.dcl` | SSG_SPRACHE (SSG_LIB_Commands.lsp) | diff --git a/Lisp/Gefaellestrecke.lsp b/Lisp/Gefaellestrecke.lsp index 4f1f5e5..d64514f 100644 --- a/Lisp/Gefaellestrecke.lsp +++ b/Lisp/Gefaellestrecke.lsp @@ -1611,6 +1611,12 @@ startpunkt last-ent nach-aus-pt (float hz) (float L_stau) es-seite)) + ;; Edit-Metadaten (Fahrtrichtung, Gefaellewinkel, Element-Winkel) als XDATA + ;; auf das GF_N-INSERT schreiben, damit GEFAELLESTRECKE_EDIT die Strecke + ;; verlustfrei nachbauen kann. Nicht Teil des Sivas-Attributsatzes. + (gf-xdata-schreiben gf-insert hz winkel as-winkel es-winkel + (caddr startpunkt) deltaL-total) + (princ "\n\n=========================================") (princ (ssg-text "gf-status-erfolgreich-eingefuegt")) (princ "\n=========================================") @@ -1620,14 +1626,246 @@ (list gf-insert gf-deltaH (float deltaL-total)) ) +;; ============================================================ +;; TEIL 5b: DCL-DIALOG (Modus 1 - parametrische Eingabe) +;; ============================================================ + +;; Parametrische Eingabe fuer Modus 1 / Edit ueber DCL-Dialog +;; (dcl/gefaellestrecke.dcl). Analog zum Kreisel-Dialog, ergaenzt +;; um die Angabe der Einfuegehoehe (Z). +;; prefill: Assoziationsliste zur Vorbelegung (fuer GEFAELLESTRECKE_EDIT) mit +;; Schluesseln "deltaL" "winkel" "hz" "as-seite" "es-seite" "einfuegehoehe" +;; "as-winkel" "es-winkel" - oder nil fuer Neuanlage (Modus 1) mit Defaults. +;; Rueckgabe: (deltaL winkel hz-winkel as-seite es-seite einfuegehoehe +;; as-winkel es-winkel) +;; oder nil bei Abbruch / fehlendem Dialog. +(defun gf-dialog-eingabe ( prefill / dcl-pfad dat ergebnis + dlg-deltal dlg-hoehe dlg-winkel + dlg-richtung dlg-aus dlg-ein + dlg-aus-winkel dlg-ein-winkel + hz-winkel as-seite es-seite as-winkel es-winkel) + (setq dcl-pfad (strcat (getenv "DXFM_DCL") "/gefaellestrecke.dcl")) + (setq dat (load_dialog dcl-pfad)) + (if (not (new_dialog "gefaellestrecke" dat)) + (progn + (if (>= dat 0) (unload_dialog dat)) + (alert (ssg-textf "gf-alert-dialog-fehlt" (list dcl-pfad))) + nil + ) + (progn + ;; Vorbelegung: aus prefill (Edit) oder Defaults (Neuanlage) + (set_tile "deltal" + (if prefill (rtos (cdr (assoc "deltaL" prefill)) 2 0) + (rtos (ssg-cfg-or "gefaelle" "default_delta_l" 5000.0) 2 0))) + (set_tile "einfuegehoehe" + (if prefill (rtos (cdr (assoc "einfuegehoehe" prefill)) 2 1) "0")) + (set_tile "winkel" + (if prefill (rtos (cdr (assoc "winkel" prefill)) 2 1) + (rtos (ssg-cfg-or "gefaelle" "default_winkel" 3.0) 2 1))) + + ;; Fahrtrichtung + (start_list "fahrtrichtung") + (add_list (ssg-textf "gf-dlg-richtung-0" (list grad-zeichen))) + (add_list (ssg-textf "gf-dlg-richtung-90" (list grad-zeichen))) + (add_list (ssg-textf "gf-dlg-richtung-180" (list grad-zeichen))) + (add_list (ssg-textf "gf-dlg-richtung-270" (list grad-zeichen))) + (end_list) + (set_tile "fahrtrichtung" + (if prefill + (cond ((equal (cdr (assoc "hz" prefill)) 90.0 0.1) "1") + ((equal (cdr (assoc "hz" prefill)) 180.0 0.1) "2") + ((equal (cdr (assoc "hz" prefill)) 270.0 0.1) "3") + (t "0")) + "0")) + + ;; AUS-Element: Winkel (90/30) + Seite + (start_list "aus_winkel") + (add_list "90") + (add_list "30") + (end_list) + (set_tile "aus_winkel" + (if (and prefill (equal (cdr (assoc "as-winkel" prefill)) "30")) "1" "0")) + (start_list "aus_seite") + (add_list (ssg-text "gf-dlg-links")) + (add_list (ssg-text "gf-dlg-rechts")) + (end_list) + (set_tile "aus_seite" + (if (and prefill (equal (cdr (assoc "as-seite" prefill)) "rechts")) "1" "0")) + + ;; EIN-Element: Winkel (90/30) + Seite + (start_list "ein_winkel") + (add_list "90") + (add_list "30") + (end_list) + (set_tile "ein_winkel" + (if (and prefill (equal (cdr (assoc "es-winkel" prefill)) "30")) "1" "0")) + (start_list "ein_seite") + (add_list (ssg-text "gf-dlg-links")) + (add_list (ssg-text "gf-dlg-rechts")) + (end_list) + (set_tile "ein_seite" + (if (and prefill (equal (cdr (assoc "es-seite" prefill)) "rechts")) "1" "0")) + + ;; Actions + (action_tile "accept" + (strcat + "(setq dlg-deltal (get_tile \"deltal\"))" + "(setq dlg-hoehe (get_tile \"einfuegehoehe\"))" + "(setq dlg-winkel (get_tile \"winkel\"))" + "(setq dlg-richtung (get_tile \"fahrtrichtung\"))" + "(setq dlg-aus-winkel (get_tile \"aus_winkel\"))" + "(setq dlg-aus (get_tile \"aus_seite\"))" + "(setq dlg-ein-winkel (get_tile \"ein_winkel\"))" + "(setq dlg-ein (get_tile \"ein_seite\"))" + "(done_dialog 1)" + ) + ) + (action_tile "cancel" "(done_dialog 0)") + + (setq ergebnis (start_dialog)) + (unload_dialog dat) + + (if (= ergebnis 1) + (progn + (setq hz-winkel + (cond ((= dlg-richtung "1") 90.0) + ((= dlg-richtung "2") 180.0) + ((= dlg-richtung "3") 270.0) + (t 0.0))) + (setq as-seite (if (= dlg-aus "1") "rechts" "links")) + (setq es-seite (if (= dlg-ein "1") "rechts" "links")) + (setq as-winkel (if (= dlg-aus-winkel "1") "30" "90")) + (setq es-winkel (if (= dlg-ein-winkel "1") "30" "90")) + (list + (atof dlg-deltal) + (atof dlg-winkel) + hz-winkel + as-seite + es-seite + (atof dlg-hoehe) + as-winkel + es-winkel) + ) + nil + ) + ) + ) +) + +;; Gemeinsamer Abschluss fuer Modus 1 + 2: Laengenberechnung, +;; Zusammenfassung, Bestaetigung und Einfuegen. +;; startpunkt: Einfuegepunkt AUS-Element (inkl. Z aus Einfuegehoehe) +;; as-winkel/es-winkel: "30" oder "90" (Element-Variante) +(defun gf-modus12-abschluss (deltaL winkel hz-winkel as-seite es-seite startpunkt + as-winkel es-winkel / + rad sep-x L_stau antwort) + (if (null as-winkel) (setq as-winkel "90")) + (if (null es-winkel) (setq es-winkel "90")) + (setq winkel (float winkel)) + (setq rad (* winkel (/ pi 180.0))) + (princ (ssg-textf "gf-status-winkel" (list (rtos winkel 2 1) grad-zeichen))) + + ;; AS-/ES-Masse fuer die gewaehlte Winkel-/Seiten-Variante setzen + ;; (aktualisiert aus-dx/ein-dx vor der Laengenberechnung) + (vf-set-as-masse as-winkel as-seite) + (vf-set-es-masse es-winkel es-seite) + + ;; Laengenberechnung + ;; deltaL = aus_dx + L_stau*cos(rad) + sep*cos(rad) + ein_dx + (setq sep-x (* (ssg-cfg-or "gefaelle" "separator_breite" 300.0) (cos rad))) + (setq L_stau (/ (- deltaL aus-dx ein-dx sep-x) (cos rad))) + + ;; Validierung + (if (<= L_stau 0.1) + (progn + (alert (ssg-textf "gf-alert-staustrecke-ungueltig" (list (rtos L_stau 2 2)))) + (exit) + ) + ) + + ;; Zusammenfassung + (princ "\n\n=========================================") + (princ (ssg-textf "gf-status-fahrtrichtung" (list (rtos hz-winkel 2 1) grad-zeichen))) + (princ (ssg-textf "gf-status-zus2-winkel" (list (rtos winkel 2 1) grad-zeichen))) + (princ (ssg-textf "gf-status-zus2-aus-seite" (list as-seite))) + (princ (ssg-textf "gf-status-zus2-ein-seite" (list es-seite))) + (princ (ssg-textf "gf-status-zus2-lstau" (list (rtos L_stau 2 2)))) + (princ "\n=========================================") + + ;; Bestaetigung + (setq antwort (getstring (ssg-text "gf-prompt-einfuegen"))) + (if (not (= antwort "2")) + (gefaellestrecke-einfuegen L_stau (fix winkel) startpunkt as-seite es-seite hz-winkel deltaL as-winkel es-winkel) + (princ (ssg-text "gf-status-abgebrochen")) + ) +) + +;; Nicht-interaktiver Neuaufbau (fuer GEFAELLESTRECKE_EDIT): Element-Masse +;; setzen, L_stau berechnen und einfuegen - ohne Zusammenfassung/Rueckfrage. +;; Rueckgabe: (ename deltaH deltaL) von gefaellestrecke-einfuegen oder nil. +(defun gf-rebuild (deltaL winkel hz-winkel as-seite es-seite startpunkt + as-winkel es-winkel / + rad sep-x L_stau) + (if (null as-winkel) (setq as-winkel "90")) + (if (null es-winkel) (setq es-winkel "90")) + (setq winkel (float winkel)) + (setq rad (* winkel (/ pi 180.0))) + (vf-set-as-masse as-winkel as-seite) + (vf-set-es-masse es-winkel es-seite) + (setq sep-x (* (ssg-cfg-or "gefaelle" "separator_breite" 300.0) (cos rad))) + (setq L_stau (/ (- deltaL aus-dx ein-dx sep-x) (cos rad))) + (if (<= L_stau 0.1) + (progn + (alert (ssg-textf "gf-alert-staustrecke-ungueltig" (list (rtos L_stau 2 2)))) + nil + ) + (gefaellestrecke-einfuegen + L_stau (fix winkel) startpunkt as-seite es-seite hz-winkel deltaL as-winkel es-winkel) + ) +) + +;; ============================================================ +;; XDATA-Metadaten fuer den Edit-Nachbau (App "SSG_GF_EDIT"). +;; Bewusst NICHT im Sivas-Attributsatz, damit der Export unveraendert bleibt. +;; ============================================================ +(if (null *gf-xdata-app*) (setq *gf-xdata-app* "SSG_GF_EDIT")) + +;; Fahrtrichtung/Winkel/Element-Varianten als XDATA auf das GF_N-INSERT schreiben. +(defun gf-xdata-schreiben (ent hz winkel as-winkel es-winkel z-start deltaL /) + (regapp *gf-xdata-app*) + (entmod + (append (entget ent) + (list + (list -3 + (list *gf-xdata-app* + (cons 1000 (rtos (float hz) 2 4)) + (cons 1000 (rtos (float winkel) 2 4)) + (cons 1000 (if as-winkel as-winkel "90")) + (cons 1000 (if es-winkel es-winkel "90")) + (cons 1000 (rtos (float z-start) 2 4)) + (cons 1000 (rtos (float deltaL) 2 4))))))) + ent +) + +;; XDATA lesen -> (hz-str winkel-str as-winkel es-winkel z-str deltaL-str) oder nil. +(defun gf-xdata-lesen (ent / xd app-data) + (setq xd (entget ent (list *gf-xdata-app*))) + (setq app-data (cdr (assoc -3 xd))) + (if app-data + (mapcar 'cdr (cdr (car app-data))) + nil + ) +) + ;; ============================================================ ;; TEIL 6: HAUPTBEFEHL ;; ============================================================ -(defun c:GEFAELLESTRECKE ( / antwort eingabe-modus deltaL winkel rad L_stau sep-x +(defun c:GEFAELLESTRECKE ( / antwort deltaL winkel as-seite es-seite gf-as-winkel gf-es-winkel line-points startpunkt endpunkt differenz - deltaX deltaY startpunkt-fuer-einfuegen hz-winkel) + deltaX deltaY startpunkt-fuer-einfuegen hz-winkel + dlg-werte) (princ "\n=========================================") (princ (ssg-text "gf-banner-titel")) @@ -1655,95 +1893,52 @@ (gf-linienzug-modus) ) - ;; Modus 1+2: einfache Gefaellestrecke - (t - (if (= antwort "2") - (setq eingabe-modus "Linie") - (setq eingabe-modus "Manuell") + ;; Modus 2: 3D-Linie (Konsoleneingabe) + ((= antwort "2") + (princ (ssg-text "gf-modus2-header")) + (command "BKS" "W") + (princ (ssg-text "gf-status-bks-welt")) + (setq line-points + (get-line-start-end-points + (ssg-text "gf-prompt-3dlinie-richtung"))) + (if (null line-points) + (progn (alert (ssg-text "gf-alert-keine-linie")) (exit)) ) - - ;; Modus 1: Manuelle Eingabe - (if (= eingabe-modus "Manuell") - (progn - (princ (ssg-text "gf-modus1-header")) - (setq startpunkt-fuer-einfuegen - (getpoint (ssg-text "gf-prompt-startpunkt-aus"))) - (if (null startpunkt-fuer-einfuegen) - (setq startpunkt-fuer-einfuegen '(0 0 0))) - (setq deltaL (getreal (ssg-textf "gf-prompt-deltal-abstand" (list delta-sym)))) - (if (or (null deltaL) (<= deltaL 0)) - (setq deltaL (ssg-cfg-or "gefaelle" "default_delta_l" 5000.0))) - ) - ;; Modus 2: 3D-Linie - (progn - (princ (ssg-text "gf-modus2-header")) - (command "BKS" "W") - (princ (ssg-text "gf-status-bks-welt")) - (setq line-points - (get-line-start-end-points - (ssg-text "gf-prompt-3dlinie-richtung"))) - (if (null line-points) - (progn (alert (ssg-text "gf-alert-keine-linie")) (exit)) - ) - (setq startpunkt (car line-points) - endpunkt (cadr line-points)) - (setq differenz (punkt-differenz startpunkt endpunkt)) - (setq deltaX (car differenz) - deltaY (cadr differenz)) - ;; deltaL: horizontale 2D-Projektion entlang Fahrtrichtung - (setq deltaL (sqrt (+ (* deltaX deltaX) (* deltaY deltaY)))) - ;; hz-Winkel: Fahrtrichtung aus Linienpunkten (in Grad, 0=Ost) - (setq hz-winkel (* (angle '(0.0 0.0) (list deltaX deltaY)) (/ 180.0 pi))) - (princ (ssg-textf "gf-status-fahrtrichtung-linie" (list (rtos hz-winkel 2 1) grad-zeichen))) - (princ (ssg-textf "gf-status-deltal-mm" (list delta-sym (rtos deltaL 2 2)))) - ;; Startpunkt separat waehlen (unabhaengig von Linie) - (setq startpunkt-fuer-einfuegen - (getpoint (ssg-text "gf-prompt-startpunkt-aus"))) - (if (null startpunkt-fuer-einfuegen) - (progn (princ (ssg-text "gf-fehler-kein-startpunkt")) (exit)) - ) - ) + (setq startpunkt (car line-points) + endpunkt (cadr line-points)) + (setq differenz (punkt-differenz startpunkt endpunkt)) + (setq deltaX (car differenz) + deltaY (cadr differenz)) + ;; deltaL: horizontale 2D-Projektion entlang Fahrtrichtung + (setq deltaL (sqrt (+ (* deltaX deltaX) (* deltaY deltaY)))) + ;; hz-Winkel: Fahrtrichtung aus Linienpunkten (in Grad, 0=Ost) + (setq hz-winkel (* (angle '(0.0 0.0) (list deltaX deltaY)) (/ 180.0 pi))) + (princ (ssg-textf "gf-status-fahrtrichtung-linie" (list (rtos hz-winkel 2 1) grad-zeichen))) + (princ (ssg-textf "gf-status-deltal-mm" (list delta-sym (rtos deltaL 2 2)))) + ;; Startpunkt separat waehlen (unabhaengig von Linie) + (setq startpunkt-fuer-einfuegen + (getpoint (ssg-text "gf-prompt-startpunkt-aus"))) + (if (null startpunkt-fuer-einfuegen) + (progn (princ (ssg-text "gf-fehler-kein-startpunkt")) (exit)) ) (princ "\n\n=========================================") (princ (ssg-textf "gf-status-deltal-mm" (list delta-sym (rtos deltaL 2 2)))) (princ "\n=========================================") - ;; Winkelabfrage (beide Modi: manuell) + ;; Winkelabfrage (setq winkel (getreal (ssg-text "gf-prompt-gefaellewinkel-def3"))) (if (or (null winkel) (<= winkel 0)) (setq winkel (ssg-cfg-or "gefaelle" "default_winkel" 3.0))) (setq winkel (float winkel)) - (setq rad (* winkel (/ pi 180.0))) - (princ (ssg-textf "gf-status-winkel" (list (rtos winkel 2 1) grad-zeichen))) - ;; Fahrtrichtung waehlen (nur Modus 1; Modus 2 berechnet hz aus Linie) - (if (= eingabe-modus "Manuell") - (progn - (princ (ssg-text "vfc-fahrtrichtung-header")) - (princ (ssg-textf "vfc-fahrtrichtung-0" (list grad-zeichen))) - (princ (ssg-textf "vfc-fahrtrichtung-90" (list grad-zeichen))) - (princ (ssg-textf "vfc-fahrtrichtung-180" (list grad-zeichen))) - (princ (ssg-textf "vfc-fahrtrichtung-270" (list grad-zeichen))) - (setq antwort (getstring (ssg-text "prompt-wahl-1-2-3-4"))) - (cond - ((= antwort "2") (setq hz-winkel 90.0)) - ((= antwort "3") (setq hz-winkel 180.0)) - ((= antwort "4") (setq hz-winkel 270.0)) - (t (setq hz-winkel 0.0)) - ) - (princ (ssg-textf "gf-status-fahrtrichtung" (list (rtos hz-winkel 2 1) grad-zeichen))) - ) - ) - - ;; AUS-Element: 30/90 (vor Seite) + Seite, dann Masse fuer Variante setzen + ;; AUS-Element: 30/90 + Seite (setq gf-as-winkel (vf-frage-element-winkel "vf-winkel-aus-header")) (princ (ssg-text "gf-seite-aus-header")) (princ (ssg-text "gf-seite-links")) (princ (ssg-text "gf-seite-rechts")) (setq antwort (getstring (ssg-text "prompt-wahl-1-2"))) (setq as-seite (if (= antwort "2") "rechts" "links")) - (vf-set-as-masse gf-as-winkel as-seite) ;; EIN-Element: 30/90 + Seite (setq gf-es-winkel (vf-frage-element-winkel "vf-winkel-ein-header")) @@ -1752,41 +1947,148 @@ (princ (ssg-text "gf-seite-rechts")) (setq antwort (getstring (ssg-text "prompt-wahl-1-2"))) (setq es-seite (if (= antwort "2") "rechts" "links")) - (vf-set-es-masse gf-es-winkel es-seite) - ;; Laengenberechnung (aus-dx/ein-dx jetzt fuer die gewaehlte Variante) - ;; deltaL = aus_dx + L_stau*cos(rad) + sep*cos(rad) + ein_dx - (setq sep-x (* (ssg-cfg-or "gefaelle" "separator_breite" 300.0) (cos rad))) - (setq L_stau (/ (- deltaL aus-dx ein-dx sep-x) (cos rad))) + ;; Gemeinsamer Abschluss (Masse, Laenge, Zusammenfassung, Einfuegen) + (gf-modus12-abschluss deltaL winkel hz-winkel as-seite es-seite + startpunkt-fuer-einfuegen gf-as-winkel gf-es-winkel) + ) - ;; Validierung - (if (<= L_stau 0.1) - (progn - (alert (ssg-textf "gf-alert-staustrecke-ungueltig" (list (rtos L_stau 2 2)))) - (exit) - ) - ) - - ;; Zusammenfassung - (princ "\n\n=========================================") - (princ (ssg-textf "gf-status-fahrtrichtung" (list (rtos hz-winkel 2 1) grad-zeichen))) - (princ (ssg-textf "gf-status-zus2-winkel" (list (rtos winkel 2 1) grad-zeichen))) - (princ (ssg-textf "gf-status-zus2-aus-seite" (list as-seite))) - (princ (ssg-textf "gf-status-zus2-ein-seite" (list es-seite))) - (princ (ssg-textf "gf-status-zus2-lstau" (list (rtos L_stau 2 2)))) - (princ "\n=========================================") - - ;; Bestaetigung - (setq antwort (getstring (ssg-text "gf-prompt-einfuegen"))) - (if (not (= antwort "2")) - (gefaellestrecke-einfuegen L_stau (fix winkel) startpunkt-fuer-einfuegen as-seite es-seite hz-winkel deltaL gf-as-winkel gf-es-winkel) + ;; Modus 1: Manuelle Eingabe via DCL-Dialog + (t + (princ (ssg-text "gf-modus1-header")) + ;; Startpunkt (X/Y) in der Zeichnung waehlen; Z kommt aus dem Dialog + (setq startpunkt-fuer-einfuegen + (getpoint (ssg-text "gf-prompt-startpunkt-aus"))) + (if (null startpunkt-fuer-einfuegen) + (setq startpunkt-fuer-einfuegen '(0 0 0))) + (setq dlg-werte (gf-dialog-eingabe nil)) + (if (null dlg-werte) (princ (ssg-text "gf-status-abgebrochen")) + (progn + (setq deltaL (nth 0 dlg-werte) + winkel (nth 1 dlg-werte) + hz-winkel (nth 2 dlg-werte) + as-seite (nth 3 dlg-werte) + es-seite (nth 4 dlg-werte) + gf-as-winkel (nth 6 dlg-werte) + gf-es-winkel (nth 7 dlg-werte)) + ;; Einfuegehoehe (Z) aus Dialog auf gewaehlten Startpunkt anwenden + (setq startpunkt-fuer-einfuegen + (list (car startpunkt-fuer-einfuegen) + (cadr startpunkt-fuer-einfuegen) + (nth 5 dlg-werte))) + (if (or (null deltaL) (<= deltaL 0)) + (setq deltaL (ssg-cfg-or "gefaelle" "default_delta_l" 5000.0))) + (if (or (null winkel) (<= winkel 0)) + (setq winkel (ssg-cfg-or "gefaelle" "default_winkel" 3.0))) + (gf-modus12-abschluss deltaL winkel hz-winkel as-seite es-seite + startpunkt-fuer-einfuegen gf-as-winkel gf-es-winkel) + ) ) ) ) nil ) +;; ============================================================ +;; TEIL 7: GEFAELLESTRECKE_EDIT - Doppelklick/Kontextmenue-Bearbeitung +;; ============================================================ + +;; Bearbeitet eine bestehende Gefaellestrecke (GF_n-Block) ueber den +;; DCL-Dialog: liest die aktuellen Parameter (XDATA, sonst Attribute), +;; zeigt sie vorbelegt im Dialog und baut die Strecke bei OK am selben +;; Startpunkt neu auf. Wird vom Doppelklick-Dispatcher SSG_BLOCKEDIT +;; fuer Blocknamen "GF_*" aufgerufen. +(defun c:GEFAELLESTRECKE_EDIT ( / ss ent ed bname startpunkt attribs xd + hz winkel as-winkel es-winkel z-start deltaL + prefill dlg-werte) + ;; Implied Selection VOR ssg-start pruefen (ssg-start hebt Selektion auf) + (setq ss (ssget "I")) + (if (and ss (= (sslength ss) 1) + (= (cdr (assoc 0 (entget (ssname ss 0)))) "INSERT")) + (setq ent (ssname ss 0)) + ) + + (ssg-start "GEFAELLESTRECKE_EDIT" '(("OSMODE") ("ATTREQ") ("ATTDIA"))) + (setvar "OSMODE" 0) + + ;; Block auswaehlen (falls nicht vorselektiert) + (if (null ent) + (progn + (setq ss (ssget ":S" '((0 . "INSERT")))) + (if ss (setq ent (ssname ss 0))) + ) + ) + (if (null ent) (progn (ssg-end) (exit))) + + (setq ed (entget ent)) + (setq bname (cdr (assoc 2 ed))) + (if (not (wcmatch bname "GF_*")) + (progn (alert (ssg-textf "gf-edit-kein-gf" (list bname))) (ssg-end) (exit))) + + (setq startpunkt (cdr (assoc 10 ed))) + (setq attribs (ssg-attrib-read ent)) + + ;; Nur einfache Gefaellestrecken (TYP "Gefaellestrecke") lassen sich ueber den + ;; Dialog verlustfrei nachbauen. Streckengruppen (mit Bogen / Linienzug / VF) + ;; wuerden beim Neuaufbau ihre Bogengeometrie verlieren -> abbrechen. + (if (/= (cond ((cdr (assoc "TYP" attribs))) ("")) "Gefaellestrecke") + (progn + (alert (ssg-textf "gf-edit-nur-einfach" + (list (cond ((cdr (assoc "TYP" attribs))) ("?"))))) + (ssg-end) + (exit) + ) + ) + + ;; Parameter bestimmen: bevorzugt aus XDATA (verlustfrei), sonst aus Attributen. + ;; Aus Attributen sind Fahrtrichtung (hz) und Element-Winkel nicht ableitbar + ;; -> Default 0 Grad / 90 Grad; der Nutzer kann sie im Dialog korrigieren. + (setq xd (gf-xdata-lesen ent)) + (if xd + (setq hz (atof (nth 0 xd)) + winkel (atof (nth 1 xd)) + as-winkel (nth 2 xd) + es-winkel (nth 3 xd) + z-start (atof (nth 4 xd)) + deltaL (atof (nth 5 xd))) + (setq hz 0.0 + winkel (atof (cond ((cdr (assoc "GF_WINKEL" attribs))) ("3.0"))) + as-winkel "90" + es-winkel "90" + z-start (atof (cond ((cdr (assoc "HOEHE_VON_mm" attribs))) ("0"))) + deltaL (atof (cond ((cdr (assoc "DELTA_L_mm" attribs))) ("0")))) + ) + + (setq prefill + (list (cons "deltaL" deltaL) + (cons "winkel" winkel) + (cons "hz" hz) + (cons "as-seite" (cond ((cdr (assoc "SEITE_AS" attribs))) ("links"))) + (cons "es-seite" (cond ((cdr (assoc "SEITE_ES" attribs))) ("links"))) + (cons "einfuegehoehe" z-start) + (cons "as-winkel" as-winkel) + (cons "es-winkel" es-winkel))) + + (setq dlg-werte (gf-dialog-eingabe prefill)) + (if (null dlg-werte) + (princ (ssg-text "gf-status-abgebrochen")) + (progn + ;; Alten GF-Verbund loeschen und am selben Startpunkt (X/Y) mit der + ;; im Dialog gewaehlten Einfuegehoehe (Z) neu aufbauen. + (entdel ent) + (gf-rebuild + (nth 0 dlg-werte) (nth 1 dlg-werte) (nth 2 dlg-werte) + (nth 3 dlg-werte) (nth 4 dlg-werte) + (list (car startpunkt) (cadr startpunkt) (nth 5 dlg-werte)) + (nth 6 dlg-werte) (nth 7 dlg-werte)) + (princ (ssg-textf "gf-edit-neu-aufgebaut" (list bname))) + ) + ) + (ssg-end) + (princ) +) + ;; ============================================================ ;; START ;; ============================================================ diff --git a/Lisp/README.md b/Lisp/README.md index 1ff7d35..e70233f 100644 --- a/Lisp/README.md +++ b/Lisp/README.md @@ -233,9 +233,30 @@ Lädt alle Module in der richtigen Reihenfolge. Ermittelt den Pfad automatisch ### `VarioFoerderer.lsp` – Vario-Förderer -Parametrischer Generator für ILS Vario-Förderanlagen. Liest Block-Geometrie aus einer DWG-Bibliothek (`DXFM_BLOCKS`). Alle Konstanten (Abstände, Winkel, Skalierung) werden via `ssg-cfg-or` aus `component_defaults.json` (Sektion `vario`) gelesen. +Kompatibilitäts-Wrapper, der `vf_core.lsp` lädt (Plugin-Dispatcher-Architektur; lädt seinerseits `vf_standard.lsp`, `vf_etage.lsp`, `vf_linienzug.lsp`). Parametrischer Generator für ILS Vario-Förderanlagen. Liest Block-Geometrie aus einer DWG-Bibliothek (`DXFM_BLOCKS`). Alle Konstanten (Abstände, Winkel, Skalierung) werden via `ssg-cfg-or` aus `component_defaults.json` (Sektion `vario`) gelesen. -**Befehl:** `c:FOERDERANLAGE` +**Befehl:** `c:FOERDERANLAGE` / `c:VarioFoerderer` – wählt zuerst den Anlagen-Typ (Standard/Etage/Linienzug): + +- **Typ „standard“ + Werteingabe:** läuft über den DCL-Dialog + [`dcl/variofoerderer.dcl`](../dcl/variofoerderer.dcl) (zwei Dialoge, `vfs-standard-dialog-ablauf` + in `vf_standard.lsp`): Dialog 1 (`vfs-dialog-eingabe-basis`) erfasst deltaL, deltaH, + Förderrichtung (Auf/Ab), Einfügehöhe, Fahrtrichtung und Seite; anschließend berechnet + `berechne-standard` die gültigen Winkel-Varianten, und Dialog 2 + (`vfs-dialog-winkel-verteilung`) lässt Winkel und L_GF-Verteilung (vorne/hinten) wählen. + Startpunkt (X/Y) wird wie bei der Gefällestrecke vorher in der Zeichnung gepickt. +- **Typ „standard“ + 3D-Linie**, sowie **Typ „etage“** und **„linienzug“** bleiben + vollständig interaktiv (Kommandozeile), unverändert gegenüber dem bisherigen Ablauf. + +**Bearbeiten (`c:VARIOFOERDERER_EDIT`):** Doppelklick auf einen `VF_n`-Block – bzw. +Rechtsklick-Kontextmenü/`SSG_BLOCKEDIT` – öffnet dieselben Dialoge, vorbelegt mit den +aktuellen Parametern, und baut die Strecke bei OK am selben Startpunkt neu auf (neue +`VF_n`-Nummer). Da `VF_n`-Blöcke auch von Etage- und Linienzug-Anlagen erzeugt werden +(deren komplexere Geometrie ein einfacher Neuaufbau zerstören würde), markiert +`vfs-xdata-schreiben` nur per Standard-Dialog erstellte Blöcke mit XDATA (App +`SSG_VF_EDIT`, Fahrtrichtung `hz`); Blöcke ohne diese Markierung weist +`c:VARIOFOERDERER_EDIT` mit einer Meldung ab, statt sie zu verändern. Winkel und L_GF1 +werden aus den Sivas-Attributen (`VF_WINKEL`, `L_GF_m`) vorbelegt (Verteilung stets als +„Eigene Werte“ mit dem exakten aktuellen L_GF1). **Wichtige interne Funktionen** (werden auch von `Gefaellestrecke.lsp` genutzt): @@ -260,10 +281,36 @@ Setzt `VarioFoerderer.lsp` voraus (nutzt dessen `init-bibliothek`, `insert-block **Befehl:** `c:GEFAELLESTRECKE` -Zwei Modi: +Drei Modi: -- **Modus 1:** Startpunkt + ΔL (Horizontalabstand) interaktiv abfragen -- **Modus 2:** Bestehende 3D-Linie wählen (ΔL und Winkel werden daraus berechnet) +- **Modus 1:** Startpunkt (X/Y) picken, restliche Parameter über den DCL-Dialog + [`dcl/gefaellestrecke.dcl`](../dcl/gefaellestrecke.dcl) eingeben (ΔL, Einfügehöhe Z, + Gefällewinkel, Fahrtrichtung, AUS-/EIN-Element je Winkel 30/90 und Seite links/rechts). + Die Einfügehöhe aus dem Dialog setzt die Z-Koordinate des Startpunkts. +- **Modus 2:** Bestehende 3D-Linie wählen (ΔL und Fahrtrichtung werden daraus berechnet), + restliche Parameter interaktiv über die Kommandozeile. +- **Modus 3:** Linienzug aus LINE-/ARC-Objekten; ARC-Segmente werden durch + Gefällebogen-Blöcke ersetzt, Winkel aus ΔH und Geometrie berechnet. + +**Dialog-Hilfsfunktionen:** `gf-dialog-eingabe` (lädt/liest den DCL-Dialog, liefert +`(deltaL winkel hz-winkel as-seite es-seite einfuegehoehe as-winkel es-winkel)` oder `nil` +bei Abbruch) und `gf-modus12-abschluss` (gemeinsamer Abschluss für Modus 1+2: setzt die +Element-Masse per `vf-set-as-masse`/`vf-set-es-masse`, berechnet die Staustreckenlänge, +zeigt die Zusammenfassung und ruft `gefaellestrecke-einfuegen`). + +Die AUS-/EIN-Element-Varianten (30/90 Grad, links/rechts) liegen als DWG-Blöcke +`AS_Element_<30|90>_` bzw. `ES_Element_…` in `data/ils/2D|3D/`; ihre Maße +werden direkt aus dem Block gemessen (`vf-element-masse`), es gibt dafür keine JSON-Tabelle. + +**Bearbeiten (`c:GEFAELLESTRECKE_EDIT`):** Doppelklick auf einen Gefällestrecken-Block +(`GF_n`) – bzw. Rechtsklick-Kontextmenü/`SSG_BLOCKEDIT` – öffnet denselben DCL-Dialog, +vorbelegt mit den aktuellen Parametern, und baut die Strecke bei OK am selben Startpunkt +neu auf. Damit Fahrtrichtung und Element-Winkel (30/90) verlustfrei rekonstruiert werden, +schreibt `gefaellestrecke-einfuegen` diese beim Erstellen als XDATA (App `SSG_GF_EDIT`, +Helfer `gf-xdata-schreiben`/`gf-xdata-lesen`) auf das `GF_n`-INSERT – bewusst **nicht** in +den Sivas-Attributsatz, damit der Export unverändert bleibt. Ältere Blöcke ohne XDATA +werden aus den Attributen vorbelegt (Fahrtrichtung 0°, Element-Winkel 90° als Vorgabe). +Der Neuaufbau läuft über `gf-rebuild` (nicht-interaktiv, ohne Konsolen-Rückfrage). --- diff --git a/Lisp/SSG_LIB_Commands.lsp b/Lisp/SSG_LIB_Commands.lsp index 8fa4d45..db64330 100644 --- a/Lisp/SSG_LIB_Commands.lsp +++ b/Lisp/SSG_LIB_Commands.lsp @@ -219,6 +219,20 @@ (command "_.BEDIT" bname) ) + ;; Gefaellestrecke-Bloecke (GF_n) -> GEFAELLESTRECKE_EDIT (DCL-Dialog) + ((wcmatch bname "GF_*") + (princ (ssg-textf "cmd-gefaelle-bearbeiten" (list bname))) + (c:GEFAELLESTRECKE_EDIT) + ) + + ;; VarioFoerderer-Bloecke (VF_n) -> VARIOFOERDERER_EDIT (DCL-Dialog); + ;; weist Bloecke ohne SSG_VF_EDIT-XDATA (Etage/Linienzug/Altbestand) + ;; intern selbst ab, siehe c:VARIOFOERDERER_EDIT in vf_standard.lsp. + ((wcmatch bname "VF_*") + (princ (ssg-textf "cmd-variofoerderer-bearbeiten" (list bname))) + (c:VARIOFOERDERER_EDIT) + ) + ;; Omniflo-Bloecke -> OMNI_EDIT ;; Erkennung: TEILEART-Attribut vorhanden, AP110-Blockname oder rein numerisch ((progn diff --git a/Lisp/vf_core.lsp b/Lisp/vf_core.lsp index c2570a5..6750cc9 100644 --- a/Lisp/vf_core.lsp +++ b/Lisp/vf_core.lsp @@ -1151,8 +1151,9 @@ (if (car (atoms-family 1 '("SSG-ID-GENERATE"))) (ssg-id-generate vf-insert)) (princ (ssg-textf "vfc-block-erstellt-eingefuegt" (list vf-bname))) + vf-insert ) - (princ (ssg-text "vfc-fehler-keine-entities")) + (progn (princ (ssg-text "vfc-fehler-keine-entities")) nil) ) ) @@ -1260,6 +1261,26 @@ (setq vf-nummer (vf-next-number)) (princ (ssg-textf "vfc-naechste-vf-nummer" (list (itoa vf-nummer)))) + ;; 2b. Standardfall (anlage-typ "standard") + Werteingabe -> DCL-Dialog + ;; (dcl/variofoerderer.dcl, siehe vf_standard.lsp: vfs-standard-dialog-ablauf). + ;; 3D-Linie-Eingabe und alle anderen Anlagen-Typen (Etage, Linienzug) bleiben + ;; unveraendert interaktiv (fallen unten in den bestehenden Konsolen-Ablauf, + ;; der die Eingabemodus-Frage dabei erneut stellt - siehe vf-eingabe-abfragen). + (if (= anlage-typ "standard") + (progn + (princ (ssg-text "vfc-eingabemodus-header")) + (princ (ssg-text "vfc-eingabemodus-3dlinie")) + (princ (ssg-text "vfc-eingabemodus-werte")) + (setq antwort (getstring (ssg-text "vfc-prompt-wahl-1-2-ohne-default"))) + (if (= antwort "2") + (progn + (vfs-standard-dialog-ablauf vf-nummer) + (exit) + ) + ) + ) + ) + ;; 3. Gemeinsame Eingabe (deltaL, deltaH, richtung, startpunkt) - richtung ;; steht bei beiden Eingabemodi (Werteingabe UND 3D-Linie) bereits fest. (setq eingabe (vf-eingabe-abfragen)) diff --git a/Lisp/vf_standard.lsp b/Lisp/vf_standard.lsp index 6f046c3..c5b330b 100644 --- a/Lisp/vf_standard.lsp +++ b/Lisp/vf_standard.lsp @@ -699,6 +699,427 @@ endpunkt ) +;; ============================================================ +;; DCL-DIALOG (Standardfall) - dcl/variofoerderer.dcl +;; Ersetzt die interaktive Werteingabe aus vf-eingabe-abfragen +;; (Konsolen-Modus "Werteingabe") fuer anlage-typ "standard" durch +;; zwei DCL-Dialoge: Basis-Eingabe und Winkel/Verteilung. Die +;; 3D-Linie-Eingabe sowie die Typen "etage"/"linienzug" bleiben +;; unveraendert interaktiv (siehe Verzweigung in c:VarioFoerderer). +;; ============================================================ + +;; Basis-Eingabe (Dialog 1): deltaL, deltaH, Richtung, Einfuegehoehe, +;; Fahrtrichtung, Seite. +;; prefill: Assoziationsliste zur Vorbelegung (fuer VARIOFOERDERER_EDIT) mit +;; Schluesseln "deltaL" "deltaH" "richtung" "einfuegehoehe" "hz" "seite" +;; - oder nil fuer Neuanlage mit Defaults. +;; Rueckgabe: (deltaL deltaH richtung einfuegehoehe hz seite) oder nil bei Abbruch. +(defun vfs-dialog-eingabe-basis (prefill / dcl-pfad dat ergebnis + dlg-deltal dlg-deltah dlg-richtung dlg-hoehe + dlg-fahrtrichtung dlg-seite + deltaL deltaH richtung hz seite z-start) + (setq dcl-pfad (strcat (getenv "DXFM_DCL") "/variofoerderer.dcl")) + (setq dat (load_dialog dcl-pfad)) + (if (not (new_dialog "variofoerderer_basis" dat)) + (progn + (if (>= dat 0) (unload_dialog dat)) + (alert (ssg-textf "gf-alert-dialog-fehlt" (list dcl-pfad))) + nil + ) + (progn + ;; Vorbelegung: aus prefill (Edit) oder Defaults (Neuanlage) + (set_tile "deltal" + (if prefill (rtos (cdr (assoc "deltaL" prefill)) 2 0) "15000")) + (set_tile "deltah" + (if prefill (rtos (cdr (assoc "deltaH" prefill)) 2 0) "3000")) + (set_tile "einfuegehoehe" + (if prefill (rtos (cdr (assoc "einfuegehoehe" prefill)) 2 1) "0")) + + (start_list "richtung") + (add_list (ssg-text "vfs-dlg-richtung-auf")) + (add_list (ssg-text "vfs-dlg-richtung-ab")) + (end_list) + (set_tile "richtung" + (if (and prefill (equal (cdr (assoc "richtung" prefill)) "Ab")) "1" "0")) + + (start_list "fahrtrichtung") + (add_list (ssg-textf "gf-dlg-richtung-0" (list grad-zeichen))) + (add_list (ssg-textf "gf-dlg-richtung-90" (list grad-zeichen))) + (add_list (ssg-textf "gf-dlg-richtung-180" (list grad-zeichen))) + (add_list (ssg-textf "gf-dlg-richtung-270" (list grad-zeichen))) + (end_list) + (set_tile "fahrtrichtung" + (if prefill + (cond ((equal (cdr (assoc "hz" prefill)) 90.0 0.1) "1") + ((equal (cdr (assoc "hz" prefill)) 180.0 0.1) "2") + ((equal (cdr (assoc "hz" prefill)) 270.0 0.1) "3") + (t "0")) + "0")) + + (start_list "seite") + (add_list (ssg-text "gf-dlg-rechts")) + (add_list (ssg-text "gf-dlg-links")) + (end_list) + (set_tile "seite" + (if (and prefill (equal (cdr (assoc "seite" prefill)) "links")) "1" "0")) + + ;; Actions + (action_tile "accept" + (strcat + "(setq dlg-deltal (get_tile \"deltal\"))" + "(setq dlg-deltah (get_tile \"deltah\"))" + "(setq dlg-richtung (get_tile \"richtung\"))" + "(setq dlg-hoehe (get_tile \"einfuegehoehe\"))" + "(setq dlg-fahrtrichtung (get_tile \"fahrtrichtung\"))" + "(setq dlg-seite (get_tile \"seite\"))" + "(done_dialog 1)" + ) + ) + (action_tile "cancel" "(done_dialog 0)") + + (setq ergebnis (start_dialog)) + (unload_dialog dat) + + (if (= ergebnis 1) + (progn + (setq deltaL (atof dlg-deltal)) + (setq deltaH (atof dlg-deltah)) + (setq richtung (if (= dlg-richtung "1") "Ab" "Auf")) + (setq z-start (atof dlg-hoehe)) + (setq hz + (cond ((= dlg-fahrtrichtung "1") 90.0) + ((= dlg-fahrtrichtung "2") 180.0) + ((= dlg-fahrtrichtung "3") 270.0) + (t 0.0))) + (setq seite (if (= dlg-seite "1") "links" "rechts")) + (list deltaL deltaH richtung z-start hz seite) + ) + nil + ) + ) + ) +) + +;; Winkel/Verteilung-Eingabe (Dialog 2): Winkel-Popup aus den vorher +;; berechneten gueltigen Varianten (gueltige-winkel/ergebnis-liste, siehe +;; berechne-alle-winkel), plus L_GF-Verteilung vorne/hinten (analog Schritt +;; 8+9 in c:VarioFoerderer). +;; prefill: Assoziationsliste ("winkel" . best-winkel) ("verteilung-idx" . "0".."3") +;; ("lgf1" . mm) - oder nil fuer Vorbelegung mit dem kleinsten gueltigen Winkel. +;; Rueckgabe: (best-winkel L_GF L_VF L_GF1 L_GF2 verteilung-modus) oder nil bei Abbruch. +(defun vfs-dialog-winkel-verteilung (gueltige-winkel ergebnis-liste staustrecke-basis prefill / + dcl-pfad dat ergebnis w eintrag idx sel-idx init-verteilung + dlg-winkel dlg-verteilung dlg-lgf1 + best-winkel L_GF L_VF verteilung-modus L_GF1 L_GF2) + (setq dcl-pfad (strcat (getenv "DXFM_DCL") "/variofoerderer.dcl")) + (setq dat (load_dialog dcl-pfad)) + (if (not (new_dialog "variofoerderer_winkel" dat)) + (progn + (if (>= dat 0) (unload_dialog dat)) + (alert (ssg-textf "gf-alert-dialog-fehlt" (list dcl-pfad))) + nil + ) + (progn + ;; Winkel-Popup aus den gueltigen Varianten aufbauen + (start_list "winkel") + (foreach w gueltige-winkel + (setq eintrag (assoc w ergebnis-liste)) + (if (= w 0) + (add_list (ssg-textf "vfs-dlg-winkel-horizontal" + (list (rtos (nth 1 eintrag) 2 1) (rtos (nth 2 eintrag) 2 1)))) + (add_list (ssg-textf "vfs-dlg-winkel-option" + (list (itoa w) grad-zeichen (rtos (nth 1 eintrag) 2 1) (rtos (nth 2 eintrag) 2 1)))) + ) + ) + (end_list) + ;; Vorbelegung: Winkel aus prefill (falls noch gueltig), sonst erster + ;; (kleinster) gueltiger Winkel - analog der automatischen Konsolen-Wahl. + (setq sel-idx 0 idx 0) + (if prefill + (foreach w gueltige-winkel + (if (equal w (cdr (assoc "winkel" prefill))) (setq sel-idx idx)) + (setq idx (1+ idx)) + ) + ) + (set_tile "winkel" (itoa sel-idx)) + + ;; Verteilung: Popup + manuelles L_GF1-Feld (nur bei "Eigene Werte" aktiv) + (start_list "verteilung") + (add_list (ssg-text "vfs-dlg-verteilung-1")) + (add_list (ssg-text "vfs-dlg-verteilung-2")) + (add_list (ssg-text "vfs-dlg-verteilung-3")) + (add_list (ssg-text "vfs-dlg-verteilung-4")) + (end_list) + (setq init-verteilung (if prefill (cdr (assoc "verteilung-idx" prefill)) "0")) + (set_tile "verteilung" init-verteilung) + (set_tile "lgf1_manuell" + (if (and prefill (cdr (assoc "lgf1" prefill))) + (rtos (cdr (assoc "lgf1" prefill)) 2 1) "0")) + (mode_tile "lgf1_manuell" (if (= init-verteilung "3") 0 1)) + + (action_tile "verteilung" + "(mode_tile \"lgf1_manuell\" (if (= (get_tile \"verteilung\") \"3\") 0 1))") + + (action_tile "accept" + (strcat + "(setq dlg-winkel (get_tile \"winkel\"))" + "(setq dlg-verteilung (get_tile \"verteilung\"))" + "(setq dlg-lgf1 (get_tile \"lgf1_manuell\"))" + "(done_dialog 1)" + ) + ) + (action_tile "cancel" "(done_dialog 0)") + + (setq ergebnis (start_dialog)) + (unload_dialog dat) + + (if (= ergebnis 1) + (progn + (setq best-winkel (nth (atoi dlg-winkel) gueltige-winkel)) + (setq eintrag (assoc best-winkel ergebnis-liste)) + (setq L_GF (nth 1 eintrag) L_VF (nth 2 eintrag)) + (setq verteilung-modus dlg-verteilung) + (cond + ((= verteilung-modus "0") + (setq L_GF1 (/ L_GF 2.0) L_GF2 (/ L_GF 2.0))) + ((= verteilung-modus "1") + (setq L_GF1 (float staustrecke-basis)) + (setq L_GF2 (max 0.0 (- L_GF (float staustrecke-basis))))) + ((= verteilung-modus "2") + (setq L_GF2 (float staustrecke-basis)) + (setq L_GF1 (max 0.0 (- L_GF (float staustrecke-basis))))) + ((= verteilung-modus "3") + (setq L_GF1 (atof dlg-lgf1)) + (if (or (null L_GF1) (< L_GF1 0)) (setq L_GF1 (/ L_GF 2.0))) + (if (> L_GF1 L_GF) (setq L_GF1 L_GF)) + (setq L_GF2 (max 0.0 (- L_GF L_GF1)))) + (t (setq L_GF1 (/ L_GF 2.0) L_GF2 (/ L_GF 2.0))) + ) + (list best-winkel L_GF L_VF L_GF1 L_GF2 verteilung-modus) + ) + nil + ) + ) + ) +) + +;; ============================================================ +;; XDATA-Metadaten fuer den Edit-Nachbau (App "SSG_VF_EDIT"). +;; Markiert ein VF_n-INSERT als "mit dem Standard-Dialog erstellt" und +;; speichert die Fahrtrichtung (hz) - der einzige Wert, der sich nicht aus +;; den Sivas-Attributen rekonstruieren laesst (ANTRIEBFAHRTRICHTUNG, SEITE_AS, +;; VF_WINKEL, L_GF_m stehen dort bereits). Bewusst NICHT im Sivas-Attributsatz. +;; Ohne diese Markierung (z.B. Etage-, Linienzug- oder Altbestand-Bloecke) +;; verweigert c:VARIOFOERDERER_EDIT die Bearbeitung, um deren komplexere +;; Geometrie nicht versehentlich durch einen einfachen Neuaufbau zu ersetzen. +;; ============================================================ +(if (null *vf-xdata-app*) (setq *vf-xdata-app* "SSG_VF_EDIT")) + +(defun vfs-xdata-schreiben (ent hz /) + (regapp *vf-xdata-app*) + (entmod + (append (entget ent) + (list + (list -3 + (list *vf-xdata-app* + (cons 1000 "standard") + (cons 1000 (rtos (float hz) 2 4))))))) + ent +) + +;; XDATA lesen -> (typ-marker hz-str) oder nil. +(defun vfs-xdata-lesen (ent / xd app-data) + (setq xd (entget ent (list *vf-xdata-app*))) + (setq app-data (cdr (assoc -3 xd))) + (if app-data + (mapcar 'cdr (cdr (car app-data))) + nil + ) +) + +;; ============================================================ +;; Gemeinsame Berechnung + Winkel/Verteilung-Dialog + Einfuegung. +;; Wird sowohl von der Neuanlage (vfs-standard-dialog-ablauf) als auch vom +;; Edit-Befehl (c:VARIOFOERDERER_EDIT) verwendet. +;; winkel-prefill: Vorbelegung fuer vfs-dialog-winkel-verteilung oder nil. +;; ============================================================ +(defun vfs-standard-dialog-berechnen-einfuegen (vf-nummer deltaL deltaH richtung startpunkt hz seite + winkel-prefill / + ergebnis ergebnis-liste gueltige-winkel horizontal-info + winkelwahl best-winkel L_GF L_VF L_GF1 L_GF2 verteilung-modus + n-scanner hoehe-von hoehe-bis lastEnt vf-insert staustrecke-basis) + (setq ergebnis (berechne-standard deltaL deltaH richtung seite)) + (setq ergebnis-liste (cadddr ergebnis)) + (setq staustrecke-basis (ssg-cfg-or "vario" "staustrecke_basis" 1000)) + + ;; Horizontale Zwischenstrecke als Zusatzoption (nur bei "Ab" erreichbar, + ;; siehe Kommentar in c:VarioFoerderer Schritt 5b). + (setq horizontal-info nil) + (if (= richtung "Ab") + (progn + (setq horizontal-info (berechne-horizontale-mitte deltaL deltaH "Ab")) + (if (and horizontal-info (caddr horizontal-info)) + (setq ergebnis-liste + (append ergebnis-liste (list (list 0 (car horizontal-info) (cadr horizontal-info) T)))) + (setq horizontal-info nil) + ) + ) + ) + + (setq gueltige-winkel + (mapcar 'car + (vl-remove-if-not + (function (lambda (e) (and (nth 3 e) (numberp (nth 1 e)) (numberp (nth 2 e)) + (>= (nth 1 e) 0) (>= (nth 2 e) 0)))) + ergebnis-liste))) + + (if (null gueltige-winkel) + (alert (ssg-textf "vfc-alert-kein-winkel" + (list (chr 916) (rtos deltaL 2 0) (rtos deltaH 2 0) richtung))) + (progn + (setq winkelwahl (vfs-dialog-winkel-verteilung gueltige-winkel ergebnis-liste staustrecke-basis winkel-prefill)) + (if (null winkelwahl) + (princ (ssg-text "vfc-vorgang-abgebrochen")) + (progn + (setq best-winkel (nth 0 winkelwahl) + L_GF (nth 1 winkelwahl) + L_VF (nth 2 winkelwahl) + L_GF1 (nth 3 winkelwahl) + L_GF2 (nth 4 winkelwahl) + verteilung-modus (nth 5 winkelwahl)) + (setq n-scanner 0) + (setq hoehe-von (caddr startpunkt)) + (setq hoehe-bis + (cond ((= richtung "Auf") (+ hoehe-von deltaH)) + ((= richtung "Ab") (- hoehe-von deltaH)) + (t hoehe-von))) + (setq lastEnt (vf-lastent-ohne-attribute)) + + (variofoerderer-einfuegen deltaL deltaH richtung best-winkel L_GF1 L_GF2 L_VF startpunkt seite hz) + + (setq vf-insert + (vf-block-erstellen "standard" seite vf-nummer n-scanner + hoehe-von hoehe-bis deltaH deltaL L_VF L_GF1 L_GF2 + richtung best-winkel startpunkt lastEnt hz)) + (if vf-insert (vfs-xdata-schreiben vf-insert hz)) + (princ "\n=========================================") + ) + ) + ) + ) +) + +;; Neuanlage (Menue/Konsole -> Dialog): Startpunkt (X/Y) picken, Basis-Dialog, +;; dann gemeinsame Berechnung/Winkel-Dialog/Einfuegung. +(defun vfs-standard-dialog-ablauf (vf-nummer / eingabe deltaL deltaH richtung + z-start hz seite startpunkt startpunkt-xy) + (setq startpunkt-xy (getpoint (ssg-text "vfc-prompt-startpunkt-aus"))) + (if (null startpunkt-xy) (setq startpunkt-xy '(0 0 0))) + + (setq eingabe (vfs-dialog-eingabe-basis nil)) + (if (null eingabe) + (princ (ssg-text "vfc-vorgang-abgebrochen")) + (progn + (setq deltaL (nth 0 eingabe) + deltaH (nth 1 eingabe) + richtung (nth 2 eingabe) + z-start (nth 3 eingabe) + hz (nth 4 eingabe) + seite (nth 5 eingabe)) + (setq startpunkt (list (car startpunkt-xy) (cadr startpunkt-xy) z-start)) + (vfs-standard-dialog-berechnen-einfuegen vf-nummer deltaL deltaH richtung startpunkt hz seite nil) + ) + ) +) + +;; ============================================================ +;; VARIOFOERDERER_EDIT - Doppelklick/Kontextmenue-Bearbeitung +;; ============================================================ + +;; Bearbeitet einen bestehenden Standard-VarioFoerderer (VF_n-Block, mit +;; SSG_VF_EDIT-XDATA) ueber die DCL-Dialoge: liest die aktuellen Parameter, +;; zeigt sie vorbelegt und baut die Strecke bei OK am selben Startpunkt neu +;; auf. Wird vom Doppelklick-Dispatcher SSG_BLOCKEDIT fuer Blocknamen "VF_*" +;; aufgerufen. Bloecke ohne SSG_VF_EDIT-XDATA (Etage, Linienzug, Altbestand) +;; werden abgewiesen, da ihre Geometrie sich nicht verlustfrei aus dem +;; einfachen Standard-Schema rekonstruieren laesst. +(defun c:VARIOFOERDERER_EDIT ( / ss ent ed bname startpunkt attribs xd + deltaL deltaH richtung z-start hz seite + eingabe vf-nummer best-winkel-attr L_GF_m-teile + prefill-basis prefill-winkel) + ;; Implied Selection VOR ssg-start pruefen (ssg-start hebt Selektion auf) + (setq ss (ssget "I")) + (if (and ss (= (sslength ss) 1) + (= (cdr (assoc 0 (entget (ssname ss 0)))) "INSERT")) + (setq ent (ssname ss 0)) + ) + + (ssg-start "VARIOFOERDERER_EDIT" '(("OSMODE") ("ATTREQ") ("ATTDIA"))) + (setvar "OSMODE" 0) + + (if (null ent) + (progn + (setq ss (ssget ":S" '((0 . "INSERT")))) + (if ss (setq ent (ssname ss 0))) + ) + ) + (if (null ent) (progn (ssg-end) (exit))) + + (setq ed (entget ent)) + (setq bname (cdr (assoc 2 ed))) + (if (not (wcmatch bname "VF_*")) + (progn (alert (ssg-textf "vfs-edit-kein-vf" (list bname))) (ssg-end) (exit))) + + (setq xd (vfs-xdata-lesen ent)) + (if (or (null xd) (/= (car xd) "standard")) + (progn (alert (ssg-textf "vfs-edit-keine-xdata" (list bname))) (ssg-end) (exit))) + + (setq startpunkt (cdr (assoc 10 ed))) + (setq attribs (ssg-attrib-read ent)) + (setq hz (atof (nth 1 xd))) + (setq deltaL (atof (cond ((cdr (assoc "DELTA_L_mm" attribs))) ("15000")))) + (setq deltaH (atof (cond ((cdr (assoc "DELTA_H_mm" attribs))) ("3000")))) + (setq richtung (cond ((cdr (assoc "ANTRIEBFAHRTRICHTUNG" attribs))) ("Auf"))) + (setq seite (cond ((cdr (assoc "SEITE_AS" attribs))) ("rechts"))) + (setq z-start (caddr startpunkt)) + + (setq prefill-basis + (list (cons "deltaL" deltaL) (cons "deltaH" deltaH) (cons "richtung" richtung) + (cons "einfuegehoehe" z-start) (cons "hz" hz) (cons "seite" seite))) + + (setq eingabe (vfs-dialog-eingabe-basis prefill-basis)) + (if (null eingabe) + (princ (ssg-text "vfc-vorgang-abgebrochen")) + (progn + (setq deltaL (nth 0 eingabe) + deltaH (nth 1 eingabe) + richtung (nth 2 eingabe) + z-start (nth 3 eingabe) + hz (nth 4 eingabe) + seite (nth 5 eingabe)) + (setq startpunkt (list (car startpunkt) (cadr startpunkt) z-start)) + + ;; Vorbelegung Winkel/Verteilung: bestehenden Winkel vorschlagen, L_GF- + ;; Verteilung als "Eigene Werte" mit dem aktuellen L_GF1 (aus L_GF_m) - + ;; das ist unabhaengig vom urspruenglichen Verteilungsmodus immer exakt. + (setq best-winkel-attr (atoi (cond ((cdr (assoc "VF_WINKEL" attribs))) ("0")))) + (setq L_GF_m-teile (ssg-cfg-split-comma (cond ((cdr (assoc "L_GF_m" attribs))) ("0.0,0.0")))) + (setq prefill-winkel + (list (cons "winkel" best-winkel-attr) + (cons "verteilung-idx" "3") + (cons "lgf1" (* 1000.0 (atof (car L_GF_m-teile)))))) + + (entdel ent) + (setq vf-nummer (vf-next-number)) + (vfs-standard-dialog-berechnen-einfuegen vf-nummer deltaL deltaH richtung + startpunkt hz seite prefill-winkel) + (princ (ssg-textf "vfs-edit-neu-aufgebaut" (list bname))) + ) + ) + (ssg-end) + (princ) +) + ;; ============================================================ ;; REGISTRIERUNG ;; ============================================================ diff --git a/dcl/gefaellestrecke.dcl b/dcl/gefaellestrecke.dcl new file mode 100644 index 0000000..e406e6d --- /dev/null +++ b/dcl/gefaellestrecke.dcl @@ -0,0 +1,82 @@ +// ============================================================ +// gefaellestrecke.dcl - Gefaellestrecke Parameter-Eingabe +// Parametrische Eingabe fuer die einfache Gefaellestrecke +// (Modus 1 - Manuell) analog zum Kreisel-Dialog, ergaenzt um +// die Angabe der Einfuegehoehe. +// Eingaben entsprechen dem interaktiven Skript Gefaellestrecke.lsp: +// Abstand deltaL, Gefaellewinkel, Fahrtrichtung, +// Seite AUS-Element, Seite EIN-Element. +// ============================================================ + +gefaellestrecke : dialog { + label = "Gefaellestrecke erstellen"; + + : column { + + : boxed_column { + label = "Geometrie"; + + : row { + : edit_box { + key = "deltal"; + label = "Abstand deltaL (mm):"; + edit_width = 10; + } + : edit_box { + key = "einfuegehoehe"; + label = "Einfuegehoehe (mm):"; + edit_width = 10; + } + } + + : edit_box { + key = "winkel"; + label = "Gefaellewinkel (Grad):"; + edit_width = 10; + } + } + + : boxed_column { + label = "Ausrichtung"; + + : popup_list { + key = "fahrtrichtung"; + label = "Fahrtrichtung:"; + width = 30; + } + } + + : boxed_row { + label = "AUS-Element"; + + : popup_list { + key = "aus_winkel"; + label = "Winkel:"; + width = 12; + } + : popup_list { + key = "aus_seite"; + label = "Seite:"; + width = 14; + } + } + + : boxed_row { + label = "EIN-Element"; + + : popup_list { + key = "ein_winkel"; + label = "Winkel:"; + width = 12; + } + : popup_list { + key = "ein_seite"; + label = "Seite:"; + width = 14; + } + } + + } + + ok_cancel; +} diff --git a/dcl/variofoerderer.dcl b/dcl/variofoerderer.dcl new file mode 100644 index 0000000..86b213f --- /dev/null +++ b/dcl/variofoerderer.dcl @@ -0,0 +1,99 @@ +// ============================================================ +// variofoerderer.dcl - Standard-VarioFoerderer Parameter-Eingabe +// Zwei Dialoge fuer den Standardfall (anlage-typ "standard") von +// c:VarioFoerderer, ersetzen die interaktive Werteingabe (Modus +// "Werteingabe") aus vf-eingabe-abfragen / vf_core.lsp: +// Dialog 1 (variofoerderer_basis): deltaL, deltaH, Foerderrichtung, +// Einfuegehoehe, Fahrtrichtung, Seite. +// Dialog 2 (variofoerderer_winkel): Winkel (dynamisch berechnete +// gueltige Varianten), Verteilung der Gefaellestrecken vorne/hinten. +// Die 3D-Linie-Eingabe (Modus "3D-Linie") und andere Anlagen-Typen +// (Etage, Linienzug) bleiben interaktiv (Kommandozeile). +// ============================================================ + +variofoerderer_basis : dialog { + label = "VarioFoerderer (Standard) - Eingabe"; + + : column { + + : boxed_column { + label = "Geometrie"; + + : row { + : edit_box { + key = "deltal"; + label = "Horizontale Distanz deltaL (mm):"; + edit_width = 10; + } + : edit_box { + key = "deltah"; + label = "Hoehenunterschied deltaH (mm):"; + edit_width = 10; + } + } + + : row { + : popup_list { + key = "richtung"; + label = "Foerderrichtung:"; + width = 16; + } + : edit_box { + key = "einfuegehoehe"; + label = "Einfuegehoehe (mm):"; + edit_width = 10; + } + } + } + + : boxed_column { + label = "Ausrichtung"; + + : popup_list { + key = "fahrtrichtung"; + label = "Fahrtrichtung:"; + width = 30; + } + : popup_list { + key = "seite"; + label = "Seite:"; + width = 16; + } + } + + } + + ok_cancel; +} + +variofoerderer_winkel : dialog { + label = "VarioFoerderer (Standard) - Winkel / Verteilung"; + + : column { + + : popup_list { + key = "winkel"; + label = "Winkel:"; + width = 45; + } + + : boxed_column { + label = "Verteilung Gefaellestrecken (vorne/hinten)"; + + : popup_list { + key = "verteilung"; + label = "Modus:"; + width = 35; + } + : edit_box { + key = "lgf1_manuell"; + label = "L_GF1 vorne (mm):"; + edit_width = 10; + is_enabled = false; + } + } + + } + + ok_cancel; +} diff --git a/lang/de_DE.json b/lang/de_DE.json index 5f398c9..3924606 100644 --- a/lang/de_DE.json +++ b/lang/de_DE.json @@ -298,6 +298,17 @@ "vfc-einfuegen-frage": "\n\nMoechten Sie die Foerderanlage einfuegen?", "vfc-einfuegen-optionen": "\n 1 - Ja, Module einfuegen\n 2 - Nein, abbrechen", "vfc-vorgang-abgebrochen": "\nVorgang abgebrochen.", + "vfs-dlg-richtung-auf": "Auf", + "vfs-dlg-richtung-ab": "Ab", + "vfs-dlg-winkel-horizontal": "Horizontal (GF=%1 mm, VF=%2 mm)", + "vfs-dlg-winkel-option": "%1%2 (GF=%3 mm, VF=%4 mm)", + "vfs-dlg-verteilung-1": "Gleichmaessig (L_GF1 = L_GF2 = L_GF/2)", + "vfs-dlg-verteilung-2": "Vorne Minimum, hinten Rest", + "vfs-dlg-verteilung-3": "Hinten Minimum, vorne Rest", + "vfs-dlg-verteilung-4": "Eigene Werte eingeben", + "vfs-edit-kein-vf": "Block %1 ist keine VarioFoerderer-Strecke.", + "vfs-edit-keine-xdata": "VarioFoerderer %1 wurde nicht mit diesem Dialog erstellt (Etage/Linienzug/Altbestand) und kann hier nicht bearbeitet werden.", + "vfs-edit-neu-aufgebaut": "\nVarioFoerderer %1 neu aufgebaut.", "vfe-alert-bib-nicht-init": "Etage-Bibliothek nicht initialisiert!", "vfe-berechnung-header": "\n ETAGE-BERECHNUNG FUER ALLE WINKEL", "vfe-bib-bereits-init": "\n Etage-Bibliothek bereits initialisiert.", @@ -376,6 +387,11 @@ "cmd-kein-block-ausgewaehlt": "\nKein Block ausgewaehlt.", "cmd-kreisel-bearbeiten": "\nKreisel bearbeiten: %1", "cmd-eckrad-bearbeiten": "\nEckrad bearbeiten: %1", + "cmd-gefaelle-bearbeiten": "\nGefaellestrecke bearbeiten: %1", + "cmd-variofoerderer-bearbeiten": "\nVarioFoerderer bearbeiten: %1", + "gf-edit-kein-gf": "Block %1 ist keine Gefaellestrecke.", + "gf-edit-nur-einfach": "Nur einfache Gefaellestrecken (ohne Bogen) koennen ueber den Dialog bearbeitet werden.\nTYP: %1", + "gf-edit-neu-aufgebaut": "\nGefaellestrecke %1 neu aufgebaut.", "cmd-omniflo-bearbeiten": "\nOmniflo Element bearbeiten: %1", "cmd-attribute-bearbeiten": "\nAttribute bearbeiten: %1", "layer-prompt-ziel-layer": "\nObjekt auf dem Ziel-Layer waehlen: ", @@ -499,6 +515,13 @@ "gf-status-z-hoehe-ks-aus": "\n Z-Hoehe KS_AUS: %1 mm", "gf-status-fertig": "\n>>> Fertig! <<<", "gf-status-abgebrochen": "\nAbgebrochen.", + "gf-alert-dialog-fehlt": "Dialog-Datei nicht gefunden:\n%1", + "gf-dlg-richtung-0": "0%1 (X+, Ost)", + "gf-dlg-richtung-90": "90%1 (Y+, Nord)", + "gf-dlg-richtung-180": "180%1 (X-, West)", + "gf-dlg-richtung-270": "270%1 (Y-, Sued)", + "gf-dlg-links": "Links", + "gf-dlg-rechts": "Rechts", "gf-status-gf-block-erstellt": "\n>>> GF-Block erstellt: %1 (%2)", "gf-status-schritt1-aus": "\n\n1/4: %1 Fahrtrichtung=%2%3 KS_EIN-hz=%4%5 v=%6%7", "gf-status-schritt2-staustrecke": "\n\n2/4: Staustrecke L=%1 mm hz=%2%3 v=%4%5", diff --git a/lang/en_GB.json b/lang/en_GB.json index 31d8f5c..c2750d7 100644 --- a/lang/en_GB.json +++ b/lang/en_GB.json @@ -298,6 +298,17 @@ "vfc-einfuegen-frage": "\n\nDo you want to insert the conveyor system?", "vfc-einfuegen-optionen": "\n 1 - Yes, insert modules\n 2 - No, cancel", "vfc-vorgang-abgebrochen": "\nProcess cancelled.", + "vfs-dlg-richtung-auf": "Up", + "vfs-dlg-richtung-ab": "Down", + "vfs-dlg-winkel-horizontal": "Horizontal (GF=%1 mm, VF=%2 mm)", + "vfs-dlg-winkel-option": "%1%2 (GF=%3 mm, VF=%4 mm)", + "vfs-dlg-verteilung-1": "Even (L_GF1 = L_GF2 = L_GF/2)", + "vfs-dlg-verteilung-2": "Front minimum, rear remainder", + "vfs-dlg-verteilung-3": "Rear minimum, front remainder", + "vfs-dlg-verteilung-4": "Enter custom values", + "vfs-edit-kein-vf": "Block %1 is not a VarioFoerderer conveyor.", + "vfs-edit-keine-xdata": "VarioFoerderer %1 was not created with this dialog (Etage/Linienzug/legacy) and cannot be edited here.", + "vfs-edit-neu-aufgebaut": "\nVarioFoerderer %1 rebuilt.", "vfe-alert-bib-nicht-init": "Etage library not initialised!", "vfe-berechnung-header": "\n ETAGE CALCULATION FOR ALL ANGLES", "vfe-bib-bereits-init": "\n Etage library already initialised.", @@ -376,6 +387,11 @@ "cmd-kein-block-ausgewaehlt": "\nNo block selected.", "cmd-kreisel-bearbeiten": "\nEdit Kreisel: %1", "cmd-eckrad-bearbeiten": "\nEdit Eckrad: %1", + "cmd-gefaelle-bearbeiten": "\nEdit Gefaellestrecke: %1", + "cmd-variofoerderer-bearbeiten": "\nEdit VarioFoerderer: %1", + "gf-edit-kein-gf": "Block %1 is not a Gefaellestrecke.", + "gf-edit-nur-einfach": "Only simple Gefaellestrecken (without arcs) can be edited via the dialog.\nTYP: %1", + "gf-edit-neu-aufgebaut": "\nGefaellestrecke %1 rebuilt.", "cmd-omniflo-bearbeiten": "\nEdit Omniflo element: %1", "cmd-attribute-bearbeiten": "\nEdit attributes: %1", "layer-prompt-ziel-layer": "\nSelect object on the target layer: ", @@ -499,6 +515,13 @@ "gf-status-z-hoehe-ks-aus": "\n Z height KS_AUS: %1 mm", "gf-status-fertig": "\n>>> Done! <<<", "gf-status-abgebrochen": "\nCancelled.", + "gf-alert-dialog-fehlt": "Dialog file not found:\n%1", + "gf-dlg-richtung-0": "0%1 (X+, East)", + "gf-dlg-richtung-90": "90%1 (Y+, North)", + "gf-dlg-richtung-180": "180%1 (X-, West)", + "gf-dlg-richtung-270": "270%1 (Y-, South)", + "gf-dlg-links": "Left", + "gf-dlg-rechts": "Right", "gf-status-gf-block-erstellt": "\n>>> GF block created: %1 (%2)", "gf-status-schritt1-aus": "\n\n1/4: %1 travel dir=%2%3 KS_EIN-hz=%4%5 v=%6%7", "gf-status-schritt2-staustrecke": "\n\n2/4: Buffer section L=%1 mm hz=%2%3 v=%4%5", diff --git a/menu/SSG_LIB.mnu b/menu/SSG_LIB.mnu index 4aa84ec..6a5b2d9 100644 --- a/menu/SSG_LIB.mnu +++ b/menu/SSG_LIB.mnu @@ -144,5 +144,7 @@ [SSG_LIB Edit Context] [Kreisel bearbeiten](ssg-ensure "KreiselInsert") KreiselEdit [Omniflo Element bearbeiten](ssg-ensure "OmniModulInsert") OMNI_EDIT +[Gefaellestrecke bearbeiten](ssg-ensure "Gefaellestrecke") GEFAELLESTRECKE_EDIT +[VarioFoerderer bearbeiten](ssg-ensure "VarioFoerderer") VARIOFOERDERER_EDIT [--] [SSG Bearbeiten (Auto)](ssg-ensure "SSG_LIB_Commands") SSG_BLOCKEDIT diff --git a/tests/test_gefaellestrecke.lsp b/tests/test_gefaellestrecke.lsp index 7818e6b..d0dc5fc 100644 --- a/tests/test_gefaellestrecke.lsp +++ b/tests/test_gefaellestrecke.lsp @@ -25,8 +25,12 @@ ;; --- Ergebnis eines Testfalls als JSON-String erzeugen --- -(defun gefaellestrecke:test-json (test-id modus hz deltaL winkel h-von h-bis d-h status / +;; as-winkel/es-winkel: "30" oder "90" (Element-Variante); default "90" +(defun gefaellestrecke:test-json (test-id modus hz deltaL winkel h-von h-bis d-h status + as-winkel es-winkel / json) + (if (null as-winkel) (setq as-winkel "90")) + (if (null es-winkel) (setq es-winkel "90")) (setq json (strcat " {\n" " \"test_id\": \"" test-id "\",\n" @@ -34,6 +38,8 @@ " \"hz_grad\": " (rtos (float hz) 2 1) ",\n" " \"deltaL_mm\": " (rtos (float deltaL) 2 0) ",\n" " \"winkel_grad\": " (rtos (float winkel) 2 3) ",\n" + " \"as_winkel\": \"" as-winkel "\",\n" + " \"es_winkel\": \"" es-winkel "\",\n" " \"hoehe_von_mm\": " (rtos (float h-von) 2 0) ",\n" " \"hoehe_bis_mm\": " (rtos (float h-bis) 2 0) ",\n" " \"delta_h_mm\": " (rtos (float d-h) 2 0) ",\n" @@ -296,6 +302,7 @@ m2-start m2-deltaH m2-deltaL m3-start m3-end m3-line-vla m3b-start m3b-end m3b-e1 m3b-e2 m3b-e3 m3b-objliste + w30-rad w30-sepx w30-lstau lbl-layer lbl-h lbl-h2) ;; Block-Bibliothek initialisieren (setzt aus-dx, ein-dx, aus-dz, ein-dz usw.) @@ -376,7 +383,7 @@ (setq gf-ins (car (gefaellestrecke-einfuegen test-L_stau test-winkel (list 0 0 test-z-start) - "links" "links" 0.0 test-deltaL))) + "links" "links" 0.0 test-deltaL "90" "90"))) (if gf-ins (progn (setq attribs (ssg-attrib-read gf-ins)) @@ -388,13 +395,13 @@ " dH=" (itoa d-h) " mm W=" w-str " OK")) (setq results-list (cons (gefaellestrecke:test-json "M1_hz000" 1 0.0 test-deltaL - (atof w-str) h-von h-bis d-h "GEBAUT") + (atof w-str) h-von h-bis d-h "GEBAUT" "90" "90") results-list))) (progn (princ " FEHLER") (setq results-list (cons (gefaellestrecke:test-json "M1_hz000" 1 0.0 test-deltaL - 0.0 0 0 0 "FEHLER") + 0.0 0 0 0 "FEHLER" "90" "90") results-list)))) ;; --- M1: hz=90 Grad (Nord), Start (15000, 0, 5000) --- @@ -408,7 +415,7 @@ (setq gf-ins (car (gefaellestrecke-einfuegen test-L_stau test-winkel (list 15000 0 test-z-start) - "links" "links" 90.0 test-deltaL))) + "links" "links" 90.0 test-deltaL "90" "90"))) (if gf-ins (progn (setq attribs (ssg-attrib-read gf-ins)) @@ -420,13 +427,13 @@ " dH=" (itoa d-h) " mm W=" w-str " OK")) (setq results-list (cons (gefaellestrecke:test-json "M1_hz090" 1 90.0 test-deltaL - (atof w-str) h-von h-bis d-h "GEBAUT") + (atof w-str) h-von h-bis d-h "GEBAUT" "90" "90") results-list))) (progn (princ " FEHLER") (setq results-list (cons (gefaellestrecke:test-json "M1_hz090" 1 90.0 test-deltaL - 0.0 0 0 0 "FEHLER") + 0.0 0 0 0 "FEHLER" "90" "90") results-list)))) ;; --- M1: hz=180 Grad (West), Start (0, -15000, 5000) --- @@ -440,7 +447,7 @@ (setq gf-ins (car (gefaellestrecke-einfuegen test-L_stau test-winkel (list 0 -15000 test-z-start) - "links" "links" 180.0 test-deltaL))) + "links" "links" 180.0 test-deltaL "90" "90"))) (if gf-ins (progn (setq attribs (ssg-attrib-read gf-ins)) @@ -452,13 +459,13 @@ " dH=" (itoa d-h) " mm W=" w-str " OK")) (setq results-list (cons (gefaellestrecke:test-json "M1_hz180" 1 180.0 test-deltaL - (atof w-str) h-von h-bis d-h "GEBAUT") + (atof w-str) h-von h-bis d-h "GEBAUT" "90" "90") results-list))) (progn (princ " FEHLER") (setq results-list (cons (gefaellestrecke:test-json "M1_hz180" 1 180.0 test-deltaL - 0.0 0 0 0 "FEHLER") + 0.0 0 0 0 "FEHLER" "90" "90") results-list)))) ;; --- M1: hz=270 Grad (Sued), Start (15000, -15000, 5000) --- @@ -472,7 +479,7 @@ (setq gf-ins (car (gefaellestrecke-einfuegen test-L_stau test-winkel (list 15000 -15000 test-z-start) - "links" "links" 270.0 test-deltaL))) + "links" "links" 270.0 test-deltaL "90" "90"))) (if gf-ins (progn (setq attribs (ssg-attrib-read gf-ins)) @@ -484,13 +491,13 @@ " dH=" (itoa d-h) " mm W=" w-str " OK")) (setq results-list (cons (gefaellestrecke:test-json "M1_hz270" 1 270.0 test-deltaL - (atof w-str) h-von h-bis d-h "GEBAUT") + (atof w-str) h-von h-bis d-h "GEBAUT" "90" "90") results-list))) (progn (princ " FEHLER") (setq results-list (cons (gefaellestrecke:test-json "M1_hz270" 1 270.0 test-deltaL - 0.0 0 0 0 "FEHLER") + 0.0 0 0 0 "FEHLER" "90" "90") results-list)))) ;; ============================================================ @@ -522,7 +529,7 @@ (princ (strcat "\n\n M2_hz000: Start=(0,-35000," (rtos test-z-start 2 0) ") ...")) (setq ergebnis (gefaellestrecke-einfuegen - test-L_stau test-winkel m2-start "links" "links" 0.0 test-deltaL)) + test-L_stau test-winkel m2-start "links" "links" 0.0 test-deltaL "90" "90")) (setq gf-ins (car ergebnis)) (if gf-ins (progn @@ -541,7 +548,7 @@ " mm W=" w-str " OK")) (setq results-list (cons (gefaellestrecke:test-json "M2_hz000" 2 0.0 test-deltaL - (atof w-str) h-von h-bis m2-deltaH "GEBAUT") + (atof w-str) h-von h-bis m2-deltaH "GEBAUT" "90" "90") results-list))) (progn ;; Fallback: sin(3deg) * 10000 ≈ 523mm @@ -550,7 +557,7 @@ (princ (strcat " FEHLER (Fallback " delta-sym "H=" (rtos m2-deltaH 2 3) ")")) (setq results-list (cons (gefaellestrecke:test-json "M2_hz000" 2 0.0 test-deltaL - 0.0 0 0 0 "FEHLER") + 0.0 0 0 0 "FEHLER" "90" "90") results-list)))) ;; ============================================================ @@ -618,20 +625,20 @@ " W=" w-str " OK")) (setq results-list (cons (gefaellestrecke:test-json "M3_hz270" 3 270.0 test-deltaL - (atof w-str) h-von h-bis d-h "GEBAUT") + (atof w-str) h-von h-bis d-h "GEBAUT" "90" "90") results-list))) (progn (princ "\n -> FEHLER (modus3-einfuegen)") (setq results-list (cons (gefaellestrecke:test-json "M3_hz270" 3 270.0 test-deltaL - 0.0 0 0 0 "FEHLER") + 0.0 0 0 0 "FEHLER" "90" "90") results-list))))) ;; 3D-Linie konnte nicht erzeugt werden (progn (princ "\n FEHLER: 3D-Linie nicht erzeugbar!") (setq results-list (cons (gefaellestrecke:test-json "M3_hz270" 3 270.0 test-deltaL - 0.0 0 0 0 "FEHLER_LINIE") + 0.0 0 0 0 "FEHLER_LINIE" "90" "90") results-list)))) ;; ============================================================ @@ -688,15 +695,71 @@ " " delta-sym "H-Soll=200 mm W=" w-str " OK")) (setq results-list (cons (gefaellestrecke:test-json "M3_Bogen" 3 0.0 14785.0 - (atof w-str) h-von h-bis d-h "GEBAUT") + (atof w-str) h-von h-bis d-h "GEBAUT" "90" "90") results-list))) (progn (princ "\n -> FEHLER (modus3-einfuegen mit Bogen)") (setq results-list (cons (gefaellestrecke:test-json "M3_Bogen" 3 0.0 14785.0 - 0.0 0 0 0 "FEHLER") + 0.0 0 0 0 "FEHLER" "90" "90") results-list)))) + ;; ============================================================ + ;; MODUS 1 - 30-Grad AUS-/EIN-Elemente (statt 90 Grad) + ;; Prueft die per DCL-Dialog waehlbare Element-Variante 30 Grad. + ;; hz=0, Start (30000, 0, 5000). Eigene L_stau-Berechnung, da die + ;; 30-Grad-Elemente andere aus-dx/ein-dx-Masse besitzen. + ;; ============================================================ + (princ "\n\n================================================================") + (princ "\n MODUS 1 - 30-Grad Elemente (M1_w30)") + (princ "\n================================================================") + + (entmake (list '(0 . "TEXT") + (cons 10 (list 30000 1200.0 test-z-start)) + (cons 11 (list 30000 1200.0 test-z-start)) + (cons 40 lbl-h) + (cons 1 (strcat "M1 | hz=0" grad-zeichen " | AS/ES=30" grad-zeichen + " | " delta-sym "L=10000 | W=3" grad-zeichen)) + '(7 . "Standard") (cons 8 lbl-layer) '(72 . 1) '(73 . 2))) + + ;; 30-Grad-Element-Masse setzen und L_stau dafuer berechnen + (vf-set-as-masse "30" "links") + (vf-set-es-masse "30" "links") + (setq w30-rad (* test-winkel (/ pi 180.0))) + (setq w30-sepx (* 300.0 (cos w30-rad))) + (setq w30-lstau + (/ (- test-deltaL (float aus-dx) (float ein-dx) w30-sepx) (cos w30-rad))) + (princ (strcat "\n\n M1_w30: AS/ES=30 Grad, aus-dx=" (rtos (float aus-dx) 2 1) + " ein-dx=" (rtos (float ein-dx) 2 1) + " L_stau=" (rtos w30-lstau 2 1) " ...")) + (setq gf-ins + (car (gefaellestrecke-einfuegen + w30-lstau test-winkel (list 30000 0 test-z-start) + "links" "links" 0.0 test-deltaL "30" "30"))) + (if gf-ins + (progn + (setq attribs (ssg-attrib-read gf-ins)) + (setq h-von (atoi (cdr (assoc "HOEHE_VON_mm" attribs)))) + (setq h-bis (atoi (cdr (assoc "HOEHE_BIS_mm" attribs)))) + (setq d-h (abs (- h-von h-bis))) + (setq w-str (cdr (assoc "GF_WINKEL" attribs))) + (princ (strcat " von=" (itoa h-von) " bis=" (itoa h-bis) + " dH=" (itoa d-h) " mm W=" w-str " OK")) + (setq results-list + (cons (gefaellestrecke:test-json "M1_w30" 1 0.0 test-deltaL + (atof w-str) h-von h-bis d-h "GEBAUT" "30" "30") + results-list))) + (progn + (princ " FEHLER") + (setq results-list + (cons (gefaellestrecke:test-json "M1_w30" 1 0.0 test-deltaL + 0.0 0 0 0 "FEHLER" "30" "30") + results-list)))) + + ;; Element-Masse wieder auf 90 Grad zuruecksetzen (fuer Folgelaeufe) + (vf-set-as-masse "90" "links") + (vf-set-es-masse "90" "links") + ;; ============================================================ ;; Zusammenfassung und Export ;; ============================================================ diff --git a/tests/test_gefaellestrecke.py b/tests/test_gefaellestrecke.py index b2357b6..59fb4d5 100644 --- a/tests/test_gefaellestrecke.py +++ b/tests/test_gefaellestrecke.py @@ -82,14 +82,30 @@ class TestGefaellestreckeStatus: ) def test_alle_modus1_gebaut(self): - """Alle vier Modus-1-Richtungen (hz=0/90/180/270) muessen GEBAUT sein.""" + """Alle Modus-1-Testfaelle (Richtungs-Sweep + 30-Grad-Variante) muessen GEBAUT sein.""" + erwartet = [t["test_id"] for t in self.tests if t["modus"] == 1] modus1 = [r for r in self.results if r["modus"] == 1] - assert len(modus1) == 4, f"Erwartet 4 Modus-1-Testfaelle, gefunden {len(modus1)}" + assert len(modus1) == len(erwartet), ( + f"Erwartet {len(erwartet)} Modus-1-Testfaelle, gefunden {len(modus1)}" + ) for r in modus1: assert r["status"] == "GEBAUT", ( f'{r["test_id"]}: erwartet GEBAUT, ist "{r["status"]}"' ) + def test_modus1_30grad_gebaut(self): + """Die per Dialog waehlbare 30-Grad-Element-Variante (M1_w30) muss GEBAUT sein.""" + r = self.results_by_id.get("M1_w30") + if r is None: + pytest.skip("M1_w30 nicht in Ergebnissen (aeltere Testdaten?)") + assert r["status"] == "GEBAUT", ( + f'M1_w30: erwartet GEBAUT, ist "{r["status"]}"' + ) + assert r.get("as_winkel") == "30" and r.get("es_winkel") == "30", ( + f'M1_w30: Element-Winkel sollten 30 sein, sind ' + f'AS={r.get("as_winkel")} ES={r.get("es_winkel")}' + ) + def test_modus2_und_modus3_gebaut(self): """Modus 2 und Modus 3 muessen GEBAUT sein.""" for test_id in ("M2_hz000", "M3_hz270"): @@ -179,10 +195,12 @@ class TestGefaellestreckeModusKonsistenz: self.tests_by_id = {t["test_id"]: t for t in self.testdata if "test_id" in t} def test_modus1_richtungsunabhaengig(self): - """Alle vier Modus-1-Richtungen muessen dieselbe Hoehe/Winkel liefern, - da nur die Fahrtrichtung (hz), nicht die Geometrie variiert.""" + """Die vier Modus-1-Richtungen (90-Grad-Elemente) muessen dieselbe Hoehe/Winkel + liefern, da nur die Fahrtrichtung (hz), nicht die Geometrie variiert. Die + 30-Grad-Variante (M1_w30) ist bewusst ausgenommen (andere Element-Masse).""" modus1 = [r for r in self.results - if r["modus"] == 1 and r["status"] == "GEBAUT"] + if r["modus"] == 1 and r["status"] == "GEBAUT" + and r.get("as_winkel", "90") == "90"] assert len(modus1) >= 2, "Zu wenige Modus-1-Ergebnisse fuer Vergleich" referenz = modus1[0] for r in modus1[1:]: diff --git a/tests/testdata/gefaellestrecke_tests.json b/tests/testdata/gefaellestrecke_tests.json index 2451942..74f9328 100644 --- a/tests/testdata/gefaellestrecke_tests.json +++ b/tests/testdata/gefaellestrecke_tests.json @@ -10,7 +10,9 @@ "hz_grad": 0.0, "start_mm": [0, 0, 5000], "as_seite": "links", - "es_seite": "links" + "es_seite": "links", + "as_winkel": "90", + "es_winkel": "90" }, { "test_id": "M1_hz090", @@ -18,7 +20,9 @@ "hz_grad": 90.0, "start_mm": [15000, 0, 5000], "as_seite": "links", - "es_seite": "links" + "es_seite": "links", + "as_winkel": "90", + "es_winkel": "90" }, { "test_id": "M1_hz180", @@ -26,7 +30,9 @@ "hz_grad": 180.0, "start_mm": [0, -15000, 5000], "as_seite": "links", - "es_seite": "links" + "es_seite": "links", + "as_winkel": "90", + "es_winkel": "90" }, { "test_id": "M1_hz270", @@ -34,7 +40,20 @@ "hz_grad": 270.0, "start_mm": [15000, -15000, 5000], "as_seite": "links", - "es_seite": "links" + "es_seite": "links", + "as_winkel": "90", + "es_winkel": "90" + }, + { + "test_id": "M1_w30", + "modus": 1, + "hz_grad": 0.0, + "start_mm": [30000, 0, 5000], + "as_seite": "links", + "es_seite": "links", + "as_winkel": "30", + "es_winkel": "30", + "beschreibung": "Modus 1 mit 30-Grad AUS-/EIN-Elementen (statt 90 Grad)" }, { "test_id": "M2_hz000", @@ -44,7 +63,9 @@ "linie_start_mm": [0, -35000, 0], "linie_ende_mm": [10000, -35000, 0], "as_seite": "links", - "es_seite": "links" + "es_seite": "links", + "as_winkel": "90", + "es_winkel": "90" }, { "test_id": "M3_hz270", @@ -54,7 +75,9 @@ "deltaH_quelle": "M2_hz000", "deltaL_quelle": "M2_hz000", "as_seite": "links", - "es_seite": "links" + "es_seite": "links", + "as_winkel": "90", + "es_winkel": "90" }, { "test_id": "M3_Bogen", @@ -64,6 +87,8 @@ "ende_mm": [38500, -43500, 1800], "bogen": {"winkel_grad": 90, "radius_mm": 500, "seite": "links"}, "as_seite": "links", - "es_seite": "links" + "es_seite": "links", + "as_winkel": "90", + "es_winkel": "90" } ] From 903e086e7f58b924fbdac8e30349ab3a20df57e0 Mon Sep 17 00:00:00 2001 From: Michael Stangl Date: Mon, 20 Jul 2026 16:46:16 +0200 Subject: [PATCH 3/3] =?UTF-8?q?Bezeichnung=20statt=20Name=20bei=20ILS=20KR?= =?UTF-8?q?eisel=20und=20Eckrad=20verwendet.=20Eigene=20Spalte=20f=C3=BCr?= =?UTF-8?q?=20Bezeichnung=20im=20Sivas=20Export=20dazu=20gebaut?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lisp/KreiselInsert.lsp | 16 ++++++++-------- lib/export_csv.py | 2 +- lib/export_sivas.py | 23 +++++++++++++++++++++-- tests/reference/kreisel_results.json | 16 ++++++++-------- tests/test_foerderer.py | 10 +++++----- 5 files changed, 43 insertions(+), 24 deletions(-) diff --git a/Lisp/KreiselInsert.lsp b/Lisp/KreiselInsert.lsp index cb8c6a3..d4dde50 100644 --- a/Lisp/KreiselInsert.lsp +++ b/Lisp/KreiselInsert.lsp @@ -72,7 +72,7 @@ ;; Attribut-Definitionen: ((TAG DEFAULT) ...) (setq *kreisel-attrib-defs* - '(("NAME" "") + '(("Bezeichnung" "") ("DREHRICHTUNG" "UZS") ("ANZAHL_SEPARATOR" "2") ("KREISELART" "STANDARD") @@ -90,7 +90,7 @@ ;; Eckrad Attribut-Definitionen: ((TAG DEFAULT) ...) (setq *eckrad-attrib-defs* - '(("NAME" "") + '(("Bezeichnung" "") ("DREHRICHTUNG" "UZS") ("DREHUNG" "0") ("NUMMER" "0") @@ -325,9 +325,9 @@ ) ) - ;; NAME automatisch setzen: "KreiselN" - (setq attribs (subst (cons "NAME" (strcat "Kreisel" (itoa kreisel-nummer))) - (assoc "NAME" attribs) attribs)) + ;; Bezeichnung automatisch setzen: "KreiselN" + (setq attribs (subst (cons "Bezeichnung" (strcat "Kreisel" (itoa kreisel-nummer))) + (assoc "Bezeichnung" attribs) attribs)) ;; --- HÖHE BESTIMMEN --- ;; Priorität 1: Aus basePoint (wenn 3D Punkt mit Z > 0) @@ -928,7 +928,7 @@ ) ;; Tiles initialisieren - (set_tile "name" (cdr (assoc "NAME" attribs))) + (set_tile "name" (cdr (assoc "Bezeichnung" attribs))) (set_tile "abstand" (cdr (assoc "ABSTAND" attribs))) (set_tile "hoehe" (cdr (assoc "HOEHE" attribs))) @@ -976,8 +976,8 @@ (setq newRotation (kreisel-idx-to-rotation (atoi dlg-ausrichtung))) ;; Attribute aktualisieren - (setq attribs (subst (cons "NAME" dlg-name) - (assoc "NAME" attribs) attribs)) + (setq attribs (subst (cons "Bezeichnung" dlg-name) + (assoc "Bezeichnung" attribs) attribs)) (setq attribs (subst (cons "HOEHE" dlg-hoehe) (assoc "HOEHE" attribs) attribs)) (setq attribs (subst (cons "KREISELART" diff --git a/lib/export_csv.py b/lib/export_csv.py index e416c54..4865dce 100644 --- a/lib/export_csv.py +++ b/lib/export_csv.py @@ -214,7 +214,7 @@ def build_kreisel_merkmale(block): "Höhe in m": hoehe_m, "Drehrichtung": attribs.get("DREHRICHTUNG", "UZS"), "Drehung": block.get("rotation", 0.0), - "Name": attribs.get("NAME", ""), + "Name": attribs.get("Bezeichnung", ""), } diff --git a/lib/export_sivas.py b/lib/export_sivas.py index 288fbc4..b01fefc 100644 --- a/lib/export_sivas.py +++ b/lib/export_sivas.py @@ -4,7 +4,12 @@ export_sivas.py - Erzeugt Sivas-Export-CSV mit gruppierten Zeilen und Summierungszeilen. Neues CSV-Format: - Nr;TeileArt;SivasNummer;Dispogruppe;Anzahl;Laenge[mm];Hoehe[m];details + Nr;TeileArt;SivasNummer;Bezeichnung;Dispogruppe;Anzahl;Laenge[mm];Hoehe[m];details + + - Bezeichnung: Wert des Attributs "Bezeichnung" (Kreisel/Eckrad/VF/GF/Strecke). + Bei gruppierten Zeilen (mehrere Bloecke je Zeile) kommasepariert. + Bei Omniflo-Zeilen (Bogen/Weiche/Gerade) und der Automation-Summenzeile + bleibt das Feld leer, da es sich um Sammlungen gleichartiger Objekte handelt. - Omniflo Bogen/Weiche: gruppiert nach SivasNummer, Anzahl summiert - Omniflo Gerade: gruppiert nach ARTINR-Attribut, Laenge[mm] summiert @@ -271,7 +276,9 @@ def process_blocks(blocks, lookup): gerade_groups = {} kreisel_list = [] # Liste von blocks eckrad_ids = [] # IDs aller Eckrad-Bloecke + eckrad_bezeichnungen = [] # Bezeichnung-Attribut aller Eckrad-Bloecke strecke_ids = [] # IDs loser Strecke-Elemente (Vario/Staustrecke ausserhalb VF_N) + strecke_bezeichnungen = [] # Bezeichnung-Attribut loser Strecke-Elemente vario_list = [] # Liste von VarioFoerderer-Compound-Bloecken (VF_N), je einer Zeile gefaelle_list = [] # Liste von Gefaellestrecke-Bloecken (GF_*), je einer Zeile @@ -372,6 +379,7 @@ def process_blocks(blocks, lookup): if matches_any(bname, BLOCKPATTERNS.get("pattern_eckrad", [])): counters["anzahl_eckraeder"] += 1 eckrad_ids.append(get_id(block)) + eckrad_bezeichnungen.append(block.get("attribs", {}).get("Bezeichnung", "")) continue # --- ILS Kreisel --- @@ -443,6 +451,7 @@ def process_blocks(blocks, lookup): if matches_any(bname, BLOCKPATTERNS.get("pattern_strecke_module", [])): counters["anzahl_variofoerderer"] += 1 strecke_ids.append(get_id(block)) + strecke_bezeichnungen.append(block.get("attribs", {}).get("Bezeichnung", "")) # --- Zeilen aufbauen --- items = [] @@ -454,6 +463,7 @@ def process_blocks(blocks, lookup): "teileart": "Omniflo Bogen", "sivasnr": sivasnr, "sivasnr_quoted": False, + "bezeichnung": "", "dispgruppe": 10, "anzahl": g["anzahl"], "laenge_mm": "", @@ -468,6 +478,7 @@ def process_blocks(blocks, lookup): "teileart": "Omniflo Weiche", "sivasnr": sivasnr, "sivasnr_quoted": False, + "bezeichnung": "", "dispgruppe": 10, "anzahl": g["anzahl"], "laenge_mm": "", @@ -482,6 +493,7 @@ def process_blocks(blocks, lookup): "teileart": "Omniflo Gerade", "sivasnr": g["artinr"], "sivasnr_quoted": True, + "bezeichnung": "", "dispgruppe": 10, "anzahl": g["anzahl"], "laenge_mm": int(g["laenge_mm"]), @@ -499,6 +511,7 @@ def process_blocks(blocks, lookup): "teileart": teileart, "sivasnr": sivasnr, "sivasnr_quoted": True, + "bezeichnung": block.get("attribs", {}).get("Bezeichnung", ""), "dispgruppe": 20, "anzahl": 1, "laenge_mm": "", @@ -514,6 +527,7 @@ def process_blocks(blocks, lookup): "teileart": teileart, "sivasnr": sivasnr, "sivasnr_quoted": True, + "bezeichnung": ", ".join(b for b in eckrad_bezeichnungen if b), "dispgruppe": 20, "anzahl": counters["anzahl_eckraeder"], "laenge_mm": "", @@ -532,6 +546,7 @@ def process_blocks(blocks, lookup): "teileart": teileart, "sivasnr": sivasnr, "sivasnr_quoted": True, + "bezeichnung": ", ".join(b for b in strecke_bezeichnungen if b), "dispgruppe": 20, "anzahl": len(strecke_ids), "laenge_mm": "", @@ -549,6 +564,7 @@ def process_blocks(blocks, lookup): "teileart": teileart, "sivasnr": sivasnr, "sivasnr_quoted": True, + "bezeichnung": block.get("attribs", {}).get("Bezeichnung", ""), "dispgruppe": 20, "anzahl": 1, "laenge_mm": "", @@ -578,6 +594,7 @@ def process_blocks(blocks, lookup): "teileart": teileart, "sivasnr": sivasnr, "sivasnr_quoted": True, + "bezeichnung": ", ".join(b for b in group["bezeichnungen"] if b), "dispgruppe": 20, "anzahl": len(group["ids"]), "laenge_mm": "", @@ -620,6 +637,7 @@ def process_blocks(blocks, lookup): "teileart": auto_teileart, "sivasnr": auto_sivasnr, "sivasnr_quoted": True, + "bezeichnung": "", "dispgruppe": 20, "anzahl": 1, "laenge_mm": "", @@ -638,6 +656,7 @@ def format_csv_line(item): f'{item["nr"]}' f';"{item["teileart"]}"' f';{sivasnr}' + f';"{item["bezeichnung"]}"' f';{item["dispgruppe"]}' f';{item["anzahl"]}' f';{item["laenge_mm"]}' @@ -669,7 +688,7 @@ def main(): items, _ = process_blocks(blocks, lookup) - header = "Nr;TeileArt;SivasNummer;Dispogruppe;Anzahl;Laenge[mm];Hoehe[m];details" + header = "Nr;TeileArt;SivasNummer;Bezeichnung;Dispogruppe;Anzahl;Laenge[mm];Hoehe[m];details" with open(output_csv, "w", encoding="utf-8") as f: f.write(header + "\n") for item in items: diff --git a/tests/reference/kreisel_results.json b/tests/reference/kreisel_results.json index 0b5ada1..e32a8c7 100644 --- a/tests/reference/kreisel_results.json +++ b/tests/reference/kreisel_results.json @@ -6,7 +6,7 @@ "block_handle": "11A", "insert_point": [500, 500, 2500], "actual_attributes": { - "NAME": "Kreisel1", + "Bezeichnung": "Kreisel1", "DREHRICHTUNG": "UZS", "N_SEPARATOREN": "2", "KREISELART": "STANDARD", @@ -27,7 +27,7 @@ "block_handle": "14A", "insert_point": [-500, 500, 2500], "actual_attributes": { - "NAME": "Kreisel2", + "Bezeichnung": "Kreisel2", "DREHRICHTUNG": "UZS", "N_SEPARATOREN": "2", "KREISELART": "STANDARD", @@ -48,7 +48,7 @@ "block_handle": "17A", "insert_point": [-500, -500, 2500], "actual_attributes": { - "NAME": "Kreisel3", + "Bezeichnung": "Kreisel3", "DREHRICHTUNG": "UZS", "N_SEPARATOREN": "2", "KREISELART": "STANDARD", @@ -69,7 +69,7 @@ "block_handle": "1AA", "insert_point": [500, -500, 2500], "actual_attributes": { - "NAME": "Kreisel4", + "Bezeichnung": "Kreisel4", "DREHRICHTUNG": "UZS", "N_SEPARATOREN": "2", "KREISELART": "STANDARD", @@ -90,7 +90,7 @@ "block_handle": "1DA", "insert_point": [3000, 3000, 3000], "actual_attributes": { - "NAME": "Kreisel5", + "Bezeichnung": "Kreisel5", "DREHRICHTUNG": "UZS", "N_SEPARATOREN": "2", "KREISELART": "STANDARD", @@ -111,7 +111,7 @@ "block_handle": "20F", "insert_point": [500, 6000, 1500], "actual_attributes": { - "NAME": "Kreisel6", + "Bezeichnung": "Kreisel6", "DREHRICHTUNG": "UZS", "N_SEPARATOREN": "2", "KREISELART": "PIN", @@ -132,7 +132,7 @@ "block_handle": "243", "insert_point": [-500, 6500, 1500], "actual_attributes": { - "NAME": "Kreisel7", + "Bezeichnung": "Kreisel7", "DREHRICHTUNG": "UZS", "N_SEPARATOREN": "2", "KREISELART": "PIN", @@ -153,7 +153,7 @@ "block_handle": "277", "insert_point": [3000, 8000, 1500], "actual_attributes": { - "NAME": "Kreisel8", + "Bezeichnung": "Kreisel8", "DREHRICHTUNG": "UZS", "N_SEPARATOREN": "2", "KREISELART": "PIN", diff --git a/tests/test_foerderer.py b/tests/test_foerderer.py index 29a28de..7515323 100644 --- a/tests/test_foerderer.py +++ b/tests/test_foerderer.py @@ -289,7 +289,7 @@ class TestFoerdererExportSivas: def test_header_felder(self): """Sivas-CSV-Header muss korrekte Spalten haben.""" - expected = ["Nr", "TeileArt", "SivasNummer", + expected = ["Nr", "TeileArt", "SivasNummer", "Bezeichnung", "Dispogruppe", "Anzahl", "Laenge[mm]", "Hoehe[m]", "details"] for i, name in enumerate(expected): assert self.header[i] == name, ( @@ -317,10 +317,10 @@ class TestFoerdererExportSivas: def test_details_sind_valides_json(self): """details-Spalte muss valides JSON enthalten.""" for i, row in enumerate(self.rows): - if len(row) < 8: + if len(row) < 9: continue try: - json.loads(row[7]) + json.loads(row[8]) except json.JSONDecodeError as e: pytest.fail(f"Zeile {i + 2}: details ist kein valides JSON: {e}") @@ -330,10 +330,10 @@ class TestFoerdererExportSivas: "Laengen aller Variofoerderer", "Anzahl aller Separatoren"} for row in self.rows: - if len(row) < 8: + if len(row) < 9: continue if row[1].strip('"') == "ILS 2.0 Automation und Pneumatik - Gesamtanlage": - details = json.loads(row[7]) + details = json.loads(row[8]) missing = required - set(details.keys()) assert not missing, ( f"Automation-details: fehlende Felder: {missing}"