feat: VF-Linienzug Linie-GF - Separator VOR/NACH, Endpunkt/Zielhoehe-Vorwahl, steigende GF (Sonderfall bis 3 Grad)

Auf der neuen Kollegen-Architektur (Stufe 0-2: Spec/Journal/Replay-System
in vf_spec.lsp, Frage-Schale/Bauteil-Trennung) neu aufgebaut, nachdem der
urspruengliche Stand (Commit e76e24e) auf einer aelteren Basis beruhte und
per Rebase-Analyse als eigenstaendige, unabhaengige Erweiterung erkannt
wurde (der Kollege hat nichts entfernt - diese Features existierten vorher
nur lokal uncommitted).

Drei Erweiterungen fuer "Neue Linie: GF" in vf-linienzug-modus:
- Separator VOR/NACH direkt vor bzw. nach der Gefaellestrecke einfuegbar.
- Aeussere Vorwahl "Wie soll die Laenge bestimmt werden?": Endpunkt waehlen
  (bisheriges Verhalten) oder Zielhoehe zuerst festlegen (Winkel wird
  daraus berechnet).
- Dritte Option "Steigende GF" (Sonderfall, 0 bis 3 Grad): erlaubt eine GF,
  die statt zu fallen steigt. Neuer Helfer vfl-bogen-uebergang erkennt
  anhand der tatsaechlichen Frame-Neigung, ob 0/1/2 Uebergangsboegen
  (Vario_Bogen_auf_3/ab_3) noetig sind, und baut sie automatisch - im
  Normalfall (fallend->fallend) ein No-Op.

Lisp/vf_spec.lsp (Kollegen-Spec/Headless-Bau-System) um die drei neuen
Felder (laenge_modus/sep_vor/sep_nach/steigend_modus) erweitert, mit
Defaults, die alte Specs unveraendert lassen - haelt die "Spec-Pfad ==
interaktiver Pfad"-Invariante seines Systems (TEST_HUNDM05/VF_SPEC_BAU)
intakt. tests/test_vf_spec.lsp entsprechend mit neuen Testfaellen ergaenzt.

Neuer Regressionstest TEST_LINIENZUG_STUFE2 (linienzug_tests_stufe2.json)
sowie Anleitung doc/vf_linienzug_modus1_anleitung.md aus dem urspruenglichen
Commit uebernommen; die Gefaelle-Hoehe/Winkel-Optionsreihenfolge im
Testdatensatz an die jetzt geltende (Kollegen-)Konvention angepasst.

Offen (bewusst zurueckgestellt): lib/vf_spec_export.py (Python-Vorwaerts-
Emitter, Pendant zu vf_spec.lsp) noch nicht nachgezogen - siehe Folge-Commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Yuelin Wang
2026-09-11 14:18:31 +02:00
co-authored by Claude Sonnet 5
parent f4edb7339d
commit 23d653c4b3
8 changed files with 1092 additions and 148 deletions
+445 -127
View File
@@ -2403,6 +2403,75 @@
(vfl-frame-3grad pt hz)) (vfl-frame-3grad pt hz))
frame)) frame))
;; Genereller Uebergang zwischen zwei Basis-Neigungen (0/+3/-3 Grad,
;; Vorzeichenkonvention wie frame->hz-winkel: positiv=fallend, negativ=
;; steigend). Die Bibliothek kennt nur einen 3-Grad-Bogen gegenueber FLACH
;; (0 Grad) - ein Wechsel zwischen der fallenden (+3) und der steigenden (-3)
;; Basis braucht daher IMMER zwei Boegen ueber 0 als Zwischenhalt. "auf"-
;; Boegen senken die Neigung numerisch (Richtung steigend), "ab"-Boegen
;; erhoehen sie (Richtung fallend) - siehe vfl-nach-3grad/vfl-hor-koerper-
;; bauen, deren Uebergaenge hier verallgemeinert werden. Ist kein Uebergang
;; noetig (Start-/Zielbasis identisch), no-op.
;; Rueckgabe: (neuer-frame fussabdruck-summe) - fussabdruck-summe (mm, auf hz
;; projiziert) muss der Aufrufer von der geplanten Restlaenge/Zielstrecke
;; abziehen.
(defun vfl-bogen-uebergang (frame ziel-winkel / t0 basis-ziel hz pt m summe vor-pt)
(setq hz (car (frame->hz-winkel frame)))
(setq t0 (cadr (frame->hz-winkel frame)))
(setq t0 (cond ((< (abs t0) *vfk-frame-flach-toleranz*) 0)
((< t0 0) (- (fix *vfk-gefaelle-winkel*)))
(t (fix *vfk-gefaelle-winkel*))))
(setq basis-ziel (cond ((< (abs ziel-winkel) *vfl-gf-winkel-toleranz*) 0)
((< ziel-winkel 0) (- (fix *vfk-gefaelle-winkel*)))
(t (fix *vfk-gefaelle-winkel*))))
(setq summe 0.0)
(if (/= t0 basis-ziel)
(progn
;; Schritt 1: t0 -> 0 (falls t0 noch nicht flach ist).
(if (/= t0 0)
(progn
(setq vor-pt (car frame))
(if (> t0 0)
(progn
(setq m (get-bogen-mass bogen-auf (fix *vfk-gefaelle-winkel*)))
(princ (ssg-text "vfl-bogen-auf3-uebergang"))
(setq pt (insert-rotated-block-with-ks "Vario_Bogen_auf_3_TEF_rechts" vor-pt
(fix *vfk-gefaelle-winkel*) (car m) (caddr m) hz)))
(progn
(setq m (get-bogen-mass bogen-ab (fix *vfk-gefaelle-winkel*)))
(princ (ssg-text "vfl-bogen-ab3-uebergang"))
(setq pt (insert-rotated-block-with-ks "Vario_Bogen_ab_3_TEF_rechts" vor-pt
(- (fix *vfk-gefaelle-winkel*)) (car m) (caddr m) hz)))
)
(setq summe (+ summe (vfl-projiziere-distanz vor-pt pt hz)))
(setq frame (make-frame-from-dir pt (hz-winkel->xu hz 0)))
)
)
;; Schritt 2: 0 -> basis-ziel (falls das Ziel nicht flach ist).
(if (/= basis-ziel 0)
(progn
(setq vor-pt (car frame))
(if (> basis-ziel 0)
(progn
(setq m (get-bogen-mass bogen-ab (fix *vfk-gefaelle-winkel*)))
(princ (ssg-text "vfl-bogen-ab3-uebergang"))
(setq pt (insert-rotated-block-with-ks "Vario_Bogen_ab_3_TEF_rechts" vor-pt
0 (car m) (caddr m) hz)))
(progn
(setq m (get-bogen-mass bogen-auf (fix *vfk-gefaelle-winkel*)))
(princ (ssg-text "vfl-bogen-auf3-uebergang"))
(setq pt (insert-rotated-block-with-ks "Vario_Bogen_auf_3_TEF_rechts" vor-pt
0 (car m) (caddr m) hz)))
)
(setq summe (+ summe (vfl-projiziere-distanz vor-pt pt hz)))
(setq frame (make-frame-from-dir pt (hz-winkel->xu hz basis-ziel)))
)
)
)
)
(list frame summe)
)
;; Horizontales Sub-Segment bauen. Die flache Zone (0 Grad) wird NICHT mehr ;; 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 ;; 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 ;; FLACH. Der ab_3-Uebergang kommt erst, wenn ein 3-Grad-Element folgt
@@ -3461,7 +3530,9 @@
anzahl-gf anzahl-vf vfl-nummer lastEnt seg-lastEnt anzahl-gf anzahl-vf vfl-nummer lastEnt seg-lastEnt
gf-max-winkel gf-ok kettenanfang gf-max-winkel gf-ok kettenanfang
as-vorhanden erg old-error vfl-ins dbg-an as-vorhanden erg old-error vfl-ins dbg-an
hoehe-vorschlaege hoehe-ab hoehe-auf hoehe-fallback) hoehe-vorschlaege hoehe-ab hoehe-auf hoehe-fallback
gf-laenge-modus gf-steigend-modus sep-vor-gf sep-nach-gf
sep-anzahl erg-bogen)
(princ "\n\n=========================================") (princ "\n\n=========================================")
(princ (ssg-text "vfl-modus1-header")) (princ (ssg-text "vfl-modus1-header"))
(princ "\n=========================================") (princ "\n=========================================")
@@ -3706,146 +3777,393 @@
;; --- Neue Linie: GF (Nutzer legt den Typ explizit fest) --- ;; --- Neue Linie: GF (Nutzer legt den Typ explizit fest) ---
((= wahl "Linie-GF") ((= wahl "Linie-GF")
;; Fahrtrichtung folgt dem vorherigen Element (Frame); nur das erste
;; Segment (frame=nil) definiert die Richtung frei.
(setq linie-mess (vfl-neue-linie-messen p-aktuell
(if frame (car (frame->hz-winkel frame)) nil)))
(if (null linie-mess) (progn (princ (ssg-text "vfl-abgebrochen")) (exit)))
(setq deltaL (car linie-mess) hz-neu (cadr linie-mess))
(setq kettenanfang (null frame)) (setq kettenanfang (null frame))
(setq gf-max-winkel (float (ssg-cfg-or "vario" "gefaelle_winkel" 3))) (setq gf-max-winkel (float (ssg-cfg-or "vario" "gefaelle_winkel" 3)))
(if (< deltaL 1.0) (setq gf-ok nil sep-vor-gf nil sep-nach-gf nil)
(princ (ssg-text "vfl-fehler-linie-zu-kurz")) ;; NEU: Vorfrage "Wie soll die Laenge bestimmt werden?" - IMMER
;; gezeigt. gf-laenge-modus="2" => Zielhoehe zuerst (Zweig B), "3" =>
;; steigende GF (Sonderfall, Zweig C, KEIN Separator), sonst (Default
;; "1") Endpunkt waehlen (Zweig A, heutiges Verhalten + Separator).
(princ (ssg-text "vfl-gf-laenge-modus-header"))
(princ (ssg-text "vfl-gf-laenge-modus-opt-endpunkt"))
(princ (ssg-text "vfl-gf-laenge-modus-opt-hoehe"))
(princ (ssg-text "vfl-gf-laenge-modus-opt-steigend"))
(setq gf-laenge-modus (vfl-menu (ssg-text "prompt-wahl-1-2")
(list (ssg-text "vfl-gf-laenge-modus-opt-endpunkt")
(ssg-text "vfl-gf-laenge-modus-opt-hoehe")
(ssg-text "vfl-gf-laenge-modus-opt-steigend")) 1
"vfl-gf-laenge-modus-header"))
(cond
((= gf-laenge-modus "3")
;; --- Zweig C: Steigende GF (Sonderfall, bis gf-max-winkel Grad) ---
;; Eigenstaendig: KEIN Separator (gilt nur fuer die normale
;; fallende GF). Innerhalb dieses Zweigs nochmal dieselbe Wahl
;; (Endpunkt vs. Zielhoehe), mit Ziel-Richtung "Auf" statt "Ab".
;; Der noetige Uebergangsbogen (falls die Vorgaenger-Neigung nicht
;; schon zur steigenden Basis passt) wird zentral im gemeinsamen
;; Abschluss unten ueber vfl-bogen-uebergang eingefuegt.
(princ (ssg-text "vfl-gf-laenge-modus-header"))
(princ (ssg-text "vfl-gf-laenge-modus-opt-endpunkt"))
(princ (ssg-text "vfl-gf-laenge-modus-opt-hoehe"))
(setq gf-steigend-modus (vfl-menu (ssg-text "prompt-wahl-1-2")
(list (ssg-text "vfl-gf-laenge-modus-opt-endpunkt")
(ssg-text "vfl-gf-laenge-modus-opt-hoehe")) 1
"vfl-gf-laenge-modus-header"))
(if (= gf-steigend-modus "2")
(progn
;; --- Zweig C2: Zielhoehe zuerst (steigend) ---
(if kettenanfang
(progn
(setq linie-mess (vfl-neue-linie-messen p-aktuell nil))
(if (null linie-mess) (progn (princ (ssg-text "vfl-abgebrochen")) (exit)))
(setq hz-neu (cadr linie-mess))
(setq erg (vfl-kettenanfang-baustein p-aktuell hz-neu 1.0e9 as-seite as-vorhanden))
(setq frame (nth 0 erg) p-aktuell (car frame))
)
(setq hz-neu (car (frame->hz-winkel frame)))
)
(setq gf-ok t)
(setq hoehe-neu
(vfl-in-real (ssg-textf "vfl-prompt-hoehe-linienendpunkt"
(list (rtos (caddr p-aktuell) 2 1)))))
(if (null hoehe-neu) (setq hoehe-neu (caddr p-aktuell)))
(setq deltaH (- hoehe-neu (caddr p-aktuell)))
(if (< deltaH 0.0)
(progn
(vfl-meldung (ssg-text "vfl-alert-gf-steigend-falsche-richtung"))
(setq gf-ok nil))
(progn
(setq winkel
(vfl-in-real (ssg-textf "vfl-prompt-neigungswinkel-steigend" (list (rtos gf-max-winkel 2 1)))))
(if (null winkel) (setq winkel gf-max-winkel))
(if (or (< winkel 0.0) (> winkel gf-max-winkel))
(progn
(vfl-meldung (ssg-textf "vfl-alert-winkel-ungueltig-steigend"
(list (rtos winkel 2 1) (rtos gf-max-winkel 2 1))))
(setq gf-ok nil))
(if (and (< winkel 1.0e-6) (> deltaH 1.0e-6))
(progn
(vfl-meldung (ssg-textf "vfl-alert-winkel-ungueltig-steigend"
(list (rtos winkel 2 1) (rtos gf-max-winkel 2 1))))
(setq gf-ok nil))
(progn
(setq deltaL
(if (< winkel 1.0e-6) 0.0
(/ deltaH (/ (sin (* winkel (/ pi 180.0))) (cos (* winkel (/ pi 180.0)))))))
(if (< deltaL 1.0)
(progn (princ (ssg-text "vfl-fehler-linie-zu-kurz")) (setq gf-ok nil)))
(setq winkel (- winkel))
)
)
)
)
)
)
(progn
;; --- Zweig C1: Endpunkt waehlen (steigend) ---
(setq linie-mess (vfl-neue-linie-messen p-aktuell
(if frame (car (frame->hz-winkel frame)) nil)))
(if (null linie-mess) (progn (princ (ssg-text "vfl-abgebrochen")) (exit)))
(setq deltaL (car linie-mess) hz-neu (cadr linie-mess))
(if (< deltaL 1.0)
(princ (ssg-text "vfl-fehler-linie-zu-kurz"))
(progn
(if kettenanfang
(progn
(setq erg (vfl-kettenanfang-baustein p-aktuell hz-neu deltaL as-seite as-vorhanden))
(setq frame (nth 0 erg) deltaL (nth 1 erg) p-aktuell (car frame))
)
)
(if (< deltaL 1.0)
(princ (ssg-text "vfl-fehler-linie-zu-kurz"))
(progn
(setq gf-ok t)
(setq winkel
(vfl-in-real (ssg-textf "vfl-prompt-neigungswinkel-steigend" (list (rtos gf-max-winkel 2 1)))))
(if (null winkel) (setq winkel gf-max-winkel))
(if (or (< winkel 0.0) (> winkel gf-max-winkel))
(progn
(vfl-meldung (ssg-textf "vfl-alert-winkel-ungueltig-steigend"
(list (rtos winkel 2 1) (rtos gf-max-winkel 2 1))))
(setq gf-ok nil))
(setq winkel (- winkel))
)
)
)
)
)
)
)
)
((= gf-laenge-modus "2")
;; --- Zweig B: Zielhoehe zuerst festlegen, Laenge wird berechnet ---
(if kettenanfang
(progn
(setq linie-mess (vfl-neue-linie-messen p-aktuell nil))
(if (null linie-mess) (progn (princ (ssg-text "vfl-abgebrochen")) (exit)))
(setq hz-neu (cadr linie-mess))
(setq erg (vfl-kettenanfang-baustein p-aktuell hz-neu 1.0e9 as-seite as-vorhanden))
(setq frame (nth 0 erg) p-aktuell (car frame))
)
(setq hz-neu (car (frame->hz-winkel frame)))
)
(setq rad3 (* gf-max-winkel (/ pi 180.0)))
(princ (ssg-text "vfl-sep-vor-gf-frage"))
(princ (ssg-text "vfl-ja"))
(princ (ssg-text "vfl-nein"))
(setq sep-vor-gf (= (vfl-menu (ssg-text "vfl-prompt-wahl-1-2-def2")
(list (ssg-text "vfl-ja") (ssg-text "vfl-nein")) 2
"vfl-sep-vor-gf-frage") "1"))
(princ (ssg-text "vfl-sep-nach-gf-frage"))
(princ (ssg-text "vfl-ja"))
(princ (ssg-text "vfl-nein"))
(setq sep-nach-gf (= (vfl-menu (ssg-text "vfl-prompt-wahl-1-2-def2")
(list (ssg-text "vfl-ja") (ssg-text "vfl-nein")) 2
"vfl-sep-nach-gf-frage") "1"))
(setq sep-anzahl (+ (if sep-vor-gf 1 0) (if sep-nach-gf 1 0)))
(setq gf-ok t)
(setq hoehe-neu
(vfl-in-real (ssg-textf "vfl-prompt-hoehe-linienendpunkt"
(list (rtos (vfl-gf-hoehe-vorschlag (caddr p-aktuell)) 2 1)))))
(if (null hoehe-neu) (setq hoehe-neu (vfl-gf-hoehe-vorschlag (caddr p-aktuell))))
(setq deltaH (- hoehe-neu (caddr p-aktuell)))
(setq richtung (if (>= deltaH 0.0) "Auf" "Ab"))
(setq deltaH (abs deltaH))
(if (= richtung "Auf")
(progn
(vfl-meldung (ssg-text "vfl-alert-gf-kann-nicht-steigen"))
(if dbg-an
(dbgmsg (strcat "GEOMETRIE FEHLGESCHLAGEN: Linie-GF (Zielhoehe zuerst) kann nicht steigen"
" (deltaH=" (rtos deltaH 2 0) ")")))
(setq gf-ok nil))
(progn
(if (> sep-anzahl 0)
(setq deltaH (max 0.0 (- deltaH (* sep-anzahl *vfk-separator-laenge* (sin rad3))))))
(setq winkel
(vfl-in-real (ssg-textf "vfl-prompt-neigungswinkel-laenge-berechnet" (list (rtos gf-max-winkel 2 1)))))
(if (null winkel) (setq winkel gf-max-winkel))
(if (or (<= winkel 0.0) (> winkel gf-max-winkel))
(progn
(vfl-meldung (ssg-textf "vfl-alert-winkel-ungueltig"
(list (rtos winkel 2 1) (rtos gf-max-winkel 2 1))))
(setq gf-ok nil))
(progn
(setq deltaL (/ deltaH (/ (sin (* winkel (/ pi 180.0))) (cos (* winkel (/ pi 180.0))))))
(if (< deltaL 1.0)
(progn (princ (ssg-text "vfl-fehler-linie-zu-kurz")) (setq gf-ok nil)))
)
)
)
)
)
(t
;; --- Zweig A: Endpunkt waehlen (heutiges Verhalten + Separator) ---
(setq linie-mess (vfl-neue-linie-messen p-aktuell
(if frame (car (frame->hz-winkel frame)) nil)))
(if (null linie-mess) (progn (princ (ssg-text "vfl-abgebrochen")) (exit)))
(setq deltaL (car linie-mess) hz-neu (cadr linie-mess))
(if (< deltaL 1.0)
(princ (ssg-text "vfl-fehler-linie-zu-kurz"))
(progn
;; Kettenanfang: AS-Element (falls gewuenscht) SOFORT einfuegen
;; (Richtung hz-neu jetzt bekannt) und die GF-Restlaenge aus dem
;; TATSAECHLICHEN KS_AUS nachrechnen (siehe vfl-kettenanfang-
;; baustein). Keine Fussabdruck-Schaetzung mehr: sowohl KS_EIN-
;; als auch Ursprung-basierte Vorab-Messung ignorieren die
;; Drehteller-Rotation, die insert-block-mixed-to-ks beim
;; echten Einfuegen anwendet, und liefern daher einen falschen
;; Wert (empirisch bestaetigt: ~210mm Schaetzung vs.
;; tatsaechlich ~420mm noetiger Versatz).
(if kettenanfang
(progn
(setq erg (vfl-kettenanfang-baustein p-aktuell hz-neu deltaL as-seite as-vorhanden))
(setq frame (nth 0 erg) deltaL (nth 1 erg) p-aktuell (car frame))
)
)
;; Nach dem AS-Einfuegen kann die reale Restlaenge (aus dem
;; tatsaechlichen KS_AUS neu gemessen) kleiner als der
;; urspruenglich gepickte Rohwert sein - bei einem knapp
;; gepickten Punkt sogar NEGATIV. Sofortiger Abbruch DIESES
;; Zweigs (kein Weiterfragen fuer ein Segment, das ohnehin
;; nicht gebaut werden kann).
(if (< deltaL 1.0)
(princ (ssg-text "vfl-fehler-linie-zu-kurz"))
(progn
(setq gf-ok t)
;; Separator VOR/NACH JETZT fragen, BEVOR Hoehe/Winkel
;; berechnet wird - gueltige Trennstellen GF-GF bzw.
;; GF-Umlenkstation. Beide Laengen (genaehert mit dem
;; Referenzwinkel gf-max-winkel, der tatsaechliche
;; GF-Winkel steht erst danach fest) werden SOFORT vom
;; gepickten deltaL abgezogen, damit die Kette exakt am
;; gepickten Punkt endet statt danach ueberzustehen.
(setq rad3 (* gf-max-winkel (/ pi 180.0)))
(princ (ssg-text "vfl-sep-vor-gf-frage"))
(princ (ssg-text "vfl-ja"))
(princ (ssg-text "vfl-nein"))
(setq sep-vor-gf (= (vfl-menu (ssg-text "vfl-prompt-wahl-1-2-def2")
(list (ssg-text "vfl-ja") (ssg-text "vfl-nein")) 2
"vfl-sep-vor-gf-frage") "1"))
(princ (ssg-text "vfl-sep-nach-gf-frage"))
(princ (ssg-text "vfl-ja"))
(princ (ssg-text "vfl-nein"))
(setq sep-nach-gf (= (vfl-menu (ssg-text "vfl-prompt-wahl-1-2-def2")
(list (ssg-text "vfl-ja") (ssg-text "vfl-nein")) 2
"vfl-sep-nach-gf-frage") "1"))
(setq sep-anzahl (+ (if sep-vor-gf 1 0) (if sep-nach-gf 1 0)))
(if (> sep-anzahl 0)
(setq deltaL (- deltaL (* sep-anzahl *vfk-separator-laenge* (cos rad3)))))
(if (< deltaL 1.0)
(progn (princ (ssg-text "vfl-fehler-linie-zu-kurz")) (setq gf-ok nil)))
(if gf-ok
(progn
(if (vfl-wizard-aktiv)
(vl-catch-all-apply 'vflw-gruppe-gefaelle-impl
(list (vfl-gf-hoehe-vorschlag (caddr p-aktuell)))))
(princ (ssg-text "vfl-gefaelle-festlegen-header"))
(princ (ssg-text "vfl-gefaelle-opt-hoehe"))
(princ (ssg-text "vfl-gefaelle-opt-winkel"))
(setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2")
(list (ssg-text "vfl-gefaelle-opt-hoehe")
(ssg-text "vfl-gefaelle-opt-winkel")) 1
"vfl-gefaelle-festlegen-header"))
(if (= antwort "2")
(progn
;; Winkel direkt vorgeben - deltaH ergibt sich aus
;; deltaL*tan(winkel). deltaL ist hier schon um
;; Separator VOR/NACH reduziert.
(setq winkel
(vfl-in-real (ssg-textf "vfl-prompt-neigungswinkel" (list (rtos gf-max-winkel 2 1)))))
(if (null winkel) (setq winkel gf-max-winkel))
(if (or (<= winkel 0.0) (> winkel gf-max-winkel))
(progn
(vfl-meldung (ssg-textf "vfl-alert-winkel-ungueltig"
(list (rtos winkel 2 1) (rtos gf-max-winkel 2 1))))
(setq gf-ok nil))
(progn
(setq richtung "Ab")
(setq deltaH (* deltaL (/ (sin (* winkel (/ pi 180.0)))
(cos (* winkel (/ pi 180.0))))))
)
)
)
(progn
;; Gegebene Hoehe - Winkel wird daraus abgeleitet
;; und gegen den GF-Maximalwinkel geprueft.
(setq hoehe-neu
(vfl-in-real (ssg-textf "vfl-prompt-hoehe-linienendpunkt"
(list (rtos (vfl-gf-hoehe-vorschlag (caddr p-aktuell)) 2 1)))))
(if (null hoehe-neu) (setq hoehe-neu (vfl-gf-hoehe-vorschlag (caddr p-aktuell))))
(setq deltaH (- hoehe-neu (caddr p-aktuell)))
(setq richtung (if (>= deltaH 0.0) "Auf" "Ab"))
(setq deltaH (abs deltaH))
(if (> sep-anzahl 0)
(setq deltaH (max 0.0 (- deltaH (* sep-anzahl *vfk-separator-laenge* (sin rad3))))))
(cond
((= richtung "Auf")
(vfl-meldung (ssg-text "vfl-alert-gf-kann-nicht-steigen"))
(if dbg-an
(dbgmsg (strcat "GEOMETRIE FEHLGESCHLAGEN: Linie-GF kann nicht steigen"
" (deltaL=" (rtos deltaL 2 0) " deltaH=" (rtos deltaH 2 0) ")")))
(setq gf-ok nil))
(t
(setq winkel (* (atan (/ deltaH deltaL)) (/ 180.0 pi)))
(if (> winkel gf-max-winkel)
(progn
(vfl-meldung (ssg-textf "vfl-alert-gefaelle-zu-steil"
(list (rtos winkel 2 1) (rtos gf-max-winkel 2 1))))
(if dbg-an
(dbgmsg (strcat "GEOMETRIE FEHLGESCHLAGEN: Linie-GF zu steil (deltaL="
(rtos deltaL 2 0) " deltaH=" (rtos deltaH 2 0)
" winkel=" (rtos winkel 2 1) " max=" (rtos gf-max-winkel 2 1) ")")))
(setq gf-ok nil))
)
)
)
)
)
)
)
)
)
)
)
)
)
;; --- Gemeinsamer Abschluss fuer ALLE drei Zweige ---
(if gf-ok
(progn (progn
;; Kettenanfang: AS-Element (falls gewuenscht) SOFORT einfuegen ;; Uebergangsbogen(n) einfuegen, falls die aktuelle Frame-Neigung
;; (Richtung hz-neu jetzt bekannt) und die GF-Restlaenge aus dem ;; nicht zur Ziel-Neigung dieser Linie passt: noetig fuer den
;; TATSAECHLICHEN KS_AUS nachrechnen (siehe vfl-kettenanfang- ;; Steigend-Sonderfall (Zweig C, winkel < 0) UND falls der
;; baustein). Keine Fussabdruck-Schaetzung mehr: sowohl KS_EIN- als ;; VORGAENGER eine steigende GF war und jetzt normal (Zweig A/B)
;; auch Ursprung-basierte Vorab-Messung ignorieren die Dreh­teller- ;; weitergebaut wird. Im unveraenderten Normalfall (Vorgaenger-
;; Rotation, die insert-block-mixed-to-ks beim echten Einfuegen ;; Neigung bereits 0/+3, Ziel +3 wie bisher) ist die Bedingung
;; anwendet, und liefern daher einen falschen Wert (empirisch ;; falsch - kein Verhaltensunterschied zu vorher.
;; bestaetigt: ~210mm Schaetzung vs. tatsaechlich ~420mm noetiger (if (or (< winkel 0.0) (< (cadr (frame->hz-winkel frame)) (- *vfl-gf-winkel-toleranz*)))
;; Versatz).
(if kettenanfang
(progn (progn
(setq erg (vfl-kettenanfang-baustein p-aktuell hz-neu deltaL as-seite as-vorhanden)) (setq erg-bogen (vfl-bogen-uebergang frame winkel))
(setq frame (nth 0 erg) deltaL (nth 1 erg) p-aktuell (car frame)) (setq frame (car erg-bogen))
(setq deltaL (- deltaL (cadr erg-bogen)))
(if (< deltaL 1.0)
(progn (princ (ssg-text "vfl-fehler-linie-zu-kurz")) (setq gf-ok nil)))
) )
) )
;; Nach dem AS-Einfuegen kann die reale Restlaenge (aus dem (if gf-ok
;; tatsaechlichen KS_AUS neu gemessen) kleiner als der urspruenglich
;; gepickte Rohwert sein - bei einem knapp gepickten Punkt sogar
;; NEGATIV (das AS-Element allein braucht bereits mehr Platz als bis
;; zum Klickpunkt vorhanden war). Ohne diese zweite Pruefung wuerde
;; vfl-insert-gf-segment mit negativem deltaL "erfolgreich" eine
;; entartete/unsichtbare GF-Strecke bauen (leerer Sprung zwischen AS
;; und dem naechsten Element) statt den Nutzer erneut picken zu
;; lassen - siehe vfl-fehler-linie-zu-kurz oben (dieselbe Meldung).
;; Sofortiger Abbruch DIESES Zweigs (kein Weiterfragen nach Winkel/
;; Hoehe fuer ein Segment, das ohnehin nicht gebaut werden kann) -
;; gf-ok wird unten sonst mehrfach ueberschrieben.
(if (< deltaL 1.0)
(princ (ssg-text "vfl-fehler-linie-zu-kurz"))
(progn (progn
(setq gf-ok t) ;; Separator VOR (Antwort aus der Vorab-Frage oben) - VOR
(if (vfl-wizard-aktiv) ;; dem GF-Segment selbst einfuegen, am GF-Winkel.
(vl-catch-all-apply 'vflw-gruppe-gefaelle-impl (if sep-vor-gf
(list (vfl-gf-hoehe-vorschlag (caddr p-aktuell)))))
(princ (ssg-text "vfl-gefaelle-festlegen-header"))
(princ (ssg-text "vfl-gefaelle-opt-hoehe"))
(princ (ssg-text "vfl-gefaelle-opt-winkel"))
(setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2")
(list (ssg-text "vfl-gefaelle-opt-hoehe")
(ssg-text "vfl-gefaelle-opt-winkel")) 1
"vfl-gefaelle-festlegen-header"))
(if (= antwort "2")
(progn (progn
;; Winkel direkt vorgeben - deltaH ergibt sich aus deltaL*tan(winkel). (setq frame (make-frame-from-dir
(setq winkel (gf-insert-hz-with-ks "Staustrecke_Separator_SP_300_mm"
(vfl-in-real (ssg-textf "vfl-prompt-neigungswinkel" (list (rtos gf-max-winkel 2 1))))) (car frame) hz-neu winkel *vfk-separator-laenge* 0)
(if (null winkel) (setq winkel gf-max-winkel)) (hz-winkel->xu hz-neu winkel)))
(if (or (<= winkel 0.0) (> winkel gf-max-winkel)) (setq *vfl-acc-separator* (1+ *vfl-acc-separator*))
(progn
(vfl-meldung (ssg-textf "vfl-alert-winkel-ungueltig"
(list (rtos winkel 2 1) (rtos gf-max-winkel 2 1))))
(setq gf-ok nil))
(progn
(setq richtung "Ab")
(setq deltaH (* deltaL (/ (sin (* winkel (/ pi 180.0)))
(cos (* winkel (/ pi 180.0))))))
)
)
)
(progn
;; Gegebene Hoehe - wie bisher, aber Winkel wird daraus abgeleitet
;; und gegen den GF-Maximalwinkel geprueft (GF kann nicht steigen).
;; p-aktuell ist ab hier immer der reale Referenzpunkt (bei
;; Kettenanfang das echte KS_AUS des AS-Elements). Vorschlag/
;; Enter-Default = vfl-gf-hoehe-vorschlag (siehe dort), NICHT
;; die unveraenderte Ist-Hoehe - sonst waere deltaH=0 und die
;; GF-Pruefung wiese den Wert als "kann nicht steigen" ab.
(setq hoehe-neu
(vfl-in-real (ssg-textf "vfl-prompt-hoehe-linienendpunkt"
(list (rtos (vfl-gf-hoehe-vorschlag (caddr p-aktuell)) 2 1)))))
(if (null hoehe-neu) (setq hoehe-neu (vfl-gf-hoehe-vorschlag (caddr p-aktuell))))
(setq deltaH (- hoehe-neu (caddr p-aktuell)))
(setq richtung (if (>= deltaH 0.0) "Auf" "Ab"))
(setq deltaH (abs deltaH))
(cond
((= richtung "Auf")
(vfl-meldung (ssg-text "vfl-alert-gf-kann-nicht-steigen"))
(if dbg-an
(dbgmsg (strcat "GEOMETRIE FEHLGESCHLAGEN: Linie-GF kann nicht steigen"
" (deltaL=" (rtos deltaL 2 0) " deltaH=" (rtos deltaH 2 0) ")")))
(setq gf-ok nil))
(t
(setq winkel (* (atan (/ deltaH deltaL)) (/ 180.0 pi)))
(if (> winkel gf-max-winkel)
(progn
(vfl-meldung (ssg-textf "vfl-alert-gefaelle-zu-steil"
(list (rtos winkel 2 1) (rtos gf-max-winkel 2 1))))
(if dbg-an
(dbgmsg (strcat "GEOMETRIE FEHLGESCHLAGEN: Linie-GF zu steil (deltaL="
(rtos deltaL 2 0) " deltaH=" (rtos deltaH 2 0)
" winkel=" (rtos winkel 2 1) " max=" (rtos gf-max-winkel 2 1) ")")))
(setq gf-ok nil))
)
)
)
) )
) )
(if gf-ok (if dbg-an
(dbgmsg (strcat "GEOMETRIE: vfl-insert-gf-segment (deltaL=" (rtos deltaL 2 0)
" hz=" (rtos hz-neu 2 1) " winkel=" (rtos winkel 2 1) ")")))
(setq frame (vfl-insert-gf-segment (car frame) hz-neu deltaL winkel))
(setq anzahl-gf (1+ anzahl-gf))
(vfl-acc-gf-seg (/ deltaL (cos (* (float winkel) (/ pi 180.0)))) winkel)
(setq letzter-typ "GF")
(setq p-aktuell (car frame))
(if dbg-an
(dbgmsg (strcat "GEOMETRIE ERFOLG: GF-Segment gebaut, neue Position="
(dbg-tostring p-aktuell))))
;; Separator NACH (Antwort aus der Vorab-Frage oben) - DIREKT
;; nach dem GF-Segment einfuegen, noch VOR der Kettenende-
;; Frage.
(if sep-nach-gf
(progn (progn
(if dbg-an (setq frame (make-frame-from-dir
(dbgmsg (strcat "GEOMETRIE: vfl-insert-gf-segment (deltaL=" (rtos deltaL 2 0) (gf-insert-hz-with-ks "Staustrecke_Separator_SP_300_mm"
" hz=" (rtos hz-neu 2 1) " winkel=" (rtos winkel 2 1) ")"))) (car frame) hz-neu winkel *vfk-separator-laenge* 0)
(setq frame (vfl-insert-gf-segment (car frame) hz-neu deltaL winkel)) (hz-winkel->xu hz-neu winkel)))
(setq anzahl-gf (1+ anzahl-gf)) (setq *vfl-acc-separator* (1+ *vfl-acc-separator*))
(vfl-acc-gf-seg (/ deltaL (cos (* (float winkel) (/ pi 180.0)))) winkel) (setq letzter-typ "Separator")
(setq letzter-typ "GF")
(setq p-aktuell (car frame)) (setq p-aktuell (car frame))
(if dbg-an
(dbgmsg (strcat "GEOMETRIE ERFOLG: GF-Segment gebaut, neue Position="
(dbg-tostring p-aktuell))))
(princ (ssg-text "vfl-ist-kettenende-header"))
(princ (ssg-text "vfl-kettenende-opt-ja-es"))
(princ (ssg-text "vfl-kettenende-opt-ja-ohne-es"))
(princ (ssg-text "vfl-kettenende-opt-nein"))
(setq antwort (vfl-menu (ssg-text "vfl-prompt-wahl-1-3-def3")
(list (ssg-text "vfl-kettenende-opt-ja-es")
(ssg-text "vfl-kettenende-opt-ja-ohne-es")
(ssg-text "vfl-kettenende-opt-nein")) 3
"vfl-ist-kettenende-header"))
(cond
((= antwort "1")
(setq es-seite (vfl-frage-es-seite))
(setq frame (vfl-insert-es-element "GF" frame hz-neu winkel
(caddr (car frame)) es-seite))
(setq fertig t))
((= antwort "2") (setq fertig t))
)
) )
) )
(princ (ssg-text "vfl-ist-kettenende-header"))
(princ (ssg-text "vfl-kettenende-opt-ja-es"))
(princ (ssg-text "vfl-kettenende-opt-ja-ohne-es"))
(princ (ssg-text "vfl-kettenende-opt-nein"))
(setq antwort (vfl-menu (ssg-text "vfl-prompt-wahl-1-3-def3")
(list (ssg-text "vfl-kettenende-opt-ja-es")
(ssg-text "vfl-kettenende-opt-ja-ohne-es")
(ssg-text "vfl-kettenende-opt-nein")) 3
"vfl-ist-kettenende-header"))
(cond
((= antwort "1")
(setq es-seite (vfl-frage-es-seite))
(setq frame (vfl-insert-es-element "GF" frame hz-neu winkel
(caddr (car frame)) es-seite))
(setq fertig t))
((= antwort "2") (setq fertig t))
)
) )
) )
) )
+46 -7
View File
@@ -49,6 +49,12 @@
(setq *vsp-tab-seite* '(("links" . "1") ("rechts" . "2"))) (setq *vsp-tab-seite* '(("links" . "1") ("rechts" . "2")))
(setq *vsp-tab-variante* '(("aussen" . "1") ("innen" . "2"))) (setq *vsp-tab-variante* '(("aussen" . "1") ("innen" . "2")))
(setq *vsp-tab-gefaelle* '(("hoehe" . "1") ("winkel" . "2"))) (setq *vsp-tab-gefaelle* '(("hoehe" . "1") ("winkel" . "2")))
;; Vorwahl "Wie soll die Laenge der Gefaellestrecke bestimmt werden?"
;; (vfl-menu in vf-linienzug-modus, wahl="Linie-GF"). Die innere Wahl im
;; Steigend-Sonderfall (Endpunkt/Hoehe) nutzt dieselbe Tabelle - dort sind
;; nur die Codes "1"/"2" gueltig, "steigend" kommt dort nie vor.
(setq *vsp-tab-gf-laenge-modus* '(("endpunkt" . "1") ("hoehe" . "2")
("steigend" . "3")))
(setq *vsp-tab-verteilung* '(("haelfte" . "1") ("einlauf" . "2"))) (setq *vsp-tab-verteilung* '(("haelfte" . "1") ("einlauf" . "2")))
(setq *vsp-tab-im-vf* '(("horizontal" . "1") ("vario-kurve" . "2") (setq *vsp-tab-im-vf* '(("horizontal" . "1") ("vario-kurve" . "2")
("auf-ab" . "3"))) ("auf-ab" . "3")))
@@ -219,7 +225,7 @@
(vsp-wert-str (ssg-val kopf "as_winkel") "as_winkel") (vsp-wert-str (ssg-val kopf "as_winkel") "as_winkel")
(vsp-code-raus *vsp-tab-seite* (ssg-val kopf "as_seite") "as_seite")))) (vsp-code-raus *vsp-tab-seite* (ssg-val kopf "as_seite") "as_seite"))))
(defun vsp-sektion (sek subs erster / label tab code art) (defun vsp-sektion (sek subs erster / label tab code art modus smodus)
(setq label (ssg-val sek "glied")) (setq label (ssg-val sek "glied"))
(setq *vsp-wo* (strcat "Sektion " (if label label "(ohne glied)"))) (setq *vsp-wo* (strcat "Sektion " (if label label "(ohne glied)")))
(cond (cond
@@ -250,12 +256,45 @@
(vsp-code-raus *vsp-tab-seite* (ssg-val sek "seite") "seite")) (vsp-code-raus *vsp-tab-seite* (ssg-val sek "seite") "seite"))
((= label "Linie-GF") ((= label "Linie-GF")
(vsp-linie sek) ;; Vorwahl "laenge_modus" (endpunkt/hoehe/steigend, Default
(setq art (ssg-val sek "gefaelle")) ;; "endpunkt" - deckt alte Specs ohne dieses Feld ab, identisch
(vsp-code-raus *vsp-tab-gefaelle* art "gefaelle") ;; zum bisherigen alleinigen Ablauf). "steigend" ist der
(if (= art "hoehe") ;; Sonderfall (GF bis 3 Grad steigend, KEIN Separator, eigene
(vsp-zahl "REAL" (ssg-val sek "hoehe") "hoehe") ;; innere Endpunkt/Hoehe-Wahl "steigend_modus"). Die DL(+hz)-
(vsp-zahl "REAL" (ssg-val sek "winkel") "winkel")) ;; Eingabe (vsp-linie) faellt bei "hoehe"/"steigend"+"hoehe" NUR
;; beim allerersten Segment der Kette an (Kettenanfang braucht
;; einen Pick fuer die freie Richtung; jedes weitere Segment erbt
;; die Richtung vom Vorgaenger ohne eigenen Pick) - siehe erster.
(setq modus (ssg-val sek "laenge_modus"))
(if (null modus) (setq modus "endpunkt"))
(vsp-code-raus *vsp-tab-gf-laenge-modus* modus "laenge_modus")
(cond
((= modus "steigend")
(setq smodus (ssg-val sek "steigend_modus"))
(if (null smodus) (setq smodus "endpunkt"))
(vsp-code-raus *vsp-tab-gf-laenge-modus* smodus "steigend_modus")
(if (= smodus "endpunkt")
(vsp-linie sek)
(if erster (vsp-linie sek)))
(if (= smodus "hoehe")
(vsp-zahl "REAL" (ssg-val sek "hoehe") "hoehe"))
(vsp-zahl "REAL" (ssg-val sek "winkel") "winkel"))
((= modus "hoehe")
(if erster (vsp-linie sek))
(vsp-raus "STR" (vsp-ja-code (ssg-val sek "sep_vor")))
(vsp-raus "STR" (vsp-ja-code (ssg-val sek "sep_nach")))
(vsp-zahl "REAL" (ssg-val sek "hoehe") "hoehe")
(vsp-zahl "REAL" (ssg-val sek "winkel") "winkel"))
(T ;; "endpunkt"
(vsp-linie sek)
(vsp-raus "STR" (vsp-ja-code (ssg-val sek "sep_vor")))
(vsp-raus "STR" (vsp-ja-code (ssg-val sek "sep_nach")))
(setq art (ssg-val sek "gefaelle"))
(vsp-code-raus *vsp-tab-gefaelle* art "gefaelle")
(if (= art "hoehe")
(vsp-zahl "REAL" (ssg-val sek "hoehe") "hoehe")
(vsp-zahl "REAL" (ssg-val sek "winkel") "winkel")))
)
(vsp-code-raus *vsp-tab-ende3* (ssg-val sek "ende") "ende")) (vsp-code-raus *vsp-tab-ende3* (ssg-val sek "ende") "ende"))
((= label "Linie-VF") ((= label "Linie-VF")
+194
View File
@@ -0,0 +1,194 @@
# VF-Linienzug Modus 1 - Anleitung anhand eines realen Beispiels
Diese Anleitung erklaert Modus 1 des VF-Linienzugs ("Manuelle Eingabe",
`vf-linienzug-modus` in `Lisp/vf_linienzug.lsp`) nicht abstrakt, sondern
Schritt fuer Schritt anhand eines echten, in BricsCAD gebauten und
verifizierten Beispiels. Dasselbe Beispiel ist als automatisierter
Regressionstest hinterlegt: `tests/testdata/linienzug_tests_stufe2.json`,
aufrufbar in BricsCAD ueber `TEST_LINIENZUG_STUFE2` (siehe
`tests/test_linienzug.lsp`). Wer die Zahlen selbst nachvollziehen will, kann
diesen Testlauf jederzeit wiederholen.
Fuer die Architektur-Details (Frame-Kette, Journal/XDATA, Wizard-Dialoge)
siehe `doc/variofoerderer.md` und `doc/VarioFoerderer_Linienzug_Prinzipien.md`.
Diese Anleitung konzentriert sich auf die Bedienung und die typischen
Stolperfallen.
## 1. Was ist Modus 1?
Modus 1 baut eine VF-Linienzug-Kette **interaktiv, Element fuer Element**:
Sie klicken/geben nacheinander Punkte, Laengen, Winkel und Ja/Nein-
Entscheidungen ein, und jede Antwort erzeugt sofort ein Stueck Geometrie
(Umlenkstation, Gefaellestrecke, VF-Einheit, Vario-Kurve, ...). Das
unterscheidet Modus 1 von:
- **Modus 2** (Ziel-Hoehe-Modus): Sie zeichnen vorher einen Pfad (LINE/ARC-
Kette) und eine Zielhoehe, das Programm loest die Kette automatisch.
- **Modus 3** (Vorwaerts-Nachbau): rekonstruiert eine Kette aus einer
bestehenden Zeichnung (Legacy, wird umgebaut).
**Aufruf:** Befehl `FOERDERANLAGE` (bzw. `VarioFoerderer`) -> Anlagen-Typ
"Linienzug" waehlen -> Modus "1" (Manuelle Eingabe).
Jede Eingabe wird in einem Journal mitgeschrieben und als XDATA auf den
fertigen `VF_n`-Block geschrieben. Ein Abbruch (ESC) loescht dadurch nichts -
die bis dahin gebaute Teilkette bleibt als Block stehen und ist per
Doppelklick weiter bearbeitbar.
## 2. Das Beispiel im Ueberblick
Die Kette aus `linienzug_tests_stufe2.json` sieht so aus (Grundriss, von
oben betrachtet):
```
Start (Z=2154) --AS 90 links--> Horizontal-VF --Vario 90 rechts innen-->
--Horizontal-VF--> --Vario 90 rechts innen--> Linie-GF (3 Grad, Separator
vor+nach) --> Horizontal-VF --Vario 30 rechts innen--> Auf 27 Grad
--Vario 30 links aussen--> Kettenende (Motorstation + ES 90 links)
```
Kurz gesagt: die Kette startet auf Z=2154, macht zwei 90-Grad-Schwenks im
Grundriss, faellt ueber ein eigenstaendiges GF-Stueck (3 Grad) etwas ab,
steigt dann in einer zweiten VF-Einheit um 27 Grad wieder an, und muss am
Ende exakt auf die Starthoehe (2154mm) zurueckkommen. Genau dieser letzte
Schritt - der Rest-Hoehenausgleich passt zu keinem der Standard-Winkel mehr -
ist der interessante Teil (siehe Abschnitt 4, Stufe 1/Stufe 2).
## 3. Die Fragen der Reihe nach
Diese Tabelle zitiert die tatsaechlichen Konsolen-Fragen aus dem Testfall
(Kommentar-Spalte = `kommentar`-Feld im JSON) und erklaert, was jede Frage
bedeutet.
| # | Frage (sinngemaess) | Antwort im Beispiel | Bedeutung |
|---|---|---|---|
| 1 | Startpunkt der Kette | Klick (X/Y) | Beliebiger Punkt in der Zeichnung |
| 2 | Hoehe (Z) Startpunkt | 2154 | Absolute Einbauhoehe des Kettenanfangs |
| 3 | AS-Element setzen? | Ja | Umlenkstation am Anfang - "AS" = Ausgangs-/Anfangs-Element |
| 4 | AS-Element Winkel | 90 Grad | Baugroesse des Umlenkstations-Blocks (90 oder 30 Grad Kurvenradius) |
| 5 | AS-Seite | links | Auf welcher Seite die Umlenkstation seitlich ausschwenkt |
| 6 | Naechstes Element | Horizontal-VF | Kettenanfang-Menue hat 6 Optionen (GF-Bogen, Vario-Kurve, Linie-GF, Linie-VF, Horizontal-VF, Linienende) |
| 7 | Endpunkt Horizontal-VF | Klick | Laenge/Richtung der ersten VF-Einheit |
| 8 | Ziel-Hoehe Eingang GF1 | 1980 | Hoehe, auf die der Eingangs-GF-Anteil der VF-Einheit einregelt |
| 9 | Ist der Endpunkt der Foerderer? | Nein | Innerhalb der VF-Einheit: "geht die Kette hier zu Ende?" |
| 10-13 | Naechstes im VarioFoerderer -> Vario-Kurve 90 rechts innen | - | Grundriss-Schwenk INNERHALB der laufenden VF-Einheit |
| 14 | Ist der Endpunkt der Foerderer? | Nein | s.o. |
| 15-16 | Horizontaler Foerderer, Endpunkt | Klick | Weiteres gerades VF-Sub-Segment |
| 17 | Ist der Endpunkt der Foerderer? | Nein | s.o. |
| 18-21 | Vario-Kurve 90 rechts innen | - | Zweiter Grundriss-Schwenk |
| 22 | Ist der Endpunkt der Foerderer? | Ja, nur Motorstation | Schliesst die 1. VF-Einheit ab (Motorstation, kein ES) |
| 23 | Ist das Kettenende (ES-Element)? | Nein, weiterbauen | Nach der Motorstation: geht die Kette weiter? |
| 24 | Zusaetzlichen Separator hier? | Nein | Optionaler Separator direkt an der Motorstation |
| 25 | Naechstes Element | Neue Linie: GF | Jetzt folgt ein reines Gefaellestrecke-Element (kein VF) |
| 26 | Laenge bestimmen ueber | Endpunkt waehlen | Alternativ: Zielhoehe zuerst festlegen (siehe `vfl-gf-laenge-modus`) |
| 27 | Endpunkt Gefaellestrecke | Klick | Bestimmt deltaL der GF-Geraden |
| 28-29 | Separator VOR / NACH? | Ja / Ja | GF-Stuecke werden ueber Separatoren mechanisch begrenzt |
| 30-31 | Gefaelle festlegen -> Neigungswinkel | 3 Grad | Direkter Winkel (Alternative: Zielhoehe -> Winkel wird berechnet) |
| 32 | Ist das Kettenende? | Nein, weiterbauen | 3-Optionen-Menue: Ja+ES / Ja ohne ES / Nein |
| 33 | Naechstes Element | Horizontal-VF (2. Einheit) | Neue VF-Einheit beginnt direkt am GF-Ausgang (Separator als Eingang) |
| 34-49 | (wie oben: Vario-Kurven, Auf/Ab-Foerderer 27 Grad) | - | s.o. |
| 50 | Ist der Endpunkt der Foerderer? | Ja, Motorstation + Kettenende | Startet den automatischen Kettenende-Abschluss |
| 51-53 | ES-Element setzen? / Winkel / Seite | Ja / 90 / links | Ausgangs-Element, spiegelbildlich zum AS-Element |
| 54 | Endpunkt Kettenende | Klick | Letzter gewuenschter Punkt der Kette |
| 55 | Ziel-Hoehe Kettenende | 2154 | Muss wieder exakt die Starthoehe sein |
| 56 | (automatisch, keine Eingabe) | - | Stufe-1/Stufe-2-Loeser rechnet, siehe Abschnitt 4 |
| 57 | GF2=86mm unter Mindestlaenge - trotzdem bauen? | Ja | Sicherheitsfrage, siehe Abschnitt 4 |
**Wichtig zu Frage 9/14/17 ("Ist der Endpunkt der Foerderer?"):** Diese Frage
kommt IMMER, wenn Sie innerhalb einer laufenden VF-Einheit einen Punkt
gepickt haben. "Nein" heisst: der gepickte Punkt ist nur eine
Zwischen-Etappe, es geht in derselben VF-Einheit weiter (Vario-Kurve,
weiteres gerades Stueck, Auf/Ab). "Ja" beendet die VF-Einheit mit einer
Motorstation - optional zusaetzlich mit ES-Element (echtes Kettenende).
## 4. Stufe 1 / Stufe 2 - wie die letzte Strecke berechnet wird
Am Kettenende (Frage 55/56) ist die Aufgabe: von der aktuellen Position/
Hoehe zur gewuenschten Ziel-Hoehe kommen, UND dabei die feste Baugruppe
Motorstation + Separator (+ ES-Element, falls gewaehlt) unterbringen. Das
Programm versucht das in zwei Stufen (`vfl-winkel-stufe-entscheidung`,
`Lisp/vf_linienzug.lsp`):
- **Stufe 1 - Winkeltabelle:** Es probiert der Reihe nach die festen
Vario-Kurven-Winkel 3/6/9/12/15/18/21/27/33/39/45/51 Grad durch
(`berechne-alle-winkel`) und prueft, ob einer davon eine PASSENDE,
positive VF-Laenge ergibt. Das ist der Normalfall (siehe die Zeilen
"34-49" in der Tabelle: der Auf/Ab-Foerderer in der 2. VF-Einheit findet
so einen passenden Winkel, Auswahl-Index 8 = 27 Grad).
- **Stufe 2 - Horizontale Mitte + GF-Ausgleich:** Wenn KEIN Tabellenwinkel
passt (wie im Beispiel: alle 12 Kandidaten ergeben eine negative
VF-Laenge, die Reststrecke ist einfach zu kurz fuer einen echten
Vertikalbogen), rechnet `berechne-horizontale-mitte` eine andere Loesung:
ein HORIZONTALES VF-Stueck (winkel=0) plus eine kurze Gefaellestrecke
(GF2), die exakt den fehlenden Hoehenunterschied ausgleicht. Im Beispiel:
GF2=86mm, VF=286mm.
Die Formel dahinter ist simpel und lohnt sich zu verinnerlichen: bei einem
FESTEN Neigungswinkel von 3 Grad braucht ein Hoehenausgleich von `H`
Millimetern eine GF-Laenge von `H / tan(3 Grad) ~= H * 19`. Ein winziger
Hoehenrest von z.B. 49mm braucht demnach bereits ~935mm GF-Laenge - siehe
Abschnitt 5.a. GF2 im Beispiel (86mm) ist kuerzer als die uebliche
Mindestlaenge (400mm, `*vfl-gf-min-laenge*`) - deshalb die Rueckfrage in
Schritt 57 ("trotzdem mit 86mm bauen?"). "Ja" baut trotzdem exakt mit dem
berechneten Wert; "Nein" wuerde den bis dahin gebauten Teil verwerfen (siehe
`vfl-loesche-seit`/`vfl-modus-abbruch-sichern`).
**Merksatz:** Stufe 1 sucht einen "sauberen" Standard-Winkel; Stufe 2 ist
der Rettungsanker, wenn dafuer schlicht nicht genug Platz ist - er tauscht
einen Vertikalbogen gegen "flach + minimaler GF-Ausgleich".
## 5. Typische Stolperfallen
### a) Gefaellestrecke (GF) ist "laengenhungrig" bei 3 Grad
3 Grad ist ein SEHR flacher Winkel. `tan(3 Grad) ~= 0.0524`, also braucht
jeder Millimeter Hoehenunterschied ungefaehr **19mm** Laenge:
```
Laenge = Hoehendifferenz / tan(3 Grad) ~= Hoehendifferenz * 19
```
Wer nur wenige mm bis cm Hoehenunterschied ausgleichen will, bekommt damit
schnell GF-Laengen im halben bis ganzen Meterbereich. Das ist die haeufigste
Ursache fuer "die Reststrecke ist zu kurz"-Meldungen und fuer den
Stufe-2-Fallback aus Abschnitt 4. Falls eine GF partout nicht passt: entweder
mehr Grundriss-Laenge einplanen, oder pruefen, ob ein Vertikalbogen
(Vario-Kurve mit groesserem Winkel, siehe Stufe 1) die bessere Wahl ist.
### b) Motorstation + Separator + ES-Element haben einen FESTEN Fussabdruck
Am Kettenende ist nicht die ganze gepickte Reststrecke frei verfuegbar -
ein fester Anteil ist fuer Motorstation und Separator reserviert
(`*vfk-feste-horizontal-kettenende*` = 800mm = 500mm Motorstation + 300mm
Separator), und falls zusaetzlich ein ES-Element gewaehlt wird, kommt dessen
eigener Fussabdruck (abhaengig von 30/90 Grad Bauform) noch oben drauf. Wer
diese festen 800mm (+ ES) nicht einplant, landet schnell bei einer negativen
"freien" Reststrecke - genau der Fall, der in Abschnitt 4 den Stufe-2-
Fallback ausloest.
### c) Seitlicher Versatz bei 90-Grad-AS/ES-Elementen ist NORMAL
Ein AS- oder ES-Element mit 90 Grad Bauform dreht die Fahrtrichtung nicht
einfach "auf der Stelle" - sein Block hat eine reale Baugroesse mit einem
seitlichen (quer zur Fahrtrichtung liegenden) Versatz. Im Standard-Testfall
(`linienzug_tests.json`) betraegt dieser Versatz ca. 637mm (`dY`), im
Stufe-2-Testfall ca. 638mm (`dX`, da die Fahrtrichtung dort anders liegt).
**Das ist kein Baufehler** - es ist die tatsaechliche Geometrie des
90-Grad-ES/AS-Blocks. Ein "Ist-Ziel-Report" mit einer Restabweichung von
ca. 638mm quer zur letzten Fahrtrichtung, aber ~0mm in Laengs-/Hoehenrichtung,
ist ein KORREKTES Ergebnis, keine Abweichung, die weiter untersucht werden
muss.
## 6. Nachvollziehen / Nachbauen
- Live in BricsCAD: `FOERDERANLAGE` -> Linienzug -> Modus 1, und die
Eingaben aus der Tabelle in Abschnitt 3 der Reihe nach eintippen/klicken.
- Automatisiert (ohne Klicks): Befehl `TEST_LINIENZUG_STUFE2` (nach Laden
von `tests/test_linienzug.lsp`) spielt exakt dieselbe Sequenz aus
`tests/testdata/linienzug_tests_stufe2.json` ab und meldet das
Ist-Ziel-Ergebnis in der Konsole.
- Debug-Log: mit `dbgopen`/dem zentralen Debug-Schalter (siehe
`Lisp/ssg_dbg.lsp`) lassen sich alle `GEOMETRIE:`/`GEOMETRIE ERFOLG:`-
Zeilen in eine `.dbg`-Datei im Log-Verzeichnis mitschreiben - hilfreich,
um genau wie in diesem Beispiel eine reale Sitzung nachtraeglich in
einen Testfall zu verwandeln.
+10
View File
@@ -32,6 +32,16 @@
"vfl-hinweis-hoehe-vorschlag-fehlt": "\n(Hinweis: In diese Richtung ist bei dieser Segmentlaenge kein baubarer Vorschlag gefunden - bitte Wert manuell pruefen.)", "vfl-hinweis-hoehe-vorschlag-fehlt": "\n(Hinweis: In diese Richtung ist bei dieser Segmentlaenge kein baubarer Vorschlag gefunden - bitte Wert manuell pruefen.)",
"vfl-alert-gf-kann-nicht-steigen": "Gefaellestrecke (GF) kann nicht steigen - bitte tiefere Zielhoehe waehlen oder Ab/Auf VF benutzen.", "vfl-alert-gf-kann-nicht-steigen": "Gefaellestrecke (GF) kann nicht steigen - bitte tiefere Zielhoehe waehlen oder Ab/Auf VF benutzen.",
"vfl-alert-gefaelle-zu-steil": "Gefaelle zu steil fuer GF: %1 Grad (erlaubt max %2 Grad).\nBitte kleinere Hoehendifferenz waehlen oder Ab/Auf VF benutzen.", "vfl-alert-gefaelle-zu-steil": "Gefaelle zu steil fuer GF: %1 Grad (erlaubt max %2 Grad).\nBitte kleinere Hoehendifferenz waehlen oder Ab/Auf VF benutzen.",
"vfl-gf-laenge-modus-header": "\nWie soll die Laenge dieser Gefaellestrecke bestimmt werden?",
"vfl-gf-laenge-modus-opt-endpunkt": "\n 1 - Endpunkt waehlen (Laenge ergibt sich)",
"vfl-gf-laenge-modus-opt-hoehe": "\n 2 - Zielhoehe festlegen (Laenge wird berechnet)",
"vfl-gf-laenge-modus-opt-steigend": "\n 3 - Steigende GF (Sonderfall, bis 3 Grad)",
"vfl-prompt-neigungswinkel-laenge-berechnet": "\nNeigungswinkel (Grad, 0 < Winkel <= %1) [%1] (deltaL wird berechnet und eingefuegt): ",
"vfl-prompt-neigungswinkel-steigend": "\nSteigungswinkel (Grad, 0 <= Winkel <= %1, 0 = flach) [%1]: ",
"vfl-alert-winkel-ungueltig-steigend": "Steigungswinkel ungueltig: %1 Grad. Erlaubt: 0 <= Winkel <= %2 Grad.",
"vfl-alert-gf-steigend-falsche-richtung": "Steigende Gefaellestrecke: die gewaehlte Zielhoehe liegt unter der aktuellen Hoehe - bitte hoehere Zielhoehe waehlen oder den normalen (fallenden) Modus benutzen.",
"vfl-sep-vor-gf-frage": "\n\nSeparator VOR dieser Gefaellestrecke einfuegen?",
"vfl-sep-nach-gf-frage": "\n\nSeparator NACH dieser Gefaellestrecke einfuegen?",
"vfl-ist-kettenende-header": "\nIst das das Kettenende?", "vfl-ist-kettenende-header": "\nIst das das Kettenende?",
"vfl-kettenende-opt-ja-es": "\n 1 - Ja (ES-Element setzen)", "vfl-kettenende-opt-ja-es": "\n 1 - Ja (ES-Element setzen)",
"vfl-kettenende-opt-ja-ohne-es": "\n 2 - Ja (ohne ES-Element setzen)", "vfl-kettenende-opt-ja-ohne-es": "\n 2 - Ja (ohne ES-Element setzen)",
+10
View File
@@ -32,6 +32,16 @@
"vfl-hinweis-hoehe-vorschlag-fehlt": "\n(Note: no buildable suggestion found for that direction at this segment length - please check the value manually.)", "vfl-hinweis-hoehe-vorschlag-fehlt": "\n(Note: no buildable suggestion found for that direction at this segment length - please check the value manually.)",
"vfl-alert-gf-kann-nicht-steigen": "Gravity section (GF) cannot rise - please pick a lower target height or use down/up VF.", "vfl-alert-gf-kann-nicht-steigen": "Gravity section (GF) cannot rise - please pick a lower target height or use down/up VF.",
"vfl-alert-gefaelle-zu-steil": "Slope too steep for GF: %1 degrees (max allowed %2 degrees).\nPlease pick a smaller height difference or use down/up VF.", "vfl-alert-gefaelle-zu-steil": "Slope too steep for GF: %1 degrees (max allowed %2 degrees).\nPlease pick a smaller height difference or use down/up VF.",
"vfl-gf-laenge-modus-header": "\nHow should the length of this gravity section be determined?",
"vfl-gf-laenge-modus-opt-endpunkt": "\n 1 - Pick end point (length results from it)",
"vfl-gf-laenge-modus-opt-hoehe": "\n 2 - Set target height (length will be calculated)",
"vfl-gf-laenge-modus-opt-steigend": "\n 3 - Rising GF (special case, up to 3 degrees)",
"vfl-prompt-neigungswinkel-laenge-berechnet": "\nIncline angle (degrees, 0 < angle <= %1) [%1] (deltaL will be calculated and inserted): ",
"vfl-prompt-neigungswinkel-steigend": "\nRising angle (degrees, 0 <= angle <= %1, 0 = level) [%1]: ",
"vfl-alert-winkel-ungueltig-steigend": "Rising angle invalid: %1 degrees. Allowed: 0 <= angle <= %2 degrees.",
"vfl-alert-gf-steigend-falsche-richtung": "Rising gravity conveyor: the chosen target height is below the current height - please choose a higher target height or use the normal (falling) mode.",
"vfl-sep-vor-gf-frage": "\n\nInsert separator BEFORE this gravity section?",
"vfl-sep-nach-gf-frage": "\n\nInsert separator AFTER this gravity section?",
"vfl-ist-kettenende-header": "\nIs this the chain end?", "vfl-ist-kettenende-header": "\nIs this the chain end?",
"vfl-kettenende-opt-ja-es": "\n 1 - Yes (place ES element)", "vfl-kettenende-opt-ja-es": "\n 1 - Yes (place ES element)",
"vfl-kettenende-opt-ja-ohne-es": "\n 2 - Yes (without placing ES element)", "vfl-kettenende-opt-ja-ohne-es": "\n 2 - Yes (without placing ES element)",
+42 -10
View File
@@ -33,7 +33,10 @@
;; ;;
;; Aufruf in BricsCAD: ;; Aufruf in BricsCAD:
;; (load (strcat (getenv "DXFMAKRO") "/tests/test_linienzug.lsp")) ;; (load (strcat (getenv "DXFMAKRO") "/tests/test_linienzug.lsp"))
;; TEST_LINIENZUG ;; TEST_LINIENZUG (Standard-Szenario, linienzug_tests.json, in
;; alltests.json/SSG_RUN_ALL_TESTS registriert)
;; TEST_LINIENZUG_STUFE2 (Stufe-2-Fallback-Regressionstest Kettenende,
;; linienzug_tests_stufe2.json, manueller Aufruf)
;; ============================================================ ;; ============================================================
;; Naechste Skript-Antwort liefern (FIFO). Bei leerer Queue Warnung + "". ;; Naechste Skript-Antwort liefern (FIFO). Bei leerer Queue Warnung + "".
@@ -126,8 +129,17 @@
) )
) )
(defun c:TEST_LINIENZUG ( / json-datei daten meta schritte-anz rest-anz status ;; --- Gemeinsamer Replay-Kern, von allen c:TEST_LINIENZUG*-Befehlen genutzt ---
old-vgp old-gs old-gi old-gr err) ;; befehl-anzeige: nur fuer die Konsolen-Ueberschrift/ssg-start (String).
;; test-id: Bezeichner fuer das *linienzug-test-results*-JSON (run-all-
;; Konvention, siehe linienzug:export-results).
;; Rueckgabe: (status schritte-anz rest-anz) - status ist "OK"/"ABWEICHUNG"/
;; "FEHLER". Setzt zusaetzlich *linienzug-test-results* (letzter Aufruf
;; gewinnt - identisch zum bisherigen Verhalten von c:TEST_LINIENZUG, das
;; ebenfalls nur einen einzigen Testfall pro Lauf kennt).
(defun tlz-run-datei (json-datei test-id befehl-anzeige /
daten meta schritte-anz rest-anz status
old-vgp old-gs old-gi old-gr err)
(if (or (not *lib-initialized*) (null bogen-auf)) (init-bibliothek)) (if (or (not *lib-initialized*) (null bogen-auf)) (init-bibliothek))
(if (null (car (atoms-family 1 '("VF-LINIENZUG-MODUS")))) (if (null (car (atoms-family 1 '("VF-LINIENZUG-MODUS"))))
(progn (progn
@@ -138,12 +150,10 @@
) )
(princ "\n================================================================") (princ "\n================================================================")
(princ "\n TEST_LINIENZUG - VF-Linienzug Replay-Test") (princ (strcat "\n " befehl-anzeige " - VF-Linienzug Replay-Test"))
(princ "\n================================================================") (princ "\n================================================================")
;; --- Eingabe-Skript aus JSON laden --- ;; --- Eingabe-Skript aus JSON laden ---
(setq json-datei
(strcat (getenv "DXFMAKRO") "\\tests\\testdata\\linienzug_tests.json"))
(setq daten (ssg-load-json json-datei)) (setq daten (ssg-load-json json-datei))
(if (null daten) (if (null daten)
(progn (progn
@@ -158,7 +168,7 @@
(princ (strcat "\n Eingabe-Schritte: " (itoa schritte-anz))) (princ (strcat "\n Eingabe-Schritte: " (itoa schritte-anz)))
(if meta (princ (strcat "\n Szenario: " (ssg-val meta "beschreibung")))) (if meta (princ (strcat "\n Szenario: " (ssg-val meta "beschreibung"))))
(ssg-start "TEST_LINIENZUG" '(("OSMODE") ("ATTREQ") ("ATTDIA"))) (ssg-start befehl-anzeige '(("OSMODE") ("ATTREQ") ("ATTDIA")))
(setvar "OSMODE" 0) (setvar "OSMODE" 0)
(setvar "ATTREQ" 0) (setvar "ATTREQ" 0)
(setvar "ATTDIA" 0) (setvar "ATTDIA" 0)
@@ -192,11 +202,11 @@
(if (vl-catch-all-error-p err) (if (vl-catch-all-error-p err)
(progn (progn
(setq status "FEHLER") (setq status "FEHLER")
(princ (strcat "\n TEST_LINIENZUG: FEHLER -> " (princ (strcat "\n " befehl-anzeige ": FEHLER -> "
(vl-catch-all-error-message err)))) (vl-catch-all-error-message err))))
(progn (progn
(setq status (if (= rest-anz 0) "OK" "ABWEICHUNG")) (setq status (if (= rest-anz 0) "OK" "ABWEICHUNG"))
(princ "\n TEST_LINIENZUG: Kette gebaut.") (princ (strcat "\n " befehl-anzeige ": Kette gebaut."))
(if (> rest-anz 0) (if (> rest-anz 0)
(princ (strcat "\n WARNUNG: " (itoa rest-anz) (princ (strcat "\n WARNUNG: " (itoa rest-anz)
" Skript-Eintraege NICHT verbraucht (Ablauf weicht ab).")) " Skript-Eintraege NICHT verbraucht (Ablauf weicht ab)."))
@@ -214,12 +224,34 @@
;; --- Ergebnis fuer run-all als JSON-String ablegen --- ;; --- Ergebnis fuer run-all als JSON-String ablegen ---
(setq *linienzug-test-results* (setq *linienzug-test-results*
(strcat " {\n" (strcat " {\n"
" \"test_id\": \"LZ_Standard\",\n" " \"test_id\": \"" test-id "\",\n"
" \"status\": \"" status "\",\n" " \"status\": \"" status "\",\n"
" \"schritte_gesamt\": " (itoa schritte-anz) ",\n" " \"schritte_gesamt\": " (itoa schritte-anz) ",\n"
" \"schritte_offen\": " (itoa rest-anz) "\n" " \"schritte_offen\": " (itoa rest-anz) "\n"
" }")) " }"))
(ssg-end) (ssg-end)
(list status schritte-anz rest-anz)
)
(defun c:TEST_LINIENZUG ( / json-datei)
(setq json-datei
(strcat (getenv "DXFMAKRO") "\\tests\\testdata\\linienzug_tests.json"))
(tlz-run-datei json-datei "LZ_Standard" "TEST_LINIENZUG")
(princ)
)
;; Zweiter Testfall (Regressionstest, in dieser Session neu aufgenommen):
;; Stufe-2-Fallback beim Kettenende (typ=VF winkel=0, horizontales VF + GF-
;; Ausgleich), siehe Beschreibung/Kommentare in linienzug_tests_stufe2.json.
;; Eigenstaendiger Befehl statt zweitem Array-Eintrag in linienzug_tests.json,
;; damit LZ_Standard unangetastet bleibt (jede JSON-Datei bleibt ein
;; einzelnes Szenario, wie von tlz-daten->queue/-meta erwartet). NICHT in
;; tests/alltests.json registriert - manueller Einzelaufruf, bei Bedarf
;; spaeter dort ergaenzen.
(defun c:TEST_LINIENZUG_STUFE2 ( / json-datei)
(setq json-datei
(strcat (getenv "DXFMAKRO") "\\tests\\testdata\\linienzug_tests_stufe2.json"))
(tlz-run-datei json-datei "LZ_Stufe2_Kettenende" "TEST_LINIENZUG_STUFE2")
(princ) (princ)
) )
+50 -4
View File
@@ -125,20 +125,63 @@
(defun tsp-tests-glieder ( / spec) (defun tsp-tests-glieder ( / spec)
;; --- Linie-GF, Kettenende mit ES --- ;; --- Linie-GF, Kettenende mit ES ---
;; Erste Sektion: Menue-Code "1" (4 Optionen, ohne Frame). ;; Erste Sektion: Menue-Code "1" (4 Optionen, ohne Frame). laenge_modus
;; "endpunkt" (Default, Zweig A): DL(+hz), dann Separator vor/nach, dann
;; die Gefaelle-Wahl (Hoehe/Winkel) wie bisher.
(setq spec (tsp-spec T (list (setq spec (tsp-spec T (list
(tsp-sek (list (cons "glied" "Linie-GF") (cons "dl" 5500.0) (tsp-sek (list (cons "glied" "Linie-GF") (cons "laenge_modus" "endpunkt")
(cons "hz" 0.0) (cons "gefaelle" "winkel") (cons "dl" 5500.0) (cons "hz" 0.0)
(cons "sep_vor" 0) (cons "sep_nach" 0)
(cons "gefaelle" "winkel")
(cons "winkel" 3.0) (cons "ende" "ja-mit-es")) nil) (cons "winkel" 3.0) (cons "ende" "ja-mit-es")) nil)
(tsp-es "90" "rechts")))) (tsp-es "90" "rechts"))))
(tsp-journal-eq "Linie-GF + ES" (tsp-journal-eq "Linie-GF + ES"
(append (tsp-pre-mit-as) (append (tsp-pre-mit-as)
(list (cons "STEP" "Linie-GF") (cons "STR" "1") (list (cons "STEP" "Linie-GF") (cons "STR" "1")
(cons "STR" "1") ; laenge_modus: endpunkt
(cons "DL" 5500.0) (cons "REAL" 0.0) (cons "DL" 5500.0) (cons "REAL" 0.0)
(cons "STR" "2") (cons "STR" "2") ; Separator vor / nach
(cons "STR" "2") (cons "REAL" 3.0) (cons "STR" "1") (cons "STR" "2") (cons "REAL" 3.0) (cons "STR" "1")
(cons "STEP" "ES") (cons "STR" "90") (cons "STR" "2"))) (cons "STEP" "ES") (cons "STR" "90") (cons "STR" "2")))
(vsp-journal spec)) (vsp-journal spec))
;; --- Linie-GF, Zielhoehe zuerst (laenge_modus "hoehe") ---
;; Kein Separator-Paar VOR der Hoehen-/Winkel-Eingabe wie im Endpunkt-Zweig
;; - hier stehen sie NACH DL(+hz), aber vor Hoehe/Winkel (siehe
;; vf-linienzug-modus, Zweig B).
(setq spec (tsp-spec T (list
(tsp-sek (list (cons "glied" "Linie-GF") (cons "laenge_modus" "hoehe")
(cons "dl" 100.0) (cons "hz" 0.0)
(cons "sep_vor" 1) (cons "sep_nach" 0)
(cons "hoehe" 1800.0) (cons "winkel" 3.0)
(cons "ende" "nein")) nil))))
(tsp-journal-eq "Linie-GF, Zielhoehe zuerst"
(append (tsp-pre-mit-as)
(list (cons "STEP" "Linie-GF") (cons "STR" "1")
(cons "STR" "2") ; laenge_modus: hoehe
(cons "DL" 100.0) (cons "REAL" 0.0)
(cons "STR" "1") (cons "STR" "2") ; Separator vor(ja)/nach(nein)
(cons "REAL" 1800.0) (cons "REAL" 3.0)
(cons "STR" "3")))
(vsp-journal spec))
;; --- Linie-GF, Sonderfall Steigend, Endpunkt-Unterwahl ---
;; KEIN Separator-Feld (die steigende GF kennt keine Separator-Frage).
(setq spec (tsp-spec T (list
(tsp-sek (list (cons "glied" "Linie-GF") (cons "laenge_modus" "steigend")
(cons "steigend_modus" "endpunkt")
(cons "dl" 800.0) (cons "hz" 0.0)
(cons "winkel" 2.0) (cons "ende" "nein")) nil))))
(tsp-journal-eq "Linie-GF, Steigend + Endpunkt"
(append (tsp-pre-mit-as)
(list (cons "STEP" "Linie-GF") (cons "STR" "1")
(cons "STR" "3") ; laenge_modus: steigend
(cons "STR" "1") ; steigend_modus: endpunkt
(cons "DL" 800.0) (cons "REAL" 0.0)
(cons "REAL" 2.0)
(cons "STR" "3")))
(vsp-journal spec))
;; --- Horizontal-VF: horizontaler Erstkoerper --- ;; --- Horizontal-VF: horizontaler Erstkoerper ---
;; Nur hier stehen Separator vor/nach VOR der Endpunkt-Antwort ;; Nur hier stehen Separator vor/nach VOR der Endpunkt-Antwort
;; (vfl-baue-horizontal-koerper im Ziel-Modus). ;; (vfl-baue-horizontal-koerper im Ziel-Modus).
@@ -187,7 +230,8 @@
;; --- GF-Bogen als ZWEITE Sektion --- ;; --- GF-Bogen als ZWEITE Sektion ---
;; Ab der zweiten Sektion gilt die 5-Optionen-Tabelle: Linie-GF ist dort ;; Ab der zweiten Sektion gilt die 5-Optionen-Tabelle: Linie-GF ist dort
;; "2" (nicht mehr "1"), GF-Bogen ist "1". ;; "2" (nicht mehr "1"), GF-Bogen ist "1". laenge_modus/sep_vor/sep_nach
;; fehlen absichtlich (Default "endpunkt"/nein/nein, deckt alte Specs ab).
(setq spec (tsp-spec T (list (setq spec (tsp-spec T (list
(tsp-sek (list (cons "glied" "Linie-GF") (cons "dl" 3000.0) (tsp-sek (list (cons "glied" "Linie-GF") (cons "dl" 3000.0)
(cons "hz" 180.0) (cons "gefaelle" "hoehe") (cons "hz" 180.0) (cons "gefaelle" "hoehe")
@@ -197,7 +241,9 @@
(tsp-journal-eq "GF-Bogen als zweite Sektion (Menue-Codes am Frame)" (tsp-journal-eq "GF-Bogen als zweite Sektion (Menue-Codes am Frame)"
(append (tsp-pre-mit-as) (append (tsp-pre-mit-as)
(list (cons "STEP" "Linie-GF") (cons "STR" "1") (list (cons "STEP" "Linie-GF") (cons "STR" "1")
(cons "STR" "1") ; laenge_modus: endpunkt (Default)
(cons "DL" 3000.0) (cons "REAL" 180.0) (cons "DL" 3000.0) (cons "REAL" 180.0)
(cons "STR" "2") (cons "STR" "2") ; Separator vor/nach (Default: nein)
(cons "STR" "1") (cons "REAL" 2500.0) (cons "STR" "3") (cons "STR" "1") (cons "REAL" 2500.0) (cons "STR" "3")
(cons "STEP" "GF-Bogen") (cons "STR" "1") (cons "STEP" "GF-Bogen") (cons "STR" "1")
(cons "INT" 60) (cons "STR" "2"))) (cons "INT" 60) (cons "STR" "2")))
+295
View File
@@ -0,0 +1,295 @@
[
{
"beschreibung": "Stufe-2-Fallback-Regressionstest (Kettenende): Start Z2154 AS 90 Grad links -> Horizontal-VF (2x Vario-Kurve 90 rechts innen) -> Linie-GF 3 Grad (Sep vor+nach) -> Horizontal-VF (Vario-Kurve 30 rechts innen -> Auf 27 Grad -> Vario-Kurve 30 links aussen) -> Kettenende ueber 'Ist der Endpunkt der Foerderer?'-Option 3 mit ES 90 Grad links; GF2 (86mm) liegt unter der Mindestlaenge (400mm), Stufe 1 (Winkeltabelle) scheitert komplett (alle Kandidaten negative Laenge) -> Stufe 2 (vfl-winkel-stufe-entscheidung) liefert typ=VF winkel=0 (horizontales VF + GF-Ausgleich). Reproduziert den in dieser Session gefixten Bug (Stufe-2-Fallback griff im Kettenende-Zweig 'Ab, steiler als 3 Grad' bisher nicht, wenn Stufe 1 scheiterte) - erster erfolgreicher Stufe-2-Bau der Session, live in BricsCAD verifiziert.",
"erwartung_dx_mm": 638,
"erwartung_dy_mm": 0,
"erwartung_dz_mm": 0,
"erwartung_hinweis": "dX~638 = seitlicher Fussabdruck ES_Element_90 quer zur letzten Fahrtrichtung (Bauform des 90-Grad-ES-Elements, kein Baufehler). Kernzweck des Testfalls: Stufe-2-Fallback beim Kettenende (typ=VF winkel=0 im Kettenende-Abschluss, GF2=86mm unter Mindestlaenge -> 'trotzdem mit 86mm bauen?'=Ja)."
},
{
"typ": "point_abs",
"wert": [24377.817458, -10300, 2067],
"kommentar": "getpoint Startpunkt der Kette (Rohwert aus dem realen BricsCAD-Testlauf)"
},
{
"typ": "real",
"wert": 2154.0,
"kommentar": "getreal Hoehe (Z) Startpunkt"
},
{
"typ": "string",
"wert": "1",
"kommentar": "getstring AS-Element setzen? (Ja)"
},
{
"typ": "string",
"wert": "1",
"kommentar": "getstring AS-Element Winkel - Menu-Index 1=90 Grad/2=30 Grad (vf-frage-element-winkel mappt den rohen Index auf den Winkel-STRING '90'/'30'; im Debug-Journal erscheint bereits der gemappte Wert '90', die Konsolen-/Mock-Eingabe selbst bleibt der Index)"
},
{
"typ": "string",
"wert": "1",
"kommentar": "getstring AS-Seite (links)"
},
{
"typ": "string",
"wert": "5",
"kommentar": "getstring Naechstes Element (Kettenanfang-Menue, 6 Optionen: 5 = Horizontal-VF)"
},
{
"typ": "point_rel",
"dL": 6082.461,
"hz": 90,
"kommentar": "getpoint Endpunkt Horizontal-VF (freie Richtung am Kettenanfang, hz-vorgabe=nil - hz=90 ist die vom Code gesnappte Richtung, direkt aus dem Debug-Journal (REAL-Eintrag unmittelbar nach dem DL-Eintrag) uebernommen; dieser REAL-Eintrag ist ein reiner Journal-Nebeneffekt von vfl-in-abstand und KEIN eigener Eingabe-Schritt, siehe Lisp/vf_linienzug.lsp vfl-in-abstand"
},
{
"typ": "real",
"wert": 1980.0,
"kommentar": "getreal Ziel-Hoehe Eingang GF1 (eingang-modus=voll/nil, vollstaendiger Neuaufbau des VF-Eingangs)"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Ist der Endpunkt der Foerderer? (Nein) - erste Frage innerhalb vfl-baue-horizontal-koerper (ziel-modus)"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Naechstes im VarioFoerderer (Vario-Kurve)"
},
{
"typ": "int",
"wert": 90,
"kommentar": "getint Vario-Kurve Winkel (90 Grad, roher Winkelwert - KEIN Auswahl-Index, siehe vfl-menu-winkel)"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Vario-Kurve Seite (rechts)"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Vario-Kurve Variante (innen)"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Ist der Endpunkt der Foerderer? (Nein)"
},
{
"typ": "string",
"wert": "1",
"kommentar": "getstring Naechstes im VarioFoerderer (Horizontaler Foerderer)"
},
{
"typ": "point_rel",
"dL": 1300,
"hz": 0,
"kommentar": "getpoint Horizontal-Endpunkt innerhalb der VF-Einheit. hz=0 ANNAHME (NICHT direkt im Debug-/Konsolen-Auszug als 'hz=' ausgewiesen, sondern aus der Turn-Arithmetik hergeleitet: Start-hz=90, davor bereits eine 90-Grad-rechts-Vario-Kurve (-90) -> 90-90=0; dieselbe Turn-Konvention [rechts=-Winkel/links=+Winkel] ist an ZWEI anderen Stellen dieses Testfalls unabhaengig durch explizite 'hz='-Werte im Konsolen-Auszug bestaetigt, siehe die Vario_Bogen_ab_3/ab_27-Eintraege). BITTE in BricsCAD live gegenpruefen (einzige nicht direkt beleg-bestaetigte hz-Rekonstruktion dieses Testfalls)."
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Ist der Endpunkt der Foerderer? (Nein) - innerhalb vfl-baue-horizontal-koerper (ziel-modus)"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Naechstes im VarioFoerderer (Vario-Kurve)"
},
{
"typ": "int",
"wert": 90,
"kommentar": "getint Vario-Kurve Winkel (90 Grad)"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Vario-Kurve Seite (rechts)"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Vario-Kurve Variante (innen)"
},
{
"typ": "string",
"wert": "1",
"kommentar": "getstring Ist der Endpunkt der Foerderer? (Ja, nur Motorstation) - schliesst die 1. Horizontal-VF-Einheit ab"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Ist das Kettenende (ES-Element)? (Nein, weiterbauen) - vfl-vf-einheit-abschluss"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Zusaetzlichen Separator an dieser Stelle einfuegen? (Nein)"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Naechstes Element (Hauptmenue, 5 Optionen, Frame vorhanden: 2 = Neue Linie: GF)"
},
{
"typ": "string",
"wert": "1",
"kommentar": "getstring Wie soll die Laenge dieser Gefaellestrecke bestimmt werden? (1 = Endpunkt waehlen)"
},
{
"typ": "point_rel",
"dL": 978.394396,
"hz": -90,
"kommentar": "getpoint Endpunkt Gefaellestrecke. hz=-90 DIREKT aus dem Debug-Journal bestaetigt (GEOMETRIE-Zeile 'vfl-insert-gf-segment (deltaL=379 hz=-90 winkel=3)')"
},
{
"typ": "string",
"wert": "1",
"kommentar": "getstring Separator VOR dieser Gefaellestrecke einfuegen? (Ja)"
},
{
"typ": "string",
"wert": "1",
"kommentar": "getstring Separator NACH dieser Gefaellestrecke einfuegen? (Ja)"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Gefaelle festlegen (1 = Hoehe, 2 = Neigungswinkel eingeben - Reihenfolge im Code seit dieser Sitzung getauscht)"
},
{
"typ": "real",
"wert": 3.0,
"kommentar": "getreal Neigungswinkel (3 Grad)"
},
{
"typ": "string",
"wert": "3",
"kommentar": "getstring Ist das Kettenende? (3 = Nein, weiterbauen - 3-Optionen-Menue: 1=Ja+ES, 2=Ja ohne ES, 3=Nein)"
},
{
"typ": "string",
"wert": "4",
"kommentar": "getstring Naechstes Element (5 Optionen: 4 = Neue VF mit horizontalem Anfang/Horizontal-VF)"
},
{
"typ": "point_rel",
"dL": 888.101488,
"hz": -90,
"kommentar": "getpoint Endpunkt Horizontal-VF (2. Einheit). hz=-90 unveraendert gegenueber dem vorigen GF-Segment (keine Richtungsaenderung dazwischen); eingang-modus=separator (letztes Glied war der Separator NACH der Gefaellestrecke) -> KEINE Ziel-Hoehe-Frage fuer GF1 (im Unterschied zur 1. Horizontal-VF-Einheit)"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Ist der Endpunkt der Foerderer? (Nein) - innerhalb vfl-baue-horizontal-koerper (ziel-modus)"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Naechstes im VarioFoerderer (Vario-Kurve)"
},
{
"typ": "int",
"wert": 30,
"kommentar": "getint Vario-Kurve Winkel (30 Grad)"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Vario-Kurve Seite (rechts)"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Vario-Kurve Variante (innen)"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Ist der Endpunkt der Foerderer? (Nein)"
},
{
"typ": "string",
"wert": "3",
"kommentar": "getstring Naechstes im VarioFoerderer (Auf/Ab-Foerderer)"
},
{
"typ": "point_rel",
"dL": 1614.972606,
"hz": -120,
"kommentar": "getpoint Endpunkt Auf/Ab-Foerderer. hz=-120 DIREKT bestaetigt (Konsolen-Auszug zeigt fuer den anschliessenden Vario_Bogen_ab_3_TEF_rechts explizit 'hz=-120 Grad' bei derselben Fahrtrichtung)"
},
{
"typ": "string",
"wert": "1",
"kommentar": "getstring Kette kommt flach an - Richtung waehlen (1 = Auf)"
},
{
"typ": "int",
"wert": 8,
"kommentar": "getint Winkel-/Hoehen-Tabelle: Auswahl-Index (NICHT der Winkel selbst) unter den GUELTIGEN Kandidaten - gueltig sind hier 3/6/9/12/15/18/21/27 Grad (8 Werte), Index 8 = letzter gueltiger Kandidat = 27 Grad (siehe Konsolen-Auszug '5/11: Vario_Bogen_auf_27_TEF_rechts')"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Ist der Endpunkt der Foerderer? (Nein)"
},
{
"typ": "string",
"wert": "2",
"kommentar": "getstring Naechstes im VarioFoerderer (Vario-Kurve)"
},
{
"typ": "int",
"wert": 30,
"kommentar": "getint Vario-Kurve Winkel (30 Grad)"
},
{
"typ": "string",
"wert": "1",
"kommentar": "getstring Vario-Kurve Seite (links)"
},
{
"typ": "string",
"wert": "1",
"kommentar": "getstring Vario-Kurve Variante (aussen) - Konsolen-Auszug bestaetigt 'Vario_Kurve_links_30_TEF_aussen'"
},
{
"typ": "string",
"wert": "3",
"kommentar": "getstring Ist der Endpunkt der Foerderer? (3 = Ja, Motorstation setzen + Kettenende definieren) - startet den Stufe-1/Stufe-2-Kettenende-Zweig (vfl-body-abschluss/vfl-body-zerlegung)"
},
{
"typ": "string",
"wert": "1",
"kommentar": "getstring ES-Element setzen? (Ja)"
},
{
"typ": "string",
"wert": "1",
"kommentar": "getstring ES-Element Winkel - Menu-Index 1=90 Grad/2=30 Grad (wie AS-Winkel oben, gleiches Mapping ueber vf-frage-element-winkel)"
},
{
"typ": "string",
"wert": "1",
"kommentar": "getstring ES-Seite (links)"
},
{
"typ": "point_rel",
"dL": 1802.808856,
"hz": -90,
"kommentar": "getpoint Endpunkt Kettenende (vfl-body-abschluss). hz=-90 DIREKT bestaetigt: die im Debug-Log erreichte Position (25544.710135 -8783.275914 2270.287821) hat exakt dieselbe X- und Z-Koordinate wie die KS_AUS-Position der vorigen Vario-Kurve (25544.71 / 2270.29 lt. Konsolen-Auszug) - nur Y aendert sich, das ist ausschliesslich mit hz=-90 (reine Y-Richtung) vereinbar"
},
{
"typ": "real",
"wert": 2154.0,
"kommentar": "getreal Ziel-Hoehe Kettenende. Ab hier laeuft vfl-body-zerlegung AUTOMATISCH: Stufe 1 (Winkeltabelle 3/6/9/.../51 Grad) scheitert komplett (alle Kandidaten ergeben negative L_VF), Stufe 2 (vfl-winkel-stufe-entscheidung/berechne-horizontale-mitte) liefert typ=VF winkel=0 L_GF=86 L_VF=286 - KEINE weitere Eingabe fuer diese Entscheidung noetig"
},
{
"typ": "string",
"wert": "1",
"kommentar": "getstring Berechnetes GF2=86mm liegt unter der ueblichen Mindestlaenge (400mm) - trotzdem mit den exakt berechneten 86mm bauen? (Ja) - schliesst die Kette ab (VF_n mit GF=1 VF=5)"
}
]