From 5193f4018cb6039818158e81d45d3e9cfd8eef12 Mon Sep 17 00:00:00 2001 From: Simon Hensch Date: Wed, 16 Sep 2026 09:54:13 +0200 Subject: [PATCH] =?UTF-8?q?[ADD]=20Ger=C3=BCstoptionen=20werden=20nun=20mi?= =?UTF-8?q?t=20dem=20Befehl=20GERUEST=20in=20Bricscad=20aufgerufen;=20?= =?UTF-8?q?=C3=9Cber=20ein=20Dropdown=20in=20den=20Oberfl=C3=A4chen=20kann?= =?UTF-8?q?=20zwischen=20verschiedenen=20Arten=20(S=C3=A4ulen,=20Abh=C3=A4?= =?UTF-8?q?nger,...)=20umgeschaltet=20werden,=20dann=20erscheint=20ein=20n?= =?UTF-8?q?eues=20Men=C3=BC;=20Zusammenstellung=20der=20Anbauteile=20?= =?UTF-8?q?=C3=BCber=20die=20Oberfl=C3=A4che=20mit=20Anzahl=20und=20Checkb?= =?UTF-8?q?ox;=20Speichern=20von=20konfigurierten=20Zusammenstellungen=20n?= =?UTF-8?q?och=20fehlerhaft?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 + CLAUDE.md | 5 +- Lisp/GeruestInsert.lsp | 1082 ++++++++++++++++++++++---- Lisp/ssg_core.lsp | 27 + data/geruest/R2-Saeule_dummy.dwg | Bin 43607 -> 43832 bytes data/geruest/Rohrabhaenger_dummy.dwg | Bin 45273 -> 45816 bytes data/json/geruest.json | 152 ++-- dcl/geruest.dcl | 61 -- lang/de_DE.json | 16 +- lang/en_GB.json | 16 +- menu/SSG_LIB.mnu | 7 +- tests/test_unit.lsp | 145 +++- 12 files changed, 1239 insertions(+), 276 deletions(-) delete mode 100644 dcl/geruest.dcl diff --git a/.gitignore b/.gitignore index 86fe85e..eaacde0 100644 --- a/.gitignore +++ b/.gitignore @@ -190,6 +190,10 @@ data/block_libraries/ils_library.dxf # Tool-Caches .skylos/ +# Eigene Geruest-Konfigurationen (vom Dialog GERUEST_* geschrieben). +# Benutzerdaten - die gepflegte Bibliothek steht in data/json/geruest.json. +cfg/geruest_user.json + # Grosse Test-Zeichnungen (Arbeitsstaende, gehoeren nicht ins Repo) /tests/test.dwg /tests/test.dxf diff --git a/CLAUDE.md b/CLAUDE.md index ba8f525..b7a7371 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -75,7 +75,7 @@ Die `start_briscad.bat` ruft `setenv.bat` auf und startet BricsCAD. | VarioFoerderer | `VarioFoerderer` | FOERDERANLAGE, VARIOFOERDERER_EDIT, Vario_Kette_Merge | | Gefaellestrecke | `Gefaellestrecke` | GEFAELLESTRECKE, GEFAELLESTRECKE_EDIT | | Omniflo (komplett) | `OmniModulInsert` | OMNI_APB_*, OMNI_W*_*, OMNI_AP*, OMNI_TEF_*, OMNI_TV_*, OMNI_EDIT | -| Geruest | `GeruestInsert` | GERUEST_ROHRABHAENGER, GERUEST_SAEULE_2ZOLL, GERUEST_SAEULE_1ZOLL, GERUEST_WANDBEFESTIGUNG | +| Geruest | `GeruestInsert` | GERUEST (ein Dialog fuer alle Bereiche) | | Connections | `Connection_Edit` + `SSG_LIB_Commands` | CONNECTION_INSERT, CONNECTION_EDIT, ILS_EINGANG_INSERT, ILS_AUSGANG_INSERT | | Export | `export` | EXPORTSIVAS, EXPORTCSV, CALLPYTHON | | Konfiguration | `SSG_LIB_Commands` + `DrawPlanquadrate` | SSG_DIM_SWITCH, SSG_SPRACHE, PLANQUADRATE_TOGGLE (DRAW_PLANQUADRATE/DELETE_PLANQUADRATE) | @@ -107,7 +107,7 @@ Detaillierte Dokumentation aller Funktionen und Befehle: siehe `Lisp/README.md`. | `OmniModulInsert.lsp` | OMNI_LOAD, OMNI_APB_*, OMNI_W*_*, OMNI_AP60/AP110, OMNI_TEF_*, OMNI_TV_*, OMNI_APBW_*, OMNI_EDIT | Omniflo-Komponenten: Boegen, Weichen, Verbinder, Transferwagen, Edit-Dialog | | `SSG_LIB_Commands.lsp` | ILS_BTMT_Beladung, ILS_BTMT_Entladung, ILS_SCANNER_INSERT, ILS_SEPARATOR_INSERT, ILS_EINGANG_INSERT, ILS_AUSGANG_INSERT, ILS_ETF_*, ILS_GK*_*, SSG_BLOCKEDIT, SSG_DIM_SWITCH, SSG_SPRACHE | BTMT Be-/Entladung (`ils-insert-station`: wiederholtes Einfuegen von `BTMT-Beladung` bzw. `SC_Entladung`, Drehung interaktiv, Layer aus `LAYER`-Attribut, Dimension via `ssg-ils-block-laden`) + Sensoren (`ils-insert-sensor`) + Eingang-/Ausgang-Symbol (frei platzierbar wie BTMT Be-/Entladung, ueber `ils-insert-station` mit den Rohnamen `Eingang`/`Ausgang`, Bloecke `Eingang_.dwg`/`Ausgang_.dwg` in data/ils) + Dummy-Befehle fuer Menue + Doppelklick/Kontextmenue-Dispatcher + Konfiguration (2D/3D, Sprache) | | `Tro_Edit.lsp` | TRO_INSERT, TRO_EDIT | TRO-Marker (Block `TRO_SYM_`) fuer sps_skel (lib/tro_annotate.py): TRO_INSERT fuegt eine bestehende Blockdefinition an einem gewaehlten Punkt ein und setzt ID/TYPE (bricht ab, wenn der Typ noch keine Blockdefinition in der Zeichnung hat - die entsteht nur ueber tro_annotate.py); TRO_EDIT aendert ID/TYPE eines bestehenden Markers per Dialog. Typliste aus `tro_types.lsp` (von `tro_annotate.py --emit-lisp` nach DXFM_LISP geschrieben). Der Dispatcher SSG_BLOCKEDIT leitet Blocknamen `TRO_*` zu TRO_EDIT. | -| `GeruestInsert.lsp` | GERUEST_ROHRABHAENGER, GERUEST_SAEULE_2ZOLL, GERUEST_SAEULE_1ZOLL, GERUEST_WANDBEFESTIGUNG | Geruest-Baugruppen aus `data/json/geruest.json` konfigurieren und einfuegen. Dialog (`dcl/geruest.dcl`): Basis-Variante aus `geometrie_varianten` (Dropdown), Anbauteile aus `anbaugruppen` (`list_box` mit `multiple_select` - im Projekt der erste), Laenge (gegen `attribute.Laenge` min/max geprueft, Retry-Schleife) und Farbe (`attribute.Farbe`, Freitext bei `frei_option`). Baugruppen ohne `attribute`-Block (Wandbefestigung) bekommen Laenge/Farbe gesperrt. Der Block wird - wie KREISEL_n/VF_n - SELBST zusammengebaut: Dummy-DWG aus `data/geruest` liefert nur Geometrie, das Attribut-Schema kommt aus `*geruest-attrib-defs*` (ARTINR, LAENGE, BEZEICHNUNG, TEILEART, FARBE, LAYER) und wird per `ssg-attrib-merge` mit den Dummy-Tags zusammengefuehrt - sonst verschluckt `ssg-attrib-set-on` stillschweigend jeden Tag, den die Dummy-DWG nicht als ATTDEF kennt. Blockname `GR__` (Kollisionszaehler wie in OmniModulInsert). **Das `GR_`-Praefix ist nicht kosmetisch**: `SSG_BLOCKEDIT` haelt JEDEN Block mit TEILEART-Attribut fuer ein Omniflo-Element, deshalb steht in `SSG_LIB_Commands.lsp` ein `GR_*`-Zweig VOR dem Omniflo-Zweig. ARTINR listet jede Sivas-ID so oft, wie `menge` es vorgibt (benannte Gruppen mit `teile` werden rekursiv aufgeloest); BEZEICHNUNG verkettet mit `" + "`, NICHT mit Komma - die Artikelbezeichnungen enthalten selbst Kommas ("ROHR R 1\" 33,7 x 2,6"). Zuordnung Menue -> JSON laeuft ueber das ASCII-Feld `id`, weil die aeusseren JSON-Schluessel Umlaute bzw. Anfuehrungszeichen tragen. CSV-/Sivas-Export: `pattern_geruest = GR_*` (cfg/export.cfg), `csv:collect-export-blocks`/`ssg-id-collect-blocks` sammeln GR_-Bloecke wie BTMT eigenstaendig; `*geruest-attrib-defs*` traegt zusaetzlich ein leeres `ID`-ATTDEF (Voraussetzung fuer `ssg-id-check-all`, das nur bestehende ATTRIBs beschreiben kann). ARTINR/BEZEICHNUNG/TEILEART/LAENGE landen in export_csv.py/export_sivas.py in festen Spalten, FARBE/LAYER (+ ARTINR bei export_csv.py) im Merkmale-/details-JSON. Noch NICHT enthalten: Edit-Pfad (GERUEST_EDIT) | +| `GeruestInsert.lsp` | GERUEST | Geruest-Baugruppen aus `data/json/geruest.json` konfigurieren und einfuegen. **Ein Dialog fuer alle Bereiche**: oben ein Dropdown `bereich` (Rohrabhaenger / Saeulen / Wandbefestigung, Reihenfolge = Dateireihenfolge, Anzeigename aus dem ASCII-Feld `titel`). DCL hat **kein Reiter-Tile**, und da sich die Teilezeilen je Bereich unterscheiden, schliesst ein Bereichswechsel den Dialog (`done_dialog 4`) und die Schleife in `c:GERUEST` baut ihn neu auf; der Stand jedes Bereichs bleibt in `*geruest-zustand*` erhalten, ein versehentlicher Wechsel verwirft also nichts. 1"- und 2"-Saeulen liegen in EINER Baugruppe `Saeulen` mit gemeinsamer Anbauteil-Liste; **`teileart` und `dummy` duerfen je Geometrie-Variante ueberschrieben werden** (`geruest-variante-wert`, Variante gewinnt vor Baugruppe) - so liefert `R 1"` weiterhin TEILEART `Geruest Saeule 1 Zoll` und die `R 2"`-Varianten `Geruest Saeule 2 Zoll`, die Groessen bleiben im Export also unterscheidbar. **Die Dialogdefinition wird zur Laufzeit erzeugt** (`geruest-dcl-schreiben` -> temporaere Datei per `vl-filename-mktemp`, nach `unload_dialog` wieder geloescht) - es gibt KEINE `dcl/geruest.dcl`. Grund: je Anbauteil wird ein eigenes Mengenfeld gebraucht, und eine DCL-Liste (`list_box`) kann keine Eingabefelder enthalten; DCL kann weder Dateien einbinden noch Tiles zur Laufzeit erzeugen. Inhalt: Basis-Variante aus `geometrie_varianten` (Dropdown), Laenge (gegen `attribute.Laenge` min/max geprueft, Retry-Schleife), Farbe (`attribute.Teilefarbe`, Freitext bei `frei_option`) und **zwei Befestigungslisten** ("oben"/"unten"), die BEIDE alle `anbaugruppen` enthalten - dasselbe Teil kann oben, unten oder gleichzeitig an beiden Positionen sitzen, je mit eigener Stueckzahl (vorbelegt aus `menge`). Baugruppen ohne `attribute`-Block (Wandbefestigung) bekommen Laenge/Farbe gesperrt, Baugruppen ohne Anbauteile bekommen gar keine Spalten (ein leerer DCL-Cluster ist unzulaessig). Im generierten Label wird `"` zu `''` ersetzt (`geruest-dcl-label`) - fuenf Artikelnamen enthalten ein Anfuehrungszeichen, das das DCL-Label sonst vorzeitig beendet und die Datei zerstoert. **Konfigurationen**: gepflegte Bibliothek je Baugruppe unter `konfigurationen` in `geruest.json` (schreibgeschuetzt) plus eigene in `cfg/geruest_user.json` (gitignored, `geruest-user-schreiben`; getrennte Datei, damit Benutzerdaten und versionierte Bibliothek nicht kollidieren). Eigene Eintraege sind in der Liste mit `*` markiert und nur diese loeschbar. Konfigurationen verweisen ueber den ANZEIGENAMEN auf Variante und Anbauteile; ein unbekannter Name wird gemeldet und uebersprungen, statt die Konfiguration scheitern zu lassen. Speichern/Loeschen laufen ueber `done_dialog 2`/`3` (Dialog schliessen, Aktion, neu zeigen) - `getstring` funktioniert aus einem `action_tile`-Rueckruf heraus nicht. Der Block wird - wie KREISEL_n/VF_n - SELBST zusammengebaut: Dummy-DWG aus `data/geruest` liefert nur Geometrie, das Attribut-Schema kommt aus `*geruest-attrib-defs*` (ID, ARTINR, LAENGE, BEZEICHNUNG, TEILEART, TEILEFARBE, LAYER) und wird per `ssg-attrib-merge` mit den Dummy-Tags zusammengefuehrt - sonst verschluckt `ssg-attrib-set-on` stillschweigend jeden Tag, den die Dummy-DWG nicht als ATTDEF kennt. Blockname `GR__` (Kollisionszaehler wie in OmniModulInsert). **Das `GR_`-Praefix ist nicht kosmetisch**: `SSG_BLOCKEDIT` haelt JEDEN Block mit TEILEART-Attribut fuer ein Omniflo-Element, deshalb steht in `SSG_LIB_Commands.lsp` ein `GR_*`-Zweig VOR dem Omniflo-Zweig. ARTINR listet jede Sivas-ID so oft, wie die gewaehlte Stueckzahl es vorgibt - erst Basis, dann Befestigung oben, dann unten; bei einer benannten Gruppe (`teile`) wird das ganze Set n-mal wiederholt, die Mengen INNERHALB des Sets bleiben dabei erhalten (`geruest-artikelnummern-n`). BEZEICHNUNG nennt Position und Menge je Teil ("oben 2x ...") und verkettet mit `" + "`, NICHT mit Komma - die Artikelbezeichnungen enthalten selbst Kommas ("ROHR R 1\" 33,7 x 2,6"). Zuordnung Menue -> JSON laeuft ueber das ASCII-Feld `id`, weil die aeusseren JSON-Schluessel Umlaute bzw. Anfuehrungszeichen tragen. CSV-/Sivas-Export: `pattern_geruest = GR_*` (cfg/export.cfg), `csv:collect-export-blocks`/`ssg-id-collect-blocks` sammeln GR_-Bloecke wie BTMT eigenstaendig; `*geruest-attrib-defs*` traegt zusaetzlich ein leeres `ID`-ATTDEF (Voraussetzung fuer `ssg-id-check-all`, das nur bestehende ATTRIBs beschreiben kann). ARTINR/BEZEICHNUNG/TEILEART/LAENGE landen in export_csv.py/export_sivas.py in festen Spalten, TEILEFARBE/LAYER (+ ARTINR bei export_csv.py) im Merkmale-/details-JSON. Noch NICHT enthalten: Edit-Pfad (GERUEST_EDIT) | | `menu-inventory.lsp` | MenuInventory | BricsCAD Diagnostik (Menuegruppen, Plugins, Startdateien) | | `DrawPlanquadrate.lsp` | DRAW_PLANQUADRATE, DELETE_PLANQUADRATE, PLANQUADRATE_TOGGLE | Zeichnet auf Z=0 ein Planquadrat-Linienraster (senkrecht/waagerecht), im Abstand/Zaehlschema der aktiven `[Planquadrate]`-Konfiguration aus `cfg/export.cfg` (dieselbe Quelle wie `lib/export_planquadrat.py` fuer den CSV-Export). Bereich kommt NICHT aus den Zeichnungs-Extents, sondern aus derselben Blockmenge wie EXPORTCSV (`csv:collect-export-blocks`, export.lsp; siehe `plq-export-bbox`): Ursprung (Index 1) UND obere Grenze kommen BEIDE aus der tatsaechlichen Bounding-Box jedes Blocks (`csv:get-bbox`, wie `bbox_columns()`/`planquadrat_column()`/`_bbox_min()` in `lib/export_csv.py` bzw. `lib/export_planquadrat.py`), NICHT aus dem reinen Einfuegepunkt - bei einem gedrehten/exzentrisch eingefuegten Block (z.B. ein um 90 Grad gedrehter Kreisel) kann das Bounding-Box-Zentrum weit vom Einfuegepunkt abweichen; Ursprung und Zellzuordnung muessen auf derselben Referenz beruhen, sonst driften sie auseinander (realer Bug, behoben). Reine Hilfsgeometrie (Kreisel-Umfahrungsboegen als eigene Linien, Texte) zaehlt bewusst nicht mit, sonst weicht das Raster vom CSV-Export ab. `x_origin`/`y_origin` ueberschreiben den Ursprung, Fallback auf EXTMIN/EXTMAX falls keine Export-Bloecke vorhanden sind bzw. `csv:get-bbox` fuer einen Block scheitert. Spalten-/Zeilenzahl wird IMMER aufgerundet auf volle Rasterschritte, damit alle senkrechten Linien auf der letzten waagerechten enden und umgekehrt - das Raster bildet immer ein geschlossenes Rechteck. Zaehlung (alphabetisch/numerisch je Achse) als Text am linken/unteren Rand. Das fertige Raster (Linien+Texte) wird per `_.-BLOCK`/`_.INSERT` zu einem einzigen Block `ZZ_PLANQUADRATE` bei (0,0,0) zusammengefasst. Layer aus `cfg/layer.cfg` (`[allgemein]` Key `planquadrate`, Fallback `ZZ_Planquadrate`). DRAW_PLANQUADRATE entfernt vor dem Neuaufbau zuerst einen evtl. vorhandenen Block (`plq-block-entfernen`: INSERTs loeschen + `_-PURGE _B`), DELETE_PLANQUADRATE ruft nur diese Entfernung auf. PLANQUADRATE_TOGGLE ("Planquadrate an/aus", Menue: Konfiguration) prueft per `tblsearch`/`ssget "X"`, ob der Block existiert, und dispatcht auf Zeichnen bzw. Loeschen. Lazy geladen ueber `(ssg-ensure "DrawPlanquadrate")`, nicht in der Core-Ladereihenfolge. Die Planquadrat-SPALTE im CSV-Export (`export_csv.py::planquadrat_column`/`resolve_origins`, `lib/export_planquadrat.py::compute_planquadrat`/`_bbox_min`) rechnet ebenfalls durchgaengig auf Bounding-Box-Basis (nicht nur Einfuegepunkt) und liefert eine SPANNE ("A/1-J/1") statt nur einer Zelle, wenn ein Element mehr als eine Zelle ueberdeckt (Kreisel, lange VF-/GF-Ketten). | @@ -123,7 +123,6 @@ Detaillierte Dokumentation aller Funktionen und Befehle: siehe `Lisp/README.md`. | `sprache.dcl` | SSG_SPRACHE (SSG_LIB_Commands.lsp) | | `tro_insert.dcl` | TRO_INSERT (Tro_Edit.lsp) | | `tro_edit.dcl` | TRO_EDIT (Tro_Edit.lsp) | -| `geruest.dcl` | Alle GERUEST_*-Befehle (GeruestInsert.lsp, `geruest-dialog`) - ein Dialog fuer alle Baugruppen, Listen zur Laufzeit gefuellt | | `vfl_edit.dcl` | Linienzug-Editieren/Fortsetzen (vf_linienzug.lsp, `vfl-dlg-position`, aufgerufen aus `vfl-edit-ent`) | | `vf_linienzug_wizard.dcl` | Linienzug-Assistent (Modus 1, 2 UND 3): zwei generische Einzel-Dialoge (Zahl-Eingabe, Mehrfachauswahl; `vflw-zahl`/`vflw-wahl`, aufgerufen aus `vfl-in-real`/`vfl-in-string`/`vfl-in-int` bei aktivem `*vfl-wizard-mode*`) PLUS sieben Gruppen-Dialoge, die mehrere immer unmittelbar aufeinanderfolgende Fragen in einem Bildschirm zusammenfassen (Kettenstart, AS-Element, GF-Bogen, ES-Element, Vario-Kurve, Gefaelle festlegen, Ziel-Hoehe, Horizontales Stueck - inkl. Punktwahl-Button mit X/Y/Z-Anzeige; `vflw-gruppe-*-impl` in vf_linienzug.lsp fuellen dafuer vorab eine kleine Pending-Queue `*vflw-pending*`, die einzelnen `vfl-in-*`-Aufrufe bleiben dabei unveraendert) PLUS drei Segment-Gruppen-Dialoge fuer die segmentweise Klassifizierung in Modus 2/3 (`vflw_seg_linie_m2` = Typ+GF-Neigung, `vflw_seg_linie_m3` = 4-fach-Typ+kontextabh. Wertfeld, `vflw_seg_bogen` = Typ+Variante; `vflw-seg-*-impl`). Beide Modi heben vor der Frage das aktuelle Pfad-Segment (LINE/ARC) per `vfl-seg-highlight` (`redraw`) in der Zeichnung hervor und zeigen "Segment i/n ..." im Dialogkopf. Details: `doc/variofoerderer.md` | diff --git a/Lisp/GeruestInsert.lsp b/Lisp/GeruestInsert.lsp index 2ce823c..16732c8 100644 --- a/Lisp/GeruestInsert.lsp +++ b/Lisp/GeruestInsert.lsp @@ -1,10 +1,20 @@ ;; ============================================================ ;; GeruestInsert.lsp - Geruest-Baugruppen konfigurieren und einfuegen ;; -;; Liest data/json/geruest.json (rekursiv per ssg-json-laden), -;; laesst den Nutzer im Dialog dcl/geruest.dcl eine Basis-Variante, beliebige -;; Anbauteile, Laenge und Farbe waehlen, und fuegt das Ergebnis als Block -;; GR__ ein. +;; Liest data/json/geruest.json (rekursiv per ssg-json-laden), laesst den +;; Nutzer eine Basis-Variante, Anbauteile fuer "Befestigung oben" UND +;; "Befestigung unten" (je mit eigener Stueckzahl), Laenge und Farbe waehlen, +;; und fuegt das Ergebnis als Block GR__ ein. +;; +;; Beide Befestigungslisten enthalten ALLE Anbauteile der Baugruppe - dasselbe +;; Teil kann oben, unten oder gleichzeitig an beiden Positionen sitzen. +;; +;; Die Dialogdefinition wird zur Laufzeit erzeugt (geruest-dcl-schreiben): +;; eine DCL-Liste kann keine Eingabefelder enthalten, ein Mengenfeld je Teil +;; ist also nur mit generierten Zeilen moeglich. +;; +;; Konfigurationen kommen aus zwei getrennten Quellen: der gepflegten +;; Bibliothek in geruest.json und den eigenen in cfg/geruest_user.json. ;; ;; Der Block wird - wie KREISEL_n/VF_n - selbst zusammengebaut: die Dummy-DWG ;; aus data/geruest liefert nur die Geometrie, das Attribut-Schema kommt aus @@ -16,8 +26,13 @@ ;; TEILEART-Attribut fuer ein Omniflo-Element (SSG_LIB_Commands.lsp). Der ;; Doppelklick-Dispatcher erkennt Geruest-Bloecke an diesem Praefix. ;; -;; Befehle: GERUEST_ROHRABHAENGER, GERUEST_SAEULE_2ZOLL, -;; GERUEST_SAEULE_1ZOLL, GERUEST_WANDBEFESTIGUNG +;; Alle Bereiche liegen in EINEM Dialog; oben wird per Dropdown zwischen +;; Rohrabhaenger, Saeulen und Wandbefestigung umgeschaltet. DCL hat kein +;; Reiter-Tile, und da sich die Teilezeilen je Bereich unterscheiden, wird der +;; Dialog beim Wechsel geschlossen und neu aufgebaut - der Stand jedes +;; Bereichs bleibt dabei in *geruest-zustand* erhalten. +;; +;; Befehl: GERUEST ;; ============================================================ (vl-load-com) @@ -36,7 +51,7 @@ ("LAENGE" "") ("BEZEICHNUNG" "") ("TEILEART" "") - ("FARBE" "") + ("TEILEFARBE" "") ("LAYER" "") ) ) @@ -89,22 +104,39 @@ *geruest-daten* ) -;; Baugruppe ueber ihr ASCII-Feld "id" suchen. Der aeussere JSON-Schluessel -;; ("Rohrabhaenger" mit Umlaut, "2\" Saeulen" mit Anfuehrungszeichen) taugt -;; dafuer nicht - er ist aus ASCII-Quelltext nicht vergleichbar. -(defun geruest-baugruppe (id / erg) +;; Alle Baugruppen als ((id . objekt) ...) in Dateireihenfolge - diese +;; Reihenfolge bestimmt auch die Reihenfolge im Bereichs-Dropdown. +(defun geruest-baugruppen-liste ( / erg) + (setq erg nil) (foreach eintrag (geruest-daten) - ;; equal statt = : fehlt einer Baugruppe das id-Feld, liefert ssg-json-get - ;; nil - = ist mit nil-Argumenten nicht zuverlaessig. - (if (and (null erg) - (listp (cdr eintrag)) - (equal (ssg-json-get (cdr eintrag) "id") id)) - (setq erg (cdr eintrag)) + (if (listp (cdr eintrag)) + (setq erg (append erg + (list (cons (ssg-json-get (cdr eintrag) "id") (cdr eintrag))))) ) ) erg ) +;; Anzeigename einer Baugruppe fuer Dropdown und Kopfzeile. Bewusst das +;; ASCII-Feld "titel" und nicht der aeussere JSON-Schluessel - der traegt +;; Umlaute ("Rohrabhänger", "Säulen"). +(defun geruest-titel (bg / t1) + (setq t1 (ssg-json-get bg "titel")) + (if (and t1 (= (type t1) 'STR) (/= t1 "")) + t1 + (ssg-json-get bg "teileart") + ) +) + +;; Wert einer Variante mit Rueckfall auf die Baugruppe. Genutzt fuer +;; "teileart" (die Saeulen-Varianten unterscheiden 1 Zoll von 2 Zoll, was im +;; Export sichtbar bleiben muss) und "dummy" (eine Variante darf eine eigene +;; DWG mitbringen, sobald es sie gibt). +(defun geruest-variante-wert (variante bg schluessel / w) + (setq w (if variante (ssg-json-get variante schluessel))) + (if (and w (/= w "")) w (ssg-json-get bg schluessel)) +) + ;; ============================================================ ;; TEILE AUSWERTEN @@ -168,37 +200,673 @@ ;; DIALOG ;; ============================================================ -;; "0 3 5" -> (0 3 5). Rueckgabe von get_tile bei multiple_select. -(defun geruest-indizes (s / erg tok i c) - (setq erg nil tok "" i 1) - (if (null s) (setq s "")) - (while (<= i (strlen s)) - (setq c (substr s i 1)) - (if (= c " ") - (progn - (if (/= tok "") (setq erg (cons (atoi tok) erg))) - (setq tok "") - ) - (setq tok (strcat tok c)) - ) +;; Reine Ziffernfolge? atoi allein taugt als Pruefung nicht - "abc" liefert +;; dort still 0. +(defun geruest-zahl-p (s / i c ok) + (setq ok (> (strlen s) 0) i 1) + (while (and ok (<= i (strlen s))) + (setq c (ascii (substr s i 1))) + (if (or (< c 48) (> c 57)) (setq ok nil)) + (setq i (1+ i)) + ) + ok +) + +;; Eingabe aus einem Mengenfeld auswerten. +;; Leer -> Vorgabewert aus dem JSON, gueltig -> Zahl >= 1, sonst nil. +(defun geruest-menge-wert (s vorgabe) + (setq s (ssg-cfg-trim (if s s ""))) + (cond + ((= s "") vorgabe) + ((not (geruest-zahl-p s)) nil) + ((< (atoi s) 1) nil) + (T (atoi s)) + ) +) + +;; Zahl aus dem JSON als Anzeigetext (fehlender Wert -> ""). +(defun geruest-zahl-string (w) + (cond + ((null w) "") + ((= (type w) 'STR) w) + ((= (type w) 'INT) (itoa w)) + ((= (type w) 'REAL) (rtos w 2 0)) + (T "") + ) +) + +;; Artikelnummern eines Eintrags bei frei gewaehlter Stueckzahl. +;; Einzelteil: die Sivas-ID anzahl-mal. Benannte Gruppe ("teile"): das +;; komplette Set anzahl-mal - die Mengen INNERHALB des Sets bleiben erhalten +;; (ein Schraubenset bleibt 1 Scheibe + 2 Muttern + 1 Schraube je Satz). +(defun geruest-artikelnummern-n (eintrag anzahl / teile einmal erg i) + (setq teile (ssg-json-get eintrag "teile")) + (if teile + (progn + (setq einmal nil) + (foreach teil teile + (setq einmal (append einmal (geruest-artikelnummern teil))) + ) + (setq erg nil i 0) + (while (< i anzahl) + (setq erg (append erg einmal)) + (setq i (1+ i)) + ) + erg + ) + (geruest-wiederholen + (geruest-id-string (ssg-json-get eintrag "sivas_id")) anzahl) + ) +) + +;; Index eines Eintrags (Variante oder Anbauteil) ueber seinen Anzeigenamen. +(defun geruest-name-index (name liste / i idx) + (setq i 0 idx nil) + (foreach e liste + (if (and (null idx) (equal (geruest-eintrag-name e) name)) (setq idx i)) + (setq i (1+ i)) + ) + idx +) + +;; Schluessel in einer Alist setzen oder anhaengen. +(defun geruest-alist-setzen (alist key wert) + (if (assoc key alist) + (subst (cons key wert) (assoc key alist) alist) + (append alist (list (cons key wert))) + ) +) + +;; n-tes Element ersetzen - AutoLISP kann nth nicht schreibend nutzen. +(defun geruest-liste-setzen (lst idx wert / erg i) + (setq erg nil i 0) + (foreach e lst + (setq erg (cons (if (= i idx) wert e) erg)) (setq i (1+ i)) ) - (if (/= tok "") (setq erg (cons (atoi tok) erg))) (reverse erg) ) -;; Dialog zeigen. Rueckgabe: -;; (variante-index (anbau-indizes) laenge-string farbe-string) oder nil -(defun geruest-dialog (bg / dcl-pfad dat varianten anbauteile attr laenge-def - farb-opts frei-opt fertig rueckgabe ergebnis - dlg-variante dlg-anbau dlg-laenge dlg-farbe dlg-frei - min-l max-l wert-l farbe-txt) - (setq dcl-pfad (strcat (getenv "DXFM_DCL") "/geruest.dcl")) - (setq varianten (ssg-json-get bg "geometrie_varianten")) - (setq anbauteile (ssg-json-get bg "anbaugruppen")) +(defun geruest-nil-liste (n / erg i) + (setq erg nil i 0) + (while (< i n) (setq erg (cons nil erg)) (setq i (1+ i))) + erg +) + + +;; ============================================================ +;; KONFIGURATIONEN +;; ============================================================ +;; Zwei Quellen: die gepflegte Bibliothek in data/json/geruest.json +;; (schreibgeschuetzt) und die eigenen Zusammenstellungen in +;; cfg/geruest_user.json. Beide werden NICHT vermischt - Benutzerdaten in der +;; versionierten Datendatei wuerden bei jedem Git-Update kollidieren. + +(defun geruest-user-pfad ( / ) + (cond + ((getenv "DXFM_CFG") + (strcat (vl-string-translate "\\" "/" (getenv "DXFM_CFG")) + "/geruest_user.json")) + ((getenv "DXFMAKRO") + (strcat (vl-string-translate "\\" "/" (getenv "DXFMAKRO")) + "/cfg/geruest_user.json")) + ) +) + +;; Benutzerdatei laden (Baugruppen-id -> Liste von Konfigurationen). +;; Eine fehlende Datei ist kein Fehler - sie entsteht beim ersten Speichern. +(defun geruest-user-daten ( / pfad) + (if (null *geruest-user-daten*) + (progn + (setq pfad (geruest-user-pfad)) + (if (and pfad (findfile pfad)) + (setq *geruest-user-daten* (ssg-json-laden pfad)) + ) + ) + ) + *geruest-user-daten* +) + +;; Alle Konfigurationen einer Baugruppe, Bibliothek zuerst. +;; Eintrag: (benutzer-p . json-objekt) +(defun geruest-konfigs (bg / erg) + (setq erg nil) + (foreach k (ssg-json-get bg "konfigurationen") + (setq erg (append erg (list (cons nil k)))) + ) + (foreach k (ssg-json-get (geruest-user-daten) (ssg-json-get bg "id")) + (setq erg (append erg (list (cons T k)))) + ) + erg +) + +;; Anzeigename; eigene Eintraege mit * markiert, damit erkennbar ist, was +;; geloescht werden kann. +(defun geruest-konfig-name (k) + (strcat (if (car k) "* " "") (ssg-json-get (cdr k) "name")) +) + +;; Listenindex einer Konfiguration im popup_list (0 = "", die +;; Konfigurationen beginnen deshalb bei 1). 0, wenn nicht gefunden. +(defun geruest-konfig-index (name / i idx) + (setq i 1 idx 0) + (foreach k *geruest-konfig-liste* + (if (and (= idx 0) (equal (ssg-json-get (cdr k) "name") name)) + (setq idx i)) + (setq i (1+ i)) + ) + idx +) + +;; Positionsliste ("oben"/"unten") einer Konfiguration auf die Anbauteile +;; abbilden. Rueckgabe: ein Eintrag je Anbauteil - nil (nicht gewaehlt) oder +;; die Stueckzahl. Ein unbekannter Artikelname wird gemeldet und +;; uebersprungen, statt die ganze Konfiguration scheitern zu lassen. +(defun geruest-konfig-position (konfig schluessel anbauteile / mengen idx n) + (setq mengen (geruest-nil-liste (length anbauteile))) + (foreach e (ssg-json-get konfig schluessel) + (setq idx (geruest-name-index (ssg-json-get e "artikel") anbauteile)) + (if idx + (progn + (setq n (ssg-json-get e "menge")) + (setq mengen (geruest-liste-setzen mengen idx + (if (and n (numberp n) (> n 0)) (fix n) 1))) + ) + (princ (ssg-textf "gr-konfig-teil-unbekannt" + (list (ssg-json-get e "artikel") + (ssg-json-get konfig "name")))) + ) + ) + mengen +) + + +;; ============================================================ +;; DIALOGDEFINITION ERZEUGEN +;; ============================================================ +;; Die Anbauteil-Zeilen haengen an der Baugruppe, deshalb wird die DCL-Datei +;; pro Aufruf geschrieben statt fest vorgehalten: DCL kann weder Dateien +;; einbinden noch Tiles zur Laufzeit erzeugen, und eine Liste (list_box) kann +;; keine Eingabefelder enthalten - ohne Generierung gaebe es also kein +;; Mengenfeld je Teil. + +;; Ein Anfuehrungszeichen im Artikelnamen wuerde das DCL-Label vorzeitig +;; beenden und die erzeugte Datei unbrauchbar machen (fuenf Bezeichnungen +;; enthalten eines, z.B. ROHRKLAMMER 1" KPL. VERTIKAL). Betrifft nur die +;; Anzeige - ARTINR und BEZEICHNUNG nutzen weiter den Originalnamen. +(defun geruest-dcl-label (s / i c erg) + (if (null s) (setq s "")) + (setq erg "" i 1) + (while (<= i (strlen s)) + (setq c (substr s i 1)) + (setq erg (strcat erg (if (= c "\"") "''" c))) + (setq i (1+ i)) + ) + erg +) + +;; Eine Befestigungsspalte. Das Mengenfeld steht LINKS vom Ankreuzfeld: die +;; Eingabefelder sind dadurch alle gleich breit und die Teilenamen beginnen +;; auf gleicher Hoehe - DCL richtet Zeileninhalte nicht von sich aus aus. +;; +;; Zwei DCL-Eigenheiten muessen dafuer gesetzt werden: +;; - alignment = left, weil DCL Tiles in einer Spalte sonst ZENTRIERT: bei +;; unterschiedlich langen Teilenamen wandern die Zeilen sonst verschieden +;; weit nach rechts und die Liste franst aus. +;; - fixed_width = true auf der Zeile, weil eine gestreckte row ihre Tiles +;; ueber die volle Breite VERTEILT - zwischen Mengenfeld und Ankreuzfeld +;; klaffte dadurch eine wechselnd grosse Luecke. Mit fixed_width wird die +;; Zeile nur so breit wie ihr Inhalt, das Ankreuzfeld sitzt also direkt am +;; Mengenfeld. +(defun geruest-dcl-spalte (f anbauteile prefix titel / i) + (write-line " : boxed_column {" f) + (write-line (strcat " label = \"" titel "\";") f) + (write-line " alignment = left;" f) + (setq i 0) + (foreach a anbauteile + (write-line " : row {" f) + (write-line " alignment = left;" f) + (write-line " fixed_width = true;" f) + (write-line (strcat " : edit_box { key = \"" prefix "m" (itoa i) + "\"; label = \"\"; edit_width = 3;" + " fixed_width = true; }") f) + (write-line (strcat " : toggle { key = \"" prefix (itoa i) + "\"; label = \"" + (geruest-dcl-label (geruest-eintrag-name a)) + "\"; fixed_width = true; }") f) + (write-line " }" f) + (setq i (1+ i)) + ) + (write-line " }" f) +) + +;; Rueckgabe: Pfad der temporaeren DCL-Datei oder nil. +(defun geruest-dcl-schreiben (anbauteile / pfad f) + (setq pfad (vl-filename-mktemp "geruest" nil ".dcl")) + (setq f (open pfad "w")) + (if (null f) + nil + (progn + (write-line "geruest : dialog {" f) + (write-line " label = \"Geruest-Baugruppe konfigurieren\";" f) + ;; Bereichswechsel (Rohrabhaenger/Saeulen/Wandbefestigung). DCL hat kein + ;; Reiter-Tile, deshalb ein Dropdown; sein Rueckruf schliesst den Dialog + ;; und die Schleife baut ihn mit dem neuen Bereich neu auf - die + ;; Teilezeilen unterscheiden sich je Bereich und lassen sich zur + ;; Laufzeit nicht austauschen. + (write-line " : popup_list { key = \"bereich\"; label = \"Bereich:\"; width = 30; }" f) + (write-line " : text { key = \"kopf\"; label = \"\"; width = 64; }" f) + (write-line " : row {" f) + (write-line " : popup_list { key = \"konfig\"; label = \"Konfiguration:\"; width = 40; }" f) + (write-line " : button { key = \"kspeichern\"; label = \"Speichern...\"; fixed_width = true; }" f) + (write-line " : button { key = \"kloeschen\"; label = \"Loeschen\"; fixed_width = true; }" f) + (write-line " }" f) + (write-line " : boxed_column {" f) + (write-line " label = \"Basis\";" f) + (write-line " : popup_list { key = \"variante\"; label = \"Variante:\"; width = 56; }" f) + (write-line " : row {" f) + (write-line " : edit_box { key = \"laenge\"; label = \"Laenge (mm):\"; edit_width = 10; }" f) + (write-line " : popup_list { key = \"farbe\"; label = \"Farbe:\"; width = 25; }" f) + (write-line " : edit_box { key = \"farbe_frei\"; label = \"eigene:\"; edit_width = 12; }" f) + (write-line " }" f) + (write-line " }" f) + ;; Ohne Anbauteile bleiben die Spalten weg - ein leerer Cluster ist + ;; in DCL nicht zulaessig (betrifft z.B. die 1-Zoll-Saeule). + (if (> (length anbauteile) 0) + (progn + (write-line " : row {" f) + (geruest-dcl-spalte f anbauteile "o" "Befestigung oben (Anzahl / Teil)") + (geruest-dcl-spalte f anbauteile "u" "Befestigung unten (Anzahl / Teil)") + (write-line " }" f) + ) + ) + (write-line " ok_cancel;" f) + (write-line "}" f) + (close f) + pfad + ) + ) +) + + +;; ============================================================ +;; DIALOGZUSTAND +;; ============================================================ +;; Die Tiles werden in Schleifen gelesen und geschrieben, ihre Anzahl steht +;; erst zur Laufzeit fest. Der Zustand liegt darum in Globals: action_tile +;; wertet seinen Rueckruf als String AUSSERHALB des lexikalischen Scopes der +;; Dialogfunktion aus, auf deren Locals kaeme er nicht. + +;; Ausgangszustand einer Spalte: nichts angehakt, Mengenfeld mit der +;; JSON-Vorgabe vorbelegt (beim Klemmring also 2). +(defun geruest-dlg-standard ( / erg) + (setq erg nil) + (foreach a *geruest-anbauteile* + (setq erg (append erg (list (cons "0" (itoa (geruest-menge a)))))) + ) + erg +) + +;; Mengenliste (nil oder Zahl je Teil) in den Tile-Zustand umsetzen. +(defun geruest-mengen->tiles (mengen / erg i a) + (setq erg nil i 0) + (foreach m mengen + (setq a (nth i *geruest-anbauteile*)) + (setq erg (append erg + (list (if m + (cons "1" (itoa m)) + (cons "0" (itoa (geruest-menge a))))))) + (setq i (1+ i)) + ) + erg +) + +(defun geruest-dlg-lesen ( / i n) + (setq *geruest-dlg-variante* (get_tile "variante")) + (setq *geruest-dlg-laenge* (get_tile "laenge")) + (setq *geruest-dlg-farbe* (get_tile "farbe")) + (setq *geruest-dlg-frei* (get_tile "farbe_frei")) + (setq *geruest-dlg-konfig* (get_tile "konfig")) + (setq *geruest-dlg-bereich* (get_tile "bereich")) + (setq n (length *geruest-anbauteile*) i 0) + (setq *geruest-dlg-oben* nil *geruest-dlg-unten* nil) + (while (< i n) + (setq *geruest-dlg-oben* + (append *geruest-dlg-oben* + (list (cons (get_tile (strcat "o" (itoa i))) + (get_tile (strcat "om" (itoa i))))))) + (setq *geruest-dlg-unten* + (append *geruest-dlg-unten* + (list (cons (get_tile (strcat "u" (itoa i))) + (get_tile (strcat "um" (itoa i))))))) + (setq i (1+ i)) + ) + (princ) +) + +(defun geruest-dlg-schreiben ( / i n paar) + (set_tile "variante" *geruest-dlg-variante*) + (set_tile "laenge" *geruest-dlg-laenge*) + ;; Nur setzen, wenn die Liste ueberhaupt gefuellt ist - ein Index auf eine + ;; leere popup_list ist nicht definiert (Baugruppen ohne attribute-Block). + (if *geruest-farb-liste* (set_tile "farbe" *geruest-dlg-farbe*)) + (set_tile "farbe_frei" *geruest-dlg-frei*) + (setq n (length *geruest-anbauteile*) i 0) + (while (< i n) + (setq paar (nth i *geruest-dlg-oben*)) + (set_tile (strcat "o" (itoa i)) (car paar)) + (set_tile (strcat "om" (itoa i)) (cdr paar)) + (setq paar (nth i *geruest-dlg-unten*)) + (set_tile (strcat "u" (itoa i)) (car paar)) + (set_tile (strcat "um" (itoa i)) (cdr paar)) + (setq i (1+ i)) + ) + (princ) +) + +;; Aktuell gewaehlte Farbe als Text. +(defun geruest-dlg-farbwert ( / idx) + (setq idx (atoi *geruest-dlg-farbe*)) + (cond + ((null *geruest-farb-liste*) "") + ((= idx *geruest-frei-idx*) *geruest-dlg-frei*) + ((nth idx *geruest-farb-liste*) (nth idx *geruest-farb-liste*)) + (T "") + ) +) + +;; Konfiguration anwenden und die Tiles nachziehen. Wird direkt aus dem +;; popup_list-Rueckruf aufgerufen - set_tile aendert die Anzeige im laufenden +;; Dialog, ein Neuaufbau ist nicht noetig. Index 0 = "(keine)". +(defun geruest-dlg-konfig-waehlen (idx / k vidx farbe fidx) + (setq *geruest-dlg-konfig* (itoa idx)) + (if (<= idx 0) + (progn + (setq *geruest-dlg-oben* (geruest-dlg-standard)) + (setq *geruest-dlg-unten* (geruest-dlg-standard)) + ) + (progn + (setq k (nth (1- idx) *geruest-konfig-liste*)) + (if k + (progn + (setq k (cdr k)) + (setq vidx (geruest-name-index (ssg-json-get k "variante") + *geruest-varianten*)) + (if vidx + (setq *geruest-dlg-variante* (itoa vidx)) + (princ (ssg-textf "gr-konfig-variante-unbekannt" + (list (ssg-json-get k "variante") + (ssg-json-get k "name")))) + ) + (setq *geruest-dlg-laenge* + (geruest-zahl-string (ssg-json-get k "laenge"))) + ;; Farbe: bekannter Wert -> Listeneintrag, sonst Freitext (sofern + ;; die Baugruppe das zulaesst). + (setq farbe (ssg-json-get k "teilefarbe")) + (setq fidx (geruest-name-farbindex farbe)) + (cond + (fidx (setq *geruest-dlg-farbe* (itoa fidx) + *geruest-dlg-frei* "")) + ((and farbe (/= farbe "") (>= *geruest-frei-idx* 0)) + (setq *geruest-dlg-farbe* (itoa *geruest-frei-idx*) + *geruest-dlg-frei* farbe)) + ) + (setq *geruest-dlg-oben* + (geruest-mengen->tiles + (geruest-konfig-position k "oben" *geruest-anbauteile*))) + (setq *geruest-dlg-unten* + (geruest-mengen->tiles + (geruest-konfig-position k "unten" *geruest-anbauteile*))) + ) + ) + ) + ) + (geruest-dlg-schreiben) + (mode_tile "farbe_frei" + (if (= (atoi *geruest-dlg-farbe*) *geruest-frei-idx*) 0 1)) + (princ) +) + +(defun geruest-name-farbindex (wert / i idx) + (setq i 0 idx nil) + (foreach f *geruest-farb-liste* + (if (and (null idx) (equal f wert)) (setq idx i)) + (setq i (1+ i)) + ) + idx +) + +;; Angehakte Teile einer Spalte mit gueltiger Menge einsammeln. +;; Rueckgabe: (T . ((index . menge) ...)) oder nil bei ungueltigem Mengenfeld. +;; Das fuehrende T ist noetig, weil "nichts ausgewaehlt" ein gueltiges +;; Ergebnis ist und sich sonst nicht von einem Fehler unterscheiden liesse. +(defun geruest-mengen-lesen (zustand / erg i a m ok) + (setq erg nil i 0 ok T) + (foreach paar zustand + (setq a (nth i *geruest-anbauteile*)) + (if (and ok (= (car paar) "1")) + (progn + (setq m (geruest-menge-wert (cdr paar) (geruest-menge a))) + (if m + (setq erg (append erg (list (cons i m)))) + (setq ok nil) + ) + ) + ) + (setq i (1+ i)) + ) + (if ok (cons T erg)) +) + +;; Aktuellen Dialogzustand als Konfigurations-Datensatz aufbauen - in +;; derselben Form, die der Parser aus der Datei liefert, damit der Rest des +;; Moduls nicht zwischen Bibliothek und eigener Konfiguration unterscheiden +;; muss. +(defun geruest-dlg-record (name) + (list (cons "name" name) + (cons "variante" + (geruest-eintrag-name + (nth (atoi *geruest-dlg-variante*) *geruest-varianten*))) + (cons "laenge" *geruest-dlg-laenge*) + (cons "teilefarbe" (geruest-dlg-farbwert)) + (cons "oben" (geruest-dlg-teile *geruest-dlg-oben*)) + (cons "unten" (geruest-dlg-teile *geruest-dlg-unten*)) + ) +) + +(defun geruest-dlg-teile (zustand / erg i a m) + (setq erg nil i 0) + (foreach paar zustand + (setq a (nth i *geruest-anbauteile*)) + (if (= (car paar) "1") + (progn + (setq m (geruest-menge-wert (cdr paar) (geruest-menge a))) + (if m + (setq erg (append erg + (list (list (cons "artikel" (geruest-eintrag-name a)) + (cons "menge" m))))) + ) + ) + ) + (setq i (1+ i)) + ) + erg +) + + +;; ============================================================ +;; BENUTZERKONFIGURATIONEN SCHREIBEN +;; ============================================================ +;; Bewusst ein auf dieses Schema zugeschnittener Schreiber und kein +;; generischer JSON-Writer: beim Schreiben ist die Abbildung des Parsers +;; mehrdeutig - eine leere Liste kann aus [], {}, false oder null entstanden +;; sein. Ein generischer Writer wuerde ein Versprechen geben, das er nicht +;; halten kann. + +(defun geruest-teile-schreiben (f teile / n i) + (setq n (length teile) i 0) + (foreach e teile + (setq i (1+ i)) + (write-line + (strcat " { \"artikel\": \"" + (ssg-json-escape (ssg-json-get e "artikel")) + "\", \"menge\": " (itoa (ssg-json-get e "menge")) " }" + (if (< i n) "," "")) + f) + ) +) + +(defun geruest-konfig-liste-schreiben (f konfigs / n i) + (setq n (length konfigs) i 0) + (foreach k konfigs + (setq i (1+ i)) + (write-line " {" f) + (write-line (strcat " \"name\": \"" + (ssg-json-escape (ssg-json-get k "name")) "\",") f) + (write-line (strcat " \"variante\": \"" + (ssg-json-escape (ssg-json-get k "variante")) "\",") f) + ;; Laenge als String: sie darf leer sein (Baugruppen ohne Laengenangabe), + ;; und "laenge": ohne Wert waere kein gueltiges JSON. Der Leser + ;; (geruest-zahl-string) nimmt String wie Zahl entgegen. + (write-line (strcat " \"laenge\": \"" + (ssg-json-escape (geruest-zahl-string + (ssg-json-get k "laenge"))) "\",") f) + (write-line (strcat " \"teilefarbe\": \"" + (ssg-json-escape (ssg-json-get k "teilefarbe")) "\",") f) + (write-line " \"oben\": [" f) + (geruest-teile-schreiben f (ssg-json-get k "oben")) + (write-line " ]," f) + (write-line " \"unten\": [" f) + (geruest-teile-schreiben f (ssg-json-get k "unten")) + (write-line " ]" f) + (write-line (if (< i n) " }," " }") f) + ) +) + +;; cfg/geruest_user.json komplett neu schreiben. +(defun geruest-user-schreiben ( / pfad f n i) + (setq pfad (geruest-user-pfad)) + (if (null pfad) + (progn (princ (ssg-text "gr-fehler-datenpfad")) nil) + (progn + (setq f (open pfad "w")) + (if (null f) + (progn (princ (ssg-textf "gr-fehler-schreiben" (list pfad))) nil) + (progn + (write-line "{" f) + (setq n (length *geruest-user-daten*) i 0) + (foreach bgruppe *geruest-user-daten* + (setq i (1+ i)) + (write-line (strcat " \"" (ssg-json-escape (car bgruppe)) + "\": [") f) + (geruest-konfig-liste-schreiben f (cdr bgruppe)) + (write-line (if (< i n) " ]," " ]") f) + ) + (write-line "}" f) + (close f) + T + ) + ) + ) + ) +) + +;; Eigene Konfiguration ablegen; ein gleichnamiger eigener Eintrag wird +;; ersetzt. Die Bibliothek aus geruest.json bleibt unberuehrt. +(defun geruest-konfig-sichern (bg name / id liste neu ersetzt) + (setq id (ssg-json-get bg "id")) + (setq neu (geruest-dlg-record name)) + (setq ersetzt nil) + (setq liste + (mapcar '(lambda (k) + (if (equal (ssg-json-get k "name") name) + (progn (setq ersetzt T) neu) + k)) + (ssg-json-get (geruest-user-daten) id))) + (if (not ersetzt) (setq liste (append liste (list neu)))) + (setq *geruest-user-daten* + (geruest-alist-setzen (geruest-user-daten) id liste)) + (if (geruest-user-schreiben) + (princ (ssg-textf "gr-konfig-gespeichert" (list name))) + ) +) + +;; Gewaehlte eigene Konfiguration nach Rueckfrage entfernen. +(defun geruest-konfig-entfernen (bg / idx k id name liste) + (setq idx (atoi *geruest-dlg-konfig*)) + (if (<= idx 0) + (princ (ssg-text "gr-konfig-nichts-gewaehlt")) + (progn + (setq k (nth (1- idx) *geruest-konfig-liste*)) + (cond + ((null k) nil) + ((null (car k)) (princ (ssg-text "gr-konfig-bibliothek-fest"))) + (T + (setq name (ssg-json-get (cdr k) "name")) + (initget "Ja Nein") + (if (equal (getkword (ssg-textf "gr-konfig-loeschen-frage" + (list name))) "Ja") + (progn + (setq id (ssg-json-get bg "id")) + (setq liste (vl-remove-if + '(lambda (e) + (equal (ssg-json-get e "name") name)) + (ssg-json-get (geruest-user-daten) id))) + (setq *geruest-user-daten* + (geruest-alist-setzen (geruest-user-daten) id liste)) + (if (geruest-user-schreiben) + (princ (ssg-textf "gr-konfig-geloescht" (list name)))) + (setq *geruest-dlg-konfig* "0") + ) + ) + ) + ) + ) + ) +) + +;; Dialogzustand eines Bereichs sichern bzw. zurueckholen, damit ein +;; Bereichswechsel die Eingaben nicht verwirft. +(defun geruest-zustand-sichern (id) + (setq *geruest-zustand* + (geruest-alist-setzen *geruest-zustand* id + (list *geruest-dlg-variante* *geruest-dlg-laenge* *geruest-dlg-farbe* + *geruest-dlg-frei* *geruest-dlg-konfig* + *geruest-dlg-oben* *geruest-dlg-unten*))) +) + +;; Rueckgabe T, wenn ein passender Zustand gefunden und gesetzt wurde. Die +;; Laengenpruefung faengt den Fall ab, dass die Datendatei zwischen zwei +;; Aufrufen geaendert wurde und die Teilezahl nicht mehr passt. +(defun geruest-zustand-laden (id / z) + (setq z (cdr (assoc id *geruest-zustand*))) + (if (and z (= (length (nth 5 z)) (length *geruest-anbauteile*))) + (progn + (setq *geruest-dlg-variante* (nth 0 z) + *geruest-dlg-laenge* (nth 1 z) + *geruest-dlg-farbe* (nth 2 z) + *geruest-dlg-frei* (nth 3 z) + *geruest-dlg-konfig* (nth 4 z) + *geruest-dlg-oben* (nth 5 z) + *geruest-dlg-unten* (nth 6 z)) + T + ) + ) +) + +;; Dialog fuer einen Bereich zeigen. Rueckgabe: +;; ("OK" variante-index ((idx . menge) ...) ((idx . menge) ...) laenge farbe) +;; - drittes Element = Befestigung oben, viertes = unten +;; ("WECHSEL" bereich-index) - Nutzer hat den Bereich umgestellt +;; nil - abgebrochen +(defun geruest-dialog (bg bereich-idx bereiche / dcl-pfad dat attr laenge-def + farb-opts frei-opt min-l max-l fertig rueckgabe + ergebnis wert-l oben-m unten-m name neu-idx) + (setq *geruest-varianten* (ssg-json-get bg "geometrie_varianten")) + (setq *geruest-anbauteile* (ssg-json-get bg "anbaugruppen")) (setq attr (ssg-json-get bg "attribute")) (setq laenge-def (if attr (ssg-json-get attr "Laenge"))) - (setq farb-opts (if attr (ssg-json-get attr "Farbe"))) + (setq farb-opts (if attr (ssg-json-get attr "Teilefarbe"))) (setq min-l (if laenge-def (ssg-json-get laenge-def "min"))) (setq max-l (if laenge-def (ssg-json-get laenge-def "max"))) @@ -220,46 +888,68 @@ ) ) - (setq dlg-variante "0" dlg-anbau "" dlg-laenge "" dlg-farbe "0" dlg-frei "") + (setq *geruest-konfig-liste* (geruest-konfigs bg)) + ;; Frueheren Stand dieses Bereichs wiederherstellen, sonst Ausgangszustand + (if (null (geruest-zustand-laden (ssg-json-get bg "id"))) + (progn + (setq *geruest-dlg-variante* "0" *geruest-dlg-laenge* "" + *geruest-dlg-farbe* "0" *geruest-dlg-frei* "" + *geruest-dlg-konfig* "0") + (setq *geruest-dlg-oben* (geruest-dlg-standard)) + (setq *geruest-dlg-unten* (geruest-dlg-standard)) + ) + ) (setq fertig nil rueckgabe nil) - ;; Schleife, damit der Dialog nach einer ungueltigen Laenge mit den - ;; bisherigen Eingaben erneut erscheint (Projektmuster, vgl. vf_linienzug). + ;; Die Dialogdefinition haengt nur an der Teileliste, nicht am Zustand - + ;; einmal erzeugen und erst nach der Schleife loeschen (unload_dialog muss + ;; vorher gelaufen sein). + (setq dcl-pfad (geruest-dcl-schreiben *geruest-anbauteile*)) + + ;; Schleife: ungueltige Eingaben, Speichern und Loeschen zeigen den Dialog + ;; mit dem bisherigen Stand erneut (Projektmuster, vgl. vf_linienzug). (while (not fertig) - (setq dat (load_dialog dcl-pfad)) - (if (or (< dat 0) (not (new_dialog "geruest" dat))) + (setq dat (if dcl-pfad (load_dialog dcl-pfad) -1)) + (if (or (null dcl-pfad) (< dat 0) (not (new_dialog "geruest" dat))) (progn (alert (ssg-text "gr-fehler-dcl")) (setq fertig T) ) (progn - (set_tile "kopf" (ssg-textf "gr-dlg-kopf" - (list (ssg-json-get bg "teileart")))) + (start_list "bereich") + (foreach b bereiche (add_list (geruest-titel (cdr b)))) + (end_list) + (set_tile "bereich" (itoa bereich-idx)) + + (set_tile "kopf" (ssg-textf "gr-dlg-kopf" (list (geruest-titel bg)))) + + (start_list "konfig") + (add_list (ssg-text "gr-konfig-keine")) + (foreach k *geruest-konfig-liste* (add_list (geruest-konfig-name k))) + (end_list) + (set_tile "konfig" *geruest-dlg-konfig*) (start_list "variante") - (foreach v varianten (add_list (geruest-eintrag-name v))) + (foreach v *geruest-varianten* (add_list (geruest-eintrag-name v))) (end_list) - (set_tile "variante" dlg-variante) - (start_list "anbau") - (foreach a anbauteile (add_list (geruest-eintrag-name a))) - (end_list) - (if (/= dlg-anbau "") (set_tile "anbau" dlg-anbau)) - - ;; Laenge/Farbe nur bei Baugruppen mit "attribute"-Block - (if laenge-def - (set_tile "laenge" dlg-laenge) - (mode_tile "laenge" 1) - ) (if *geruest-farb-liste* (progn (start_list "farbe") (foreach f *geruest-farb-liste* (add_list f)) (end_list) - (set_tile "farbe" dlg-farbe) - (set_tile "farbe_frei" dlg-frei) + ) + ) + + ;; Alle Tiles aus dem Zustand fuellen (auch Haken und Mengenfelder) + (geruest-dlg-schreiben) + + ;; Laenge/Farbe nur bei Baugruppen mit "attribute"-Block + (if (null laenge-def) (mode_tile "laenge" 1)) + (if *geruest-farb-liste* + (progn (mode_tile "farbe_frei" - (if (= (atoi dlg-farbe) *geruest-frei-idx*) 0 1)) + (if (= (atoi *geruest-dlg-farbe*) *geruest-frei-idx*) 0 1)) (action_tile "farbe" "(mode_tile \"farbe_frei\" (if (= (atoi $value) *geruest-frei-idx*) 0 1))") ) @@ -269,49 +959,96 @@ ) ) - (action_tile "accept" - (strcat "(setq dlg-variante (get_tile \"variante\"))" - "(setq dlg-anbau (get_tile \"anbau\"))" - "(setq dlg-laenge (get_tile \"laenge\"))" - "(setq dlg-farbe (get_tile \"farbe\"))" - "(setq dlg-frei (get_tile \"farbe_frei\"))" - "(done_dialog 1)")) + (action_tile "konfig" "(geruest-dlg-konfig-waehlen (atoi $value))") + ;; Bereichswechsel: Dialog schliessen, die Schleife in c:GERUEST baut + ;; ihn mit dem neuen Bereich neu auf. + (action_tile "bereich" "(geruest-dlg-lesen) (done_dialog 4)") + (action_tile "accept" "(geruest-dlg-lesen) (done_dialog 1)") + (action_tile "kspeichern" "(geruest-dlg-lesen) (done_dialog 2)") + (action_tile "kloeschen" "(geruest-dlg-lesen) (done_dialog 3)") (action_tile "cancel" "(done_dialog 0)") (setq ergebnis (start_dialog)) (unload_dialog dat) (cond - ((/= ergebnis 1) (setq fertig T)) + ((= ergebnis 0) (setq fertig T)) + + ;; Bereichswechsel: Stand dieses Bereichs sichern und an den + ;; Aufrufer zurueckmelden, damit er den neuen Bereich oeffnet. + ((= ergebnis 4) + (setq neu-idx (atoi *geruest-dlg-bereich*)) + (geruest-zustand-sichern (ssg-json-get bg "id")) + (if (= neu-idx bereich-idx) + nil + (progn + (setq rueckgabe (list "WECHSEL" neu-idx)) + (setq fertig T) + ) + ) + ) + + ;; Speichern: der Name wird NACH dem Schliessen abgefragt - + ;; getstring funktioniert aus einem action_tile-Rueckruf heraus + ;; nicht (dasselbe Muster wie die Punktwahl im VF-Assistenten). + ((= ergebnis 2) + (setq name (getstring T (ssg-text "gr-konfig-name"))) + (if (and name (/= (ssg-cfg-trim name) "")) + (progn + (setq name (ssg-cfg-trim name)) + (geruest-konfig-sichern bg name) + (setq *geruest-konfig-liste* (geruest-konfigs bg)) + ;; Gerade gespeicherten Eintrag auswaehlen, damit Liste und + ;; angezeigter Stand zusammenpassen (Index 0 = ""). + (setq *geruest-dlg-konfig* + (itoa (geruest-konfig-index name))) + ) + (progn + (princ (ssg-text "gr-abgebrochen")) + (setq *geruest-konfig-liste* (geruest-konfigs bg)) + ) + ) + ) + + ((= ergebnis 3) + (geruest-konfig-entfernen bg) + (setq *geruest-konfig-liste* (geruest-konfigs bg)) + ) + ;; Laenge pruefen - Bereichspruefung erfolgt im Projekt immer nach ;; done_dialog, DCL kann das nicht. ((and laenge-def - (progn (setq wert-l (atof dlg-laenge)) - (or (= (ssg-cfg-trim dlg-laenge) "") + (progn (setq wert-l (atof *geruest-dlg-laenge*)) + (or (= (ssg-cfg-trim *geruest-dlg-laenge*) "") (and min-l (< wert-l min-l)) (and max-l (> wert-l max-l))))) (alert (ssg-textf "gr-alert-laenge" - (list dlg-laenge + (list *geruest-dlg-laenge* (if min-l (rtos min-l 2 0) "0") (if max-l (rtos max-l 2 0) "-")))) ) + (T - (setq farbe-txt - (cond - ((null *geruest-farb-liste*) "") - ((= (atoi dlg-farbe) *geruest-frei-idx*) dlg-frei) - (T (nth (atoi dlg-farbe) *geruest-farb-liste*)) + (setq oben-m (geruest-mengen-lesen *geruest-dlg-oben*)) + (setq unten-m (geruest-mengen-lesen *geruest-dlg-unten*)) + (if (or (null oben-m) (null unten-m)) + (alert (ssg-text "gr-alert-menge")) + (progn + (setq rueckgabe + (list "OK" + (atoi *geruest-dlg-variante*) + (cdr oben-m) + (cdr unten-m) + (if laenge-def *geruest-dlg-laenge* "") + (geruest-dlg-farbwert))) + (setq fertig T) ) ) - (setq rueckgabe (list (atoi dlg-variante) - (geruest-indizes dlg-anbau) - (if laenge-def dlg-laenge "") - (if farbe-txt farbe-txt ""))) - (setq fertig T) ) ) ) ) ) + (if dcl-pfad (vl-file-delete dcl-pfad)) rueckgabe ) @@ -334,11 +1071,14 @@ ) ;; Dummy-Geometrie + eigene ATTDEFs zu einem Block zusammenfassen und am -;; gewaehlten Punkt einfuegen. werte = alist der berechneten Attributwerte. +;; gewaehlten Punkt einfuegen. werte = alist der berechneten Attributwerte, +;; variante = gewaehlte Geometrie-Variante (liefert ggf. eine eigene DWG). ;; Rueckgabe: (blockname . entity) oder nil. -(defun geruest-block-bauen (bg werte pt / datei dummy lastEnt dummyEnt e ss - bname blockEnt dummy-attribs attribs) - (setq datei (ssg-json-get bg "dummy")) +(defun geruest-block-bauen (bg variante werte pt / datei dummy lastEnt dummyEnt + e ss bname blockEnt dummy-attribs attribs) + ;; Eine Variante darf eine eigene Dummy-DWG mitbringen; sonst gilt die der + ;; Baugruppe. + (setq datei (geruest-variante-wert variante bg "dummy")) (setq dummy (geruest-dummy-pfad datei)) (if (or (null dummy) (null (findfile dummy))) (progn @@ -410,65 +1150,109 @@ ;; ABLAUF ;; ============================================================ -(defun geruest-run (id / bg wahl varianten anbauteile variante gewaehlt - artnr bezeichnung teile-namen werte pt ergebnis) - (setq bg (geruest-baugruppe id)) - (cond - ((null bg) - (if (geruest-daten) (princ (ssg-textf "gr-fehler-baugruppe" (list id)))) +;; Artikelnummern einer Befestigungsauswahl ((index . menge) ...). +(defun geruest-auswahl-artnr (auswahl anbauteile / erg) + (setq erg nil) + (foreach paar auswahl + (setq erg (append erg + (geruest-artikelnummern-n (nth (car paar) anbauteile) (cdr paar)))) + ) + erg +) + +;; Namensteile einer Befestigungsauswahl fuer die BEZEICHNUNG, +;; z.B. ("oben 2x ROHRKLAMMER 1\" KPL. VERTIKAL"). +(defun geruest-auswahl-namen (auswahl anbauteile position / erg) + (setq erg nil) + (foreach paar auswahl + (setq erg (append erg + (list (strcat position " " (itoa (cdr paar)) "x " + (geruest-eintrag-name (nth (car paar) anbauteile)))))) + ) + erg +) + +;; Ausgewaehlte Konfiguration einfuegen. +;; wahl = (variante-index oben unten laenge farbe) - die Dialog-Rueckgabe ohne +;; das fuehrende "OK". +(defun geruest-einfuegen (bg wahl / varianten anbauteile variante teileart + artnr bezeichnung teile-namen werte pt ergebnis) + (setq varianten (ssg-json-get bg "geometrie_varianten")) + (setq anbauteile (ssg-json-get bg "anbaugruppen")) + (setq variante (nth (car wahl) varianten)) + ;; TEILEART kann die Variante vorgeben (Saeule 1 Zoll vs. 2 Zoll) - + ;; sonst gilt der Wert der Baugruppe. + (setq teileart (geruest-variante-wert variante bg "teileart")) + + ;; ARTINR: jede Sivas-ID so oft wie die gewaehlte Stueckzahl, erst die + ;; Basis, dann Befestigung oben, dann unten. Dasselbe Teil an beiden + ;; Positionen erscheint entsprechend mehrfach. + (setq artnr (geruest-artikelnummern variante)) + (setq artnr (append artnr (geruest-auswahl-artnr (cadr wahl) anbauteile))) + (setq artnr (append artnr (geruest-auswahl-artnr (caddr wahl) anbauteile))) + + ;; BEZEICHNUNG: mit " + " verketten - die Artikelbezeichnungen enthalten + ;; selbst Kommas ("ROHR R 1\" 33,7 x 2,6"). + (setq teile-namen (list teileart (geruest-eintrag-name variante))) + (setq teile-namen (append teile-namen + (geruest-auswahl-namen (cadr wahl) anbauteile + (ssg-text "gr-position-oben")))) + (setq teile-namen (append teile-namen + (geruest-auswahl-namen (caddr wahl) anbauteile + (ssg-text "gr-position-unten")))) + (setq bezeichnung (ssg-join teile-namen " + ")) + + (setq werte (list (cons "ARTINR" (ssg-join artnr ",")) + (cons "BEZEICHNUNG" bezeichnung) + (cons "TEILEART" teileart))) + (if (/= (nth 3 wahl) "") + (setq werte (append werte (list (cons "LAENGE" (nth 3 wahl)))))) + (if (/= (nth 4 wahl) "") + (setq werte (append werte (list (cons "TEILEFARBE" (nth 4 wahl)))))) + + (setq pt (getpoint (ssg-text "gr-prompt-einfuegepunkt"))) + (if (null pt) + (princ (ssg-text "gr-abgebrochen")) + (progn + (ssg-start "Geruest" '(("OSMODE") ("ATTREQ") ("ATTDIA"))) + (setq ergebnis (geruest-block-bauen bg variante werte pt)) + (ssg-end) + (if ergebnis + (princ (ssg-textf "gr-fertig" (list teileart (car ergebnis)))) + ) ) - ((null (setq varianten (ssg-json-get bg "geometrie_varianten"))) - (princ (ssg-textf "gr-fehler-keine-varianten" (list id))) - ) - (T - (setq anbauteile (ssg-json-get bg "anbaugruppen")) - (setq wahl (geruest-dialog bg)) - (if (null wahl) - (princ (ssg-text "gr-abgebrochen")) - (progn - (setq variante (nth (car wahl) varianten)) - (setq gewaehlt nil) - (foreach i (cadr wahl) - (if (< i (length anbauteile)) - (setq gewaehlt (append gewaehlt (list (nth i anbauteile)))) - ) + ) + (princ) +) + + +;; Ein Dialog fuer alle Geruest-Bereiche. Der Bereichswechsel laeuft ueber +;; diese Schleife, weil der Dialog dafuer neu aufgebaut werden muss. +(defun c:GERUEST ( / bereiche idx bg erg weiter) + (setq *geruest-zustand* nil) + (setq bereiche (geruest-baugruppen-liste)) + (if (null bereiche) + (princ (ssg-text "gr-fehler-keine-baugruppen")) + (progn + (setq idx 0 weiter T) + (while weiter + (setq bg (cdr (nth idx bereiche))) + (cond + ((null (ssg-json-get bg "geometrie_varianten")) + (princ (ssg-textf "gr-fehler-keine-varianten" + (list (ssg-json-get bg "id")))) + (setq weiter nil) ) - - ;; ARTINR: jede Sivas-ID so oft, wie "menge" es vorgibt - (setq artnr (geruest-artikelnummern variante)) - (foreach a gewaehlt - (setq artnr (append artnr (geruest-artikelnummern a))) - ) - - ;; BEZEICHNUNG: mit " + " verketten - die Artikelbezeichnungen - ;; enthalten selbst Kommas ("ROHR R 1\" 33,7 x 2,6"). - (setq teile-namen (list (ssg-json-get bg "teileart") - (geruest-eintrag-name variante))) - (foreach a gewaehlt - (setq teile-namen (append teile-namen - (list (geruest-eintrag-name a)))) - ) - (setq bezeichnung (ssg-join teile-namen " + ")) - - (setq werte (list (cons "ARTINR" (ssg-join artnr ",")) - (cons "BEZEICHNUNG" bezeichnung) - (cons "TEILEART" (ssg-json-get bg "teileart")))) - (if (/= (caddr wahl) "") - (setq werte (append werte (list (cons "LAENGE" (caddr wahl)))))) - (if (/= (cadddr wahl) "") - (setq werte (append werte (list (cons "FARBE" (cadddr wahl)))))) - - (setq pt (getpoint (ssg-text "gr-prompt-einfuegepunkt"))) - (if (null pt) - (princ (ssg-text "gr-abgebrochen")) - (progn - (ssg-start "Geruest" '(("OSMODE") ("ATTREQ") ("ATTDIA"))) - (setq ergebnis (geruest-block-bauen bg werte pt)) - (ssg-end) - (if ergebnis - (princ (ssg-textf "gr-fertig" - (list (ssg-json-get bg "teileart") (car ergebnis)))) - ) + (T + (setq erg (geruest-dialog bg idx bereiche)) + (cond + ((null erg) + (princ (ssg-text "gr-abgebrochen")) + (setq weiter nil)) + ((equal (car erg) "WECHSEL") (setq idx (cadr erg))) + (T + (setq weiter nil) + (geruest-einfuegen bg (cdr erg))) ) ) ) @@ -478,11 +1262,5 @@ (princ) ) - -(defun c:GERUEST_ROHRABHAENGER () (geruest-run "Rohrabhaenger")) -(defun c:GERUEST_SAEULE_2ZOLL () (geruest-run "Saeulen_2Zoll")) -(defun c:GERUEST_SAEULE_1ZOLL () (geruest-run "Saeulen_1Zoll")) -(defun c:GERUEST_WANDBEFESTIGUNG () (geruest-run "Wandbefestigung")) - -(princ "\nGeruestInsert.lsp geladen. Befehle: GERUEST_ROHRABHAENGER, GERUEST_SAEULE_2ZOLL, GERUEST_SAEULE_1ZOLL, GERUEST_WANDBEFESTIGUNG") +(princ "\nGeruestInsert.lsp geladen. Befehl: GERUEST") (princ) diff --git a/Lisp/ssg_core.lsp b/Lisp/ssg_core.lsp index 8972d77..bc040eb 100644 --- a/Lisp/ssg_core.lsp +++ b/Lisp/ssg_core.lsp @@ -1767,6 +1767,33 @@ (cdr (assoc key obj)) ) +;; String fuer die Ausgabe in JSON maskieren (Gegenstueck zu ssg-json-string). +;; Der Backslash MUSS zuerst geprueft werden, sonst wuerde die Maskierung der +;; Anfuehrungszeichen gleich wieder mitmaskiert. +;; Hinweis: csv:json-escape in export.lsp leistet dasselbe, ist aber modul-lokal +;; am Export-Modul - bewusst nicht zusammengelegt, das waere ein eigener Schritt. +(defun ssg-json-escape (s / i c erg) + (if (null s) (setq s "")) + (setq erg "" i 1) + (while (<= i (strlen s)) + (setq c (substr s i 1)) + (setq erg + (strcat erg + (cond + ((= c "\\") "\\\\") + ((= c "\"") "\\\"") + ((= c (chr 10)) "\\n") + ((= c (chr 13)) "\\r") + ((= c (chr 9)) "\\t") + (T c) + ) + ) + ) + (setq i (1+ i)) + ) + erg +) + ;; Verschachteltes JSON-Objekt parsen (1 Ebene tief) ;; {"section": {"key": val, ...}, ...} diff --git a/data/geruest/R2-Saeule_dummy.dwg b/data/geruest/R2-Saeule_dummy.dwg index a0b9d74dc92fcd3e506144e7291bc99e6fa0625d..7c508280971c1a05dd3d246ee202c691458fb249 100644 GIT binary patch delta 6415 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 delta 6244 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 Be_Q|n diff --git a/data/geruest/Rohrabhaenger_dummy.dwg b/data/geruest/Rohrabhaenger_dummy.dwg index 106441496755cd714d3713419e924bcbe3963cdb..fcecd90161f86fd4f6d17edfffe938655631008e 100644 GIT binary patch delta 8755 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 QU=$C5qA36W^Sk~30Tn6y2mk;8 diff --git a/data/json/geruest.json b/data/json/geruest.json index 3ef2d41..85d6b42 100644 --- a/data/json/geruest.json +++ b/data/json/geruest.json @@ -1,6 +1,7 @@ { "Rohrabhänger": { "id": "Rohrabhaenger", + "titel": "Rohrabhaenger", "teileart": "Geruest Rohrabhaenger", "dummy": "Rohrabhaenger_dummy.dwg", "attribute": { @@ -10,11 +11,11 @@ "max": 6000, "einheit": "mm" }, - "Farbe": { + "Teilefarbe": { "typ": "auswahl", "optionen": [ - "RAL7035", - "RAL7030" + "RAL 7035 Lichtgrau", + "RAL 7030 Steingrau" ], "frei_erlaubt": true, "frei_option": "Individuell" @@ -133,50 +134,39 @@ "sivas_id": 922021003, "menge": 1 } - ] - }, - "1\" Säulen": { - "id": "Saeulen_1Zoll", - "teileart": "Geruest Saeule 1 Zoll", - "dummy": "R2-Saeule_dummy.dwg", - "attribute": { - "Laenge": { - "typ": "number", - "min": 0, - "max": 6000, - "einheit": "mm" - }, - "Farbe": { - "typ": "auswahl", - "optionen": [ - "RAL7035", - "RAL7030" - ], - "frei_erlaubt": true, - "frei_option": "Individuell" - } - }, - "geometrie_varianten": [ + ], + "konfigurationen": [ { - "name": "R 1\"", - "teile": [ - { - "artikel": "ROHR R 1\" 33,7 x 2,6", - "sivas_id": 821006026, - "menge": 1 - }, - { - "artikel": "CAD - BODENSÄULE R 1\" - BODENPLATTE KPL.", - "sivas_id": 821004200, - "menge": 1 - } + "name": "Abhaengung Spannpratze / Rohrklammer", + "variante": "ROHR R 1\" 33,7 x 2,6", + "laenge": 2000, + "teilefarbe": "RAL 7035 Lichtgrau", + "oben": [ + { "artikel": "SPANNPRATZE SELBSTSICHERND KPL.", "menge": 1 }, + { "artikel": "Schraubenset", "menge": 1 } + ], + "unten": [ + { "artikel": "ROHRKLAMMER 1\" KPL. VERTIKAL", "menge": 2 } + ] + }, + { + "name": "Abhaengung Duebel / T-Verbindung", + "variante": "ROHR R 1\" 33,7 x 2,6 einseitig gequetscht", + "laenge": 1500, + "teilefarbe": "RAL 7035 Lichtgrau", + "oben": [ + { "artikel": "DURCHSTECKANKER HST M12X115X20", "menge": 2 } + ], + "unten": [ + { "artikel": "ROHRKLAMMER 1\" T-VERBINDUNG KPL.", "menge": 1 } ] } ] }, - "2\" Säulen": { - "id": "Saeulen_2Zoll", - "teileart": "Geruest Saeule 2 Zoll", + "Säulen": { + "id": "Saeulen", + "titel": "Saeulen", + "teileart": "Geruest Saeule", "dummy": "R2-Saeule_dummy.dwg", "attribute": { "Laenge": { @@ -185,11 +175,11 @@ "max": 6000, "einheit": "mm" }, - "Farbe": { + "Teilefarbe": { "typ": "auswahl", "optionen": [ - "RAL7035", - "RAL7030" + "RAL 7035 Lichtgrau", + "RAL 7030 Steingrau" ], "frei_erlaubt": true, "frei_option": "Individuell" @@ -208,8 +198,25 @@ ] }, "geometrie_varianten": [ + { + "name": "R 1\"", + "teileart": "Geruest Saeule 1 Zoll", + "teile": [ + { + "artikel": "ROHR R 1\" 33,7 x 2,6", + "sivas_id": 821006026, + "menge": 1 + }, + { + "artikel": "CAD - BODENSÄULE R 1\" - BODENPLATTE KPL.", + "sivas_id": 821004200, + "menge": 1 + } + ] + }, { "name": "R 2\"", + "teileart": "Geruest Saeule 2 Zoll", "teile": [ { "artikel": "BODENSÄULE R 2\" 60,3 MIT FUSSPLATTE", @@ -225,6 +232,7 @@ }, { "name": "R 2\" Haltearm 1-ARMIG", + "teileart": "Geruest Saeule 2 Zoll", "teile": [ { "artikel": "BODENSÄULE R 2\" 60,3 MIT FUSSPLATTE", @@ -245,6 +253,7 @@ }, { "name": "R 2\" Haltearm 2-ARMIG 90 Grad", + "teileart": "Geruest Saeule 2 Zoll", "teile": [ { "artikel": "BODENSÄULE R 2\" 60,3 MIT FUSSPLATTE", @@ -265,6 +274,7 @@ }, { "name": "R 2\" Haltearm 2-ARMIG 180 Grad", + "teileart": "Geruest Saeule 2 Zoll", "teile": [ { "artikel": "BODENSÄULE R 2\" 60,3 MIT FUSSPLATTE", @@ -285,6 +295,7 @@ }, { "name": "R 2\" Haltearm 3-ARMIG", + "teileart": "Geruest Saeule 2 Zoll", "teile": [ { "artikel": "BODENSÄULE R 2\" 60,3 MIT FUSSPLATTE", @@ -340,10 +351,43 @@ "sivas_id": 922083009, "menge": 2 } + ], + "konfigurationen": [ + { + "name": "Bodensaeule 1 Zoll, Standardhoehe", + "variante": "R 1\"", + "laenge": 1000, + "teilefarbe": "RAL 7035 Lichtgrau", + "oben": [], + "unten": [] + }, + { + "name": "Bodensaeule 2 Zoll, Haltearm 1-armig", + "variante": "R 2\" Haltearm 1-ARMIG", + "laenge": 1200, + "teilefarbe": "RAL 7035 Lichtgrau", + "oben": [ + { "artikel": "KLEMMRING MIT MUFFE EINFACH KPL.", "menge": 2 } + ], + "unten": [] + }, + { + "name": "Bodensaeule 2 Zoll, Haltearm 2-armig 90 Grad", + "variante": "R 2\" Haltearm 2-ARMIG 90 Grad", + "laenge": 1200, + "teilefarbe": "RAL 7035 Lichtgrau", + "oben": [ + { "artikel": "KLEMMRING MIT MUFFE DOPPELT 90 Grad, KPL.", "menge": 2 } + ], + "unten": [ + { "artikel": "QUERSCHELLE F. 1\" UND 2\" ROHR GESCHW./VZ. M. 4X M10X30 KPL.", "menge": 2 } + ] + } ] }, "Wandbefestigung": { "id": "Wandbefestigung", + "titel": "Wandbefestigung", "teileart": "Geruest Wandbefestigung", "dummy": "Rohrabhaenger_dummy.dwg", "geometrie_varianten": [ @@ -414,6 +458,24 @@ } ] } + ], + "konfigurationen": [ + { + "name": "Wandflansch mit Duebeln", + "variante": "WANDFLANSCH EINZELTEIL", + "oben": [ + { "artikel": "Nylondübel 12x60 inkl. Holzschrauben", "menge": 1 } + ], + "unten": [] + }, + { + "name": "Winkel 300 mit Schwerlastanker", + "variante": "WINKEL 60X60X6X300", + "oben": [ + { "artikel": "HST M12X115X20", "menge": 1 } + ], + "unten": [] + } ] } } \ No newline at end of file diff --git a/dcl/geruest.dcl b/dcl/geruest.dcl deleted file mode 100644 index 201871c..0000000 --- a/dcl/geruest.dcl +++ /dev/null @@ -1,61 +0,0 @@ -// ============================================================ -// geruest.dcl - Konfiguration einer Geruest-Baugruppe -// Ein Dialog fuer ALLE Baugruppen (Rohrabhaenger, Saeulen, Wandbefestigung). -// Kopfzeile, Varianten-Dropdown, Farbliste und Anbauteil-Liste werden zur -// Laufzeit aus data/json/geruest.json befuellt - siehe -// Lisp/GeruestInsert.lsp, Abschnitt "DIALOG". -// Baugruppen ohne "attribute"-Block (z.B. Wandbefestigung) bekommen die -// Laengen- und Farbfelder per mode_tile gesperrt. -// ============================================================ - -geruest : dialog { - label = "Geruest-Baugruppe konfigurieren"; - - : text { - key = "kopf"; - label = ""; - width = 64; - } - - : boxed_column { - label = "Basis"; - - : popup_list { - key = "variante"; - label = "Variante:"; - width = 56; - } - - : row { - : edit_box { - key = "laenge"; - label = "Laenge (mm):"; - edit_width = 10; - } - : popup_list { - key = "farbe"; - label = "Farbe:"; - width = 18; - } - : edit_box { - key = "farbe_frei"; - label = "eigene:"; - edit_width = 12; - } - } - } - - : boxed_column { - label = "Anbauteile (Mehrfachauswahl mit Strg)"; - - : list_box { - key = "anbau"; - label = ""; - multiple_select = true; - width = 64; - height = 12; - } - } - - ok_cancel; -} diff --git a/lang/de_DE.json b/lang/de_DE.json index dca1407..ee52218 100644 --- a/lang/de_DE.json +++ b/lang/de_DE.json @@ -981,6 +981,20 @@ "gr-alert-laenge": "Laenge ungueltig: '%1' mm. Erlaubt: %2 bis %3 mm.", "gr-prompt-einfuegepunkt": "\nEinfuegepunkt waehlen: ", "gr-abgebrochen": "\nAbgebrochen.", - "gr-fertig": "\nGeruest-Baugruppe '%1' eingefuegt (Block %2)." + "gr-fertig": "\nGeruest-Baugruppe '%1' eingefuegt (Block %2).", + "gr-alert-menge": "Ungueltige Stueckzahl. Erlaubt ist eine ganze Zahl ab 1; ein leeres Feld uebernimmt den Vorgabewert.", + "gr-position-oben": "oben", + "gr-position-unten": "unten", + "gr-konfig-keine": "", + "gr-konfig-name": "\nName der Konfiguration: ", + "gr-konfig-gespeichert": "\nKonfiguration '%1' gespeichert.", + "gr-konfig-geloescht": "\nKonfiguration '%1' geloescht.", + "gr-konfig-loeschen-frage": "\nKonfiguration '%1' wirklich loeschen? [Ja/Nein] : ", + "gr-konfig-nichts-gewaehlt": "\nKeine Konfiguration gewaehlt.", + "gr-konfig-bibliothek-fest": "\nKonfigurationen der Bibliothek koennen nicht geloescht werden - nur eigene (mit * markiert).", + "gr-konfig-teil-unbekannt": "\nGeruest: Anbauteil '%1' aus Konfiguration '%2' ist bei dieser Baugruppe unbekannt - uebersprungen.", + "gr-konfig-variante-unbekannt": "\nGeruest: Variante '%1' aus Konfiguration '%2' ist bei dieser Baugruppe unbekannt - Auswahl unveraendert.", + "gr-fehler-schreiben": "\nGeruest: Datei konnte nicht geschrieben werden: %1", + "gr-fehler-keine-baugruppen": "\nGeruest: Die Datendatei enthaelt keine Baugruppen." } } diff --git a/lang/en_GB.json b/lang/en_GB.json index 67a507e..be66089 100644 --- a/lang/en_GB.json +++ b/lang/en_GB.json @@ -981,6 +981,20 @@ "gr-alert-laenge": "Invalid length: '%1' mm. Allowed: %2 to %3 mm.", "gr-prompt-einfuegepunkt": "\nSelect insertion point: ", "gr-abgebrochen": "\nCancelled.", - "gr-fertig": "\nScaffold assembly '%1' inserted (block %2)." + "gr-fertig": "\nScaffold assembly '%1' inserted (block %2).", + "gr-alert-menge": "Invalid quantity. Enter a whole number of 1 or more; an empty field uses the default.", + "gr-position-oben": "top", + "gr-position-unten": "bottom", + "gr-konfig-keine": "", + "gr-konfig-name": "\nConfiguration name: ", + "gr-konfig-gespeichert": "\nConfiguration '%1' saved.", + "gr-konfig-geloescht": "\nConfiguration '%1' deleted.", + "gr-konfig-loeschen-frage": "\nReally delete configuration '%1'? [Ja/Nein] : ", + "gr-konfig-nichts-gewaehlt": "\nNo configuration selected.", + "gr-konfig-bibliothek-fest": "\nLibrary configurations cannot be deleted - only your own (marked with *).", + "gr-konfig-teil-unbekannt": "\nScaffold: add-on part '%1' from configuration '%2' is unknown for this assembly - skipped.", + "gr-konfig-variante-unbekannt": "\nScaffold: variant '%1' from configuration '%2' is unknown for this assembly - selection unchanged.", + "gr-fehler-schreiben": "\nScaffold: could not write file: %1", + "gr-fehler-keine-baugruppen": "\nScaffold: the data file contains no assemblies." } } diff --git a/menu/SSG_LIB.mnu b/menu/SSG_LIB.mnu index 539922f..cc6d597 100644 --- a/menu/SSG_LIB.mnu +++ b/menu/SSG_LIB.mnu @@ -110,12 +110,7 @@ [~APBW 110 R550/67,5 208x347]^C^C(ssg-ensure "OmniModulInsert") OMNI_APBW_R550_675_208 [<-~APBW 110 R550/67,5 58x497]^C^C(ssg-ensure "OmniModulInsert") OMNI_APBW_R550_675_58 [<-Element bearbeiten]^C^C(ssg-ensure "OmniModulInsert") OMNI_EDIT -[->Geruest] -[Rohrabhaenger]^C^C(ssg-ensure "GeruestInsert") GERUEST_ROHRABHAENGER -[->Saeulen] -[2 Zoll Saeulen]^C^C(ssg-ensure "GeruestInsert") GERUEST_SAEULE_2ZOLL -[<-1 Zoll Saeulen]^C^C(ssg-ensure "GeruestInsert") GERUEST_SAEULE_1ZOLL -[<-Wandbefestigung]^C^C(ssg-ensure "GeruestInsert") GERUEST_WANDBEFESTIGUNG +[Geruest...]^C^C(ssg-ensure "GeruestInsert") GERUEST [->SPS] [->TRO] [Marker einfuegen]^C^C(ssg-ensure "Tro_Edit") TRO_INSERT diff --git a/tests/test_unit.lsp b/tests/test_unit.lsp index 36920bf..6b76493 100644 --- a/tests/test_unit.lsp +++ b/tests/test_unit.lsp @@ -471,12 +471,27 @@ (tu-eq "json/escape-newline" (chr 10) (ssg-json-get (ssg-json-parse "{\"a\": \"\\n\"}") "a")) + ;; ssg-json-escape: Gegenstueck zum Parser + (tu-eq "json-escape/quote" "a\\\"b" (ssg-json-escape "a\"b")) + (tu-eq "json-escape/backslash" "a\\\\b" (ssg-json-escape "a\\b")) + (tu-eq "json-escape/newline" "a\\nb" (ssg-json-escape (strcat "a" (chr 10) "b"))) + (tu-eq "json-escape/leer" "" (ssg-json-escape nil)) + ;; Rundlauf: maskieren -> parsen ergibt den Originaltext zurueck + (tu-eq "json-escape/rundlauf" "ROHRKLAMMER 1\" KPL. VERTIKAL" + (ssg-json-get + (ssg-json-parse + (strcat "{\"a\": \"" + (ssg-json-escape "ROHRKLAMMER 1\" KPL. VERTIKAL") "\"}")) + "a")) + ;; Echte Datendatei: der eigentliche Praxisfall (setq _datei (strcat (getenv "DXFMAKRO") "/data/json/geruest.json")) (if (findfile _datei) (progn (setq _daten (ssg-json-laden _datei)) - (tu-eq "json/geruest-baugruppen" 4 (length _daten)) + ;; Drei Bereiche: Rohrabhaenger, Saeulen (1 und 2 Zoll zusammengefuehrt), + ;; Wandbefestigung + (tu-eq "json/geruest-baugruppen" 3 (length _daten)) ;; Baugruppe ueber das ASCII-Feld "id" finden (aeusserer Schluessel ;; traegt Umlaute bzw. Anfuehrungszeichen) (setq _bg nil) @@ -509,7 +524,8 @@ ;; ------------------------------------------------------------ ;; TESTS: GeruestInsert.lsp - reine Hilfsfunktionen ;; ------------------------------------------------------------ -(defun tu-tests-geruest ( / _eintrag _gruppe) +(defun tu-tests-geruest ( / _eintrag _gruppe _teile _konfig _rec _pfad _f + _zurueck _bgtest _saeulen) (princ "\n[GeruestInsert]") (if (and (null (atoms-family 1 '("GERUEST-ARTIKELNUMMERN"))) (atoms-family 1 '("SSG-ENSURE"))) @@ -518,11 +534,6 @@ (if (null (atoms-family 1 '("GERUEST-ARTIKELNUMMERN"))) (tu-record nil "geruest/modul" "GeruestInsert.lsp nicht geladen") (progn - ;; Indizes aus get_tile bei multiple_select - (tu-eq "geruest-indizes/leer" '() (geruest-indizes "")) - (tu-eq "geruest-indizes/eins" '(0) (geruest-indizes "0")) - (tu-eq "geruest-indizes/drei" '(0 3 5) (geruest-indizes "0 3 5")) - (tu-eq "geruest-id-string/int" "821006026" (geruest-id-string 821006026)) (tu-eq "geruest-id-string/str" "0_B10071" (geruest-id-string "0_B10071")) (tu-eq "geruest-id-string/nil" nil (geruest-id-string nil)) @@ -547,6 +558,126 @@ (tu-eq "geruest-artnr/ohne-menge" '("333") (geruest-artikelnummern (ssg-json-parse "{\"artikel\": \"C\", \"sivas_id\": 333}"))) + + ;; --- Mengenfeld --- + (tu-eq "geruest-zahl-p/ziffern" T (geruest-zahl-p "12")) + (tu-eq "geruest-zahl-p/text" nil (geruest-zahl-p "1a")) + (tu-eq "geruest-zahl-p/leer" nil (geruest-zahl-p "")) + ;; Leeres Feld uebernimmt die JSON-Vorgabe, ungueltiges liefert nil + (tu-eq "geruest-menge-wert/leer" 2 (geruest-menge-wert "" 2)) + (tu-eq "geruest-menge-wert/zahl" 5 (geruest-menge-wert "5" 2)) + (tu-eq "geruest-menge-wert/null" nil (geruest-menge-wert "0" 2)) + (tu-eq "geruest-menge-wert/text" nil (geruest-menge-wert "abc" 2)) + (tu-eq "geruest-menge-wert/negativ" nil (geruest-menge-wert "-3" 2)) + + ;; --- Stueckzahl ersetzt die JSON-Menge (Einzelteil) --- + (tu-eq "geruest-artnr-n/einzelteil" '("825002019" "825002019" "825002019") + (geruest-artikelnummern-n _eintrag 3)) + ;; --- Gruppe: ganzes Set n-mal, Mengen INNERHALB bleiben erhalten --- + (tu-eq "geruest-artnr-n/gruppe" '("111" "222" "222" "111" "222" "222") + (geruest-artikelnummern-n _gruppe 2)) + + ;; --- DCL-Label: Anfuehrungszeichen wuerde die Datei zerstoeren --- + (tu-eq "geruest-dcl-label/quote" "ROHRKLAMMER 1'' KPL. VERTIKAL" + (geruest-dcl-label "ROHRKLAMMER 1\" KPL. VERTIKAL")) + (tu-eq "geruest-dcl-label/ohne" "WINKEL 60X60X6X60" + (geruest-dcl-label "WINKEL 60X60X6X60")) + + ;; --- Hilfslisten --- + (tu-eq "geruest-nil-liste" '(nil nil nil) (geruest-nil-liste 3)) + (tu-eq "geruest-liste-setzen" '(nil 7 nil) + (geruest-liste-setzen (geruest-nil-liste 3) 1 7)) + (tu-eq "geruest-alist-setzen/neu" '(("a" . 1)) + (geruest-alist-setzen nil "a" 1)) + (tu-eq "geruest-alist-setzen/ersetzt" '(("a" . 2)) + (geruest-alist-setzen '(("a" . 1)) "a" 2)) + (tu-eq "geruest-zahl-string/int" "2000" (geruest-zahl-string 2000)) + (tu-eq "geruest-zahl-string/leer" "" (geruest-zahl-string nil)) + + ;; --- Konfiguration auf Anbauteile abbilden --- + (setq _teile (list _eintrag _gruppe)) + (tu-eq "geruest-name-index/treffer" 1 + (geruest-name-index "Schraubenset" _teile)) + (tu-eq "geruest-name-index/fehlt" nil + (geruest-name-index "gibt es nicht" _teile)) + ;; Bekanntes Teil wird uebernommen, unbekanntes uebersprungen (nicht + ;; die ganze Konfiguration verworfen) + (setq _konfig (ssg-json-parse + (strcat "{\"name\": \"K1\", \"oben\": [" + "{\"artikel\": \"Schraubenset\", \"menge\": 3}," + "{\"artikel\": \"unbekannt\", \"menge\": 1}]}"))) + (tu-eq "geruest-konfig-position" '(nil 3) + (geruest-konfig-position _konfig "oben" _teile)) + + ;; --- Variante gewinnt vor Baugruppe (TEILEART/dummy) --- + ;; Die zusammengefuehrte Saeulen-Baugruppe unterscheidet 1 Zoll und + ;; 2 Zoll nur noch ueber das teileart-Feld der Variante. + (setq _bgtest (ssg-json-parse + "{\"teileart\": \"Basis\", \"dummy\": \"basis.dwg\"}")) + (tu-eq "geruest-variante-wert/variante-gewinnt" "Speziell" + (geruest-variante-wert + (ssg-json-parse "{\"teileart\": \"Speziell\"}") _bgtest "teileart")) + (tu-eq "geruest-variante-wert/rueckfall" "Basis" + (geruest-variante-wert + (ssg-json-parse "{\"name\": \"X\"}") _bgtest "teileart")) + (tu-eq "geruest-variante-wert/ohne-variante" "basis.dwg" + (geruest-variante-wert nil _bgtest "dummy")) + + ;; --- Zusammengefuehrte Saeulen-Baugruppe --- + (setq _saeulen nil) + (foreach _e (geruest-baugruppen-liste) + (if (equal (car _e) "Saeulen") (setq _saeulen (cdr _e)))) + (tu-eq "geruest/saeulen-vorhanden" T (if _saeulen T nil)) + (if _saeulen + (progn + (tu-eq "geruest/saeulen-varianten" 6 + (length (ssg-json-get _saeulen "geometrie_varianten"))) + (tu-eq "geruest/saeulen-anbauteile" 7 + (length (ssg-json-get _saeulen "anbaugruppen"))) + (tu-eq "geruest/saeulen-titel" "Saeulen" (geruest-titel _saeulen)) + ;; R 1" ist die erste Variante und traegt die eigene TEILEART + (tu-eq "geruest/saeulen-1zoll-teileart" "Geruest Saeule 1 Zoll" + (geruest-variante-wert + (car (ssg-json-get _saeulen "geometrie_varianten")) + _saeulen "teileart")) + ) + ) + + ;; --- Rundlauf Schreiber -> Parser --- + ;; Prueft den Konfigurations-Schreiber ohne BricsCAD-Dialog. Bewusst in + ;; eine temporaere Datei: ein Test darf cfg/geruest_user.json nicht + ;; ueberschreiben. Die Testdaten enthalten absichtlich Anfuehrungs- + ;; zeichen - genau daran wuerde eine fehlende Maskierung scheitern. + (setq _rec + (list (cons "name" "Mein \"Satz\"") + (cons "variante" "ROHR R 1\" 33,7 x 2,6") + (cons "laenge" "2000") + (cons "teilefarbe" "RAL 7035 Lichtgrau") + (cons "oben" (list (list (cons "artikel" "ROHRKLAMMER 1\" KPL. VERTIKAL") + (cons "menge" 2)))) + (cons "unten" nil))) + (setq _pfad (vl-filename-mktemp "grtest" nil ".json")) + (setq _f (open _pfad "w")) + (write-line "{ \"Test\": [" _f) + (geruest-konfig-liste-schreiben _f (list _rec)) + (write-line "] }" _f) + (close _f) + (setq _zurueck (car (ssg-json-get (ssg-json-laden _pfad) "Test"))) + (vl-file-delete _pfad) + (tu-eq "geruest-schreiber/name" "Mein \"Satz\"" + (ssg-json-get _zurueck "name")) + (tu-eq "geruest-schreiber/variante" "ROHR R 1\" 33,7 x 2,6" + (ssg-json-get _zurueck "variante")) + (tu-eq "geruest-schreiber/laenge" "2000" + (ssg-json-get _zurueck "laenge")) + (tu-eq "geruest-schreiber/farbe" "RAL 7035 Lichtgrau" + (ssg-json-get _zurueck "teilefarbe")) + (tu-eq "geruest-schreiber/oben-artikel" "ROHRKLAMMER 1\" KPL. VERTIKAL" + (ssg-json-get (car (ssg-json-get _zurueck "oben")) "artikel")) + (tu-eq "geruest-schreiber/oben-menge" 2 + (ssg-json-get (car (ssg-json-get _zurueck "oben")) "menge")) + (tu-eq "geruest-schreiber/unten-leer" nil + (ssg-json-get _zurueck "unten")) ) ) )