diff --git a/Lisp/Gefaellestrecke.lsp b/Lisp/Gefaellestrecke.lsp index b43f739..80df121 100644 --- a/Lisp/Gefaellestrecke.lsp +++ b/Lisp/Gefaellestrecke.lsp @@ -1497,10 +1497,10 @@ (list (cons "Bezeichnung" gf-bname) (cons "MONTAGEHOEHE_m" "0.000") - (cons "HOEHE_VON" (itoa (fix hoehe-von))) - (cons "HOEHE_BIS" (itoa (fix hoehe-bis))) - (cons "DELTA_H" (itoa (fix (abs deltaH)))) - (cons "DELTA_L" (itoa (fix deltaL))) + (cons "HOEHE_VON_mm" (itoa (fix hoehe-von))) + (cons "HOEHE_BIS_mm" (itoa (fix hoehe-bis))) + (cons "DELTA_H_mm" (itoa (fix (abs deltaH)))) + (cons "DELTA_L_mm" (itoa (fix deltaL))) (cons "TYP" typ-str) (cons "SEITE_AS" seite) (cons "SEITE_ES" es-seite) diff --git a/Lisp/ssg_core.lsp b/Lisp/ssg_core.lsp index 0191762..52870eb 100644 --- a/Lisp/ssg_core.lsp +++ b/Lisp/ssg_core.lsp @@ -390,15 +390,17 @@ ;; ============================================================ ;; Vorderteil (immer vorhanden). Bezeichnung = Blockname (GF_n/VF_n), ;; Artinr. fest 6220 (alle GF/VF). TYP-Wert wird zur Laufzeit gesetzt. +;; ID zuerst (global, jeder eingefuegte Baustein - Kreisel/GF/VF - erhaelt eine +;; neue ID), danach Bezeichnung (= Blockname). Hoehen/Delta mit Einheit _mm. (setq *strecke-attr-front* - '(("Bezeichnung" "") - ("ID" "") + '(("ID" "") + ("Bezeichnung" "") ("Artinr." "6220") ("MONTAGEHOEHE_m" "0.000") - ("HOEHE_VON" "0") - ("HOEHE_BIS" "0") - ("DELTA_H" "0") - ("DELTA_L" "0") + ("HOEHE_VON_mm" "0") + ("HOEHE_BIS_mm" "0") + ("DELTA_H_mm" "0") + ("DELTA_L_mm" "0") ("TYP" "Streckengruppe") ("SEITE_AS" "rechts") ("SEITE_ES" "rechts") diff --git a/Lisp/vf_core.lsp b/Lisp/vf_core.lsp index 0e84d36..bb9523d 100644 --- a/Lisp/vf_core.lsp +++ b/Lisp/vf_core.lsp @@ -1050,10 +1050,10 @@ (cons "Bezeichnung" vf-bname) ;; Artinr. bleibt auf Schema-Vorgabe "6220" (cons "MONTAGEHOEHE_m" (rtos montagehoehe-m 2 3)) - (cons "HOEHE_VON" (itoa (fix hoehe-von))) - (cons "HOEHE_BIS" (itoa (fix hoehe-bis))) - (cons "DELTA_H" (itoa (fix deltaH))) - (cons "DELTA_L" (itoa (fix deltaL))) + (cons "HOEHE_VON_mm" (itoa (fix hoehe-von))) + (cons "HOEHE_BIS_mm" (itoa (fix hoehe-bis))) + (cons "DELTA_H_mm" (itoa (fix deltaH))) + (cons "DELTA_L_mm" (itoa (fix deltaL))) (cons "TYP" "Streckengruppe") (cons "SEITE_AS" seite) (cons "SEITE_ES" seite) diff --git a/Lisp/vf_linienzug.lsp b/Lisp/vf_linienzug.lsp index d94da62..f130c61 100644 --- a/Lisp/vf_linienzug.lsp +++ b/Lisp/vf_linienzug.lsp @@ -76,6 +76,7 @@ *vfl-acc-motorseite* '() ; Seite ("rechts"/"links") je Motorstation *vfl-acc-gfbogen* '() ; Alist ("L_90".n ...) GF-Boegen *vfl-acc-variokurve* '() ; Alist ("A_90".n ...) Vario-Kurven + *vfl-ziel-punkt* nil ; Soll-ES-Punkt fuer Option-3-Ist-Ziel-Report *vfl-acc-separator* 0)) ; Anzahl eingefuegter Separatoren (300 mm) ;; Alist-Zaehler erhoehen / lesen @@ -147,7 +148,7 @@ ;; typ=nil : weder GF noch VF fuer dieses deltaL/deltaH geometrisch moeglich (defun vfl-segment-entscheidung (deltaL deltaH richtung / winkel-natuerlich wahl horizontal-info) - (if (not *lib-initialized*) (init-bibliothek)) + (if (or (not *lib-initialized*) (null bogen-auf)) (init-bibliothek)) (cond ;; Zu kurz fuer eine VF-Einheit: Umlenkstation (500 mm) + Motorstation ;; (500 mm) belegen zusammen 1000 mm deltaL - darunter ist kein VF baubar. @@ -235,9 +236,17 @@ ;; Fahrtrichtung, der Nutzer gibt praktisch nur die Laenge vor (eine gerade ;; Foerderstrecke kann die Richtung nicht aendern). Nur beim allerersten ;; Segment (hz-vorgabe=nil) definiert der gewaehlte Punkt die Richtung frei. +;; Punkt-Abfrage mit FESTER 2-Arity (Basispunkt + Prompt). Verhaltensneutraler +;; Wrapper um das variadische Built-in getpoint: mit Basispunkt (base) wird die +;; 2-Argument-Form genutzt (Gummiband), ohne (base=nil) die reine Prompt-Form. +;; Zweck: ein Testmock kann diese feste Signatur ersetzen (getpoint selbst kann +;; als defun nicht 1- UND 2-argumentig gemockt werden). +(defun vfl-getpoint (base prompt) + (if base (getpoint base prompt) (getpoint prompt))) + ;; Rueckgabe: (deltaL hz) oder nil bei Abbruch. (defun vfl-neue-linie-messen (p-akt hz-vorgabe / p2 rad ux uy) - (setq p2 (getpoint p-akt + (setq p2 (vfl-getpoint p-akt (if hz-vorgabe "\n\nEndpunkt entlang Fahrtrichtung waehlen (bestimmt die Laenge): " "\n\nEndpunkt der Linie (XY, beliebige Richtung): "))) @@ -280,11 +289,207 @@ ;; VF-Linien-Sub-Segments aus vfl-segment-entscheidung. ;; gf-am-ausgang: T => halbe Staustrecke als GF2 am Ausgang (ohne Separator), ;; nil => gesamte Staustrecke am Einlauf (GF1), kein GF2. -;; Rueckgabe: (frame anzahl-koerper) +;; Neigung des Frames aus der xu-Richtung ablesen: T => (nahezu) flach (0 Grad), +;; nil => auf 3-Grad-Basis. Damit wird der Uebergang auf_3/ab_3 nur dann gesetzt, +;; wenn wirklich ein Neigungswechsel noetig ist. +(defun vfl-frame-flach-p (frame) + (< (abs (cadr (frame->hz-winkel frame))) 1.5)) + +;; Separator (300 mm) HORIZONTAL (0 Grad) an einen Punkt anfuegen - fuer das +;; horizontale Stueck, wo der Separator in der 0-Grad-Ebene liegt (NICHT auf der +;; 3-Grad-Basis wie vfl-insert-separator). Rueckgabe: Endpunkt. +(defun vfl-sep-hz (pt hz / ep) + (princ "\n [horizontal] Separator (300 mm, 0 Grad)") + (setq ep (gf-insert-hz-with-ks "Staustrecke_Separator_SP_300_mm" pt hz 0 300 0)) + (setq *vfl-acc-separator* (1+ *vfl-acc-separator*)) + ep) + +;; Uebergang zurueck auf die 3-Grad-Basis, FALLS der Frame gerade flach (0 Grad) +;; ist: fuegt einen Vario_Bogen_ab_3 ein. Wird vor jedem 3-Grad-Element +;; (gewinkeltes VF, Motorstation, ES) aufgerufen, damit der ab_3-Uebergang erst +;; DANN kommt, wenn er wirklich gebraucht wird (die flache Zone bleibt sonst +;; flach). Ist der Frame schon auf 3-Grad-Basis, bleibt er unveraendert. +(defun vfl-nach-3grad (frame / hz m pt) + (if (vfl-frame-flach-p frame) + (progn + (setq hz (car (frame->hz-winkel frame))) + (setq m (get-bogen-mass bogen-ab 3)) + (princ "\n\nVario_Bogen_ab_3 (Uebergang horizontal -> 3 Grad)") + (setq pt (insert-rotated-block-with-ks "Vario_Bogen_ab_3" (car frame) + 0 (car m) (caddr m) hz)) + (vfl-frame-3grad pt hz)) + frame)) + +;; Horizontales Sub-Segment bauen. Die flache Zone (0 Grad) wird NICHT mehr +;; automatisch mit ab_3 auf die 3-Grad-Basis zurueckgefuehrt - das Stueck ENDET +;; FLACH. Der ab_3-Uebergang kommt erst, wenn ein 3-Grad-Element folgt +;; (vfl-nach-3grad). Der auf_3-Eintritt wird nur gesetzt, wenn der Frame noch +;; NICHT flach ist (sonst bleibt die laufende flache Zone erhalten). Separatoren +;; VOR/NACH liegen in der 0-Grad-Ebene. Rueckgabe: neuer Frame (flach, 0 Grad). +(defun vfl-baue-horizontal-koerper (frame hz dL / pt m1 sep-vor) + (setq pt (car frame)) + (princ "\n\nZusaetzlichen Separator VOR dem horizontalen Stueck?") + (princ "\n 1 - Ja\n 2 - Nein") + (setq sep-vor (= (getstring "\nIhre Wahl (1/2) [2]: ") "1")) + ;; auf_3-Eintritt nur, wenn noch NICHT flach (sonst flache Zone fortsetzen) + (if (not (vfl-frame-flach-p frame)) + (progn + (setq m1 (get-bogen-mass bogen-auf 3)) + (princ "\n\nVario_Bogen_auf_3 (Uebergang 3 Grad -> horizontal)") + (setq pt (insert-rotated-block-with-ks "Vario_Bogen_auf_3" pt + (ssg-cfg-or "vario" "gefaelle_winkel" 3) (car m1) (caddr m1) hz)))) + ;; optionaler Separator VOR - in der horizontalen Ebene (0 Grad) + (if sep-vor (setq pt (vfl-sep-hz pt hz))) + ;; horizontale Zwischenstrecke (0 Grad) + (princ (strcat "\n\nHorizontale Zwischenstrecke (0 Grad, L=" (rtos dL 2 2) " mm)")) + (setq pt (insert-inclined-scaled-block "Staustrecke_SP_1000_mm" pt dL 0 hz)) + (vfl-acc-vf-seg "horizontal" 0 dL) + ;; optionaler Separator NACH - ebenfalls horizontal (0 Grad) + (princ "\n\nZusaetzlichen Separator NACH dem horizontalen Stueck?") + (princ "\n 1 - Ja\n 2 - Nein") + (if (= (getstring "\nIhre Wahl (1/2) [2]: ") "1") (setq pt (vfl-sep-hz pt hz))) + ;; KEIN ab_3 mehr -> das Stueck ENDET FLACH (0 Grad) + (make-frame-from-dir pt (hz-winkel->xu hz 0.0)) +) + +;; ============================================================ +;; OPTION 3: KETTENENDE AUS LAUFENDER VF-EINHEIT (ein Motor) +;; ============================================================ +;; Zerlegt den verbleibenden GERADEN Lauf bis zum Ziel-ES mit der bewaehrten +;; STANDARD-Logik (wie aussen), aber fuer den Mid-Body-Fall: +;; - Der Einlauf (AS + GF1 + Separator + Umlenk) ist bereits gebaut und wird +;; NICHT beruecksichtigt -> aus-dx/aus-dz = 0. +;; - Fest vor dem ES stehen nur Motor(500) + Auslauf-Separator(300) +;; -> FESTE_HORIZONTAL = 800. +;; Ergebnis der Standard-Logik (GF2 = GF, VARIIERT mit dem Winkel): +;; steiler als 3 Grad / steigend -> gewinkeltes VF + GF2 (Winkeltabelle), +;; flacher als 3 Grad -> horizontales VF + GF2 (Rest-Laenge ueber +;; das horizontale Stueck ausgeglichen). +;; Die Hoehe kommt also aus VF-Winkel/horizontal + GF2, die ueberschuessige +;; Laenge aus dem horizontalen VF - genau die zweistufige Logik. +;; Rueckgabe: (typ winkel L_GF2 L_VF) - typ "GF"/"VF"/nil (wie +;; vfl-segment-entscheidung; L_GF2 varriert, Mindestwert siehe vfl-body-abschluss). +(defun vfl-body-zerlegung (deltaL deltaH richtung / + save-ausdx save-ausdz save-feste + winkel-natuerlich wahl horizontal-info res) + (if (or (not *lib-initialized*) (null bogen-auf)) (init-bibliothek)) + ;; Budget-Globals temporaer auf den Mid-Body-Fall umbiegen (mit Restore). + (setq save-ausdx aus-dx save-ausdz aus-dz save-feste FESTE_HORIZONTAL) + (setq aus-dx 0.0 aus-dz 0.0 FESTE_HORIZONTAL 800.0) + (setq res + (cond + ;; kuerzer als Motor(500)+Separator(300): kein Abschluss baubar + ((< deltaL 800.0) (list nil nil nil nil)) + ;; praktisch flach: horizontales VF + GF2 + ((< deltaH 1.0) + (setq horizontal-info (berechne-horizontale-mitte deltaL deltaH "Ab")) + (if (and horizontal-info (caddr horizontal-info)) + (list "VF" 0 (car horizontal-info) (cadr horizontal-info)) + (list nil nil nil nil))) + ;; steigend: nur gewinkeltes VF + GF2 moeglich + ((= richtung "Auf") + (setq wahl (vfl-waehle-winkel (nth 3 (berechne-alle-winkel deltaL deltaH "Auf" 800.0)))) + (if wahl (list "VF" (nth 0 wahl) (nth 1 wahl) (nth 2 wahl)) (list nil nil nil nil))) + ;; fallend: natuerlichen Neigungswinkel gegen die 3-Grad-Eigenneigung pruefen + (t + (setq winkel-natuerlich (* (atan (/ deltaH deltaL)) (/ 180.0 pi))) + (cond + ;; ~3 Grad -> reine GF2 (kein Koerper, GF2 traegt Laenge + 3-Grad-Absenkung) + ((<= (abs (- winkel-natuerlich 3.0)) *vfl-gf-winkel-toleranz*) + (list "GF" 3.0 nil nil)) + ;; steiler als 3 Grad -> Stufe 1: gewinkeltes VF + GF2 (GF2 variiert mit Winkel) + ((> winkel-natuerlich 3.0) + (setq wahl (vfl-waehle-winkel (nth 3 (berechne-alle-winkel deltaL deltaH "Ab" 800.0)))) + (if wahl (list "VF" (nth 0 wahl) (nth 1 wahl) (nth 2 wahl)) (list nil nil nil nil))) + ;; flacher als 3 Grad -> Stufe 2: horizontales VF + GF2 + (t + (setq horizontal-info (berechne-horizontale-mitte deltaL deltaH "Ab")) + (if (and horizontal-info (caddr horizontal-info)) + (list "VF" 0 (car horizontal-info) (cadr horizontal-info)) + (progn + (setq wahl (vfl-waehle-winkel (nth 3 (berechne-alle-winkel deltaL deltaH "Ab" 800.0)))) + (if wahl (list "VF" (nth 0 wahl) (nth 1 wahl) (nth 2 wahl)) (list nil nil nil nil))))))))) + ;; Budget-Globals zuruecksetzen + (setq aus-dx save-ausdx aus-dz save-ausdz FESTE_HORIZONTAL save-feste) + res) + +;; Fragt Zielpunkt (XY) + Zielhoehe (Z) ab, zerlegt den Rest (vfl-body- +;; zerlegung, Standard-Logik) und baut den Koerper VOR dem Motor: gewinkeltes +;; VF (winkel>0) oder horizontales VF (winkel=0). GF2 (variiert mit dem Winkel, +;; Mindestwert *vfl-gf-min-laenge*=400) sitzt hinter dem Motor und wird als +;; Laenge zurueckgegeben (der Aufrufer setzt sie beim Auslauf ein und schliesst +;; mit Motor -> GF2 -> Separator -> ES ab). Speichert den Soll-Zielpunkt in +;; *vfl-ziel-punkt* fuer den Ist-Ziel-Report. +;; Rueckgabe: (frame anzahl-koerper letzt-hz gf2-laenge) oder nil. +(defun vfl-body-abschluss (frame letzt-hz p-umlenk / + linie-mess dL hzn hn dH richtn rad3 dec typ w lgf lvf + gf2 cnt p0 tx ty) + ;; Der Abschluss-Koerper (vfs-vf-koerper) und der Motor gehen von 3-Grad-Basis + ;; aus - eine gerade laufende flache Zone hier mit ab_3 abschliessen. + (setq frame (vfl-nach-3grad frame)) + (setq linie-mess (vfl-neue-linie-messen (car frame) letzt-hz)) + (if (null linie-mess) + (progn (princ "\n Abgebrochen - kein Zielpunkt gewaehlt.") nil) + (progn + (setq dL (car linie-mess) hzn (cadr linie-mess)) + (setq hn (getreal (strcat "\nHoehe (Z) des Kettenendes (ES-Element) [" + (rtos (caddr (car frame)) 2 1) "]: "))) + (if (null hn) (setq hn (caddr (car frame)))) + (setq dH (- hn (caddr (car frame)))) + (setq richtn (if (>= dH 0.0) "Auf" "Ab")) + (setq dH (abs dH)) + (setq rad3 (* (float (ssg-cfg-or "vario" "gefaelle_winkel" 3)) (/ pi 180.0))) + (princ (strcat "\n>>> Kettenende-Modus: Ziel dL=" (rtos dL 2 0) + " mm, dH=" (rtos dH 2 0) " mm (" richtn "). GF2 variiert mit dem Winkel.")) + (setq dec (vfl-body-zerlegung dL dH richtn)) + (setq typ (nth 0 dec) w (nth 1 dec) lgf (nth 2 dec) lvf (nth 3 dec)) + (if (null typ) + (progn + (princ (strcat "\n FEHLER: Rest nicht baubar (zu steil/zu kurz fuer" + " Schwanz Motor+GF2+Separator+ES). Bitte weiter zeichnen oder flacher planen.")) + nil) + (progn + ;; Soll-ES-Punkt fuer den Ist-Ziel-Report merken (Startpunkt + + ;; projizierter Laenge entlang der Fahrtrichtung). + (setq p0 (car frame)) + (setq tx (+ (car p0) (* dL (cos (* hzn (/ pi 180.0)))))) + (setq ty (+ (cadr p0) (* dL (sin (* hzn (/ pi 180.0)))))) + (setq *vfl-ziel-punkt* (list tx ty hn)) + (setq cnt 0) + ;; Koerper VOR dem Motor: winkel 0 = horizontales VF, sonst gewinkeltes VF. + ;; typ "GF" -> kein Koerper (Rest laeuft komplett ueber GF2). + (if (and (= typ "VF") lvf (> lvf 1.0)) + (progn + (setq frame (vfl-frame-3grad (vfs-vf-koerper (car frame) richtn w lvf hzn) hzn)) + (vfl-acc-vf-seg richtn w lvf) + (setq cnt (1+ cnt)))) + (setq letzt-hz hzn) + (vfl-20m-check p-umlenk (car frame)) + ;; GF2-Laenge (hinter dem Motor): aus der Zerlegung (variiert mit Winkel), + ;; sonst (typ "GF") aus der 3-Grad-Geometrie abgeleitet. Mindestwert 400. + (setq gf2 + (cond ((and lgf (> lgf 0.0)) lgf) + ((= typ "GF") + (max 0.0 (- (/ (- dL (abs (if ein-dx ein-dx 576.0))) (cos rad3)) 800.0))) + (t 0.0))) + (if (and (> gf2 0.0) (< gf2 *vfl-gf-min-laenge*)) + (progn + (princ (strcat "\n HINWEIS: berechnetes GF2=" (rtos gf2 2 0) + " mm < Minimum " (rtos *vfl-gf-min-laenge* 2 0) + " mm -> auf Minimum angehoben (ES endet dadurch etwas weiter; siehe Ist-Ziel-Report).")) + (setq gf2 *vfl-gf-min-laenge*))) + (list frame cnt hzn gf2) + ) + ) + ) + ) +) + +;; Rueckgabe: (frame anzahl-koerper ziel-ende) (defun vfl-vf-einheit (frame hz1 richtung1 winkel1 L_GF1 L_VF1 gf-am-ausgang / p-umlenk vf-count antwort L_GF-eff L_GF1-bau L_GF2-bau linie-mess dL dH hn hzn richtn ent typ w lgf lvf - letzt-hz fertig) + letzt-hz fertig ziel-ende ziel-gf2 res3 gf2-eff) ;; Gesamte Staustrecke (mind. *vfl-gf-min-laenge*) auf Einlauf/Ausgang verteilen. (setq L_GF-eff (max L_GF1 *vfl-gf-min-laenge*)) (if gf-am-ausgang @@ -298,21 +503,39 @@ (setq *vfl-acc-separator* (1+ *vfl-acc-separator*)) ; Einlauf-Separator (in vfs-vf-entry) (setq p-umlenk (car frame)) - ;; --- Erstes Koerper-Sub-Segment (das bereits klassifizierte Linien-Segment) --- - (setq frame (vfl-frame-3grad (vfs-vf-koerper (car frame) richtung1 winkel1 L_VF1 hz1) hz1)) + ;; --- Erstes Koerper-Sub-Segment --- + ;; winkel1=0 => horizontaler Anfang (Option 3): mit Separator-vor/nach-Abfrage. + (if (= (fix winkel1) 0) + (setq frame (vfl-baue-horizontal-koerper frame hz1 L_VF1)) + (progn + (setq frame (vfl-frame-3grad (vfs-vf-koerper (car frame) richtung1 winkel1 L_VF1 hz1) hz1)) + (vfl-acc-vf-seg richtung1 winkel1 L_VF1) + ) + ) (setq vf-count 1 letzt-hz hz1) - (vfl-acc-vf-seg richtung1 winkel1 L_VF1) (vfl-20m-check p-umlenk (car frame)) ;; --- Fortsetzungs-Schleife bis Foerderer-Ende --- (setq fertig nil) (while (not fertig) (princ "\n\nIst der Endpunkt der Foerderer?") - (princ "\n 1 - Ja (Motorstation setzen)") + (princ "\n 1 - Ja (nur Motorstation setzen)") (princ "\n 2 - Nein (weiterbauen)") - (setq antwort (getstring "\nIhre Wahl (1/2) [2]: ")) + (princ "\n 3 - Ja (Motorstation setzen, Kettenende definieren)") + (setq antwort (getstring "\nIhre Wahl (1/2/3) [2]: ")) (if (= antwort "1") (setq fertig t) + (if (= antwort "3") + ;; --- Option 3: Kettenende exakt am Zielpunkt (nur EIN Motor) --- + (progn + (setq res3 (vfl-body-abschluss frame letzt-hz p-umlenk)) + (if res3 + (setq frame (nth 0 res3) + vf-count (+ vf-count (nth 1 res3)) + letzt-hz (nth 2 res3) + ziel-gf2 (nth 3 res3) + ziel-ende t + fertig t))) (progn (princ "\n\nNaechstes Element im VarioFoerderer:") (princ "\n 1 - Horizontaler Foerderer (Linie ohne Hoehendifferenz)") @@ -333,9 +556,9 @@ (setq dL (car linie-mess) hzn (cadr linie-mess)) (if (> dL 1.0) (progn - (setq frame (vfl-frame-3grad (vfs-vf-koerper (car frame) "Ab" 0 dL hzn) hzn)) + ;; inkl. Separator-vor/nach-Abfrage + (setq frame (vfl-baue-horizontal-koerper frame hzn dL)) (setq vf-count (1+ vf-count) letzt-hz hzn) - (vfl-acc-vf-seg "horizontal" 0 dL) (vfl-20m-check p-umlenk (car frame)) ) (princ "\n Linie zu kurz (oder entgegen der Fahrtrichtung) - uebersprungen.") @@ -345,6 +568,8 @@ ) ;; --- Auf/Ab-Foerderer (folgt der aktuellen Fahrtrichtung) --- (t + ;; gewinkeltes VF braucht 3-Grad-Basis -> flache Zone ggf. mit ab_3 beenden + (setq frame (vfl-nach-3grad frame)) (setq linie-mess (vfl-neue-linie-messen (car frame) letzt-hz)) (if linie-mess (progn @@ -378,6 +603,7 @@ ) ) ) + ) ) ) @@ -387,11 +613,17 @@ ;; Motorseite erfassen: derzeit immer "rechts" (nur diese DWG vorhanden; ;; die "links"-Einzel-DWG wird spaeter ergaenzt). (setq *vfl-acc-motorseite* (append *vfl-acc-motorseite* (list "rechts"))) + ;; Motorstation braucht 3-Grad-Basis: falls die Kette gerade flach endet + ;; (horizontales Stueck ohne ab_3), hier den ab_3-Uebergang nachholen. + (setq frame (vfl-nach-3grad frame)) + ;; Auslauf-GF2: im Kettenende-Modus (Option 3) neu berechnet (ziel-gf2), + ;; sonst aus der GF-Verteilungs-Frage (L_GF2-bau). + (setq gf2-eff (if ziel-ende ziel-gf2 L_GF2-bau)) (setq frame (vfl-frame-3grad - (vfs-vf-exit (car frame) L_GF2-bau letzt-hz nil) letzt-hz)) - (if (> L_GF2-bau 0.1) - (vfl-acc-gf-seg L_GF2-bau (ssg-cfg-or "vario" "gefaelle_winkel" 3))) - (list frame vf-count) + (vfs-vf-exit (car frame) gf2-eff letzt-hz nil) letzt-hz)) + (if (> gf2-eff 0.1) + (vfl-acc-gf-seg gf2-eff (ssg-cfg-or "vario" "gefaelle_winkel" 3))) + (list frame vf-count ziel-ende) ) ;; GF-Bogen (horizontale Kurve, Neigung bleibt wie im aktuellen Frame). @@ -426,10 +658,10 @@ (defun vfl-insert-vario-kurve (frame / kwinkel kseite kvariante antwort blockname hz flach-frame) (princ "\n\nVario-Kurve - Winkel waehlen:") - (princ "\n 1 - 30 Grad\n 2 - 60 Grad\n 3 - 90 Grad") - (setq antwort (getint "\nIhre Wahl (1/2/3) [3]: ")) - (if (null antwort) (setq antwort 3)) - (setq kwinkel (cond ((= antwort 1) 30) ((= antwort 2) 60) (t 90))) + (princ "\n 1 - 90 Grad\n 2 - 60 Grad\n 3 - 30 Grad") + (setq antwort (getint "\nIhre Wahl (1/2/3) [1]: ")) + (if (null antwort) (setq antwort 1)) + (setq kwinkel (cond ((= antwort 1) 90) ((= antwort 2) 60) (t 30))) (princ "\nVario-Kurve - Seite waehlen:") (princ "\n 1 - Links\n 2 - Rechts") (setq antwort (getstring "\nIhre Wahl (1/2) [1]: ")) @@ -567,14 +799,16 @@ (list (cons "Bezeichnung" vfl-bname) (cons "MONTAGEHOEHE_m" (rtos (/ (+ hoehe-von hoehe-bis) 2000.0) 2 3)) - (cons "HOEHE_VON" (itoa (fix hoehe-von))) - (cons "HOEHE_BIS" (itoa (fix hoehe-bis))) - (cons "DELTA_H" (itoa (fix (abs (- hoehe-bis hoehe-von))))) - (cons "DELTA_L" (itoa (fix delta-l-total))) + (cons "HOEHE_VON_mm" (itoa (fix hoehe-von))) + (cons "HOEHE_BIS_mm" (itoa (fix hoehe-bis))) + (cons "DELTA_H_mm" (itoa (fix (abs (- hoehe-bis hoehe-von))))) + (cons "DELTA_L_mm" (itoa (fix delta-l-total))) (cons "TYP" typ-str) (cons "SEITE_AS" as-seite) (cons "SEITE_ES" es-seite) - (cons "ANZAHL_GF" (itoa anzahl-gf)) + ;; ANZAHL_GF = alle GF-Stuecke: eigenstaendige GF-Segmente UND die GF1/GF2 + ;; jeder VF-Einheit (alle ueber vfl-acc-gf-seg in *vfl-acc-lgf* gesammelt). + (cons "ANZAHL_GF" (itoa (length *vfl-acc-lgf*))) (cons "L_GF_m" (vfl-join-komma *vfl-acc-lgf*)) (cons "GF_WINKEL" (vfl-join-komma *vfl-acc-gfwinkel*)) ;; GF-Boegen (Richtungswechsel im GF-Teil), gezaehlt nach Seite+Winkel @@ -606,13 +840,77 @@ vfl-insert ) +;; Baut eine komplette VarioFoerderer-Einheit und schliesst sie ab: +;; GF-Verteilung fragen -> vfl-vf-einheit (Umlenk..Motor, mehrsegmentig) +;; -> Kettenende? Ja: Separator+ES (Ende); Nein: optionaler Zwischen-Separator. +;; winkel1=0 => horizontaler Anfangs-Koerper (Option "Neue horizontal VF"). +;; auto-ende: T => keine Kettenende-Frage, es wird direkt Separator + ES +;; gesetzt (Option "Neue Linie BIS Kettenende"). +;; Rueckgabe: (frame anzahl-koerper ende-flag es-seite-oder-nil). +(defun vfl-vf-einheit-abschluss (frame hz richtung winkel L_GF L_VF auto-ende / + antwort res es-s ende) + ;; GF-Verteilung: halbe Staustrecke am Ausgang (GF2) oder alles am Einlauf. + (princ "\n\nStaustrecke (GF) - Verteilung waehlen:") + (princ "\n 1 - 1/2GF am Ausgang (GF2), 1/2GF am Einlauf (GF1)") + (princ "\n 2 - Gesamte Staustrecke am Einlauf (GF1), kein GF2") + (setq antwort (getstring "\nIhre Wahl (1/2) [2]: ")) + (setq res (vfl-vf-einheit frame hz richtung winkel L_GF L_VF (= antwort "1"))) + (setq frame (nth 0 res)) + ;; Kettenende? Bei auto-ende (Kettenende-Modus) ohne Frage direkt ES setzen. + ;; Sonst fragen - zwischen einem AS und ES koennen mehrere Foerderer liegen. + ;; auto-ende (Option 4) ODER ziel-ende aus der laufenden Einheit (Option 3): + ;; ohne Frage direkt Separator + ES setzen. + (if (or auto-ende (nth 2 res)) + (setq antwort "1") + (progn + (princ "\n\nIst das das Kettenende (ES-Element)?") + (princ "\n 1 - Ja (Separator + ES-Element setzen)") + (princ "\n 2 - Nein (weiterbauen)") + (setq antwort (getstring "\nIhre Wahl (1/2) [2]: ")) + ) + ) + (if (= antwort "1") + (progn + (setq es-s (vfl-frage-es-seite)) + (setq frame (vfl-insert-es-element "VF" frame + (car (frame->hz-winkel frame)) 0.0 + (caddr (car frame)) es-s)) + (setq ende t) + ;; Ist-Ziel-Report (Option 3): Soll-ES (aus vfl-body-abschluss) vs. Ist-ES. + (if *vfl-ziel-punkt* + (progn + (princ "\n\n>>> IST-ZIEL-VERGLEICH (ES-Element):") + (princ (strcat "\n Soll: X=" (rtos (car *vfl-ziel-punkt*) 2 1) + " Y=" (rtos (cadr *vfl-ziel-punkt*) 2 1) + " Z=" (rtos (caddr *vfl-ziel-punkt*) 2 1))) + (princ (strcat "\n Ist : X=" (rtos (car (car frame)) 2 1) + " Y=" (rtos (cadr (car frame)) 2 1) + " Z=" (rtos (caddr (car frame)) 2 1))) + (princ (strcat "\n Abweichung: dX=" + (rtos (- (car (car frame)) (car *vfl-ziel-punkt*)) 2 1) + " dY=" (rtos (- (cadr (car frame)) (cadr *vfl-ziel-punkt*)) 2 1) + " dZ=" (rtos (- (caddr (car frame)) (caddr *vfl-ziel-punkt*)) 2 1) " mm")) + (setq *vfl-ziel-punkt* nil) + ) + ) + ) + (progn + (princ "\n\nZusaetzlichen Separator an dieser Stelle einfuegen?") + (princ "\n 1 - Ja\n 2 - Nein") + (setq antwort (getstring "\nIhre Wahl (1/2) [2]: ")) + (if (= antwort "1") (setq frame (vfl-insert-separator frame))) + ) + ) + (list frame (nth 1 res) ende es-s) +) + ;; ============================================================ ;; TEIL 5: HAUPTBEFEHL - MODUS 1 (MANUELLE EINGABE) ;; ============================================================ (defun vf-linienzug-modus ( / startpunkt start-hoehe as-seite es-seite antwort wahl p-aktuell linie-mess hoehe-neu hoehe-bis deltaL deltaH richtung hz-neu entscheidung typ winkel L_GF L_VF vf-einheit-res - frame letzter-typ fertig + frame letzter-typ fertig linie-ende-modus rad3 anzahl-gf anzahl-vf vfl-nummer lastEnt) (princ "\n\n=========================================") (princ "\n VF-LINIENZUG - Modus 1: Manuelle Eingabe") @@ -634,9 +932,9 @@ ) ) - (if (not *lib-initialized*) (init-bibliothek)) + (if (or (not *lib-initialized*) (null bogen-auf)) (init-bibliothek)) - (setq startpunkt (getpoint "\n\nStartpunkt der Kette waehlen: ")) + (setq startpunkt (vfl-getpoint nil "\n\nStartpunkt der Kette waehlen: ")) (if (null startpunkt) (progn (princ "\nAbgebrochen.") (exit))) ;; Hoehe (Z) des Startpunkts abfragen und in die Z-Koordinate uebernehmen. (setq start-hoehe @@ -664,14 +962,32 @@ ;; auf 3-Grad-Neigung) darf ein GF-Bogen oder eine neue Linie folgen. Nur ;; am Kettenanfang folgt direkt eine neue Linie. Vario-Kurven werden ;; INNERHALB der VF-Einheit behandelt (vfl-vf-einheit). - (setq wahl "Linie") - (if (or (= letzter-typ "GF") (= letzter-typ "VF")) + ;; Menue immer anzeigen; GF-Bogen nur, wenn bereits ein Frame existiert + ;; (am Kettenanfang gibt es keinen Vorgaenger-Rahmen fuer einen Bogen). + ;; Lueckenlose Nummerierung: GF-Bogen (nur mit Frame) ist immer Option 1; + ;; ohne Frame ruecken die uebrigen Optionen auf 1..3 auf. + (princ "\n\nNaechstes Element waehlen:") + (setq linie-ende-modus nil) + (if frame (progn - (princ "\n\nNaechstes Element waehlen:") (princ "\n 1 - GF-Bogen (horizontale Kurve)") - (princ "\n 2 - Neue Linie") - (setq antwort (getstring "\nIhre Wahl (1/2) [2]: ")) - (setq wahl (if (= antwort "1") "GF-Bogen" "Linie")) + (princ "\n 2 - Neue Linie (GF, Ab/Auf VF)") + (princ "\n 3 - Neue VF mit horizontalem Anfang (Horizontal-Stueck)") + (princ "\n 4 - Neue Linie BIS Kettenende (danach Separator + ES-Element)") + (setq antwort (getstring "\nIhre Wahl (1-4) [2]: ")) + (setq wahl (cond ((= antwort "1") "GF-Bogen") + ((= antwort "3") "Horizontal-VF") + ((= antwort "4") (setq linie-ende-modus t) "Linie") + (t "Linie"))) ; "2" oder leer + ) + (progn + (princ "\n 1 - Neue Linie (GF, Ab/Auf VF)") + (princ "\n 2 - Neue VF mit horizontalem Anfang (Horizontal-Stueck)") + (princ "\n 3 - Neue Linie BIS Kettenende (danach Separator + ES-Element)") + (setq antwort (getstring "\nIhre Wahl (1-3) [1]: ")) + (setq wahl (cond ((= antwort "2") "Horizontal-VF") + ((= antwort "3") (setq linie-ende-modus t) "Linie") + (t "Linie"))) ; "1" oder leer ) ) @@ -680,6 +996,33 @@ (setq frame (vfl-insert-gf-bogen frame)) (setq p-aktuell (car frame)) ) + + ;; --- Neue horizontal VF: VF-Einheit mit horizontalem Anfangs-Koerper --- + ((= wahl "Horizontal-VF") + (setq linie-mess (vfl-neue-linie-messen p-aktuell + (if frame (car (frame->hz-winkel frame)) nil))) + (if (null linie-mess) (progn (princ "\nAbgebrochen.") (exit))) + (setq deltaL (car linie-mess) hz-neu (cadr linie-mess)) + (if (< deltaL 1000.0) + (princ "\nFEHLER: Horizontaler VF braucht >= 1000 mm (Umlenk + Motor) - laenger zeichnen.") + (progn + ;; Kettenanfang: AS-Element (flach, wie beim VF) einfuegen + (if (null frame) + (setq frame (vfl-insert-as-element "VF" p-aktuell hz-neu 0.0 as-seite))) + ;; VF-Einheit mit horizontalem ersten Koerper (winkel=0, L_VF=deltaL, + ;; L_GF = Mindestlaenge fuer den Einlauf-Anschluss). + (setq vf-einheit-res + (vfl-vf-einheit-abschluss frame hz-neu "Ab" 0 *vfl-gf-min-laenge* deltaL nil)) + (setq frame (nth 0 vf-einheit-res)) + (setq anzahl-vf (+ anzahl-vf (nth 1 vf-einheit-res))) + (if (nth 2 vf-einheit-res) (setq fertig t)) + (if (nth 3 vf-einheit-res) (setq es-seite (nth 3 vf-einheit-res))) + (setq letzter-typ "VF") + (setq p-aktuell (car frame)) + ) + ) + ) + (t ;; --- Neue Linie --- ;; Fahrtrichtung folgt dem vorherigen Element (Frame); nur das erste ;; Segment (frame=nil) definiert die Richtung frei. @@ -695,7 +1038,9 @@ ;; braucht >= 1000 mm deltaL (Umlenk- + Motorstation = 500+500 mm), ;; und eine GF ist mindestens 3 Grad geneigt. Die Endhoehe ergibt ;; sich damit fest aus deltaH = deltaL*tan(3 Grad). - (if (< deltaL 1000.0) + ;; Im Kettenende-Modus (Option 4) wird IMMER nach der Zielhoehe + ;; gefragt (Kurzsegment-Automatik hier ueberspringen). + (if (and (< deltaL 1000.0) (not linie-ende-modus)) (progn (setq typ "GF" winkel 3.0 richtung "Ab" L_GF nil L_VF nil) (setq deltaH (* deltaL (/ (sin (* 3.0 (/ pi 180.0))) @@ -712,6 +1057,25 @@ (setq deltaH (- hoehe-neu (caddr p-aktuell))) (setq richtung (if (>= deltaH 0.0) "Auf" "Ab")) (setq deltaH (abs deltaH)) + ;; Kettenende-Modus (Option 4): Der abgefragte Endpunkt (XY+Z) + ;; ist das exakte Ende der Kette NACH Separator + ES-Element. + ;; Deren Footprint (Separator 300 mm bei ~3 Grad + ES-Element + ;; ein-dx/ein-dz) muss vom baubaren GF/VF-Segment reserviert + ;; (abgezogen) werden, damit das ES-Element genau am Zielpunkt + ;; endet. Naeherung: Separator-Neigung = 3 Grad (Auslauf). + (if linie-ende-modus + (progn + (setq rad3 (* (float (ssg-cfg-or "vario" "gefaelle_winkel" 3)) + (/ pi 180.0))) + (setq deltaL (max 1.0 (- deltaL (* 300.0 (cos rad3)) + (abs (if ein-dx ein-dx 0.0))))) + (setq deltaH (max 0.0 (- deltaH (* 300.0 (sin rad3)) + (abs (if ein-dz ein-dz 0.0))))) + (princ (strcat "\n>>> Kettenende-Modus: Footprint fuer Separator + ES" + " reserviert -> baubar deltaL=" (rtos deltaL 2 0) + " mm, deltaH=" (rtos deltaH 2 0) " mm.")) + ) + ) (setq entscheidung (vfl-segment-entscheidung deltaL deltaH richtung)) (setq typ (nth 0 entscheidung) winkel (nth 1 entscheidung) L_GF (nth 2 entscheidung) L_VF (nth 3 entscheidung)) @@ -740,10 +1104,17 @@ (vfl-acc-gf-seg (/ deltaL (cos (* (float winkel) (/ pi 180.0)))) winkel) (setq letzter-typ "GF") (setq p-aktuell (car frame)) - (princ "\nIst das das Kettenende?") - (princ "\n 1 - Ja (ES-Element setzen)") - (princ "\n 2 - Nein (weiterbauen)") - (setq antwort (getstring "\nIhre Wahl (1/2) [2]: ")) + ;; Kettenende-Modus (Option 4): ohne Frage direkt ES setzen. + ;; Sonst nachfragen, ob die Kette hier endet. + (if linie-ende-modus + (setq antwort "1") + (progn + (princ "\nIst das das Kettenende?") + (princ "\n 1 - Ja (ES-Element setzen)") + (princ "\n 2 - Nein (weiterbauen)") + (setq antwort (getstring "\nIhre Wahl (1/2) [2]: ")) + ) + ) (if (= antwort "1") (progn (setq es-seite (vfl-frage-es-seite)) @@ -754,48 +1125,17 @@ ) ) (progn - ;; --- VarioFoerderer-Einheit (eine Umlenk- bis eine - ;; Motorstation, mehrsegmentig; interaktiv in vfl-vf-einheit) --- - ;; GF-Verteilung fragen: halbe Staustrecke als GF2 am Ausgang - ;; oder gesamte Staustrecke am Einlauf. - (princ "\n\nStaustrecke (GF) - Verteilung waehlen:") - (princ "\n 1 - Haelfte am Ausgang (GF2), Haelfte am Einlauf (GF1)") - (princ "\n 2 - Gesamte Staustrecke am Einlauf (GF1), kein GF2") - (setq antwort (getstring "\nIhre Wahl (1/2) [2]: ")) + ;; --- VarioFoerderer-Einheit (Umlenk..Motor, mehrsegmentig) --- + ;; linie-ende-modus=T -> direkt Kettenende (Separator + ES). (setq vf-einheit-res - (vfl-vf-einheit frame hz-neu richtung winkel L_GF L_VF - (= antwort "1"))) + (vfl-vf-einheit-abschluss frame hz-neu richtung winkel L_GF L_VF + linie-ende-modus)) (setq frame (nth 0 vf-einheit-res)) (setq anzahl-vf (+ anzahl-vf (nth 1 vf-einheit-res))) + (if (nth 2 vf-einheit-res) (setq fertig t)) + (if (nth 3 vf-einheit-res) (setq es-seite (nth 3 vf-einheit-res))) (setq letzter-typ "VF") (setq p-aktuell (car frame)) - ;; Kettenende? Zwischen einem AS und ES koennen mehrere - ;; Foerderer (Motoren) liegen -> bei Nein optional einen - ;; Separator einfuegen und weiterbauen. - (princ "\n\nIst das das Kettenende (ES-Element)?") - (princ "\n 1 - Ja (Separator + ES-Element setzen)") - (princ "\n 2 - Nein (weiterbauen)") - (setq antwort (getstring "\nIhre Wahl (1/2) [2]: ")) - (if (= antwort "1") - (progn - (setq es-seite (vfl-frage-es-seite)) - (setq frame (vfl-insert-es-element "VF" frame - (car (frame->hz-winkel frame)) 0.0 - (caddr (car frame)) es-seite)) - (setq fertig t) - ) - (progn - (princ "\n\nZusaetzlichen Separator an dieser Stelle einfuegen?") - (princ "\n 1 - Ja\n 2 - Nein") - (setq antwort (getstring "\nIhre Wahl (1/2) [2]: ")) - (if (= antwort "1") - (progn - (setq frame (vfl-insert-separator frame)) - (setq p-aktuell (car frame)) - ) - ) - ) - ) ) ) ) diff --git a/Lisp/vf_standard.lsp b/Lisp/vf_standard.lsp index a7007e4..ba8fc7b 100644 --- a/Lisp/vf_standard.lsp +++ b/Lisp/vf_standard.lsp @@ -95,7 +95,11 @@ ;; ============================================================ (defun init-bibliothek ( / temp-obj bogen-winkel ks-data ks-ein-pos ks-aus-pos dx dy dz bogen-name) - (if (and *lib-initialized* (tblsearch "BLOCK" "AS_Element_90_links")) + ;; "bereits initialisiert" nur, wenn das Flag gesetzt UND die Bogen-Tabelle + ;; tatsaechlich befuellt ist. Sonst (Stuck-State: Flag gesetzt, aber bogen-auf + ;; leer - z.B. nach einer fehlgeschlagenen Extraktion) neu initialisieren. + (if (and *lib-initialized* bogen-auf bogen-ab + (tblsearch "BLOCK" "AS_Element_90_links")) (progn (princ "\n Standard-Bibliothek bereits initialisiert.") t diff --git a/menu/SSG_LIB.mnu b/menu/SSG_LIB.mnu index afeb8f3..7962e0a 100644 --- a/menu/SSG_LIB.mnu +++ b/menu/SSG_LIB.mnu @@ -122,7 +122,7 @@ [Alle Tests ausfuehren]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_run_all.lsp")) SSG_RUN_ALL_TESTS [--] [Kreisel]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_kreisel.lsp")) TEST_KREISEL -[Foerderer]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_foerderer.lsp")) TEST_FOERDERER +[Foerderer]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_foerderer.lsp")) TEST_FOERDERER TEST_LINIENZUG [Omniflo]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_omniflo.lsp")) TEST_OMNIFLO [Gefaellestrecke]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_gefaellestrecke.lsp")) TEST_GEFAELLESTRECKE [<-Export CSV/Sivas ALL]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_export_all.lsp")) TEST_EXPORT_ALL diff --git a/tests/test_foerderer.lsp b/tests/test_foerderer.lsp index 99ecc06..c443f2c 100644 --- a/tests/test_foerderer.lsp +++ b/tests/test_foerderer.lsp @@ -105,8 +105,10 @@ startpunkt hoehe-von hoehe-bis lastEnt sub-typ) - ;; Bibliothek initialisieren (laedt Block-Library und extrahiert Masse) - (if (not *lib-initialized*) + ;; Bibliothek initialisieren (laedt Block-Library und extrahiert Masse). + ;; Auch bei gesetztem Flag, aber leerer Bogen-Tabelle neu initialisieren + ;; (Selbstheilung gegen Stuck-State). + (if (or (not *lib-initialized*) (null bogen-auf)) (init-bibliothek) ) @@ -274,3 +276,156 @@ (princ "\n================================================================") (princ) ) + + +;; ============================================================ +;; C:TEST_LINIENZUG - Regressionstest fuer den VF-Linienzug (Modus 1) +;; ------------------------------------------------------------ +;; EIGENSTAENDIG: beruehrt weder c:TEST_FOERDERER noch dessen Funktionen. +;; ============================================================ +;; Der VF-Linienzug (vf-linienzug-modus) ist stark interaktiv (getpoint / +;; getstring / getint / getreal). Dieser Test spielt eine reale Session als +;; Skript ab: die vier Eingabe-Funktionen werden NUR waehrend des Testlaufs +;; durch Mocks ersetzt (mit Save/Restore), danach wieder hergestellt. +;; +;; Repliziertes Szenario (aus manuellem Test): +;; Start Z=4500, AS rechts +;; -> Neue Linie (Ab, Ziel-Z=1800, Winkel 45 Grad), GF ganz am Einlauf +;; -> Horizontal 900 mm mit Separator VOR + NACH (beide horizontal, 0 Grad) +;; -> Vario-Kurve 90 links aussen +;; -> Horizontal 250 mm (ohne Separatoren) +;; -> Vario-Kurve 90 links aussen +;; -> Option 3 (Kettenende definieren): Ziel-Z=4500, Winkel 45 Grad, ES rechts +;; +;; Erwartetes Ergebnis (Ist-Ziel-Report): +;; dX ~ 0, dZ ~ 0 (Ziel exakt getroffen), +;; dY ~ 637 mm = seitlicher Fussabdruck des ES_Element_90 (90-Grad-Schwenk; +;; KEIN Baufehler, sondern die Bauform des ES-Elements). +;; +;; Aufruf in BricsCAD: +;; (load (strcat (getenv "DXFMAKRO") "/tests/test_foerderer.lsp")) +;; TEST_LINIENZUG +;; ============================================================ + +;; Naechste Skript-Antwort liefern (FIFO). Bei leerer Queue Warnung + "". +(defun tlz-pop ( / v) + (if *tlz-q* + (progn (setq v (car *tlz-q*)) (setq *tlz-q* (cdr *tlz-q*)) v) + (progn (princ "\n [TEST_LINIENZUG] WARNUNG: Skript-Queue leer - unerwartete Abfrage!") "") + ) +) + +;; Mock-Eingabefunktionen. getpoint erhaelt als 1. Argument den Basispunkt +;; (p-akt) bei Folge-Segmenten bzw. einen Prompt-String beim Startpunkt. +;; Skript-Eintrag (A x y z) -> absoluter Punkt +;; Skript-Eintrag (P dL hz) -> Basispunkt + dL entlang Richtung hz (Grad), +;; sodass die Projektion in vfl-neue-linie-messen +;; exakt dL ergibt. +(defun tlz-getpoint (a b / spec base rad) + (setq spec (tlz-pop)) + (setq base (if (and a (listp a)) a '(0.0 0.0 0.0))) + (if (and (listp spec) (= (car spec) 'A)) + (list (float (cadr spec)) (float (caddr spec)) (float (cadddr spec))) + (progn + (setq rad (* (float (caddr spec)) (/ pi 180.0))) + (list (+ (car base) (* (float (cadr spec)) (cos rad))) + (+ (cadr base) (* (float (cadr spec)) (sin rad))) + (caddr base)) + ) + ) +) +(defun tlz-getstring (a) (tlz-pop)) +(defun tlz-getint (a) (tlz-pop)) +(defun tlz-getreal (a) (tlz-pop)) + +(defun c:TEST_LINIENZUG ( / old-vgp old-gs old-gi old-gr err) + (if (or (not *lib-initialized*) (null bogen-auf)) (init-bibliothek)) + (if (null (car (atoms-family 1 '("VF-LINIENZUG-MODUS")))) + (progn + (alert (strcat "vf-linienzug-modus nicht geladen!\n" + "Bitte Menue/Module laden (VarioFoerderer + Gefaellestrecke).")) + (exit) + ) + ) + + (princ "\n================================================================") + (princ "\n TEST_LINIENZUG - VF-Linienzug Replay-Test") + (princ "\n================================================================") + + (ssg-start "TEST_LINIENZUG" '(("OSMODE") ("ATTREQ") ("ATTDIA"))) + (setvar "OSMODE" 0) + (setvar "ATTREQ" 0) + (setvar "ATTDIA" 0) + + ;; --- Skript der Eingaben in exakter Aufruf-Reihenfolge --- + (setq *tlz-q* + (list + (list 'A 0 0 0) ; getpoint Startpunkt der Kette + 4500.0 ; getreal Hoehe (Z) Startpunkt + "2" ; getstring AS-Seite (rechts) + "1" ; getstring Naechstes Element (Neue Linie) + (list 'P 5500 0) ; getpoint Endpunkt Linie (frei, +X, dL=5500) + 1800.0 ; getreal Hoehe (Z) Linienendpunkt + 1 ; getint Winkelwahl (45 Grad) + "2" ; getstring GF-Verteilung (alles am Einlauf) + "2" ; getstring Ist Endpunkt? (Nein) + "1" ; getstring Naechstes im VF (Horizontal) + (list 'P 900 0) ; getpoint Horizontal-Endpunkt (dL=900, hz=0) + "1" ; getstring Separator VOR? (Ja) + "1" ; getstring Separator NACH? (Ja) + "2" ; getstring Ist Endpunkt? (Nein) + "2" ; getstring Naechstes (Vario-Kurve) + 1 ; getint Kurve-Winkel (90) + "1" ; getstring Kurve-Seite (links) + "1" ; getstring Kurve-Variante (aussen) + "2" ; getstring Ist Endpunkt? (Nein) + "1" ; getstring Naechstes (Horizontal) + (list 'P 250 90) ; getpoint Horizontal-Endpunkt (dL=250, hz=90) + "2" ; getstring Separator VOR? (Nein) + "2" ; getstring Separator NACH? (Nein) + "2" ; getstring Ist Endpunkt? (Nein) + "2" ; getstring Naechstes (Vario-Kurve) + 1 ; getint Kurve-Winkel (90) + "1" ; getstring Kurve-Seite (links) + "1" ; getstring Kurve-Variante (aussen) + "3" ; getstring Ist Endpunkt? (Option 3 - Kettenende) + (list 'P 6215 180) ; getpoint Option-3-Endpunkt (dL=6215, hz=180) + 4500.0 ; getreal Hoehe (Z) Kettenende (ES) + 2 ; getint Option-3-Winkelwahl (45 Grad) + "2" ; getstring ES-Seite (rechts) + ) + ) + + ;; --- Eingabe-Funktionen sichern und durch Mocks ersetzen --- + ;; Punkte ueber den Produktions-Wrapper vfl-getpoint (feste 2-Arity), + ;; getstring/getint/getreal (alle 1-argumentig) direkt. + (setq old-vgp vfl-getpoint old-gs getstring old-gi getint old-gr getreal) + (setq vfl-getpoint tlz-getpoint + getstring tlz-getstring + getint tlz-getint + getreal tlz-getreal) + + ;; --- Linienzug-Modus abspielen (Fehler abfangen, damit Restore sicher laeuft) --- + (setq err (vl-catch-all-apply 'vf-linienzug-modus '())) + + ;; --- Eingabe-Funktionen wiederherstellen (IMMER) --- + (setq vfl-getpoint old-vgp getstring old-gs getint old-gi getreal old-gr) + + (princ "\n================================================================") + (if (vl-catch-all-error-p err) + (princ (strcat "\n TEST_LINIENZUG: FEHLER -> " + (vl-catch-all-error-message err))) + (progn + (princ "\n TEST_LINIENZUG: Kette gebaut.") + (if *tlz-q* + (princ (strcat "\n WARNUNG: " (itoa (length *tlz-q*)) + " Skript-Eintraege NICHT verbraucht (Ablauf weicht ab).")) + (princ "\n Alle Skript-Eingaben verbraucht (Ablauf wie erwartet).")) + (princ "\n Erwartung Ist-Ziel: dX~0, dZ~0, dY~637 mm (ES_90-Seitenversatz).") + ) + ) + (princ "\n================================================================") + + (ssg-end) + (princ) +) diff --git a/tests/test_gefaellestrecke.lsp b/tests/test_gefaellestrecke.lsp index 9c5928e..7818e6b 100644 --- a/tests/test_gefaellestrecke.lsp +++ b/tests/test_gefaellestrecke.lsp @@ -78,7 +78,7 @@ ;; as-seite : "links" oder "rechts" (AUS-Element) ;; es-seite : "links" oder "rechts" (EIN-Element) ;; Rueckgabe : GF_N INSERT ename oder nil bei Fehler -(defun gefaellestrecke:modus3-einfuegen (linie-vla startpunkt endpunkt-ref as-seite es-seite / +(defun gefaellestrecke:modus3-einfuegen (objekt-liste startpunkt endpunkt-ref as-seite es-seite / as-block es-block deltaH kette segmente seg entry-hz exit-hz grade @@ -98,8 +98,8 @@ (setq es-block (strcat "ES_Element_90_" es-seite)) (setq deltaH (abs (- (caddr startpunkt) (caddr endpunkt-ref)))) - ;; 1. Kette sortieren und analysieren - (setq kette (gf-sortiere-objekte (list linie-vla) startpunkt)) + ;; 1. Kette sortieren und analysieren (objekt-liste = 1..n LINE/ARC-Objekte) + (setq kette (gf-sortiere-objekte objekt-liste startpunkt)) (if (null kette) ;; Fehler: keine Kette -> nil zurueckgeben (progn (princ "\n FEHLER: keine sortierte Kette!") nil) @@ -278,7 +278,8 @@ bL90 bL60 bL30 bR90 bR60 bR30 startpunkt last-ent nach-aus-pt entry-hz - (caddr (nth first-idx segmente)))) + (caddr (nth first-idx segmente)) + es-seite)) gf-ins ) ) @@ -294,6 +295,7 @@ gf-ins ergebnis attribs h-von h-bis d-h w-str 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 lbl-layer lbl-h lbl-h2) ;; Block-Bibliothek initialisieren (setzt aus-dx, ein-dx, aus-dz, ein-dz usw.) @@ -378,10 +380,10 @@ (if gf-ins (progn (setq attribs (ssg-attrib-read gf-ins)) - (setq h-von (atoi (cdr (assoc "HOEHE_VON" attribs)))) - (setq h-bis (atoi (cdr (assoc "HOEHE_BIS" attribs)))) + (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 "WINKEL" attribs))) + (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 @@ -410,10 +412,10 @@ (if gf-ins (progn (setq attribs (ssg-attrib-read gf-ins)) - (setq h-von (atoi (cdr (assoc "HOEHE_VON" attribs)))) - (setq h-bis (atoi (cdr (assoc "HOEHE_BIS" attribs)))) + (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 "WINKEL" attribs))) + (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 @@ -442,10 +444,10 @@ (if gf-ins (progn (setq attribs (ssg-attrib-read gf-ins)) - (setq h-von (atoi (cdr (assoc "HOEHE_VON" attribs)))) - (setq h-bis (atoi (cdr (assoc "HOEHE_BIS" attribs)))) + (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 "WINKEL" attribs))) + (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 @@ -474,10 +476,10 @@ (if gf-ins (progn (setq attribs (ssg-attrib-read gf-ins)) - (setq h-von (atoi (cdr (assoc "HOEHE_VON" attribs)))) - (setq h-bis (atoi (cdr (assoc "HOEHE_BIS" attribs)))) + (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 "WINKEL" attribs))) + (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 @@ -525,14 +527,14 @@ (if gf-ins (progn (setq attribs (ssg-attrib-read gf-ins)) - (setq h-von (atoi (cdr (assoc "HOEHE_VON" attribs)))) - (setq h-bis (atoi (cdr (assoc "HOEHE_BIS" attribs)))) + (setq h-von (atoi (cdr (assoc "HOEHE_VON_mm" attribs)))) + (setq h-bis (atoi (cdr (assoc "HOEHE_BIS_mm" attribs)))) ;; Volle Fliesskomma-Genauigkeit direkt aus der Rueckgabe verwenden ;; (nicht aus den auf ganze mm gerundeten HOEHE_VON/HOEHE_BIS-Attributen), ;; damit Modus 3 exakt mit diesen Werten weiterrechnen kann. (setq m2-deltaH (cadr ergebnis)) (setq m2-deltaL (caddr ergebnis)) - (setq w-str (cdr (assoc "WINKEL" attribs))) + (setq w-str (cdr (assoc "GF_WINKEL" attribs))) (princ (strcat " von=" (itoa h-von) " bis=" (itoa h-bis) " " delta-sym "H=" (rtos m2-deltaH 2 3) " mm " delta-sym "L=" (rtos m2-deltaL 2 3) @@ -601,14 +603,14 @@ (setq m3-line-vla (vlax-ename->vla-object (entlast))) (setq gf-ins (gefaellestrecke:modus3-einfuegen - m3-line-vla m3-start m3-end "links" "links")) + (list m3-line-vla) m3-start m3-end "links" "links")) (if gf-ins (progn (setq attribs (ssg-attrib-read gf-ins)) - (setq h-von (atoi (cdr (assoc "HOEHE_VON" attribs)))) - (setq h-bis (atoi (cdr (assoc "HOEHE_BIS" attribs)))) + (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 "WINKEL" attribs))) + (setq w-str (cdr (assoc "GF_WINKEL" attribs))) (princ (strcat "\n -> von=" (itoa h-von) " bis=" (itoa h-bis) " " delta-sym "H=" (itoa d-h) " mm" " " delta-sym "H-Soll=" (rtos m2-deltaH 2 3) " mm" @@ -632,6 +634,69 @@ 0.0 0 0 0 "FEHLER_LINIE") results-list)))) + ;; ============================================================ + ;; MODUS 3 - Linienzug MIT Bogen (Linie + 90-Grad-Bogen R500 + Linie) + ;; Start Hoehe 2000, waagerecht +X 8000 mm (links->rechts), dann + ;; 90-Grad-Bogen R500 (Auslauf Richtung +Y), dann +Y 6000 mm, + ;; Endpunkt Hoehe 1800 (dH = 200 mm ueber den ganzen Weg). + ;; ============================================================ + (princ "\n\n================================================================") + (princ "\n MODUS 3 - Linienzug mit Bogen (8000 +X / 90 Grad R500 / 6000 +Y)") + (princ "\n================================================================") + (setq m3b-start (list 30000.0 -50000.0 2000.0)) ; Kettenanfang, Hoehe 2000 + (setq m3b-end (list 38500.0 -43500.0 1800.0)) ; Kettenende, Hoehe 1800 + + ;; Abschnitts-Ueberschrift in der Zeichnung + (entmake (list '(0 . "TEXT") + (cons 10 (list 30000 -52000.0 2000.0)) + (cons 11 (list 30000 -52000.0 2000.0)) + (cons 40 lbl-h2) + (cons 1 "MODUS 3 - Linienzug mit 90 Grad-Bogen R500") + '(7 . "Standard") (cons 8 lbl-layer) '(72 . 0) '(73 . 2))) + + ;; Geometrie: LINE (+X 8000) + ARC (90 Grad R500, links) + LINE (+Y 6000). + ;; ARC: Mittelpunkt (38000,-49500), Start 270 Grad -> Ende 360 Grad = 90 Grad + ;; gegen den Uhrzeigersinn (Linkskurve, +X -> +Y). + (entmake (list '(0 . "LINE") + (cons 10 (list 30000.0 -50000.0 2000.0)) + (cons 11 (list 38000.0 -50000.0 2000.0)) (cons 8 lbl-layer))) + (setq m3b-e1 (entlast)) + (entmake (list '(0 . "ARC") + (cons 10 (list 38000.0 -49500.0 2000.0)) + (cons 40 500.0) + (cons 50 (* 1.5 pi)) + (cons 51 (* 2.0 pi)) (cons 8 lbl-layer))) + (setq m3b-e2 (entlast)) + (entmake (list '(0 . "LINE") + (cons 10 (list 38500.0 -49500.0 2000.0)) + (cons 11 (list 38500.0 -43500.0 2000.0)) (cons 8 lbl-layer))) + (setq m3b-e3 (entlast)) + (setq m3b-objliste (list (vlax-ename->vla-object m3b-e1) + (vlax-ename->vla-object m3b-e2) + (vlax-ename->vla-object m3b-e3))) + (setq gf-ins + (gefaellestrecke:modus3-einfuegen m3b-objliste m3b-start m3b-end "links" "links")) + (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 "\n -> von=" (itoa h-von) " bis=" (itoa h-bis) + " " delta-sym "H=" (itoa d-h) " mm" + " " 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") + 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") + results-list)))) + ;; ============================================================ ;; Zusammenfassung und Export ;; ============================================================ diff --git a/tests/testdata/gefaellestrecke_tests.json b/tests/testdata/gefaellestrecke_tests.json index 00eb37c..2451942 100644 --- a/tests/testdata/gefaellestrecke_tests.json +++ b/tests/testdata/gefaellestrecke_tests.json @@ -55,5 +55,15 @@ "deltaL_quelle": "M2_hz000", "as_seite": "links", "es_seite": "links" + }, + { + "test_id": "M3_Bogen", + "modus": 3, + "beschreibung": "Linienzug mit 90-Grad-Bogen R500 (8000 +X / Bogen links / 6000 +Y)", + "start_mm": [30000, -50000, 2000], + "ende_mm": [38500, -43500, 1800], + "bogen": {"winkel_grad": 90, "radius_mm": 500, "seite": "links"}, + "as_seite": "links", + "es_seite": "links" } ]