;; ============================================================ ;; KETTENFOERDERER.LSP - Schraegfoerderer Stahl (Omniflo) ;; ============================================================ ;; Baut aus drei vorhandenen Bloecken eine Kette zusammen und fasst sie zu ;; EINEM Block KF_ zusammen - dasselbe Muster wie KREISEL_n/VF_n/GF_n: ;; ;; KF_UM_ (Umlenkstation, unten) ;; KS_AUS --> KS_EIN ;; KF_LAENGE (Zwischenstueck, in X SKALIERT - wie die Staustrecke ;; beim VarioFoerderer) ;; KS_AUS --> KS_EIN ;; KF_AN_ (Antriebsstation, oben) ;; ;; GEOMETRIE - wichtigste Entwurfsentscheidung: ;; Die Neigung (26 bzw. 30 Grad) ist KEIN Rechenparameter. Sie steckt fest in ;; der Geometrie der Varianten-Bloecke (KF_UM_26 gegen KF_UM_30). Der ;; Einbauwinkel jedes Folgebausteins wird darum zur Laufzeit aus der RICHTUNG ;; des KS_AUS seines Vorgaengers gemessen (ks-xy-winkel/ks-block-messen in ;; ssg_ks_insert.lsp) - es gibt hier keine gepflegte Winkeltabelle, die beim ;; Austausch einer DWG veralten koennte. Der JSON-Wert "winkel" dient nur der ;; Variantenwahl, der Anzeige und dem Attribut KF_WINKEL. ;; ;; Die Bloecke sind eine 2D-SEITENANSICHT: die Steigung liegt in der ;; XY-Zeichenebene, die Hoehenachse ist Y (nicht Z wie bei VF/GF). Die ;; Neigung ist damit eine reine Rz-Drehung - deshalb genuegt ;; insert-block-by-ks/insert-block-ks-skaliert, die Rz*Ry-Primitiven des ;; VarioFoerderers werden hier nicht gebraucht. ;; ;; Die Einbauhoehe H (Abstand KS_EIN der Umlenkung bis KS_AUS des Antriebs auf ;; der Y-Achse) bestimmt die Laenge des Zwischenstuecks ueber die ;; Hoehenbilanz, siehe kf-laenge-berechnen. ;; ;; Abhaengigkeiten: ssg_core, ssg_lang, ssg_layer, ssg_id, ssg_ks_insert, ;; OmniModulInsert (nur fuer omni:omniflo-datei-finden - die KF-DWGs liegen ;; wie alle Omniflo-Bauteile in data/omniflo//). ;; ============================================================ (vl-load-com) ;; modelspace/doc werden von den KS-Einfuegeprimitiven als GLOBALE Variablen ;; erwartet (gesetzt von vf_core.lsp bzw. Gefaellestrecke.lsp). Beim isolierten ;; Laden dieses Moduls - etwa in einem Testskript - ist noch keines davon ;; geladen, darum hier guarded nachziehen (gleiches Muster wie TEFInsert.lsp). (if (null doc) (setq doc (vla-get-ActiveDocument (vlax-get-acad-object)))) (if (null modelspace) (setq modelspace (vla-get-ModelSpace doc))) ;; Einfuegeprimitiven guarded nachladen (die MNL laedt ssg_ks_insert als ;; Kernmodul, beim isolierten Test-Laden fehlt es aber). Geprueft wird ueber ;; atoms-family, nicht ueber (null ) - ein noch nie definiertes ;; Funktionssymbol ist zwar nil, ein definiertes liefert aber eine USUBR, und ;; die Abfrage waere damit vom Ladezustand abhaengig statt von der Existenz. (if (not (car (atoms-family 1 '("INSERT-BLOCK-KS-SKALIERT")))) (if (findfile (ssg-lisp-datei-pfad "ssg_ks_insert.lsp")) (load (ssg-lisp-datei-pfad "ssg_ks_insert.lsp")))) ;; ------------------------------------------------------------ ;; LAUFZEIT-PROTOKOLL (Schalter "kf-zeit") ;; ------------------------------------------------------------ ;; Schreibt Zwischenzeiten ueber ssg_dbg (dbgmsg) statt per princ ins ;; BricsCAD-Fenster - die Zeilen landen damit in einer .dbg-Datei im ;; DXFM_LOG-Verzeichnis und sind ueber den Schalter abschaltbar, koennen ;; also dauerhaft im Code bleiben (siehe Debug-Abschnitt in CLAUDE.md). ;; Einschalten: (dbg-schalter-setzen "kf-zeit" 1), dann KETTENFOERDERER. ;; Zweck: klaeren, wo die spuerbare Verzoegerung beim ersten Menueklick ;; entsteht (Lazy-Laden des Moduls / JSON lesen / Dialogaufbau). ;; Eigener Dateihandle statt dbgmsg: ssg_dbg fuehrt mit *dbg-path* genau EINE ;; global offene Datei - die Zeilen wuerden sonst stillschweigend in einem ;; fremden Protokoll landen (gleiche Ueberlegung wie bei ssg-sepliste-dbg). (if (not (boundp '*kf-zeit-pfad*)) (setq *kf-zeit-pfad* nil)) (defun kf-zeit (was t0 / dir fh) (if (and t0 (car (atoms-family 1 '("DBG-SCHALTER-P"))) (dbg-schalter-p "kf-zeit")) (progn (if (null *kf-zeit-pfad*) (progn (setq dir (getenv "DXFM_LOG")) (if (or (null dir) (= dir "")) (setq dir ".")) (setq *kf-zeit-pfad* (strcat dir "\\kf_zeit.dbg")))) (setq fh (open *kf-zeit-pfad* "a")) (if fh (progn (write-line (strcat was ": " (itoa (fix (- (getvar "MILLISECS") t0))) " ms seit Befehlsstart") fh) (close fh))))) (princ)) ;; ============================================================ ;; DATEN (data/json/kettenfoerderer.json) ;; ============================================================ ;; Verschachteltes JSON -> ssg-json-laden (rekursiver Parser), NICHT der ;; zeilenbasierte ssg-load-json: die Datei hat verschachtelte Objekte und ;; Kommas in den Beschreibungstexten. (if (not (boundp '*kf-daten*)) (setq *kf-daten* nil)) (defun kf-daten-datei ( / basis) (setq basis (getenv "DXFM_DATA")) (if (or (null basis) (= basis "")) (setq basis (strcat (getenv "DXFMAKRO") "/data"))) (strcat basis "/json/kettenfoerderer.json")) ;; Geladene Daten (Cache). kf-daten-reset erzwingt das Neulesen nach einer ;; Aenderung an der JSON, ohne BricsCAD neu zu starten. (defun kf-daten ( / datei roh) (if (null *kf-daten*) (progn (setq datei (kf-daten-datei)) (if (findfile datei) (progn (setq roh (ssg-json-laden datei)) (setq *kf-daten* (ssg-json-get roh "kettenfoerderer"))) (princ (ssg-textf "kf-fehler-daten-fehlt" (list datei)))))) *kf-daten*) (defun kf-daten-reset ( ) (setq *kf-daten* nil) (princ)) ;; ============================================================ ;; REINE RECHENKERNE (ohne Zeichnung/Datei testbar - tests/test_unit.lsp) ;; ============================================================ ;; JSON-Wert als Zahl. Der Parser liefert Zahlen je nach Schreibweise als ;; REAL/INT oder (bei gequoteten Werten) als String - beides zulassen. (defun kf-zahl (v) (cond ((null v) nil) ((numberp v) (float v)) ((= (type v) 'STR) (atof v)) (t nil))) ;; Laenge des skalierbaren Zwischenstuecks aus der Hoehenbilanz. ;; H = dy-um + [ L*sin(hz) + dy-nativ*cos(hz) ] + dy-an ;; aufgeloest nach L. Der Term dy-nativ*cos(hz) faellt weg, wenn das ;; Zwischenstueck waagerecht gezeichnet ist (Normalfall) - er steht hier, ;; damit auch ein bereits geneigt gezeichnetes KF_LAENGE richtig gerechnet ;; wird. dy-nativ skaliert BEWUSST nicht mit: skaliert wird nur in X. ;; h-soll gewuenschte Einbauhoehe H (mm) ;; dy-um Y-Versatz der eingebauten Umlenkstation ;; dy-an Y-Versatz der eingebauten Antriebsstation ;; dy-nativ Y-Anteil des KS_AUS-KS_EIN-Versatzes von KF_LAENGE (Blockmass) ;; hz Einbauwinkel des Zwischenstuecks (Grad) ;; Rueckgabe: Laenge entlang der Blockachse (mm), oder nil wenn die Neigung ;; keinen Hoehengewinn liefert (sin(hz)=0 - dann ist H nicht erreichbar). (defun kf-laenge-berechnen (h-soll dy-um dy-an dy-nativ hz / rad sh ch rest) (setq rad (* (float hz) (/ pi 180.0))) (setq sh (sin rad) ch (cos rad)) (if (< (abs sh) 1e-9) nil (progn (setq rest (- (float h-soll) (float dy-um) (float dy-an) (* (float dy-nativ) ch))) (/ rest sh)))) ;; X-Massstab fuer KF_LAENGE: gewuenschte Laenge / native Blocklaenge. ;; nil, wenn der Block keine messbare X-Laenge hat. (defun kf-skala-berechnen (laenge dx-nativ) (if (or (null laenge) (null dx-nativ) (< (abs dx-nativ) 1e-9)) nil (/ (float laenge) (float dx-nativ)))) ;; Errechnete Laenge gegen die Grenzen aus der JSON pruefen. ;; Rueckgabe: nil = in Ordnung, sonst der Sprach-Key der Meldung. (defun kf-laenge-pruefen (laenge lmin lmax) (cond ((null laenge) "kf-fehler-winkel-null") ((<= laenge 0.0) "kf-fehler-hoehe-zu-klein") ((and lmin (< laenge (float lmin))) "kf-fehler-laenge-unter-min") ((and lmax (> laenge (float lmax))) "kf-fehler-laenge-ueber-max") (t nil))) ;; Y-Anteil des KS_AUS-KS_EIN-Versatzes nach Einbau mit Rz(hz) und X-Massstab. ;; In der 2D-Seitenansicht ist Y die Hoehenachse. (defun kf-dy-eingebaut (dx-nativ dy-nativ hz skala / rad) (setq rad (* (float hz) (/ pi 180.0))) (+ (* (* (float skala) (float dx-nativ)) (sin rad)) (* (float dy-nativ) (cos rad)))) ;; Einbauwinkel (Rz, Grad) eines Folgebausteins: sein KS_EIN soll in dieselbe ;; Richtung zeigen wie das KS_AUS seines Vorgaengers. ;; hz = Weltrichtung(KS_AUS Vorgaenger) - Eigenrichtung(KS_EIN Folgebaustein) ;; Fehlt die Eigenrichtung des Folgebausteins, wird er wie sein Vorgaenger ;; ausgerichtet; fehlt die Vorgaengerrichtung, ist keine Aussage moeglich (nil). (defun kf-hz-folge (phi-aus beta-ein) (cond ((null phi-aus) nil) ((null beta-ein) (ks-winkel-normalisieren phi-aus)) (t (ks-winkel-normalisieren (- phi-aus beta-ein))))) ;; Variante zu einem Winkel suchen. Rueckgabe: Varianten-Objekt oder nil. (defun kf-variante-finden (winkel varianten / treffer w) (setq treffer nil) (foreach v varianten (setq w (kf-zahl (ssg-json-get v "winkel"))) (if (and (null treffer) w (< (abs (- w (float winkel))) 0.01)) (setq treffer v))) treffer) ;; ------------------------------------------------------------ ;; ATTRIBUT-SCHEMA ;; ------------------------------------------------------------ ;; EIGENSTAENDIGE Liste - bewusst NICHT vom Strecken-Schema des ;; VarioFoerderers (`ssg-strecke-attrib-defs`) abgeleitet. Anfangs war sie ;; das, was dem Block rund zwei Dutzend geerbte Tags eintrug, die der ;; Kettenfoerderer gar nicht kennt (SEITE_AS/ES, GF_Bogen_*, ANZAHL_VF, ;; MOTORSEITE, VF_Bogen_*, ANZAHL_SEPARATOR ...). Sie standen leer am Block, ;; waren im Attributeditor nur Ballast und haetten im Export Werte ;; vorgetaeuscht, die es hier nicht gibt. Eine explizite Liste ist zudem ;; leichter zu lesen als eine Erbung mit Ausnahmeliste. ;; ;; Was die Tags bedeuten: ;; ID - vergibt ssg-id-generate (Voraussetzung fuer den Export) ;; Bezeichnung - Blockname KF_, CSV-Spalte ;; ARTINR - Sivas-Artikelnummer, CSV-Spalte "SivasNummer" ;; FOERDERERTYP - Bauart im Klartext ("Schraegfoerderer Stahl"), ;; Merkmal 350. Der Wert kommt aus der JSON ;; (Feld "foerderertyp"), nicht aus dem Code - so ;; bleibt diese Datei frei von Sonderzeichen und der ;; Text ist an einer Stelle pflegbar. ;; WINKEL - Neigungswinkel als nackte Zahl ("26"/"30"). ;; Den Sivas-Klartext mit Gradzeichen erzeugt erst die ;; Tabelle [kf_typ_anzeige] (cfg/sivas.cfg, Merkmal ;; 359) - Attributwerte bleiben so ASCII. ;; STANDARD_STEUERUNG - Merkmal 361, "1"/"0" ueber Transform ja_nein. ;; Steht direkt hinter WINKEL, wie im Merkmalstamm. ;; H_EINBAUHOEHE_mm - Merkmal 360 ;; L_KF_mm - Laenge des skalierten Zwischenstuecks, CSV-Spalte ;; FARBE - Merkmal 354 ;; ANTRIEBSMOTOR - Merkmal 355 (eigenes Merkmal, nicht Teil von 354) ;; ANTRIEBSART - Merkmal 357, FEST "1.1 kW" (Wert aus der JSON, ;; im Dialog ein Dropdown mit EINER, zwingend gewaehlten Option) ;; MOVIMOT - Merkmal 356, "1"/"0" ueber Transform ja_nein ;; VERBINDUNGSELEMENTE - Merkmal 391 ;; LAYER - Ziel-Layer der Baugruppe ;; ;; KEINE TEILEART: sie ist fuer jeden Kettenfoerderer gleich und steht darum ;; als Festwert im Export (KF_TEILEART in export_sivas.py) statt an jedem ;; Block. Die Erkennung laeuft ohnehin ueber den Blocknamen (pattern_ ;; kettenfoerderer), nicht ueber TEILEART. (setq *kf-attrib-defs-liste* '(("ID" "") ("Bezeichnung" "") ("ARTINR" "") ("FOERDERERTYP" "Schraegfoerderer Stahl") ("WINKEL" "26") ;; Reihenfolge wie im Sivas-Merkmalstamm: 361 folgt direkt auf den Typ. ("STANDARD_STEUERUNG" "0") ("H_EINBAUHOEHE_mm" "0") ("L_KF_mm" "0") ("FARBE" "RAL 7035") ("ANTRIEBSMOTOR" "Europa 50 Hz") ;; Merkmal 357 - beim Kettenfoerderer fest, im Dialog nur Anzeige. ("ANTRIEBSART" "1.1 kW") ("MOVIMOT" "0") ("VERBINDUNGSELEMENTE" "Nein") ("LAYER" "F-3"))) (defun kf-attrib-defs ( ) *kf-attrib-defs-liste*) ;; Antriebsart (Merkmal 357). Beim Kettenfoerderer gibt es genau EINE Option ;; ("1.1 kW"), die im Dialog zwingend gewaehlt ist. Sie steht als Liste in der ;; JSON (wie alle anderen Auswahlen), nicht im Code. Rueckgabe: der "wert" des ;; ERSTEN Eintrags - bei einer Ein-Eintrag-Liste also immer derselbe Wert. (defun kf-antriebsart (daten) (kf-auswahl-wert (ssg-json-get daten "antriebsart") 0 "1.1 kW")) ;; Position eines Tags in einer ATTDEF-Liste (0-basiert), nil wenn nicht ;; enthalten. Die REIHENFOLGE ist nicht beliebig: sie bestimmt, in welcher ;; Folge die Attribute im Blockeditor und in den Exportzeilen erscheinen, und ;; folgt darum dem Sivas-Merkmalstamm. Reiner Kern, in test_unit geprueft. (defun kf-tag-index (tag defs / i treffer) (setq i 0 treffer nil) (foreach d defs (if (and (null treffer) (= (car d) tag)) (setq treffer i)) (setq i (1+ i))) treffer) ;; ============================================================ ;; BLOCK-DATEIEN (data/omniflo//) ;; ============================================================ ;; Die KF-Bloecke liegen - anders als die ILS-Bloecke in data/ils (flach, mit ;; Dimensionssuffix _2D/_3D) - in den Omniflo-Ordnern ohne Suffix. Die ;; KS-Primitiven in ssg_ks_insert.lsp suchen ueber ssg-ils-block-laden aber in ;; data/ils. Darum wird die Blockdefinition HIER vorab aus dem Omniflo-Ordner ;; in die Zeichnung geladen; ensure-block-loaded findet sie dann per ;; tblsearch bereits vor und laesst sie unangetastet. (defun kf-block-laden (blockname / pfad omniflo-pfad temp-obj) (if (tblsearch "BLOCK" blockname) blockname (progn (setq omniflo-pfad (getenv "DXFM_OMNIFLO")) (setq pfad (if (and omniflo-pfad (car (atoms-family 1 '("OMNI:OMNIFLO-DATEI-FINDEN")))) (omni:omniflo-datei-finden blockname omniflo-pfad) nil)) ;; Rueckfall ohne OmniModulInsert/DXFM_OMNIFLO: direkt im Datenordner (if (null pfad) (setq pfad (kf-block-datei-direkt blockname))) (if pfad (progn ;; vla-InsertBlock mit DATEIPFAD definiert den Block unter seinem ;; Dateinamen (gleiches Vorgehen wie dinformate-block-laden). (setq temp-obj (vla-InsertBlock modelspace (vlax-3D-point '(0 0 0)) pfad 1.0 1.0 1.0 0)) (if (and temp-obj (not (vlax-erased-p temp-obj))) (vla-Delete temp-obj)) (if (tblsearch "BLOCK" blockname) blockname nil)) (progn (princ (ssg-textf "kf-fehler-block-datei" (list blockname))) nil))))) ;; Blockdatei ohne DXFM_OMNIFLO suchen: data/omniflo// in der aktuellen ;; Dimension, dann in der anderen (der 3D-Datenbestand ist unvollstaendig). (defun kf-block-datei-direkt (blockname / basis dim kandidaten treffer) (setq basis (getenv "DXFM_DATA")) (if (or (null basis) (= basis "")) (setq basis (strcat (getenv "DXFMAKRO") "/data"))) (setq dim (if (car (atoms-family 1 '("SSG-ILS-DIM-AKTUELL"))) (ssg-ils-dim-aktuell) "2D")) (setq kandidaten (list (strcat basis "/omniflo/" dim "/" blockname ".dwg") (strcat basis "/omniflo/" dim "/" blockname ".dxf") (strcat basis "/omniflo/" (if (= dim "2D") "3D" "2D") "/" blockname ".dwg") (strcat basis "/omniflo/" (if (= dim "2D") "3D" "2D") "/" blockname ".dxf"))) (setq treffer nil) (foreach k kandidaten (if (and (null treffer) (findfile k)) (setq treffer (findfile k)))) treffer) ;; ============================================================ ;; AUFRICHTUNG: die Baugruppe steht im Modell ;; ============================================================ ;; Die Bausteine sind in der XY-Ebene gezeichnet (X = Fahrtrichtung, ;; Y = Hoehe) - eine Seitenansicht. Eingebaut werden sie AUFGERICHTET: um ;; 90 Grad um die Fahrtrichtungsachse gekippt, sodass die Hoehe nach Z ;; wandert. Von oben sieht man dann die Draufsicht KF_2D, von der Seite die ;; Kette; beide bilden EIN Objekt ohne Versatz. ;; ;; Gesamtdrehung je Baustein: Rz(basis) * Rx(90) * Rz(eigen) ;; Rz(eigen) richtet den Baustein in seiner EIGENEN Zeichenebene aus ;; (der aus den KS gemessene Verkettungswinkel, s. kf-hz-folge) ;; Rx(90) richtet die Ebene auf - Y_lokal wird zu Z_welt ;; Rz(basis) dreht die fertige Baugruppe in der Draufsicht ;; Die Reihenfolge ist nicht beliebig: Rz(eigen) muss VOR dem Aufrichten ;; wirken (es ist eine Drehung in der Blockebene), Rz(basis) danach (es ist ;; eine Drehung im Modell). (defun kf-matrix-aufrecht (basis-hz eigen-hz) (mat3-mul-mat3 (ks-rot-z basis-hz) (mat3-mul-mat3 (ks-rot-x 90.0) (ks-rot-z eigen-hz)))) ;; ============================================================ ;; GEOMETRIE EINER VARIANTE MESSEN ;; ============================================================ ;; Misst alle drei Bloecke EINMAL und leitet daraus die Einbauwinkel und die ;; Hoehenbeitraege ab. Ergebnis ist ein Record (Assoc-Liste), den der Dialog ;; fuer die Laengenvorschau und der Bau fuer die eigentliche Verkettung nutzt - ;; gemessen wird also genau einmal je Variante. ;; ;; Felder: "hz-um" "hz-l" "hz-an" Einbauwinkel (Grad, relativ zur Basisdrehung) ;; "dy-um" "dy-an" Hoehenbeitrag der beiden Stationen ;; "dx-l" "dy-l" nativer KS-Versatz von KF_LAENGE ;; "fehler" Sprach-Key, falls etwas fehlt (sonst nil) (defun kf-geometrie-messen (variante / b-um b-l b-an ks-um ks-l ks-an v-um v-l v-an beta-aus-um beta-ein-l beta-aus-l beta-ein-an hz-um hz-l hz-an dy-um dy-an fehler) (setq b-um (ssg-json-get variante "block_umlenkung") b-l (ssg-json-get variante "block_laenge") b-an (ssg-json-get variante "block_antrieb")) (setq fehler nil) (if (not (and (kf-block-laden b-um) (kf-block-laden b-l) (kf-block-laden b-an))) (setq fehler "kf-fehler-block-datei") (progn (setq ks-um (ks-block-messen b-um) ks-l (ks-block-messen b-l) ks-an (ks-block-messen b-an)) (setq v-um (ks-versatz ks-um) v-l (ks-versatz ks-l) v-an (ks-versatz ks-an)) (if (not (and v-um v-l v-an)) ;; Welcher Baustein klemmt, gehoert ins Protokoll: die Meldung allein ;; ("in mindestens einem Baustein fehlt ein KS") hat bei einem realen ;; Fehlschlag nicht gereicht, um die Ursache zu finden - sie lag nicht ;; an den Bloecken, sondern an einem falsch aufgeloesten Blocknamen ;; (siehe ensure-block-loaded in ssg_ks_insert.lsp). (progn (foreach paar (list (cons b-um v-um) (cons b-l v-l) (cons b-an v-an)) (if (null (cdr paar)) (princ (ssg-textf "kf-pruef-ks-fehlt-kurz" (list (car paar)))))) (setq fehler "kf-fehler-ks-fehlt")) (progn ;; Eigenrichtungen der KS in Blockkoordinaten (setq beta-aus-um (ks-xy-winkel (cadr (assoc "KS_AUS" ks-um))) beta-ein-l (ks-xy-winkel (cadr (assoc "KS_EIN" ks-l))) beta-aus-l (ks-xy-winkel (cadr (assoc "KS_AUS" ks-l))) beta-ein-an (ks-xy-winkel (cadr (assoc "KS_EIN" ks-an)))) ;; Die Umlenkstation gibt die Basisrichtung vor (hz 0); jeder ;; Folgebaustein richtet sich am KS_AUS seines Vorgaengers aus. (setq hz-um 0.0) (setq hz-l (cond ((kf-hz-folge (if beta-aus-um (+ beta-aus-um hz-um) nil) beta-ein-l)) (0.0))) (setq hz-an (cond ((kf-hz-folge (if beta-aus-l (+ beta-aus-l hz-l) nil) beta-ein-an)) (hz-l))) ;; Hoehenbeitrag der beiden Stationen (Massstab 1) (setq dy-um (kf-dy-eingebaut (car v-um) (cadr v-um) hz-um 1.0)) (setq dy-an (kf-dy-eingebaut (car v-an) (cadr v-an) hz-an 1.0)))))) (list (cons "fehler" fehler) (cons "hz-um" (cond (hz-um) (0.0))) (cons "hz-l" (cond (hz-l) (0.0))) (cons "hz-an" (cond (hz-an) (0.0))) (cons "dy-um" (cond (dy-um) (0.0))) (cons "dy-an" (cond (dy-an) (0.0))) (cons "dx-l" (cond ((car v-l)) (0.0))) (cons "dy-l" (cond ((cadr v-l)) (0.0))))) ;; Laenge + Massstab fuer eine Hoehe aus einem gemessenen Geometrie-Record. ;; Rueckgabe: (laenge skala fehler-key) - laenge/skala nil bei Fehler. (defun kf-laenge-fuer-hoehe (geo hoehe lmin lmax / laenge skala key) (if (cdr (assoc "fehler" geo)) (list nil nil (cdr (assoc "fehler" geo))) (progn (setq laenge (kf-laenge-berechnen hoehe (cdr (assoc "dy-um" geo)) (cdr (assoc "dy-an" geo)) (cdr (assoc "dy-l" geo)) (cdr (assoc "hz-l" geo)))) (setq key (kf-laenge-pruefen laenge lmin lmax)) (if key (list nil nil key) (progn (setq skala (kf-skala-berechnen laenge (cdr (assoc "dx-l" geo)))) (if (null skala) (list nil nil "kf-fehler-ks-fehlt") (list laenge skala nil))))))) ;; ============================================================ ;; KETTE BAUEN (aufgerichtet, 3D) ;; ============================================================ ;; Fuegt die drei Bausteine ab startpunkt verkettet ein - AUFGERICHTET, die ;; Hoehe laeuft also in Z (siehe kf-matrix-aufrecht). basis-hz dreht die ;; fertige Baugruppe in der Draufsicht. ;; Rueckgabe: Weltpunkt des KS_AUS der Antriebsstation (Kettenende). (defun kf-kette-bauen (variante geo skala startpunkt basis-hz / pt) (setq pt startpunkt) (setq pt (insert-block-ks-matrix (ssg-json-get variante "block_umlenkung") pt (kf-matrix-aufrecht basis-hz (cdr (assoc "hz-um" geo))) 1.0)) (setq pt (insert-block-ks-matrix (ssg-json-get variante "block_laenge") pt (kf-matrix-aufrecht basis-hz (cdr (assoc "hz-l" geo))) skala)) (setq pt (insert-block-ks-matrix (ssg-json-get variante "block_antrieb") pt (kf-matrix-aufrecht basis-hz (cdr (assoc "hz-an" geo))) 1.0)) pt) ;; ------------------------------------------------------------ ;; DRAUFSICHT (KF_2D) - deckungsgleich mit der Kette ;; ------------------------------------------------------------ ;; KF_2D gehoert in DIESELBE Lage wie die Kette, nicht daneben: sein KS_EIN ;; faellt mit KS_EIN der Umlenkung zusammen, sein KS_AUS mit KS_AUS des ;; Antriebs. Dadurch ist die Baugruppe EIN Koerper - von oben sieht man die ;; Draufsicht, von der Seite die Kette. ;; ;; Zwischenzeitlich lagen beide Ansichten 1000 mm uebereinander (versatz_y). ;; Das war eine Hilfskonstruktion, solange die Kette flach in der XY-Ebene ;; lag; seit sie aufgerichtet eingebaut wird, ist sie unnoetig. ;; ;; KF_2D ist selbst flach gezeichnet und wird darum NICHT aufgerichtet, ;; sondern nur um die Steigung GENEIGT (Rz(basis) * Ry(-winkel)) und auf die ;; SCHRAEGLAENGE skaliert. In der Draufsicht erscheint es dadurch genau mit ;; der Grundrisslaenge - die Verkuerzung uebernimmt die Projektion, sie muss ;; nicht gerechnet werden. ;; Neigungswinkel und Schraeglaenge aus Start- und Endpunkt der Kette. ;; Rueckgabe: (schraeglaenge winkel-grad), oder nil bei Laenge 0. ;; Reiner Rechenkern - der eigentliche Pruefpunkt der 3D-Umstellung. (defun kf-neigung-aus-punkten (p1 p2 basis-hz / rad dx dy dz horiz laenge) (setq rad (* (float basis-hz) (/ pi 180.0))) (setq dx (- (car p2) (car p1)) dy (- (cadr p2) (cadr p1)) dz (- (cond ((caddr p2)) (0.0)) (cond ((caddr p1)) (0.0)))) ;; Horizontalanteil auf die Kettenrichtung projiziert (nicht die blosse ;; X-Differenz) - sonst stimmt es bei gedreht eingefuegter Kette nicht. (setq horiz (+ (* dx (cos rad)) (* dy (sin rad)))) (setq laenge (sqrt (+ (* horiz horiz) (* dz dz)))) (if (< laenge 1e-6) nil (list laenge (/ (* 180.0 (atan dz horiz)) pi)))) ;; Draufsicht einfuegen - deckungsgleich mit der Kette. ;; kette-start/kette-ende = KS_EIN der Umlenkung bzw. KS_AUS des Antriebs. ;; Rueckgabe: die verwendete Schraeglaenge (mm), sonst nil. (defun kf-ansicht-2d-bauen (daten kette-start kette-ende basis-hz / cfg bname masse laenge winkel ks ein-dx skala R) (setq cfg (ssg-json-get daten "ansicht_2d")) (setq bname (if cfg (ssg-json-get cfg "block"))) (if (or (null bname) (= bname "")) nil (progn (setq masse (kf-neigung-aus-punkten kette-start kette-ende basis-hz)) (if (null masse) (progn (princ (ssg-text "kf-2d-laenge-null")) nil) (progn (setq laenge (car masse) winkel (cadr masse)) (if (null (kf-block-laden bname)) (progn (princ (ssg-textf "kf-fehler-block-datei" (list bname))) nil) (progn (setq ks (ks-block-messen bname)) (setq ein-dx (car (cond ((ks-versatz ks)) ('(0.0 0.0 0.0))))) (setq skala (kf-skala-berechnen laenge ein-dx)) (if (null skala) (progn (princ (ssg-textf "kf-2d-ks-fehlt" (list bname))) nil) (progn ;; Ry(-winkel) hebt die flache Scheibe um die Steigung an; ;; das Vorzeichen folgt der Rechte-Hand-Regel (positives dz ;; = Drehung gegen den Uhrzeigersinn um die Y-Achse). (setq R (mat3-mul-mat3 (ks-rot-z basis-hz) (ks-rot-y (- winkel)))) (insert-block-ks-matrix bname kette-start R skala) (princ (ssg-textf "kf-2d-gebaut" (list bname (rtos laenge 2 0) (rtos winkel 2 1)))) laenge))))))))) ;; ============================================================ ;; DREHWINKEL DES BLOCKS (XDATA) ;; ============================================================ ;; Der Drehwinkel der Baugruppe wird beim Bau in die GEOMETRIE eingerechnet ;; (kf-matrix-aufrecht), nicht ueber die Drehung der Blockreferenz: ;; ssg-block-wrap-welt fuegt den fertigen Block immer mit Drehung 0 ein. Die ;; Referenz traegt damit nirgends mehr, wie die Kette gedreht wurde - KF_EDIT ;; wuerde sie beim Neuaufbau ungedreht neu bauen. Darum steht der Winkel als ;; XDATA am Block (Projektregel: Editierdaten gehoeren in XDATA, nicht in ;; .dbg). Eine spaetere Drehung der Referenz von Hand kommt ueber ;; Gruppencode 50 hinzu und wird addiert. (setq *kf-xdata-app* "SSG_KF_EDIT") ;; Gesamtdrehung = gebauter Winkel (XDATA, Grad) + Drehung der Referenz ;; (Gruppencode 50, Bogenmass), auf -180..180 Grad normalisiert. Reiner ;; Rechenkern; fehlende Werte zaehlen als 0 (Altbestand ohne XDATA). (defun kf-drehung-gesamt (xdata-grad dxf50-rad / g r) (setq g (cond (xdata-grad (float xdata-grad)) (0.0))) (setq r (cond (dxf50-rad (float dxf50-rad)) (0.0))) (ks-winkel-normalisieren (+ g (/ (* 180.0 r) pi)))) (defun kf-xdata-schreiben (ent drehung-grad / eintrag) (regapp *kf-xdata-app*) (setq eintrag (list *kf-xdata-app* (cons 1000 "kf-edit") (cons 1040 (float drehung-grad)))) (entmod (append (entget ent) (list (list -3 eintrag)))) ent) ;; Gebauter Drehwinkel (Grad) aus der XDATA, nil wenn keine vorhanden ist. (defun kf-xdata-lesen (ent / xd daten paar) (setq xd (entget ent (list *kf-xdata-app*))) (setq daten (cdr (assoc -3 xd))) (if daten (progn (setq paar (assoc 1040 (cdr (car daten)))) (if paar (cdr paar) nil)) nil)) ;; ============================================================ ;; BLOCK ABSCHLIESSEN ;; ============================================================ ;; Sammelt alle seit last-ent erzeugten Objekte, legt die ATTDEFs an, fasst ;; alles zu KF_ zusammen und beschriftet den fertigen Block. ;; Vorbild: gf-block-erstellen (Gefaellestrecke.lsp). (defun kf-block-erstellen (variante geo hoehe laenge verbindung startpunkt last-ent kf-nummer farbe antriebsmotor movimot steuerung drehung / kf-bname kf-ss ent kf-insert winkel-str) (setq kf-bname (strcat "KF_" (itoa kf-nummer))) ;; Winkel als nackte Ganzzahl ("26"/"30") - bewusst ueber itoa/fix statt ;; rtos: rtos haengt an DIMZIN/LUPREC/LUNITS der Zeichnung und liefert je ;; nach Zeichnungsprofil unterschiedliche Schreibweisen. Dieser Wert landet ;; als Attribut WINKEL am Block und wird vom Sivas-Export als Schluessel der ;; Tabelle [kf_typ_anzeige] verwendet - er muss profilunabhaengig sein ;; (gleiche Ueberlegung wie bei ssg-sepliste-fmt-koord). (setq winkel-str (itoa (fix (+ 0.5 (kf-zahl (ssg-json-get variante "winkel")))))) ;; ATTDEFs (unsichtbar, am Startpunkt) nach dem KF-Schema (foreach def (kf-attrib-defs) (entmake (list '(0 . "ATTDEF") (cons 10 startpunkt) (cons 11 startpunkt) '(40 . 50.0) (cons 1 (cadr def)) (cons 2 (car def)) (cons 3 (car def)) '(70 . 1) '(72 . 0) '(74 . 0)))) ;; Alle Entities seit last-ent einsammeln (setq kf-ss (ssadd)) (setq ent (if last-ent (entnext last-ent) (entnext))) (while ent (ssadd ent kf-ss) (setq ent (entnext ent))) ;; Block definieren und einfuegen (OSMODE/ELEVATION/BKS werden dabei sauber ;; abgeschaltet, siehe ssg-block-wrap-welt) (ssg-block-wrap-welt kf-bname startpunkt kf-ss) (setq kf-insert (entlast)) ;; Gebauten Drehwinkel festhalten, damit KF_EDIT ihn wiederfindet (kf-xdata-schreiben kf-insert drehung) (ssg-layer-setzen kf-insert (cond ((ssg-json-get variante "layer")) ((ssg-json-get (kf-daten) "layer")) ("kettenfoerderer"))) (ssg-attrib-set-on kf-insert (list (cons "Bezeichnung" kf-bname) ;; FOERDERERTYP = Bauart im Klartext (aus der JSON, mit Sonderzeichen), ;; WINKEL = Neigungswinkel als nackte Zahl ("26"/"30") (cons "FOERDERERTYP" (cond ((ssg-json-get (kf-daten) "foerderertyp")) ("Schraegfoerderer Stahl"))) (cons "WINKEL" winkel-str) (cons "H_EINBAUHOEHE_mm" (itoa (fix (+ 0.5 (float hoehe))))) (cons "L_KF_mm" (itoa (fix (+ 0.5 (float laenge))))) (cons "FARBE" (cond (farbe) ("RAL 7035"))) (cons "ANTRIEBSMOTOR" (cond (antriebsmotor) ("Europa 50 Hz"))) (cons "ANTRIEBSART" (kf-antriebsart (kf-daten))) (cons "MOVIMOT" (cond (movimot) ("0"))) (cons "STANDARD_STEUERUNG" (cond (steuerung) ("0"))) (cons "VERBINDUNGSELEMENTE" (cond (verbindung) ("Nein"))) (cons "ARTINR" (cond ((ssg-json-get variante "sivas_id")) (""))))) (ssg-id-generate kf-insert) kf-insert) ;; Naechste freie KF-Nummer in der Zeichnung. (defun kf-naechste-nummer ( / n ss i nm num) (setq n 0) (setq ss (ssget "X" '((0 . "INSERT") (2 . "KF_*")))) (if ss (progn (setq i 0) (while (< i (sslength ss)) (setq nm (cdr (assoc 2 (entget (ssname ss i))))) (setq num (atoi (substr nm 4))) (if (> num n) (setq n num)) (setq i (1+ i))))) (1+ n)) ;; ============================================================ ;; DIALOG ;; ============================================================ ;; Zustand zwischen den Aufrufen (Dialogstand bleibt je Sitzung erhalten). (if (not (boundp '*kf-dlg-variante*)) (setq *kf-dlg-variante* 0)) (if (not (boundp '*kf-dlg-variante-gesetzt*)) (setq *kf-dlg-variante-gesetzt* nil)) (if (not (boundp '*kf-dlg-hoehe*)) (setq *kf-dlg-hoehe* nil)) (if (not (boundp '*kf-dlg-verbindung*)) (setq *kf-dlg-verbindung* 0)) (if (not (boundp '*kf-dlg-farbe*)) (setq *kf-dlg-farbe* 0)) (if (not (boundp '*kf-dlg-antriebsmotor*)) (setq *kf-dlg-antriebsmotor* 0)) (if (not (boundp '*kf-dlg-movimot*)) (setq *kf-dlg-movimot* 0)) (if (not (boundp '*kf-dlg-steuerung*)) (setq *kf-dlg-steuerung* 0)) ;; ------------------------------------------------------------ ;; DCL-VORLAGE: Varianten als Radiobuttons ;; ------------------------------------------------------------ ;; DCL kann Tiles weder zur Laufzeit erzeugen noch radio_button-Labels per ;; set_tile aendern - der Varianten-Block wird darum an der Markerzeile ;; //@VARIANTEN@ eingesetzt (gleiches Vorgehen wie geruest_traeger.dcl). ;; Radiobuttons statt popup_list, weil es nur zwei Werte gibt und beide ohne ;; Aufklappen sichtbar sein sollen. (defun kf-radio-key (i) (strcat "v" (itoa i))) (defun kf-radio-index (key) (if (and key (> (strlen key) 1)) (atoi (substr key 2)) 0)) ;; Beschriftung einer Variante im Dialog: NUR der Winkel ("26 °"), nicht der ;; volle Variantenname - der Gruppenrahmen nennt bereits "Schraegfoerderer ;; Stahl Typ", der Name waere dort doppelt. Die Regelausfuehrung bekommt den ;; Zusatz "(Standard)". ;; ;; Welche Variante das ist, steht als "standard": true in der JSON und NICHT ;; im Code - sonst muesste bei einer Umstellung auf 30 Grad der Code ;; angefasst werden. Das Gradzeichen kommt als (chr 176) ueber einen ;; Platzhalter, damit die Sprachdatei ASCII bleibt (Projektkonvention). (defun kf-variante-standard-p (variante) (not (null (ssg-json-get variante "standard")))) (defun kf-variante-label (variante / w) (setq w (kf-zahl (ssg-json-get variante "winkel"))) (if w (ssg-textf (if (kf-variante-standard-p variante) "kf-variante-label-standard" "kf-variante-label") (list (itoa (fix (+ 0.5 w))) (chr 176))) (cond ((ssg-json-get variante "name")) ("")))) ;; Index der Standard-Variante (fuer die Vorauswahl beim ersten Aufruf); ;; 0, wenn keine als Standard markiert ist. (defun kf-standard-index (varianten / i treffer) (setq i 0 treffer nil) (foreach v varianten (if (and (null treffer) (kf-variante-standard-p v)) (setq treffer i)) (setq i (1+ i))) (cond (treffer) (0))) (defun kf-dcl-varianten-zeilen (varianten / i erg) (setq erg nil i 0) (foreach v varianten (setq erg (append erg (list (strcat " : radio_button { key = \"" (kf-radio-key i) "\"; label = \"" (kf-variante-label v) "\"; }")))) (setq i (1+ i))) erg) ;; Rueckgabe: Pfad der gefuellten temporaeren DCL-Datei oder nil. (defun kf-dcl-schreiben (varianten) (ssg-dcl-vorlage-fuellen (kf-dcl-pfad "kettenfoerderer.dcl") "kettenfoerderer" (list (cons "VARIANTEN" (kf-dcl-varianten-zeilen varianten))))) ;; ------------------------------------------------------------ ;; EINBAUHOEHE AUS ZWEI GEPICKTEN PUNKTEN ;; ------------------------------------------------------------ ;; Zweite Eingabeart neben dem Zahlenfeld: statt die Hoehe zu tippen, werden ;; zwei Punkte in der Zeichnung gewaehlt (z.B. Oberkante Zufuehrung und ;; Oberkante Abgabe) und ihr Hoehenunterschied uebernommen. ;; ;; Gemessen wird die Differenz in Z: die Baugruppe steht aufgerichtet im Modell ;; (siehe kf-matrix-aufrecht), ihre Hoehe ist also die Hoehe der Zeichnung. ;; Damit nachvollziehbar bleibt, was gemessen wurde, nennt die Meldung BEIDE ;; Differenzen (dZ und dY): wer in einer DRAUFSICHT pickt, sieht sofort, dass ;; dort dZ = 0 ist, und kann es melden. ;; ;; Reiner Rechenkern (ohne Zeichnung testbar): nimmt zwei Punkte, liefert ;; (dz dy) als Betraege - die HOEHE zuerst. (defun kf-punkt-differenzen (p1 p2) (if (and p1 p2 (listp p1) (listp p2)) (list (abs (- (cond ((caddr p2)) (0.0)) (cond ((caddr p1)) (0.0)))) (abs (- (cadr p2) (cadr p1)))) nil)) ;; Zwei Punkte abfragen und die Hoehe daraus bestimmen. ;; Rueckgabe: Hoehe in mm, oder nil bei Abbruch. (defun kf-hoehe-aus-punkten ( / p1 p2 d) (setq p1 (getpoint (ssg-text "kf-prompt-messpunkt-1"))) (if (null p1) nil (progn (setq p2 (getpoint p1 (ssg-text "kf-prompt-messpunkt-2"))) (if (null p2) nil (progn (setq d (kf-punkt-differenzen p1 p2)) (princ (ssg-textf "kf-messung-ergebnis" (list (rtos (car d) 2 1) (rtos (cadr d) 2 1)))) (car d)))))) ;; Wert eines Auswahleintrags ("wert", sonst "name", sonst Vorgabe). ;; Gemeinsam genutzt von Dialog und Konsolen-Rueckfall. (defun kf-auswahl-wert (liste idx vorgabe / e) (setq e (nth idx liste)) (cond ((null e) vorgabe) ((ssg-json-get e "wert")) ((ssg-json-get e "name")) (t vorgabe))) ;; Dialog zeigen - SCHLEIFE, bis OK oder Abbruch. Rueckgabe: ;; (variante hoehe verbindung farbe antriebsmotor movimot) ;; oder nil bei Abbruch. ;; ;; Die Schleife gibt es wegen der zweiten Hoehen-Eingabeart: der Knopf ;; "Hoehe messen" schliesst den Dialog (done_dialog 2), laesst zwei Punkte in ;; der Zeichnung waehlen und zeigt ihn mit dem gemessenen Wert erneut. Aus ;; einem action_tile heraus laesst sich kein getpoint aufrufen - dasselbe ;; Vorgehen wie beim Speichern/Loeschen im Geruest-Dialog (done_dialog 2/3). ;; ;; Die Laengenvorschau unter dem Hoehenfeld gab es zwischenzeitlich; sie ist ;; auf Nutzerwunsch wieder entfallen. Ein nicht baubarer Wert wird darum erst ;; nach OK gemeldet (alert in c:KETTENFOERDERER). (defun kf-dialog (daten / varianten verbindungen farben motoren movimots steuerungen idx ergebnis gemessen) (setq varianten (ssg-json-get daten "varianten") verbindungen (ssg-json-get daten "verbindungselemente") farben (ssg-json-get daten "farbe") motoren (ssg-json-get daten "antriebsmotor") movimots (ssg-json-get daten "movimot") steuerungen (ssg-json-get daten "standard_steuerung")) (if (null *kf-dlg-hoehe*) (setq *kf-dlg-hoehe* (cond ((kf-zahl (ssg-json-get (ssg-json-get daten "einbauhoehe") "vorgabe"))) (1500.0)))) ;; Beim ERSTEN Aufruf je Sitzung die Regelausfuehrung vorwaehlen; danach ;; bleibt die zuletzt getroffene Wahl stehen. (if (null *kf-dlg-variante-gesetzt*) (progn (setq *kf-dlg-variante* (kf-standard-index varianten)) (setq *kf-dlg-variante-gesetzt* T))) (setq idx 2) (while (= idx 2) (setq idx (kf-dialog-zeigen daten varianten verbindungen farben motoren movimots steuerungen)) (if (= idx 2) (progn (setq gemessen (kf-hoehe-aus-punkten)) (if gemessen (setq *kf-dlg-hoehe* gemessen)))) ) (if (= idx 1) (list (nth *kf-dlg-variante* varianten) *kf-dlg-hoehe* (kf-auswahl-wert verbindungen *kf-dlg-verbindung* "Nein") (kf-auswahl-wert farben *kf-dlg-farbe* "RAL 7035") (kf-auswahl-wert motoren *kf-dlg-antriebsmotor* "Europa 50 Hz") (kf-auswahl-wert movimots *kf-dlg-movimot* "0") (kf-auswahl-wert steuerungen *kf-dlg-steuerung* "0")) nil)) ;; Den Dialog EINMAL zeigen. Rueckgabe: 1 = OK, 0 = Abbruch, 2 = "Hoehe ;; messen" (der Aufrufer pickt dann zwei Punkte und ruft erneut auf). (defun kf-dialog-zeigen (daten varianten verbindungen farben motoren movimots steuerungen / dcl-pfad dcl-id idx vb-namen fa-namen nz-namen mm-namen st-namen) (setq vb-namen (mapcar '(lambda (v) (cond ((ssg-json-get v "name")) (""))) verbindungen)) (setq fa-namen (mapcar '(lambda (v) (cond ((ssg-json-get v "name")) (""))) farben)) (setq nz-namen (mapcar '(lambda (v) (cond ((ssg-json-get v "name")) (""))) motoren)) (setq mm-namen (mapcar '(lambda (v) (cond ((ssg-json-get v "name")) (""))) movimots)) (setq st-namen (mapcar '(lambda (v) (cond ((ssg-json-get v "name")) (""))) steuerungen)) ;; Vorlage mit den Varianten-Radiobuttons fuellen (Temp-Datei) (setq dcl-pfad (kf-dcl-schreiben varianten)) (if (not dcl-pfad) (progn (alert (ssg-text "kf-fehler-dcl")) 0) (progn (setq dcl-id (load_dialog dcl-pfad)) (if (not (new_dialog "kettenfoerderer" dcl-id)) (progn (unload_dialog dcl-id) (vl-file-delete dcl-pfad) (alert (ssg-text "kf-fehler-dcl")) 0) (progn (start_list "verbindung") (mapcar 'add_list vb-namen) (end_list) (start_list "farbe") (mapcar 'add_list fa-namen) (end_list) (start_list "antriebsmotor") (mapcar 'add_list nz-namen) (end_list) (start_list "movimot") (mapcar 'add_list mm-namen) (end_list) (start_list "standard_steuerung") (mapcar 'add_list st-namen) (end_list) ;; Radiobuttons: der gewaehlte Button wird einzeln gesetzt, nicht ;; die Gruppe - set_tile auf den Gruppenschluessel waehlt nicht aus. (if (>= *kf-dlg-variante* (length varianten)) (setq *kf-dlg-variante* 0)) (set_tile (kf-radio-key *kf-dlg-variante*) "1") (set_tile "verbindung" (itoa *kf-dlg-verbindung*)) (set_tile "farbe" (itoa *kf-dlg-farbe*)) (set_tile "antriebsmotor" (itoa *kf-dlg-antriebsmotor*)) (set_tile "movimot" (itoa *kf-dlg-movimot*)) (set_tile "standard_steuerung" (itoa *kf-dlg-steuerung*)) (set_tile "hoehe" (rtos *kf-dlg-hoehe* 2 0)) ;; Merkmal 357: Dropdown mit einer einzigen Option, immer gewaehlt (start_list "antriebsart") (mapcar 'add_list (mapcar '(lambda (v) (cond ((ssg-json-get v "name")) (""))) (ssg-json-get daten "antriebsart"))) (end_list) (set_tile "antriebsart" "0") ;; Die Radiobutton-Gruppe meldet den KEY des gewaehlten Buttons ;; ("v0"/"v1"), nicht dessen Index - daher kf-radio-index. (action_tile "variante" "(setq *kf-dlg-variante* (kf-radio-index $value))") (action_tile "hoehe" "(setq *kf-dlg-hoehe* (atof $value))") (action_tile "verbindung" "(setq *kf-dlg-verbindung* (atoi $value))") (action_tile "farbe" "(setq *kf-dlg-farbe* (atoi $value))") (action_tile "antriebsmotor" "(setq *kf-dlg-antriebsmotor* (atoi $value))") (action_tile "movimot" "(setq *kf-dlg-movimot* (atoi $value))") (action_tile "standard_steuerung" "(setq *kf-dlg-steuerung* (atoi $value))") ;; Zweite Hoehen-Eingabeart: Dialog schliessen, der Aufrufer laesst ;; zwei Punkte waehlen und zeigt ihn danach erneut. (action_tile "hoehe_messen" "(done_dialog 2)") (action_tile "accept" "(done_dialog 1)") (action_tile "cancel" "(done_dialog 0)") (setq idx (start_dialog)) (unload_dialog dcl-id) (vl-file-delete dcl-pfad) idx))))) ;; DCL-Pfad aufloesen: DXFM_DCL, sonst dcl/ unter der Projektwurzel. ;; Gleiches Vorgehen wie geruest-dcl-pfad (GeruestInsert.lsp) - bewusst eine ;; eigene kurze Funktion statt eines Zugriffs quer ins Geruest-Modul, damit ;; dieses Modul ohne GeruestInsert ladbar bleibt. (defun kf-dcl-pfad (datei / basis) (setq basis (cond ((getenv "DXFM_DCL")) ((strcat (getenv "DXFMAKRO") "/dcl")))) (if (and basis (/= basis "")) (findfile (strcat basis "/" datei)) nil)) ;; ============================================================ ;; KONSOLEN-RUECKFALL (ohne GUI, fuer automatische Tests) ;; ============================================================ ;; Eine Auswahlliste auf der Konsole abfragen. Rueckgabe: 0-basierter Index ;; (bei ungueltiger Eingabe bleibt die Vorgabe stehen). Alle vier Auswahlen ;; laufen hierueber - vorher stand derselbe Block viermal untereinander. (defun kf-konsole-wahl (liste header-key prompt-key vorgabe-idx / i v antwort) (princ (ssg-text header-key)) (setq i 1) (foreach v liste (princ (ssg-textf "kf-konsole-option" (list (itoa i) (ssg-json-get v "name")))) (setq i (1+ i))) (setq antwort (ssg-in-int (ssg-text prompt-key) (1+ vorgabe-idx))) (if (or (null antwort) (< antwort 1) (> antwort (length liste))) vorgabe-idx (1- antwort))) (defun kf-konsole (daten / varianten verbindungen farben motoren movimots steuerungen hoehe) (setq varianten (ssg-json-get daten "varianten") verbindungen (ssg-json-get daten "verbindungselemente") farben (ssg-json-get daten "farbe") motoren (ssg-json-get daten "antriebsmotor") movimots (ssg-json-get daten "movimot") steuerungen (ssg-json-get daten "standard_steuerung")) (setq *kf-dlg-variante* (kf-konsole-wahl varianten "kf-konsole-variante-header" "kf-konsole-variante-prompt" *kf-dlg-variante*)) ;; Hoehe: Zahl tippen ODER 0 eingeben und zwei Punkte messen (zweite ;; Eingabeart, entspricht dem Knopf im Dialog). (princ (ssg-text "kf-konsole-hoehe-hinweis")) (setq hoehe (ssg-in-real (ssg-text "kf-konsole-hoehe-prompt") (cond (*kf-dlg-hoehe*) (1500.0)))) (if (and hoehe (<= hoehe 0.0)) (setq hoehe (kf-hoehe-aus-punkten))) (if (null hoehe) (setq hoehe (cond (*kf-dlg-hoehe*) (1500.0)))) (setq *kf-dlg-hoehe* hoehe) ;; Reihenfolge wie im Dialog und im Sivas-Merkmalstamm: 361 vor 354. (setq *kf-dlg-steuerung* (kf-konsole-wahl steuerungen "kf-konsole-steuerung-header" "kf-konsole-steuerung-prompt" *kf-dlg-steuerung*)) (setq *kf-dlg-farbe* (kf-konsole-wahl farben "kf-konsole-farbe-header" "kf-konsole-farbe-prompt" *kf-dlg-farbe*)) (setq *kf-dlg-antriebsmotor* (kf-konsole-wahl motoren "kf-konsole-antriebsmotor-header" "kf-konsole-antriebsmotor-prompt" *kf-dlg-antriebsmotor*)) (setq *kf-dlg-movimot* (kf-konsole-wahl movimots "kf-konsole-movimot-header" "kf-konsole-movimot-prompt" *kf-dlg-movimot*)) (setq *kf-dlg-verbindung* (kf-konsole-wahl verbindungen "kf-konsole-verbindung-header" "kf-konsole-verbindung-prompt" *kf-dlg-verbindung*)) (list (nth *kf-dlg-variante* varianten) hoehe (kf-auswahl-wert verbindungen *kf-dlg-verbindung* "Nein") (kf-auswahl-wert farben *kf-dlg-farbe* "RAL 7035") (kf-auswahl-wert motoren *kf-dlg-antriebsmotor* "Europa 50 Hz") (kf-auswahl-wert movimots *kf-dlg-movimot* "0") (kf-auswahl-wert steuerungen *kf-dlg-steuerung* "0"))) ;; ============================================================ ;; C:KETTENFOERDERER - Hauptbefehl ;; ============================================================ (defun c:KETTENFOERDERER ( / daten eingabe variante hoehe verbindung farbe antriebsmotor movimot steuerung geo erg laenge skala endpunkt lmin lmax startpunkt drehung last-ent kf-nummer ent t0) ;; Laufzeit des Dialogaufbaus protokollieren (Schalter "kf-zeit", siehe ;; ssg_dbg.lsp). Hintergrund: der Dialog erschien beim ersten Menueklick ;; spuerbar verzoegert. Statt zu raten, wird die Zeit bis zum Dialog ;; gemessen - so laesst sich unterscheiden, ob das Lazy-Laden des Moduls, ;; das Lesen der JSON oder der Dialogaufbau selbst die Zeit kostet. (setq t0 (getvar "MILLISECS")) (setq daten (kf-daten)) (kf-zeit "Daten geladen" t0) (if (null daten) (princ (ssg-text "kf-fehler-daten-leer")) (progn (setq lmin (kf-zahl (ssg-json-get (ssg-json-get daten "laenge") "min")) lmax (kf-zahl (ssg-json-get (ssg-json-get daten "laenge") "max"))) (kf-zeit "vor Dialog" t0) (setq eingabe (if (ssg-gui-p) (kf-dialog daten) (kf-konsole daten))) (if (null eingabe) (princ (ssg-text "kf-abgebrochen")) (progn (setq variante (nth 0 eingabe) hoehe (nth 1 eingabe) verbindung (nth 2 eingabe) farbe (nth 3 eingabe) antriebsmotor (nth 4 eingabe) movimot (nth 5 eingabe) steuerung (nth 6 eingabe)) (setq geo (kf-geometrie-messen variante)) (setq erg (kf-laenge-fuer-hoehe geo hoehe lmin lmax)) (if (caddr erg) (progn (if (ssg-gui-p) (alert (ssg-text (caddr erg)))) (princ (ssg-text (caddr erg)))) (progn (setq laenge (car erg) skala (cadr erg)) (setq startpunkt (getpoint (ssg-text "kf-prompt-startpunkt"))) (if (null startpunkt) (princ (ssg-text "kf-abgebrochen")) (progn (setq drehung (getangle startpunkt (ssg-text "kf-prompt-drehung"))) (setq drehung (if drehung (/ (* 180.0 drehung) pi) 0.0)) (ssg-start "Kettenfoerderer" '(("OSMODE") ("ATTREQ") ("ATTDIA"))) (setvar "OSMODE" 0) (setvar "ATTREQ" 0) (setvar "ATTDIA" 0) (setq last-ent (ssg-lastent-ohne-attribute)) (setq kf-nummer (kf-naechste-nummer)) ;; Kette und Draufsicht liegen DECKUNGSGLEICH - beide ;; beginnen am gewaehlten Punkt. Die Draufsicht gehoert mit ;; in dieselbe Baugruppe, darum VOR kf-block-erstellen. (setq endpunkt (kf-kette-bauen variante geo skala startpunkt drehung)) (kf-ansicht-2d-bauen daten startpunkt endpunkt drehung) (setq ent (kf-block-erstellen variante geo hoehe laenge verbindung startpunkt last-ent kf-nummer farbe antriebsmotor movimot steuerung drehung)) (ssg-end) (princ (ssg-textf "kf-fertig" (list (strcat "KF_" (itoa kf-nummer)) (rtos (float hoehe) 2 0) (rtos laenge 2 0)))))))))))) (princ)) ;; ============================================================ ;; C:KF_EDIT - Doppelklick auf einen KF_n-Block ;; ============================================================ ;; Zeigt den Dialog mit den Werten des gewaehlten Blocks und baut ihn nach OK ;; an SEINER Stelle neu auf. Vorbild: GEFAELLESTRECKE_EDIT. ;; ;; Einfuegepunkt und Drehung kommen aus dem Block selbst (Gruppencode 10 und ;; 50), nicht aus XDATA: der Kettenfoerderer hat keine Aufbau-Historie, die ;; ueber diese beiden Werte hinausgeht - alles Uebrige steht in den ;; Attributen. Ein von Hand verschobener oder gedrehter Block wird dadurch ;; korrekt an seiner NEUEN Lage neu gebaut. ;; ;; Der alte Block wird erst geloescht, wenn der Benutzer den Dialog mit OK ;; bestaetigt hat - ein Abbruch laesst die Zeichnung unveraendert. ;; Dialogzustand aus den Attributen eines Blocks vorbelegen. Sucht je ;; Auswahlliste den Index, dessen "wert" zum Attributwert passt; ein ;; unbekannter Wert (von Hand editiert) laesst die Vorgabe stehen, statt den ;; Dialog scheitern zu lassen. (defun kf-index-fuer-wert (liste wert vorgabe / i treffer) (setq i 0 treffer nil) (foreach e liste (if (and (null treffer) wert (equal (ssg-json-get e "wert") wert)) (setq treffer i)) (setq i (1+ i))) (cond (treffer) (vorgabe))) (defun kf-dlg-aus-attributen (daten attribs / w v) (setq w (cdr (assoc "WINKEL" attribs))) (if w (progn (setq v (kf-variante-finden (atof w) (ssg-json-get daten "varianten"))) (if v (setq *kf-dlg-variante* (kf-tag-index-liste v (ssg-json-get daten "varianten")))))) (setq w (cdr (assoc "H_EINBAUHOEHE_mm" attribs))) (if (and w (/= w "")) (setq *kf-dlg-hoehe* (atof w))) (setq *kf-dlg-farbe* (kf-index-fuer-wert (ssg-json-get daten "farbe") (cdr (assoc "FARBE" attribs)) *kf-dlg-farbe*)) (setq *kf-dlg-antriebsmotor* (kf-index-fuer-wert (ssg-json-get daten "antriebsmotor") (cdr (assoc "ANTRIEBSMOTOR" attribs)) *kf-dlg-antriebsmotor*)) (setq *kf-dlg-movimot* (kf-index-fuer-wert (ssg-json-get daten "movimot") (cdr (assoc "MOVIMOT" attribs)) *kf-dlg-movimot*)) (setq *kf-dlg-steuerung* (kf-index-fuer-wert (ssg-json-get daten "standard_steuerung") (cdr (assoc "STANDARD_STEUERUNG" attribs)) *kf-dlg-steuerung*)) (setq *kf-dlg-verbindung* (kf-index-fuer-wert (ssg-json-get daten "verbindungselemente") (cdr (assoc "VERBINDUNGSELEMENTE" attribs)) *kf-dlg-verbindung*)) ;; Die Variante wurde eben aus dem Block gesetzt - die Standard-Vorauswahl ;; darf sie nicht mehr ueberschreiben. (setq *kf-dlg-variante-gesetzt* T) (princ)) ;; Position eines Eintrags in einer Liste (0-basiert), 0 wenn nicht gefunden. (defun kf-tag-index-liste (eintrag liste / i treffer) (setq i 0 treffer nil) (foreach e liste (if (and (null treffer) (equal e eintrag)) (setq treffer i)) (setq i (1+ i))) (cond (treffer) (0))) (defun c:KF_EDIT ( / ent ed bname attribs daten eingabe variante hoehe verbindung farbe antriebsmotor movimot steuerung geo erg laenge skala lmin lmax startpunkt drehung last-ent kf-nummer endpunkt) (setq ent (car (entsel (ssg-text "kf-edit-waehlen")))) (if (null ent) (princ (ssg-text "kf-abgebrochen")) (progn (setq ed (entget ent)) (setq bname (cdr (assoc 2 ed))) (if (or (/= (cdr (assoc 0 ed)) "INSERT") (not (wcmatch bname "KF_*"))) (alert (ssg-text "kf-edit-kein-kf")) (progn (setq daten (kf-daten)) (if (null daten) (princ (ssg-text "kf-fehler-daten-leer")) (progn (setq attribs (ssg-attrib-read ent)) (setq startpunkt (cdr (assoc 10 ed))) ;; Der gebaute Winkel steckt in der XDATA, eine nachtraegliche ;; Drehung der Referenz in Gruppencode 50 - beides zaehlt. (setq drehung (kf-drehung-gesamt (kf-xdata-lesen ent) (cdr (assoc 50 ed)))) (kf-dlg-aus-attributen daten attribs) (setq lmin (kf-zahl (ssg-json-get (ssg-json-get daten "laenge") "min")) lmax (kf-zahl (ssg-json-get (ssg-json-get daten "laenge") "max"))) (setq eingabe (if (ssg-gui-p) (kf-dialog daten) (kf-konsole daten))) (if (null eingabe) (princ (ssg-text "kf-abgebrochen")) (progn (setq variante (nth 0 eingabe) hoehe (nth 1 eingabe) verbindung (nth 2 eingabe) farbe (nth 3 eingabe) antriebsmotor (nth 4 eingabe) movimot (nth 5 eingabe) steuerung (nth 6 eingabe)) (setq geo (kf-geometrie-messen variante)) (setq erg (kf-laenge-fuer-hoehe geo hoehe lmin lmax)) (if (caddr erg) (progn (if (ssg-gui-p) (alert (ssg-text (caddr erg)))) (princ (ssg-text (caddr erg)))) (progn (setq laenge (car erg) skala (cadr erg)) (ssg-start "Kettenfoerderer Edit" '(("OSMODE") ("ATTREQ") ("ATTDIA"))) (setvar "OSMODE" 0) (setvar "ATTREQ" 0) (setvar "ATTDIA" 0) ;; Erst jetzt loeschen - ein Abbruch oben laesst die ;; Zeichnung unveraendert. (entdel ent) (setq last-ent (ssg-lastent-ohne-attribute)) (setq kf-nummer (kf-naechste-nummer)) (setq endpunkt (kf-kette-bauen variante geo skala startpunkt drehung)) (kf-ansicht-2d-bauen daten startpunkt endpunkt drehung) (kf-block-erstellen variante geo hoehe laenge verbindung startpunkt last-ent kf-nummer farbe antriebsmotor movimot steuerung drehung) (ssg-end) (princ (ssg-textf "kf-edit-fertig" (list bname (strcat "KF_" (itoa kf-nummer))))))))))))))) (princ)) ;; ============================================================ ;; C:KF_KS_PRUEFEN - Diagnose ;; ============================================================ ;; Liest fuer jede Variante die KS-Lage aller drei Bloecke aus und gibt sie ;; aus: Ursprung und Richtung von KS_EIN/KS_AUS, daraus die abgeleiteten ;; Einbauwinkel und Hoehenbeitraege. Erste Anlaufstelle, wenn eine Kette ;; nicht zusammenpasst - zeigt sofort, ob ein Block kein KS_EIN/KS_AUS hat ;; oder ob seine Achsen anders liegen als erwartet. (defun c:KF_KS_PRUEFEN ( / daten varianten geo b) (setq daten (kf-daten)) (if (null daten) (princ (ssg-text "kf-fehler-daten-leer")) (progn (setq varianten (ssg-json-get daten "varianten")) (princ (ssg-text "kf-pruef-kopf")) (foreach v varianten (princ (ssg-textf "kf-pruef-variante" (list (ssg-json-get v "name")))) (foreach feld '("block_umlenkung" "block_laenge" "block_antrieb") (setq b (ssg-json-get v feld)) (kf-pruef-block b)) (setq geo (kf-geometrie-messen v)) (if (cdr (assoc "fehler" geo)) (princ (ssg-textf "kf-pruef-fehler" (list (ssg-text (cdr (assoc "fehler" geo)))))) (princ (ssg-textf "kf-pruef-geo" (list (rtos (cdr (assoc "hz-um" geo)) 2 2) (rtos (cdr (assoc "hz-l" geo)) 2 2) (rtos (cdr (assoc "hz-an" geo)) 2 2) (rtos (cdr (assoc "dy-um" geo)) 2 2) (rtos (cdr (assoc "dy-an" geo)) 2 2) (rtos (cdr (assoc "dx-l" geo)) 2 2) (rtos (cdr (assoc "dy-l" geo)) 2 2)))))))) (princ)) ;; Einen Block vermessen und protokollieren (Helfer von KF_KS_PRUEFEN). (defun kf-pruef-block (blockname / ks ein aus) (if (null (kf-block-laden blockname)) (princ (ssg-textf "kf-pruef-block-fehlt" (list blockname))) (progn (setq ks (ks-block-messen blockname)) (setq ein (cadr (assoc "KS_EIN" ks)) aus (cadr (assoc "KS_AUS" ks))) (if (not (and ein aus)) (princ (ssg-textf "kf-pruef-ks-fehlt" (list blockname (if ein (ssg-text "kf-pruef-ja") (ssg-text "kf-pruef-nein")) (if aus (ssg-text "kf-pruef-ja") (ssg-text "kf-pruef-nein"))))) (princ (ssg-textf "kf-pruef-block" (list blockname (rtos (car (car ein)) 2 2) (rtos (cadr (car ein)) 2 2) (cond ((rtos (ks-xy-winkel ein) 2 2)) ("-")) (rtos (car (car aus)) 2 2) (rtos (cadr (car aus)) 2 2) (cond ((rtos (ks-xy-winkel aus) 2 2)) ("-"))))))))) (setq *ssg-KettenFoerderer-loaded* T) (princ "\n>>> KettenFoerderer.lsp geladen (KETTENFOERDERER, KF_KS_PRUEFEN)") (princ)