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/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/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 3c4f3fd..e71d7a2 100644 --- a/Lisp/SSG_LIB_Commands.lsp +++ b/Lisp/SSG_LIB_Commands.lsp @@ -268,6 +268,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 601ba84..3e59657 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/data/dxf/834372115_symbol.dwg b/data/dxf/834372115_symbol.dwg deleted file mode 100644 index 2f563dd..0000000 Binary files a/data/dxf/834372115_symbol.dwg and /dev/null differ 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 7e648e4..595457c 100644 --- a/lang/de_DE.json +++ b/lang/de_DE.json @@ -361,6 +361,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.", @@ -439,6 +450,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: ", @@ -562,6 +578,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 838b2ee..fe53362 100644 --- a/lang/en_GB.json +++ b/lang/en_GB.json @@ -361,6 +361,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.", @@ -439,6 +450,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: ", @@ -562,6 +578,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/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/menu/SSG_LIB.mnu b/menu/SSG_LIB.mnu index 4505271..dacf030 100644 --- a/menu/SSG_LIB.mnu +++ b/menu/SSG_LIB.mnu @@ -147,5 +147,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/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}" 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" } ]