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
+350 -32
View File
@@ -2403,6 +2403,75 @@
(vfl-frame-3grad pt hz))
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
;; 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
@@ -3461,7 +3530,9 @@
anzahl-gf anzahl-vf vfl-nummer lastEnt seg-lastEnt
gf-max-winkel gf-ok kettenanfang
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 (ssg-text "vfl-modus1-header"))
(princ "\n=========================================")
@@ -3706,26 +3777,203 @@
;; --- Neue Linie: GF (Nutzer legt den Typ explizit fest) ---
((= wahl "Linie-GF")
;; Fahrtrichtung folgt dem vorherigen Element (Frame); nur das erste
;; Segment (frame=nil) definiert die Richtung frei.
(setq kettenanfang (null frame))
(setq gf-max-winkel (float (ssg-cfg-or "vario" "gefaelle_winkel" 3)))
(setq gf-ok nil sep-vor-gf nil sep-nach-gf nil)
;; 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))
(setq kettenanfang (null frame))
(setq gf-max-winkel (float (ssg-cfg-or "vario" "gefaelle_winkel" 3)))
(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 Dreh­teller-
;; 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).
;; 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))
@@ -3733,21 +3981,42 @@
)
)
;; 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 (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.
;; 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)))))
@@ -3760,7 +4029,9 @@
"vfl-gefaelle-festlegen-header"))
(if (= antwort "2")
(progn
;; Winkel direkt vorgeben - deltaH ergibt sich aus deltaL*tan(winkel).
;; 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))
@@ -3777,13 +4048,8 @@
)
)
(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.
;; 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)))))
@@ -3791,6 +4057,8 @@
(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"))
@@ -3814,8 +4082,46 @@
)
)
)
)
)
)
)
)
)
)
)
;; --- Gemeinsamer Abschluss fuer ALLE drei Zweige ---
(if gf-ok
(progn
;; Uebergangsbogen(n) einfuegen, falls die aktuelle Frame-Neigung
;; nicht zur Ziel-Neigung dieser Linie passt: noetig fuer den
;; Steigend-Sonderfall (Zweig C, winkel < 0) UND falls der
;; VORGAENGER eine steigende GF war und jetzt normal (Zweig A/B)
;; weitergebaut wird. Im unveraenderten Normalfall (Vorgaenger-
;; Neigung bereits 0/+3, Ziel +3 wie bisher) ist die Bedingung
;; falsch - kein Verhaltensunterschied zu vorher.
(if (or (< winkel 0.0) (< (cadr (frame->hz-winkel frame)) (- *vfl-gf-winkel-toleranz*)))
(progn
(setq erg-bogen (vfl-bogen-uebergang frame winkel))
(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)))
)
)
(if gf-ok
(progn
;; Separator VOR (Antwort aus der Vorab-Frage oben) - VOR
;; dem GF-Segment selbst einfuegen, am GF-Winkel.
(if sep-vor-gf
(progn
(setq frame (make-frame-from-dir
(gf-insert-hz-with-ks "Staustrecke_Separator_SP_300_mm"
(car frame) hz-neu winkel *vfk-separator-laenge* 0)
(hz-winkel->xu hz-neu winkel)))
(setq *vfl-acc-separator* (1+ *vfl-acc-separator*))
)
)
(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) ")")))
@@ -3827,6 +4133,20 @@
(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
(setq frame (make-frame-from-dir
(gf-insert-hz-with-ks "Staustrecke_Separator_SP_300_mm"
(car frame) hz-neu winkel *vfk-separator-laenge* 0)
(hz-winkel->xu hz-neu winkel)))
(setq *vfl-acc-separator* (1+ *vfl-acc-separator*))
(setq letzter-typ "Separator")
(setq p-aktuell (car frame))
)
)
(princ (ssg-text "vfl-ist-kettenende-header"))
(princ (ssg-text "vfl-kettenende-opt-ja-es"))
(princ (ssg-text "vfl-kettenende-opt-ja-ohne-es"))
@@ -3849,8 +4169,6 @@
)
)
)
)
)
;; --- Neue Linie: Ab/Auf VF (Nutzer legt den Typ explizit fest) ---
((= wahl "Linie-VF")
+41 -2
View File
@@ -49,6 +49,12 @@
(setq *vsp-tab-seite* '(("links" . "1") ("rechts" . "2")))
(setq *vsp-tab-variante* '(("aussen" . "1") ("innen" . "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-im-vf* '(("horizontal" . "1") ("vario-kurve" . "2")
("auf-ab" . "3")))
@@ -219,7 +225,7 @@
(vsp-wert-str (ssg-val kopf "as_winkel") "as_winkel")
(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 *vsp-wo* (strcat "Sektion " (if label label "(ohne glied)")))
(cond
@@ -250,12 +256,45 @@
(vsp-code-raus *vsp-tab-seite* (ssg-val sek "seite") "seite"))
((= label "Linie-GF")
;; Vorwahl "laenge_modus" (endpunkt/hoehe/steigend, Default
;; "endpunkt" - deckt alte Specs ohne dieses Feld ab, identisch
;; zum bisherigen alleinigen Ablauf). "steigend" ist der
;; Sonderfall (GF bis 3 Grad steigend, KEIN Separator, eigene
;; innere Endpunkt/Hoehe-Wahl "steigend_modus"). Die DL(+hz)-
;; 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-zahl "REAL" (ssg-val sek "winkel") "winkel")))
)
(vsp-code-raus *vsp-tab-ende3* (ssg-val sek "ende") "ende"))
((= 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-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-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-kettenende-opt-ja-es": "\n 1 - Ja (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-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-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-kettenende-opt-ja-es": "\n 1 - Yes (place ES element)",
"vfl-kettenende-opt-ja-ohne-es": "\n 2 - Yes (without placing ES element)",
+41 -9
View File
@@ -33,7 +33,10 @@
;;
;; Aufruf in BricsCAD:
;; (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 + "".
@@ -126,7 +129,16 @@
)
)
(defun c:TEST_LINIENZUG ( / json-datei daten meta schritte-anz rest-anz status
;; --- Gemeinsamer Replay-Kern, von allen c:TEST_LINIENZUG*-Befehlen genutzt ---
;; 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 (null (car (atoms-family 1 '("VF-LINIENZUG-MODUS"))))
@@ -138,12 +150,10 @@
)
(princ "\n================================================================")
(princ "\n TEST_LINIENZUG - VF-Linienzug Replay-Test")
(princ (strcat "\n " befehl-anzeige " - VF-Linienzug Replay-Test"))
(princ "\n================================================================")
;; --- Eingabe-Skript aus JSON laden ---
(setq json-datei
(strcat (getenv "DXFMAKRO") "\\tests\\testdata\\linienzug_tests.json"))
(setq daten (ssg-load-json json-datei))
(if (null daten)
(progn
@@ -158,7 +168,7 @@
(princ (strcat "\n Eingabe-Schritte: " (itoa schritte-anz)))
(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 "ATTREQ" 0)
(setvar "ATTDIA" 0)
@@ -192,11 +202,11 @@
(if (vl-catch-all-error-p err)
(progn
(setq status "FEHLER")
(princ (strcat "\n TEST_LINIENZUG: FEHLER -> "
(princ (strcat "\n " befehl-anzeige ": FEHLER -> "
(vl-catch-all-error-message err))))
(progn
(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)
(princ (strcat "\n WARNUNG: " (itoa rest-anz)
" Skript-Eintraege NICHT verbraucht (Ablauf weicht ab)."))
@@ -214,12 +224,34 @@
;; --- Ergebnis fuer run-all als JSON-String ablegen ---
(setq *linienzug-test-results*
(strcat " {\n"
" \"test_id\": \"LZ_Standard\",\n"
" \"test_id\": \"" test-id "\",\n"
" \"status\": \"" status "\",\n"
" \"schritte_gesamt\": " (itoa schritte-anz) ",\n"
" \"schritte_offen\": " (itoa rest-anz) "\n"
" }"))
(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)
)
+50 -4
View File
@@ -125,20 +125,63 @@
(defun tsp-tests-glieder ( / spec)
;; --- 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
(tsp-sek (list (cons "glied" "Linie-GF") (cons "dl" 5500.0)
(cons "hz" 0.0) (cons "gefaelle" "winkel")
(tsp-sek (list (cons "glied" "Linie-GF") (cons "laenge_modus" "endpunkt")
(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)
(tsp-es "90" "rechts"))))
(tsp-journal-eq "Linie-GF + ES"
(append (tsp-pre-mit-as)
(list (cons "STEP" "Linie-GF") (cons "STR" "1")
(cons "STR" "1") ; laenge_modus: endpunkt
(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 "STEP" "ES") (cons "STR" "90") (cons "STR" "2")))
(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 ---
;; Nur hier stehen Separator vor/nach VOR der Endpunkt-Antwort
;; (vfl-baue-horizontal-koerper im Ziel-Modus).
@@ -187,7 +230,8 @@
;; --- GF-Bogen als ZWEITE Sektion ---
;; 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
(tsp-sek (list (cons "glied" "Linie-GF") (cons "dl" 3000.0)
(cons "hz" 180.0) (cons "gefaelle" "hoehe")
@@ -197,7 +241,9 @@
(tsp-journal-eq "GF-Bogen als zweite Sektion (Menue-Codes am Frame)"
(append (tsp-pre-mit-as)
(list (cons "STEP" "Linie-GF") (cons "STR" "1")
(cons "STR" "1") ; laenge_modus: endpunkt (Default)
(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 "STEP" "GF-Bogen") (cons "STR" "1")
(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)"
}
]