Merge remote-tracking branch 'origin/master'

# Conflicts:
#	CLAUDE.md
This commit is contained in:
2026-10-31 08:32:37 +01:00
31 changed files with 6460 additions and 511 deletions
+10 -4
View File
File diff suppressed because one or more lines are too long
+400 -259
View File
@@ -26,6 +26,14 @@
;; insert-block-by-ks/insert-block-ks-skaliert, die Rz*Ry-Primitiven des ;; insert-block-by-ks/insert-block-ks-skaliert, die Rz*Ry-Primitiven des
;; VarioFoerderers werden hier nicht gebraucht. ;; VarioFoerderers werden hier nicht gebraucht.
;; ;;
;; HAUPT- UND HILFSBLOCK: KF_<n> ist die Seitenansicht (ungedreht, Einfuege-
;; punkt = KS_EIN der Umlenkung) mit Beschriftung und Hoehenmass; nur dieser
;; Block wird exportiert. Die Draufsicht KF_2D ist ein getrennter Hilfsblock
;; KF2D_<n> (zum Anschliessen von Schienen), der aus der projizierten Laenge
;; entsteht, beim ersten Mal 1000 mm unter dem Hauptblock liegt, frei
;; verschiebbar und loeschbar ist und per Doppelklick auf KF_<n> neu erzeugt
;; wird. Beide tragen denselben Schluessel in der XDATA (SSG_KF_EDIT).
;;
;; Die Einbauhoehe H (Abstand KS_EIN der Umlenkung bis KS_AUS des Antriebs auf ;; Die Einbauhoehe H (Abstand KS_EIN der Umlenkung bis KS_AUS des Antriebs auf
;; der Y-Achse) bestimmt die Laenge des Zwischenstuecks ueber die ;; der Y-Achse) bestimmt die Laenge des Zwischenstuecks ueber die
;; Hoehenbilanz, siehe kf-laenge-berechnen. ;; Hoehenbilanz, siehe kf-laenge-berechnen.
@@ -223,7 +231,8 @@
;; STANDARD_STEUERUNG - Merkmal 361, "1"/"0" ueber Transform ja_nein. ;; STANDARD_STEUERUNG - Merkmal 361, "1"/"0" ueber Transform ja_nein.
;; Steht direkt hinter WINKEL, wie im Merkmalstamm. ;; Steht direkt hinter WINKEL, wie im Merkmalstamm.
;; H_EINBAUHOEHE_mm - Merkmal 360 ;; H_EINBAUHOEHE_mm - Merkmal 360
;; L_KF_mm - Laenge des skalierten Zwischenstuecks, CSV-Spalte ;; L_KF_mm - WAAGERECHTE Laenge des ganzen Foerderers (der Draufsicht
;; KF_2D), CSV-Spalte "Laenge"
;; FARBE - Merkmal 354 ;; FARBE - Merkmal 354
;; ANTRIEBSMOTOR - Merkmal 355 (eigenes Merkmal, nicht Teil von 354) ;; ANTRIEBSMOTOR - Merkmal 355 (eigenes Merkmal, nicht Teil von 354)
;; ANTRIEBSART - Merkmal 357, FEST "1.1 kW" (Wert aus der JSON, ;; ANTRIEBSART - Merkmal 357, FEST "1.1 kW" (Wert aus der JSON,
@@ -327,27 +336,6 @@
(if (and (null treffer) (findfile k)) (setq treffer (findfile k)))) (if (and (null treffer) (findfile k)) (setq treffer (findfile k))))
treffer) 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 ;; GEOMETRIE EINER VARIANTE MESSEN
;; ============================================================ ;; ============================================================
@@ -433,156 +421,261 @@
(list laenge skala nil))))))) (list laenge skala nil)))))))
;; ============================================================ ;; ============================================================
;; KETTE BAUEN (aufgerichtet, 3D) ;; KETTE BAUEN (Seitenansicht)
;; ============================================================ ;; ============================================================
;; Fuegt die drei Bausteine ab startpunkt verkettet ein - AUFGERICHTET, die ;; Fuegt die drei Bausteine ab startpunkt verkettet ein. Die Kette ist eine
;; Hoehe laeuft also in Z (siehe kf-matrix-aufrecht). basis-hz dreht die ;; SEITENANSICHT: sie liegt flach in der XY-Ebene (X = Fahrtrichtung,
;; fertige Baugruppe in der Draufsicht. ;; Y = Hoehe), so wie die Bausteine gezeichnet sind. Es wird nichts
;; aufgerichtet und nichts gedreht; jeder Baustein bekommt nur den aus den KS
;; gemessenen Verkettungswinkel (kf-hz-folge).
;; (Zwischenzeitlich wurde die Kette aufgerichtet in Z eingebaut - nach
;; Abstimmung wieder zurueckgenommen; die Draufsicht ist jetzt ein EIGENER
;; Hilfsblock, siehe kf-hilfsblock-bauen.)
;; Rueckgabe: Weltpunkt des KS_AUS der Antriebsstation (Kettenende). ;; Rueckgabe: Weltpunkt des KS_AUS der Antriebsstation (Kettenende).
(defun kf-kette-bauen (variante geo skala startpunkt basis-hz / pt) (defun kf-kette-bauen (variante geo skala startpunkt / pt)
(setq pt startpunkt) (setq pt startpunkt)
(setq pt (insert-block-ks-matrix (setq pt (insert-block-by-ks
(ssg-json-get variante "block_umlenkung") (ssg-json-get variante "block_umlenkung") pt
pt (cdr (assoc "hz-um" geo))))
(kf-matrix-aufrecht basis-hz (cdr (assoc "hz-um" geo))) (setq pt (insert-block-ks-skaliert
1.0)) (ssg-json-get variante "block_laenge") pt
(setq pt (insert-block-ks-matrix (cdr (assoc "hz-l" geo)) skala))
(ssg-json-get variante "block_laenge") (setq pt (insert-block-by-ks
pt (ssg-json-get variante "block_antrieb") pt
(kf-matrix-aufrecht basis-hz (cdr (assoc "hz-l" geo))) (cdr (assoc "hz-an" 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) pt)
;; ------------------------------------------------------------ ;; ------------------------------------------------------------
;; DRAUFSICHT (KF_2D) - deckungsgleich mit der Kette ;; MASSE AUS START- UND ENDPUNKT (reine Rechenkerne)
;; ------------------------------------------------------------ ;; ------------------------------------------------------------
;; KF_2D gehoert in DIESELBE Lage wie die Kette, nicht daneben: sein KS_EIN ;; Horizontalanteil von p1 nach p2, auf die Kettenrichtung (basis-hz, Grad)
;; faellt mit KS_EIN der Umlenkung zusammen, sein KS_AUS mit KS_AUS des ;; projiziert und nicht die blosse X-Differenz - sonst stimmt es bei einem von
;; Antriebs. Dadurch ist die Baugruppe EIN Koerper - von oben sieht man die ;; Hand gedrehten Block nicht. Die Hoehe (Y) geht NICHT ein.
;; Draufsicht, von der Seite die Kette. (defun kf-horiz-projektion (p1 p2 basis-hz / rad dx dy)
;;
;; 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 rad (* (float basis-hz) (/ pi 180.0)))
(setq dx (- (car p2) (car p1)) (setq dx (- (car p2) (car p1))
dy (- (cadr p2) (cadr p1)) dy (- (cadr p2) (cadr p1)))
dz (- (cond ((caddr p2)) (0.0)) (cond ((caddr p1)) (0.0)))) (+ (* dx (cos rad)) (* dy (sin rad))))
;; 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. ;; WAAGERECHTE (projizierte) Laenge der Kette = Laenge der Draufsicht KF_2D.
;; kette-start/kette-ende = KS_EIN der Umlenkung bzw. KS_AUS des Antriebs. ;; Das ist der Wert im Attribut L_KF_mm. Immer positiv.
;; Rueckgabe: die verwendete Schraeglaenge (mm), sonst nil. (defun kf-waagerecht-laenge (p1 p2 basis-hz)
(defun kf-ansicht-2d-bauen (daten kette-start kette-ende basis-hz / (abs (kf-horiz-projektion p1 p2 basis-hz)))
cfg bname masse laenge winkel ks ein-dx skala R)
;; Neigungswinkel der Variante als nackte Ganzzahl ("26"/"30"), bewusst ueber
;; itoa/fix statt rtos: rtos haengt an DIMZIN/LUPREC/LUNITS der Zeichnung. Der
;; Wert steht im Attribut WINKEL und ist Schluessel der Tabelle
;; [kf_typ_anzeige] im Sivas-Export - er muss profilunabhaengig sein.
(defun kf-winkel-text (variante)
(itoa (fix (+ 0.5 (cond ((kf-zahl (ssg-json-get variante "winkel"))) (0.0))))))
;; Punkt um versatz-y in Y verschoben (Z bleibt, fehlendes Z = 0).
(defun kf-versatzpunkt (p versatz-y)
(list (car p) (+ (cadr p) (float versatz-y)) (cond ((caddr p)) (0.0))))
;; Versatz des Hilfsblocks (Draufsicht) gegen den Hauptblock beim ERSTEN
;; Einfuegen, in mm (Y). Steht in der JSON, nicht im Code; Vorgabe -1000.
;; (Pruefung ueber if, nicht ueber and/or: and liefert T statt der Zahl.)
(defun kf-hilfsblock-versatz (daten / cfg wert)
(setq cfg (ssg-json-get daten "ansicht_2d")) (setq cfg (ssg-json-get daten "ansicht_2d"))
(setq bname (if cfg (ssg-json-get cfg "block"))) (setq wert (if cfg (kf-zahl (ssg-json-get cfg "versatz_y"))))
(if (or (null bname) (= bname "")) (cond (wert) (-1000.0)))
nil
;; ------------------------------------------------------------
;; BESCHRIFTUNG UND HOEHENMASS
;; ------------------------------------------------------------
;; Beschriftung wie bei VF_/GF_ (gf-make-label): ein TEXT, Hoehe 100, auf
;; einem eigenen Layer aus cfg/layer.cfg. Haupt- und Hilfsblock tragen
;; dieselbe Zeile: "KF_1 26 Grad-Zeichen H=1500mm L_horizontal=4951mm".
(defun kf-label-text (nummer winkel-str hoehe waagerecht)
(strcat "KF_" (itoa nummer) " " winkel-str (chr 176)
" H=" (itoa (fix (+ 0.5 (float hoehe)))) "mm"
" L_horizontal=" (itoa (fix (+ 0.5 (float waagerecht)))) "mm"))
;; Layer der Beschriftung (Baugruppe "kettenfoerderer_beschriftung" in
;; cfg/layer.cfg), sonst Hartwert - wie bei GF_/VF_.
(defun kf-beschriftungs-layer ( / lay)
(setq lay (ssg-layer-anlegen "kettenfoerderer_beschriftung" nil))
(if (null lay)
(progn (progn
(setq masse (kf-neigung-aus-punkten kette-start kette-ende basis-hz)) (setq lay "KF_Beschriftung")
(if (null masse) (ssg-make-layer lay "7" nil)))
(progn (princ (ssg-text "kf-2d-laenge-null")) nil) lay)
(progn
(setq laenge (car masse) winkel (cadr masse)) (defun kf-label-zeichnen (txt pt lay)
(if (null (kf-block-laden bname)) (entmake
(progn (princ (ssg-textf "kf-fehler-block-datei" (list bname))) nil) (list '(0 . "TEXT")
(progn (cons 10 pt)
(setq ks (ks-block-messen bname)) (cons 11 pt)
(setq ein-dx (car (cond ((ks-versatz ks)) ('(0.0 0.0 0.0))))) '(40 . 100.0)
(setq skala (kf-skala-berechnen laenge ein-dx)) (cons 1 txt)
(if (null skala) '(7 . "Standard")
(progn (princ (ssg-textf "kf-2d-ks-fehlt" (list bname))) nil) (cons 8 lay)
(progn '(72 . 1)
;; Ry(-winkel) hebt die flache Scheibe um die Steigung an; '(73 . 2)))
;; das Vorzeichen folgt der Rechte-Hand-Regel (positives dz (princ))
;; = Drehung gegen den Uhrzeigersinn um die Y-Achse).
(setq R (mat3-mul-mat3 (ks-rot-z basis-hz) ;; Senkrechtes Hoehenmass zwischen p-unten und p-oben, rechts neben dem
(ks-rot-y (- winkel)))) ;; Kettenende. Echtes DIMENSION-Objekt per ActiveX (AddDimRotated, 90 Grad) -
(insert-block-ks-matrix bname kette-start R skala) ;; kein Befehlsaufruf, der bei einem Fehler im Befehl haengen bleiben koennte.
(princ (ssg-textf "kf-2d-gebaut" ;; Jeder Schritt laeuft unter vl-catch-all-apply: scheitert das Mass, geht der
(list bname (rtos laenge 2 0) (rtos winkel 2 1)))) ;; Bau OHNE Mass weiter und meldet es (Hinweis, kein Abbruch). Textgroesse und
laenge))))))))) ;; Pfeile werden gesetzt, damit das Mass neben der 100-mm-Beschriftung lesbar
;; ist; fehlt eine Eigenschaft, bleibt der Wert der Bemassungsvariablen.
(defun kf-hoehenmass-zeichnen (p-unten p-oben lay / obj)
(setq obj (vl-catch-all-apply 'vla-AddDimRotated
(list modelspace
(vlax-3d-point p-unten)
(vlax-3d-point p-oben)
(vlax-3d-point (list (+ (car p-oben) 400.0)
(/ (+ (cadr p-unten) (cadr p-oben)) 2.0)
(caddr p-oben)))
(/ pi 2.0))))
(if (vl-catch-all-error-p obj)
(progn (princ (ssg-text "kf-bemassung-fehlt")) nil)
(progn
(foreach prop (list (list 'vla-put-TextHeight 100.0)
(list 'vla-put-ArrowheadSize 50.0)
(list 'vla-put-PrimaryUnitsPrecision 0)
(list 'vla-put-Layer lay))
(vl-catch-all-apply (car prop) (cons obj (cdr prop))))
T)))
;; Alles seit last-ent Entstandene als Auswahlsatz (fuer _.-BLOCK).
(defun kf-sammeln (last-ent / ss ent)
(setq ss (ssadd))
(setq ent (if last-ent (entnext last-ent) (entnext)))
(while ent
(ssadd ent ss)
(setq ent (entnext ent)))
ss)
;; Referenz um rot-rad (Bogenmass) um ihren Einfuegepunkt drehen. Der Block
;; wird immer ungedreht gebaut (ssg-block-wrap-welt fuegt mit Drehung 0 ein);
;; eine von Hand vorgenommene Drehung wird so beim Neuaufbau wieder aufgesetzt.
(defun kf-rotation-setzen (ent rot-rad / ed)
(if (and ent rot-rad (/= rot-rad 0.0))
(progn
(setq ed (entget ent))
(if (assoc 50 ed)
(entmod (subst (cons 50 rot-rad) (assoc 50 ed) ed)))))
ent)
;; ============================================================ ;; ============================================================
;; DREHWINKEL DES BLOCKS (XDATA) ;; SCHLUESSEL ZWISCHEN HAUPT- UND HILFSBLOCK (XDATA)
;; ============================================================ ;; ============================================================
;; Der Drehwinkel der Baugruppe wird beim Bau in die GEOMETRIE eingerechnet ;; Haupt- (KF_<n>) und Hilfsblock (KF2D_<n>) tragen denselben Schluessel als
;; (kf-matrix-aufrecht), nicht ueber die Drehung der Blockreferenz: ;; XDATA. Der Name taugt dafuer nicht: die Nummer wechselt bei JEDEM Neuaufbau
;; ssg-block-wrap-welt fuegt den fertigen Block immer mit Drehung 0 ein. Die ;; (KF_EDIT vergibt eine neue). Mit dem Schluessel findet KF_EDIT den
;; Referenz traegt damit nirgends mehr, wie die Kette gedreht wurde - KF_EDIT ;; Hilfsblock auch dann, wenn der Benutzer ihn verschoben hat, und erzeugt ihn
;; wuerde sie beim Neuaufbau ungedreht neu bauen. Darum steht der Winkel als ;; an seiner jetzigen Stelle neu (Projektregel: Editierdaten gehoeren in
;; XDATA am Block (Projektregel: Editierdaten gehoeren in XDATA, nicht in ;; XDATA, nicht in .dbg).
;; .dbg). Eine spaetere Drehung der Referenz von Hand kommt ueber
;; Gruppencode 50 hinzu und wird addiert.
(setq *kf-xdata-app* "SSG_KF_EDIT") (setq *kf-xdata-app* "SSG_KF_EDIT")
;; Gesamtdrehung = gebauter Winkel (XDATA, Grad) + Drehung der Referenz (defun kf-neuer-key () (ssg-timestamp))
;; (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) (defun kf-xdata-schreiben (ent key / eintrag)
(regapp *kf-xdata-app*) (regapp *kf-xdata-app*)
(setq eintrag (list *kf-xdata-app* (setq eintrag (list *kf-xdata-app*
(cons 1000 "kf-edit") (cons 1000 "kf-edit")
(cons 1040 (float drehung-grad)))) (cons 1000 key)))
(entmod (append (entget ent) (list (list -3 eintrag)))) (entmod (append (entget ent) (list (list -3 eintrag))))
ent) ent)
;; Gebauter Drehwinkel (Grad) aus der XDATA, nil wenn keine vorhanden ist. ;; Schluessel aus einer entget-Liste mit XDATA (reiner Kern, testbar): das
(defun kf-xdata-lesen (ent / xd daten paar) ;; ZWEITE 1000-Feld; nil bei fehlender oder Altbestands-XDATA (nur Marker).
(setq xd (entget ent (list *kf-xdata-app*))) (defun kf-key-aus-xdata (xd / daten teile)
(setq daten (cdr (assoc -3 xd))) (setq daten (cdr (assoc -3 xd)) teile '())
(if daten (if daten
(foreach p (cdr (car daten))
(if (= (car p) 1000) (setq teile (append teile (list (cdr p)))))))
(if (>= (length teile) 2) (cadr teile) nil))
(defun kf-xdata-key (ent)
(kf-key-aus-xdata (entget ent (list *kf-xdata-app*))))
;; Hilfsblock (KF2D_*) mit diesem Schluessel suchen; nil, wenn es keinen gibt
;; (nie gebaut oder vom Benutzer geloescht).
(defun kf-hilfsblock-finden (key / ss i e treffer)
(setq treffer nil)
(if key
(progn (progn
(setq paar (assoc 1040 (cdr (car daten)))) (regapp *kf-xdata-app*)
(if paar (cdr paar) nil)) (setq ss (ssget "X" (list '(0 . "INSERT") '(2 . "KF2D_*")
nil)) (list -3 (list *kf-xdata-app*)))))
(if ss
(progn
(setq i 0)
(while (and (null treffer) (< i (sslength ss)))
(setq e (ssname ss i))
(if (equal (kf-xdata-key e) key) (setq treffer e))
(setq i (1+ i)))))))
treffer)
;; ============================================================
;; HILFSBLOCK KF2D_<n> (Draufsicht)
;; ============================================================
;; Die Draufsicht KF_2D ist KEIN Teil des Foerderers, sondern ein
;; Hilfsblock, an dem der Benutzer Schienen anschliesst. Darum:
;; - eigener Block KF2D_<n> (nicht KF_*): die Export-Muster erfassen ihn nicht,
;; exportiert wird nur der Hauptblock KF_<n>
;; - flach in der XY-Ebene, auf die PROJIZIERTE Laenge des Foerderers skaliert
;; - unabhaengig verschiebbar; loescht der Benutzer ihn, bleibt der Hauptblock
;; unberuehrt, und KF_EDIT erzeugt ihn bei Bedarf neu
;; - traegt dieselbe Beschriftung wie der Hauptblock
;; Rueckgabe: die Referenz, sonst nil.
(defun kf-hilfsblock-bauen (daten nummer key winkel-str hoehe waagerecht
einfuegepunkt rot-rad /
cfg bname ks ein-dx skala last-ent lay hname ss ins)
(setq cfg (ssg-json-get daten "ansicht_2d"))
(setq bname (if cfg (ssg-json-get cfg "block")))
(cond
((or (null bname) (= bname "")) nil)
((null (kf-block-laden bname))
(princ (ssg-textf "kf-fehler-block-datei" (list bname)))
nil)
(t
(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 waagerecht ein-dx))
(if (null skala)
(progn (princ (ssg-textf "kf-2d-ks-fehlt" (list bname))) nil)
(progn
(setq hname (strcat "KF2D_" (itoa nummer)))
(setq last-ent (ssg-lastent-ohne-attribute))
(insert-block-ks-skaliert bname einfuegepunkt 0.0 skala)
(setq lay (kf-beschriftungs-layer))
(kf-label-zeichnen
(kf-label-text nummer winkel-str hoehe waagerecht)
(list (+ (car einfuegepunkt) (/ waagerecht 2.0))
(+ (cadr einfuegepunkt) 250.0)
(cond ((caddr einfuegepunkt)) (0.0)))
lay)
(setq ss (kf-sammeln last-ent))
(ssg-block-wrap-welt hname einfuegepunkt ss)
(setq ins (entlast))
(ssg-layer-setzen ins (cond ((ssg-json-get daten "layer")) ("kettenfoerderer")))
(kf-rotation-setzen ins rot-rad)
(kf-xdata-schreiben ins key)
(princ (ssg-textf "kf-2d-gebaut" (list hname (rtos waagerecht 2 0))))
ins)))))
;; ============================================================ ;; ============================================================
;; BLOCK ABSCHLIESSEN ;; BLOCK ABSCHLIESSEN
;; ============================================================ ;; ============================================================
;; Sammelt alle seit last-ent erzeugten Objekte, legt die ATTDEFs an, fasst ;; Beschriftung und Hoehenmass zeichnen, alle seit last-ent erzeugten Objekte
;; alles zu KF_<n> zusammen und beschriftet den fertigen Block. ;; sammeln, ATTDEFs anlegen und alles zu KF_<n> zusammenfassen. Vorbild:
;; Vorbild: gf-block-erstellen (Gefaellestrecke.lsp). ;; gf-block-erstellen (Gefaellestrecke.lsp).
(defun kf-block-erstellen (variante geo hoehe laenge verbindung ;; waagerecht = projizierte Laenge (Attribut L_KF_mm), key = Schluessel zum
startpunkt last-ent kf-nummer ;; Hilfsblock, rot-rad = von Hand gesetzte Drehung der Referenz (0 beim ersten
farbe antriebsmotor movimot steuerung drehung / ;; Einfuegen).
kf-bname kf-ss ent kf-insert winkel-str) (defun kf-block-erstellen (variante geo hoehe waagerecht verbindung
startpunkt endpunkt last-ent kf-nummer
farbe antriebsmotor movimot steuerung key rot-rad /
kf-bname kf-ss kf-insert winkel-str lay z)
(setq kf-bname (strcat "KF_" (itoa kf-nummer))) (setq kf-bname (strcat "KF_" (itoa kf-nummer)))
;; Winkel als nackte Ganzzahl ("26"/"30") - bewusst ueber itoa/fix statt (setq winkel-str (kf-winkel-text variante))
;; rtos: rtos haengt an DIMZIN/LUPREC/LUNITS der Zeichnung und liefert je (setq z (cond ((caddr startpunkt)) (0.0)))
;; 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 ;; ATTDEFs (unsichtbar, am Startpunkt) nach dem KF-Schema
(foreach def (kf-attrib-defs) (foreach def (kf-attrib-defs)
(entmake (entmake
@@ -596,18 +689,20 @@
'(70 . 1) '(70 . 1)
'(72 . 0) '(72 . 0)
'(74 . 0)))) '(74 . 0))))
;; Alle Entities seit last-ent einsammeln ;; Beschriftung unter der Kette, Hoehenmass rechts neben dem Kettenende
(setq kf-ss (ssadd)) (setq lay (kf-beschriftungs-layer))
(setq ent (if last-ent (entnext last-ent) (entnext))) (kf-label-zeichnen
(while ent (kf-label-text kf-nummer winkel-str hoehe waagerecht)
(ssadd ent kf-ss) (list (/ (+ (car startpunkt) (car endpunkt)) 2.0) (- (cadr startpunkt) 300.0) z)
(setq ent (entnext ent))) lay)
(kf-hoehenmass-zeichnen (list (car endpunkt) (cadr startpunkt) z)
(list (car endpunkt) (cadr endpunkt) z)
lay)
(setq kf-ss (kf-sammeln last-ent))
;; Block definieren und einfuegen (OSMODE/ELEVATION/BKS werden dabei sauber ;; Block definieren und einfuegen (OSMODE/ELEVATION/BKS werden dabei sauber
;; abgeschaltet, siehe ssg-block-wrap-welt) ;; abgeschaltet, siehe ssg-block-wrap-welt)
(ssg-block-wrap-welt kf-bname startpunkt kf-ss) (ssg-block-wrap-welt kf-bname startpunkt kf-ss)
(setq kf-insert (entlast)) (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-layer-setzen kf-insert (cond ((ssg-json-get variante "layer"))
((ssg-json-get (kf-daten) "layer")) ((ssg-json-get (kf-daten) "layer"))
("kettenfoerderer"))) ("kettenfoerderer")))
@@ -620,7 +715,9 @@
("Schraegfoerderer Stahl"))) ("Schraegfoerderer Stahl")))
(cons "WINKEL" winkel-str) (cons "WINKEL" winkel-str)
(cons "H_EINBAUHOEHE_mm" (itoa (fix (+ 0.5 (float hoehe))))) (cons "H_EINBAUHOEHE_mm" (itoa (fix (+ 0.5 (float hoehe)))))
(cons "L_KF_mm" (itoa (fix (+ 0.5 (float laenge))))) ;; WAAGERECHTE (projizierte) Laenge des ganzen Foerderers, nicht die
;; Laenge des schraegen Mittelstuecks
(cons "L_KF_mm" (itoa (fix (+ 0.5 (float waagerecht)))))
(cons "FARBE" (cond (farbe) ("RAL 7035"))) (cons "FARBE" (cond (farbe) ("RAL 7035")))
(cons "ANTRIEBSMOTOR" (cond (antriebsmotor) ("Europa 50 Hz"))) (cons "ANTRIEBSMOTOR" (cond (antriebsmotor) ("Europa 50 Hz")))
(cons "ANTRIEBSART" (kf-antriebsart (kf-daten))) (cons "ANTRIEBSART" (kf-antriebsart (kf-daten)))
@@ -629,21 +726,58 @@
(cons "VERBINDUNGSELEMENTE" (cond (verbindung) ("Nein"))) (cons "VERBINDUNGSELEMENTE" (cond (verbindung) ("Nein")))
(cons "ARTINR" (cond ((ssg-json-get variante "sivas_id")) (""))))) (cons "ARTINR" (cond ((ssg-json-get variante "sivas_id")) ("")))))
(ssg-id-generate kf-insert) (ssg-id-generate kf-insert)
;; Reihenfolge: erst Drehung (entmod der Referenz), ZULETZT die XDATA
(kf-rotation-setzen kf-insert rot-rad)
(kf-xdata-schreiben kf-insert key)
kf-insert) kf-insert)
;; Naechste freie KF-Nummer in der Zeichnung. ;; Naechste freie KF-Nummer in der Zeichnung.
(defun kf-naechste-nummer ( / n ss i nm num) ;; Nummer aus einem Blocknamen "KF_<n>" (rein numerisch), sonst nil. Die
;; Bausteine der Kette heissen ebenfalls "KF_...": "KF_2D" (Draufsicht),
;; "KF_UM_26", "KF_LAENGE" duerfen NICHT als Nummer gelesen werden - atoi
;; liefert fuer "2D" naemlich 2 und fuer "UM_26" 0. Reiner Rechenkern.
(defun kf-nummer-aus-name (nm / rest i ok)
(setq nm (strcase nm))
(if (and (> (strlen nm) 3) (= (substr nm 1 3) "KF_"))
(progn
(setq rest (substr nm 4) i 1 ok T)
(while (and ok (<= i (strlen rest)))
(if (not (wcmatch (substr rest i 1) "#")) (setq ok nil))
(setq i (1+ i)))
(if ok (atoi rest) nil))
nil))
;; Erste freie Nummer ab start: ueberspringt jede Nummer, fuer die belegt-p
;; (Symbol einer Ein-Argument-Funktion) T liefert. Reiner Rechenkern, das
;; Praedikat macht die Zeichnungsabfrage austauschbar und damit testbar.
(defun kf-freie-nummer (start belegt-p / n)
(setq n start)
(while (apply belegt-p (list n)) (setq n (1+ n)))
n)
;; Ist die Block-DEFINITION KF_<n> ODER KF2D_<n> in der Zeichnung vorhanden?
(defun kf-nummer-belegt-p (n)
(not (null (or (tblsearch "BLOCK" (strcat "KF_" (itoa n)))
(tblsearch "BLOCK" (strcat "KF2D_" (itoa n)))))))
;; Naechste freie KF-Nummer. Bewusst NICHT nur ueber die Referenzen im Modell:
;; loescht man einen Foerderer (oder baut KF_EDIT ihn neu auf), lebt seine
;; Block-DEFINITION in der Blocktabelle weiter. Wird deren Nummer erneut
;; vergeben, trifft das spaetere _.-BLOCK auf einen vorhandenen Namen - die
;; "Neu definieren?"-Abfrage verschluckt die Folge-Eingaben, der Block entsteht
;; nicht, und die Einzelteile bleiben LOSE im Modell liegen (Fehlerbild "Block
;; aufgeloest"). GF_n und VF_n pruefen aus demselben Grund die Blocktabelle.
(defun kf-naechste-nummer ( / n ss i num)
(setq n 0) (setq n 0)
(setq ss (ssget "X" '((0 . "INSERT") (2 . "KF_*")))) (setq ss (ssget "X" '((0 . "INSERT") (2 . "KF_*"))))
(if ss (if ss
(progn (progn
(setq i 0) (setq i 0)
(while (< i (sslength ss)) (while (< i (sslength ss))
(setq nm (cdr (assoc 2 (entget (ssname ss i))))) (setq num (kf-nummer-aus-name (cdr (assoc 2 (entget (ssname ss i))))))
(setq num (atoi (substr nm 4))) (if (and num (> num n)) (setq n num))
(if (> num n) (setq n num))
(setq i (1+ i))))) (setq i (1+ i)))))
(1+ n)) (kf-freie-nummer (1+ n) 'kf-nummer-belegt-p))
;; ============================================================ ;; ============================================================
;; DIALOG ;; DIALOG
@@ -723,8 +857,8 @@
;; zwei Punkte in der Zeichnung gewaehlt (z.B. Oberkante Zufuehrung und ;; zwei Punkte in der Zeichnung gewaehlt (z.B. Oberkante Zufuehrung und
;; Oberkante Abgabe) und ihr Hoehenunterschied uebernommen. ;; Oberkante Abgabe) und ihr Hoehenunterschied uebernommen.
;; ;;
;; Gemessen wird die Differenz in Z: die Baugruppe steht aufgerichtet im Modell ;; Gemessen wird die Differenz in Z (die Hoehe der Punkte in der Zeichnung); die
;; (siehe kf-matrix-aufrecht), ihre Hoehe ist also die Hoehe der Zeichnung. ;; Differenz in Y wird nur mit ausgegeben.
;; Damit nachvollziehbar bleibt, was gemessen wurde, nennt die Meldung BEIDE ;; Damit nachvollziehbar bleibt, was gemessen wurde, nennt die Meldung BEIDE
;; Differenzen (dZ und dY): wer in einer DRAUFSICHT pickt, sieht sofort, dass ;; Differenzen (dZ und dY): wer in einer DRAUFSICHT pickt, sieht sofort, dass
;; dort dZ = 0 ist, und kann es melden. ;; dort dZ = 0 ist, und kann es melden.
@@ -948,72 +1082,91 @@
(kf-auswahl-wert movimots *kf-dlg-movimot* "0") (kf-auswahl-wert movimots *kf-dlg-movimot* "0")
(kf-auswahl-wert steuerungen *kf-dlg-steuerung* "0"))) (kf-auswahl-wert steuerungen *kf-dlg-steuerung* "0")))
;; ============================================================
;; KF_BAUEN - gemeinsamer Bau fuer Befehl und Edit
;; ============================================================
;; Baut Hauptblock KF_<n> (Seitenansicht mit Beschriftung + Hoehenmass) und
;; danach den Hilfsblock KF2D_<n> (Draufsicht). Beide Bloecke werden ungedreht
;; gebaut; eine Drehung (Edit eines von Hand gedrehten Blocks) setzt
;; kf-rotation-setzen danach wieder auf.
;; eingabe = Ergebnis von kf-dialog/kf-konsole (Variante, Hoehe, ...)
;; startpunkt = KS_EIN der Umlenkstation (Einfuegepunkt Hauptblock)
;; rot-rad = Drehung des Hauptblocks, hilfs-pt/hilfs-rot = Lage des Hilfsblocks
;; key = Schluessel Haupt <-> Hilfsblock (nil -> neuer)
;; Rueckgabe: Liste (Blockname Hoehe Laenge) bei Erfolg, sonst nil (die
;; Fehlermeldung ist dann schon ausgegeben, die Zeichnung unveraendert).
(defun kf-bauen (daten eingabe startpunkt rot-rad hilfs-pt hilfs-rot key
loeschen /
variante hoehe verbindung farbe antriebsmotor movimot steuerung
geo erg laenge skala lmin lmax endpunkt waagerecht
last-ent kf-nummer winkel-str)
(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 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 geo (kf-geometrie-messen variante))
(setq erg (kf-laenge-fuer-hoehe geo hoehe lmin lmax))
(cond
((caddr erg)
(if (ssg-gui-p) (alert (ssg-text (caddr erg))))
(princ (ssg-text (caddr erg)))
nil)
(t
(setq laenge (car erg) skala (cadr erg))
(ssg-start "Kettenfoerderer" '(("OSMODE") ("ATTREQ") ("ATTDIA")))
(setvar "OSMODE" 0)
(setvar "ATTREQ" 0)
(setvar "ATTDIA" 0)
;; Edit: alte Bloecke (Liste von Enames) erst jetzt loeschen
(foreach e loeschen (if e (entdel e)))
(if (null key) (setq key (kf-neuer-key)))
(setq last-ent (ssg-lastent-ohne-attribute))
(setq kf-nummer (kf-naechste-nummer))
(setq winkel-str (kf-winkel-text variante))
(setq endpunkt (kf-kette-bauen variante geo skala startpunkt))
(setq waagerecht (kf-waagerecht-laenge startpunkt endpunkt 0.0))
(kf-block-erstellen variante geo hoehe waagerecht verbindung
startpunkt endpunkt last-ent kf-nummer
farbe antriebsmotor movimot steuerung key rot-rad)
(kf-hilfsblock-bauen daten kf-nummer key winkel-str hoehe waagerecht
hilfs-pt hilfs-rot)
(ssg-end)
(list (strcat "KF_" (itoa kf-nummer)) hoehe laenge))))
;; ============================================================ ;; ============================================================
;; C:KETTENFOERDERER - Hauptbefehl ;; C:KETTENFOERDERER - Hauptbefehl
;; ============================================================ ;; ============================================================
(defun c:KETTENFOERDERER ( / daten eingabe variante hoehe verbindung (defun c:KETTENFOERDERER ( / daten eingabe startpunkt erg t0)
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 ;; Laufzeit des Dialogaufbaus protokollieren (Schalter "kf-zeit", siehe
;; ssg_dbg.lsp). Hintergrund: der Dialog erschien beim ersten Menueklick ;; ssg_dbg.lsp): unterscheidet Lazy-Laden, JSON-Lesen und Dialogaufbau.
;; 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 t0 (getvar "MILLISECS"))
(setq daten (kf-daten)) (setq daten (kf-daten))
(kf-zeit "Daten geladen" t0) (kf-zeit "Daten geladen" t0)
(if (null daten) (if (null daten)
(princ (ssg-text "kf-fehler-daten-leer")) (princ (ssg-text "kf-fehler-daten-leer"))
(progn (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) (kf-zeit "vor Dialog" t0)
(setq eingabe (if (ssg-gui-p) (kf-dialog daten) (kf-konsole daten))) (setq eingabe (if (ssg-gui-p) (kf-dialog daten) (kf-konsole daten)))
(if (null eingabe) (if (null eingabe)
(princ (ssg-text "kf-abgebrochen")) (princ (ssg-text "kf-abgebrochen"))
(progn (progn
(setq variante (nth 0 eingabe) (setq startpunkt (getpoint (ssg-text "kf-prompt-startpunkt")))
hoehe (nth 1 eingabe) (if (null startpunkt)
verbindung (nth 2 eingabe) (princ (ssg-text "kf-abgebrochen"))
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 (progn
(if (ssg-gui-p) (alert (ssg-text (caddr erg)))) ;; Hilfsblock beim ersten Mal 1000 mm unter dem Hauptblock
(princ (ssg-text (caddr erg)))) (setq erg (kf-bauen daten eingabe startpunkt 0.0
(progn (kf-versatzpunkt startpunkt (kf-hilfsblock-versatz daten))
(setq laenge (car erg) skala (cadr erg)) 0.0 nil nil))
(setq startpunkt (getpoint (ssg-text "kf-prompt-startpunkt"))) (if erg
(if (null startpunkt) (princ (ssg-textf "kf-fertig"
(princ (ssg-text "kf-abgebrochen")) (list (car erg) (rtos (float (cadr erg)) 2 0)
(progn (rtos (caddr erg) 2 0)))))))))))
(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)) (princ))
;; ============================================================ ;; ============================================================
@@ -1023,10 +1176,11 @@
;; an SEINER Stelle neu auf. Vorbild: GEFAELLESTRECKE_EDIT. ;; an SEINER Stelle neu auf. Vorbild: GEFAELLESTRECKE_EDIT.
;; ;;
;; Einfuegepunkt und Drehung kommen aus dem Block selbst (Gruppencode 10 und ;; Einfuegepunkt und Drehung kommen aus dem Block selbst (Gruppencode 10 und
;; 50), nicht aus XDATA: der Kettenfoerderer hat keine Aufbau-Historie, die ;; 50), alles Uebrige steht in den Attributen. Ein von Hand verschobener oder
;; ueber diese beiden Werte hinausgeht - alles Uebrige steht in den ;; gedrehter Block wird dadurch an seiner NEUEN Lage neu gebaut. Die XDATA
;; Attributen. Ein von Hand verschobener oder gedrehter Block wird dadurch ;; traegt nur den Schluessel zum Hilfsblock KF2D_<n>: er wird mit dem Hauptblock
;; korrekt an seiner NEUEN Lage neu gebaut. ;; neu erzeugt (auch wenn der Benutzer ihn geloescht hatte) und behaelt die
;; Lage, an die der Benutzer ihn zuletzt gelegt hat.
;; ;;
;; Der alte Block wird erst geloescht, wenn der Benutzer den Dialog mit OK ;; Der alte Block wird erst geloescht, wenn der Benutzer den Dialog mit OK
;; bestaetigt hat - ein Abbruch laesst die Zeichnung unveraendert. ;; bestaetigt hat - ein Abbruch laesst die Zeichnung unveraendert.
@@ -1082,70 +1236,57 @@
(setq i (1+ i))) (setq i (1+ i)))
(cond (treffer) (0))) (cond (treffer) (0)))
(defun c:KF_EDIT ( / ent ed bname attribs daten eingabe variante hoehe (defun c:KF_EDIT ( / ss ent ed bname attribs daten eingabe startpunkt
verbindung farbe antriebsmotor movimot steuerung rot-rad key hilfs hilfs-pt hilfs-rot erg)
geo erg laenge skala lmin lmax startpunkt drehung ;; Erst die VORGEWAEHLTE Auswahl (Doppelklick setzt sie), nur ohne sie per
last-ent kf-nummer endpunkt) ;; Klick nachfragen - wie GEFAELLESTRECKE_EDIT/KreiselEdit.
(setq ent (car (entsel (ssg-text "kf-edit-waehlen")))) (setq ss (ssget "I"))
(setq ent (if (and ss (= (sslength ss) 1))
(ssname ss 0)
(car (entsel (ssg-text "kf-edit-waehlen")))))
(if (null ent) (if (null ent)
(princ (ssg-text "kf-abgebrochen")) (princ (ssg-text "kf-abgebrochen"))
(progn (progn
(setq ed (entget ent)) (setq ed (entget ent))
(setq bname (cdr (assoc 2 ed))) (setq bname (cdr (assoc 2 ed)))
(if (or (/= (cdr (assoc 0 ed)) "INSERT") (not (wcmatch bname "KF_*"))) ;; Nur echte Foerderer "KF_<n>": die Bausteine (KF_UM_26, KF_2D, ...)
;; tragen ebenfalls das Praefix KF_, haben aber keine Attribute.
(if (or (/= (cdr (assoc 0 ed)) "INSERT") (null (kf-nummer-aus-name bname)))
(alert (ssg-text "kf-edit-kein-kf")) (alert (ssg-text "kf-edit-kein-kf"))
(progn (progn
(setq daten (kf-daten)) (setq daten (kf-daten))
(if (null daten) (if (null daten)
(princ (ssg-text "kf-fehler-daten-leer")) (princ (ssg-text "kf-fehler-daten-leer"))
(progn (progn
(setq attribs (ssg-attrib-read ent)) (setq attribs (ssg-attrib-read ent))
(setq startpunkt (cdr (assoc 10 ed))) (setq startpunkt (cdr (assoc 10 ed)))
;; Der gebaute Winkel steckt in der XDATA, eine nachtraegliche (setq rot-rad (cond ((cdr (assoc 50 ed))) (0.0)))
;; Drehung der Referenz in Gruppencode 50 - beides zaehlt. ;; Schluessel zum Hilfsblock; Altbestand ohne Schluessel -> nil,
(setq drehung (kf-drehung-gesamt (kf-xdata-lesen ent) ;; dann gibt es keinen auffindbaren Hilfsblock und ein neuer
(cdr (assoc 50 ed)))) ;; entsteht an der Standardposition.
(setq key (kf-xdata-key ent))
(setq hilfs (kf-hilfsblock-finden key))
(if hilfs
(setq hilfs-pt (cdr (assoc 10 (entget hilfs)))
hilfs-rot (cond ((cdr (assoc 50 (entget hilfs)))) (0.0)))
(setq hilfs-pt (kf-versatzpunkt startpunkt
(kf-hilfsblock-versatz daten))
hilfs-rot 0.0))
(kf-dlg-aus-attributen daten attribs) (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))) (setq eingabe (if (ssg-gui-p) (kf-dialog daten) (kf-konsole daten)))
(if (null eingabe) (if (null eingabe)
(princ (ssg-text "kf-abgebrochen")) (princ (ssg-text "kf-abgebrochen"))
(progn (progn
(setq variante (nth 0 eingabe) ;; Der Hilfsblock wird bei JEDEM Edit neu erzeugt, damit er
hoehe (nth 1 eingabe) ;; zur neuen Hoehe/Laenge passt - an der Stelle, an der der
verbindung (nth 2 eingabe) ;; Benutzer ihn zuletzt hingelegt hat.
farbe (nth 3 eingabe) (setq erg
antriebsmotor (nth 4 eingabe) (kf-bauen daten eingabe startpunkt rot-rad hilfs-pt hilfs-rot
movimot (nth 5 eingabe) (cond (key) (kf-neuer-key))
steuerung (nth 6 eingabe)) (list ent hilfs)))
(setq geo (kf-geometrie-messen variante)) (if erg
(setq erg (kf-laenge-fuer-hoehe geo hoehe lmin lmax)) (princ (ssg-textf "kf-edit-fertig"
(if (caddr erg) (list bname (car 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)) (princ))
;; ============================================================ ;; ============================================================
+2598
View File
File diff suppressed because it is too large Load Diff
+234
View File
@@ -0,0 +1,234 @@
;; ============================================================
;; LagerPruefung.lsp - Regelprofile der Lagerplanung (Phase 0)
;;
;; Laedt das Laenderprofil cfg/regelwerk/<LAND>.json, loest die Vererbung
;; (erbt_von) auf und liefert je Regel den STRENGSTEN Wert aller Quellen,
;; deren Bedingung zutrifft - mit Protokoll, welche Quelle gewonnen hat.
;; Jede Pruefregel der Lagerplanung wird genau einmal hier implementiert
;; (Python rendert nur, siehe doc/plan/lager_rfq.md).
;;
;; Spaetere Phasen (Treppen-/Leiterpruefung, Fluchtweg, Verstoss-Marker,
;; LAGER_PRUEFEN) kommen in dieses Modul.
;;
;; Profil-Format (Auszug):
;; {"land": "DE", "erbt_von": null, "gepflegt": true,
;; "regeln": [{"id": "treppe.steigung_max", "art": "max", "einheit": "mm",
;; "werte": [{"wert": 190, "quelle": "ASR A1.8", "abschnitt": "4.5",
;; "fassung": "...", "bedingung": "...", "verifiziert": false}]}]}
;; art = "min" -> Mindestwert, strengster = groesster
;; "max" -> Hoechstwert, strengster = kleinster
;; "wert" -> fester Wert, der erste zutreffende gilt
;; Ein Eintrag mit "tabelle" [[bis, wert] ...] + "tabellen_schluessel" liefert
;; den Wert der ersten Zeile, deren "bis" den Kontextwert abdeckt.
;; "bedingung": "<schluessel> <op> <wert>", op = = <= >= < >. Fehlt der
;; Schluessel im Kontext, wird der Eintrag KONSERVATIV mit einbezogen
;; (Protokoll: "Bedingung nicht auswertbar").
;; ============================================================
(if (not (boundp '*lg-profil-cache*)) (setq *lg-profil-cache* nil))
;; ------------------------------------------------------------
;; Profil laden
;; ------------------------------------------------------------
(defun lg-regelwerk-pfad (land / cfg)
(setq cfg (getenv "DXFM_CFG"))
(if cfg
(strcat (vl-string-right-trim "/\\" (vl-string-translate "\\" "/" cfg))
"/regelwerk/" (strcase land) ".json"))
)
;; Zwei Profile mischen: Regeln von kind ersetzen gleichnamige (id) von
;; basis vollstaendig, neue kommen hinzu. Kopf-Felder (land, gepflegt ...)
;; kommen vom Kind. Reine Funktion.
(defun lg-profil-mischen (basis kind / regeln r id alt)
(setq regeln (ssg-json-get basis "regeln"))
(foreach r (ssg-json-get kind "regeln")
(setq id (ssg-json-get r "id"))
(if (setq alt (vl-some '(lambda (x) (if (= (ssg-json-get x "id") id) x)) regeln))
(setq regeln (subst r alt regeln))
(setq regeln (append regeln (list r))))
)
(cons (cons "regeln" regeln)
(vl-remove-if '(lambda (p) (= (car p) "regeln")) kind))
)
;; Profil inkl. Vererbung laden (rekursiv, Zirkelschutz ueber besucht).
;; Rueckgabe: gemischtes Profil (Alist) oder nil.
(defun lg-profil-laden-roh (land besucht / pfad daten basis-land basis)
(setq pfad (lg-regelwerk-pfad land))
(if (and pfad (findfile pfad)
(not (member (strcase land) besucht)))
(progn
(setq daten (ssg-json-laden pfad))
(setq basis-land (ssg-json-get daten "erbt_von"))
(if (and basis-land (= (type basis-land) 'STR) (/= basis-land ""))
(progn
(setq basis (lg-profil-laden-roh basis-land (cons (strcase land) besucht)))
(if basis (lg-profil-mischen basis daten) daten))
daten)
)
)
)
;; Gecachtes Profil des Landes. Meldet ein nicht gepflegtes Profil einmalig.
(defun lg-profil (land / eintrag profil)
(if (null land) (setq land "DE"))
(setq eintrag (assoc (strcase land) *lg-profil-cache*))
(if eintrag
(cdr eintrag)
(progn
(setq profil (lg-profil-laden-roh land nil))
(if (null profil)
(princ (ssg-textf "lg-regel-profil-fehlt" (list land (lg-regelwerk-pfad land))))
(if (not (ssg-json-get profil "gepflegt"))
(princ (ssg-textf "lg-regel-profil-ungepflegt" (list land)))))
(setq *lg-profil-cache* (cons (cons (strcase land) profil) *lg-profil-cache*))
profil
)
)
)
(defun lg-profil-reset ( / ) (setq *lg-profil-cache* nil) (princ))
(defun lg-regel-finden (profil id)
(vl-some '(lambda (r) (if (= (ssg-json-get r "id") id) r))
(ssg-json-get profil "regeln"))
)
;; ------------------------------------------------------------
;; Aufloesung (reine Funktionen)
;; ------------------------------------------------------------
;; Kontextwert als Zahl, falls moeglich (Strings wie "3100" werden gewandelt).
(defun lg-kontext-zahl (v)
(cond
((numberp v) (float v))
;; NICHT ((and (= (type v) 'STR) (distof v 2))) - and liefert T statt der Zahl
((= (type v) 'STR) (distof v 2))
(T nil)
)
)
;; Bedingung pruefen. Rueckgabe: 'ja, 'nein oder 'unbekannt (Schluessel fehlt
;; im Kontext oder Bedingung nicht lesbar).
(defun lg-bedingung-pruefen (bedingung kontext / ops op pos key wert ist a b)
(if (or (null bedingung) (= bedingung ""))
'ja
(progn
;; laengere Operatoren zuerst, sonst wird "<=" als "<" gelesen
(setq ops '("<=" ">=" "=" "<" ">") op nil)
(foreach o ops
(if (and (null op) (setq pos (vl-string-search o bedingung)))
(setq op o
key (vl-string-trim " " (substr bedingung 1 pos))
wert (vl-string-trim " " (substr bedingung (+ pos 1 (strlen o)))))))
(cond
((null op) 'unbekannt)
((null (setq ist (cdr (assoc key kontext)))) 'unbekannt)
((= op "=")
(if (and (lg-kontext-zahl ist) (lg-kontext-zahl wert))
(if (equal (lg-kontext-zahl ist) (lg-kontext-zahl wert) 1e-9) 'ja 'nein)
(if (= (strcase (vl-princ-to-string ist)) (strcase wert)) 'ja 'nein)))
((and (setq a (lg-kontext-zahl ist)) (setq b (lg-kontext-zahl wert)))
(if (cond ((= op "<=") (<= a b)) ((= op ">=") (>= a b))
((= op "<") (< a b)) ((= op ">") (> a b)))
'ja 'nein))
(T 'unbekannt)
)
)
)
)
;; Wert eines Eintrags (fester Wert oder Tabellenzeile). nil, wenn der
;; Tabellenschluessel im Kontext fehlt oder ueber der letzten Zeile liegt
;; (dann die letzte Zeile = groesster Tabellenwert, konservativ).
(defun lg-eintrag-wert (eintrag kontext / tab k n erg)
(setq tab (ssg-json-get eintrag "tabelle"))
(if (null tab)
(ssg-json-get eintrag "wert")
(progn
(setq k (ssg-json-get eintrag "tabellen_schluessel")
n (lg-kontext-zahl (cdr (assoc k kontext))))
(if (null n)
;; Schluessel fehlt -> konservativ der groesste Tabellenwert
(apply 'max (mapcar 'cadr tab))
(progn
(setq erg (vl-some '(lambda (z) (if (<= n (car z)) (cadr z))) tab))
(if erg erg (cadr (last tab))))))
)
)
;; Text "Quelle Abschnitt (Fassung)" eines Eintrags fuer das Protokoll.
(defun lg-eintrag-quelle (eintrag / q a f)
(setq q (cond ((ssg-json-get eintrag "quelle")) ("?"))
a (ssg-json-get eintrag "abschnitt")
f (ssg-json-get eintrag "fassung"))
(strcat q (if a (strcat " " a) "") (if f (strcat " (" f ")") ""))
)
;; Regel aufloesen. kontext = Alist (("brandgefaehrdung" . "normal") ...).
;; Rueckgabe: (wert gewinner-eintrag protokoll) oder nil, wenn kein Eintrag
;; zutrifft. protokoll = Liste von Strings (je Kandidat eine Zeile + Ergebnis).
(defun lg-regel-aufloesen (regel kontext / art kandidaten prot status w best)
(setq art (cond ((ssg-json-get regel "art")) ("wert"))
kandidaten nil prot nil)
(foreach e (ssg-json-get regel "werte")
(setq status (lg-bedingung-pruefen (ssg-json-get e "bedingung") kontext)
w (lg-eintrag-wert e kontext))
(cond
((eq status 'nein)
(setq prot (cons (strcat "nicht anwendbar: " (lg-eintrag-quelle e)
" [" (ssg-json-get e "bedingung") "]") prot)))
((null w) nil)
(T
(setq kandidaten (append kandidaten (list (cons w e))))
(setq prot (cons (strcat (vl-princ-to-string w) " " (lg-eintrag-quelle e)
(if (eq status 'unbekannt)
" [Bedingung nicht auswertbar - konservativ einbezogen]"
""))
prot)))
)
)
(if kandidaten
(progn
(setq best (car kandidaten))
(foreach k (cdr kandidaten)
(cond
((and (= art "min") (> (car k) (car best))) (setq best k))
((and (= art "max") (< (car k) (car best))) (setq best k))
)
)
(setq prot (cons (strcat "-> " (vl-princ-to-string (car best))
" (" art ", strengster: " (lg-eintrag-quelle (cdr best)) ")")
prot))
(list (car best) (cdr best) (reverse prot))
)
)
)
;; Bequemer Zugriff: Wert der Regel id im Profil des Landes (nil = unbekannt).
(defun lg-regel-wert (id kontext land / regel erg)
(setq regel (lg-regel-finden (lg-profil land) id))
(if regel
(car (lg-regel-aufloesen regel kontext))
(progn (princ (ssg-textf "lg-regel-unbekannt" (list id))) nil))
)
;; Diagnose: alle Regeln des Landes mit Ergebnis und Protokoll in die Konsole.
(defun c:LG_REGELN ( / land profil kontext erg)
(setq land (if (car (atoms-family 1 '("LG-LAND"))) (lg-land) "DE"))
(setq profil (lg-profil land))
(setq kontext (list (cons "brandgefaehrdung" "erhoeht_ohne_loeschanlage")))
(princ (ssg-textf "lg-regel-liste-kopf" (list land (itoa (length (ssg-json-get profil "regeln"))))))
(foreach r (ssg-json-get profil "regeln")
(setq erg (lg-regel-aufloesen r kontext))
(princ (strcat "\n " (ssg-json-get r "id") " = "
(if erg (vl-princ-to-string (car erg)) "-")
" " (cond ((ssg-json-get r "einheit")) (""))))
(foreach z (caddr erg) (princ (strcat "\n " z)))
)
(princ)
)
(princ)
+45
View File
@@ -867,6 +867,51 @@ Das fertige Raster (Linien + Zaehl-Texte) wird zu einem einzigen Block
--- ---
### `LagerInsert.lsp` / `LagerPruefung.lsp` – Lagerplanung (GOH-Buehnenlager -> RFQ-YAML)
Parametrische Attributbloecke fuer ein mehrgeschossiges Haengewarenlager; die
Mengen gehen ueber `LAGER_EXPORT` als Overlay-YAML an das RFQ-Programm
(`lib/lager_yaml_export.py`). Konzept, Datenmodell und YAML-Zuordnung:
[`doc/plan/lager_rfq.md`](../doc/plan/lager_rfq.md). Stand: Phase 0 + 1
(Mengen, 2D).
| Befehl | Beschreibung |
| --- | --- |
| `c:LG_GESCHOSS` | Belagskontur waehlen/zeichnen, Dialog `lg_geschoss` (`dcl/lager.dcl`), Block `LG_GESCHOSS` + XDATA-Verknuepfung zur Kontur |
| `c:LG_GESCHOSS_AKTIV` | Aktives Geschoss setzen, Layer `LG_*_G<n>` der anderen Geschosse frieren (0 = alle zeigen) |
| `c:LG_GESCHOSS_KOPIEREN` | Geschoss mit Belagskontur, Bereichen, Reihen, Gaengen und goh_racks 1:1 in ein oder mehrere neue Geschosse kopieren (Dialog `lg_geschoss_kopie`: Quelle - Vorgabe oberstes Geschoss -, Anzahl, Geschosshoehe je Kopie, Belag, Versatz X/Y je Kopie, 0 = deckungsgleich). Belaege gleich dem Quell-Geschossbelag folgen dem neuen Belag; Verknuepfungen, Layer `_G<n>` und IDs sind neu |
| `c:LG_BEREICH` | Lagerbereich-Kontur + Dialog `lg_bereich`; Auto-Fuellung mit `LG_REIHE_<L>_<T>` / `LG_GANG_<L>_<B>_<BELAG>`, optional Randgaenge Nord/Ost/Sued/West entlang der Konturkanten (auch L-Form) |
| `c:LG_REIHE` | Einzelne Regalreihen per Start/Ende oder getippter Laenge; wie bei `LG_GANG` mit dem Bereich verknuepft und ausgeschnitten statt neu berechnet (`lg-manuell-einfuegen`) |
| `c:LG_GANG` | Einzelne Gaenge (Dialog `lg_gang`: Breite, Art gang/randgang/quergang, Belag), mittig auf der Linie - oder ueber die Option `Rechteck` als Flaeche, gezeichnet mit dem BricsCAD-Befehl RECHTECK samt aller seiner Optionen (Abmessungen, Flaeche, Drehung ...; `lg-rechteck-zeichnen`, Auswertung `lg-rechteck-ecken-aus-poly`, Fase/Rundung wird abgelehnt) (Lage und Breite aus dem Rechteck, laengere Seite = Gangrichtung; Kern `lg-gang-aus-ecken`, `lg-gang-rechteck-einfuegen`, `Linie` schaltet zurueck); liegt die Mitte in einem Bereich, wird der Gang mit ihm verknuepft. Der Gang schneidet seine Flaeche aus den automatisch erzeugten Reihen/Gaengen heraus (`lg-sperre-anwenden`), OHNE den Bereich neu zu berechnen - von Hand geloeschte oder geaenderte Objekte bleiben, wie sie sind; bei jeder Neuberechnung bleibt er stehen (Sperrflaeche) |
| `c:LG_EDIT` | Bearbeiten (auch per Doppelklick ueber `SSG_BLOCKEDIT`, Zweig `LG_*`): Bereich -> Neuberechnung (von Hand eingefuegte und geaenderte Reihen/Gaenge bleiben stehen und werden ausgespart), Reihe/Gang -> Neuaufbau, danach Zuschnitt der rangniedrigeren Objekte (`auto` < `auto_geaendert` < `manuell`, `lg-quelle-rang`) statt Neuberechnung des Bereichs |
| `c:LG_REGALE` | goh_racks zaehlen (`lg-racks-sammeln`): Anzahl, Laenge und Laenge x Ebenen je Geschoss (Zuordnung wie beim Export) plus Gesamtsumme in der Befehlszeile |
| `c:LAGER_EXPORT` | `<dwg>_lager_raw.json` + Python -> `<dwg>_drawing.yaml` und `<dwg>_nachweis.html` in `DXFM_RESULTS` |
| `c:LAGER_HAND_YAML` | Hand-YAML fuer den Abgleich (Konfliktliste) festlegen |
| `c:LG_REGELN` | Regelprofil (`cfg/regelwerk/<Land>.json`) mit aufgeloesten Werten und Quellen anzeigen |
Gangflaechen sind je Belag schraffiert (Schraffur in der Blockdefinition, Muster aus
`data/json/lager.json` `belag_schraffur`: Spanplatte ANSI31, Gitterrost GRID,
Hallenboden keine).
Reine Kerne (`lg-fuellung-berechnen`, `lg-randgang-streifen`, `lg-kompass`,
`lg-schraffur-vorgabe`, `lg-geschoss-fuer-punkt-kern`, `lg-geschoss-aus-layer`,
`lg-rack-geschoss`, `lg-kopie-*`, `lg-zahl-einheit`, `lg-insert-tf`/`lg-tf-verketten`,
`lg-regel-aufloesen`, `lg-profil-mischen`, JSON-Schreiber, Feldpruefung) in
`tests/test_unit.lsp` (`tu-tests-lager`); Zeichnungsablauf in
`tests/test_lager.lsp` (`TEST_LAGER`, inkl. Rack-Erkennung mit Verschachtelung und
MINSERT sowie goh_rack-Regression gegen `tests/testdata/goh_storage_test.dwg`).
`goh_rack`-Bloecke (parametrischer BricsCAD-Block) erkennt und zaehlt `LagerInsert.lsp`
selbst (`lg-racks-sammeln`): Name ueber die Objekteigenschaft `EffectiveName`,
Parameter `Laenge`/`Ebenen` (`lager.json` `goh_rack`) als MCAD-Eigenschaft, dynamische
Eigenschaft, Attribut oder ATTDEF-Vorgabe, Einheiten auf mm umgerechnet; gefunden auch
in assoziativen Arrays, MINSERT-Rastern und verschachtelten Bloecken. `LAGER_EXPORT`
ordnet jedes goh_rack zuerst ueber den Layer `LG_*_G<n>` seines obersten INSERTs
(`lg-rk-top`), erst dann ueber die Geschosskontur zu. `LG_REGALE` zeigt die Zaehlung
je Geschoss in der Befehlszeile.
---
### `Jam_Edit.lsp` – Staubereiche (SPS) ### `Jam_Edit.lsp` – Staubereiche (SPS)
Staubereiche zeichnet sps_skel (`tro_annotate.py --anlage`) als Block `JAM_AREA` Staubereiche zeichnet sps_skel (`tro_annotate.py --anlage`) als Block `JAM_AREA`
+16
View File
@@ -434,6 +434,16 @@
(command "_.EATTEDIT") (command "_.EATTEDIT")
) )
;; Lager-Bloecke (LG_GESCHOSS, LG_BEREICH, LG_REIHE_*, LG_GANG_*) ->
;; eigener Editor in LagerInsert.lsp. Ebenfalls VOR dem Omniflo-Zweig,
;; aus demselben Grund wie GR_* (Attribut-Bloecke).
((wcmatch (strcase bname) "LG_*")
(ssg-ensure "LagerInsert")
(ssg-start nil nil)
(lg-edit-ent ent)
(ssg-end)
)
;; Kettenfoerderer (KF_n, KettenFoerderer.lsp) -> KF_EDIT (DCL-Dialog mit ;; Kettenfoerderer (KF_n, KettenFoerderer.lsp) -> KF_EDIT (DCL-Dialog mit
;; Neuaufbau an derselben Stelle). MUSS - aus demselben Grund wie GR_* - ;; Neuaufbau an derselben Stelle). MUSS - aus demselben Grund wie GR_* -
;; vor dem Omniflo-Zweig stehen: traegt die Baugruppe ein TEILEART- ;; vor dem Omniflo-Zweig stehen: traegt die Baugruppe ein TEILEART-
@@ -452,6 +462,12 @@
) )
) )
;; Hilfsblock (Draufsicht) des Kettenfoerderers: kein eigener Edit - er wird
;; mit dem Hauptblock KF_<n> neu erzeugt (Doppelklick dort).
((wcmatch bname "KF2D_*")
(princ (ssg-text "cmd-kf2d-hilfsblock"))
)
;; Omniflo-Bloecke -> OMNI_EDIT ;; Omniflo-Bloecke -> OMNI_EDIT
;; Erkennung: TEILEART-Attribut vorhanden, AP110-Blockname oder rein numerisch ;; Erkennung: TEILEART-Attribut vorhanden, AP110-Blockname oder rein numerisch
((progn ((progn
+204
View File
@@ -1629,6 +1629,210 @@
) )
;; ------------------------------------------------------------
;; POLYGON-HELFER (2D, reine Berechnung - ohne Zeichnung testbar)
;; ------------------------------------------------------------
;; Ein Polygon ist eine Liste von Punkten ((x y [z]) ...), implizit
;; geschlossen (der letzte Punkt wird mit dem ersten verbunden; ein
;; doppelter Schlusspunkt stoert nicht). Z wird ignoriert.
;; Genutzt von LagerInsert.lsp (Geschoss-/Bereichskonturen, Auto-Fuellung),
;; bewusst hier, weil nichts davon lagerspezifisch ist.
;; Vorzeichenbehaftete Flaeche (Gauss'sche Trapezformel): > 0 = Umlauf
;; gegen den Uhrzeigersinn, < 0 = im Uhrzeigersinn.
(defun ssg-poly-flaeche-signiert (pts / s p q rest)
(setq s 0.0 rest pts)
(while rest
(setq p (car rest)
q (if (cdr rest) (cadr rest) (car pts)))
(setq s (+ s (- (* (car p) (cadr q)) (* (car q) (cadr p)))))
(setq rest (cdr rest))
)
(/ s 2.0)
)
;; Flaeche, immer >= 0.
(defun ssg-poly-flaeche (pts)
(abs (ssg-poly-flaeche-signiert pts))
)
;; Kanten als Liste ((p1 p2) ...), inklusive Schlusskante. Kanten der
;; Laenge 0 (doppelter Schlusspunkt) fallen weg.
(defun ssg-poly-kanten (pts / erg rest p q)
(setq erg nil rest pts)
(while rest
(setq p (car rest)
q (if (cdr rest) (cadr rest) (car pts)))
(if (> (distance (list (car p) (cadr p)) (list (car q) (cadr q))) 1e-9)
(setq erg (cons (list p q) erg)))
(setq rest (cdr rest))
)
(reverse erg)
)
;; Umfang (Summe aller Kantenlaengen in XY).
(defun ssg-poly-umfang (pts / s)
(setq s 0.0)
(foreach k (ssg-poly-kanten pts)
(setq s (+ s (distance (list (car (car k)) (cadr (car k)))
(list (car (cadr k)) (cadr (cadr k)))))))
s
)
;; Achsparallele Huelle -> ((xmin ymin) (xmax ymax)), nil fuer leere Liste.
(defun ssg-poly-bbox (pts)
(if pts
(list (list (apply 'min (mapcar 'car pts)) (apply 'min (mapcar 'cadr pts)))
(list (apply 'max (mapcar 'car pts)) (apply 'max (mapcar 'cadr pts))))
)
)
;; Punkt im Polygon? (Strahlverfahren, Punkte AUF dem Rand sind nicht
;; eindeutig zugeordnet - fuer Zuordnungen mit Toleranz unkritisch.)
(defun ssg-punkt-in-poly-p (pt pts / innen p q x y)
(setq innen nil x (car pt) y (cadr pt))
(foreach k (ssg-poly-kanten pts)
(setq p (car k) q (cadr k))
(if (and (not (eq (> (cadr p) y) (> (cadr q) y)))
(< x (+ (car p) (/ (* (- (car q) (car p)) (- y (cadr p)))
(- (cadr q) (cadr p))))))
(setq innen (not innen)))
)
innen
)
;; Punkt um zentrum (2D) um winkel (Bogenmass, gegen Uhrzeigersinn) drehen.
;; Ein Z-Wert bleibt erhalten.
(defun ssg-punkt-drehen (pt winkel zentrum / dx dy c s erg)
(setq dx (- (car pt) (car zentrum))
dy (- (cadr pt) (cadr zentrum))
c (cos winkel)
s (sin winkel))
(setq erg (list (+ (car zentrum) (- (* dx c) (* dy s)))
(+ (cadr zentrum) (+ (* dx s) (* dy c)))))
(if (caddr pt) (append erg (list (caddr pt))) erg)
)
(defun ssg-poly-drehen (pts winkel zentrum)
(mapcar '(lambda (p) (ssg-punkt-drehen p winkel zentrum)) pts)
)
;; Innere Abschnitte der waagrechten Geraden y im Polygon.
;; Rueckgabe: aufsteigend sortierte Intervalle ((x1 x2) ...).
;; Halboffene Kantenregel (ymin <= y < ymax): eine Gerade genau durch eine
;; Ecke zaehlt die Kante nur einmal; waagrechte Kanten zaehlen nie.
(defun ssg-poly-schnitt-y (pts y / xs p q erg)
(setq xs nil)
(foreach k (ssg-poly-kanten pts)
(setq p (car k) q (cadr k))
(if (or (and (<= (cadr p) y) (< y (cadr q)))
(and (<= (cadr q) y) (< y (cadr p))))
(setq xs (cons (+ (car p) (/ (* (- (car q) (car p)) (- y (cadr p)))
(- (cadr q) (cadr p))))
xs)))
)
(setq xs (vl-sort xs '<) erg nil)
(while (cadr xs)
(if (> (- (cadr xs) (car xs)) 1e-9)
(setq erg (cons (list (car xs) (cadr xs)) erg)))
(setq xs (cddr xs))
)
(reverse erg)
)
;; Polygon an der Waagrechten y = grenze abschneiden (Sutherland-Hodgman,
;; eine Kante). oben = T behaelt den Teil mit y >= grenze, sonst y <= grenze.
;; Rueckgabe: Punktliste (2D), leer = nichts bleibt.
(defun ssg-poly-clip-y (pts grenze oben / erg rest p q pin qin)
(setq erg nil rest pts)
(while rest
(setq p (car rest)
q (if (cdr rest) (cadr rest) (car pts)))
(setq pin (if oben (>= (cadr p) grenze) (<= (cadr p) grenze))
qin (if oben (>= (cadr q) grenze) (<= (cadr q) grenze)))
(if pin (setq erg (cons (list (car p) (cadr p)) erg)))
(if (not (eq pin qin))
(setq erg (cons (list (+ (car p) (/ (* (- (car q) (car p)) (- grenze (cadr p)))
(- (cadr q) (cadr p))))
grenze)
erg)))
(setq rest (cdr rest))
)
(reverse erg)
)
;; x-Ausdehnung des Polygonteils im Band y0 <= y <= y1: (xmin xmax) oder nil.
;; Ein Polygon, das das Band nur an einer Kante beruehrt (Teil ohne Hoehe),
;; zaehlt nicht - ein Gang direkt neben einer Reihe sperrt sie nicht.
;; Exakt fuer konvexe Polygone (Gang-Rechtecke); bei konkaven die Huelle.
(defun ssg-poly-band-x (pts y0 y1 / c ys xs)
(setq c (ssg-poly-clip-y (ssg-poly-clip-y pts y0 T) y1 nil))
(if c
(progn
(setq ys (mapcar 'cadr c) xs (mapcar 'car c))
(if (and (> (- (apply 'max ys) (apply 'min ys)) 1e-6)
(> (- (apply 'max xs) (apply 'min xs)) 1e-6))
(list (apply 'min xs) (apply 'max xs)))))
)
;; Schnittmenge zweier sortierter Intervall-Listen ((a1 a2) ...).
(defun ssg-intervalle-schnitt (a b / erg lo hi)
(setq erg nil)
(while (and a b)
(setq lo (max (car (car a)) (car (car b)))
hi (min (cadr (car a)) (cadr (car b))))
(if (> hi lo) (setq erg (cons (list lo hi) erg)))
(if (< (cadr (car a)) (cadr (car b)))
(setq a (cdr a))
(setq b (cdr b)))
)
(reverse erg)
)
;; Intervall-Liste a ohne die Intervalle aus b (beide sortiert).
(defun ssg-intervalle-minus (a b / erg iv rest x1 x2 bb)
(setq erg nil)
(foreach iv a
(setq rest (list iv))
(foreach bb b
(setq rest
(apply 'append
(mapcar
'(lambda (r / x1 x2)
(setq x1 (car r) x2 (cadr r))
(cond
((or (<= (cadr bb) x1) (>= (car bb) x2)) (list r))
(T (append
(if (> (car bb) x1) (list (list x1 (car bb))))
(if (< (cadr bb) x2) (list (list (cadr bb) x2)))))))
rest)))
)
(setq erg (append erg rest))
)
erg
)
;; Eckpunkte einer LWPOLYLINE in Weltkoordinaten (Z aus der Erhebung, Code
;; 38). Bogensegmente (Bulge /= 0) werden wie gerade Kanten behandelt; ob
;; welche vorkommen, meldet ssg-lwpoly-boegen-p.
(defun ssg-lwpoly-punkte (ent / ed z nrm erg)
(setq ed (entget ent)
z (cond ((cdr (assoc 38 ed))) (0.0))
nrm (cond ((cdr (assoc 210 ed))) ('(0.0 0.0 1.0))))
(setq erg nil)
(foreach g ed
(if (= (car g) 10)
(setq erg (cons (trans (list (cadr g) (caddr g) z) nrm 0) erg)))
)
(reverse erg)
)
(defun ssg-lwpoly-boegen-p (ent)
(vl-some '(lambda (g) (and (= (car g) 42) (not (equal (cdr g) 0.0 1e-9))))
(entget ent))
)
;; ------------------------------------------------------------ ;; ------------------------------------------------------------
;; DCL ZUR LAUFZEIT GENERIEREN ;; DCL ZUR LAUFZEIT GENERIEREN
;; ------------------------------------------------------------ ;; ------------------------------------------------------------
+16 -4
View File
@@ -85,10 +85,21 @@
;; Realer Fehler, den das behebt: die KS-Messung eines KF-Bausteins lief ins ;; Realer Fehler, den das behebt: die KS-Messung eines KF-Bausteins lief ins
;; Leere und meldete "KS_EIN/KS_AUS fehlt", obwohl beide KS im Block vorhanden ;; Leere und meldete "KS_EIN/KS_AUS fehlt", obwohl beide KS im Block vorhanden
;; waren - gesucht wurde nur ein Blockname, den es nicht gibt. ;; waren - gesucht wurde nur ein Blockname, den es nicht gibt.
;;
;; Zweiter Sonderfall - Namen, die SELBST auf _2D/_3D enden (z.B. die
;; Kettenfoerderer-Draufsicht "KF_2D"): ssg-ils-blockname laesst solche Namen
;; unveraendert (eff = blockname), die Bedingung "eff existiert nicht" kann
;; dann nie zutreffen, und der Block ging in den ILS-Ladepfad. Der suchte
;; data/ils/KF_2D.dwg und meldete "Block-Datei nicht gefunden", obwohl der
;; Block in der Zeichnung vorhanden war. Massgeblich ist darum, ob es fuer den
;; Namen ueberhaupt eine ILS-Datei gibt: ohne Datei ist es kein ILS-Block und
;; die vorhandene Definition wird genommen. Echte ILS-Bloecke haben eine Datei
;; und laufen unveraendert durch den Ladepfad (Aktualisierung aus der Datei).
(defun ensure-block-loaded (blockname / eff) (defun ensure-block-loaded (blockname / eff)
(setq eff (ssg-ils-blockname blockname)) (setq eff (ssg-ils-blockname blockname))
(if (and (not (tblsearch "BLOCK" eff)) (if (and (tblsearch "BLOCK" blockname)
(tblsearch "BLOCK" blockname)) (or (= eff blockname) (not (tblsearch "BLOCK" eff)))
(null (ssg-ils-block-datei blockname)))
blockname blockname
(progn (progn
(if (not (ssg-ils-block-laden blockname)) (if (not (ssg-ils-block-laden blockname))
@@ -558,8 +569,9 @@
;; Bewusst matrixbasiert statt mit einem weiteren Winkelparameter: die ;; Bewusst matrixbasiert statt mit einem weiteren Winkelparameter: die
;; Aufrichtung ist nur EIN Anwendungsfall, und jeder weitere haette eine ;; Aufrichtung ist nur EIN Anwendungsfall, und jeder weitere haette eine
;; weitere Spezialfunktion gebraucht. Den Aufrufern bleibt freigestellt, ;; weitere Spezialfunktion gebraucht. Den Aufrufern bleibt freigestellt,
;; welche Drehungen sie kombinieren (beim Kettenfoerderer ;; welche Drehungen sie kombinieren. (Der Kettenfoerderer hat sie kurz fuer
;; Rz(basis) * Rx(90) * Rz(eigen), siehe kf-matrix-aufrecht). ;; Rz(basis) * Rx(90) * Rz(eigen) genutzt, ist inzwischen aber wieder eine flache
;; Seitenansicht - aktuell ohne Aufrufer.)
;; Elementare Rotationsmatrizen (3x3, Winkel in GRAD). ;; Elementare Rotationsmatrizen (3x3, Winkel in GRAD).
(defun ks-rot-x (grad / r c s) (defun ks-rot-x (grad / r c s)
+4 -1
View File
@@ -45,7 +45,10 @@
(load (strcat base-path "ssg_blocktools.lsp") "ssg_blocktools.lsp nicht gefunden") (load (strcat base-path "ssg_blocktools.lsp") "ssg_blocktools.lsp nicht gefunden")
(load (strcat base-path "ssg_texttools.lsp") "ssg_texttools.lsp nicht gefunden") (load (strcat base-path "ssg_texttools.lsp") "ssg_texttools.lsp nicht gefunden")
(load (strcat base-path "ssg_stueckliste.lsp") "ssg_stueckliste.lsp nicht gefunden") (load (strcat base-path "ssg_stueckliste.lsp") "ssg_stueckliste.lsp nicht gefunden")
(prompt "\nSSG-Bibliotheken geladen: ssg_core, ssg_lang, ssg_dbg, ssg_dialog, ssg_layer, ssg_input, KreiselInsert, VarioFoerderer, Gefaellestrecke, export, OmniModulInsert, SSG_LIB_Commands, ssg_blocktools, ssg_texttools, ssg_stueckliste") ;; Lagerplanung: Regelprofile VOR LagerInsert, das sie nutzt
(load (strcat base-path "LagerPruefung.lsp") "LagerPruefung.lsp nicht gefunden")
(load (strcat base-path "LagerInsert.lsp") "LagerInsert.lsp nicht gefunden")
(prompt "\nSSG-Bibliotheken geladen: ssg_core, ssg_lang, ssg_dbg, ssg_dialog, ssg_layer, ssg_input, KreiselInsert, VarioFoerderer, Gefaellestrecke, export, OmniModulInsert, SSG_LIB_Commands, ssg_blocktools, ssg_texttools, ssg_stueckliste, LagerPruefung, LagerInsert")
(princ) (princ)
) )
+230
View File
@@ -0,0 +1,230 @@
# =====================================================================================
# GOH-Regal-Ausschreibung – Projektdaten (Schema v2)
# =====================================================================================
# Regeln:
# * Feld weglassen -> nicht relevant, erscheint nicht im Dokument
# * Feld = null -> OFFENER PUNKT: erscheint als "to be clarified" (rot, kursiv) und
# zusätzlich in der Tabelle "Open points" am Ende des Dokuments
# * Felder ohne Angabe verwenden die Standardwerte/-texte aus rfq/model.py
# * Zahlen mit Punkt als Dezimaltrennzeichen eingeben (3.10), Ausgabe erfolgt deutsch (3,1)
# * In Freitexten (remarks, scope, ...) sind {floors} und {floors_word} erlaubt
# * "# BSP" = Beispielwert, im Original-Dokument nicht enthalten -> prüfen/ersetzen
# =====================================================================================
# ---------------------------------------------------------------- Anfrage / Dokument
doc:
title: Budget request for steel racking scaffolding/platform
subtitle: Steel structure for a four-level garment-on-hanger (GOH) rack # BSP
request_type: budget # budget | binding
reference: RFQ-GOH-Storage-2026 # z. B. RFQ-2026-017
date: 07.10.2026 # Ausgabedatum, z. B. 07.10.2026
revision: "0"
contact:
name: Max Mustermann
function: Project Engineering # BSP
phone: +49 xxx xxx
email: xxx@schoenenberger.de
# output_name: SSG_RFP_GOH_racking_Projekt-X
# ---------------------------------------------------------------- Projekt
project:
name: goh-slo_pol # Projektname, wie er dem Anbieter genannt werden darf
# project_no: P-12345
# end_customer: ... # weglassen, wenn vertraulich
building_type: a new warehouse
industry: textile logistics
rack_type: static hanging goods rack
additional_purpose: The scaffolding is also used to suspend the conveyor technology.
# ---------------------------------------------------------------- Termine
deadlines:
questions: 10.10.2026 # Frist für technische Rückfragen
offer: End of October 2026 # Angebotsabgabe
# award: Q1/2027
approval_drawings: null # Freigabepläne des Anbieters
delivery: null
assembly_start: Mid of 2027 (expected)
assembly_end: null
# handover: ...
# ---------------------------------------------------------------- Baustelle
site:
location: Slovakia or Poland
address: null
#access_notes: null # Zufahrt, Anlieferung, Entladung, Kranstellfläche
#working_hours: null
hall_available_from: Mid of 2027 # Halle geschlossen/beheizt verfügbar ab
#provided_by_client: # Beistellungen des Auftraggebers
# - Electricity (230/400 V) at one connection point in the hall # BSP
# - Sanitary facilities # BSP
# ---------------------------------------------------------------- Gebäude / Halle
building:
hall_clear_height_m: 12 # Unterkante Dachtragwerk
sprinkler_clearance_m: null # erforderlicher Freiraum unter Deckensprinklern
column_grid: 12 x 24 m # z. B. "12,0 x 24,0 m"
climate: null # z. B. "closed, heated hall, +5 … +30 °C"
corrosivity_category: C1 # BSP – beheizte Halle
floor_slab:
thickness_mm: null
concrete_class: null
permissible_load_kn_m2: null # bisher "Permissible soil load still to be clarified"
permissible_point_load_kn: null
floor_heating: null # true -> Dübel-Einschränkungen
# anchoring_notes: max. drilling depth 120 mm (sealing layer)
# ---------------------------------------------------------------- Erdbeben
seismic:
ag_ms2: 0.63 # Bemessungswert der Bodenbeschleunigung (Original: a_b)
ground_type: null # Baugrundklasse A–E
importance_class: null
code: EN 1998-1 | STN EN 1998-1 # BSP
# ---------------------------------------------------------------- Bemessung / Ausführung
design:
standards: [EN 1990, EN 1991-1-1, EN 1993-1-1, EN 1090-2] # BSP
execution_class: EXC2 # BSP
deflection_general: L/300 # BSP
#deflection_rail_beams: null # wichtig für Trolley-Betrieb, z. B. "L/500, max. 5 mm"
# tolerances: ...
#surface_protection: null # z. B. hot-dip galvanized / powder-coated
# colour: RAL 5010
fire_protection: null # z. B. "Sprinkler system acc. to FM Global"
#max_escape_route_m: null
# extra: [Further requirement ...]
# ---------------------------------------------------------------- Konfiguration / Layout
layout:
block_length_m: null
block_width_m: null
# aisles: 40
# aisle_length_m: 2938.9 # Summe aller Ganglängen je Ebene; sonst aus rack_length_m berechnet
aisle_width_m: 1.00
rack_rows_per_aisle: 2
rack_depth_m: 0.7 # Tiefe einer Regalreihe (Bügelbreite)
trolley_rails_per_aisle: 1
#max_column_spacing_m: null # Vorgabe, falls Gänge/Durchfahrten frei bleiben müssen
storey_height_m: 3.1 # nur Vorgabe, wenn floor_elevation_m fehlt
# column_height_m: 11.85 # sonst berechnet: oberste Belagshöhe + lichte Höhe oben
rail:
type: null # Schienentyp/-profil des Auftraggebers
fixation_spacing_m: 2
supplied_by: client # client | supplier
suspended_by: client # client | supplier
interface: null # z. B. "threaded rod M12 through cross beam"
# ---------------------------------------------------------------- Ebenen (von unten nach oben)
# rack_length_m = Laufmeter Hängeschiene je Hängeebene, SUMME ALLER REIHEN der Ebene.
# rail_height_m = Oberkante Schiene über Belag der Ebene, goods_length_m = max. Warenlänge.
# Treppen/Leitern (stairs_down/ladders_down) führen zur Ebene darunter.
levels:
- remark: ground floor
floor_elevation_m: 0.00
clear_height_m: 2.43 # BSP
hanging_levels:
- {rail_height_m: 2.30, rack_length_m: 5877.8, goods_length_m: 1.20} # Höhen BSP
- {rail_height_m: 1.10, rack_length_m: 5877.8, goods_length_m: 1.10}
- floor_elevation_m: 3.10
clear_height_m: 2.95 # BSP
hanging_levels:
- {rail_height_m: 2.80, rack_length_m: 5877.8, goods_length_m: 1.20}
- {rail_height_m: 1.40, rack_length_m: 5877.8, goods_length_m: 1.20}
walkway: {area_m2: 4286, spec: chipboard}
grating: null # Fläche Gitterrost (Absturzsicherung) noch offen
railing_m: 380
kickplate_m: null
stairs_down: 1 # oder: [{qty: 1, width_m: 1.0, note: straight stair}]
ladders_down: 7
# openings: [{qty: 4, size: "1,2 x 1,0 m", purpose: conveyor passage}]
# pallet_gates: 2
# loads: {platform_imposed_kg_m2: 300, conveyor_kg_m: 40} # überschreibt globale Lasten
- floor_elevation_m: 6.20
clear_height_m: 2.95 # BSP
hanging_levels:
- {rail_height_m: 2.80, rack_length_m: 5877.8, goods_length_m: 1.20}
- {rail_height_m: 1.40, rack_length_m: 5877.8, goods_length_m: 1.20}
walkway: {area_m2: 4286, spec: chipboard}
grating: null
railing_m: 380
kickplate_m: null
stairs_down: 1
ladders_down: 7
- floor_elevation_m: 9.30
clear_height_m: 2.55 # bis Stützenkopf -> Stützenhöhe 11,85 m
hanging_levels:
- {rail_height_m: 2.40, rack_length_m: 5877.8, goods_length_m: 1.10} # BSP
- {rail_height_m: 1.20, rack_length_m: 5877.8, goods_length_m: 1.10}
walkway: {area_m2: 4286, spec: chipboard}
grating: null
railing_m: 380
kickplate_m: null
stairs_down: 1
ladders_down: 7
# ---------------------------------------------------------------- Beläge und Absturzsicherung
flooring:
walkway:
material: chipboard
thickness_mm: null
fire_class: null
grating:
purpose: fall protection within the rack rows
type: null
mesh: null
sprinkler_permeable: null
kickplate_height_mm: 100 # ersetzt das widersprüchliche "skirting boards min. 5 cm"
railing:
height_m: 1.0
knee_rail: true
# ---------------------------------------------------------------- Treppen und Leitern
access:
stairs:
standard: EN ISO 14122-3
width_m: null
handrails: both sides
ladders:
type: emergency ladder
standard: EN ISO 14122-4
cage: null
gate: self-closing safety gate at each ladder opening
# ---------------------------------------------------------------- Lastannahmen (global, je Ebene überschreibbar)
loads:
platform_imposed_kg_m2: 250
imposed_category: null # Nutzungskategorie nach EN 1991-1-1 / NA SK
point_load_kn: null
railing_horizontal_kn_m: null # z. B. 0,5 kN/m
rail_dead_kg_m: 10 # Trolley-Schiene inkl. Abhängung (ständig)
rail_goods_kg_m: 80 # Trolleys und Hängeware (veränderlich)
hanging_per_level_kg_m: 60 # Hängeware je m Hängeschiene je Hängeebene
grating_kg_m2: 100
building_services_kg_m2: 25
conveyor_kg_m: null # abgehängte Fördertechnik (siehe project.additional_purpose)
# conveyor_horizontal_kn: 2
collision_protection: null # Anfahrschutz im EG, falls Flurförderzeuge verkehren
# ---------------------------------------------------------------- Kaufmännisches
commercial:
currency: EUR
incoterms: DAP site (Incoterms 2020) # BSP
payment_terms: null
warranty_months: null
offer_validity: 3 months # BSP
offer_language: English
# ---------------------------------------------------------------- Anlagen
attachments:
- {ref: "1", title: Layout drawing rack block, revision: null} # BSP
- {ref: "2", title: Sections with level heights, revision: null} # BSP
# Optional überschreibbare Standardtexte (siehe rfq/model.py):
# remarks: [...]
# scope: [...]
# exclusions: [...]
# documentation: [...]
# requested_info: [...]
# client: {description: [...]}
+17
View File
@@ -174,6 +174,23 @@ planquadrate = ZZ_Planquadrate, 8
# ZUSAMMENGEBAUTE Baugruppe ohne eigene Blockdatei, der Layer muss darum hier # ZUSAMMENGEBAUTE Baugruppe ohne eigene Blockdatei, der Layer muss darum hier
# stehen. Die drei Bausteine darin behalten ihre eigenen Layer. # stehen. Die drei Bausteine darin behalten ihre eigenen Layer.
kettenfoerderer = Omni_Kettenfoerderer, 4 kettenfoerderer = Omni_Kettenfoerderer, 4
; Beschriftung (KF_n ... H=, L projiziert=) und Hoehenmass, auf Haupt- und Hilfsblock
kettenfoerderer_beschriftung = KF_Beschriftung, 7
# ============================================================
# Lagerplanung (LagerInsert.lsp, Bloecke LG_*)
# ============================================================
# Wert = BASIS-Layername, Farbe. Der tatsaechliche Layer haengt die
# Geschossnummer an ("LG_Reihe" -> "LG_Reihe_G2"), damit LG_GESCHOSS_AKTIV
# die Layer der anderen Geschosse frieren kann - die 2D-Grundrisse der
# Geschosse liegen uebereinander. Gelesen von lg-layer (LagerInsert.lsp).
[lager]
geschoss = LG_Geschoss, 7
bereich = LG_Bereich, 4
reihe = LG_Reihe, 3
gang = LG_Gang, 8
goh_rack = LG_Regal, 3
# ============================================================ # ============================================================
+222
View File
@@ -0,0 +1,222 @@
{
"land": "DE",
"bezeichnung": "Deutschland - ArbStaettV/ASR, DGUV Regel 108-007, EN ISO 14122-3/-4",
"gepflegt": true,
"erbt_von": null,
"hinweis": "Startwerte aus der Planung (doc/plan/lager_rfq.md). verifiziert=false, bis Wert, Abschnitt und Fassung gegen den Originaltext geprueft sind. Die Aufloesung (lg-regel-aufloesen, LagerPruefung.lsp) waehlt je Regel den STRENGSTEN Wert aller Quellen, deren Bedingung zutrifft: art=min -> groesster Wert, art=max -> kleinster Wert.",
"regeln": [
{
"id": "fluchtweg.laenge_max",
"art": "max",
"einheit": "mm",
"messung": "Luftlinie",
"kommentar": "Textil-Haengeware: erhoehte Brandgefaehrdung pruefen. Ohne Angabe gilt die konservative Annahme erhoeht_ohne_loeschanlage.",
"werte": [
{"wert": 35000, "quelle": "ASR A2.3", "abschnitt": "5 (2)", "fassung": "<Stand eintragen>", "bedingung": "brandgefaehrdung = normal", "verifiziert": false},
{"wert": 35000, "quelle": "ASR A2.3", "abschnitt": "5 (2)", "fassung": "<Stand eintragen>", "bedingung": "brandgefaehrdung = erhoeht_mit_loeschanlage", "verifiziert": false},
{"wert": 25000, "quelle": "ASR A2.3", "abschnitt": "5 (2)", "fassung": "<Stand eintragen>", "bedingung": "brandgefaehrdung = erhoeht_ohne_loeschanlage", "verifiziert": false}
]
},
{
"id": "fluchtweg.laufweg_faktor_max",
"art": "max",
"einheit": "faktor",
"kommentar": "tatsaechliche Laufweglaenge <= Faktor x Fluchtweglaenge (Luftlinie). Beide Werte werden geprueft.",
"werte": [
{"wert": 1.5, "quelle": "ASR A2.3", "abschnitt": "5 (2)", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "fluchtweg.breite_min",
"art": "min",
"einheit": "mm",
"kommentar": "Tabelle nach Personenzahl (PERSONEN am Geschoss). Zeilen [bis_personen, breite_mm]; Minimum 875 mm.",
"werte": [
{"tabelle": [[5, 875], [20, 1000], [200, 1200], [300, 1800], [400, 2400]], "tabellen_schluessel": "personen", "quelle": "ASR A2.3", "abschnitt": "5 Tabelle 3", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "fluchtweg.leiter_anrechenbar",
"art": "wert",
"einheit": "bool",
"kommentar": "Leitern sind kein Fluchtweg, nur Notausstieg - ladders_down zaehlt nicht fuer die Fluchtwegpruefung.",
"werte": [
{"wert": 0, "quelle": "ASR A2.3", "abschnitt": "4", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "treppe.steigung_min",
"art": "min",
"einheit": "mm",
"werte": [
{"wert": 140, "quelle": "ASR A1.8", "abschnitt": "4.5", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "treppe.steigung_max",
"art": "max",
"einheit": "mm",
"werte": [
{"wert": 190, "quelle": "ASR A1.8", "abschnitt": "4.5", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "treppe.auftritt_min",
"art": "min",
"einheit": "mm",
"werte": [
{"wert": 260, "quelle": "ASR A1.8", "abschnitt": "4.5", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "treppe.auftritt_max",
"art": "max",
"einheit": "mm",
"werte": [
{"wert": 320, "quelle": "ASR A1.8", "abschnitt": "4.5", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "treppe.schrittmass_min",
"art": "min",
"einheit": "mm",
"kommentar": "Schrittmass 2 x Steigung + Auftritt; Schnittmenge ASR A1.8 und EN ISO 14122-3.",
"werte": [
{"wert": 590, "quelle": "ASR A1.8", "abschnitt": "4.5", "fassung": "<Stand eintragen>", "verifiziert": false},
{"wert": 600, "quelle": "EN ISO 14122-3", "abschnitt": "5", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "treppe.schrittmass_max",
"art": "max",
"einheit": "mm",
"werte": [
{"wert": 650, "quelle": "ASR A1.8", "abschnitt": "4.5", "fassung": "<Stand eintragen>", "verifiziert": false},
{"wert": 660, "quelle": "EN ISO 14122-3", "abschnitt": "5", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "treppe.winkel_min",
"art": "min",
"einheit": "grad",
"werte": [
{"wert": 30, "quelle": "EN ISO 14122-3", "abschnitt": "4", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "treppe.winkel_max",
"art": "max",
"einheit": "grad",
"kommentar": "bevorzugter Bereich 30-38 Grad, zulaessig bis 45 Grad - konservativ 38.",
"werte": [
{"wert": 38, "quelle": "EN ISO 14122-3", "abschnitt": "4", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "treppe.laufhoehe_max",
"art": "max",
"einheit": "mm",
"kommentar": "Hoehe eines Laufs ohne Podest. Bei 3,10 m Geschosshoehe ist ein Podest noetig (strenger als ASR mit 18 Stufen).",
"werte": [
{"wert": 3000, "quelle": "EN ISO 14122-3", "abschnitt": "4", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "treppe.stufen_je_lauf_max",
"art": "max",
"einheit": "stueck",
"werte": [
{"wert": 18, "quelle": "ASR A1.8", "abschnitt": "4.5", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "lichte_hoehe.verkehrsweg_min",
"art": "min",
"einheit": "mm",
"werte": [
{"wert": 2000, "quelle": "ASR A1.8", "abschnitt": "4.1", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "lichte_hoehe.treppe_min",
"art": "min",
"einheit": "mm",
"kommentar": "Kopfhoehe ueber der Treppe; EN ISO 14122-3 verlangt mehr als die ASR.",
"werte": [
{"wert": 2000, "quelle": "ASR A1.8", "abschnitt": "4.5", "fassung": "<Stand eintragen>", "verifiziert": false},
{"wert": 2300, "quelle": "EN ISO 14122-3", "abschnitt": "4", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "gelaender.hoehe_min",
"art": "min",
"einheit": "mm",
"kommentar": "Die Beispiel-YAML hat 1,0 m - die Konfliktliste meldet die Abweichung.",
"werte": [
{"wert": 1000, "quelle": "ASR A2.1", "abschnitt": "5.2", "fassung": "<Stand eintragen>", "bedingung": "absturzhoehe <= 12000", "verifiziert": false},
{"wert": 1100, "quelle": "ASR A2.1", "abschnitt": "5.2", "fassung": "<Stand eintragen>", "bedingung": "absturzhoehe > 12000", "verifiziert": false},
{"wert": 1100, "quelle": "EN ISO 14122-3", "abschnitt": "7", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "gelaender.bordbrett_min",
"art": "min",
"einheit": "mm",
"werte": [
{"wert": 100, "quelle": "EN ISO 14122-3", "abschnitt": "7", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "gelaender.knieleiste_abstand_max",
"art": "max",
"einheit": "mm",
"werte": [
{"wert": 500, "quelle": "EN ISO 14122-3", "abschnitt": "7", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "absturz.sicherung_ab",
"art": "max",
"einheit": "mm",
"kommentar": "Absturzsicherung ab dieser Absturzhoehe; konservativ werden alle Buehnenkanten gesichert.",
"werte": [
{"wert": 1000, "quelle": "ASR A2.1", "abschnitt": "4.1", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "leiter.rueckenschutz_ab",
"art": "max",
"einheit": "mm",
"werte": [
{"wert": 3000, "quelle": "EN ISO 14122-4", "abschnitt": "4", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "leiter.steighoehe_je_lauf_max",
"art": "max",
"einheit": "mm",
"werte": [
{"wert": 6000, "quelle": "EN ISO 14122-4", "abschnitt": "4", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "gang.breite_hauptgang_min",
"art": "min",
"einheit": "mm",
"kommentar": "Hauptgang mit Personenverkehr. Wert aus DGUV 108-007 in Phase 0 gegen den Originaltext pruefen.",
"werte": [
{"wert": 1250, "quelle": "DGUV Regel 108-007", "abschnitt": "<Abschnitt eintragen>", "fassung": "<Stand eintragen>", "verifiziert": false}
]
},
{
"id": "gang.breite_nebengang_min",
"art": "min",
"einheit": "mm",
"kommentar": "Regal-Nebengang (Gang zwischen Regalreihen, nur Personen). Wert aus DGUV 108-007 in Phase 0 gegen den Originaltext pruefen.",
"werte": [
{"wert": 750, "quelle": "DGUV Regel 108-007", "abschnitt": "<Abschnitt eintragen>", "fassung": "<Stand eintragen>", "verifiziert": false},
{"wert": 875, "quelle": "ASR A1.8", "abschnitt": "4.2", "fassung": "<Stand eintragen>", "bedingung": "gang_ist_fluchtweg = ja", "verifiziert": false}
]
}
]
}
+8
View File
@@ -0,0 +1,8 @@
{
"land": "PL",
"bezeichnung": "Polen - Geruest, erbt alle Werte aus DE",
"gepflegt": false,
"erbt_von": "DE",
"hinweis": "Nicht gepflegtes Laenderprofil: alle Regeln kommen aus DE. Abweichende nationale Werte als eigene Regel-Records (gleiche id) unter regeln eintragen - sie ersetzen dann den DE-Record vollstaendig. Solange gepflegt=false ist, weist der Nachweis eine Warnung aus.",
"regeln": []
}
+8
View File
@@ -0,0 +1,8 @@
{
"land": "SK",
"bezeichnung": "Slowakei - Geruest, erbt alle Werte aus DE",
"gepflegt": false,
"erbt_von": "DE",
"hinweis": "Nicht gepflegtes Laenderprofil: alle Regeln kommen aus DE. Abweichende nationale Werte als eigene Regel-Records (gleiche id) unter regeln eintragen - sie ersetzen dann den DE-Record vollstaendig. Solange gepflegt=false ist, weist der Nachweis eine Warnung aus.",
"regeln": []
}
+2 -2
View File
@@ -9,8 +9,8 @@
"einbauhoehe": { "vorgabe": 1500, "min": 300, "max": 8000 }, "einbauhoehe": { "vorgabe": 1500, "min": 300, "max": 8000 },
"laenge": { "min": 100, "max": 20000 }, "laenge": { "min": 100, "max": 20000 },
"_hinweis_ansicht": "KF_2D liegt DECKUNGSGLEICH mit der Kette (KS_EIN auf KS_EIN der Umlenkung, KS_AUS auf KS_AUS des Antriebs) - die Baugruppe ist EIN Koerper. Den frueheren Versatz (versatz_y) gibt es nicht mehr, seit die Kette aufgerichtet eingebaut wird.", "_hinweis_ansicht": "KF_2D ist ein eigener Hilfsblock KF2D_<n> (Draufsicht zum Anschliessen von Schienen), kein Teil des Foerderers. Er wird auf die projizierte Laenge skaliert und beim ERSTEN Einfuegen versatz_y mm (Y) gegen den Hauptblock versetzt; danach liegt er, wo der Benutzer ihn hinlegt.",
"ansicht_2d": { "block": "KF_2D" }, "ansicht_2d": { "block": "KF_2D", "versatz_y": -1000 },
"_hinweis_antriebsart": "Merkmal 357 ist beim Kettenfoerderer FEST: die Liste hat genau EINEN Eintrag, der im Dialog als Dropdown erscheint und zwingend gewaehlt ist. Weitere Antriebsarten (z.B. 0.55 kW) waeren einfach eine weitere Zeile.", "_hinweis_antriebsart": "Merkmal 357 ist beim Kettenfoerderer FEST: die Liste hat genau EINEN Eintrag, der im Dialog als Dropdown erscheint und zwingend gewaehlt ist. Weitere Antriebsarten (z.B. 0.55 kW) waeren einfach eine weitere Zeile.",
"antriebsart": [ "antriebsart": [
+62
View File
@@ -0,0 +1,62 @@
{
"version": 1,
"beschreibung": "Vorgaben fuer die Lagerplanung (LagerInsert.lsp). Alle Masse in mm, Winkel in Grad. Die Werte belegen die Dialoge vor; was im Dialog bzw. am Block steht, gewinnt.",
"land": "DE",
"goh_rack": {
"beschreibung": "Parametrischer BricsCAD-Block, der als Regalreihe mitgezaehlt wird (lg-racks-sammeln in LagerInsert.lsp). param_laenge/param_ebenen = Namen der MCAD-Parameter.",
"blockname": "goh_rack",
"param_laenge": "Laenge",
"param_ebenen": "Ebenen",
"tiefe_mm": 700
},
"geschoss": {
"konstr_hoehe_mm": 250,
"personen": 20,
"brandgefaehrdung": "erhoeht_ohne_loeschanlage",
"belag_art": "spanplatte",
"belag_arten": ["spanplatte", "gitterrost", "hallenboden"]
},
"belag_yaml": {
"beschreibung": "Belagsart (Attribut BELAG_ART/BELAG) -> Wert fuer walkway.spec der RFQ-YAML. hallenboden = keine Buehne (kein walkway/grating/railing).",
"spanplatte": "chipboard",
"gitterrost": "grating",
"hallenboden": ""
},
"brandgefaehrdungen": ["normal", "erhoeht_mit_loeschanlage", "erhoeht_ohne_loeschanlage"],
"bereich": {
"gangbreite_mm": 1000,
"regaltiefe_mm": 700,
"reihen_je_gang": 2,
"schienen_je_gang": 1,
"quergang_abstand_mm": 0,
"quergang_breite_mm": 1200,
"min_reihenlaenge_mm": 500,
"min_ganglaenge_mm": 100,
"richtung_grad": 0,
"belag": "spanplatte",
"randgang_mm": {"n": 0, "o": 0, "s": 0, "w": 0}
},
"belag_schraffur": {
"beschreibung": "Schraffur der Gangflaechen (LG_GANG_*) je Belag: muster = Schraffurmuster aus der BricsCAD-Musterdatei (metrisch: iso.pat), massstab, winkel in Grad; null = keine Schraffur. ANSI31 x 50 = Linien alle ca. 160 mm, GRID x 8 = Raster ca. 200 mm.",
"spanplatte": {"muster": "ANSI31", "massstab": 50, "winkel": 0},
"gitterrost": {"muster": "GRID", "massstab": 8, "winkel": 0},
"hallenboden": null
},
"reihe": {
"beschreibung": "haengeebenen = Schienenhoehe ueber Belag / max. Warenlaenge je Haengeebene, durch Semikolon getrennt, oberste zuerst.",
"haengeebenen": "2300/1200;1100/1100",
"schienen": 1
},
"flaechen": {
"beschreibung": "reihen_als_gitterrost: Grundflaeche der Regalreihen (Laenge x Tiefe) auf Buehnen zaehlt als Gitterrost (Absturzsicherung in den Reihen).",
"reihen_als_gitterrost": true
}
}
Binary file not shown.
Binary file not shown.
+116
View File
@@ -0,0 +1,116 @@
// ============================================================
// lager.dcl - Dialoge der Lagerplanung (LagerInsert.lsp)
//
// Die Tile-Keys entsprechen den Feldlisten *lg-felder-geschoss*,
// *lg-felder-bereich*, *lg-felder-reihe*, *lg-felder-gang* und
// *lg-felder-kopie* in LagerInsert.lsp
// (generischer Treiber lg-dialog: popup_list fuer Auswahlfelder,
// edit_box fuer alles andere, Pruefung beim OK in lg-dialog-ok).
// Alle Masse in mm.
// ============================================================
lg_geschoss : dialog {
label = "Lager - Geschoss";
: column {
: row {
: edit_box { key = "nr"; label = "Nummer:"; edit_width = 6; }
: edit_box { key = "bezeichnung"; label = "Bezeichnung:"; edit_width = 24; }
}
: boxed_column {
label = "Hoehen (mm)";
: edit_box { key = "hoehe"; label = "Belag Oberkante:"; edit_width = 10; }
: edit_box { key = "konstr"; label = "Konstruktionshoehe Belag:"; edit_width = 10; }
: edit_box { key = "lichte"; label = "Lichte Hoehe (leer = berechnet):"; edit_width = 10; }
}
: boxed_column {
label = "Belag und Fluchtweg-Annahmen";
: popup_list { key = "belag"; label = "Belagsart:"; width = 34; }
: edit_box { key = "personen"; label = "Personen im Geschoss:"; edit_width = 6; }
: popup_list { key = "brand"; label = "Brandgefaehrdung:"; width = 34; }
}
}
ok_cancel;
errtile;
}
lg_bereich : dialog {
label = "Lager - Lagerbereich (Auto-Fuellung)";
: column {
: boxed_column {
label = "Reihen und Gaenge (mm)";
: edit_box { key = "gang"; label = "Gangbreite:"; edit_width = 10; }
: edit_box { key = "tiefe"; label = "Regaltiefe:"; edit_width = 10; }
: popup_list { key = "reihen"; label = "Reihen je Gang:"; width = 20; }
: edit_box { key = "schienen"; label = "Schienen je Gang:"; edit_width = 10; }
: edit_box { key = "richtung"; label = "Reihenrichtung (Grad):"; edit_width = 10; }
}
: boxed_column {
label = "Quergaenge (mm)";
: edit_box { key = "q_abstand"; label = "Abstand (0 = keine):"; edit_width = 10; }
: edit_box { key = "q_breite"; label = "Breite:"; edit_width = 10; }
}
: boxed_column {
label = "Randgaenge entlang der Kontur (mm, 0 = keiner; Nord = +Y)";
: row {
: edit_box { key = "rand_n"; label = "Nord:"; edit_width = 7; }
: edit_box { key = "rand_o"; label = "Ost:"; edit_width = 7; }
: edit_box { key = "rand_s"; label = "Sued:"; edit_width = 7; }
: edit_box { key = "rand_w"; label = "West:"; edit_width = 7; }
}
}
: boxed_column {
label = "Haengeware und Belag";
: edit_box { key = "ebenen"; label = "Haengeebenen (Schiene/Ware;...):"; edit_width = 24; }
: popup_list { key = "belag"; label = "Belag der Gaenge:"; width = 30; }
}
}
ok_cancel;
errtile;
}
lg_gang : dialog {
label = "Lager - Gang";
: column {
: edit_box { key = "laenge"; label = "Laenge (mm):"; edit_width = 10; }
: edit_box { key = "breite"; label = "Breite (mm):"; edit_width = 10; }
: popup_list { key = "art"; label = "Art:"; width = 30; }
: popup_list { key = "belag"; label = "Belag:"; width = 30; }
: text { label = "Linie: Gang liegt mittig auf der Linie, Breite wie oben."; }
: text { label = "Option Rechteck: Flaeche mit RECHTECK zeichnen, Lage und Breite daraus."; }
}
ok_cancel;
errtile;
}
lg_geschoss_kopie : dialog {
label = "Lager - Geschoss kopieren";
: column {
: popup_list { key = "quelle"; label = "Quellgeschoss (Nr.):"; width = 30; }
: edit_box { key = "anzahl"; label = "Anzahl neuer Geschosse:"; edit_width = 6; }
: edit_box { key = "abstand"; label = "Geschosshoehe je Kopie (mm):"; edit_width = 10; }
: popup_list { key = "belag"; label = "Belag der neuen Geschosse:"; width = 30; }
: boxed_column {
label = "Versatz je Kopie (mm, 0 = deckungsgleich)";
: row {
: edit_box { key = "versatz_x"; label = "X:"; edit_width = 10; }
: edit_box { key = "versatz_y"; label = "Y:"; edit_width = 10; }
}
}
: text { label = "Kopiert Belag, Bereiche, Reihen, Gaenge und goh_rack 1:1."; }
: text { label = "Belaege gleich dem Quell-Geschossbelag folgen dem neuen Belag."; }
}
ok_cancel;
errtile;
}
lg_reihe : dialog {
label = "Lager - Regalreihe";
: column {
: edit_box { key = "laenge"; label = "Laenge (mm):"; edit_width = 10; }
: edit_box { key = "tiefe"; label = "Regaltiefe (mm):"; edit_width = 10; }
: edit_box { key = "ebenen"; label = "Haengeebenen (Schiene/Ware;...):"; edit_width = 24; }
: edit_box { key = "schienen"; label = "Schienen:"; edit_width = 10; }
}
ok_cancel;
errtile;
}
+102 -4
View File
@@ -1189,6 +1189,104 @@
"chh-summe": "\n Phase 3: %1 Familie(n), %2 mit Buehne; angepasst: Kreisel %3, Strecken innerhalb %4, zwischen Familien %5; Konflikte %6; einzeln gefragt %7", "chh-summe": "\n Phase 3: %1 Familie(n), %2 mit Buehne; angepasst: Kreisel %3, Strecken innerhalb %4, zwischen Familien %5; Konflikte %6; einzeln gefragt %7",
"exp-kreisel-weichen": "\n[%1] Kreisel-Weichenpunkte: %2 Beruehrung(en), %3 Kollision(en) -> %4", "exp-kreisel-weichen": "\n[%1] Kreisel-Weichenpunkte: %2 Beruehrung(en), %3 Kollision(en) -> %4",
"exp-kreisel-kollision": "\n[%1] WARNUNG: %2 Kreisel-Kollision(en) - Befehl KREISEL_WEICHEN zeigt sie an.", "exp-kreisel-kollision": "\n[%1] WARNUNG: %2 Kreisel-Kollision(en) - Befehl KREISEL_WEICHEN zeigt sie an.",
"lg-daten-fehlt": "\n[Lager] Datendatei nicht gefunden: %1 - es gelten die eingebauten Vorgaben.",
"lg-dialog-fehlt": "\n[Lager] Dialog nicht ladbar (%1) - Eingabe ueber die Konsole.",
"lg-einfuegen-fehler": "\n[Lager] Block %1 konnte nicht eingefuegt werden: %2",
"lg-feld-ungueltig": "Ungueltiger Wert: %1",
"lg-wert-ungueltig": "\nUngueltiger Wert - bitte erneut eingeben.",
"lg-wahl-ungueltig": "\nBitte einen der angezeigten Werte eingeben.",
"lg-feld-nr": "\nGeschoss-Nummer",
"lg-feld-bezeichnung": "\nBezeichnung",
"lg-feld-hoehe": "\nBelagshoehe (Oberkante, mm)",
"lg-feld-konstr": "\nKonstruktionshoehe des Belags (mm)",
"lg-feld-lichte": "\nLichte Hoehe (mm, leer = berechnet)",
"lg-feld-belag": "\nBelagsart",
"lg-feld-personen": "\nPersonen im Geschoss",
"lg-feld-brand": "\nBrandgefaehrdung",
"lg-feld-gang": "\nGangbreite (mm)",
"lg-feld-tiefe": "\nRegaltiefe (mm)",
"lg-feld-reihen": "\nReihen je Gang",
"lg-feld-schienen": "\nSchienen je Gang",
"lg-feld-q_abstand": "\nQuergang-Abstand (mm, 0 = keine)",
"lg-feld-q_breite": "\nQuergang-Breite (mm)",
"lg-feld-richtung": "\nReihenrichtung (Grad)",
"lg-feld-ebenen": "\nHaengeebenen Schienenhoehe/Warenlaenge;... (mm)",
"lg-feld-laenge": "\nLaenge (mm)",
"lg-feld-rand_n": "\nRandgang Nord (mm, 0 = keiner)",
"lg-feld-rand_o": "\nRandgang Ost (mm, 0 = keiner)",
"lg-feld-rand_s": "\nRandgang Sued (mm, 0 = keiner)",
"lg-feld-rand_w": "\nRandgang West (mm, 0 = keiner)",
"lg-feld-breite": "\nGangbreite (mm)",
"lg-feld-art": "\nArt des Gangs",
"lg-titel-gang": "Lager: Gaenge einfuegen",
"lg-prompt-gang-start": "\nStartpunkt der Gangmitte oder [%1] <Enter = Ende>: ",
"lg-key-rechteck": "Rechteck",
"lg-rechteck-hinweis": "\nGangflaeche mit RECHTECK zeichnen (alle Optionen wie Abmessungen/Drehung moeglich), Enter = Ende.",
"lg-rechteck-kein-rechteck": "\n[Lager] Keine einfache Rechteckflaeche (4 Ecken, ohne Fase/Rundung) - kein Gang eingefuegt.",
"lg-rechteck-zu-klein": "\n[Lager] Rechteck zu klein (Seite unter 1 mm) - kein Gang eingefuegt.",
"lg-gaenge-eingefuegt": "\n%1 Gang/Gaenge eingefuegt.",
"lg-schraffur-unbekannt": "\n[Lager] Belag %1 hat keinen Eintrag in belag_schraffur (lager.json) - Gaenge ohne Schraffur.",
"lg-schraffur-fehler": "\n[Lager] Schraffur %1 fuer %2 nicht moeglich (%3) - Gang ohne Schraffur.",
"lg-blockdef-ergaenzt": "\n[Lager] Blockdefinition %1 um %2 ergaenzt (Altbestand).",
"lg-titel-kopie": "Lager: Geschoss kopieren",
"lg-feld-quelle": "\nQuellgeschoss (Nr.)",
"lg-feld-anzahl": "\nAnzahl neuer Geschosse",
"lg-feld-abstand": "\nGeschosshoehe je Kopie (mm)",
"lg-feld-versatz_x": "\nVersatz X je Kopie (mm, 0 = deckungsgleich)",
"lg-feld-versatz_y": "\nVersatz Y je Kopie (mm, 0 = deckungsgleich)",
"lg-kopie-abstand-ungueltig": "\n[Lager] Geschosshoehe je Kopie muss groesser 0 sein - nichts kopiert.",
"lg-kopie-hoehe-belegt": "\n[Lager] Auf Belagshoehe %1 mm liegt bereits ein Geschoss - Kopie wird trotzdem angelegt.",
"lg-kopie-racks-mehrdeutig": "\n[Lager] %1 goh_rack ohne eindeutiges Geschoss (Konturen uebereinander, kein Layer _G<nr>) - nicht kopiert.",
"lg-kopie-racks-umgehaengt": "\n%1 goh_rack auf den Regal-Layer von Geschoss %2 gelegt (Zuordnung bei deckungsgleichen Geschossen).",
"lg-kopie-fertig": "\nGeschoss %1 kopiert -> Geschoss %2 auf %3 mm: %4 Regalreihe(n), %5 Gang-/Quergangflaeche(n), %6 goh_rack.",
"lg-kopie-hinweis-aktiv": "\nLetzte Kopie ist aktives Geschoss. Anzeige umschalten mit LG_GESCHOSS_AKTIV.",
"lg-regale-keine": "\n[Lager] Keine Bloecke %1 im Modellbereich gefunden.",
"lg-regale-kopf": "\nRegale (goh_rack) je Geschoss: Anzahl, Laenge, Laenge x Ebenen",
"lg-regale-zeile": "\n %1: %2 Regal(e), %3 m, %4 m",
"lg-regale-geschoss": "Geschoss %1",
"lg-regale-ohne-geschoss": "ohne Geschoss",
"lg-regale-summe": "Gesamt",
"lg-regale-ohne-laenge": "\n[Lager] %1 Regal(e) ohne lesbare Laenge - mit 0 gezaehlt.",
"lg-titel-geschoss": "Lager: Geschoss anlegen",
"lg-titel-bereich": "Lager: Lagerbereich anlegen",
"lg-titel-reihe": "Lager: Regalreihen einfuegen",
"lg-titel-edit": "Lager: bearbeiten",
"lg-titel-hand-yaml": "Hand-YAML fuer den Abgleich (leer = keiner)",
"lg-prompt-kontur-geschoss": "\nBelagskontur des Geschosses waehlen (geschlossene Polylinie) <Enter = zeichnen>: ",
"lg-prompt-kontur-bereich": "\nKontur des Lagerbereichs waehlen (geschlossene Polylinie) <Enter = zeichnen>: ",
"lg-kontur-zeichnen": "\nKontur als Polylinie zeichnen (Enter beendet, wird geschlossen).",
"lg-keine-kontur": "\nDas ist keine Polylinie mit mindestens 3 Punkten.",
"lg-kontur-boegen": "\nHinweis: Bogensegmente der Kontur werden als Gerade gerechnet.",
"lg-kein-geschoss": "\n[Lager] Noch kein Geschoss angelegt - zuerst LG_GESCHOSS.",
"lg-prompt-geschoss-nr": "\nGeschoss nicht eindeutig - Geschoss-Nummer",
"lg-prompt-geschoss-aktiv": "\nAktives Geschoss (0 = alle anzeigen)",
"lg-geschoss-unbekannt": "\n[Lager] Geschoss %1 gibt es nicht.",
"lg-geschoss-existiert": "\n[Lager] Geschoss %1 gibt es schon - andere Nummer waehlen.",
"lg-geschoss-angelegt": "\nGeschoss %1 angelegt (Belag %2 mm) und aktiv gesetzt.",
"lg-geschoss-aktiv": "\nAktives Geschoss: %1 - die Layer der uebrigen Geschosse sind gefroren.",
"lg-geschoss-alle": "\nAlle Geschosse sichtbar, kein aktives Geschoss.",
"lg-geaendert": "\nGeaendert.",
"lg-bereich-ohne-kontur": "\n[Lager] Die Kontur des Bereichs fehlt - Fuellung nicht moeglich.",
"lg-bereich-beibehalten": "\nBereich %2: %1 von Hand eingefuegte oder geaenderte Reihe(n)/Gaenge bleiben erhalten und werden ausgespart.",
"lg-bereich-gefuellt": "\nBereich %1: %2 Regalreihe(n), %3 Gang-/Quergangflaeche(n), Restbreite %4 mm.",
"lg-prompt-reihe-start": "\nStartpunkt der Regalreihe <Enter = Ende>: ",
"lg-prompt-reihe-ende": "\nEndpunkt oder Laenge (mm): ",
"lg-prompt-reihe-richtung": "\nRichtung: ",
"lg-reihen-eingefuegt": "\n%1 Regalreihe(n) eingefuegt.",
"lg-exp-keine-geschosse": "Keine Geschosse (LG_GESCHOSS) in der Zeichnung - levels bleiben leer.",
"lg-exp-geschoss-ohne-kontur": "Geschoss %1 hat keine Belagskontur mehr.",
"lg-exp-rack-ohne-geschoss": "goh_rack bei X=%1 Y=%2 keinem Geschoss eindeutig zuzuordnen.",
"lg-exp-rack-ohne-laenge": "goh_rack %1 ohne lesbare Laenge.",
"lg-exp-datei-fehler": "\n[Lager] Datei nicht schreibbar: %1",
"lg-exp-raw": "\n[Lager] Rohdaten: %1",
"lg-exp-yaml": "\n[Lager] Overlay-YAML: %1\n[Lager] Nachweis: %2",
"lg-exp-ohne-python": "\n[Lager] Python-Aufruf nicht moeglich (export.lsp/DXFM_LIB fehlt) - YAML von Hand erzeugen: python lib/lager_yaml_export.py <raw.json> <out.yaml>",
"lg-exp-python-fehler": "\n[Lager] lager_yaml_export.py endete mit Code %1 - siehe %2",
"lg-hand-yaml": "\nHand-YAML fuer den Abgleich: %1",
"lg-regel-profil-fehlt": "\n[Lager] Regelprofil %1 nicht gefunden (%2).",
"lg-regel-profil-ungepflegt": "\n[Lager] WARNUNG: Regelprofil %1 ist nicht gepflegt - es gelten die geerbten Werte.",
"lg-regel-unbekannt": "\n[Lager] Regel %1 nicht im Profil.",
"lg-regel-liste-kopf": "\nRegelprofil %1: %2 Regeln (Kontext: erhoehte Brandgefaehrdung ohne Loeschanlage)",
"kf-fehler-daten-fehlt": "\nFEHLER: Kettenfoerderer-Daten nicht gefunden: %1", "kf-fehler-daten-fehlt": "\nFEHLER: Kettenfoerderer-Daten nicht gefunden: %1",
"kf-fehler-daten-leer": "\nFEHLER: Kettenfoerderer-Daten konnten nicht gelesen werden.", "kf-fehler-daten-leer": "\nFEHLER: Kettenfoerderer-Daten konnten nicht gelesen werden.",
"kf-fehler-block-datei": "\nFEHLER: Blockdatei fuer %1 nicht gefunden (erwartet in data/omniflo/<Dimension>/).", "kf-fehler-block-datei": "\nFEHLER: Blockdatei fuer %1 nicht gefunden (erwartet in data/omniflo/<Dimension>/).",
@@ -1202,11 +1300,11 @@
"kf-prompt-messpunkt-2": "\nZweiten Messpunkt waehlen (Hoehenbezug oben): ", "kf-prompt-messpunkt-2": "\nZweiten Messpunkt waehlen (Hoehenbezug oben): ",
"kf-messung-ergebnis": "\n>>> Gemessen: Hoehendifferenz dZ = %1 mm (zur Kontrolle dY = %2 mm).", "kf-messung-ergebnis": "\n>>> Gemessen: Hoehendifferenz dZ = %1 mm (zur Kontrolle dY = %2 mm).",
"kf-konsole-hoehe-hinweis": "\n(0 eingeben, um die Hoehe aus zwei Punkten zu messen)", "kf-konsole-hoehe-hinweis": "\n(0 eingeben, um die Hoehe aus zwei Punkten zu messen)",
"kf-2d-gebaut": "\n>>> Draufsicht %1 eingefuegt: Schraeglaenge %2 mm, Neigung %3 Grad.", "kf-2d-gebaut": "\n>>> Hilfsblock %1 (Draufsicht) eingefuegt: KF waagerechte Laenge %2 mm.",
"kf-2d-laenge-null": "\nDraufsicht uebersprungen: die Kette hat keine messbare Grundrisslaenge.", "kf-bemassung-fehlt": "\nHinweis: Das Hoehenmass konnte nicht gezeichnet werden - der Foerderer wurde trotzdem gebaut.",
"cmd-kf2d-hilfsblock": "\nHilfsblock (Draufsicht): kein eigener Dialog - Doppelklick auf den Hauptblock KF_<n> erzeugt ihn neu.",
"kf-2d-ks-fehlt": "\nDraufsicht uebersprungen: %1 hat kein messbares KS_EIN/KS_AUS.", "kf-2d-ks-fehlt": "\nDraufsicht uebersprungen: %1 hat kein messbares KS_EIN/KS_AUS.",
"kf-prompt-startpunkt": "\nEinfuegepunkt waehlen (KS_EIN der Draufsicht; die Seitenansicht entsteht darueber): ", "kf-prompt-startpunkt": "\nEinfuegepunkt waehlen (KS_EIN der Umlenkstation): ",
"kf-prompt-drehung": "\nDrehwinkel der Kette: ",
"kf-abgebrochen": "\nAbgebrochen.", "kf-abgebrochen": "\nAbgebrochen.",
"kf-fertig": "\n>>> %1 erstellt: Einbauhoehe %2 mm, Zwischenstueck %3 mm.", "kf-fertig": "\n>>> %1 erstellt: Einbauhoehe %2 mm, Zwischenstueck %3 mm.",
"kf-konsole-variante-header": "\n\nSchraegfoerderer Stahl Typ waehlen:", "kf-konsole-variante-header": "\n\nSchraegfoerderer Stahl Typ waehlen:",
+102 -4
View File
@@ -1189,6 +1189,104 @@
"chh-summe": "\n Phase 3: %1 family(ies), %2 on a platform; adjusted: Kreisel %3, sections within family %4, between families %5; conflicts %6; asked individually %7", "chh-summe": "\n Phase 3: %1 family(ies), %2 on a platform; adjusted: Kreisel %3, sections within family %4, between families %5; conflicts %6; asked individually %7",
"exp-kreisel-weichen": "\n[%1] Kreisel switch points: %2 contact(s), %3 collision(s) -> %4", "exp-kreisel-weichen": "\n[%1] Kreisel switch points: %2 contact(s), %3 collision(s) -> %4",
"exp-kreisel-kollision": "\n[%1] WARNING: %2 Kreisel collision(s) - command KREISEL_WEICHEN shows them.", "exp-kreisel-kollision": "\n[%1] WARNING: %2 Kreisel collision(s) - command KREISEL_WEICHEN shows them.",
"lg-daten-fehlt": "\n[Storage] Data file not found: %1 - built-in defaults apply.",
"lg-dialog-fehlt": "\n[Storage] Dialog not loadable (%1) - input via command line.",
"lg-einfuegen-fehler": "\n[Storage] Block %1 could not be inserted: %2",
"lg-feld-ungueltig": "Invalid value: %1",
"lg-wert-ungueltig": "\nInvalid value - please enter again.",
"lg-wahl-ungueltig": "\nPlease enter one of the listed values.",
"lg-feld-nr": "\nLevel number",
"lg-feld-bezeichnung": "\nName",
"lg-feld-hoehe": "\nFloor elevation (top, mm)",
"lg-feld-konstr": "\nFloor construction depth (mm)",
"lg-feld-lichte": "\nClear height (mm, empty = calculated)",
"lg-feld-belag": "\nFloor type",
"lg-feld-personen": "\nPersons on the level",
"lg-feld-brand": "\nFire risk",
"lg-feld-gang": "\nAisle width (mm)",
"lg-feld-tiefe": "\nRack depth (mm)",
"lg-feld-reihen": "\nRows per aisle",
"lg-feld-schienen": "\nRails per aisle",
"lg-feld-q_abstand": "\nCross aisle spacing (mm, 0 = none)",
"lg-feld-q_breite": "\nCross aisle width (mm)",
"lg-feld-richtung": "\nRow direction (degrees)",
"lg-feld-ebenen": "\nHanging levels rail height/goods length;... (mm)",
"lg-feld-laenge": "\nLength (mm)",
"lg-feld-rand_n": "\nPerimeter aisle north (mm, 0 = none)",
"lg-feld-rand_o": "\nPerimeter aisle east (mm, 0 = none)",
"lg-feld-rand_s": "\nPerimeter aisle south (mm, 0 = none)",
"lg-feld-rand_w": "\nPerimeter aisle west (mm, 0 = none)",
"lg-feld-breite": "\nAisle width (mm)",
"lg-feld-art": "\nAisle type",
"lg-titel-gang": "Storage: insert aisles",
"lg-prompt-gang-start": "\nStart point of the aisle centre line or [%1] <Enter = finish>: ",
"lg-key-rechteck": "Rectangle",
"lg-rechteck-hinweis": "\nDraw the aisle area with RECTANG (all options such as dimensions/rotation available), Enter = finish.",
"lg-rechteck-kein-rechteck": "\n[Storage] Not a plain rectangle (4 corners, no chamfer/fillet) - no aisle inserted.",
"lg-rechteck-zu-klein": "\n[Storage] Rectangle too small (side below 1 mm) - no aisle inserted.",
"lg-gaenge-eingefuegt": "\n%1 aisle(s) inserted.",
"lg-schraffur-unbekannt": "\n[Storage] Floor type %1 has no entry in belag_schraffur (lager.json) - aisles without hatch.",
"lg-schraffur-fehler": "\n[Storage] Hatch %1 for %2 not possible (%3) - aisle without hatch.",
"lg-blockdef-ergaenzt": "\n[Storage] Block definition %1 extended by %2 (existing drawing).",
"lg-titel-kopie": "Storage: copy level",
"lg-feld-quelle": "\nSource level (no.)",
"lg-feld-anzahl": "\nNumber of new levels",
"lg-feld-abstand": "\nLevel height per copy (mm)",
"lg-feld-versatz_x": "\nOffset X per copy (mm, 0 = congruent)",
"lg-feld-versatz_y": "\nOffset Y per copy (mm, 0 = congruent)",
"lg-kopie-abstand-ungueltig": "\n[Storage] Level height per copy must be greater than 0 - nothing copied.",
"lg-kopie-hoehe-belegt": "\n[Storage] A level already exists at floor height %1 mm - copy is created anyway.",
"lg-kopie-racks-mehrdeutig": "\n[Storage] %1 goh_rack without a unique level (stacked contours, no layer _G<no>) - not copied.",
"lg-kopie-racks-umgehaengt": "\n%1 goh_rack moved to the rack layer of level %2 (assignment for congruent levels).",
"lg-kopie-fertig": "\nLevel %1 copied -> level %2 at %3 mm: %4 rack row(s), %5 aisle/cross aisle area(s), %6 goh_rack.",
"lg-kopie-hinweis-aktiv": "\nLast copy is the active level. Switch the display with LG_GESCHOSS_AKTIV.",
"lg-regale-keine": "\n[Storage] No %1 blocks found in model space.",
"lg-regale-kopf": "\nRacks (goh_rack) per level: count, length, length x levels",
"lg-regale-zeile": "\n %1: %2 rack(s), %3 m, %4 m",
"lg-regale-geschoss": "Level %1",
"lg-regale-ohne-geschoss": "no level",
"lg-regale-summe": "Total",
"lg-regale-ohne-laenge": "\n[Storage] %1 rack(s) without readable length - counted as 0.",
"lg-titel-geschoss": "Storage: create level",
"lg-titel-bereich": "Storage: create storage area",
"lg-titel-reihe": "Storage: insert rack rows",
"lg-titel-edit": "Storage: edit",
"lg-titel-hand-yaml": "Manual YAML for comparison (empty = none)",
"lg-prompt-kontur-geschoss": "\nSelect floor contour of the level (closed polyline) <Enter = draw>: ",
"lg-prompt-kontur-bereich": "\nSelect contour of the storage area (closed polyline) <Enter = draw>: ",
"lg-kontur-zeichnen": "\nDraw the contour as polyline (Enter finishes, it will be closed).",
"lg-keine-kontur": "\nThis is not a polyline with at least 3 points.",
"lg-kontur-boegen": "\nNote: arc segments of the contour are treated as straight lines.",
"lg-kein-geschoss": "\n[Storage] No level defined yet - run LG_GESCHOSS first.",
"lg-prompt-geschoss-nr": "\nLevel not unique - level number",
"lg-prompt-geschoss-aktiv": "\nActive level (0 = show all)",
"lg-geschoss-unbekannt": "\n[Storage] Level %1 does not exist.",
"lg-geschoss-existiert": "\n[Storage] Level %1 already exists - choose another number.",
"lg-geschoss-angelegt": "\nLevel %1 created (floor %2 mm) and set active.",
"lg-geschoss-aktiv": "\nActive level: %1 - layers of the other levels are frozen.",
"lg-geschoss-alle": "\nAll levels visible, no active level.",
"lg-geaendert": "\nChanged.",
"lg-bereich-ohne-kontur": "\n[Storage] The contour of the area is missing - cannot fill.",
"lg-bereich-beibehalten": "\nArea %2: %1 manually inserted or changed row(s)/aisle(s) are kept and left out.",
"lg-bereich-gefuellt": "\nArea %1: %2 rack row(s), %3 aisle/cross aisle area(s), remaining width %4 mm.",
"lg-prompt-reihe-start": "\nStart point of the rack row <Enter = finish>: ",
"lg-prompt-reihe-ende": "\nEnd point or length (mm): ",
"lg-prompt-reihe-richtung": "\nDirection: ",
"lg-reihen-eingefuegt": "\n%1 rack row(s) inserted.",
"lg-exp-keine-geschosse": "No levels (LG_GESCHOSS) in the drawing - levels stay empty.",
"lg-exp-geschoss-ohne-kontur": "Level %1 has no floor contour any more.",
"lg-exp-rack-ohne-geschoss": "goh_rack at X=%1 Y=%2 cannot be assigned to a level.",
"lg-exp-rack-ohne-laenge": "goh_rack %1 without readable length.",
"lg-exp-datei-fehler": "\n[Storage] File not writable: %1",
"lg-exp-raw": "\n[Storage] Raw data: %1",
"lg-exp-yaml": "\n[Storage] Overlay YAML: %1\n[Storage] Report: %2",
"lg-exp-ohne-python": "\n[Storage] Cannot call Python (export.lsp/DXFM_LIB missing) - create the YAML manually: python lib/lager_yaml_export.py <raw.json> <out.yaml>",
"lg-exp-python-fehler": "\n[Storage] lager_yaml_export.py ended with code %1 - see %2",
"lg-hand-yaml": "\nManual YAML for comparison: %1",
"lg-regel-profil-fehlt": "\n[Storage] Rule profile %1 not found (%2).",
"lg-regel-profil-ungepflegt": "\n[Storage] WARNING: rule profile %1 is not maintained - inherited values apply.",
"lg-regel-unbekannt": "\n[Storage] Rule %1 not in profile.",
"lg-regel-liste-kopf": "\nRule profile %1: %2 rules (context: increased fire risk without extinguishing system)",
"kf-fehler-daten-fehlt": "\nERROR: chain conveyor data not found: %1", "kf-fehler-daten-fehlt": "\nERROR: chain conveyor data not found: %1",
"kf-fehler-daten-leer": "\nERROR: chain conveyor data could not be read.", "kf-fehler-daten-leer": "\nERROR: chain conveyor data could not be read.",
"kf-fehler-block-datei": "\nERROR: block file for %1 not found (expected in data/omniflo/<dimension>/).", "kf-fehler-block-datei": "\nERROR: block file for %1 not found (expected in data/omniflo/<dimension>/).",
@@ -1202,11 +1300,11 @@
"kf-prompt-messpunkt-2": "\nSelect second measuring point (upper reference): ", "kf-prompt-messpunkt-2": "\nSelect second measuring point (upper reference): ",
"kf-messung-ergebnis": "\n>>> Measured: height difference dZ = %1 mm (for reference dY = %2 mm).", "kf-messung-ergebnis": "\n>>> Measured: height difference dZ = %1 mm (for reference dY = %2 mm).",
"kf-konsole-hoehe-hinweis": "\n(enter 0 to measure the height from two points)", "kf-konsole-hoehe-hinweis": "\n(enter 0 to measure the height from two points)",
"kf-2d-gebaut": "\n>>> Plan view %1 inserted: slope length %2 mm, inclination %3 deg.", "kf-2d-gebaut": "\n>>> Helper block %1 (plan view) inserted: KF horizontal length %2 mm.",
"kf-2d-laenge-null": "\nPlan view skipped: the chain has no measurable plan length.", "kf-bemassung-fehlt": "\nNote: the height dimension could not be drawn - the conveyor was built anyway.",
"cmd-kf2d-hilfsblock": "\nHelper block (plan view): no dialog of its own - double-click the main block KF_<n> to regenerate it.",
"kf-2d-ks-fehlt": "\nPlan view skipped: %1 has no measurable KS_EIN/KS_AUS.", "kf-2d-ks-fehlt": "\nPlan view skipped: %1 has no measurable KS_EIN/KS_AUS.",
"kf-prompt-startpunkt": "\nSelect insertion point (KS_EIN of the plan view; the side view is built above it): ", "kf-prompt-startpunkt": "\nSelect insertion point (KS_EIN of the return station): ",
"kf-prompt-drehung": "\nRotation angle of the chain: ",
"kf-abgebrochen": "\nCancelled.", "kf-abgebrochen": "\nCancelled.",
"kf-fertig": "\n>>> %1 created: installation height %2 mm, intermediate section %3 mm.", "kf-fertig": "\n>>> %1 created: installation height %2 mm, intermediate section %3 mm.",
"kf-konsole-variante-header": "\n\nSelect inclined steel conveyor type:", "kf-konsole-variante-header": "\n\nSelect inclined steel conveyor type:",
+12 -4
View File
@@ -313,8 +313,8 @@ def kettenfoerderer_signature(block):
Merkmale wuerden schon hier dieselbe Gruppe ergeben. Sonst wuerde dort Merkmale wuerden schon hier dieselbe Gruppe ergeben. Sonst wuerde dort
"anzahl" addiert, waehrend 353 unveraendert stehen bliebe. "anzahl" addiert, waehrend 353 unveraendert stehen bliebe.
Die Laenge des Zwischenstuecks (L_KF_mm) ist eine FOLGE der Einbauhoehe und Die waagerechte Laenge (L_KF_mm) ist eine FOLGE der Einbauhoehe (und des
steht darum nicht eigens im Schluessel. Winkels) und steht darum nicht eigens im Schluessel.
""" """
attribs = block.get("attribs", {}) attribs = block.get("attribs", {})
return ( return (
@@ -702,7 +702,11 @@ def process_blocks(blocks, anzahl_weichen_ils=0):
# 359/360/391) gingen dabei verloren. Anders als das Geruest werden # 359/360/391) gingen dabei verloren. Anders als das Geruest werden
# BAUGLEICHE Foerderer zu einer Zeile zusammengefasst, weil Merkmal # BAUGLEICHE Foerderer zu einer Zeile zusammengefasst, weil Merkmal
# 353 genau diese Stueckzahl verlangt. # 353 genau diese Stueckzahl verlangt.
if matches_any(bname, BLOCKPATTERNS.get("pattern_kettenfoerderer", [])): # Nur echte Foerderer "KF_<n>": das Muster KF_* erfasst sonst auch
# lose Bausteine (KF_UM_26, KF_2D). Der Hilfsblock KF2D_<n> passt nicht
# aufs Muster und wird nie exportiert.
if (matches_any(bname, BLOCKPATTERNS.get("pattern_kettenfoerderer", []))
and re.fullmatch(r"KF_\d+", bname, re.IGNORECASE)):
sig = kettenfoerderer_signature(block) sig = kettenfoerderer_signature(block)
if sig not in kettenfoerderer_groups: if sig not in kettenfoerderer_groups:
kettenfoerderer_groups[sig] = {"block": block, "ids": [], "anzahl": 0} kettenfoerderer_groups[sig] = {"block": block, "ids": [], "anzahl": 0}
@@ -996,7 +1000,11 @@ def process_blocks(blocks, anzahl_weichen_ils=0):
"bezeichnung": attribs.get("Bezeichnung", ""), "bezeichnung": attribs.get("Bezeichnung", ""),
"dispgruppe": 20, "dispgruppe": 20,
"anzahl": gruppe["anzahl"], "anzahl": gruppe["anzahl"],
"laenge_mm": attribs.get("L_KF_mm", ""), # Bewusst LEER: Sivas berechnet ZL, EL und die Kettenlaenge selbst, aus
# dem Kettenfoerderer wird nur die Hoehe (360) uebernommen. L_KF_mm
# (waagerechte Laenge des Foerderers) steht als Attribut am Block,
# geht aber nicht in die Sivas-Zeile.
"laenge_mm": "",
"ids": gruppe["ids"], "ids": gruppe["ids"],
"details": build_kettenfoerderer_details(block, gruppe["anzahl"]), "details": build_kettenfoerderer_details(block, gruppe["anzahl"]),
}) })
+439
View File
@@ -0,0 +1,439 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
lager_yaml_export.py - Lagerplanung aus BricsCAD -> Overlay-YAML fuer das RFQ-Programm.
Liest die von LAGER_EXPORT (Lisp/LagerInsert.lsp) geschriebene Rohdatei
<zeichnung>_lager_raw.json und erzeugt daraus
<projekt>_drawing.yaml gleiches Schema wie die Hand-YAML (z.B.
cfg/example_goh_storage.yaml), aber NUR mit den
Feldern, die die Zeichnung liefert, plus
_provenance (Herkunft/Formel je Wert),
_conflicts (Abweichungen zur Hand-YAML) und
_warnings.
<projekt>_nachweis.html interner Mengen-Nachweis (optional, --nachweis)
Merge-Vertrag (umgesetzt im RFQ-Repo): Hand-Wert vorhanden und nicht null ->
Hand gewinnt; sonst Zeichnungswert. Abweichungen erscheinen in _conflicts.
Python aggregiert nur - jede fachliche Regel lebt in LISP
(Lisp/LagerPruefung.lsp, siehe doc/plan/lager_rfq.md).
Aufruf:
python lager_yaml_export.py <lager_raw.json> <out_drawing.yaml>
[--hand <hand.yaml>] [--nachweis <out.html>]
"""
import argparse
import html
import json
import sys
from collections import Counter, OrderedDict
import yaml
# Vergleichstoleranz Hand <-> Zeichnung: relativ 1 % ODER absolut 0,005
REL_TOL = 0.01
ABS_TOL = 0.005
# ------------------------------------------------------------------ Einlesen
def lade_raw(pfad):
"""Rohdaten laden. AutoLISP schreibt im ANSI-Codepage -> cp1252-Rueckfall."""
with open(pfad, "rb") as f:
roh = f.read()
try:
text = roh.decode("utf-8")
except UnicodeDecodeError:
text = roh.decode("cp1252")
return json.loads(text)
def _zahl(v, vorgabe=0.0):
return float(v) if isinstance(v, (int, float)) else vorgabe
def _m(mm, stellen=3):
return round(_zahl(mm) / 1000.0, stellen)
def _m2(mm2, stellen=1):
return round(_zahl(mm2) / 1e6, stellen)
# ------------------------------------------------------------------ Aggregation
class Sammler:
"""Werte mit Herkunft sammeln: setze() schreibt in das Overlay und die Provenienz."""
def __init__(self):
self.provenance = OrderedDict()
self.warnungen = []
def herkunft(self, pfad, wert, quelle, formel):
self.provenance[pfad] = {"wert": wert, "quelle": quelle, "formel": formel}
return wert
def warnung(self, text):
if text not in self.warnungen:
self.warnungen.append(text)
def _haeufigster(werte, pfad, s):
"""Haeufigster Wert; bei mehreren verschiedenen Werten eine Warnung."""
werte = [w for w in werte if w is not None]
if not werte:
return None
zaehlung = Counter(werte)
wert, _ = zaehlung.most_common(1)[0]
if len(zaehlung) > 1:
s.warnung("%s: Bereiche mit unterschiedlichen Werten %s - haeufigster Wert %s uebernommen"
% (pfad, sorted(zaehlung), wert))
return wert
def _haengeebenen(reihen):
"""Laenge je Haengeebene (Schienenhoehe, Warenlaenge) summieren.
Jede Reihe traegt ihre Laenge in jede ihrer Haengeebenen ein -
rack_length_m ist die Summe aller Reihen der Ebene.
"""
summen = OrderedDict()
for r in reihen:
for e in r.get("haengeebenen") or []:
key = (_zahl(e.get("schienenhoehe_mm")), _zahl(e.get("warenlaenge_mm")))
summen[key] = summen.get(key, 0.0) + _zahl(r.get("laenge_mm"))
return [{"rail_height_m": _m(h), "rack_length_m": round(l / 1000.0, 1), "goods_length_m": _m(w)}
for (h, w), l in sorted(summen.items(), key=lambda kv: -kv[0][0])]
def aggregiere(raw):
"""Rohdaten -> (overlay, sammler). overlay enthaelt nur Zeichnungswerte."""
s = Sammler()
for w in raw.get("warnungen") or []:
s.warnung(w)
belag_yaml = raw.get("belag_yaml") or {}
reihen_als_gitter = bool(raw.get("reihen_als_gitterrost"))
geschosse = sorted(raw.get("geschosse") or [],
key=lambda g: (_zahl(g.get("hoehe_belag_mm")), g.get("nr") or 0))
bereiche = raw.get("bereiche") or []
alle_reihen = list(raw.get("reihen") or [])
racks = raw.get("goh_racks") or []
gaenge = raw.get("gaenge") or []
# goh_rack-Bloecke zaehlen wie Regalreihen (Herkunft wird mitgefuehrt)
for r in racks:
alle_reihen.append(dict(r, quelle="goh_rack"))
ohne_geschoss = [r for r in alle_reihen if r.get("geschoss") is None]
if ohne_geschoss and geschosse:
s.warnung("%d Regalreihe(n) ohne Geschoss - nicht in levels enthalten" % len(ohne_geschoss))
overlay = OrderedDict()
# ---------------------------------------------------------- layout
layout = OrderedDict()
if bereiche:
quelle = "LG_BEREICH (%d Bereich(e))" % len(bereiche)
felder = [
("aisle_width_m", "gangbreite_mm", lambda v: _m(v), "GANGBREITE_mm / 1000"),
("rack_rows_per_aisle", "reihen_je_gang", int, "REIHEN_JE_GANG"),
("rack_depth_m", "regaltiefe_mm", lambda v: _m(v), "REGALTIEFE_mm / 1000"),
("trolley_rails_per_aisle", "schienen_je_gang", int, "SCHIENEN_JE_GANG"),
]
for feld, key, conv, formel in felder:
w = _haeufigster([conv(b[key]) for b in bereiche if b.get(key) is not None],
"layout." + feld, s)
if w is not None:
layout[feld] = s.herkunft("layout." + feld, w, quelle, formel)
elif alle_reihen:
w = _haeufigster([_m(r["tiefe_mm"]) for r in alle_reihen if r.get("tiefe_mm") is not None],
"layout.rack_depth_m", s)
if w is not None:
layout["rack_depth_m"] = s.herkunft("layout.rack_depth_m", w,
"Regalreihen (Tiefe)", "haeufigste Reihentiefe")
# Gaenge je Geschoss: Anzahl und Summe der Laengen (nur Gaenge zwischen den
# Reihen, ART=gang - auch manuelle). Randgaenge entlang der Kontur zaehlen
# nicht als aisle, stecken aber im Belag (walkway) und werden im Detail genannt.
gang_je_gs = {}
rand_je_gs = {}
for g in gaenge:
if g.get("art") == "gang":
n, l = gang_je_gs.get(g.get("geschoss"), (0, 0.0))
gang_je_gs[g.get("geschoss")] = (n + 1, l + _zahl(g.get("laenge_mm")))
elif g.get("art") == "randgang":
rand_je_gs[g.get("geschoss")] = rand_je_gs.get(g.get("geschoss"), 0.0) + _zahl(g.get("laenge_mm"))
if gang_je_gs:
gs_max = max(gang_je_gs, key=lambda k: gang_je_gs[k][1])
detail = ", ".join("G%s: %d / %.1f m" % (k, v[0], v[1] / 1000.0)
for k, v in sorted(gang_je_gs.items(), key=lambda kv: str(kv[0])))
if rand_je_gs:
detail += "; ohne Randgaenge " + ", ".join(
"G%s: %.1f m" % (k, v / 1000.0)
for k, v in sorted(rand_je_gs.items(), key=lambda kv: str(kv[0])))
layout["aisles"] = s.herkunft("layout.aisles", gang_je_gs[gs_max][0],
"LG_GANG_* (ART=gang)",
"Anzahl Hauptgaenge, Geschoss mit der groessten Ganglaenge (%s)" % detail)
layout["aisle_length_m"] = s.herkunft("layout.aisle_length_m",
round(gang_je_gs[gs_max][1] / 1000.0, 1),
"LG_GANG_* (ART=gang)",
"Summe Hauptganglaengen je Ebene, groesste (%s)" % detail)
bboxen = [g["kontur_bbox"] for g in geschosse if g.get("kontur_bbox")]
if bboxen:
xmin = min(b[0] for b in bboxen)
ymin = min(b[1] for b in bboxen)
xmax = max(b[2] for b in bboxen)
ymax = max(b[3] for b in bboxen)
dx, dy = xmax - xmin, ymax - ymin
layout["block_length_m"] = s.herkunft("layout.block_length_m", round(max(dx, dy) / 1000.0, 2),
"Belagskonturen LG_GESCHOSS",
"achsparallele Huelle aller Konturen, laengere Seite")
layout["block_width_m"] = s.herkunft("layout.block_width_m", round(min(dx, dy) / 1000.0, 2),
"Belagskonturen LG_GESCHOSS",
"achsparallele Huelle aller Konturen, kuerzere Seite")
if layout:
overlay["layout"] = layout
# ---------------------------------------------------------- levels
levels = []
for i, g in enumerate(geschosse):
p = "levels[%d]" % i
nr = g.get("nr")
lv = OrderedDict()
if g.get("bezeichnung"):
lv["remark"] = s.herkunft(p + ".remark", g["bezeichnung"], "LG_GESCHOSS %s" % nr, "BEZEICHNUNG")
lv["floor_elevation_m"] = s.herkunft(p + ".floor_elevation_m", _m(g.get("hoehe_belag_mm")),
"LG_GESCHOSS %s" % nr, "HOEHE_BELAG_mm / 1000")
if g.get("lichte_hoehe_mm") is not None:
lv["clear_height_m"] = s.herkunft(p + ".clear_height_m", _m(g["lichte_hoehe_mm"]),
"LG_GESCHOSS %s" % nr, "LICHTE_HOEHE_mm (Vorgabe)")
elif i + 1 < len(geschosse):
nx = geschosse[i + 1]
lichte = _zahl(nx.get("hoehe_belag_mm")) - _zahl(nx.get("konstr_hoehe_mm")) \
- _zahl(g.get("hoehe_belag_mm"))
lv["clear_height_m"] = s.herkunft(
p + ".clear_height_m", _m(lichte), "LG_GESCHOSS %s / %s" % (nr, nx.get("nr")),
"Belag darueber (%s) - Konstruktionshoehe darueber (%s) - eigener Belag (%s), mm"
% (nx.get("hoehe_belag_mm"), nx.get("konstr_hoehe_mm"), g.get("hoehe_belag_mm")))
else:
s.warnung("Geschoss %s (oberstes): clear_height_m nicht berechenbar - LICHTE_HOEHE_mm setzen" % nr)
reihen = [r for r in alle_reihen if r.get("geschoss") == nr]
hl = _haengeebenen(reihen)
if hl:
lv["hanging_levels"] = hl
n_rack = sum(1 for r in reihen if r.get("quelle") == "goh_rack")
for j, e in enumerate(hl):
s.herkunft("%s.hanging_levels[%d].rack_length_m" % (p, j), e["rack_length_m"],
"%d Regalreihe(n) Geschoss %s (davon %d goh_rack, Haengeebenen aus Vorgabe)"
% (len(reihen), nr, n_rack),
"Summe LAENGE aller Reihen mit Schienenhoehe %s / Warenlaenge %s"
% (e["rail_height_m"], e["goods_length_m"]))
spec = belag_yaml.get(g.get("belag_art") or "", g.get("belag_art"))
if spec: # Buehne (kein Hallenboden)
gs_gaenge = [x for x in gaenge if x.get("geschoss") == nr]
belag = sum(_zahl(x.get("laenge_mm")) * _zahl(x.get("breite_mm"))
for x in gs_gaenge if x.get("belag") != "gitterrost")
gitter = sum(_zahl(x.get("laenge_mm")) * _zahl(x.get("breite_mm"))
for x in gs_gaenge if x.get("belag") == "gitterrost")
reihen_fl = sum(_zahl(r.get("laenge_mm")) * _zahl(r.get("tiefe_mm")) for r in reihen)
if reihen_als_gitter:
gitter += reihen_fl
if gs_gaenge:
formel = "Summe LAENGE x BREITE der Gaenge/Quergaenge mit Belag"
quelle = "LG_GANG_* Geschoss %s, %d Flaeche(n)" % (nr, len(gs_gaenge))
else:
# Ohne Gangflaechen: Belag = Kontur minus Reihengrundflaeche
belag = _zahl(g.get("kontur_flaeche_mm2")) - reihen_fl
formel = "Konturflaeche - Grundflaeche der Regalreihen (keine LG_GANG-Flaechen)"
quelle = "Belagskontur Geschoss %s" % nr
lv["walkway"] = OrderedDict([
("area_m2", s.herkunft(p + ".walkway.area_m2", _m2(belag), quelle, formel)),
("spec", s.herkunft(p + ".walkway.spec", spec, "LG_GESCHOSS %s" % nr, "BELAG_ART")),
])
lv["grating"] = s.herkunft(
p + ".grating", _m2(gitter), "Geschoss %s" % nr,
"Gaenge mit Belag gitterrost" + (" + Grundflaeche der Regalreihen (Laenge x Tiefe)"
if reihen_als_gitter else ""))
levels.append(lv)
if levels:
overlay["levels"] = levels
return overlay, s
# ------------------------------------------------------------------ Abgleich
def _gleich(a, b):
if isinstance(a, bool) or isinstance(b, bool):
return a == b
if isinstance(a, (int, float)) and isinstance(b, (int, float)):
return abs(a - b) <= max(ABS_TOL, REL_TOL * max(abs(a), abs(b)))
return str(a).strip() == str(b).strip()
def _blaetter(obj, pfad=""):
"""(pfad, wert) aller Blattwerte; Listen per Index, _-Schluessel ausgenommen."""
if isinstance(obj, dict):
for k, v in obj.items():
if str(k).startswith("_"):
continue
yield from _blaetter(v, "%s.%s" % (pfad, k) if pfad else str(k))
elif isinstance(obj, list):
for i, v in enumerate(obj):
yield from _blaetter(v, "%s[%d]" % (pfad, i))
else:
yield pfad, obj
def _hole(obj, pfad):
"""Wert unter pfad ("levels[1].walkway.area_m2"); (gefunden, wert)."""
teile = []
for t in pfad.split("."):
while "[" in t:
name, rest = t.split("[", 1)
if name:
teile.append(name)
idx, t = rest.split("]", 1)
teile.append(int(idx))
if t:
teile.append(t)
cur = obj
for t in teile:
if isinstance(t, int):
if not isinstance(cur, list) or t >= len(cur):
return False, None
cur = cur[t]
else:
if not isinstance(cur, dict) or t not in cur:
return False, None
cur = cur[t]
return True, cur
def abgleich(overlay, hand):
"""Overlay gegen Hand-YAML: (konflikte, fuellt).
konflikte = Hand hat einen anderen Wert (Hand gewinnt beim Merge),
fuellt = Hand-Feld fehlt oder ist null (Zeichnung fuellt es).
"""
konflikte, fuellt = [], []
for pfad, wert in _blaetter(overlay):
gefunden, h = _hole(hand, pfad)
if not gefunden or h is None:
fuellt.append({"pfad": pfad, "zeichnung": wert})
elif not isinstance(h, (dict, list)) and not _gleich(h, wert):
k = {"pfad": pfad, "hand": h, "zeichnung": wert}
if isinstance(h, (int, float)) and isinstance(wert, (int, float)) and h:
k["abweichung_prozent"] = round((wert - h) / abs(h) * 100.0, 1)
konflikte.append(k)
n_hand = len(hand.get("levels") or []) if isinstance(hand, dict) else 0
n_zg = len(overlay.get("levels") or [])
if n_hand and n_zg and n_hand != n_zg:
konflikte.append({"pfad": "levels", "hand": "%d Ebenen" % n_hand,
"zeichnung": "%d Ebenen" % n_zg})
return konflikte, fuellt
# ------------------------------------------------------------------ Ausgabe
def _yaml_text(daten):
def _dict_repr(dumper, d):
return dumper.represent_mapping("tag:yaml.org,2002:map", d.items())
yaml.SafeDumper.add_representer(OrderedDict, _dict_repr)
return yaml.safe_dump(daten, sort_keys=False, allow_unicode=True,
default_flow_style=False, width=120)
def baue_ausgabe(raw, hand=None):
overlay, s = aggregiere(raw)
ausgabe = OrderedDict(overlay)
ausgabe["_provenance"] = s.provenance
konflikte, fuellt = abgleich(overlay, hand) if hand is not None else ([], [])
ausgabe["_conflicts"] = konflikte
ausgabe["_warnings"] = s.warnungen
return ausgabe, fuellt
def schreibe_yaml(pfad, ausgabe, raw):
kopf = ("# Overlay aus der Zeichnung - erzeugt von lib/lager_yaml_export.py\n"
"# Zeichnung: %s (%s)\n"
"# Merge im RFQ-Programm: Hand-YAML gewinnt, diese Werte fuellen nur fehlende/null-Felder.\n"
"# _provenance/_conflicts/_warnings sind Zusatzinformation und kein Teil des RFQ-Schemas.\n"
% (raw.get("zeichnung", "?"), raw.get("erzeugt", "?")))
with open(pfad, "w", encoding="utf-8", newline="\n") as f:
f.write(kopf)
f.write(_yaml_text(ausgabe))
def schreibe_nachweis(pfad, ausgabe, fuellt, raw):
e = html.escape
def tabelle(kopf, zeilen):
z = "".join("<tr>%s</tr>" % "".join("<td>%s</td>" % e(str(c)) for c in r) for r in zeilen)
return "<table><thead><tr>%s</tr></thead><tbody>%s</tbody></table>" % (
"".join("<th>%s</th>" % e(k) for k in kopf), z or "<tr><td colspan=9>-</td></tr>")
teile = [
"<!doctype html><html lang='de'><head><meta charset='utf-8'><title>Lager-Nachweis</title>",
"<style>body{font-family:Segoe UI,Arial,sans-serif;margin:24px;color:#222}"
"table{border-collapse:collapse;margin:8px 0 24px}td,th{border:1px solid #bbb;padding:4px 8px;"
"text-align:left;vertical-align:top}th{background:#eee}.w{color:#a40}</style></head><body>",
"<h1>Lager-Nachweis (Mengen)</h1>",
"<p>Zeichnung: %s<br>Erzeugt: %s<br>Regelprofil: %s</p>" % (
e(str(raw.get("zeichnung"))), e(str(raw.get("erzeugt"))), e(str(raw.get("land")))),
"<p class='w'>Interner Nachweis - die Pruefung gegen ASR/DGUV/EN ISO 14122 folgt in Phase 4.</p>",
"<h2>Warnungen</h2>",
"<ul>%s</ul>" % ("".join("<li class='w'>%s</li>" % e(w) for w in ausgabe["_warnings"]) or "<li>keine</li>"),
"<h2>Werte aus der Zeichnung</h2>",
tabelle(["Feld", "Wert", "Quelle", "Formel"],
[(k, v["wert"], v["quelle"], v["formel"]) for k, v in ausgabe["_provenance"].items()]),
"<h2>Konflikte mit der Hand-YAML (Hand gewinnt)</h2>",
tabelle(["Feld", "Hand", "Zeichnung", "Abweichung %"],
[(k["pfad"], k["hand"], k["zeichnung"], k.get("abweichung_prozent", ""))
for k in ausgabe["_conflicts"]]),
"<h2>Von der Zeichnung gefuellte Felder (Hand leer/null)</h2>",
tabelle(["Feld", "Zeichnung"], [(f["pfad"], f["zeichnung"]) for f in fuellt]),
"</body></html>",
]
with open(pfad, "w", encoding="utf-8", newline="\n") as f:
f.write("\n".join(teile))
def main(argv=None):
ap = argparse.ArgumentParser(description="Lager-Rohdaten -> RFQ-Overlay-YAML")
ap.add_argument("raw", help="<zeichnung>_lager_raw.json aus LAGER_EXPORT")
ap.add_argument("yaml", help="Ziel: <projekt>_drawing.yaml")
ap.add_argument("--hand", help="Hand-YAML fuer den Abgleich (Konfliktliste)")
ap.add_argument("--nachweis", help="Ziel: interner Nachweis als HTML")
a = ap.parse_args(argv)
try:
raw = lade_raw(a.raw)
except (OSError, ValueError) as ex:
print("FEHLER: Rohdaten nicht lesbar: %s" % ex, file=sys.stderr)
return 2
hand = None
if a.hand:
try:
with open(a.hand, encoding="utf-8") as f:
hand = yaml.safe_load(f) or {}
except (OSError, yaml.YAMLError) as ex:
print("FEHLER: Hand-YAML nicht lesbar: %s" % ex, file=sys.stderr)
return 2
ausgabe, fuellt = baue_ausgabe(raw, hand)
schreibe_yaml(a.yaml, ausgabe, raw)
if a.nachweis:
schreibe_nachweis(a.nachweis, ausgabe, fuellt, raw)
print("Overlay: %s (%d Werte, %d Konflikt(e), %d Warnung(en))"
% (a.yaml, len(ausgabe["_provenance"]), len(ausgabe["_conflicts"]), len(ausgabe["_warnings"])))
return 0
if __name__ == "__main__":
sys.exit(main())
+305 -167
View File
@@ -644,289 +644,355 @@
<Command>^C^C(ssg-ensure "GeruestInsert") GERUEST_TRAEGER_BESCHRIFTEN</Command> <Command>^C^C(ssg-ensure "GeruestInsert") GERUEST_TRAEGER_BESCHRIFTEN</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00141">
<Macro>
<Name>Geschoss anlegen</Name>
<Command>^C^C(ssg-ensure "LagerInsert") LG_GESCHOSS</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00142"> <MenuMacro UID="MM_00142">
<Macro>
<Name>Aktives Geschoss</Name>
<Command>^C^C(ssg-ensure "LagerInsert") LG_GESCHOSS_AKTIV</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00143">
<Macro>
<Name>Geschoss kopieren</Name>
<Command>^C^C(ssg-ensure "LagerInsert") LG_GESCHOSS_KOPIEREN</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00145">
<Macro>
<Name>Lagerbereich (Auto-Fuellung)</Name>
<Command>^C^C(ssg-ensure "LagerInsert") LG_BEREICH</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00146">
<Macro>
<Name>Regalreihen einzeln</Name>
<Command>^C^C(ssg-ensure "LagerInsert") LG_REIHE</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00147">
<Macro>
<Name>Gaenge einzeln</Name>
<Command>^C^C(ssg-ensure "LagerInsert") LG_GANG</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00148">
<Macro>
<Name>Bearbeiten</Name>
<Command>^C^C(ssg-ensure "LagerInsert") LG_EDIT</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00150">
<Macro>
<Name>Regale zaehlen (goh_rack)</Name>
<Command>^C^C(ssg-ensure "LagerInsert") LG_REGALE</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00151">
<Macro>
<Name>Regelprofil anzeigen</Name>
<Command>^C^C(ssg-ensure "LagerPruefung") LG_REGELN</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00152">
<Macro>
<Name>Hand-YAML fuer Abgleich</Name>
<Command>^C^C(ssg-ensure "LagerInsert") LAGER_HAND_YAML</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00153">
<Macro>
<Name>Export RFQ-YAML</Name>
<Command>^C^C(ssg-ensure "LagerInsert") LAGER_EXPORT</Command>
</Macro>
</MenuMacro>
<MenuMacro UID="MM_00156">
<Macro> <Macro>
<Name>Marker einfuegen</Name> <Name>Marker einfuegen</Name>
<Command>^C^C(ssg-ensure "Tro_Edit") TRO_INSERT</Command> <Command>^C^C(ssg-ensure "Tro_Edit") TRO_INSERT</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00143"> <MenuMacro UID="MM_00157">
<Macro> <Macro>
<Name>Marker bearbeiten</Name> <Name>Marker bearbeiten</Name>
<Command>^C^C(ssg-ensure "Tro_Edit") TRO_EDIT</Command> <Command>^C^C(ssg-ensure "Tro_Edit") TRO_EDIT</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00144"> <MenuMacro UID="MM_00158">
<Macro> <Macro>
<Name>Bearbeiten (Auto)</Name> <Name>Bearbeiten (Auto)</Name>
<Command>^C^C(ssg-ensure "SSG_LIB_Commands") SSG_BLOCKEDIT</Command> <Command>^C^C(ssg-ensure "SSG_LIB_Commands") SSG_BLOCKEDIT</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00146"> <MenuMacro UID="MM_00160">
<Macro> <Macro>
<Name>Verbindung einfuegen</Name> <Name>Verbindung einfuegen</Name>
<Command>^C^C(ssg-ensure "Connection_Edit") CONNECTION_INSERT</Command> <Command>^C^C(ssg-ensure "Connection_Edit") CONNECTION_INSERT</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00147"> <MenuMacro UID="MM_00161">
<Macro> <Macro>
<Name>Verbindung bearbeiten</Name> <Name>Verbindung bearbeiten</Name>
<Command>^C^C(ssg-ensure "Connection_Edit") CONNECTION_EDIT</Command> <Command>^C^C(ssg-ensure "Connection_Edit") CONNECTION_EDIT</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00149"> <MenuMacro UID="MM_00163">
<Macro> <Macro>
<Name>Eingang</Name> <Name>Eingang</Name>
<Command>^C^C(ssg-ensure "SSG_LIB_Commands") ILS_EINGANG_INSERT</Command> <Command>^C^C(ssg-ensure "SSG_LIB_Commands") ILS_EINGANG_INSERT</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00150"> <MenuMacro UID="MM_00164">
<Macro> <Macro>
<Name>Ausgang</Name> <Name>Ausgang</Name>
<Command>^C^C(ssg-ensure "SSG_LIB_Commands") ILS_AUSGANG_INSERT</Command> <Command>^C^C(ssg-ensure "SSG_LIB_Commands") ILS_AUSGANG_INSERT</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00151"> <MenuMacro UID="MM_00165">
<Macro> <Macro>
<Name>SPS Schliessen</Name> <Name>SPS Schliessen</Name>
<Command>^C^C</Command> <Command>^C^C</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00153"> <MenuMacro UID="MM_00167">
<Macro> <Macro>
<Name>Export Sivas</Name> <Name>Export Sivas</Name>
<Command>^C^C(ssg-ensure "export") EXPORTSIVAS</Command> <Command>^C^C(ssg-ensure "export") EXPORTSIVAS</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00154"> <MenuMacro UID="MM_00168">
<Macro> <Macro>
<Name>Export CSV</Name> <Name>Export CSV</Name>
<Command>^C^C(ssg-ensure "export") EXPORTCSV</Command> <Command>^C^C(ssg-ensure "export") EXPORTCSV</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00156"> <MenuMacro UID="MM_00170">
<Macro> <Macro>
<Name>Umschalten 2D und 3D</Name> <Name>Umschalten 2D und 3D</Name>
<Command>^C^C(ssg-ensure "SSG_LIB_Commands") SSG_DIM_SWITCH</Command> <Command>^C^C(ssg-ensure "SSG_LIB_Commands") SSG_DIM_SWITCH</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00157"> <MenuMacro UID="MM_00171">
<Macro> <Macro>
<Name>Alle Bloecke auf 2D</Name> <Name>Alle Bloecke auf 2D</Name>
<Command>^C^C(ssg-ensure "SSG_LIB_Commands") SSG_DIM_ALL_2D</Command> <Command>^C^C(ssg-ensure "SSG_LIB_Commands") SSG_DIM_ALL_2D</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00158"> <MenuMacro UID="MM_00172">
<Macro> <Macro>
<Name>Alle Bloecke auf 3D</Name> <Name>Alle Bloecke auf 3D</Name>
<Command>^C^C(ssg-ensure "SSG_LIB_Commands") SSG_DIM_ALL_3D</Command> <Command>^C^C(ssg-ensure "SSG_LIB_Commands") SSG_DIM_ALL_3D</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00159"> <MenuMacro UID="MM_00173">
<Macro> <Macro>
<Name>Sprache</Name> <Name>Sprache</Name>
<Command>^C^C(ssg-ensure "SSG_LIB_Commands") SSG_SPRACHE</Command> <Command>^C^C(ssg-ensure "SSG_LIB_Commands") SSG_SPRACHE</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00160"> <MenuMacro UID="MM_00174">
<Macro> <Macro>
<Name>Planquadrate an/aus</Name> <Name>Planquadrate an/aus</Name>
<Command>^C^C(ssg-ensure "DrawPlanquadrate") PLANQUADRATE_TOGGLE</Command> <Command>^C^C(ssg-ensure "DrawPlanquadrate") PLANQUADRATE_TOGGLE</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00161"> <MenuMacro UID="MM_00175">
<Macro> <Macro>
<Name>CSV-Export</Name> <Name>CSV-Export</Name>
<Command>^C^C(ssg-ensure "export") CSV_EXPORT_KONFIG</Command> <Command>^C^C(ssg-ensure "export") CSV_EXPORT_KONFIG</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00163"> <MenuMacro UID="MM_00177">
<Macro> <Macro>
<Name>Bloecke zaehlen</Name> <Name>Bloecke zaehlen</Name>
<Command>^C^C(ssg-ensure "ssg_blocktools") SSG_BLOCK_COUNT</Command> <Command>^C^C(ssg-ensure "ssg_blocktools") SSG_BLOCK_COUNT</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00164"> <MenuMacro UID="MM_00178">
<Macro> <Macro>
<Name>Bloecke loeschen</Name> <Name>Bloecke loeschen</Name>
<Command>^C^C(ssg-ensure "ssg_blocktools") SSG_BLOCK_DELETE</Command> <Command>^C^C(ssg-ensure "ssg_blocktools") SSG_BLOCK_DELETE</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00165"> <MenuMacro UID="MM_00179">
<Macro> <Macro>
<Name>Attribute ersetzen</Name> <Name>Attribute ersetzen</Name>
<Command>^C^C(ssg-ensure "ssg_blocktools") SSG_ATTR_ERSETZEN</Command> <Command>^C^C(ssg-ensure "ssg_blocktools") SSG_ATTR_ERSETZEN</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00167"> <MenuMacro UID="MM_00181">
<Macro> <Macro>
<Name>Text ersetzen</Name> <Name>Text ersetzen</Name>
<Command>^C^C(ssg-ensure "ssg_texttools") SSG_TEXT_ERSETZEN</Command> <Command>^C^C(ssg-ensure "ssg_texttools") SSG_TEXT_ERSETZEN</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00168"> <MenuMacro UID="MM_00182">
<Macro> <Macro>
<Name>Text parallel beschriften</Name> <Name>Text parallel beschriften</Name>
<Command>^C^C(ssg-ensure "ssg_texttools") SSG_TEXT_PARALLEL</Command> <Command>^C^C(ssg-ensure "ssg_texttools") SSG_TEXT_PARALLEL</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00169"> <MenuMacro UID="MM_00183">
<Macro> <Macro>
<Name>Texthoehe aendern</Name> <Name>Texthoehe aendern</Name>
<Command>^C^C(ssg-ensure "ssg_texttools") SSG_TEXT_HOEHE</Command> <Command>^C^C(ssg-ensure "ssg_texttools") SSG_TEXT_HOEHE</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00170"> <MenuMacro UID="MM_00184">
<Macro> <Macro>
<Name>Textstil aendern</Name> <Name>Textstil aendern</Name>
<Command>^C^C(ssg-ensure "ssg_texttools") SSG_TEXT_STIL</Command> <Command>^C^C(ssg-ensure "ssg_texttools") SSG_TEXT_STIL</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00172"> <MenuMacro UID="MM_00186">
<Macro> <Macro>
<Name>Faehnchen + Stueckliste aus Zeichnung</Name> <Name>Faehnchen + Stueckliste aus Zeichnung</Name>
<Command>^C^C(ssg-ensure "ssg_stueckliste") SSG_FAEHNCHEN_STUECKLISTE</Command> <Command>^C^C(ssg-ensure "ssg_stueckliste") SSG_FAEHNCHEN_STUECKLISTE</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00173"> <MenuMacro UID="MM_00187">
<Macro> <Macro>
<Name>Din Formate</Name> <Name>Din Formate</Name>
<Command>^C^C(ssg-ensure "DinFormate") DIN_FORMATE</Command> <Command>^C^C(ssg-ensure "DinFormate") DIN_FORMATE</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00175"> <MenuMacro UID="MM_00189">
<Macro> <Macro>
<Name>alle Tests ausfuehren und exportieren</Name> <Name>alle Tests ausfuehren und exportieren</Name>
<Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_run_all.lsp")) (c:SSG_RUN_ALL_TESTS_EXPORT)</Command> <Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_run_all.lsp")) (c:SSG_RUN_ALL_TESTS_EXPORT)</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00176"> <MenuMacro UID="MM_00190">
<Macro> <Macro>
<Name>Tests ausfuehren und stehen lassen</Name> <Name>Tests ausfuehren und stehen lassen</Name>
<Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_run_all.lsp")) (c:SSG_RUN_ALL_TESTS_OFFEN)</Command> <Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_run_all.lsp")) (c:SSG_RUN_ALL_TESTS_OFFEN)</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00177"> <MenuMacro UID="MM_00191">
<Macro> <Macro>
<Name>Export CSV/Sivas ALL</Name> <Name>Export CSV/Sivas ALL</Name>
<Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_export_all.lsp")) (c:TEST_EXPORT_ALL)</Command> <Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_export_all.lsp")) (c:TEST_EXPORT_ALL)</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00179"> <MenuMacro UID="MM_00193">
<Macro> <Macro>
<Name>Kreisel</Name> <Name>Kreisel</Name>
<Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_kreisel.lsp")) (c:TEST_KREISEL)</Command> <Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_kreisel.lsp")) (c:TEST_KREISEL)</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00180"> <MenuMacro UID="MM_00194">
<Macro> <Macro>
<Name>Foerderer</Name> <Name>Foerderer</Name>
<Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_foerderer.lsp")) (c:TEST_FOERDERER)</Command> <Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_foerderer.lsp")) (c:TEST_FOERDERER)</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00181"> <MenuMacro UID="MM_00195">
<Macro> <Macro>
<Name>Foerderer-Linienzug</Name> <Name>Foerderer-Linienzug</Name>
<Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_linienzug.lsp")) (c:TEST_LINIENZUG) (c:TEST_LINIENZUG_STUFE2)</Command> <Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_linienzug.lsp")) (c:TEST_LINIENZUG) (c:TEST_LINIENZUG_STUFE2)</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00182"> <MenuMacro UID="MM_00196">
<Macro> <Macro>
<Name>Omniflo</Name> <Name>Omniflo</Name>
<Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_omniflo.lsp")) (c:TEST_OMNIFLO)</Command> <Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_omniflo.lsp")) (c:TEST_OMNIFLO)</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00183"> <MenuMacro UID="MM_00197">
<Macro> <Macro>
<Name>Omniflo Strecke</Name> <Name>Omniflo Strecke</Name>
<Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_omniflo_strecke.lsp")) (c:TEST_OMNIFLO_STRECKE)</Command> <Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_omniflo_strecke.lsp")) (c:TEST_OMNIFLO_STRECKE)</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00184"> <MenuMacro UID="MM_00198">
<Macro> <Macro>
<Name>Gefaellestrecke</Name> <Name>Gefaellestrecke</Name>
<Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_gefaellestrecke.lsp")) (c:TEST_GEFAELLESTRECKE)</Command> <Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_gefaellestrecke.lsp")) (c:TEST_GEFAELLESTRECKE)</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00185"> <MenuMacro UID="MM_00199">
<Macro> <Macro>
<Name>Mubea Gesamtmodell</Name> <Name>Mubea Gesamtmodell</Name>
<Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_mubea.lsp")) (c:TEST_MUBEA)</Command> <Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_mubea.lsp")) (c:TEST_MUBEA)</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00186"> <MenuMacro UID="MM_00200">
<Macro> <Macro>
<Name>HundM05 Kreisel-Abschnitt</Name> <Name>HundM05 Kreisel-Abschnitt</Name>
<Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_hundm05.lsp")) (c:TEST_HUNDM05)</Command> <Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_hundm05.lsp")) (c:TEST_HUNDM05)</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00187"> <MenuMacro UID="MM_00201">
<Macro> <Macro>
<Name>Dematic/Arvato Flowpuffer (Vorlage)</Name> <Name>Dematic/Arvato Flowpuffer (Vorlage)</Name>
<Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_dematic_arvato.lsp")) (c:TEST_DEMATIC_ARVATO)</Command> <Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_dematic_arvato.lsp")) (c:TEST_DEMATIC_ARVATO)</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00188"> <MenuMacro UID="MM_00202">
<Macro> <Macro>
<Name>ILS 3.0 Showroom (Vorlage)</Name> <Name>ILS 3.0 Showroom (Vorlage)</Name>
<Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_showroom_ils.lsp")) (c:TEST_SHOWROOM_ILS)</Command> <Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_showroom_ils.lsp")) (c:TEST_SHOWROOM_ILS)</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00189"> <MenuMacro UID="MM_00203">
<Macro> <Macro>
<Name>Testanlage test_system</Name> <Name>Testanlage test_system</Name>
<Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_test_system.lsp")) (c:TEST_TEST_SYSTEM)</Command> <Command>^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_test_system.lsp")) (c:TEST_TEST_SYSTEM)</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00190"> <MenuMacro UID="MM_00204">
<Macro> <Macro>
<Name>KS 2D/3D pruefen (CSV)</Name> <Name>KS 2D/3D pruefen (CSV)</Name>
<Command>^C^C(load (strcat (getenv "DXFMAKRO") "/Lisp/ks_pruefung.lsp")) KS_PRUEFEN</Command> <Command>^C^C(load (strcat (getenv "DXFMAKRO") "/Lisp/ks_pruefung.lsp")) KS_PRUEFEN</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00202"> <MenuMacro UID="MM_00216">
<Macro> <Macro>
<Name>Kreisel bearbeiten</Name> <Name>Kreisel bearbeiten</Name>
<Command>(ssg-ensure "KreiselInsert") KreiselEdit</Command> <Command>(ssg-ensure "KreiselInsert") KreiselEdit</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00203"> <MenuMacro UID="MM_00217">
<Macro> <Macro>
<Name>Eckrad bearbeiten</Name> <Name>Eckrad bearbeiten</Name>
<Command>(ssg-ensure "KreiselInsert") EckradEdit</Command> <Command>(ssg-ensure "KreiselInsert") EckradEdit</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00204"> <MenuMacro UID="MM_00218">
<Macro> <Macro>
<Name>Omniflo Element bearbeiten</Name> <Name>Omniflo Element bearbeiten</Name>
<Command>(ssg-ensure "OmniModulInsert") OMNI_EDIT</Command> <Command>(ssg-ensure "OmniModulInsert") OMNI_EDIT</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00205"> <MenuMacro UID="MM_00219">
<Macro> <Macro>
<Name>Gefaellestrecke bearbeiten</Name> <Name>Gefaellestrecke bearbeiten</Name>
<Command>(ssg-ensure "Gefaellestrecke") GEFAELLESTRECKE_EDIT</Command> <Command>(ssg-ensure "Gefaellestrecke") GEFAELLESTRECKE_EDIT</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00206"> <MenuMacro UID="MM_00220">
<Macro> <Macro>
<Name>VarioFoerderer bearbeiten</Name> <Name>VarioFoerderer bearbeiten</Name>
<Command>(ssg-ensure "VarioFoerderer") VARIOFOERDERER_EDIT</Command> <Command>(ssg-ensure "VarioFoerderer") VARIOFOERDERER_EDIT</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00207"> <MenuMacro UID="MM_00221">
<Macro> <Macro>
<Name>TRO-Marker bearbeiten</Name> <Name>TRO-Marker bearbeiten</Name>
<Command>(ssg-ensure "Tro_Edit") TRO_EDIT</Command> <Command>(ssg-ensure "Tro_Edit") TRO_EDIT</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00208"> <MenuMacro UID="MM_00222">
<Macro> <Macro>
<Name>Verbindung bearbeiten</Name> <Name>Verbindung bearbeiten</Name>
<Command>(ssg-ensure "Connection_Edit") CONNECTION_EDIT</Command> <Command>(ssg-ensure "Connection_Edit") CONNECTION_EDIT</Command>
</Macro> </Macro>
</MenuMacro> </MenuMacro>
<MenuMacro UID="MM_00210"> <MenuMacro UID="MM_00224">
<Macro> <Macro>
<Name>SSG Bearbeiten (Auto)</Name> <Name>SSG Bearbeiten (Auto)</Name>
<Command>(ssg-ensure "SSG_LIB_Commands") SSG_BLOCKEDIT</Command> <Command>(ssg-ensure "SSG_LIB_Commands") SSG_BLOCKEDIT</Command>
@@ -945,10 +1011,11 @@
<PopMenuRef pUID="PM_00039"/> <PopMenuRef pUID="PM_00039"/>
<PopMenuRef pUID="PM_00132"/> <PopMenuRef pUID="PM_00132"/>
<PopMenuRef pUID="PM_00140"/> <PopMenuRef pUID="PM_00140"/>
<PopMenuRef pUID="PM_00152"/> <PopMenuRef pUID="PM_00154"/>
<PopMenuRef pUID="PM_00155"/> <PopMenuRef pUID="PM_00166"/>
<PopMenuRef pUID="PM_00162"/> <PopMenuRef pUID="PM_00169"/>
<PopMenuRef pUID="PM_00174"/> <PopMenuRef pUID="PM_00176"/>
<PopMenuRef pUID="PM_00188"/>
</PopMenu> </PopMenu>
<PopMenu UID="PM_00005"> <PopMenu UID="PM_00005">
<Name>ILS</Name> <Name>ILS</Name>
@@ -1669,327 +1736,398 @@
</PopMenuItem> </PopMenuItem>
</PopMenu> </PopMenu>
<PopMenu UID="PM_00140"> <PopMenu UID="PM_00140">
<Name>SPS</Name> <Name>Lager</Name>
<PopMenuRef pUID="PM_00141"/>
<PopMenuRef pUID="PM_00145"/>
<PopMenuItem> <PopMenuItem>
<NameRef>Eingang</NameRef> <NameRef>Geschoss anlegen</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00149"/> <MacroRef MenuMacroID="MM_00141"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Ausgang</NameRef> <NameRef>Aktives Geschoss</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00150"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>SPS Schliessen</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00151"/>
</MenuItem>
</PopMenuItem>
</PopMenu>
<PopMenu UID="PM_00141">
<Name>TRO</Name>
<PopMenuItem>
<NameRef>Marker einfuegen</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00142"/> <MacroRef MenuMacroID="MM_00142"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Marker bearbeiten</NameRef> <NameRef>Geschoss kopieren</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00143"/> <MacroRef MenuMacroID="MM_00143"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem IsSeparator="true"/>
<PopMenuItem> <PopMenuItem>
<NameRef>Bearbeiten (Auto)</NameRef> <NameRef>Lagerbereich (Auto-Fuellung)</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00144"/> <MacroRef MenuMacroID="MM_00145"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
</PopMenu>
<PopMenu UID="PM_00145">
<Name>Connections</Name>
<PopMenuItem> <PopMenuItem>
<NameRef>Verbindung einfuegen</NameRef> <NameRef>Regalreihen einzeln</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00146"/> <MacroRef MenuMacroID="MM_00146"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Verbindung bearbeiten</NameRef> <NameRef>Gaenge einzeln</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00147"/> <MacroRef MenuMacroID="MM_00147"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Bearbeiten (Auto)</NameRef> <NameRef>Bearbeiten</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00144"/> <MacroRef MenuMacroID="MM_00148"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
</PopMenu> <PopMenuItem IsSeparator="true"/>
<PopMenu UID="PM_00152">
<Name>Export</Name>
<PopMenuItem> <PopMenuItem>
<NameRef>Export Sivas</NameRef> <NameRef>Regale zaehlen (goh_rack)</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00150"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Regelprofil anzeigen</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00151"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Hand-YAML fuer Abgleich</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00152"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Export RFQ-YAML</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00153"/> <MacroRef MenuMacroID="MM_00153"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem>
<NameRef>Export CSV</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00154"/>
</MenuItem>
</PopMenuItem>
</PopMenu> </PopMenu>
<PopMenu UID="PM_00155"> <PopMenu UID="PM_00154">
<Name>Konfiguration</Name> <Name>SPS</Name>
<PopMenuRef pUID="PM_00155"/>
<PopMenuRef pUID="PM_00159"/>
<PopMenuItem> <PopMenuItem>
<NameRef>Umschalten 2D und 3D</NameRef> <NameRef>Eingang</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00156"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Alle Bloecke auf 2D</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00157"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Alle Bloecke auf 3D</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00158"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Sprache</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00159"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Planquadrate an/aus</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00160"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>CSV-Export</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00161"/>
</MenuItem>
</PopMenuItem>
</PopMenu>
<PopMenu UID="PM_00162">
<Name>Werkzeuge</Name>
<PopMenuItem>
<NameRef>Bloecke zaehlen</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00163"/> <MacroRef MenuMacroID="MM_00163"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Bloecke loeschen</NameRef> <NameRef>Ausgang</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00164"/> <MacroRef MenuMacroID="MM_00164"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Attribute ersetzen</NameRef> <NameRef>SPS Schliessen</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00165"/> <MacroRef MenuMacroID="MM_00165"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
</PopMenu>
<PopMenu UID="PM_00155">
<Name>TRO</Name>
<PopMenuItem>
<NameRef>Marker einfuegen</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00156"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Marker bearbeiten</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00157"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Bearbeiten (Auto)</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00158"/>
</MenuItem>
</PopMenuItem>
</PopMenu>
<PopMenu UID="PM_00159">
<Name>Connections</Name>
<PopMenuItem>
<NameRef>Verbindung einfuegen</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00160"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Verbindung bearbeiten</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00161"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Bearbeiten (Auto)</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00158"/>
</MenuItem>
</PopMenuItem>
</PopMenu>
<PopMenu UID="PM_00166">
<Name>Export</Name>
<PopMenuItem>
<NameRef>Export Sivas</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00167"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Export CSV</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00168"/>
</MenuItem>
</PopMenuItem>
</PopMenu>
<PopMenu UID="PM_00169">
<Name>Konfiguration</Name>
<PopMenuItem>
<NameRef>Umschalten 2D und 3D</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00170"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Alle Bloecke auf 2D</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00171"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Alle Bloecke auf 3D</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00172"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Sprache</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00173"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Planquadrate an/aus</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00174"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>CSV-Export</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00175"/>
</MenuItem>
</PopMenuItem>
</PopMenu>
<PopMenu UID="PM_00176">
<Name>Werkzeuge</Name>
<PopMenuItem>
<NameRef>Bloecke zaehlen</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00177"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Bloecke loeschen</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00178"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem>
<NameRef>Attribute ersetzen</NameRef>
<MenuItem>
<MacroRef MenuMacroID="MM_00179"/>
</MenuItem>
</PopMenuItem>
<PopMenuItem IsSeparator="true"/> <PopMenuItem IsSeparator="true"/>
<PopMenuItem> <PopMenuItem>
<NameRef>Text ersetzen</NameRef> <NameRef>Text ersetzen</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00167"/> <MacroRef MenuMacroID="MM_00181"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Text parallel beschriften</NameRef> <NameRef>Text parallel beschriften</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00168"/> <MacroRef MenuMacroID="MM_00182"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Texthoehe aendern</NameRef> <NameRef>Texthoehe aendern</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00169"/> <MacroRef MenuMacroID="MM_00183"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Textstil aendern</NameRef> <NameRef>Textstil aendern</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00170"/> <MacroRef MenuMacroID="MM_00184"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem IsSeparator="true"/> <PopMenuItem IsSeparator="true"/>
<PopMenuItem> <PopMenuItem>
<NameRef>Faehnchen + Stueckliste aus Zeichnung</NameRef> <NameRef>Faehnchen + Stueckliste aus Zeichnung</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00172"/> <MacroRef MenuMacroID="MM_00186"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Din Formate</NameRef> <NameRef>Din Formate</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00173"/> <MacroRef MenuMacroID="MM_00187"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
</PopMenu> </PopMenu>
<PopMenu UID="PM_00174"> <PopMenu UID="PM_00188">
<Name>Tests</Name> <Name>Tests</Name>
<PopMenuItem> <PopMenuItem>
<NameRef>alle Tests ausfuehren und exportieren</NameRef> <NameRef>alle Tests ausfuehren und exportieren</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00175"/> <MacroRef MenuMacroID="MM_00189"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Tests ausfuehren und stehen lassen</NameRef> <NameRef>Tests ausfuehren und stehen lassen</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00176"/> <MacroRef MenuMacroID="MM_00190"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Export CSV/Sivas ALL</NameRef> <NameRef>Export CSV/Sivas ALL</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00177"/> <MacroRef MenuMacroID="MM_00191"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem IsSeparator="true"/> <PopMenuItem IsSeparator="true"/>
<PopMenuItem> <PopMenuItem>
<NameRef>Kreisel</NameRef> <NameRef>Kreisel</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00179"/> <MacroRef MenuMacroID="MM_00193"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Foerderer</NameRef> <NameRef>Foerderer</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00180"/> <MacroRef MenuMacroID="MM_00194"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Foerderer-Linienzug</NameRef> <NameRef>Foerderer-Linienzug</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00181"/> <MacroRef MenuMacroID="MM_00195"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Omniflo</NameRef> <NameRef>Omniflo</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00182"/> <MacroRef MenuMacroID="MM_00196"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Omniflo Strecke</NameRef> <NameRef>Omniflo Strecke</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00183"/> <MacroRef MenuMacroID="MM_00197"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Gefaellestrecke</NameRef> <NameRef>Gefaellestrecke</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00184"/> <MacroRef MenuMacroID="MM_00198"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Mubea Gesamtmodell</NameRef> <NameRef>Mubea Gesamtmodell</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00185"/> <MacroRef MenuMacroID="MM_00199"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>HundM05 Kreisel-Abschnitt</NameRef> <NameRef>HundM05 Kreisel-Abschnitt</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00186"/> <MacroRef MenuMacroID="MM_00200"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Dematic/Arvato Flowpuffer (Vorlage)</NameRef> <NameRef>Dematic/Arvato Flowpuffer (Vorlage)</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00187"/> <MacroRef MenuMacroID="MM_00201"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>ILS 3.0 Showroom (Vorlage)</NameRef> <NameRef>ILS 3.0 Showroom (Vorlage)</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00188"/> <MacroRef MenuMacroID="MM_00202"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Testanlage test_system</NameRef> <NameRef>Testanlage test_system</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00189"/> <MacroRef MenuMacroID="MM_00203"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>KS 2D/3D pruefen (CSV)</NameRef> <NameRef>KS 2D/3D pruefen (CSV)</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00190"/> <MacroRef MenuMacroID="MM_00204"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
</PopMenu> </PopMenu>
<PopMenu UID="PM_00201"> <PopMenu UID="PM_00215">
<Alias>POP501</Alias> <Alias>POP501</Alias>
<Name>SSG_LIB Edit Context</Name> <Name>SSG_LIB Edit Context</Name>
<PopMenuItem> <PopMenuItem>
<NameRef>Kreisel bearbeiten</NameRef> <NameRef>Kreisel bearbeiten</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00202"/> <MacroRef MenuMacroID="MM_00216"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Eckrad bearbeiten</NameRef> <NameRef>Eckrad bearbeiten</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00203"/> <MacroRef MenuMacroID="MM_00217"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Omniflo Element bearbeiten</NameRef> <NameRef>Omniflo Element bearbeiten</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00204"/> <MacroRef MenuMacroID="MM_00218"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Gefaellestrecke bearbeiten</NameRef> <NameRef>Gefaellestrecke bearbeiten</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00205"/> <MacroRef MenuMacroID="MM_00219"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>VarioFoerderer bearbeiten</NameRef> <NameRef>VarioFoerderer bearbeiten</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00206"/> <MacroRef MenuMacroID="MM_00220"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>TRO-Marker bearbeiten</NameRef> <NameRef>TRO-Marker bearbeiten</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00207"/> <MacroRef MenuMacroID="MM_00221"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem> <PopMenuItem>
<NameRef>Verbindung bearbeiten</NameRef> <NameRef>Verbindung bearbeiten</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00208"/> <MacroRef MenuMacroID="MM_00222"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
<PopMenuItem IsSeparator="true"/> <PopMenuItem IsSeparator="true"/>
<PopMenuItem> <PopMenuItem>
<NameRef>SSG Bearbeiten (Auto)</NameRef> <NameRef>SSG Bearbeiten (Auto)</NameRef>
<MenuItem> <MenuItem>
<MacroRef MenuMacroID="MM_00210"/> <MacroRef MenuMacroID="MM_00224"/>
</MenuItem> </MenuItem>
</PopMenuItem> </PopMenuItem>
</PopMenu> </PopMenu>
+3
View File
@@ -174,6 +174,9 @@
(ssg-ensure "ssg_blocktools") (ssg-ensure "ssg_blocktools")
(ssg-ensure "ssg_texttools") (ssg-ensure "ssg_texttools")
(ssg-ensure "ssg_stueckliste") (ssg-ensure "ssg_stueckliste")
;; Lagerplanung (RFQ-YAML): Regelprofile vor LagerInsert
(ssg-ensure "LagerPruefung")
(ssg-ensure "LagerInsert")
;; Menue einhaengen ;; Menue einhaengen
(menucmd "P13=+SSG_LIB.POP1") (menucmd "P13=+SSG_LIB.POP1")
+14
View File
@@ -137,6 +137,20 @@
[Gatterträger]^C^C(ssg-ensure "GeruestInsert") GERUEST [Gatterträger]^C^C(ssg-ensure "GeruestInsert") GERUEST
[->Funktionen] [->Funktionen]
[<-<-Traeger beschriften]^C^C(ssg-ensure "GeruestInsert") GERUEST_TRAEGER_BESCHRIFTEN [<-<-Traeger beschriften]^C^C(ssg-ensure "GeruestInsert") GERUEST_TRAEGER_BESCHRIFTEN
[->Lager]
[Geschoss anlegen]^C^C(ssg-ensure "LagerInsert") LG_GESCHOSS
[Aktives Geschoss]^C^C(ssg-ensure "LagerInsert") LG_GESCHOSS_AKTIV
[Geschoss kopieren]^C^C(ssg-ensure "LagerInsert") LG_GESCHOSS_KOPIEREN
[--]
[Lagerbereich (Auto-Fuellung)]^C^C(ssg-ensure "LagerInsert") LG_BEREICH
[Regalreihen einzeln]^C^C(ssg-ensure "LagerInsert") LG_REIHE
[Gaenge einzeln]^C^C(ssg-ensure "LagerInsert") LG_GANG
[Bearbeiten]^C^C(ssg-ensure "LagerInsert") LG_EDIT
[--]
[Regale zaehlen (goh_rack)]^C^C(ssg-ensure "LagerInsert") LG_REGALE
[Regelprofil anzeigen]^C^C(ssg-ensure "LagerPruefung") LG_REGELN
[Hand-YAML fuer Abgleich]^C^C(ssg-ensure "LagerInsert") LAGER_HAND_YAML
[<-Export RFQ-YAML]^C^C(ssg-ensure "LagerInsert") LAGER_EXPORT
[->SPS] [->SPS]
[->TRO] [->TRO]
[Marker einfuegen]^C^C(ssg-ensure "Tro_Edit") TRO_INSERT [Marker einfuegen]^C^C(ssg-ensure "Tro_Edit") TRO_INSERT
+2 -1
View File
@@ -7,4 +7,5 @@ pytest==9.0.2
# (lib/export_neighbors.py). Fehlt shapely, faellt die Broad-Phase automatisch # (lib/export_neighbors.py). Fehlt shapely, faellt die Broad-Phase automatisch
# auf das Raster (omniflo_zellgroesse_mm) zurueck. # auf das Raster (omniflo_zellgroesse_mm) zurueck.
shapely==2.1.2 shapely==2.1.2
# pyyaml: Overlay-YAML der Lagerplanung (lib/lager_yaml_export.py)
pyyaml==6.0.3
+1
View File
@@ -5,6 +5,7 @@
{ "name": "vfl_schema", "module": "VarioFoerderer" }, { "name": "vfl_schema", "module": "VarioFoerderer" },
{ "name": "vf_spec", "module": "VarioFoerderer" }, { "name": "vf_spec", "module": "VarioFoerderer" },
{ "name": "lastent", "module": "GeruestInsert" }, { "name": "lastent", "module": "GeruestInsert" },
{ "name": "lager", "save": "dxf", "module": "LagerInsert" },
{ "name": "omniflo", "save": "dxf", "module": "OmniModulInsert" }, { "name": "omniflo", "save": "dxf", "module": "OmniModulInsert" },
{ "name": "omniflo_strecke", "save": "dxf", "module": "OmniModulInsert" }, { "name": "omniflo_strecke", "save": "dxf", "module": "OmniModulInsert" },
{ "name": "gefaellestrecke", "save": "dwg", "module": "Gefaellestrecke" }, { "name": "gefaellestrecke", "save": "dwg", "module": "Gefaellestrecke" },
+550
View File
@@ -0,0 +1,550 @@
;; ============================================================
;; test_lager.lsp - Szenario-Test Lagerplanung (LagerInsert.lsp)
;;
;; Baut in der aktuellen (leeren) Zeichnung ohne Dialoge auf:
;; - zwei Geschosse auf derselben 2D-Kontur (EG Hallenboden, OG 3100 mm)
;; - einen Lagerbereich 10000 x 6000 im OG mit Auto-Fuellung
;; und prueft Bloecke, Attribute, Layer, Neuberechnung (keine Verdopplung),
;; den Neuaufbau einer geaenderten Reihe und die Rohdaten von LAGER_EXPORT.
;; Zum Schluss Regression der goh_rack-Erkennung (lg-racks-sammeln) gegen
;; tests/testdata/goh_storage_test.dwg (Referenzwerte aus
;; goh_storage_test_regal_report.txt: 38946.39 mm / 77892.78 mm).
;;
;; Aufruf in BricsCAD:
;; (load (strcat (getenv "DXFMAKRO") "/tests/test_lager.lsp"))
;; TEST_LAGER
;;
;; Speichert tests/output/lager_results.json (Konvention test_run_all)
;; und tests/output/lager_tests_lager_raw.json (Eingabe fuer
;; lib/lager_yaml_export.py).
;; ============================================================
(setq *tlg-pass* 0 *tlg-fail* 0 *tlg-fails* '())
(defun tlg-eq (name erwartet ist)
(if (equal erwartet ist 1e-3)
(progn (setq *tlg-pass* (1+ *tlg-pass*))
(princ (strcat "\n [PASS] " name)))
(progn (setq *tlg-fail* (1+ *tlg-fail*))
(setq *tlg-fails* (cons name *tlg-fails*))
(princ (strcat "\n [FAIL] " name
"\n erwartet: " (vl-princ-to-string erwartet)
"\n ist: " (vl-princ-to-string ist))))))
(defun tlg-rechteck (x0 y0 x1 y1 / )
(entmakex (list '(0 . "LWPOLYLINE") '(100 . "AcDbEntity") '(8 . "0")
'(100 . "AcDbPolyline") '(90 . 4) '(70 . 1)
(list 10 x0 y0) (list 10 x1 y0) (list 10 x1 y1) (list 10 x0 y1))))
(defun tlg-attr (ent tag) (cdr (assoc tag (ssg-attrib-read ent))))
(defun tlg-layer-gefroren-p (name / e)
(setq e (tblsearch "LAYER" name))
(and e (= 1 (logand 1 (cdr (assoc 70 e))))))
(defun tlg-summe (texte feld / s)
(setq s 0.0)
(foreach t1 texte (setq s (+ s (cond ((ssg-json-get (ssg-json-parse t1) feld)) (0.0)))))
s)
;; Bloecke einer Liste, deren Name auf muster passt.
(defun tlg-nur (ents muster)
(vl-remove-if-not '(lambda (e) (wcmatch (cdr (assoc 2 (entget e))) muster)) ents))
(defun tlg-anzahl-art (ents art)
(length (vl-remove-if-not '(lambda (e) (= (tlg-attr e "ART") art)) ents)))
(defun tlg-summe-attr (ents tag / s)
(setq s 0.0)
(foreach e ents (setq s (+ s (cond ((lg-zahl (tlg-attr e tag))) (0.0)))))
s)
;; Schraffurmuster der (ersten) HATCH einer Blockdefinition oder nil.
(defun tlg-schraffur (bname / h)
(if (tblsearch "BLOCK" bname)
(if (setq h (vl-some '(lambda (ed) (if (= (cdr (assoc 0 ed)) "HATCH") ed))
(lg-blockdef-objekte bname)))
(cdr (assoc 2 h)))))
;; Bloecke nach Muster, die zu Geschoss gs (Zahl) gehoeren.
(defun tlg-gs (muster gs)
(vl-remove-if-not '(lambda (e) (= (tlg-attr e "GESCHOSS") (itoa gs))) (lg-alle-bloecke muster)))
;; Kinderzahl je Bereich eines Geschosses, sortiert.
(defun tlg-kinderzahlen (gs)
(vl-sort (mapcar '(lambda (b) (length (lg-bereich-kinder b))) (tlg-gs "LG_BEREICH" gs)) '<))
(defun tlg-anzahl-attr (ents tag wert)
(length (vl-remove-if-not '(lambda (e) (= (tlg-attr e tag) wert)) ents)))
;; ATTDEF-Tags einer Blockdefinition als Alist (tag . T).
(defun tlg-attdef-tags (bname)
(mapcar '(lambda (ed) (cons (cdr (assoc 2 ed)) T))
(vl-remove-if-not '(lambda (ed) (= (cdr (assoc 0 ed)) "ATTDEF")) (lg-blockdef-objekte bname))))
(defun lager:tests ( / k0 k1 g0 g1 kb bereich reihen gaenge r0 neu raw daten pfad f
racks laengen kap tr kb2 b2 kinder mg mg2 hg alt eb
r1 gg1 n-alt g2 r2 gg2 ids b2k n-kinder bb gr rg grp rk kand zl
b3 rh3 mg3 ge3 ag3 n3 mr3 alt3)
(setvar "CMDECHO" 0)
(ssg-gui-aus)
;; --- Geschosse auf derselben Kontur (2D-Grundrisse uebereinander) ---
(setq k0 (tlg-rechteck 0.0 0.0 10000.0 6000.0))
(setq g0 (lg-geschoss-anlegen k0
(append (list (cons "HOEHE_BELAG_mm" "0") (cons "BELAG_ART" "hallenboden")
(cons "BEZEICHNUNG" "ground floor") (cons "KONSTR_HOEHE_mm" "0"))
(lg-geschoss-vorgaben 0))))
(setq g1 (lg-geschoss-anlegen k0
(append (list (cons "HOEHE_BELAG_mm" "3100") (cons "KONSTR_HOEHE_mm" "150"))
(lg-geschoss-vorgaben 1))))
(tlg-eq "geschoss 0 angelegt" "INSERT" (if g0 (cdr (assoc 0 (entget g0)))))
(tlg-eq "geschoss 1 angelegt" "INSERT" (if g1 (cdr (assoc 0 (entget g1)))))
(tlg-eq "geschosse gesamt" 2 (length (lg-geschosse)))
(tlg-eq "geschoss 1 layer" "LG_Geschoss_G1" (cdr (assoc 8 (entget g1))))
(tlg-eq "geschoss 1 hoehe" "3100" (tlg-attr g1 "HOEHE_BELAG_mm"))
;; die vergebene Kontur wurde fuer Geschoss 1 kopiert
(tlg-eq "kontur kopiert" T (not (equal (lg-xdata-wert g0 "kontur") (lg-xdata-wert g1 "kontur"))))
(tlg-eq "kontur besitzer" (lg-handle g1)
(lg-xdata-wert (lg-ent-aus-handle (lg-xdata-wert g1 "kontur")) "besitzer"))
(tlg-eq "doppelte nummer abgewiesen" nil
(lg-geschoss-anlegen (tlg-rechteck 0.0 0.0 10.0 10.0) (lg-geschoss-vorgaben 1)))
;; --- Aktives Geschoss friert die anderen ---
(lg-geschoss-aktivieren 1)
(tlg-eq "aktiv 1" 1 (lg-geschoss-aktiv))
(tlg-eq "G0 gefroren" T (tlg-layer-gefroren-p "LG_Geschoss_G0"))
(tlg-eq "G1 nicht gefroren" nil (tlg-layer-gefroren-p "LG_Geschoss_G1"))
(tlg-eq "ermitteln = aktiv" 1 (lg-geschoss-ermitteln '(5.0 5.0 0.0)))
(tlg-eq "gefrorene geschosse gefunden" 2 (length (lg-geschosse)))
;; --- Bereich mit Auto-Fuellung ---
(setq kb (tlg-rechteck 0.0 0.0 10000.0 6000.0))
(setq bereich (lg-bereich-anlegen kb (cons (cons "GESCHOSS" "1")
(lg-bereich-vorgaben (ssg-lwpoly-punkte kb)))))
(tlg-eq "bereich angelegt" "INSERT" (if bereich (cdr (assoc 0 (entget bereich)))))
(setq reihen (lg-alle-bloecke "LG_REIHE_*") gaenge (lg-alle-bloecke "LG_GANG_*"))
(tlg-eq "reihen" 4 (length reihen))
(tlg-eq "gaenge" 2 (length gaenge))
(tlg-eq "kinder des bereichs" 6 (length (lg-bereich-kinder bereich)))
(setq r0 (car reihen))
(tlg-eq "reihe laenge" "10000" (tlg-attr r0 "LAENGE"))
(tlg-eq "reihe tiefe" "700" (tlg-attr r0 "TIEFE_mm"))
(tlg-eq "reihe geschoss" "1" (tlg-attr r0 "GESCHOSS"))
(tlg-eq "reihe quelle" "auto" (tlg-attr r0 "QUELLE"))
(tlg-eq "reihe ebenen" "2300/1200;1100/1100" (tlg-attr r0 "HAENGEEBENEN"))
(tlg-eq "reihe layer" "LG_Reihe_G1" (cdr (assoc 8 (entget r0))))
(tlg-eq "reihe blockdef" "LG_REIHE_10000_700" (cdr (assoc 2 (entget r0))))
(tlg-eq "bereich rest" "1200" (tlg-attr bereich "RESTBREITE_mm"))
(tlg-eq "gang belag" "spanplatte" (tlg-attr (car gaenge) "BELAG"))
;; --- Neuberechnung ersetzt statt verdoppelt ---
(lg-bereich-fuellen bereich)
(tlg-eq "neu gefuellt: reihen" 4 (length (lg-alle-bloecke "LG_REIHE_*")))
(tlg-eq "neu gefuellt: gaenge" 2 (length (lg-alle-bloecke "LG_GANG_*")))
;; --- Bereichsparameter aendern: Gang 1200 -> Modul 2600, 2 Module ---
(lg-attribute-setzen bereich (list (cons "GANGBREITE_mm" "1200")
(cons "QUERGANG_ABSTAND_mm" "4000")))
(lg-bereich-fuellen bereich)
(tlg-eq "quergaenge: reihenstuecke" 8 (length (lg-alle-bloecke "LG_REIHE_*")))
(tlg-eq "bereich rest neu" "800" (tlg-attr bereich "RESTBREITE_mm"))
;; --- Einzelne Reihe aendern (Neuaufbau, Kennzeichnung) ---
(setq r0 (car (lg-alle-bloecke "LG_REIHE_*")))
(setq neu (lg-linie-neu-aufbauen r0 (list (cons "LAENGE" "2000") (cons "TIEFE_mm" "700")
(cons "HAENGEEBENEN" "2300/1200") (cons "SCHIENEN" "1")
(cons "GESCHOSS" "1") (cons "BEREICH" "1")
(cons "QUELLE" "auto_geaendert"))))
(tlg-eq "reihe neu aufgebaut" "LG_REIHE_2000_700" (if neu (cdr (assoc 2 (entget neu)))))
(tlg-eq "alte reihe weg" nil (entget r0))
(tlg-eq "reihenzahl gleich" 8 (length (lg-alle-bloecke "LG_REIHE_*")))
(tlg-eq "geaenderte reihe bleibt kind" T (if (member neu (lg-bereich-kinder bereich)) T nil))
;; --- Rohdaten fuer LAGER_EXPORT ---
(setq raw (lg-export-json))
(setq daten (ssg-json-parse raw))
(tlg-eq "export geschosse" 2 (length (ssg-json-get daten "geschosse")))
(tlg-eq "export bereiche" 1 (length (ssg-json-get daten "bereiche")))
(tlg-eq "export reihen" 8 (length (ssg-json-get daten "reihen")))
(tlg-eq "export land" "DE" (ssg-json-get daten "land"))
(tlg-eq "export konturflaeche" 6.0e7
(ssg-json-get (car (vl-remove-if-not '(lambda (g) (= (ssg-json-get g "nr") 1))
(ssg-json-get daten "geschosse")))
"kontur_flaeche_mm2"))
(tlg-eq "export haengeebenen"
'((("schienenhoehe_mm" . 2300) ("warenlaenge_mm" . 1200))
(("schienenhoehe_mm" . 1100) ("warenlaenge_mm" . 1100)))
(ssg-json-get (vl-some '(lambda (r) (if (= (ssg-json-get r "laenge_mm") 4000) r))
(ssg-json-get daten "reihen"))
"haengeebenen"))
;; --- Randgaenge Nord + Sued, Schraffur je Belag ---
(setq kb2 (tlg-rechteck 20000.0 0.0 30000.0 6000.0))
(setq b2 (lg-bereich-anlegen kb2 (append (list (cons "GESCHOSS" "1")
(cons "RANDGANG_N_mm" "1000")
(cons "RANDGANG_S_mm" "1000"))
(lg-bereich-vorgaben (ssg-lwpoly-punkte kb2)))))
(tlg-eq "randgang: attribut" "1000" (if b2 (tlg-attr b2 "RANDGANG_N_mm")))
(setq kinder (lg-bereich-kinder b2))
(tlg-eq "randgang: anzahl" 2 (tlg-anzahl-art kinder "randgang"))
(tlg-eq "randgang: reihen" 2 (length (tlg-nur kinder "LG_REIHE_*")))
;; Stapel 1000..5000 -> ein Modul, Rest 1600
(tlg-eq "randgang: rest" "1600" (tlg-attr b2 "RESTBREITE_mm"))
(tlg-eq "randgang: quelle auto" "auto" (tlg-attr (car (tlg-nur kinder "LG_GANG_*")) "QUELLE"))
(tlg-eq "schraffur spanplatte" "ANSI31" (tlg-schraffur "LG_GANG_10000_1000_SPANPLATTE"))
;; --- Manueller Gang quer durch den Bereich spart die Reihen aus ---
(setq mg (lg-gang-einfuegen '(25000.0 1000.0 0.0) '(25000.0 5000.0 0.0)
(list (cons "GESCHOSS" "1") (cons "BREITE_mm" "1000") (cons "ART" "gang")
(cons "BELAG" "gitterrost"))))
(tlg-eq "manuell: eingefuegt" "LG_GANG_4000_1000_GITTERROST" (if mg (cdr (assoc 2 (entget mg)))))
(tlg-eq "manuell: quelle" "manuell" (tlg-attr mg "QUELLE"))
(tlg-eq "manuell: bereich" (tlg-attr b2 "NR") (tlg-attr mg "BEREICH"))
(tlg-eq "schraffur gitterrost" "GRID" (tlg-schraffur "LG_GANG_4000_1000_GITTERROST"))
(setq kinder (lg-bereich-kinder b2))
(tlg-eq "manuell: reihen unterbrochen" 4 (length (tlg-nur kinder "LG_REIHE_*")))
(tlg-eq "manuell: reihenlaenge" 18000.0 (tlg-summe-attr (tlg-nur kinder "LG_REIHE_*") "LAENGE"))
;; Neuaufbau: manueller Gang bleibt, Reihen bleiben ausgespart
(lg-bereich-fuellen b2)
(setq kinder (lg-bereich-kinder b2))
(tlg-eq "manuell: bleibt beim neuaufbau" T (if (entget mg) T nil))
(tlg-eq "manuell: weiter ausgespart" 4 (length (tlg-nur kinder "LG_REIHE_*")))
;; Belag aendern -> neuer Blockname/neue Schraffur
(setq mg2 (lg-linie-neu-aufbauen mg (lg-wert-setzen (ssg-attrib-read mg) "BELAG" "spanplatte")))
(tlg-eq "manuell: belag geaendert" "LG_GANG_4000_1000_SPANPLATTE" (if mg2 (cdr (assoc 2 (entget mg2)))))
(tlg-eq "manuell: bleibt kind" T (if (member mg2 (lg-bereich-kinder b2)) T nil))
;; Hallenboden: keine Schraffur; ausserhalb jedes Bereichs: nicht verknuepft
(setq hg (lg-gang-einfuegen '(40000.0 0.0 0.0) '(42000.0 0.0 0.0)
(list (cons "GESCHOSS" "1") (cons "BREITE_mm" "1500") (cons "ART" "gang")
(cons "BELAG" "hallenboden"))))
(tlg-eq "hallenboden: ohne schraffur" nil (tlg-schraffur "LG_GANG_2000_1500_HALLENBODEN"))
(tlg-eq "hallenboden: kein bereich" "" (tlg-attr hg "BEREICH"))
;; --- Altbestand: Blockdefinition um ein neues ATTDEF ergaenzen ---
(lg-blockdef "TLG_ALT" (list (lg-geom-linie '(0.0 0.0 0.0) '(100.0 0.0 0.0))
(lg-geom-attdef "A" "" '(0.0 0.0 0.0) 50.0 nil)))
(setq alt (lg-block-einfuegen "TLG_ALT" '(0.0 -5000.0 0.0) 0.0 "0" '(("A" . "1"))))
(lg-blockdef "TLG_ALT" (list (lg-geom-linie '(0.0 0.0 0.0) '(100.0 0.0 0.0))
(lg-geom-attdef "A" "" '(0.0 0.0 0.0) 50.0 nil)
(lg-geom-attdef "B" "" '(0.0 -100.0 0.0) 50.0 nil)))
(tlg-eq "altbestand: neues ATTDEF" T (if (assoc "B" (tlg-attdef-tags "TLG_ALT")) T nil))
(tlg-eq "altbestand: referenz hat neuen tag" T (if (assoc "B" (ssg-attrib-read alt)) T nil))
(tlg-eq "altbestand: alter wert bleibt" "1" (tlg-attr alt "A"))
;; --- Export mit Randgaengen und manuellen Gaengen ---
(setq raw (lg-export-json) daten (ssg-json-parse raw))
(setq eb (vl-some '(lambda (b) (if (= (ssg-json-get b "nr") (atoi (tlg-attr b2 "NR"))) b))
(ssg-json-get daten "bereiche")))
(tlg-eq "export randgang_mm" '(("n" . 1000) ("o" . 0) ("s" . 1000) ("w" . 0))
(ssg-json-get eb "randgang_mm"))
(tlg-eq "export randgaenge" 2
(length (vl-remove-if-not '(lambda (g) (= (ssg-json-get g "art") "randgang"))
(ssg-json-get daten "gaenge"))))
(tlg-eq "export manuelle gaenge" 2
(length (vl-remove-if-not '(lambda (g) (= (ssg-json-get g "quelle") "manuell"))
(ssg-json-get daten "gaenge"))))
(setq pfad (strcat (getenv "DXFMAKRO") "/tests/output/lager_tests_lager_raw.json"))
(if (setq f (open pfad "w")) (progn (princ raw f) (close f)))
;; --- Geschoss kopieren: G1 (3100, Spanplatte) -> G2, G3 mit Gitterrost ---
(setq r1 (tlg-gs "LG_REIHE_*" 1) gg1 (tlg-gs "LG_GANG_*" 1)
n-alt (+ (length r1) (length gg1)))
(setq neu (lg-geschosse-kopieren
(list (cons "QUELLE" "1") (cons "ANZAHL" "2") (cons "ABSTAND_mm" "3000")
(cons "BELAG_ART" "gitterrost") (cons "VERSATZ_X_mm" "0") (cons "VERSATZ_Y_mm" "0"))))
(tlg-eq "kopie: neue geschosse" '(2 3) neu)
(tlg-eq "kopie: geschosse gesamt" 4 (length (lg-geschosse)))
(setq g2 (car (lg-geschoss-finden 2)))
(tlg-eq "kopie: G2 hoehe" "6100" (tlg-attr g2 "HOEHE_BELAG_mm"))
(tlg-eq "kopie: G3 hoehe" "9100" (tlg-attr (car (lg-geschoss-finden 3)) "HOEHE_BELAG_mm"))
(tlg-eq "kopie: G2 belag" "gitterrost" (tlg-attr g2 "BELAG_ART"))
(tlg-eq "kopie: G2 bezeichnung" "Level 2" (tlg-attr g2 "BEZEICHNUNG"))
(tlg-eq "kopie: G2 konstr. von quelle" "150" (tlg-attr g2 "KONSTR_HOEHE_mm"))
(tlg-eq "kopie: G2 layer" "LG_Geschoss_G2" (cdr (assoc 8 (entget g2))))
(tlg-eq "kopie: G2 eigene kontur" T
(and (lg-xdata-wert g2 "kontur")
(not (equal (lg-xdata-wert g2 "kontur") (lg-xdata-wert g1 "kontur")))))
(foreach nr '(2 3)
(tlg-eq (strcat "kopie: G" (itoa nr) " reihen") (length r1) (length (tlg-gs "LG_REIHE_*" nr)))
(tlg-eq (strcat "kopie: G" (itoa nr) " gaenge") (length gg1) (length (tlg-gs "LG_GANG_*" nr)))
(tlg-eq (strcat "kopie: G" (itoa nr) " reihenlaenge")
(tlg-summe-attr r1 "LAENGE") (tlg-summe-attr (tlg-gs "LG_REIHE_*" nr) "LAENGE"))
(tlg-eq (strcat "kopie: G" (itoa nr) " kinder je bereich") (tlg-kinderzahlen 1) (tlg-kinderzahlen nr)))
(tlg-eq "kopie: quelle unveraendert" n-alt
(+ (length (tlg-gs "LG_REIHE_*" 1)) (length (tlg-gs "LG_GANG_*" 1))))
(setq r2 (tlg-gs "LG_REIHE_*" 2) gg2 (tlg-gs "LG_GANG_*" 2))
(tlg-eq "kopie: reihen-layer G2" T
(vl-every '(lambda (e) (= (cdr (assoc 8 (entget e))) "LG_Reihe_G2")) r2))
(setq ids (mapcar '(lambda (e) (tlg-attr e "ID"))
(append (lg-alle-bloecke "LG_REIHE_*") (lg-alle-bloecke "LG_GANG_*"))))
(tlg-eq "kopie: IDs eindeutig" (length ids)
(length (vl-remove-if '(lambda (x) (member x (cdr (member x ids)))) ids)))
(tlg-eq "kopie: auto_geaendert bleibt" (tlg-anzahl-attr r1 "QUELLE" "auto_geaendert")
(tlg-anzahl-attr r2 "QUELLE" "auto_geaendert"))
(tlg-eq "kopie: manuelle gaenge" 2 (tlg-anzahl-attr gg2 "QUELLE" "manuell"))
;; Kinder zeigen auf den neuen Bereich (Nummer und XDATA)
(tlg-eq "kopie: kinder verknuepft" T
(vl-every '(lambda (b / nr)
(setq nr (tlg-attr b "NR"))
(vl-every '(lambda (k) (= (tlg-attr k "BEREICH") nr)) (lg-bereich-kinder b)))
(tlg-gs "LG_BEREICH" 2)))
;; Belag folgt dem Geschoss (Spanplatte -> Gitterrost), Hallenboden-Gang bleibt
(tlg-eq "kopie: belag folgt" 0 (tlg-anzahl-attr gg2 "BELAG" "spanplatte"))
(tlg-eq "kopie: manueller gang gitterrost" 1
(length (tlg-nur (vl-remove-if-not '(lambda (e) (= (tlg-attr e "QUELLE") "manuell")) gg2)
"LG_GANG_4000_1000_GITTERROST")))
(tlg-eq "kopie: hallenboden bleibt" 1 (length (tlg-nur gg2 "LG_GANG_2000_1500_HALLENBODEN")))
(tlg-eq "kopie: bereich belag" T
(vl-every '(lambda (b) (= (tlg-attr b "BELAG") "gitterrost")) (tlg-gs "LG_BEREICH" 2)))
;; Neufuellen einer Kopie laesst die Quelle in Ruhe, manueller Gang bleibt
(setq b2k (vl-some '(lambda (b) (if (= (tlg-attr b "RANDGANG_N_mm") "1000") b)) (tlg-gs "LG_BEREICH" 2)))
(setq n-kinder (length (lg-bereich-kinder b2k)))
(lg-bereich-fuellen b2k)
(tlg-eq "kopie neu gefuellt: kinder" n-kinder (length (lg-bereich-kinder b2k)))
(tlg-eq "kopie neu gefuellt: quelle unveraendert" n-alt
(+ (length (tlg-gs "LG_REIHE_*" 1)) (length (tlg-gs "LG_GANG_*" 1))))
;; Nachtraeglich erweitern: G3 -> G4
(tlg-eq "kopie: G3 -> G4" '(4)
(lg-geschosse-kopieren (list (cons "QUELLE" "3") (cons "ANZAHL" "1") (cons "ABSTAND_mm" "3000")
(cons "BELAG_ART" "gitterrost"))))
(tlg-eq "kopie: G4 hoehe" "12100" (tlg-attr (car (lg-geschoss-finden 4)) "HOEHE_BELAG_mm"))
;; Versatz in Y
(setq neu (lg-geschosse-kopieren (list (cons "QUELLE" "1") (cons "ANZAHL" "1") (cons "ABSTAND_mm" "3100")
(cons "BELAG_ART" "spanplatte") (cons "VERSATZ_Y_mm" "20000"))))
(tlg-eq "kopie versatz: G5" '(5) neu)
(tlg-eq "kopie versatz: kontur" '((0.0 20000.0) (10000.0 26000.0))
(mapcar '(lambda (p) (list (car p) (cadr p))) (ssg-poly-bbox (cadddr (lg-geschoss-finden 5)))))
(tlg-eq "kopie versatz: reihen verschoben" T
(vl-every '(lambda (e) (>= (cadr (cdr (assoc 10 (entget e)))) 20000.0)) (tlg-gs "LG_REIHE_*" 5)))
(tlg-eq "kopie: abstand 0 abgewiesen" nil
(lg-geschosse-kopieren (list (cons "QUELLE" "1") (cons "ANZAHL" "1") (cons "ABSTAND_mm" "0"))))
(setq daten (ssg-json-parse (lg-export-json)))
(tlg-eq "kopie: export geschosse" 6 (length (ssg-json-get daten "geschosse")))
;; --- Gang per Rechteck quer durch den kopierten Randgang-Bereich (G2) ---
(setq rg (lg-gang-rechteck-einfuegen
'((26500.0 0.0 0.0) (27500.0 0.0 0.0) (27500.0 6000.0 0.0) (26500.0 6000.0 0.0))
(list (cons "GESCHOSS" "2") (cons "BREITE_mm" "1000") (cons "ART" "gang")
(cons "BELAG" "gitterrost"))))
(tlg-eq "rechteck: blockname" "LG_GANG_6000_1000_GITTERROST" (if rg (cdr (assoc 2 (entget rg)))))
(tlg-eq "rechteck: mittellinie" '(27000.0 0.0) (if rg (list (car (cdr (assoc 10 (entget rg))))
(cadr (cdr (assoc 10 (entget rg)))))))
(tlg-eq "rechteck: quelle" "manuell" (tlg-attr rg "QUELLE"))
(tlg-eq "rechteck: bereich" (tlg-attr b2k "NR") (tlg-attr rg "BEREICH"))
(tlg-eq "rechteck: kind" T (if (member rg (lg-bereich-kinder b2k)) T nil))
;; Breite aus dem Rechteck, nicht aus werte
(setq rg (lg-gang-rechteck-einfuegen
'((40000.0 10000.0 0.0) (44000.0 10000.0 0.0) (44000.0 11500.0 0.0) (40000.0 11500.0 0.0))
(list (cons "GESCHOSS" "2") (cons "BREITE_mm" "1000") (cons "ART" "gang")
(cons "BELAG" "spanplatte"))))
(tlg-eq "rechteck: breite aus flaeche" "1500" (tlg-attr rg "BREITE_mm"))
(tlg-eq "rechteck: ohne bereich" "" (tlg-attr rg "BEREICH"))
(tlg-eq "rechteck: zu klein" nil
(lg-gang-rechteck-einfuegen '((0.0 0.0) (10.0 0.0) (10.0 0.0) (0.0 0.0))
(list (cons "GESCHOSS" "2") (cons "ART" "gang") (cons "BELAG" "spanplatte"))))
;; Polylinie aus dem BricsCAD-Befehl RECHTECK auswerten
(command "_.RECTANG" "_non" '(700000.0 0.0) "_non" '(704000.0 1200.0))
(setq rk (lg-rechteck-ecken-aus-poly (entlast)))
(entdel (entlast))
(tlg-eq "rechteck-befehl: 4 ecken" 4 (if (listp rk) (length rk)))
(tlg-eq "rechteck-befehl: gang" 1200.0 (caddr (lg-gang-aus-ecken rk)))
;; um 30 Grad gedrehtes Rechteck 5000 x 800 (wie RECHTECK mit Option Drehung)
(setq rk (mapcar '(lambda (p) (polar '(0.0 0.0) (+ (angle '(0.0 0.0) p) (/ pi 6.0)) (distance '(0.0 0.0) p)))
'((0.0 0.0) (5000.0 0.0) (5000.0 800.0) (0.0 800.0))))
(entmake (append (list '(0 . "LWPOLYLINE") '(100 . "AcDbEntity") '(100 . "AcDbPolyline") '(90 . 4) '(70 . 1))
(mapcar '(lambda (p) (cons 10 p)) rk)))
(setq rk (lg-rechteck-ecken-aus-poly (entlast)))
(entdel (entlast))
(tlg-eq "rechteck-befehl: gedreht breite" 800.0 (caddr (lg-gang-aus-ecken rk)))
(tlg-eq "rechteck-befehl: gedreht laenge" 5000.0
(distance (car (lg-gang-aus-ecken rk)) (cadr (lg-gang-aus-ecken rk))))
;; Fuenfeck (z.B. Fase) -> kein Rechteck
(entmake (append (list '(0 . "LWPOLYLINE") '(100 . "AcDbEntity") '(100 . "AcDbPolyline") '(90 . 5) '(70 . 1))
(mapcar '(lambda (p) (cons 10 p)) '((0.0 0.0) (900.0 0.0) (1000.0 100.0) (1000.0 500.0) (0.0 500.0)))))
(tlg-eq "rechteck-befehl: fase abgelehnt" 'kein-rechteck (lg-rechteck-ecken-aus-poly (entlast)))
(entdel (entlast))
;; --- Rack-Erkennung: direkt, verschachtelt + gedreht, MINSERT ---
;; eigener Testblock (Parameter als Attribut bzw. ATTDEF-Vorgabe), damit
;; die Suche nach goh_rack davon unberuehrt bleibt
(lg-blockdef "TLG_RACK" (list (lg-geom-linie '(0.0 0.0 0.0) '(1000.0 0.0 0.0))
(lg-geom-attdef "Laenge" "500" '(0.0 0.0 0.0) 50.0 nil)
(lg-geom-attdef "Ebenen" "3" '(0.0 0.0 0.0) 50.0 nil)))
(lg-blockdef "TLG_GRUPPE" (list '((0 . "INSERT") (8 . "0") (2 . "TLG_RACK") (10 0.0 0.0 0.0))
'((0 . "INSERT") (8 . "0") (2 . "TLG_RACK") (10 0.0 2000.0 0.0))))
(lg-block-einfuegen "TLG_RACK" '(-100000.0 0.0 0.0) 0.0 "0" '(("Laenge" . "100 cm") ("Ebenen" . "2")))
(setq grp (lg-block-einfuegen "TLG_GRUPPE" '(-200000.0 0.0 0.0) (/ pi 2.0) "0" nil))
(vla-AddMInsertBlock (lg-modelspace) (vlax-3d-point '(-300000.0 0.0 0.0)) "TLG_RACK"
1.0 1.0 1.0 0.0 1 3 1000.0 3000.0)
(setq rk (lg-racks-finden "TLG_RACK" "Laenge" "Ebenen"))
(tlg-eq "racks: anzahl" 6 (length rk))
(tlg-eq "racks: laenge (attribut mit einheit + vorgaben)" 3500.0
(apply '+ (mapcar '(lambda (r) (cond ((lg-rk-laenge r)) (0.0))) rk)))
(tlg-eq "racks: ebenen" 17.0 (apply '+ (mapcar '(lambda (r) (cond ((lg-rk-ebenen r)) (0.0))) rk)))
(tlg-eq "racks: verschachtelt top" 2
(length (vl-remove-if-not '(lambda (r) (equal (lg-rk-top r) grp)) rk)))
;; Gruppe um 90 Grad gedreht: zweites Rack (lokal y = 2000) liegt bei x = -202000
(tlg-eq "racks: verschachtelt gedreht" T
(equal (vl-sort (mapcar '(lambda (r) (car (lg-rk-mitte r)))
(vl-remove-if-not '(lambda (r) (equal (lg-rk-top r) grp)) rk))
'<)
'(-202000.0 -200000.0) 100.0))
;; MINSERT 1 Zeile x 3 Spalten, Abstand 3000: Mitten = Einfuegepunkte
(tlg-eq "racks: minsert" '(-300000.0 -297000.0 -294000.0)
(vl-sort (mapcar '(lambda (r) (car (lg-rk-mitte r)))
(vl-remove-if-not '(lambda (r) (< (car (lg-rk-mitte r)) -250000.0)) rk))
'<))
;; --- goh_rack-Regression (Referenzwerte goh_storage_test_regal_report.txt) ---
(setq pfad (strcat (getenv "DXFMAKRO") "/tests/testdata/goh_storage_test.dwg"))
(if (findfile pfad)
(progn
;; "*" = aufgeloest einfuegen: die goh_rack-Bloecke liegen danach
;; direkt im Modellbereich wie in der Originalzeichnung
(command "_.-INSERT" (strcat "*" pfad) '(50000.0 0.0 0.0) 1 0)
(setq racks (lg-export-goh-racks nil))
(tlg-eq "goh_rack anzahl" 4 (length (car racks)))
(setq laengen (tlg-summe (car racks) "laenge_mm"))
(setq kap 0.0)
(foreach t1 (car racks)
(setq tr (ssg-json-parse t1))
(setq kap (+ kap (* (ssg-json-get tr "laenge_mm") (ssg-json-get tr "ebenen")))))
(tlg-eq "goh_rack laenge gesamt" T (equal laengen 38946.39 0.1))
(tlg-eq "goh_rack laenge x ebenen" T (equal kap 77892.78 0.1))
(princ (strcat "\n goh_rack: " (rtos laengen 2 2) " mm / " (rtos kap 2 2) " mm"))
;; --- Geschoss mit goh_racks kopieren (deckungsgleich, 2D) ---
;; Geschosskontur um die Racks legen (Mitten aus lg-racks-sammeln + Rand)
(setq bb (ssg-poly-bbox (mapcar '(lambda (p) (list (car p) (cadr p)))
(mapcar 'lg-rk-mitte (lg-racks-sammeln)))))
(setq gr (lg-geschoss-naechste-nr))
(lg-geschoss-anlegen (tlg-rechteck (- (car (car bb)) 20000.0) (- (cadr (car bb)) 20000.0)
(+ (car (cadr bb)) 20000.0) (+ (cadr (cadr bb)) 20000.0))
(append (list (cons "HOEHE_BELAG_mm" "20000")) (lg-geschoss-vorgaben gr)))
(setq neu (lg-geschosse-kopieren (list (cons "QUELLE" (itoa gr)) (cons "ANZAHL" "1")
(cons "ABSTAND_mm" "3000") (cons "BELAG_ART" "spanplatte"))))
(setq racks (lg-export-goh-racks (lg-geschoss-kandidaten)))
(tlg-eq "rack-kopie: anzahl" 8 (length (car racks)))
(tlg-eq "rack-kopie: quelle" 4
(length (vl-remove-if-not '(lambda (t1) (equal (ssg-json-get (ssg-json-parse t1) "geschoss") gr))
(car racks))))
(tlg-eq "rack-kopie: kopie" 4
(length (vl-remove-if-not '(lambda (t1) (equal (ssg-json-get (ssg-json-parse t1) "geschoss") (car neu)))
(car racks))))
(tlg-eq "rack-kopie: keine warnung" nil (cadr racks))
(tlg-eq "rack-kopie: laenge kopie" T
(equal (tlg-summe (car racks) "laenge_mm") (* 2 38946.39) 0.2))
(tlg-eq "rack-kopie: layer" T (if (tblsearch "LAYER" (strcat "LG_Regal_G" (itoa (car neu)))) T nil))
;; Auswertung je Geschoss wie LG_REGALE: je 4 Racks in Quelle und Kopie
(setq kand (lg-geschoss-kandidaten))
(setq zl (lg-racks-auswerten
(mapcar '(lambda (r) (list (lg-rack-geschoss (lg-ent-layer (lg-rk-top r)) (lg-rk-mitte r) kand)
(lg-rk-laenge r) (lg-rk-ebenen r)))
(lg-racks-sammeln))))
(tlg-eq "lg_regale: geschosse" (list gr (car neu)) (mapcar 'car zl))
(tlg-eq "lg_regale: anzahl" '(4 4) (mapcar 'cadr zl))
(tlg-eq "lg_regale: laenge x ebenen" T (equal (cadddr (car zl)) 77892.78 0.2))
(tlg-eq "lg_regale: befehl laeuft" nil (vl-catch-all-error-p (vl-catch-all-apply 'c:LG_REGALE nil))))
(tlg-eq "goh_storage_test.dwg vorhanden" T nil))
;; --- Von Hand Gesetztes bleibt: Einfuegen loest keine Neuberechnung aus ---
(setq kb (tlg-rechteck 600000.0 0.0 610000.0 6000.0))
(setq b3 (lg-bereich-anlegen kb (append (list (cons "GESCHOSS" "1"))
(lg-bereich-vorgaben (ssg-lwpoly-punkte kb)))))
(setq rh3 (tlg-nur (lg-bereich-kinder b3) "LG_REIHE_*"))
(tlg-eq "von hand: auto reihen" 4 (length rh3))
;; Anwender loescht eine automatisch erzeugte Reihe ...
(entdel (car rh3))
;; ... und setzt einen Gang quer durch den Bereich
(setq mg3 (lg-gang-einfuegen '(605000.0 0.0 0.0) '(605000.0 6000.0 0.0)
(list (cons "GESCHOSS" "1") (cons "BREITE_mm" "1000") (cons "ART" "gang")
(cons "BELAG" "spanplatte"))))
(setq rh3 (tlg-nur (lg-bereich-kinder b3) "LG_REIHE_*"))
(tlg-eq "von hand: geloeschte reihe bleibt weg" 6 (length rh3))
(tlg-eq "von hand: reihen zugeschnitten" 27000.0 (tlg-summe-attr rh3 "LAENGE"))
(tlg-eq "von hand: reste bleiben auto" 6 (tlg-anzahl-attr rh3 "QUELLE" "auto"))
;; Reihe von Hand aendern (auto -> auto_geaendert)
(setq ge3 (lg-linie-neu-aufbauen-sperre (car rh3)
(lg-wert-setzen (lg-wert-setzen (ssg-attrib-read (car rh3)) "QUELLE" "auto_geaendert")
"LAENGE" "3000")))
;; Regalreihe von Hand quer in einen automatisch erzeugten Gang setzen
(setq ag3 (vl-some '(lambda (g) (if (and (= (tlg-attr g "QUELLE") "auto") (= (tlg-attr g "ART") "gang")
(< (car (cdr (assoc 10 (entget g)))) 601000.0))
g))
(tlg-nur (lg-bereich-kinder b3) "LG_GANG_*")))
(setq n3 (length (tlg-nur (lg-bereich-kinder b3) "LG_GANG_*")))
(setq mr3 (lg-manuell-einfuegen "reihe"
(list 601000.0 (cadr (cdr (assoc 10 (entget ag3)))) 0.0)
(list 603000.0 (cadr (cdr (assoc 10 (entget ag3)))) 0.0)
(list (cons "GESCHOSS" "1") (cons "TIEFE_mm" "700") (cons "BEREICH" ""))))
(tlg-eq "von hand: reihe verknuepft" (tlg-attr b3 "NR") (tlg-attr mr3 "BEREICH"))
(tlg-eq "von hand: reihe quelle" "manuell" (tlg-attr mr3 "QUELLE"))
(tlg-eq "von hand: gang zerteilt" (1+ n3) (length (tlg-nur (lg-bereich-kinder b3) "LG_GANG_*")))
(tlg-eq "von hand: manueller gang unberuehrt" T (if (entget mg3) T nil))
;; unverknuepfte manuelle Reihe (Altbestand) in der Kontur, im Streifen
;; des manuellen Gangs (dort sicher frei von automatischen Objekten)
(setq alt3 (lg-linie-einfuegen "reihe" '(605000.0 500.0 0.0) '(605000.0 2500.0 0.0)
(list (cons "GESCHOSS" "1") (cons "TIEFE_mm" "700") (cons "QUELLE" "manuell")) nil))
;; ausdrueckliche Neuberechnung (Bereich bearbeiten): Handarbeit bleibt
(lg-bereich-fuellen b3)
(tlg-eq "neuberechnung: manueller gang bleibt" T (if (entget mg3) T nil))
(tlg-eq "neuberechnung: manuelle reihe bleibt" T (if (entget mr3) T nil))
(tlg-eq "neuberechnung: geaenderte reihe bleibt" T (if (and ge3 (entget ge3)) T nil))
(tlg-eq "neuberechnung: altbestand bleibt" T (if (entget alt3) T nil))
;; nichts Automatisches ueberdeckt mehr die Handarbeit
(tlg-eq "neuberechnung: nichts zu schneiden" '(0 0 0 0)
(mapcar 'lg-sperre-anwenden (list mg3 mr3 ge3 alt3)))
;; Gang-Rechteck buendig an eine Reihe gezeichnet, krumme Breite 999.6:
;; die Rundung darf die Ansetzkante nicht verschieben, die Reihe bleibt
(setq rh3 (vl-remove-if-not '(lambda (r) (= (tlg-attr r "QUELLE") "auto"))
(tlg-nur (lg-bereich-kinder b3) "LG_REIHE_*")))
(setq bb (lg-linie-polygon (car rh3))
kb (mapcar '(lambda (q p) (/ (- q p) (distance (car bb) (cadddr bb)))) (cadddr bb) (car bb)))
(setq rg (lg-gang-rechteck-einfuegen
(list (cadddr bb) (caddr bb)
(mapcar '+ (caddr bb) (mapcar '(lambda (x) (* x 999.6)) kb))
(mapcar '+ (cadddr bb) (mapcar '(lambda (x) (* x 999.6)) kb)))
(list (cons "GESCHOSS" "1") (cons "ART" "gang") (cons "BELAG" "spanplatte"))))
(tlg-eq "rechteck buendig: gang breite" "1000" (tlg-attr rg "BREITE_mm"))
(tlg-eq "rechteck buendig: reihe bleibt" T (if (entget (car rh3)) T nil))
(ssg-gui-an)
(princ))
(defun lager:export-results (out-dir / f pfad)
(setq pfad (strcat out-dir "/lager_results.json"))
(setq f (open pfad "w"))
(if f
(progn
(write-line "{" f)
(write-line (strcat " \"pass\": " (itoa *tlg-pass*) ",") f)
(write-line (strcat " \"fail\": " (itoa *tlg-fail*) ",") f)
(write-line (strcat " \"status\": \""
(if (= *tlg-fail* 0) "OK" "FEHLER") "\"") f)
(write-line "}" f)
(close f)
(princ (strcat "\n Ergebnis: " pfad))))
(princ))
(defun c:TEST_LAGER ( / )
(setq *tlg-pass* 0 *tlg-fail* 0 *tlg-fails* '())
(princ "\n================================================================")
(princ "\n TEST_LAGER - Lagerplanung (LagerInsert.lsp)")
(princ "\n================================================================")
(lager:tests)
(princ "\n\n----------------------------------------------------------------")
(princ (strcat "\n Ergebnis: " (itoa *tlg-pass*) " PASS, "
(itoa *tlg-fail*) " FAIL"))
(if (> *tlg-fail* 0)
(progn
(princ "\n Fehlgeschlagen:")
(foreach n (reverse *tlg-fails*) (princ (strcat "\n - " n)))))
(princ "\n================================================================")
(princ))
(princ "\nTEST_LAGER geladen. Aufruf: TEST_LAGER")
(princ)
+208
View File
@@ -0,0 +1,208 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
test_lager_yaml.py - lib/lager_yaml_export.py (Lagerplanung -> RFQ-Overlay-YAML).
Laeuft ohne BricsCAD: die Rohdaten sind von Hand nachgestellt, im Format von
LAGER_EXPORT (Lisp/LagerInsert.lsp, lg-export-json). Geprueft wird:
1. levels: Belagshoehen, lichte Hoehe aus dem Geschoss darueber,
Haengeebenen (Summe aller Reihen je Schienenhoehe/Warenlaenge, inkl.
goh_rack-Bloecke), Belag/Gitterrost nur auf Buehnen.
2. layout: Bereichsparameter, Ganganzahl/-laenge, Huelle.
3. Provenienz: jeder Overlay-Wert hat einen Herkunfts-Eintrag.
4. Abgleich gegen cfg/example_goh_storage.yaml: Abweichungen -> Konflikte,
null/fehlend -> gefuellt.
5. CLI schreibt YAML (wieder einlesbar) und Nachweis-HTML.
"""
import json
import os
import sys
import pytest
import yaml
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "lib"))
import lager_yaml_export as lye # noqa: E402
BEISPIEL_YAML = os.path.join(os.path.dirname(os.path.abspath(__file__)),
"..", "cfg", "example_goh_storage.yaml")
EBENEN_EG = [{"schienenhoehe_mm": 2300, "warenlaenge_mm": 1200},
{"schienenhoehe_mm": 1100, "warenlaenge_mm": 1100}]
EBENEN_OG = [{"schienenhoehe_mm": 2800, "warenlaenge_mm": 1200},
{"schienenhoehe_mm": 1400, "warenlaenge_mm": 1200}]
def _reihe(gs, laenge, ebenen, tiefe=700):
return {"geschoss": gs, "laenge_mm": laenge, "tiefe_mm": tiefe, "haengeebenen": ebenen,
"schienen": 1, "quelle": "auto", "bereich": 1}
def _gang(gs, laenge, breite=1000, art="gang", belag="spanplatte"):
return {"geschoss": gs, "laenge_mm": laenge, "breite_mm": breite, "art": art,
"belag": belag, "bereich": 1}
@pytest.fixture
def raw():
"""Zwei Geschosse: Hallenboden (EG) + Buehne auf 3100 mm, je ein 2-Reihen-Modul."""
return {
"version": 1, "zeichnung": "test.dwg", "erzeugt": "20261021120000", "einheit": "mm",
"land": "DE",
"belag_yaml": {"spanplatte": "chipboard", "gitterrost": "grating", "hallenboden": ""},
"reihen_als_gitterrost": True,
"geschosse": [
# bewusst nicht nach Hoehe sortiert - der Export sortiert selbst
{"nr": 1, "bezeichnung": "Level 1", "hoehe_belag_mm": 3100, "konstr_hoehe_mm": 150,
"lichte_hoehe_mm": None, "belag_art": "spanplatte", "personen": 20,
"kontur_flaeche_mm2": 60e6, "kontur_bbox": [0, 0, 10000, 6000]},
{"nr": 0, "bezeichnung": "ground floor", "hoehe_belag_mm": 0, "konstr_hoehe_mm": 0,
"lichte_hoehe_mm": None, "belag_art": "hallenboden", "personen": 20,
"kontur_flaeche_mm2": 60e6, "kontur_bbox": [0, 0, 10000, 6000]},
],
"bereiche": [
{"nr": 1, "geschoss": 0, "gangbreite_mm": 1000, "regaltiefe_mm": 700,
"reihen_je_gang": 2, "schienen_je_gang": 1},
{"nr": 2, "geschoss": 1, "gangbreite_mm": 1000, "regaltiefe_mm": 700,
"reihen_je_gang": 2, "schienen_je_gang": 1},
],
"reihen": [_reihe(0, 10000, EBENEN_EG), _reihe(0, 10000, EBENEN_EG),
_reihe(1, 10000, EBENEN_OG), _reihe(1, 10000, EBENEN_OG)],
"gaenge": [_gang(0, 10000), _gang(1, 10000),
_gang(1, 700, breite=1200, art="quergang"),
_gang(1, 500, breite=1200, art="quergang", belag="gitterrost")],
"goh_racks": [{"geschoss": 1, "name": "goh_rack", "laenge_mm": 5000, "ebenen": 2,
"tiefe_mm": 700, "haengeebenen": EBENEN_OG, "haengeebenen_quelle": "vorgabe"}],
"warnungen": ["Testwarnung"],
}
def test_levels_sortiert_und_hoehen(raw):
overlay, _ = lye.aggregiere(raw)
lv = overlay["levels"]
assert [l["floor_elevation_m"] for l in lv] == [0.0, 3.1]
assert lv[0]["remark"] == "ground floor"
# lichte Hoehe EG = 3100 - 150 (Konstruktion darueber) - 0
assert lv[0]["clear_height_m"] == pytest.approx(2.95)
# oberstes Geschoss ohne LICHTE_HOEHE_mm -> nicht berechenbar
assert "clear_height_m" not in lv[1]
def test_haengeebenen_summe_inkl_goh_rack(raw):
overlay, _ = lye.aggregiere(raw)
eg, og = overlay["levels"]
assert eg["hanging_levels"] == [
{"rail_height_m": 2.3, "rack_length_m": 20.0, "goods_length_m": 1.2},
{"rail_height_m": 1.1, "rack_length_m": 20.0, "goods_length_m": 1.1},
]
# 2 Reihen a 10 m + goh_rack 5 m, oberste Schiene zuerst
assert [h["rack_length_m"] for h in og["hanging_levels"]] == [25.0, 25.0]
assert og["hanging_levels"][0]["rail_height_m"] == 2.8
def test_belag_nur_auf_buehne(raw):
overlay, _ = lye.aggregiere(raw)
eg, og = overlay["levels"]
assert "walkway" not in eg and "grating" not in eg
# Belag: Hauptgang 10 m2 + Quergang Spanplatte 0,84 m2
assert og["walkway"]["area_m2"] == pytest.approx(10.8)
assert og["walkway"]["spec"] == "chipboard"
# Gitterrost: Quergang 0,6 m2 + Reihen 2 x 7 m2 + goh_rack 3,5 m2
assert og["grating"] == pytest.approx(18.1)
def test_belag_ohne_gaenge_aus_kontur(raw):
raw["gaenge"] = []
overlay, s = lye.aggregiere(raw)
og = overlay["levels"][1]
# 60 m2 Kontur - (2 x 7 + 3,5) m2 Reihen
assert og["walkway"]["area_m2"] == pytest.approx(42.5)
assert "Konturflaeche" in s.provenance["levels[1].walkway.area_m2"]["formel"]
def test_layout(raw):
overlay, _ = lye.aggregiere(raw)
lay = overlay["layout"]
assert lay["aisle_width_m"] == 1.0
assert lay["rack_rows_per_aisle"] == 2
assert lay["rack_depth_m"] == 0.7
assert lay["trolley_rails_per_aisle"] == 1
assert lay["aisles"] == 1 # Quergaenge zaehlen nicht
assert lay["aisle_length_m"] == 10.0
assert lay["block_length_m"] == 10.0
assert lay["block_width_m"] == 6.0
def test_randgang_im_belag_nicht_in_aisles(raw):
# Randgang 10 m x 1 m (Spanplatte) im OG: Belag waechst, aisles bleibt
raw["gaenge"].append(_gang(1, 10000, art="randgang"))
overlay, s = lye.aggregiere(raw)
og = overlay["levels"][1]
assert og["walkway"]["area_m2"] == pytest.approx(20.8)
assert overlay["layout"]["aisles"] == 1
assert overlay["layout"]["aisle_length_m"] == 10.0
assert "Randgaenge G1: 10.0 m" in s.provenance["layout.aisles"]["formel"]
def test_unterschiedliche_bereiche_warnen(raw):
raw["bereiche"][1]["gangbreite_mm"] = 1200
raw["bereiche"].append(dict(raw["bereiche"][1]))
overlay, s = lye.aggregiere(raw)
assert overlay["layout"]["aisle_width_m"] == 1.2
assert any("aisle_width_m" in w for w in s.warnungen)
def test_provenienz_vollstaendig(raw):
ausgabe, _ = lye.baue_ausgabe(raw)
# hanging_levels: Provenienz je rack_length_m; Schienenhoehe/Warenlaenge
# sind der Gruppenschluessel derselben Zeile und stehen in deren Formel.
fehlend = [p for p, _ in lye._blaetter(ausgabe)
if p not in ausgabe["_provenance"]
and not (".hanging_levels[" in p and not p.endswith(".rack_length_m"))]
assert fehlend == []
assert ausgabe["_warnings"][0] == "Testwarnung"
def test_abgleich_mit_beispiel_yaml(raw):
with open(BEISPIEL_YAML, encoding="utf-8") as f:
hand = yaml.safe_load(f)
konflikte, fuellt = lye.abgleich(lye.aggregiere(raw)[0], hand)
pfade = {k["pfad"]: k for k in konflikte}
# Hand 4 Ebenen, Zeichnung 2
assert "levels" in pfade
# Hand 1,0 m Gangbreite = Zeichnung -> kein Konflikt
assert "layout.aisle_width_m" not in pfade
# Hand 4286 m2, Zeichnung 10,8 m2
assert pfade["levels[1].walkway.area_m2"]["hand"] == 4286
assert pfade["levels[1].walkway.area_m2"]["abweichung_prozent"] < -99
# Hand null -> Zeichnung fuellt
gef = {f["pfad"] for f in fuellt}
assert "layout.block_length_m" in gef
assert "levels[1].grating" in gef
def test_hole_pfad():
obj = {"levels": [{"a": 1}, {"walkway": {"area_m2": 5}}]}
assert lye._hole(obj, "levels[1].walkway.area_m2") == (True, 5)
assert lye._hole(obj, "levels[2].walkway") == (False, None)
assert lye._hole(obj, "layout.x") == (False, None)
def test_cli(raw, tmp_path):
rp = tmp_path / "x_lager_raw.json"
rp.write_text(json.dumps(raw), encoding="cp1252")
yp = tmp_path / "x_drawing.yaml"
hp = tmp_path / "x_nachweis.html"
rc = lye.main([str(rp), str(yp), "--hand", BEISPIEL_YAML, "--nachweis", str(hp)])
assert rc == 0
daten = yaml.safe_load(yp.read_text(encoding="utf-8"))
assert daten["levels"][1]["floor_elevation_m"] == 3.1
assert daten["_conflicts"]
assert "levels[0].floor_elevation_m" in daten["_provenance"]
assert "Lager-Nachweis" in hp.read_text(encoding="utf-8")
def test_cli_fehlende_datei(tmp_path):
assert lye.main([str(tmp_path / "fehlt.json"), str(tmp_path / "o.yaml")]) == 2
+530 -61
View File
@@ -2863,66 +2863,100 @@
(tu-nil "kf-antriebsart/kein-komma" (tu-nil "kf-antriebsart/kein-komma"
(vl-string-search "," (kf-antriebsart '(("titel" . "x"))))) (vl-string-search "," (kf-antriebsart '(("titel" . "x")))))
;; --- kf-drehung-gesamt: Drehwinkel fuer KF_EDIT --- ;; --- kf-nummer-aus-name: nur rein numerische Namen zaehlen ---
;; Regression: der Drehwinkel wird beim Bau in die Geometrie eingerechnet, ;; Die Bausteine heissen ebenfalls KF_...; atoi("2D") waere 2 und wuerde
;; die Blockreferenz selbst hat IMMER Drehung 0 (ssg-block-wrap-welt). ;; die Nummernvergabe verschieben, sobald lose Teile in der Zeichnung liegen.
;; KF_EDIT las nur Gruppencode 50 und baute eine mit 90 Grad gebaute (tu-eq "kf-nummer/KF_1" 1 (kf-nummer-aus-name "KF_1"))
;; Kette ungedreht neu. Der gebaute Winkel steht jetzt als XDATA. (tu-eq "kf-nummer/KF_12" 12 (kf-nummer-aus-name "KF_12"))
(tu-eqf "kf-drehung/nur-xdata" 90.0 (kf-drehung-gesamt 90.0 0.0)) (tu-eq "kf-nummer/klein" 7 (kf-nummer-aus-name "kf_7"))
;; Referenz nachtraeglich von Hand um 90 Grad gedreht (Gruppencode 50 in (tu-nil "kf-nummer/KF_2D" (kf-nummer-aus-name "KF_2D"))
;; Bogenmass) -> beides addiert sich (tu-nil "kf-nummer/KF_UM_26" (kf-nummer-aus-name "KF_UM_26"))
(tu-eqf "kf-drehung/xdata-plus-referenz" 180.0 (tu-nil "kf-nummer/KF_LAENGE" (kf-nummer-aus-name "KF_LAENGE"))
(kf-drehung-gesamt 90.0 (/ pi 2.0))) (tu-nil "kf-nummer/nur-Praefix" (kf-nummer-aus-name "KF_"))
;; Altbestand ohne XDATA: nur die Referenzdrehung (bisheriges Verhalten) (tu-nil "kf-nummer/fremdes-Praefix" (kf-nummer-aus-name "GR_5"))
(tu-eqf "kf-drehung/ohne-xdata" 45.0 (kf-drehung-gesamt nil (/ pi 4.0)))
;; nichts bekannt -> 0, kein Absturz und kein nil
(tu-eqf "kf-drehung/nichts" 0.0 (kf-drehung-gesamt nil nil))
(tu-true "kf-drehung/ist-zahl" (numberp (kf-drehung-gesamt nil nil)))
;; Summe ueber 180 Grad wird auf -180..180 gefaltet (270 = -90)
(tu-eqf "kf-drehung/normalisiert" -90.0
(kf-drehung-gesamt 180.0 (/ pi 2.0)))
;; ganze Zahl aus der XDATA (nicht real) darf nicht abstuerzen
(tu-eqf "kf-drehung/ganzzahl" 30.0 (kf-drehung-gesamt 30 0.0))
;; --- kf-neigung-aus-punkten: Kern der 3D-Aufrichtung --- ;; --- kf-freie-nummer: Nummern mit vorhandener Block-DEFINITION ueberspringen ---
;; Liefert (Schraeglaenge Neigungswinkel) aus Ketten-Start und -Ende. ;; Regression "Block aufgeloest": KF_EDIT loescht den alten KF_1, die
;; Die Kette wird AUFGERICHTET eingebaut, die Hoehe laeuft also in Z. ;; Referenzen im Modell sind weg (Start = 1), die Definition KF_1 lebt aber
;; 3-4-5-Dreieck: 3000 waagerecht, 4000 hoch -> 5000 schraeg ;; weiter. Wurde 1 erneut vergeben, traf _.-BLOCK auf einen vorhandenen
(tu-eqf "kf-neigung/3-4-5-laenge" 5000.0 ;; Namen, die "Neu definieren?"-Abfrage verschluckte die Folge-Eingaben,
(car (kf-neigung-aus-punkten '(0.0 0.0 0.0) '(3000.0 0.0 4000.0) 0.0))) ;; und die Einzelteile blieben lose im Modell liegen.
;; 45 Grad: gleiche Betraege waagerecht und hoch (tu-eq "kf-frei/nichts-belegt" 1
(tu-eqf "kf-neigung/45-grad" 45.0 (kf-freie-nummer 1 '(lambda (n) nil)))
(cadr (kf-neigung-aus-punkten '(0.0 0.0 0.0) '(1000.0 0.0 1000.0) 0.0))) (tu-eq "kf-frei/Edit-Szenario" 2
;; waagerecht -> Winkel 0, Laenge = Horizontalabstand (kf-freie-nummer 1 '(lambda (n) (member n '(1)))))
(tu-eqf "kf-neigung/waagerecht" '(2000.0 0.0) (tu-eq "kf-frei/mehrere-belegt" 4
(kf-neigung-aus-punkten '(0.0 0.0 0.0) '(2000.0 0.0 0.0) 0.0)) (kf-freie-nummer 1 '(lambda (n) (member n '(1 2 3)))))
;; Der Horizontalanteil wird auf die KETTENRICHTUNG projiziert, nicht (tu-eq "kf-frei/Luecke-bleibt" 3
;; auf die Welt-X-Achse: bei 90 Grad Basisdrehung laeuft die Kette in Y. (kf-freie-nummer 1 '(lambda (n) (member n '(1 2 4)))))
;; Ohne Projektion kaeme hier 0 heraus und der Winkel waere 90 statt 45. (tu-eq "kf-frei/Start-mitten" 5
(tu-eqf "kf-neigung/gedreht-90" '(1414.213562 45.0) (kf-freie-nummer 3 '(lambda (n) (member n '(3 4)))))
(kf-neigung-aus-punkten '(0.0 0.0 0.0) '(0.0 1000.0 1000.0) 90.0))
;; fallende Kette -> negativer Winkel
(tu-eqf "kf-neigung/fallend" -45.0
(cadr (kf-neigung-aus-punkten '(0.0 0.0 1000.0) '(1000.0 0.0 0.0) 0.0)))
;; Start = Ende -> nil statt Division durch 0
(tu-nil "kf-neigung/nulllaenge"
(kf-neigung-aus-punkten '(5.0 5.0 5.0) '(5.0 5.0 5.0) 0.0))
;; --- kf-matrix-aufrecht: Y_lokal wird zu Z_welt --- ;; --- kf-label-text: Beschriftung von Haupt- UND Hilfsblock ---
;; Das ist die Kernaussage der Aufrichtung. Geprueft wird sie an den ;; Eine Zeile wie bei VF_/GF_: Nummer, Winkel mit Gradzeichen, Hoehe,
;; Bildern der Einheitsvektoren, nicht an den Matrixeintraegen - so ;; projizierte Laenge (ganze mm)
;; bleibt der Test lesbar und unabhaengig von der Schreibweise. (tu-eq "kf-label/normal"
(tu-eqf "kf-matrix/Y-lokal-wird-Z-welt" '(0.0 0.0 1.0) (strcat "KF_1 26" (chr 176) " H=1500mm L_horizontal=4951mm")
(mat3-mul-vec3 (kf-matrix-aufrecht 0.0 0.0) '(0.0 1.0 0.0))) (kf-label-text 1 "26" 1500.0 4951.4))
;; Die Fahrtrichtung (X) bleibt bei Basisdrehung 0 die Welt-X-Achse ;; rundet kaufmaennisch auf ganze mm, nimmt auch Ganzzahlen
(tu-eqf "kf-matrix/X-bleibt-X" '(1.0 0.0 0.0) (tu-eq "kf-label/rundet-auf"
(mat3-mul-vec3 (kf-matrix-aufrecht 0.0 0.0) '(1.0 0.0 0.0))) (strcat "KF_12 30" (chr 176) " H=2000mm L_horizontal=3001mm")
;; Basisdrehung 90 Grad dreht die Fahrtrichtung in die Welt-Y-Achse, (kf-label-text 12 "30" 2000 3000.5))
;; die Hoehe bleibt dabei in Z
(tu-eqf "kf-matrix/basis-90-dreht-X-nach-Y" '(0.0 1.0 0.0) ;; --- kf-winkel-text: Neigung als nackte Ganzzahl (Attribut WINKEL) ---
(mat3-mul-vec3 (kf-matrix-aufrecht 90.0 0.0) '(1.0 0.0 0.0))) (tu-eq "kf-winkel-text/real" "26" (kf-winkel-text '(("winkel" . 26.0))))
(tu-eqf "kf-matrix/basis-90-haelt-Z" '(0.0 0.0 1.0) (tu-eq "kf-winkel-text/string" "30" (kf-winkel-text '(("winkel" . "30"))))
(mat3-mul-vec3 (kf-matrix-aufrecht 90.0 0.0) '(0.0 1.0 0.0))) (tu-eq "kf-winkel-text/fehlt" "0" (kf-winkel-text '(("name" . "x"))))
;; --- kf-versatzpunkt: Hilfsblock beim ersten Einfuegen unter dem Hauptblock ---
(tu-eqf "kf-versatzpunkt/minus-1000" '(100.0 -980.0 5.0)
(kf-versatzpunkt '(100.0 20.0 5.0) -1000))
;; 2D-Punkt ohne Z -> Z = 0, kein Absturz
(tu-eqf "kf-versatzpunkt/ohne-z" '(0.0 -1000.0 0.0)
(kf-versatzpunkt '(0.0 0.0) -1000.0))
;; --- kf-hilfsblock-versatz: Wert steht in der JSON, Vorgabe -1000 ---
(tu-eqf "kf-versatz/aus-daten" -500.0
(kf-hilfsblock-versatz '(("ansicht_2d" ("block" . "KF_2D") ("versatz_y" . -500)))))
(tu-eqf "kf-versatz/ohne-feld" -1000.0
(kf-hilfsblock-versatz '(("ansicht_2d" ("block" . "KF_2D")))))
(tu-eqf "kf-versatz/ohne-ansicht" -1000.0
(kf-hilfsblock-versatz '(("id" . "x"))))
;; Zahl, nicht T (and/or-Typfalle)
(tu-true "kf-versatz/ist-zahl"
(numberp (kf-hilfsblock-versatz '(("id" . "x")))))
;; --- kf-key-aus-xdata: Schluessel Haupt- <-> Hilfsblock ---
(tu-eq "kf-key/aus-xdata" "20261030120000"
(kf-key-aus-xdata
'((-3 ("SSG_KF_EDIT" (1000 . "kf-edit") (1000 . "20261030120000"))))))
;; Altbestand: nur der Marker, kein Schluessel -> nil (-> neuer Schluessel)
(tu-nil "kf-key/nur-marker"
(kf-key-aus-xdata '((-3 ("SSG_KF_EDIT" (1000 . "kf-edit"))))))
(tu-nil "kf-key/keine-xdata" (kf-key-aus-xdata '((0 . "INSERT"))))
(tu-nil "kf-key/nil" (kf-key-aus-xdata nil))
;; --- kf-waagerecht-laenge: Wert des Attributs L_KF_mm ---
;; Waagerechte Laenge des GANZEN Foerderers (der Draufsicht KF_2D), also
;; ohne die Steigung - nicht die Laenge des schraegen Mittelstuecks.
;; 3-4-5: 3000 waagerecht, 4000 hoch -> waagerecht bleibt 3000 (nicht 5000)
(tu-eqf "kf-waagerecht/ohne-steigung" 3000.0
(kf-waagerecht-laenge '(0.0 0.0 0.0) '(3000.0 0.0 4000.0) 0.0))
;; nur Hoehe, keine waagerechte Strecke -> 0
(tu-eqf "kf-waagerecht/nur-hoehe" 0.0
(kf-waagerecht-laenge '(0.0 0.0 0.0) '(0.0 0.0 1500.0) 0.0))
;; auf die KETTENRICHTUNG projiziert: bei 90 Grad Basisdrehung laeuft die
;; Kette in Y, die Welt-X-Differenz waere 0
(tu-eqf "kf-waagerecht/gedreht-90" 1000.0
(kf-waagerecht-laenge '(0.0 0.0 0.0) '(0.0 1000.0 1000.0) 90.0))
(tu-eqf "kf-waagerecht/gedreht-45" 1000.0
(kf-waagerecht-laenge '(0.0 0.0 0.0) '(707.10678 707.10678 500.0) 45.0))
;; fallende Kette / Ende "hinter" dem Start: Betrag, nie negativ
(tu-eqf "kf-waagerecht/immer-positiv" 2000.0
(kf-waagerecht-laenge '(2000.0 0.0 0.0) '(0.0 0.0 800.0) 0.0))
;; muss eine Zahl sein (Typfalle: nie T/nil)
(tu-true "kf-waagerecht/ist-zahl"
(numberp (kf-waagerecht-laenge '(0.0 0.0 0.0) '(10.0 0.0 0.0) 0.0)))
;; --- Standard-Variante: steht in den DATEN, nicht im Code --- ;; --- Standard-Variante: steht in den DATEN, nicht im Code ---
(tu-true "kf-variante-standard-p/markiert" (tu-true "kf-variante-standard-p/markiert"
@@ -2941,8 +2975,8 @@
(kf-standard-index '((("winkel" . 26)) (("winkel" . 30))))) (kf-standard-index '((("winkel" . 26)) (("winkel" . 30)))))
;; --- kf-punkt-differenzen: zweite Hoehen-Eingabeart (2 Punkte) --- ;; --- kf-punkt-differenzen: zweite Hoehen-Eingabeart (2 Punkte) ---
;; Gemessen wird die Differenz in Z - die Baugruppe steht aufgerichtet im ;; Gemessen wird die Differenz in Z (Hoehe der gepickten Punkte).
;; Modell. Zurueckgegeben wird (dz dy): die HOEHE zuerst, dY nur zur ;; Zurueckgegeben wird (dz dy): die HOEHE zuerst, dY nur zur
;; Kontrolle (wer in einer Draufsicht pickt, sieht dz = 0). ;; Kontrolle (wer in einer Draufsicht pickt, sieht dz = 0).
(tu-eqf "kf-punkt-diff/dz" '(1500.0 0.0) (tu-eqf "kf-punkt-diff/dz" '(1500.0 0.0)
(kf-punkt-differenzen '(0.0 0.0 0.0) '(2000.0 0.0 1500.0))) (kf-punkt-differenzen '(0.0 0.0 0.0) '(2000.0 0.0 1500.0)))
@@ -3037,6 +3071,441 @@
;; ------------------------------------------------------------
;; TESTS: ssg_core.lsp - Polygon-Helfer (reine 2D-Berechnung)
;; ------------------------------------------------------------
(defun tu-tests-poly ( / _q _u _l)
(princ "\n[ssg_core Polygon]")
(setq _q '((0.0 0.0) (10.0 0.0) (10.0 5.0) (0.0 5.0)))
(tu-eqf "ssg-poly-flaeche/rechteck" 50.0 (ssg-poly-flaeche _q))
(tu-eqf "ssg-poly-flaeche/gegen-uhrzeiger" 50.0 (ssg-poly-flaeche (reverse _q)))
(tu-eqf "ssg-poly-umfang" 30.0 (ssg-poly-umfang _q))
(tu-eq "ssg-poly-kanten/anzahl" 4 (length (ssg-poly-kanten _q)))
(tu-eq "ssg-poly-kanten/doppelter-schlusspunkt" 4
(length (ssg-poly-kanten (append _q (list (car _q))))))
(tu-eqf "ssg-poly-bbox" '((0.0 0.0) (10.0 5.0)) (ssg-poly-bbox _q))
(tu-true "ssg-punkt-in-poly-p/innen" (ssg-punkt-in-poly-p '(5.0 2.0) _q))
(tu-nil "ssg-punkt-in-poly-p/aussen" (ssg-punkt-in-poly-p '(11.0 2.0) _q))
;; U-Form: Schnitt durch beide Schenkel
(setq _u '((0.0 0.0) (10.0 0.0) (10.0 10.0) (7.0 10.0) (7.0 3.0) (3.0 3.0) (3.0 10.0) (0.0 10.0)))
(tu-eqf "ssg-poly-flaeche/u-form" 72.0 (ssg-poly-flaeche _u))
(tu-nil "ssg-punkt-in-poly-p/u-luecke" (ssg-punkt-in-poly-p '(5.0 5.0) _u))
(tu-eqf "ssg-poly-schnitt-y/u-schenkel" '((0.0 3.0) (7.0 10.0)) (ssg-poly-schnitt-y _u 5.0))
(tu-eqf "ssg-poly-schnitt-y/u-boden" '((0.0 10.0)) (ssg-poly-schnitt-y _u 1.0))
(tu-eqf "ssg-poly-schnitt-y/unterkante" '((0.0 10.0)) (ssg-poly-schnitt-y _q 0.0))
(tu-nil "ssg-poly-schnitt-y/oberkante" (ssg-poly-schnitt-y _q 5.0))
(tu-eqf "ssg-intervalle-schnitt" '((2.0 3.0) (5.0 10.0))
(ssg-intervalle-schnitt '((0.0 10.0)) '((2.0 3.0) (5.0 12.0))))
(tu-nil "ssg-intervalle-schnitt/leer" (ssg-intervalle-schnitt '((0.0 1.0)) '((2.0 3.0))))
(tu-eqf "ssg-intervalle-minus" '((0.0 2.0) (3.0 5.0) (6.0 10.0))
(ssg-intervalle-minus '((0.0 10.0)) '((2.0 3.0) (5.0 6.0))))
(tu-eqf "ssg-intervalle-minus/ohne-b" '((0.0 10.0)) (ssg-intervalle-minus '((0.0 10.0)) nil))
(tu-eqf "ssg-intervalle-minus/ueberdeckt" nil (ssg-intervalle-minus '((2.0 3.0)) '((0.0 10.0))))
(tu-eqf "ssg-punkt-drehen/90" '(0.0 1.0) (ssg-punkt-drehen '(1.0 0.0) (/ pi 2.0) '(0.0 0.0)))
(tu-eqf "ssg-punkt-drehen/z-bleibt" '(1.0 2.0 7.0) (ssg-punkt-drehen '(1.0 2.0 7.0) 0.0 '(5.0 5.0)))
;; Umlaufsinn
(tu-eqf "ssg-poly-flaeche-signiert/ccw" 50.0 (ssg-poly-flaeche-signiert _q))
(tu-eqf "ssg-poly-flaeche-signiert/cw" -50.0 (ssg-poly-flaeche-signiert (reverse _q)))
;; Band-Clip: x-Ausdehnung des Polygonteils zwischen zwei Waagrechten
(tu-eqf "ssg-poly-band-x/rechteck" '(0.0 10.0) (ssg-poly-band-x _q 1.0 2.0))
(tu-eqf "ssg-poly-band-x/dreieck-unten" '(0.0 10.0) (ssg-poly-band-x '((0.0 0.0) (10.0 0.0) (0.0 10.0)) 0.0 5.0))
(tu-eqf "ssg-poly-band-x/dreieck-oben" '(0.0 5.0) (ssg-poly-band-x '((0.0 0.0) (10.0 0.0) (0.0 10.0)) 5.0 10.0))
(tu-nil "ssg-poly-band-x/ausserhalb" (ssg-poly-band-x _q 6.0 7.0))
;; nur an der Kante beruehrt -> sperrt nicht
(tu-nil "ssg-poly-band-x/kante" (ssg-poly-band-x _q 5.0 6.0))
)
;; ------------------------------------------------------------
;; TESTS: LagerInsert.lsp / LagerPruefung.lsp - reine Funktionen
;; (Geschoss-/Bereichsbloecke, Export und Dialoge brauchen eine Zeichnung:
;; tests/test_lager.lsp)
;; ------------------------------------------------------------
(defun tu-lg-anzahl (recs typ)
(length (vl-remove-if-not '(lambda (r) (= (car r) typ)) recs))
)
(defun tu-lg-summe (recs typ / s)
(setq s 0.0)
(foreach r recs
(if (= (car r) typ) (setq s (+ s (distance (cadr r) (caddr r))))))
s
)
(defun tu-tests-lager ( / _q _erg _l _kand _kand2 _sq0 _sq3 _regel _prof _rand _rs _tab _w)
(princ "\n[LagerInsert / LagerPruefung]")
(if (not (and (car (atoms-family 1 '("LG-FUELLUNG-BERECHNEN")))
(car (atoms-family 1 '("LG-REGEL-AUFLOESEN")))))
(tu-record nil "lager/geladen" "LagerInsert/LagerPruefung nicht geladen (ssg_load.lsp)")
(progn
;; --- Zahlen/Texte ---
(tu-eq "lg-mm-str/ganz" "2300" (lg-mm-str 2300.0))
(tu-eq "lg-mm-str/halb" "2.5" (lg-mm-str 2.5))
(tu-eq "lg-mm-str/negativ" "-1" (lg-mm-str -1.0))
(tu-eq "lg-mm-str/nil" "" (lg-mm-str nil))
(tu-eqf "lg-zahl/komma" 1.5 (lg-zahl "1,5"))
(tu-nil "lg-zahl/leer" (lg-zahl ""))
(tu-nil "lg-zahl/text" (lg-zahl "abc"))
(tu-eqf "lg-haengeebenen-parse" '((2300.0 1200.0) (1100.0 1100.0))
(lg-haengeebenen-parse "2300/1200;1100/1100"))
(tu-eqf "lg-haengeebenen-parse/ohne-ware" '((2300.0 0.0)) (lg-haengeebenen-parse "2300"))
(tu-nil "lg-haengeebenen-parse/ungueltig" (lg-haengeebenen-parse "x"))
(tu-eq "lg-haengeebenen-string" "2300/1200;1100/1100"
(lg-haengeebenen-string '((2300.0 1200.0) (1100.0 1100.0))))
(tu-eq "lg-haengeebenen-n/1" "2300/1200" (lg-haengeebenen-n "2300/1200;1100/1100" 1))
(tu-eq "lg-haengeebenen-n/zuviel" "2300/1200;1100/1100" (lg-haengeebenen-n "2300/1200;1100/1100" 5))
(tu-eq "lg-feld-pruefen/mm" "1000" (lg-feld-pruefen "mm" " 1000 "))
(tu-nil "lg-feld-pruefen/mm-neg" (lg-feld-pruefen "mm" "-5"))
(tu-nil "lg-feld-pruefen/mm-leer" (lg-feld-pruefen "mm" ""))
(tu-eq "lg-feld-pruefen/mm_leer" "" (lg-feld-pruefen "mm_leer" ""))
(tu-eq "lg-feld-pruefen/int" "2" (lg-feld-pruefen "int" "2"))
(tu-nil "lg-feld-pruefen/int-0" (lg-feld-pruefen "int" "0"))
(tu-eq "lg-feld-pruefen/int0-0" "0" (lg-feld-pruefen "int0" "0"))
(tu-nil "lg-feld-pruefen/int-bruch" (lg-feld-pruefen "int" "1.5"))
(tu-eq "lg-feld-pruefen/grad-neg" "-90" (lg-feld-pruefen "grad" "-90"))
(tu-eq "lg-feld-pruefen/ebenen" "2300/1200;1100/1100" (lg-feld-pruefen "ebenen" "2300/1200; 1100/1100"))
(tu-nil "lg-feld-pruefen/ebenen-leer" (lg-feld-pruefen "ebenen" ""))
(tu-eq "lg-layer-name/geschoss" "LG_Reihe_G2" (lg-layer-name "LG_Reihe" "2"))
(tu-eq "lg-layer-name/int" "LG_Reihe_G3" (lg-layer-name "LG_Reihe" 3))
(tu-eq "lg-layer-name/ohne" "LG_Reihe" (lg-layer-name "LG_Reihe" nil))
;; --- Auto-Fuellung: Rechteck 10000 x 6000, Gang 1000, Tiefe 700 ---
(setq _q '((0.0 0.0) (10000.0 0.0) (10000.0 6000.0) (0.0 6000.0)))
;; 2 Reihen je Gang: Modul 700+1000+700 = 2400 -> 2 Module, Rest 1200
(setq _erg (lg-fuellung-berechnen _q (list (cons "gang" 1000.0) (cons "tiefe" 700.0) (cons "reihen" 2))))
(tu-eq "lg-fuellung/2je-reihen" 4 (tu-lg-anzahl (car _erg) "reihe"))
(tu-eq "lg-fuellung/2je-gaenge" 2 (tu-lg-anzahl (car _erg) "gang"))
(tu-eq "lg-fuellung/2je-module" 2 (caddr _erg))
(tu-eqf "lg-fuellung/2je-rest" 1200.0 (cadr _erg))
(tu-eqf "lg-fuellung/2je-reihenlaenge" 40000.0 (tu-lg-summe (car _erg) "reihe"))
(tu-eqf "lg-fuellung/erste-reihe" '("reihe" (0.0 350.0) (10000.0 350.0) 700.0) (car (car _erg)))
(tu-eqf "lg-fuellung/erster-gang" '("gang" (0.0 1200.0) (10000.0 1200.0) 1000.0) (cadr (car _erg)))
;; Ruecken an Ruecken: 3. Reihe (Modul 1) direkt nach der 2. Reihe
(tu-eqf "lg-fuellung/ruecken-an-ruecken" '(0.0 2050.0) (cadr (nth 2 (car _erg))))
(tu-eqf "lg-fuellung/zweites-modul" '(0.0 2750.0) (cadr (nth 3 (car _erg))))
;; 1 Reihe je Gang: Modul 1700 -> 3 Module, Rest 900
(setq _erg (lg-fuellung-berechnen _q (list (cons "gang" 1000.0) (cons "tiefe" 700.0) (cons "reihen" 1))))
(tu-eq "lg-fuellung/1je-reihen" 3 (tu-lg-anzahl (car _erg) "reihe"))
(tu-eq "lg-fuellung/1je-gaenge" 3 (tu-lg-anzahl (car _erg) "gang"))
(tu-eqf "lg-fuellung/1je-rest" 900.0 (cadr _erg))
;; Quergaenge alle 4000 mm, 1000 breit, ein Modul (Hoehe 2400)
(tu-eqf "lg-quergang-luecken" '((4000.0 5000.0) (9000.0 10000.0))
(lg-quergang-luecken 0.0 10000.0 4000.0 1000.0 0.0))
;; keine Luecke am Reihenende, wenn dahinter kein Mindeststueck passt
(tu-eqf "lg-quergang-luecken/reihenende" '((4000.0 5000.0))
(lg-quergang-luecken 0.0 10000.0 4000.0 1000.0 500.0))
(tu-nil "lg-quergang-luecken/aus" (lg-quergang-luecken 0.0 10000.0 0.0 1000.0 500.0))
(setq _erg (lg-fuellung-berechnen '((0.0 0.0) (10000.0 0.0) (10000.0 2400.0) (0.0 2400.0))
(list (cons "gang" 1000.0) (cons "tiefe" 700.0) (cons "reihen" 2)
(cons "q_abstand" 4000.0) (cons "q_breite" 1000.0))))
;; Luecke 4000-5000; die zweite (9000-10000) entfaellt am Reihenende
(tu-eq "lg-fuellung/quer-reihenstuecke" 4 (tu-lg-anzahl (car _erg) "reihe"))
(tu-eqf "lg-fuellung/quer-reihenlaenge" 18000.0 (tu-lg-summe (car _erg) "reihe"))
(tu-eq "lg-fuellung/quer-hauptgang-durch" 1 (tu-lg-anzahl (car _erg) "gang"))
(tu-eqf "lg-fuellung/quer-hauptganglaenge" 10000.0 (tu-lg-summe (car _erg) "gang"))
;; zwei Stuecke (unter und ueber dem Hauptgang), je 700 lang
(tu-eq "lg-fuellung/quergaenge" 2 (tu-lg-anzahl (car _erg) "quergang"))
(tu-eqf "lg-fuellung/quergang-laenge" 1400.0 (tu-lg-summe (car _erg) "quergang"))
;; L-Form: oberes Modul nur halb so lang
(setq _l '((0.0 0.0) (10000.0 0.0) (10000.0 2400.0) (5000.0 2400.0) (5000.0 4800.0) (0.0 4800.0)))
(setq _erg (lg-fuellung-berechnen _l (list (cons "gang" 1000.0) (cons "tiefe" 700.0) (cons "reihen" 2))))
(tu-eqf "lg-fuellung/l-form-reihenlaenge" 30000.0 (tu-lg-summe (car _erg) "reihe"))
(tu-eqf "lg-fuellung/l-form-ganglaenge" 15000.0 (tu-lg-summe (car _erg) "gang"))
;; Kontur kleiner als ein Modul -> nichts, Rest = ganze Breite
(setq _erg (lg-fuellung-berechnen '((0.0 0.0) (5000.0 0.0) (5000.0 2000.0) (0.0 2000.0))
(list (cons "gang" 1000.0) (cons "tiefe" 700.0) (cons "reihen" 2))))
(tu-nil "lg-fuellung/zu-schmal" (car _erg))
(tu-eqf "lg-fuellung/zu-schmal-rest" 2000.0 (cadr _erg))
(tu-eqf "lg-kontur-richtung/waagrecht" 0.0 (lg-kontur-richtung _q))
(tu-eqf "lg-kontur-richtung/senkrecht" 90.0
(lg-kontur-richtung '((0.0 0.0) (1000.0 0.0) (1000.0 8000.0) (0.0 8000.0))))
;; --- Randgaenge (Himmelsrichtung, entlang der Konturkanten) ---
(tu-eq "lg-kompass/ost" "O" (lg-kompass 0.0))
(tu-eq "lg-kompass/nord" "N" (lg-kompass (/ pi 2.0)))
(tu-eq "lg-kompass/west" "W" (lg-kompass pi))
(tu-eq "lg-kompass/sued" "S" (lg-kompass (/ pi -2.0)))
(tu-eq "lg-kompass/diagonal-ns" "N" (lg-kompass (/ pi 4.0)))
(setq _rand '(("N" . 1000.0) ("O" . 1000.0) ("S" . 1000.0) ("W" . 1000.0)))
;; Rechteck, alle vier Seiten: N/S volle Laenge, O/W um 2 x 1000 gekuerzt
(setq _rs (lg-randgang-streifen _q 0.0 _rand 100.0))
(tu-eq "lg-randgang/rechteck-anzahl" 4 (length _rs))
(tu-eq "lg-randgang/rechteck-sued" "S" (car (car _rs)))
(tu-eqf "lg-randgang/rechteck-sued-mitte" '((0.0 500.0) (10000.0 500.0))
(list (cadr (car _rs)) (caddr (car _rs))))
(tu-eqf "lg-randgang/rechteck-ost-gekuerzt" '((9500.0 1000.0) (9500.0 5000.0))
(list (cadr (cadr _rs)) (caddr (cadr _rs))))
(setq _erg (lg-fuellung-berechnen _q (list (cons "gang" 1000.0) (cons "tiefe" 700.0) (cons "reihen" 2)
(cons "rand" _rand))))
(tu-eqf "lg-fuellung/rand-laenge" 28000.0 (tu-lg-summe (car _erg) "randgang"))
;; Stapel 1000..5000 -> ein Modul, Rest 1600; Reihen/Gang 1000..9000
(tu-eq "lg-fuellung/rand-module" 1 (caddr _erg))
(tu-eqf "lg-fuellung/rand-rest" 1600.0 (cadr _erg))
(tu-eqf "lg-fuellung/rand-erste-reihe" '("reihe" (1000.0 1350.0) (9000.0 1350.0) 700.0) (car (car _erg)))
(tu-eqf "lg-fuellung/rand-reihenlaenge" 16000.0 (tu-lg-summe (car _erg) "reihe"))
(tu-eqf "lg-fuellung/rand-ganglaenge" 8000.0 (tu-lg-summe (car _erg) "gang"))
(tu-eq "lg-fuellung/rand-am-ende" "randgang" (car (last (car _erg))))
;; RICHTUNG 90 Grad: Nord liegt auf der lokalen rechten Kante (Reihenende)
(setq _rs (lg-randgang-streifen _q (/ pi 2.0) '(("N" . 1000.0)) 100.0))
(tu-eq "lg-randgang/gedreht-anzahl" 1 (length _rs))
(tu-eqf "lg-randgang/gedreht-lage" '((9500.0 0.0) (9500.0 6000.0))
(list (cadr (car _rs)) (caddr (car _rs))))
(setq _erg (lg-fuellung-berechnen _q (list (cons "gang" 1000.0) (cons "tiefe" 700.0) (cons "reihen" 2)
(cons "rand" '(("N" . 1000.0))) (cons "richtung" (/ pi 2.0)))))
(tu-eqf "lg-fuellung/gedreht-reihenlaenge" 36000.0 (tu-lg-summe (car _erg) "reihe"))
(tu-eqf "lg-fuellung/gedreht-rest" 1200.0 (cadr _erg))
;; L-Form, Nord + Ost: Stufenkante bekommt den Nord-Randgang und wird
;; an der Innenecke um die Ost-Breite verlaengert; Ost-Gaenge an den
;; konvexen Ecken gekuerzt
(setq _rs (lg-randgang-streifen _l 0.0 '(("N" . 1000.0) ("O" . 1000.0)) 100.0))
(tu-eq "lg-randgang/l-anzahl" 4 (length _rs))
(tu-eq "lg-randgang/l-nordkanten" 2
(length (vl-remove-if-not '(lambda (s) (= (car s) "N")) _rs)))
(tu-eqf "lg-randgang/l-stufe-verlaengert" '((10000.0 1900.0) (4000.0 1900.0))
(list (cadr (nth 1 _rs)) (caddr (nth 1 _rs))))
(setq _erg (lg-fuellung-berechnen _l (list (cons "gang" 1000.0) (cons "tiefe" 700.0) (cons "reihen" 2)
(cons "rand" '(("N" . 1000.0) ("O" . 1000.0))))))
(tu-eqf "lg-fuellung/l-rand-laenge" 13800.0 (tu-lg-summe (car _erg) "randgang"))
(tu-eqf "lg-fuellung/l-reihenlaenge" 13000.0 (tu-lg-summe (car _erg) "reihe"))
(tu-eqf "lg-fuellung/l-ganglaenge" 4000.0 (tu-lg-summe (car _erg) "gang"))
;; Sperrrechteck (manueller Gang) quer durch die Reihen
(setq _erg (lg-fuellung-berechnen _q (list (cons "gang" 1000.0) (cons "tiefe" 700.0) (cons "reihen" 2)
(cons "sperr" '(((4000.0 0.0) (5000.0 0.0) (5000.0 6000.0) (4000.0 6000.0)))))))
(tu-eq "lg-fuellung/sperr-reihenstuecke" 8 (tu-lg-anzahl (car _erg) "reihe"))
(tu-eqf "lg-fuellung/sperr-reihenlaenge" 36000.0 (tu-lg-summe (car _erg) "reihe"))
(tu-eqf "lg-fuellung/sperr-ganglaenge" 18000.0 (tu-lg-summe (car _erg) "gang"))
;; Reststuecke unter min_reihe fallen weg, Gangstuecke ab min_gang bleiben
(setq _erg (lg-fuellung-berechnen _q (list (cons "gang" 1000.0) (cons "tiefe" 700.0) (cons "reihen" 2)
(cons "sperr" '(((0.0 0.0) (9700.0 0.0) (9700.0 6000.0) (0.0 6000.0)))))))
(tu-eq "lg-fuellung/sperr-reststueck" 0 (tu-lg-anzahl (car _erg) "reihe"))
(tu-eq "lg-fuellung/sperr-gangstueck" 2 (tu-lg-anzahl (car _erg) "gang"))
;; --- Schraffur je Belag, Gang-Blockname ---
(setq _tab (ssg-json-parse
"{\"spanplatte\": {\"muster\": \"ANSI31\", \"massstab\": 50}, \"hallenboden\": null}"))
(tu-eqf "lg-schraffur-vorgabe/spanplatte" '("ANSI31" 50.0 0.0) (lg-schraffur-vorgabe "spanplatte" _tab))
(tu-nil "lg-schraffur-vorgabe/hallenboden" (lg-schraffur-vorgabe "hallenboden" _tab))
(tu-nil "lg-schraffur-vorgabe/unbekannt" (lg-schraffur-vorgabe "beton" _tab))
(tu-nil "lg-schraffur-vorgabe/nil" (lg-schraffur-vorgabe nil _tab))
(tu-eq "lg-gang-blockname/belag" "LG_GANG_12000_1000_SPANPLATTE"
(lg-gang-blockname 12000.0 1000.0 "spanplatte"))
(tu-eq "lg-gang-blockname/ohne" "LG_GANG_12000_1000" (lg-gang-blockname 12000.0 1000.0 ""))
;; --- Geschosszuordnung ---
(setq _sq0 '((0.0 0.0 0.0) (100.0 0.0 0.0) (100.0 100.0 0.0) (0.0 100.0 0.0))
_sq3 '((0.0 0.0 3100.0) (100.0 0.0 3100.0) (100.0 100.0 3100.0) (0.0 100.0 3100.0)))
(tu-eq "lg-geschoss-punkt/einzeln" 1
(lg-geschoss-fuer-punkt-kern '(50.0 50.0 0.0) (list (list 1 0.0 _sq0))))
(tu-nil "lg-geschoss-punkt/aussen"
(lg-geschoss-fuer-punkt-kern '(150.0 50.0 0.0) (list (list 1 0.0 _sq0))))
;; 2D: beide Konturen auf Z=0 uebereinander -> nicht eindeutig
(tu-nil "lg-geschoss-punkt/2d-mehrdeutig"
(lg-geschoss-fuer-punkt-kern '(50.0 50.0 0.0) (list (list 1 0.0 _sq0) (list 2 3100.0 _sq0))))
;; 3D: Konturen auf ihrer Belagshoehe -> Z entscheidet
(setq _kand (list (list 1 0.0 _sq0) (list 2 3100.0 _sq3)))
(tu-eq "lg-geschoss-punkt/3d-oben" 2 (lg-geschoss-fuer-punkt-kern '(50.0 50.0 3200.0) _kand))
(tu-eq "lg-geschoss-punkt/3d-unten" 1 (lg-geschoss-fuer-punkt-kern '(50.0 50.0 100.0) _kand))
;; --- Geschoss kopieren ---
(tu-eq "lg-geschoss-aus-layer/regal" 2 (lg-geschoss-aus-layer "LG_Regal_G2"))
(tu-eq "lg-geschoss-aus-layer/12" 12 (lg-geschoss-aus-layer "LG_Reihe_G12"))
(tu-nil "lg-geschoss-aus-layer/ohne" (lg-geschoss-aus-layer "LG_Reihe"))
(tu-nil "lg-geschoss-aus-layer/0" (lg-geschoss-aus-layer "0"))
(tu-nil "lg-geschoss-aus-layer/suffix" (lg-geschoss-aus-layer "LG_X_G2a"))
(tu-nil "lg-geschoss-aus-layer/nil" (lg-geschoss-aus-layer nil))
;; 2D uebereinander: Kontur mehrdeutig, Layer entscheidet
(setq _kand2 (list (list 1 0.0 _sq0) (list 2 3100.0 _sq0)))
(tu-eq "lg-rack-geschoss/layer" 2 (lg-rack-geschoss "LG_Regal_G2" '(50.0 50.0 0.0) _kand2))
(tu-nil "lg-rack-geschoss/mehrdeutig" (lg-rack-geschoss "0" '(50.0 50.0 0.0) _kand2))
(tu-eq "lg-rack-geschoss/kontur" 1 (lg-rack-geschoss "0" '(50.0 50.0 0.0) (list (list 1 0.0 _sq0))))
;; Layer eines nicht vorhandenen Geschosses zaehlt nicht
(tu-eq "lg-rack-geschoss/layer-fremd" 1 (lg-rack-geschoss "LG_Regal_G7" '(50.0 50.0 0.0) (list (list 1 0.0 _sq0))))
(tu-eq "lg-kopie-belag/folgt" "spanplatte" (lg-kopie-belag "hallenboden" "hallenboden" "spanplatte"))
(tu-eq "lg-kopie-belag/eigen" "gitterrost" (lg-kopie-belag "gitterrost" "hallenboden" "spanplatte"))
(tu-nil "lg-kopie-belag/nil" (lg-kopie-belag nil "hallenboden" "spanplatte"))
(setq _w (lg-kopie-werte '(("ID" . "2A") ("GESCHOSS" . "1") ("LAYER" . "LG_Gang_G1") ("LAENGE" . "9000")
("BREITE_mm" . "1000") ("ART" . "gang") ("BELAG" . "hallenboden")
("BEREICH" . "3") ("QUELLE" . "manuell"))
2 5 "hallenboden" "spanplatte"))
(tu-eq "lg-kopie-werte/gang"
'(("GESCHOSS" . "2") ("BEREICH" . "5") ("BREITE_mm" . "1000") ("ART" . "gang")
("BELAG" . "spanplatte") ("QUELLE" . "manuell"))
_w)
(tu-eq "lg-kopie-werte/ohne-bereich" "" (cdr (assoc "BEREICH" (lg-kopie-werte '(("BEREICH" . "3")) 2 nil nil nil))))
(tu-eqf "lg-kopie-plan/2d"
'((1 3000.0 (0.0 0.0 0.0)) (2 6000.0 (0.0 0.0 0.0)))
(lg-kopie-plan 0.0 3000.0 2 0.0 0.0 nil))
(tu-eqf "lg-kopie-plan/versatz-3d"
'((1 6100.0 (20000.0 0.0 3000.0)) (2 9100.0 (40000.0 0.0 6000.0)))
(lg-kopie-plan 3100.0 3000.0 2 20000.0 0.0 T))
(tu-eqf "lg-kopie-abstand/unten" 3100.0 (lg-kopie-abstand-vorgabe 6200.0 3100.0 nil nil))
(tu-eqf "lg-kopie-abstand/lichte" 3000.0 (lg-kopie-abstand-vorgabe 0.0 nil 2750.0 250.0))
(tu-nil "lg-kopie-abstand/nichts" (lg-kopie-abstand-vorgabe 0.0 nil nil 250.0))
(tu-nil "lg-kopie-3d-p/2d" (lg-kopie-3d-p _kand2))
(tu-eq "lg-kopie-3d-p/3d" T (lg-kopie-3d-p _kand))
(tu-eq "lg-feld-pruefen/mm_vz" "-1500" (lg-feld-pruefen "mm_vz" "-1500"))
(tu-nil "lg-feld-pruefen/mm_vz-x" (lg-feld-pruefen "mm_vz" "abc"))
(tu-nil "lg-feld-pruefen/mm_vz-leer" (lg-feld-pruefen "mm_vz" ""))
;; --- Gang aus Rechteck ---
(tu-eqf "lg-gang-aus-ecken/quer"
'((0.0 500.0 0.0) (4000.0 500.0 0.0) 1000.0)
(lg-gang-aus-ecken '((0.0 0.0 0.0) (4000.0 0.0 0.0) (4000.0 1000.0 0.0) (0.0 1000.0 0.0))))
;; hochkant, Ecken gegen den Uhrzeigersinn ab oben rechts, Z uebernommen
(tu-eqf "lg-gang-aus-ecken/hoch"
'((600.0 5000.0 3100.0) (600.0 0.0 3100.0) 1200.0)
(lg-gang-aus-ecken '((1200.0 5000.0 3100.0) (0.0 5000.0 3100.0) (0.0 0.0 3100.0) (1200.0 0.0 3100.0))))
;; Breite auf ganze mm
(tu-eqf "lg-gang-aus-ecken/runden" 900.0
(caddr (lg-gang-aus-ecken '((0.0 0.0) (3000.0 0.0) (3000.0 899.6) (0.0 899.6)))))
;; Rundung verschiebt nur die Seite gegenueber der ersten Ecke
(tu-eqf "lg-gang-aus-ecken/rundung-erste-ecke-unten"
'((0.0 450.0 0.0) (3000.0 450.0 0.0) 900.0)
(lg-gang-aus-ecken '((0.0 0.0) (3000.0 0.0) (3000.0 899.6) (0.0 899.6))))
(tu-eqf "lg-gang-aus-ecken/rundung-erste-ecke-oben"
'((0.0 449.6 0.0) (3000.0 449.6 0.0) 900.0)
(lg-gang-aus-ecken '((0.0 899.6) (3000.0 899.6) (3000.0 0.0) (0.0 0.0))))
;; erste Ecke rechts: Linie beginnt an der rechten Querseite
(tu-eqf "lg-gang-aus-ecken/start-an-erster-ecke"
'((3000.0 450.0 0.0) (0.0 450.0 0.0) 900.0)
(lg-gang-aus-ecken '((3000.0 0.0) (0.0 0.0) (0.0 899.6) (3000.0 899.6))))
;; gedreht (UCS 90 Grad): laengere Seite bestimmt die Richtung
(tu-eqf "lg-gang-aus-ecken/gedreht"
'((-500.0 0.0 0.0) (-500.0 4000.0 0.0) 1000.0)
(lg-gang-aus-ecken '((0.0 0.0 0.0) (0.0 4000.0 0.0) (-1000.0 4000.0 0.0) (-1000.0 0.0 0.0))))
;; --- Zuschnitt durch von Hand gesetzte Objekte ---
;; Reihe 10000 x 700, Gang 1000 breit quer darueber -> zwei Reste
(tu-eqf "lg-linie-zuschnitt/quer" '((0.0 4500.0) (5500.0 10000.0))
(lg-linie-zuschnitt 10000.0 700.0
'(((4500.0 -2000.0) (5500.0 -2000.0) (5500.0 2000.0) (4500.0 2000.0))) 500.0))
;; Gang liegt nur an der Laengsseite an -> nichts zu schneiden
(tu-eq "lg-linie-zuschnitt/anliegend" 'unveraendert
(lg-linie-zuschnitt 10000.0 700.0
'(((0.0 350.0) (10000.0 350.0) (10000.0 1350.0) (0.0 1350.0))) 500.0))
(tu-eq "lg-linie-zuschnitt/weit-weg" 'unveraendert
(lg-linie-zuschnitt 10000.0 700.0
'(((20000.0 -500.0) (21000.0 -500.0) (21000.0 500.0) (20000.0 500.0))) 500.0))
(tu-nil "lg-linie-zuschnitt/ganz-ueberdeckt"
(lg-linie-zuschnitt 4000.0 700.0
'(((-100.0 -500.0) (4100.0 -500.0) (4100.0 500.0) (-100.0 500.0))) 500.0))
;; Rest 300 < Mindestlaenge 500 faellt weg
(tu-nil "lg-linie-zuschnitt/rest-zu-kurz"
(lg-linie-zuschnitt 10000.0 700.0
'(((300.0 -500.0) (10000.0 -500.0) (10000.0 500.0) (300.0 500.0))) 500.0))
(tu-eq "lg-quelle-rang" '(2 1 0 0)
(mapcar 'lg-quelle-rang '("manuell" "auto_geaendert" "auto" nil)))
;; --- goh_rack-Erkennung: Parameterwerte, Namen, Transformationen ---
(tu-eqf "lg-zahl-einheit/mm" 700.0 (lg-zahl-einheit "700 mm"))
(tu-eqf "lg-zahl-einheit/cm" 718.752 (lg-zahl-einheit "71.8752 cm"))
(tu-eqf "lg-zahl-einheit/m" 12900.0 (lg-zahl-einheit "12,9 m"))
(tu-eqf "lg-zahl-einheit/ohne" 2.0 (lg-zahl-einheit "2"))
(tu-eqf "lg-zahl-einheit/zahl" 3.0 (lg-zahl-einheit 3))
(tu-nil "lg-zahl-einheit/km" (lg-zahl-einheit "5 km"))
(tu-nil "lg-zahl-einheit/text" (lg-zahl-einheit "abc"))
(tu-nil "lg-zahl-einheit/leer" (lg-zahl-einheit ""))
(tu-nil "lg-zahl-einheit/nil" (lg-zahl-einheit nil))
(tu-eqf "lg-racks-auswerten"
'((1 2 3000.0 6000.0) (2 1 500.0 1500.0) (nil 2 700.0 1400.0))
(lg-racks-auswerten '((2 500.0 3.0) (nil 700.0 2.0) (1 1000.0 2.0)
(1 2000.0 2.0) (nil nil 2.0))))
(tu-nil "lg-racks-auswerten/leer" (lg-racks-auswerten nil))
(tu-eq "lg-name-brauchbar-p/name" T (lg-name-brauchbar-p "goh_rack"))
(tu-nil "lg-name-brauchbar-p/anonym" (lg-name-brauchbar-p "*U29"))
(tu-nil "lg-name-brauchbar-p/leer" (lg-name-brauchbar-p ""))
;; Einfuegung bei (100,0), 90 Grad, x-Skalierung 2: lokales (1,0,0) -> (100,2,0)
(tu-eqf "lg-insert-tf/drehung-skalierung" '(100.0 2.0 0.0)
(lg-tf-pkt (lg-insert-tf (list '(2 . "TU_KEIN_BLOCK") '(10 100.0 0.0 0.0)
(cons 50 (/ pi 2.0)) '(41 . 2.0))
0 0)
'(1.0 0.0 0.0)))
;; MINSERT-Spalte 2 bei Spaltenabstand 3000, gedreht um 90 Grad -> entlang +Y
(tu-eqf "lg-insert-tf/minsert" '(0.0 6000.0 0.0)
(car (lg-insert-tf (list '(2 . "TU_KEIN_BLOCK") '(10 0.0 0.0 0.0) (cons 50 (/ pi 2.0))
'(70 . 3) '(44 . 3000.0))
2 0)))
;; verschachtelt: Container bei (1000,0) um 90 Grad, Kind bei lokal (0,2000)
(tu-eqf "lg-tf-verketten" '(-1000.0 0.0 0.0)
(car (lg-tf-verketten
(lg-insert-tf (list '(2 . "TU_KEIN_BLOCK") '(10 1000.0 0.0 0.0) (cons 50 (/ pi 2.0))) 0 0)
(lg-insert-tf (list '(2 . "TU_KEIN_BLOCK") '(10 0.0 2000.0 0.0)) 0 0))))
(tu-nil "lg-gang-aus-ecken/entartet"
(lg-gang-aus-ecken '((0.0 0.0) (4000.0 0.0) (4000.0 0.0) (0.0 0.0))))
;; --- JSON-Schreiber ---
(tu-eq "lg-json-wert/nil" "null" (lg-json-wert nil))
(tu-eq "lg-json-wert/t" "true" (lg-json-wert T))
(tu-eq "lg-json-wert/int" "3" (lg-json-wert 3))
(tu-eq "lg-json-wert/real" "2.5" (lg-json-wert 2.5))
(tu-eq "lg-json-wert/rund" "1000" (lg-json-wert 1000.0))
(tu-eq "lg-json-wert/str" "\"a\\\"b\"" (lg-json-wert "a\"b"))
(tu-eq "lg-json-wert/roh" "[1]" (lg-json-wert (list 'ROH "[1]")))
(tu-eq "lg-json-obj" "{\"a\": 1, \"b\": \"x\"}" (lg-json-obj '(("a" . 1) ("b" . "x"))))
(tu-eq "lg-json-ebenen" "[{\"schienenhoehe_mm\": 2300, \"warenlaenge_mm\": 1200}]"
(cadr (lg-json-ebenen "2300/1200")))
;; --- Regelprofil-Aufloesung ---
(tu-eq "lg-bedingung/leer" 'ja (lg-bedingung-pruefen nil nil))
(tu-eq "lg-bedingung/gleich" 'ja (lg-bedingung-pruefen "brandgefaehrdung = normal"
'(("brandgefaehrdung" . "normal"))))
(tu-eq "lg-bedingung/ungleich" 'nein (lg-bedingung-pruefen "brandgefaehrdung = normal"
'(("brandgefaehrdung" . "erhoeht_ohne_loeschanlage"))))
(tu-eq "lg-bedingung/fehlt" 'unbekannt (lg-bedingung-pruefen "brandgefaehrdung = normal" nil))
(tu-eq "lg-bedingung/groesser" 'ja (lg-bedingung-pruefen "absturzhoehe > 12000" '(("absturzhoehe" . 13000))))
(tu-eq "lg-bedingung/kleinergl" 'nein (lg-bedingung-pruefen "absturzhoehe <= 12000" '(("absturzhoehe" . "13000"))))
;; min: strengster = groesster, max: strengster = kleinster
(setq _regel (ssg-json-parse
"{\"id\": \"s\", \"art\": \"min\", \"werte\": [{\"wert\": 590, \"quelle\": \"A\"}, {\"wert\": 600, \"quelle\": \"B\"}]}"))
(tu-eq "lg-regel/min-strengster" 600 (car (lg-regel-aufloesen _regel nil)))
(tu-eq "lg-regel/min-gewinner" "B" (ssg-json-get (cadr (lg-regel-aufloesen _regel nil)) "quelle"))
(setq _regel (ssg-json-parse
"{\"id\": \"s\", \"art\": \"max\", \"werte\": [{\"wert\": 650, \"quelle\": \"A\"}, {\"wert\": 660, \"quelle\": \"B\"}]}"))
(tu-eq "lg-regel/max-strengster" 650 (car (lg-regel-aufloesen _regel nil)))
(setq _regel (ssg-json-parse (strcat
"{\"id\": \"f\", \"art\": \"max\", \"werte\": ["
"{\"wert\": 35000, \"bedingung\": \"brandgefaehrdung = normal\"},"
"{\"wert\": 25000, \"bedingung\": \"brandgefaehrdung = erhoeht_ohne_loeschanlage\"}]}")))
(tu-eq "lg-regel/bedingung-normal" 35000
(car (lg-regel-aufloesen _regel '(("brandgefaehrdung" . "normal")))))
(tu-eq "lg-regel/bedingung-erhoeht" 25000
(car (lg-regel-aufloesen _regel '(("brandgefaehrdung" . "erhoeht_ohne_loeschanlage")))))
(tu-eq "lg-regel/bedingung-fehlt-konservativ" 25000 (car (lg-regel-aufloesen _regel nil)))
(tu-nil "lg-regel/nichts-zutreffend"
(lg-regel-aufloesen (ssg-json-parse
"{\"id\": \"x\", \"art\": \"max\", \"werte\": [{\"wert\": 1, \"bedingung\": \"a = b\"}]}")
'(("a" . "c"))))
(setq _regel (ssg-json-parse (strcat
"{\"id\": \"b\", \"art\": \"min\", \"werte\": [{\"tabelle\": [[5, 875], [20, 1000], [200, 1200]],"
" \"tabellen_schluessel\": \"personen\"}]}")))
(tu-eq "lg-regel/tabelle-15" 1000 (car (lg-regel-aufloesen _regel '(("personen" . 15)))))
(tu-eq "lg-regel/tabelle-5" 875 (car (lg-regel-aufloesen _regel '(("personen" . 5)))))
(tu-eq "lg-regel/tabelle-ueber" 1200 (car (lg-regel-aufloesen _regel '(("personen" . 500)))))
(tu-eq "lg-regel/tabelle-fehlt" 1200 (car (lg-regel-aufloesen _regel nil)))
;; Vererbung: Kind ersetzt Regel b, ergaenzt c, Kopf vom Kind
(setq _prof (lg-profil-mischen
(ssg-json-parse "{\"land\": \"DE\", \"regeln\": [{\"id\": \"a\", \"x\": 1}, {\"id\": \"b\", \"x\": 1}]}")
(ssg-json-parse "{\"land\": \"SK\", \"regeln\": [{\"id\": \"b\", \"x\": 2}, {\"id\": \"c\", \"x\": 3}]}")))
(tu-eq "lg-profil-mischen/ids" '("a" "b" "c")
(mapcar '(lambda (r) (ssg-json-get r "id")) (ssg-json-get _prof "regeln")))
(tu-eq "lg-profil-mischen/ersetzt" 2 (ssg-json-get (lg-regel-finden _prof "b") "x"))
(tu-eq "lg-profil-mischen/kopf" "SK" (ssg-json-get _prof "land"))
;; echte Profile (wie tu-tests-json gegen die Geruest-Daten): SK erbt DE
(if (and (lg-regelwerk-pfad "SK") (findfile (lg-regelwerk-pfad "SK")))
(progn
(setq _prof (lg-profil-laden-roh "SK" nil))
(tu-eq "lg-profil/sk-erbt-de" 190
(car (lg-regel-aufloesen (lg-regel-finden _prof "treppe.steigung_max") nil)))
(tu-eq "lg-profil/laufhoehe" 3000
(car (lg-regel-aufloesen (lg-regel-finden _prof "treppe.laufhoehe_max") nil)))
(tu-eq "lg-profil/schrittmass-schnittmenge" 600
(car (lg-regel-aufloesen (lg-regel-finden _prof "treppe.schrittmass_min") nil)))
(tu-eq "lg-profil/gelaender-14122" 1100
(car (lg-regel-aufloesen (lg-regel-finden _prof "gelaender.hoehe_min")
'(("absturzhoehe" . 3100)))))
(tu-nil "lg-profil/sk-ungepflegt" (ssg-json-get _prof "gepflegt")))
(tu-record nil "lg-profil/dateien" "cfg/regelwerk/SK.json nicht gefunden (DXFM_CFG?)"))
)
)
)
(defun c:TEST_UNIT ( / out-dir report) (defun c:TEST_UNIT ( / out-dir report)
(tu-reset) (tu-reset)
(princ "\n================================================================") (princ "\n================================================================")
@@ -3049,7 +3518,7 @@
"tu-tests-export" "tu-tests-hoehen" "tu-tests-gf" "tu-tests-omni" "tu-tests-kreisel" "tu-tests-kreisel-bp" "tu-tests-export" "tu-tests-hoehen" "tu-tests-gf" "tu-tests-omni" "tu-tests-kreisel" "tu-tests-kreisel-bp"
"tu-tests-sepliste" "tu-tests-json" "tu-tests-geruest" "tu-tests-sepliste" "tu-tests-json" "tu-tests-geruest"
"tu-tests-sepscan" "tu-tests-vfl-segs" "tu-tests-vfl-abbruch" "tu-tests-sepscan" "tu-tests-vfl-segs" "tu-tests-vfl-abbruch"
"tu-tests-werkzeuge" "tu-tests-kf" "tu-tests-hoehen-fam") "tu-tests-werkzeuge" "tu-tests-poly" "tu-tests-lager" "tu-tests-kf" "tu-tests-hoehen-fam")
(tu-run-block fn)) (tu-run-block fn))
(princ "\n\n----------------------------------------------------------------") (princ "\n\n----------------------------------------------------------------")