Merge branch 'goh-storage'

This commit is contained in:
2026-10-30 13:36:54 +01:00
26 changed files with 5919 additions and 173 deletions
+9 -3
View File
File diff suppressed because one or more lines are too long
+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`
+10
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-
+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
;; ------------------------------------------------------------ ;; ------------------------------------------------------------
+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: [...]}
+15
View File
@@ -176,6 +176,21 @@ planquadrate = ZZ_Planquadrate, 8
kettenfoerderer = Omni_Kettenfoerderer, 4 kettenfoerderer = Omni_Kettenfoerderer, 4
# ============================================================
# 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
# ============================================================ # ============================================================
# Farben fuer Layer OHNE Baugruppen-Key # Farben fuer Layer OHNE Baugruppen-Key
# ============================================================ # ============================================================
+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": []
}
+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.
+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;
}
+98
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>/).",
+98
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>/).",
+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
+436 -1
View File
@@ -3066,6 +3066,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================================================================")
@@ -3078,7 +3513,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----------------------------------------------------------------")