diff --git a/CLAUDE.md b/CLAUDE.md index ab00d03..84ed02c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -23,7 +23,7 @@ AutoLISP-Makros (SSG_LIB) fuer BricsCAD Pro zur automatisierten Erstellung von F | `cfg/` | Konfigurationsdateien | | `data/` | JSON-Daten, DXF-Quellen, Block-Libraries | | `data/ils/` | DWG-Bloecke fuer ILS-Kreisel/Eckrad (AN8, SP8, AP-Typen) | -| `data/json/` | Komponentendaten (Masse, Sivas-IDs) | +| `data/json/` | Komponentendaten (Masse, Sivas-IDs); Geruest: `geruest.json` (GERUEST-Dialog) und `geruest_traeger.json` (Traeger mit Linien-Logik) | | `data/block_libraries/` | DXF Block-Libraries (ils_library.dxf) | | `dcl/` | DCL-Dialogdefinitionen | | `doc/` | Dokumentation, Entwicklungsplan | @@ -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_LADESCHLEIFE_*, OMNI_EDIT | -| Geruest | `GeruestInsert` | GERUEST_SAEULEN, GERUEST_ROHRABHAENGER, GERUEST_WANDBEFESTIGUNG (derselbe Dialog, im jeweiligen Bereich geoeffnet; Rohr-Geruest-Traeger/Gattertraeger noch Platzhalter auf GERUEST) | +| Geruest | `GeruestInsert` | GERUEST_SAEULEN, GERUEST_ROHRABHAENGER, GERUEST_WANDBEFESTIGUNG (derselbe Dialog, im jeweiligen Bereich geoeffnet), GERUEST_TRAEGER (eigener Dialog), GERUEST_TRAEGER_BESCHRIFTEN (Untermenue Geruest -> Funktionen -> Traeger beschriften; alle Traeger der Zeichnung neu beschriften); Gattertraeger noch Platzhalter auf GERUEST | | 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_LADESCHLEIFE_AP110, OMNI_EDIT | Omniflo-Komponenten: Boegen, Weichen, Verbinder, Transferwagen, Ladeschleifen (`data/json/omniflo_ladeschleifen.json`, per `ssg-json-laden`, da die Beschreibungen Kommas enthalten; Blockdatei darf `.dwg` statt `.dxf` sein), 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, GERUEST_SAEULEN, GERUEST_ROHRABHAENGER, GERUEST_WANDBEFESTIGUNG | 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. **Teile-Geometrie**: jede ausgewaehlte Artikelnummer (Basis/oben/unten inkl. aufgeloester Gruppen), fuer die `data/geruest/.dwg` existiert, wird zusaetzlich einmal bei 0,0,0 mit in den Block eingefuegt (`geruest-teile-dwgs`, dedupliziert, fehlende DWGs still uebergangen) - reine visuelle Rueckmeldung, die Attribut-Logik bleibt davon unberuehrt. **TEILEFARBE** ist eine kommagetrennte Liste parallel zu ARTINR (z.B. `,,RAL7030,,RAL7030`): Teile mit `"farbig": true` in `geruest.json` (je Artikel; eine Gruppe vererbt es an ihre Teile, fehlend = false) bekommen die im Dialog gewaehlte Baugruppenfarbe, alle anderen einen leeren Eintrag. ARTINR und TEILEFARBE entstehen aus demselben Durchlauf (`geruest-teile-paare`/`-paare-n`, `geruest-farbliste`), sind also garantiert gleich lang. `export_sivas.py` schreibt daraus je Artikelnummer das Merkmal `{"108": ""}` in `details` (Nummer aus `cfg/export.cfg` `[Sivas] merkmal_teilefarbe`). 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) | +| `GeruestInsert.lsp` | GERUEST, GERUEST_SAEULEN, GERUEST_ROHRABHAENGER, GERUEST_WANDBEFESTIGUNG, GERUEST_TRAEGER, GERUEST_TRAEGER_BESCHRIFTEN | 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; fuer `dummy` ueber `geruest-dummy-wahl`: fehlt die DWG der Variante, Meldung + Rueckfall auf den Baugruppen-Dummy; ist der Dummy zugleich eine Teile-DWG, wird er per `geruest-ohne-pfad` nicht doppelt eingefuegt) - 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 Anzahl. **Zwei Mengenfelder im JSON, bewusst verschieden benannt**: `stueckzahl` = wie oft ein Teil TATSAECHLICH geschrieben wird (je Variante bzw. je Satz/Anbauteil, fehlt = 1), `anzahl_vorgabe` = NUR die Vorbelegung des Anzahl-Feldes im Dialog (Anbaugruppen, fehlt = 1); ein Anbauteil wird Anzahl x `stueckzahl` mal geschrieben, bei einer Gruppe (`teile`) zaehlt die Anzahl die Saetze (`geruest-stueckzahl`/`geruest-anzahl-vorgabe`). In Konfigurationen (`oben`/`unten`) heisst der gespeicherte Dialogwert `anzahl`. 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. **Teile-Geometrie**: jede ausgewaehlte Artikelnummer (Basis/oben/unten inkl. aufgeloester Gruppen), fuer die `data/geruest/.dwg` existiert, wird zusaetzlich einmal bei 0,0,0 mit in den Block eingefuegt (`geruest-teile-dwgs`, dedupliziert, fehlende DWGs still uebergangen) - reine visuelle Rueckmeldung, die Attribut-Logik bleibt davon unberuehrt. **TEILEFARBE** ist eine kommagetrennte Liste parallel zu ARTINR (z.B. `,,RAL7030,,RAL7030`): Teile mit `"farbig": true` in `geruest.json` (je Artikel; eine Gruppe vererbt es an ihre Teile, fehlend = false) bekommen die im Dialog gewaehlte Baugruppenfarbe, alle anderen einen leeren Eintrag. ARTINR und TEILEFARBE entstehen aus demselben Durchlauf (`geruest-teile-paare`/`-paare-n`, `geruest-farbliste`), sind also garantiert gleich lang. `export_sivas.py` schreibt daraus je Artikelnummer das Merkmal `{"108": ""}` in `details` (Nummer aus `cfg/export.cfg` `[Sivas] merkmal_teilefarbe`). 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 Anzahl x `stueckzahl` es vorgeben - 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. **Attribut-Keys in `geruest.json` sind GROSS** (`LAENGE`, `TEILEFARBE`, ...) = die Block-Attribut-Tags; `ssg-json-get` unterscheidet Gross/Klein. **Rohr-Geruest-Traeger** (`GERUEST_TRAEGER`): Traeger-Baugruppen mit Linien-Logik liegen in einer EIGENEN Datei `data/json/geruest_traeger.json` (gleicher Aufbau wie geruest.json, eigener Lader/Cache `geruest-traeger-daten`, Suche `geruest-baugruppe id daten`; gemeinsamer Lader `geruest-json-laden-cache`) - sie tauchen darum nicht im Bereichs-Dropdown von GERUEST auf; dort kann spaeter auch der Gattertraeger hin. eigener fester Dialog `dcl/geruest_traeger.dcl` (Variante, Farbe, je eine Checkbox + Feld fuer `LAENGENABZUGANFANG`/`LAENGENABZUGENDE`, Vorgabe `vorgabe`/`aktiv` aus dem attribute-Block, je Variante ueberschreibbar via `geruest-attr-feld`); danach Einfuege-Schleife bis ESC/Enter: Startpunkt, dann Endpunkt ODER getippte Laenge (+ Richtung). Aktive Abzuege ruecken die Linie beidseitig ein (Strecke 1000, Abzug Anfang 20/Ende 35 -> Start 20 mm hinter dem Klick, Linie 945 mm); `LAENGE` = Netto-Laenge, gegen `LAENGE.min/max` geprueft. KEINE Dummy-DWG: die Geometrie ist eine per `entmake` erzeugte LINE (0,0,0)-(L,0,0) auf dem `layer` der Variante, eingefuegt mit dem Richtungswinkel (ANGBASE/ANGDIR/AUNITS dafuer kurz auf 0). **Querstreben + Muffe** als verschachtelte Dummy-DWGs im selben Block, reine Grafik ohne Export: `querstrebe.dummy` (`data/geruest/dummy_Strebe_30x4.dwg`, Basispunkt Strebenmitte, 30 mm laengs X) an jeder Strebenposition; fehlt die DWG, Rueckfall auf je eine 25-mm-Querlinie an Strebenanfang/-ende (`laenge`/`querlinie`, Standard 30/25 nur im Code). `muffe.dummy` (`dummy_Muffe_1zoll.dwg`, Basispunkt Muffenmitte) am Linienende, NUR bei Varianten mit ausdruecklichem `"mit_muffe": true` (nicht aus dem Namen geraten; bei "mit 2 Muffen" trotzdem nur eine am Ende); `querstrebe`/`muffe` per Variante ueberschreibbar (`geruest-variante-wert`). Dummys werden ohne Drehung eingefuegt (Traeger laeuft im Block entlang +X), ab dem zweiten Mal per Blockname (`geruest-dummy-einfuegen`). Verteilungs-Konfiguration `querstrebe` (`abstand_max`/`rand_min`/`rand_max`, Variante vor Baugruppe), Verteilung `geruest-traeger-streben`: so wenige Streben wie moeglich, beide Raender gleich; Abstand GENAU `abstand_max`, solange der Rand in `rand_min`..`rand_max` bleibt (1300 -> 150|500|500|150), sonst Rand = `rand_min` und Abstand etwas kleiner (1100 -> 100|450|450|100, mit fest 500 geht die Laenge nicht auf); Netto <= 2*rand_max -> eine Strebe mittig, < 2*rand_min -> keine. Schema `*geruest-traeger-attrib-defs*` = `*geruest-attrib-defs*` + die zwei Abzug-Tags; gemeinsamer Block-Schluss mit den anderen Bereichen in `geruest-block-abschliessen`. Reine Berechnung (`geruest-traeger-laenge`/`-start`/`-pruefen`, `geruest-mm-zahl`) in `tests/test_unit.lsp`. **Traeger-Beschriftung** (Checkbox im Dialog, Standard an; z.B. `RT 80/80 L=2650`): EIGENES TEXT-Objekt neben dem Block, abgeleitet NUR aus dem fertigen Block (Einfuegepunkt, DXF 50, Attribute `LAENGE` + `VARIANTE`) - darum nutzen Einfuegen und `GERUEST_TRAEGER_BESCHRIFTEN` dieselbe Funktion `geruest-traeger-beschriften`, und ein von Hand verschobener/gedrehter Traeger wird beim Neubeschriften korrekt erfasst. Verknuepfung Text -> Block per XDATA `SSG_GR_BESCHR` (1005 = Block-Handle): Neubeschriften ersetzt statt verdoppelt, der Sammelbefehl entfernt verwaiste Texte. JSON `beschriftung` (je FELD Variante vor Baugruppe, `geruest-obj-feld`): Inhalt `format` (`%1` Text, `%2` Laenge auf ganze mm) + `text` (fehlt -> Variantenname) und `layer` (je Variante verschieden, darum in JEDER Variante gepflegt - keine Baugruppen-Vorgabe; fehlt er doch, aktueller Layer); Formatierung NUR `stil`/`ausrichtung`/`hoehe` - alles andere regelt der Textstil ausserhalb, es wird kein Stil angelegt (fehlt er: Meldung, aktueller TEXTSTYLE). `ausrichtung` = Codes von TEXT "Ausrichten" (T/M/B + L/C/R), Bezugspunkt auf der Traegerlinie, L/R in LESErichtung. Textwinkel entlang des Traegers, DIN: fuer 90 < w <= 270 Grad um 180 gedreht (`geruest-text-winkel`). 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). | @@ -124,6 +124,7 @@ 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_traeger.dcl` | GERUEST_TRAEGER (GeruestInsert.lsp, `geruest-traeger-dialog`) | | `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 436fe2f..e331874 100644 --- a/Lisp/GeruestInsert.lsp +++ b/Lisp/GeruestInsert.lsp @@ -5,6 +5,7 @@ ;; 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. +;; Rohr-Geruest-Traeger (GERUEST_TRAEGER) lesen data/json/geruest_traeger.json. ;; ;; Beide Befestigungslisten enthalten ALLE Anbauteile der Baugruppe - dasselbe ;; Teil kann oben, unten oder gleichzeitig an beiden Positionen sitzen. @@ -67,6 +68,24 @@ ) ) +;; Rohr-Geruest-Traeger: dasselbe Schema plus die beiden Laengenabzuege und +;; VARIANTE (Name der Geometrie-Variante). Bewusst ein eigenes Schema - die +;; anderen GR_-Bloecke behalten ihres. LAENGE ist beim Traeger die +;; NETTO-Laenge (Strecke minus Abzuege). VARIANTE macht den Block +;; selbstbeschreibend: GERUEST_TRAEGER_BESCHRIFTEN findet darueber die +;; Formatierung der Beschriftung, ohne den Dialog zu kennen. +;; Ohne null-Guard: ein Neuladen der Datei soll ein erweitertes Schema sofort +;; wirksam machen. +(setq *geruest-traeger-attrib-defs* + (append *geruest-attrib-defs* + '(("LAENGENABZUGANFANG" "") + ("LAENGENABZUGENDE" "") + ("VARIANTE" ""))) +) + +;; XDATA-App der Traeger-Beschriftung: Gruppe 1005 = Handle des Traeger-Blocks. +(setq *geruest-beschr-app* "SSG_GR_BESCHR") + (if (null *geruest-block-counter*) (setq *geruest-block-counter* 0)) @@ -82,9 +101,10 @@ ) ) -(defun geruest-json-pfad ( / basis) +;; Pfad einer Datendatei unter data/json, z.B. "geruest.json". +(defun geruest-json-pfad (datei / basis) (if (setq basis (geruest-data-verzeichnis)) - (strcat basis "/json/geruest.json") + (strcat basis "/json/" datei) ) ) @@ -94,28 +114,44 @@ ) ) -;; Datendatei einmal je Zeichnungssitzung laden. -(defun geruest-daten ( / pfad) - (if (null *geruest-daten*) +;; Datendatei unter data/json einmal je Zeichnungssitzung laden und im +;; globalen Symbol cache-sym ablegen. Rueckgabe: die geparsten Daten oder nil +;; (mit Meldung). +(defun geruest-json-laden-cache (datei cache-sym / pfad daten) + (if (null (eval cache-sym)) (progn - (setq pfad (geruest-json-pfad)) + (setq pfad (geruest-json-pfad datei)) (cond ((null pfad) (princ (ssg-text "gr-fehler-datenpfad"))) ((null (findfile pfad)) (princ (ssg-textf "gr-fehler-json-fehlt" (list pfad)))) (T - (setq *geruest-daten* (ssg-json-laden pfad)) - (if (null *geruest-daten*) + (setq daten (ssg-json-laden pfad)) + (set cache-sym daten) + (if (null daten) (princ (ssg-textf "gr-fehler-json-lesbar" (list pfad))) ) ) ) ) ) - *geruest-daten* + (eval cache-sym) +) + +;; Baugruppen des GERUEST-Dialogs (Rohrabhaenger, Saeulen, Wandbefestigung). +(defun geruest-daten ( / ) + (geruest-json-laden-cache "geruest.json" '*geruest-daten*) +) + +;; Traeger-Baugruppen mit Linien-Logik (Rohr-Geruest-Traeger, spaeter z.B. +;; Gattertraeger) - bewusst eine eigene Datei: eigener Dialog, Laenge aus der +;; Zeichnung, Streben/Muffe/Beschriftung statt Anbaugruppen/Konfigurationen. +(defun geruest-traeger-daten ( / ) + (geruest-json-laden-cache "geruest_traeger.json" '*geruest-traeger-daten*) ) ;; Alle Baugruppen als ((id . objekt) ...) in Dateireihenfolge - diese -;; Reihenfolge bestimmt auch die Reihenfolge im Bereichs-Dropdown. +;; Reihenfolge bestimmt auch die Reihenfolge im Bereichs-Dropdown. Die +;; Traeger stehen in geruest_traeger.json und gehoeren nicht hierher. (defun geruest-baugruppen-liste ( / erg) (setq erg nil) (foreach eintrag (geruest-daten) @@ -147,6 +183,40 @@ (if (and w (/= w "")) w (ssg-json-get bg schluessel)) ) +;; Pfad der Dummy-DWG fuer den Block. Ein "dummy" in der Geometrie-Variante +;; wird bevorzugt; fehlt die Datei unter data/geruest, wird gemeldet und auf +;; den "dummy" der Baugruppe zurueckgefallen. Rueckgabe: (datei . pfad) - +;; pfad ist nil, wenn kein Datenverzeichnis bekannt ist; ob die +;; Rueckfall-Datei existiert, prueft der Aufrufer. +(defun geruest-dummy-wahl (variante bg / v pfad) + (setq v (if variante (ssg-json-get variante "dummy"))) + (if (and v (= (type v) 'STR) (/= v "") + (setq pfad (geruest-dummy-pfad v)) + (findfile pfad)) + (cons v pfad) + (progn + (if (and v (= (type v) 'STR) (/= v "")) + (princ (ssg-textf "gr-fehler-dummy-variante" + (list (if pfad pfad v) + (if (ssg-json-get bg "dummy") + (ssg-json-get bg "dummy") + "-")))) + ) + (setq v (ssg-json-get bg "dummy")) + (cons v (geruest-dummy-pfad v)) + ) + ) +) + +;; Liste ohne die Eintraege, die (ohne Gross/Klein) gleich pfad sind. Damit +;; landet eine Teile-DWG, die zugleich der Dummy ist, nicht doppelt im Block. +(defun geruest-ohne-pfad (pfad liste) + (if pfad + (vl-remove-if '(lambda (p) (= (strcase p) (strcase pfad))) liste) + liste + ) +) + ;; ============================================================ ;; TEILE AUSWERTEN @@ -162,9 +232,23 @@ ) ) -(defun geruest-menge (eintrag / m) - (setq m (ssg-json-get eintrag "menge")) - (if (and m (numberp m) (> m 0)) (fix m) 1) +;; Zwei Mengenfelder im JSON, bewusst verschieden benannt: +;; "stueckzahl" = wie oft das Teil TATSAECHLICH geschrieben wird - je +;; Einbau (Variante) bzw. je Satz/Anbauteil. Fehlt = 1. +;; "anzahl_vorgabe" = NUR die Vorbelegung des Anzahl-Feldes im Dialog +;; (Anbaugruppen). Fehlt = 1. +;; Ein Anbauteil wird Anzahl (Dialog) x stueckzahl mal geschrieben; bei einer +;; Gruppe ("teile") ist die Anzahl die Zahl der Saetze. +(defun geruest-positiv-int (w / ) + (if (and w (numberp w) (> w 0)) (fix w) 1) +) + +(defun geruest-stueckzahl (eintrag) + (geruest-positiv-int (ssg-json-get eintrag "stueckzahl")) +) + +(defun geruest-anzahl-vorgabe (eintrag) + (geruest-positiv-int (ssg-json-get eintrag "anzahl_vorgabe")) ) ;; Sivas-ID als String. Im JSON stehen rein numerische IDs als Zahl @@ -190,7 +274,7 @@ ) ;; Alle Teile eines Eintrags als ((artnr . farbig) ...), jedes gemaess -;; "menge" wiederholt. Benannte Gruppen ("teile") werden rekursiv aufgeloest. +;; "stueckzahl" wiederholt. Benannte Gruppen ("teile") werden rekursiv aufgeloest. ;; farbig = darf das Teil die Baugruppenfarbe tragen ("farbig" im JSON). Eine ;; Gruppe mit "farbig": true vererbt das an ihre Teile (erbe); ein Teil kann ;; die Vererbung nicht per false aufheben - false und "fehlt" sind beide nil. @@ -207,12 +291,12 @@ (setq erg (mapcar '(lambda (nr) (cons nr farbig)) (geruest-wiederholen (geruest-id-string (ssg-json-get eintrag "sivas_id")) - (geruest-menge eintrag)))) + (geruest-stueckzahl eintrag)))) ) erg ) -;; Alle Artikelnummern eines Eintrags, jede gemaess "menge" wiederholt. +;; Alle Artikelnummern eines Eintrags, jede gemaess "stueckzahl" wiederholt. (defun geruest-artikelnummern (eintrag) (mapcar 'car (geruest-teile-paare eintrag nil)) ) @@ -267,7 +351,7 @@ ;; Eingabe aus einem Mengenfeld auswerten. ;; Leer -> Vorgabewert aus dem JSON, gueltig -> Zahl >= 1, sonst nil. -(defun geruest-menge-wert (s vorgabe) +(defun geruest-anzahl-wert (s vorgabe) (setq s (ssg-cfg-trim (if s s ""))) (cond ((= s "") vorgabe) @@ -288,30 +372,18 @@ ) ) -;; 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 +;; Teile eines Anbauteils bei der im Dialog gewaehlten Anzahl: der Eintrag +;; (Einzelteil mit seiner "stueckzahl" bzw. benannte Gruppe als kompletter +;; Satz) anzahl-mal. Die Stueckzahlen INNERHALB eines Satzes bleiben erhalten ;; (ein Schraubenset bleibt 1 Scheibe + 2 Muttern + 1 Schraube je Satz). -(defun geruest-teile-paare-n (eintrag anzahl / teile einmal erg i farbig) - (setq teile (ssg-json-get eintrag "teile")) - (setq farbig (if (ssg-json-get eintrag "farbig") T nil)) - (if teile - (progn - (setq einmal nil) - (foreach teil teile - (setq einmal (append einmal (geruest-teile-paare teil farbig))) - ) - (setq erg nil i 0) - (while (< i anzahl) - (setq erg (append erg einmal)) - (setq i (1+ i)) - ) - erg - ) - (mapcar '(lambda (nr) (cons nr farbig)) - (geruest-wiederholen - (geruest-id-string (ssg-json-get eintrag "sivas_id")) anzahl)) +(defun geruest-teile-paare-n (eintrag anzahl / einmal erg i) + (setq einmal (geruest-teile-paare eintrag nil)) + (setq erg nil i 0) + (while (< i anzahl) + (setq erg (append erg einmal)) + (setq i (1+ i)) ) + erg ) (defun geruest-artikelnummern-n (eintrag anzahl) @@ -419,7 +491,7 @@ ;; 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 +;; die Anzahl (Feld "anzahl"). 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))) @@ -427,7 +499,7 @@ (setq idx (geruest-name-index (ssg-json-get e "artikel") anbauteile)) (if idx (progn - (setq n (ssg-json-get e "menge")) + (setq n (ssg-json-get e "anzahl")) (setq mengen (geruest-liste-setzen mengen idx (if (and n (numberp n) (> n 0)) (fix n) 1))) ) @@ -561,20 +633,20 @@ (defun geruest-dlg-standard ( / erg) (setq erg nil) (foreach a *geruest-anbauteile* - (setq erg (append erg (list (cons "0" (itoa (geruest-menge a)))))) + (setq erg (append erg (list (cons "0" (itoa (geruest-anzahl-vorgabe a)))))) ) erg ) ;; Mengenliste (nil oder Zahl je Teil) in den Tile-Zustand umsetzen. -(defun geruest-mengen->tiles (mengen / erg i a) +(defun geruest-anzahlen->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))))))) + (cons "0" (itoa (geruest-anzahl-vorgabe a))))))) (setq i (1+ i)) ) erg @@ -623,17 +695,44 @@ (princ) ) -;; Aktuell gewaehlte Farbe als Text. -(defun geruest-dlg-farbwert ( / idx) - (setq idx (atoi *geruest-dlg-farbe*)) +;; Farbliste des Dialogs aus der TEILEFARBE-Definition aufbauen: Optionen + +;; freie Option ans Listenende; deren Index merkt sich der Callback, um das +;; Freitextfeld freizuschalten. Genutzt vom GERUEST- und vom Traeger-Dialog. +(defun geruest-farb-liste-setzen (farb-opts / frei-opt) + (setq *geruest-farb-liste* nil *geruest-frei-idx* -1) + (if farb-opts + (progn + (setq *geruest-farb-liste* (ssg-json-get farb-opts "optionen")) + (if (ssg-json-get farb-opts "frei_erlaubt") + (progn + (setq frei-opt (ssg-json-get farb-opts "frei_option")) + (if (null frei-opt) (setq frei-opt "Individuell")) + (setq *geruest-frei-idx* (length *geruest-farb-liste*)) + (setq *geruest-farb-liste* + (append *geruest-farb-liste* (list frei-opt))) + ) + ) + ) + ) +) + +;; Gewaehlte Farbe als Text. idx-str = Tile-Wert der Farbliste, +;; frei = Inhalt des Freitextfelds. +(defun geruest-farbwert (idx-str frei / idx) + (setq idx (atoi idx-str)) (cond ((null *geruest-farb-liste*) "") - ((= idx *geruest-frei-idx*) *geruest-dlg-frei*) + ((= idx *geruest-frei-idx*) frei) ((nth idx *geruest-farb-liste*) (nth idx *geruest-farb-liste*)) (T "") ) ) +;; Aktuell gewaehlte Farbe des GERUEST-Dialogs als Text. +(defun geruest-dlg-farbwert ( / ) + (geruest-farbwert *geruest-dlg-farbe* *geruest-dlg-frei*) +) + ;; 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)". @@ -671,10 +770,10 @@ *geruest-dlg-frei* farbe)) ) (setq *geruest-dlg-oben* - (geruest-mengen->tiles + (geruest-anzahlen->tiles (geruest-konfig-position k "oben" *geruest-anbauteile*))) (setq *geruest-dlg-unten* - (geruest-mengen->tiles + (geruest-anzahlen->tiles (geruest-konfig-position k "unten" *geruest-anbauteile*))) ) ) @@ -696,16 +795,16 @@ ) ;; Angehakte Teile einer Spalte mit gueltiger Menge einsammeln. -;; Rueckgabe: (T . ((index . menge) ...)) oder nil bei ungueltigem Mengenfeld. +;; Rueckgabe: (T . ((index . anzahl) ...)) 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) +(defun geruest-anzahlen-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))) + (setq m (geruest-anzahl-wert (cdr paar) (geruest-anzahl-vorgabe a))) (if m (setq erg (append erg (list (cons i m)))) (setq ok nil) @@ -739,11 +838,11 @@ (setq a (nth i *geruest-anbauteile*)) (if (= (car paar) "1") (progn - (setq m (geruest-menge-wert (cdr paar) (geruest-menge a))) + (setq m (geruest-anzahl-wert (cdr paar) (geruest-anzahl-vorgabe a))) (if m (setq erg (append erg (list (list (cons "artikel" (geruest-eintrag-name a)) - (cons "menge" m))))) + (cons "anzahl" m))))) ) ) ) @@ -769,7 +868,7 @@ (write-line (strcat " { \"artikel\": \"" (ssg-json-escape (ssg-json-get e "artikel")) - "\", \"menge\": " (itoa (ssg-json-get e "menge")) " }" + "\", \"anzahl\": " (itoa (ssg-json-get e "anzahl")) " }" (if (< i n) "," "")) f) ) @@ -914,38 +1013,22 @@ ) ;; Dialog fuer einen Bereich zeigen. Rueckgabe: -;; ("OK" variante-index ((idx . menge) ...) ((idx . menge) ...) laenge farbe) +;; ("OK" variante-index ((idx . anzahl) ...) ((idx . anzahl) ...) 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 + farb-opts 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 "Teilefarbe"))) + (setq laenge-def (if attr (ssg-json-get attr "LAENGE"))) + (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"))) - ;; Farboptionen + freie Option ans Listenende; Index davon merkt sich der - ;; Callback, um das Freitextfeld freizuschalten. - (setq *geruest-farb-liste* nil *geruest-frei-idx* -1) - (if farb-opts - (progn - (setq *geruest-farb-liste* (ssg-json-get farb-opts "optionen")) - (if (ssg-json-get farb-opts "frei_erlaubt") - (progn - (setq frei-opt (ssg-json-get farb-opts "frei_option")) - (if (null frei-opt) (setq frei-opt "Individuell")) - (setq *geruest-frei-idx* (length *geruest-farb-liste*)) - (setq *geruest-farb-liste* - (append *geruest-farb-liste* (list frei-opt))) - ) - ) - ) - ) + (geruest-farb-liste-setzen farb-opts) (setq *geruest-konfig-liste* (geruest-konfigs bg)) ;; Frueheren Stand dieses Bereichs wiederherstellen, sonst Ausgangszustand @@ -1087,8 +1170,8 @@ ) (T - (setq oben-m (geruest-mengen-lesen *geruest-dlg-oben*)) - (setq unten-m (geruest-mengen-lesen *geruest-dlg-unten*)) + (setq oben-m (geruest-anzahlen-lesen *geruest-dlg-oben*)) + (setq unten-m (geruest-anzahlen-lesen *geruest-dlg-unten*)) (if (or (null oben-m) (null unten-m)) (alert (ssg-text "gr-alert-menge")) (progn @@ -1136,11 +1219,15 @@ ;; als reine Geometrie bei 0,0,0 mit in den Block wandern. ;; Rueckgabe: (blockname . entity) oder nil. (defun geruest-block-bauen (bg variante werte pt teile-dwgs / 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)) + dummy-attribs attribs) + ;; Eine Variante darf eine eigene Dummy-DWG mitbringen; sonst (oder wenn + ;; deren Datei fehlt) gilt die der Baugruppe. + (setq datei (geruest-dummy-wahl variante bg)) + (setq dummy (cdr datei) + datei (car datei)) + ;; Ist der Dummy zugleich eine Teile-DWG (z.B. 821006036.dwg beim + ;; T-Abhaenger), nicht ein zweites Mal einfuegen. + (setq teile-dwgs (geruest-ohne-pfad dummy teile-dwgs)) (if (or (null dummy) (null (findfile dummy))) (progn (princ (ssg-textf "gr-fehler-dummy" (list (if dummy dummy datei)))) @@ -1173,46 +1260,59 @@ ;; ssg-attrib-merge legt alle Tags aus *geruest-attrib-defs* an und ;; uebernimmt zusaetzlich alle Tags der Dummy-DWG. (setq attribs (ssg-attrib-merge dummy-attribs *geruest-attrib-defs*)) - (foreach paar werte - (if (assoc (car paar) attribs) - (setq attribs (subst paar (assoc (car paar) attribs) attribs)) - (setq attribs (append attribs (list paar))) - ) - ) + ;; nil = Drehwinkel interaktiv am Cursor + (geruest-block-abschliessen bg attribs werte lastEnt pt nil) + ) + ) +) - (ssg-attrib-make-defs (ssg-attrib-alist-to-defs attribs) 50.0) +;; Gemeinsamer Schluss aller Geruest-Bloecke: Werte ins Schema eintragen, +;; ATTDEFs erzeugen, alles Neue seit lastEnt zu GR__ zusammenfassen +;; und bei pt einfuegen. +;; attribs = Schema-Alist (Tags + Vorgabewerte), werte = berechnete Werte +;; rot = Drehwinkel in Grad (Zahl) oder nil fuer interaktive Drehung. +;; Bei einer Zahl muss der Aufrufer ANGBASE/ANGDIR/AUNITS auf 0 +;; gesetzt haben, sonst deutet INSERT den Wert anders. +;; Rueckgabe: (blockname . entity) oder nil. +(defun geruest-block-abschliessen (bg attribs werte lastEnt pt rot + / e ss bname blockEnt) + (foreach paar werte + (if (assoc (car paar) attribs) + (setq attribs (subst paar (assoc (car paar) attribs) attribs)) + (setq attribs (append attribs (list paar))) + ) + ) - ;; Alles Neue seit lastEnt einsammeln (Dummy-INSERT + ATTDEFs) - (setq ss (ssadd)) - (setq e (if lastEnt (entnext lastEnt) (entnext))) - (while e - (ssadd e ss) - (setq e (entnext e)) - ) + (ssg-attrib-make-defs (ssg-attrib-alist-to-defs attribs) 50.0) - (setq bname (geruest-blockname (ssg-json-get bg "id"))) - (command "_.-BLOCK" bname (list 0.0 0.0 0.0) ss "") + ;; Alles Neue seit lastEnt einsammeln (Geometrie + ATTDEFs) + (setq ss (ssadd)) + (setq e (if lastEnt (entnext lastEnt) (entnext))) + (while e + (ssadd e ss) + (setq e (entnext e)) + ) - ;; Stand VOR dem Einfuegen merken: bricht der Nutzer die Drehung mit ESC - ;; ab, entsteht kein neues INSERT und (entlast) zeigt auf ein voellig - ;; fremdes Objekt - ohne diesen Vergleich wuerden dessen Attribute - ;; ueberschrieben. - (setq lastEnt (entlast)) - ;; pause = Drehwinkel interaktiv am Cursor - (command "_.INSERT" bname pt 1 1 pause) - ;; Laufende Einfuegung sicher beenden (z.B. ESC waehrend der Drehung) - (if (> (getvar "CMDACTIVE") 0) (command)) - (if (> (getvar "CMDACTIVE") 0) (command)) + (setq bname (geruest-blockname (ssg-json-get bg "id"))) + (command "_.-BLOCK" bname (list 0.0 0.0 0.0) ss "") - (setq blockEnt (entlast)) - (if (and blockEnt (not (eq blockEnt lastEnt)) - (= (cdr (assoc 0 (entget blockEnt))) "INSERT")) - (progn - (ssg-attrib-set-on blockEnt attribs) - (ssg-block-layer-vorgabe blockEnt bname attribs) - (cons bname blockEnt) - ) - ) + ;; Stand VOR dem Einfuegen merken: bricht der Nutzer die Drehung mit ESC + ;; ab, entsteht kein neues INSERT und (entlast) zeigt auf ein voellig + ;; fremdes Objekt - ohne diesen Vergleich wuerden dessen Attribute + ;; ueberschrieben. + (setq lastEnt (entlast)) + (command "_.INSERT" bname pt 1 1 (if rot rot pause)) + ;; Laufende Einfuegung sicher beenden (z.B. ESC waehrend der Drehung) + (if (> (getvar "CMDACTIVE") 0) (command)) + (if (> (getvar "CMDACTIVE") 0) (command)) + + (setq blockEnt (entlast)) + (if (and blockEnt (not (eq blockEnt lastEnt)) + (= (cdr (assoc 0 (entget blockEnt))) "INSERT")) + (progn + (ssg-attrib-set-on blockEnt attribs) + (ssg-block-layer-vorgabe blockEnt bname attribs) + (cons bname blockEnt) ) ) ) @@ -1223,7 +1323,7 @@ ;; ============================================================ ;; Teile-Paare ((artnr . farbig) ...) einer Befestigungsauswahl -;; ((index . menge) ...). +;; ((index . anzahl) ...). (defun geruest-auswahl-paare (auswahl anbauteile / erg) (setq erg nil) (foreach paar auswahl @@ -1362,5 +1462,799 @@ (defun c:GERUEST_ROHRABHAENGER ( / ) (geruest-starten "Rohrabhaenger")) (defun c:GERUEST_WANDBEFESTIGUNG ( / ) (geruest-starten "Wandbefestigung")) -(princ "\nGeruestInsert.lsp geladen. Befehle: GERUEST, GERUEST_SAEULEN, GERUEST_ROHRABHAENGER, GERUEST_WANDBEFESTIGUNG") + +;; ============================================================ +;; ROHR-GERUEST-TRAEGER (Daten: data/json/geruest_traeger.json) +;; ============================================================ +;; Eigener Dialog (dcl/geruest_traeger.dcl): Variante, Farbe und zwei +;; Laengenabzuege. Die Laenge selbst wird DANACH in der Zeichnung bestimmt - +;; Startpunkt, dann Endpunkt oder getippte Laenge (+ Richtung), wie beim +;; Kreisel. Aktive Abzuege ruecken die Linie beidseitig ein: Strecke 1000, +;; Abzug Anfang 20, Ende 35 -> Linie beginnt 20 mm hinter dem Startklick und +;; ist 945 mm lang. +;; +;; Keine Dummy-DWG: die Geometrie ist eine einzige LINE, die je Block eine +;; andere Laenge hat. Sie entsteht per entmake von (0,0,0) nach (L,0,0) auf +;; dem Layer der Variante; der Block wird am eingerueckten Startpunkt mit dem +;; Richtungswinkel eingefuegt. Es wird wiederholt eingefuegt, bis der Nutzer +;; ESC/Enter drueckt - jeder Traeger ist ein eigener GR_Traeger_-Block. + +;; Baugruppe ueber das ASCII-Feld "id" in daten (z.B. (geruest-traeger-daten)). +(defun geruest-baugruppe (id daten / erg) + (setq erg nil) + (foreach eintrag daten + (if (and (null erg) (listp (cdr eintrag)) + (equal (ssg-json-get (cdr eintrag) "id") id)) + (setq erg (cdr eintrag)) + ) + ) + erg +) + +;; Feld einer Attribut-Definition (attribute -> TAG -> feld). Eine Variante +;; darf einen eigenen "attribute"-Block mitbringen, der vor dem der Baugruppe +;; gilt (z.B. ein anderer Laengenabzug bei einer Muffen-Variante). assoc +;; statt ssg-json-get, damit ein in der Variante gesetztes false (= nil) den +;; Wert der Baugruppe ueberschreibt, statt als "fehlt" durchzufallen. +(defun geruest-attr-feld (variante bg tag feld) + (geruest-obj-feld variante bg (list "attribute" tag) feld) +) + +;; Objekt unter einem Schluesselpfad, z.B. ("attribute" "LAENGE"); nil, wenn +;; ein Schluessel fehlt. +(defun geruest-json-pfad-get (obj pfad) + (foreach k pfad (setq obj (if (and obj (listp obj)) (ssg-json-get obj k)))) + obj +) + +;; Allgemeine Form von geruest-attr-feld: Feld unter pfad, Variante vor +;; Baugruppe, je FELD aufgeloest (eine Variante nennt nur, was abweicht). +;; Genutzt fuer "attribute" und "beschriftung". +(defun geruest-obj-feld (variante bg pfad feld / def paar) + (setq def (if variante (geruest-json-pfad-get variante pfad))) + (setq paar (if (and def (listp def)) (assoc feld def))) + (if paar + (cdr paar) + (ssg-json-get (geruest-json-pfad-get bg pfad) feld) + ) +) + +;; Millimeter-Eingabe als Zahl >= 0 (Komma wie Punkt), sonst nil. +;; Leere Eingabe -> nil. +(defun geruest-mm-zahl (s / w) + (setq s (vl-string-translate "," "." (ssg-cfg-trim (if s s "")))) + (if (and (/= s "") (setq w (distof s 2)) (>= w 0.0)) w) +) + +;; Abzug-Feld: leer -> 0, gueltige Zahl -> Zahl, sonst nil. +(defun geruest-abzug-wert (s) + (if (= (ssg-cfg-trim (if s s "")) "") + 0.0 + (geruest-mm-zahl s) + ) +) + +;; Millimeterwert fuer Attribute/Meldungen: ganze Zahl ohne Nachkommastelle, +;; sonst eine Stelle. +(defun geruest-mm-string (w / r) + (setq r (fix (+ w 0.5))) + (if (equal w r 1e-6) (itoa r) (rtos w 2 1)) +) + +;; Netto-Laenge der Linie: Strecke minus beide Abzuege, nil wenn <= 0. +(defun geruest-traeger-laenge (strecke abzug-a abzug-e / netto) + (setq netto (- strecke (if abzug-a abzug-a 0.0) (if abzug-e abzug-e 0.0))) + (if (> netto 1e-6) netto) +) + +;; Eingerueckter Startpunkt: abzug-a entlang winkel ab p1, Z von p1. +(defun geruest-traeger-start (p1 winkel abzug-a / p) + (setq p (polar p1 winkel (if abzug-a abzug-a 0.0))) + (list (car p) (cadr p) (if (caddr p1) (caddr p1) 0.0)) +) + +;; Positionen der Querstreben entlang der Traegerlinie (Abstand ab Linienanfang). +;; Regeln: Abstand zwischen zwei Streben hoechstens abstand-max, beide +;; Endabstaende gleich und in [rand-min, rand-max], so wenige Streben wie +;; moeglich. +;; netto <= 2*rand-max -> eine Strebe in der Mitte +;; sonst -> k = aufgerundet (netto - 2*rand-max) / abstand-max +;; Felder (= wenigste Streben). Rand +;; r = max(rand-min, (netto - k*abstand-max) / 2): +;; der Abstand ist damit GENAU abstand-max, solange der +;; Rand >= rand-min bleibt; nur sonst bleibt der Rand +;; bei rand-min und der Abstand wird etwas kleiner. +;; Mit genau abstand-max geht nicht jede Laenge auf (1100 mm: 2 Streben -> +;; Rand 300, 3 Streben -> Rand 50) - dort ergibt sich 100 | 450 | 450 | 100. +;; r <= rand-max ist durch die Wahl von k garantiert. +;; Kuerzer als 2*rand-min -> keine Strebe (der Mindestrand waere verletzt). +(defun geruest-traeger-streben (netto abstand-max rand-min rand-max / k r s i erg) + (cond + ((or (null netto) (< netto (- (* 2.0 rand-min) 1e-6))) nil) + ((<= netto (+ (* 2.0 rand-max) 1e-6)) (list (/ netto 2.0))) + (T + (setq k (/ (- netto (* 2.0 rand-max)) abstand-max)) + ;; aufrunden, Rundungsrauschen (z.B. 2.0000000001) nicht mitzaehlen + (setq k (if (equal k (fix k) 1e-6) (fix k) (1+ (fix k)))) + (setq r (max rand-min (/ (- netto (* k abstand-max)) 2.0))) + (setq s (/ (- netto (* 2.0 r)) k)) + (setq erg nil i k) + (while (>= i 0) + (setq erg (cons (+ r (* i s)) erg)) + (setq i (1- i)) + ) + erg + ) + ) +) + +;; Querstreben-Konfiguration (Variante vor Baugruppe) als +;; (dummy laenge querlinie abstand-max rand-min rand-max) oder nil, wenn keine +;; "querstrebe" definiert ist. +;; dummy = DWG unter data/geruest (Basispunkt Strebenmitte, 30 mm laengs +;; X) oder nil +;; laenge/querlinie = nur fuer den Rueckfall ohne Dummy-DWG: zwei Querlinien +;; im Abstand laenge, je querlinie lang +;; Fehlende Zahlenwerte -> Standard 30/25/500/100/250. +(defun geruest-traeger-strebe-cfg (variante bg / q w d) + (setq q (geruest-variante-wert variante bg "querstrebe")) + (if (and q (listp q)) + (progn + (setq d (ssg-json-get q "dummy")) + (cons (if (and d (= (type d) 'STR) (/= d "")) d) + (mapcar '(lambda (feld std) + (setq w (ssg-json-get q feld)) + (if (numberp w) (float w) std)) + '("laenge" "querlinie" "abstand_max" "rand_min" "rand_max") + '(30.0 25.0 500.0 100.0 250.0))) + ) + ) +) + +;; Muffen-Dummy einer Variante: nur bei "mit_muffe": true die "dummy"-Datei +;; aus "muffe" (Variante vor Baugruppe), sonst nil. Das Flag steht bewusst +;; ausdruecklich je Variante, statt aus dem Namen ("mit Muffe") geraten zu +;; werden. Bei "mit 2 Muffen" wird trotzdem nur eine Muffe gezeichnet. +(defun geruest-traeger-muffe-dummy (variante bg / m d) + (if (and variante (ssg-json-get variante "mit_muffe")) + (progn + (setq m (geruest-variante-wert variante bg "muffe")) + (setq d (if (and m (listp m)) (ssg-json-get m "dummy"))) + (if (and d (= (type d) 'STR) (/= d "")) d) + ) + ) +) + +;; Liegt netto in [min-l, max-l]? Fehlende Grenzen gelten nicht. +(defun geruest-traeger-pruefen (netto min-l max-l) + (and netto + (or (null min-l) (>= netto min-l)) + (or (null max-l) (<= netto max-l))) +) + +;; BEZEICHNUNG: Teileart + Variante + aktive Abzuege, mit " + " verkettet +;; (Artikelnamen enthalten Kommas, siehe geruest-einfuegen). +(defun geruest-traeger-bezeichnung (teileart variante-name abzug-a abzug-e / teile) + (setq teile (list teileart variante-name)) + (if (> abzug-a 0.0) + (setq teile (append teile (list (ssg-textf "gr-tr-bez-abzug-anfang" + (list (geruest-mm-string abzug-a))))))) + (if (> abzug-e 0.0) + (setq teile (append teile (list (ssg-textf "gr-tr-bez-abzug-ende" + (list (geruest-mm-string abzug-e))))))) + (ssg-join teile " + ") +) + +(defun geruest-traeger-dcl-pfad ( / basis) + (setq basis (cond ((getenv "DXFM_DCL")) + ((getenv "DXFMAKRO") (strcat (getenv "DXFMAKRO") "/dcl")))) + (if basis + (strcat (vl-string-translate "\\" "/" basis) "/geruest_traeger.dcl") + ) +) + +;; --- Dialogzustand (Globals, siehe Abschnitt DIALOGZUSTAND oben) --- +;; Bleibt zwischen zwei GERUEST_TRAEGER-Aufrufen erhalten. + +;; Abzug-Vorgaben (aktiv + Wert) der Variante idx in den Zustand laden. +(defun geruest-tr-vorgaben-laden (idx / v) + (setq v (nth idx (ssg-json-get *geruest-tr-bg* "geometrie_varianten"))) + (setq *geruest-tr-abzug-a* + (if (geruest-attr-feld v *geruest-tr-bg* "LAENGENABZUGANFANG" "aktiv") "1" "0")) + (setq *geruest-tr-abzug-a-w* + (geruest-zahl-string (geruest-attr-feld v *geruest-tr-bg* "LAENGENABZUGANFANG" "vorgabe"))) + (setq *geruest-tr-abzug-e* + (if (geruest-attr-feld v *geruest-tr-bg* "LAENGENABZUGENDE" "aktiv") "1" "0")) + (setq *geruest-tr-abzug-e-w* + (geruest-zahl-string (geruest-attr-feld v *geruest-tr-bg* "LAENGENABZUGENDE" "vorgabe"))) +) + +(defun geruest-tr-abzug-schreiben ( / ) + (set_tile "abzug_a" *geruest-tr-abzug-a*) + (set_tile "abzug_a_w" *geruest-tr-abzug-a-w*) + (set_tile "abzug_e" *geruest-tr-abzug-e*) + (set_tile "abzug_e_w" *geruest-tr-abzug-e-w*) + (mode_tile "abzug_a_w" (if (= *geruest-tr-abzug-a* "1") 0 1)) + (mode_tile "abzug_e_w" (if (= *geruest-tr-abzug-e* "1") 0 1)) +) + +;; Variantenwechsel im Dialog: Abzug-Vorgaben der neuen Variante uebernehmen. +(defun geruest-tr-variante-gewaehlt (wert / ) + (setq *geruest-tr-variante* wert) + (geruest-tr-vorgaben-laden (atoi wert)) + (geruest-tr-abzug-schreiben) +) + +(defun geruest-tr-dlg-lesen ( / ) + (setq *geruest-tr-variante* (get_tile "variante")) + (setq *geruest-tr-farbe* (get_tile "farbe")) + (setq *geruest-tr-frei* (get_tile "farbe_frei")) + (setq *geruest-tr-abzug-a* (get_tile "abzug_a")) + (setq *geruest-tr-abzug-a-w* (get_tile "abzug_a_w")) + (setq *geruest-tr-abzug-e* (get_tile "abzug_e")) + (setq *geruest-tr-abzug-e-w* (get_tile "abzug_e_w")) + (setq *geruest-tr-beschriftung* (get_tile "beschriftung")) +) + +;; Wert eines Abzugs aus dem Zustand: inaktiv -> 0, aktiv -> Zahl oder nil. +(defun geruest-tr-abzug (aktiv wert) + (if (= aktiv "1") (geruest-abzug-wert wert) 0.0) +) + +;; Traeger-Dialog. Rueckgabe: +;; (variante-index farbe abzug-anfang abzug-ende beschriften-p) oder nil bei +;; Abbruch. +(defun geruest-traeger-dialog (bg / varianten dcl-pfad dat fertig rueckgabe + ergebnis aw ew) + (setq varianten (ssg-json-get bg "geometrie_varianten")) + (setq *geruest-tr-bg* bg) + (geruest-farb-liste-setzen + (ssg-json-get (ssg-json-get bg "attribute") "TEILEFARBE")) + ;; Ausgangszustand beim ersten Aufruf bzw. wenn der gemerkte Index nach + ;; einer JSON-Aenderung nicht mehr passt. + (if (or (null *geruest-tr-variante*) + (>= (atoi *geruest-tr-variante*) (length varianten))) + (progn + (setq *geruest-tr-variante* "0" *geruest-tr-farbe* "0" *geruest-tr-frei* "") + (geruest-tr-vorgaben-laden 0) + ) + ) + ;; Beschriftung standardmaessig an; der Haken bleibt zwischen Aufrufen + (if (null *geruest-tr-beschriftung*) (setq *geruest-tr-beschriftung* "1")) + (setq dcl-pfad (geruest-traeger-dcl-pfad)) + (setq fertig nil rueckgabe nil) + + ;; Schleife: ungueltige Eingaben zeigen den Dialog mit dem bisherigen + ;; Stand erneut - DCL kann Zahlen nicht selbst pruefen. + (while (not fertig) + (setq dat (if (and dcl-pfad (findfile dcl-pfad)) (load_dialog dcl-pfad) -1)) + (if (or (< dat 0) (not (new_dialog "geruest_traeger" dat))) + (progn + (alert (ssg-textf "gr-tr-fehler-dcl" + (list (if dcl-pfad dcl-pfad "geruest_traeger.dcl")))) + (setq fertig T) + ) + (progn + (start_list "variante") + (foreach v varianten (add_list (geruest-eintrag-name v))) + (end_list) + (set_tile "variante" *geruest-tr-variante*) + + (if *geruest-farb-liste* + (progn + (start_list "farbe") + (foreach f *geruest-farb-liste* (add_list f)) + (end_list) + (set_tile "farbe" *geruest-tr-farbe*) + (mode_tile "farbe_frei" + (if (= (atoi *geruest-tr-farbe*) *geruest-frei-idx*) 0 1)) + (action_tile "farbe" + "(mode_tile \"farbe_frei\" (if (= (atoi $value) *geruest-frei-idx*) 0 1))") + ) + (progn + (mode_tile "farbe" 1) + (mode_tile "farbe_frei" 1) + ) + ) + (set_tile "farbe_frei" *geruest-tr-frei*) + (geruest-tr-abzug-schreiben) + (set_tile "beschriftung" *geruest-tr-beschriftung*) + (set_tile "hinweis" (ssg-text "gr-tr-hinweis")) + + (action_tile "variante" "(geruest-tr-variante-gewaehlt $value)") + (action_tile "abzug_a" + "(mode_tile \"abzug_a_w\" (if (= $value \"1\") 0 1))") + (action_tile "abzug_e" + "(mode_tile \"abzug_e_w\" (if (= $value \"1\") 0 1))") + (action_tile "accept" "(geruest-tr-dlg-lesen) (done_dialog 1)") + (action_tile "cancel" "(done_dialog 0)") + (setq ergebnis (start_dialog)) + (unload_dialog dat) + + (cond + ((= ergebnis 0) (setq fertig T)) + ((or (null (setq aw (geruest-tr-abzug *geruest-tr-abzug-a* *geruest-tr-abzug-a-w*))) + (null (setq ew (geruest-tr-abzug *geruest-tr-abzug-e* *geruest-tr-abzug-e-w*)))) + (alert (ssg-text "gr-tr-alert-abzug")) + ) + (T + (setq rueckgabe + (list (atoi *geruest-tr-variante*) + (geruest-farbwert *geruest-tr-farbe* *geruest-tr-frei*) + aw ew + (= *geruest-tr-beschriftung* "1"))) + (setq fertig T) + ) + ) + ) + ) + ) + rueckgabe +) + +;; X-Positionen der beiden Querlinien einer Strebe: Anfang und Ende des +;; Flacheisens, laenge mittig um die Strebenposition x. +(defun geruest-traeger-strebe-enden (x laenge) + (list (- x (/ laenge 2.0)) (+ x (/ laenge 2.0))) +) + +;; Querstrebe darstellen: an Anfang und Ende je eine Linie quer zum Traeger +;; (Laenge querlinie, mittig auf der Traegerlinie y = 0). +(defun geruest-traeger-strebe-zeichnen (x laenge querlinie lay / hq) + (setq hq (/ querlinie 2.0)) + (foreach xe (geruest-traeger-strebe-enden x laenge) + (entmake (list '(0 . "LINE") (cons 8 lay) + (list 10 xe (- hq) 0.0) (list 11 xe hq 0.0))) + ) +) + +;; Vollstaendiger Pfad einer Dummy-DWG unter data/geruest oder nil (mit +;; Meldung), wenn sie fehlt. datei = nil -> stilles nil (nichts konfiguriert). +(defun geruest-dummy-finden (datei / pfad) + (if datei + (progn + (setq pfad (geruest-dummy-pfad datei)) + (if (and pfad (findfile pfad)) + pfad + (progn + (princ (ssg-textf "gr-fehler-dummy" (list (if pfad pfad datei)))) + nil + ) + ) + ) + ) +) + +;; Dummy-DWG als verschachtelten Block bei pt (Drehung 0) einfuegen. Ist die +;; Blockdefinition (Dateiname ohne .dwg) schon in der Zeichnung, wird per +;; Blockname eingefuegt - sonst wuerde die DWG bei jeder Strebe neu gelesen +;; (und ggf. "Block neu definieren?" gefragt). ATTREQ/ATTDIA muss der Aufrufer +;; auf 0 gesetzt haben. +(defun geruest-dummy-einfuegen (pfad pt / bname) + (setq bname (vl-filename-base pfad)) + (command "_.INSERT" (if (tblsearch "BLOCK" bname) bname pfad) pt 1 1 0) +) + +;; Linie + Querstreben + Muffe + ATTDEFs zu einem Block zusammenfassen und am +;; Startpunkt mit dem Richtungswinkel einfuegen. Im Block laeuft der Traeger +;; entlang +X, die Dummys werden darum ohne Drehung eingefuegt. +;; strebe = Ergebnis von geruest-traeger-strebe-cfg (nil = keine Streben) +;; strebe-pfad = Pfad der Strebe-DWG oder nil -> Rueckfall auf Querlinien +;; muffe-pfad = Pfad der Muffen-DWG oder nil -> keine Muffe; sonst eine +;; Muffe (Basispunkt Muffenmitte) am Linienende +;; Der Aufrufer sichert die Systemvariablen per ssg-start. +;; Rueckgabe: (blockname . entity) oder nil. +(defun geruest-traeger-block-bauen (bg layer netto strebe strebe-pfad muffe-pfad + werte pt winkel / lastEnt lay) + (setvar "ATTREQ" 0) + (setvar "ATTDIA" 0) + (setvar "OSMODE" 0) + ;; Drehwinkel wird als Grad an INSERT uebergeben - nur eindeutig mit + ;; Winkel-Nullrichtung Ost, gegen den Uhrzeigersinn, Einheit Grad. + (setvar "ANGBASE" 0.0) + (setvar "ANGDIR" 0) + (setvar "AUNITS" 0) + ;; Nicht (entlast) - siehe geruest-block-bauen. + (setq lastEnt (ssg-lastent-ohne-attribute)) + + (setq lay (if (and layer (/= layer "")) layer "0")) + ;; Aktiv setzen, damit auch die verschachtelten Dummy-INSERTs auf dem + ;; Traeger-Layer landen (CLAYER sichert ssg-start immer mit). + (ssg-make-layer lay (ssg-layer-farbe-fuer-name lay) T) + (entmake (list '(0 . "LINE") (cons 8 lay) + '(10 0.0 0.0 0.0) (list 11 netto 0.0 0.0))) + ;; strebe = (dummy laenge querlinie abstand-max rand-min rand-max) + (if strebe + (foreach x (geruest-traeger-streben netto (nth 3 strebe) (nth 4 strebe) (nth 5 strebe)) + (if strebe-pfad + (geruest-dummy-einfuegen strebe-pfad (list x 0.0 0.0)) + (geruest-traeger-strebe-zeichnen x (nth 1 strebe) (nth 2 strebe) lay) + ) + ) + ) + (if muffe-pfad + (geruest-dummy-einfuegen muffe-pfad (list netto 0.0 0.0)) + ) + + (geruest-block-abschliessen bg + (ssg-attrib-merge nil *geruest-traeger-attrib-defs*) + werte lastEnt pt (* winkel (/ 180.0 pi))) +) + +;; Einfuege-Schleife: Startpunkt, Endpunkt oder Laenge, bis ESC/Enter. +;; wahl = Rueckgabe von geruest-traeger-dialog. +(defun geruest-traeger-einfuegen (bg wahl / variante teileart layer paare farbe + abzug-a abzug-e min-l max-l bez werte-basis + werte weiter p1 eingabe strecke winkel netto + ergebnis anzahl strebe strebe-pfad muffe-pfad + beschriften) + (setq variante (nth (car wahl) (ssg-json-get bg "geometrie_varianten"))) + (setq farbe (cadr wahl) + abzug-a (caddr wahl) + abzug-e (cadddr wahl) + beschriften (nth 4 wahl)) + (setq *geruest-beschr-gemeldet* nil) + (setq teileart (geruest-variante-wert variante bg "teileart")) + (setq layer (geruest-variante-wert variante bg "layer")) + (setq min-l (geruest-attr-feld variante bg "LAENGE" "min")) + (setq max-l (geruest-attr-feld variante bg "LAENGE" "max")) + (setq paare (geruest-teile-paare variante nil)) + (setq strebe (geruest-traeger-strebe-cfg variante bg)) + ;; DWGs einmal je Durchgang aufloesen - eine fehlende Datei wird so nur + ;; einmal gemeldet, nicht bei jedem Traeger. Fehlt die Strebe-DWG, werden + ;; stattdessen Querlinien gezeichnet; fehlt die Muffe, entfaellt sie. + (setq strebe-pfad (if strebe (geruest-dummy-finden (car strebe)))) + (setq muffe-pfad (geruest-dummy-finden (geruest-traeger-muffe-dummy variante bg))) + (setq bez (geruest-traeger-bezeichnung teileart + (geruest-eintrag-name variante) abzug-a abzug-e)) + + ;; Fuer alle Traeger dieses Durchgangs gleich - nur LAENGE aendert sich. + (setq werte-basis + (list (cons "ARTINR" (ssg-join (mapcar 'car paare) ",")) + (cons "BEZEICHNUNG" bez) + (cons "TEILEART" teileart) + (cons "LAENGENABZUGANFANG" (geruest-mm-string abzug-a)) + (cons "LAENGENABZUGENDE" (geruest-mm-string abzug-e)) + ;; Block selbstbeschreibend - die Beschriftung (auch spaeter per + ;; GERUEST_TRAEGER_BESCHRIFTEN) findet darueber ihre Formatierung + (cons "VARIANTE" (geruest-eintrag-name variante)))) + (if (and layer (/= layer "")) + (setq werte-basis (append werte-basis (list (cons "LAYER" layer))))) + (if (/= farbe "") + (setq werte-basis (append werte-basis (list (cons "TEILEFARBE" + (ssg-join (geruest-farbliste paare farbe) ",")))))) + + (setq weiter T anzahl 0) + (while weiter + (setq strecke nil) + (setq p1 (getpoint (ssg-text "gr-tr-prompt-start"))) + (if (null p1) + (setq weiter nil) + (progn + ;; 128 = beliebige Eingabe: ein Punkt liefert Laenge UND Richtung, + ;; eine getippte Zahl nur die Laenge (Richtung wird nachgefragt). + (initget 128) + (setq eingabe (getpoint p1 (ssg-text "gr-tr-prompt-ende"))) + ;; Reihenfolge wichtig: nil ist auch listp, und "" darf erst nach dem + ;; Punkt-Fall verglichen werden. + (cond + ((null eingabe) (setq weiter nil)) + ((listp eingabe) + ;; Laenge in der XY-Ebene, die Linie liegt auf Z des Startpunkts + (setq strecke (distance (list (car p1) (cadr p1)) + (list (car eingabe) (cadr eingabe)))) + (setq winkel (angle p1 eingabe)) + ) + ((= eingabe "") (setq weiter nil)) + ((setq strecke (geruest-mm-zahl eingabe)) + (setq winkel (getangle p1 (ssg-text "gr-tr-prompt-richtung"))) + (if (null winkel) (setq winkel 0.0)) + ) + (T (princ (ssg-textf "gr-tr-eingabe-ungueltig" (list eingabe)))) + ) + ) + ) + + (if (and weiter strecke) + (progn + (setq netto (geruest-traeger-laenge strecke abzug-a abzug-e)) + (cond + ((null netto) + (princ (ssg-textf "gr-tr-laenge-null" + (list (geruest-mm-string strecke) + (geruest-mm-string (+ abzug-a abzug-e)))))) + ((not (geruest-traeger-pruefen netto min-l max-l)) + (princ (ssg-textf "gr-tr-laenge-bereich" + (list (geruest-mm-string netto) + (if min-l (geruest-mm-string min-l) "0") + (if max-l (geruest-mm-string max-l) "-"))))) + (T + (setq werte (cons (cons "LAENGE" (geruest-mm-string netto)) werte-basis)) + (ssg-start nil '(("OSMODE") ("ATTREQ") ("ATTDIA") + ("ANGBASE") ("ANGDIR") ("AUNITS"))) + (setq ergebnis (geruest-traeger-block-bauen bg layer netto strebe + strebe-pfad muffe-pfad werte + (geruest-traeger-start p1 winkel abzug-a) winkel)) + ;; Beschriftung aus dem fertigen Block ableiten (dieselbe Funktion + ;; wie GERUEST_TRAEGER_BESCHRIFTEN) + (if (and ergebnis beschriften) + (geruest-traeger-beschriften (cdr ergebnis))) + (ssg-end) + (if ergebnis + (progn + (setq anzahl (1+ anzahl)) + (princ (ssg-textf "gr-tr-fertig" + (list (geruest-mm-string netto) (car ergebnis)))) + ) + ) + ) + ) + ) + ) + ) + (princ (ssg-textf "gr-tr-ende" (list (itoa anzahl)))) + (princ) +) + +;; ============================================================ +;; TRAEGER-BESCHRIFTUNG +;; ============================================================ +;; Eigenes TEXT-Objekt neben dem Block, z.B. "RT 80/80 L=2650". Es wird +;; AUSSCHLIESSLICH aus dem fertigen Block abgeleitet (Einfuegepunkt, Drehung, +;; Attribute LAENGE und VARIANTE) - darum nutzen das Einfuegen und +;; GERUEST_TRAEGER_BESCHRIFTEN dieselbe Funktion, und ein von Hand +;; verschobener/gedrehter Traeger wird beim Neubeschriften korrekt erfasst. +;; Der Text traegt per XDATA (*geruest-beschr-app*, 1005) den Handle seines +;; Blocks; so wird er beim Neubeschriften ersetzt statt verdoppelt. +;; +;; Formatierung aus geruest.json, "beschriftung" (Variante vor Baugruppe, je +;; Feld): NUR stil, ausrichtung, hoehe - alles andere regelt der Textstil +;; ausserhalb (es wird kein Stil angelegt). Dazu Inhalt (format/text) und +;; Layer. + +;; Winkel auf [0, 2pi) normieren. +(defun geruest-winkel-norm (w) + (setq w (rem w (* 2.0 pi))) + (if (< w 0.0) (+ w (* 2.0 pi)) w) +) + +;; Textwinkel entlang des Traegers nach DIN-Konvention (lesbar von unten und +;; von rechts): fuer 90 < w <= 270 Grad um 180 Grad gedreht, sonst w. +(defun geruest-text-winkel (w / e) + (setq w (geruest-winkel-norm w) e 1e-9) + (if (and (> w (+ (/ pi 2.0) e)) (<= w (+ (* 1.5 pi) e))) + (geruest-winkel-norm (+ w pi)) + w + ) +) + +;; Ausrichtungs-Code wie bei TEXT "Ausrichten" (T/M/B + L/C/R, z.B. "BC") -> +;; (dxf72 dxf73), nil bei unbekanntem Code. +(defun geruest-ausrichtung-codes (code / h v) + (if (and code (= (type code) 'STR) (= (strlen code) 2)) + (progn + (setq code (strcase code)) + (setq v (cdr (assoc (substr code 1 1) '(("T" . 3) ("M" . 2) ("B" . 1))))) + (setq h (cdr (assoc (substr code 2 1) '(("L" . 0) ("C" . 1) ("R" . 2))))) + (if (and v h) (list h v)) + ) + ) +) + +;; Lage des Textes: (punkt textwinkel dxf72 dxf73). p0 = Traeger-Startpunkt, +;; w = Traegerwinkel, laenge = Traegerlaenge. Der Bezugspunkt liegt auf der +;; Linie: L = in Leserichtung linkes Ende, C = Mitte, R = rechtes Ende. Nach +;; einer 180-Grad-Drehung ist "links" also das Linienende. Unbekannter Code +;; -> BC (die Meldung gibt der Aufrufer aus). +(defun geruest-traeger-text-lage (p0 w laenge code / codes tw p1 links rechts pt) + (setq codes (geruest-ausrichtung-codes code)) + (if (null codes) (setq codes '(1 1))) + (setq tw (geruest-text-winkel w)) + (setq p1 (polar p0 w laenge)) + ;; Projektion auf die Leserichtung entscheidet, welches Ende links liegt + (if (<= (+ (* (car p0) (cos tw)) (* (cadr p0) (sin tw))) + (+ (* (car p1) (cos tw)) (* (cadr p1) (sin tw)))) + (setq links p0 rechts p1) + (setq links p1 rechts p0) + ) + (setq pt (cond ((= (car codes) 0) links) + ((= (car codes) 2) rechts) + (T (polar p0 w (/ laenge 2.0))))) + (list pt tw (car codes) (cadr codes)) +) + +;; Alle Vorkommen von alt in s durch neu ersetzen. +(defun geruest-string-ersetzen (s alt neu / pos) + (setq pos (vl-string-search alt s)) + (while pos + (setq s (strcat (substr s 1 pos) neu (substr s (+ pos (strlen alt) 1)))) + (setq pos (vl-string-search alt s (+ pos (strlen neu)))) + ) + s +) + +;; Beschriftungstext: %1 = Text der Variante, %2 = Laenge auf ganze mm. +(defun geruest-traeger-text-string (fmt txt laenge) + (geruest-string-ersetzen + (geruest-string-ersetzen fmt "%1" txt) + "%2" (itoa (fix (+ laenge 0.5)))) +) + +;; Beschriftungs-Konfiguration als Alist (format text stil ausrichtung hoehe +;; layer), je Feld Variante vor Baugruppe vor Code-Standard. Fehlt "text", +;; gilt der Variantenname bzw. ersatz-text (Block ohne bekannte Variante). +;; stil/layer = nil -> aktueller Textstil/Layer. +(defun geruest-traeger-beschr-cfg (variante bg ersatz-text / txt h) + (setq txt (geruest-beschr-feld variante bg "text")) + (if (not (and txt (= (type txt) 'STR) (/= txt ""))) + (setq txt (if variante (geruest-eintrag-name variante) ersatz-text))) + (setq h (geruest-beschr-feld variante bg "hoehe")) + (list (cons "format" (cond ((geruest-beschr-feld variante bg "format")) ("%1 L=%2"))) + (cons "text" (if txt txt "")) + (cons "stil" (geruest-beschr-feld variante bg "stil")) + (cons "ausrichtung" (cond ((geruest-beschr-feld variante bg "ausrichtung")) ("BC"))) + (cons "hoehe" (if (numberp h) (float h) 50.0)) + (cons "layer" (geruest-beschr-feld variante bg "layer"))) +) + +(defun geruest-beschr-feld (variante bg feld) + (geruest-obj-feld variante bg '("beschriftung") feld) +) + +;; Handle des Traeger-Blocks aus der XDATA einer Beschriftung (oder nil). +(defun geruest-beschr-handle (ent / xd) + (setq xd (cadr (assoc -3 (entget ent (list *geruest-beschr-app*))))) + (if xd (cdr (assoc 1005 (cdr xd)))) +) + +;; Alle Beschriftungen der Zeichnung (Auswahlsatz oder nil). +(defun geruest-beschr-alle ( / ) + (ssget "_X" (list '(0 . "TEXT") (list -3 (list *geruest-beschr-app*)))) +) + +;; Beschriftung(en) zu einem Block-Handle loeschen. +(defun geruest-beschr-entfernen (handle / ss i e) + (if (and handle (setq ss (geruest-beschr-alle))) + (progn + (setq i 0) + (repeat (sslength ss) + (setq e (ssname ss i)) + (if (equal (geruest-beschr-handle e) handle) (entdel e)) + (setq i (1+ i)) + ) + ) + ) +) + +;; Textstil pruefen: vorhanden -> Name, sonst einmal je Lauf melden und den +;; aktuellen TEXTSTYLE nehmen. *geruest-beschr-gemeldet* setzt der Aufrufer +;; je Lauf zurueck. +(defun geruest-beschr-stil (stil / ) + (cond + ((and stil (/= stil "") (tblsearch "STYLE" stil)) stil) + (T + (if (and stil (/= stil "") (not (member stil *geruest-beschr-gemeldet*))) + (progn + (princ (ssg-textf "gr-tr-beschr-stil-fehlt" (list stil (getvar "TEXTSTYLE")))) + (setq *geruest-beschr-gemeldet* (cons stil *geruest-beschr-gemeldet*)) + ) + ) + (getvar "TEXTSTYLE") + ) + ) +) + +;; Einen Traeger-Block beschriften (eine evtl. vorhandene Beschriftung wird +;; ersetzt). Rueckgabe: "ok", "ersatz" (Block ohne bekannte VARIANTE, mit den +;; Vorgaben der Baugruppe beschriftet) oder nil (nicht beschriftbar). +(defun geruest-traeger-beschriften (blockEnt / ed attribs bg vname idx variante + cfg laenge lage lay handle code erg) + (setq ed (entget blockEnt)) + (setq attribs (ssg-attrib-read blockEnt)) + (setq bg (geruest-baugruppe "Traeger" (geruest-traeger-daten))) + (setq laenge (atof (cond ((cdr (assoc "LAENGE" attribs))) ("0")))) + (if (and bg (> laenge 0.0)) + (progn + (setq vname (cdr (assoc "VARIANTE" attribs))) + (setq idx (if (and vname (/= vname "")) + (geruest-name-index vname (ssg-json-get bg "geometrie_varianten")))) + (setq variante (if idx (nth idx (ssg-json-get bg "geometrie_varianten")))) + (setq cfg (geruest-traeger-beschr-cfg variante bg + (cond ((cdr (assoc "TEILEART" attribs))) ("")))) + (setq code (cdr (assoc "ausrichtung" cfg))) + (if (and (null (geruest-ausrichtung-codes code)) + (not (member code *geruest-beschr-gemeldet*))) + (progn + (princ (ssg-textf "gr-tr-beschr-ausrichtung" (list code))) + (setq *geruest-beschr-gemeldet* (cons code *geruest-beschr-gemeldet*)) + ) + ) + (setq lage (geruest-traeger-text-lage (cdr (assoc 10 ed)) (cdr (assoc 50 ed)) + laenge code)) + (setq lay (cdr (assoc "layer" cfg))) + (if (and lay (/= lay "")) + (ssg-make-layer lay (ssg-layer-farbe-fuer-name lay) nil) + (setq lay (getvar "CLAYER")) + ) + (setq handle (cdr (assoc 5 ed))) + (geruest-beschr-entfernen handle) + (if (not (tblsearch "APPID" *geruest-beschr-app*)) (regapp *geruest-beschr-app*)) + ;; 73 ist immer >= 1, damit gilt Gruppe 11 als Ausrichtungspunkt + (if (entmake (list '(0 . "TEXT") + (cons 8 lay) + (cons 7 (geruest-beschr-stil (cdr (assoc "stil" cfg)))) + (cons 10 (nth 0 lage)) + (cons 11 (nth 0 lage)) + (cons 40 (cdr (assoc "hoehe" cfg))) + (cons 1 (geruest-traeger-text-string + (cdr (assoc "format" cfg)) + (cdr (assoc "text" cfg)) laenge)) + (cons 50 (nth 1 lage)) + (cons 72 (nth 2 lage)) + (cons 73 (nth 3 lage)) + (list -3 (list *geruest-beschr-app* (cons 1005 handle))))) + (setq erg (if variante "ok" "ersatz")) + ) + ) + ) + erg +) + +;; Alle Traeger der Zeichnung (neu) beschriften und Beschriftungen, deren +;; Block nicht mehr existiert, entfernen - unabhaengig von der Checkbox im +;; Einfuegedialog. +(defun c:GERUEST_TRAEGER_BESCHRIFTEN ( / ss i n-ok n-ersatz n-fehl n-weg e h ziel erg) + (setq *geruest-beschr-gemeldet* nil) + (setq n-ok 0 n-ersatz 0 n-fehl 0 n-weg 0) + (ssg-start nil nil) + ;; Verwaiste Beschriftungen zuerst entfernen + (if (setq ss (geruest-beschr-alle)) + (progn + (setq i 0) + (repeat (sslength ss) + (setq e (ssname ss i)) + (setq h (geruest-beschr-handle e)) + (setq ziel (if h (handent h))) + (if (not (and ziel (entget ziel) + (= (cdr (assoc 0 (entget ziel))) "INSERT"))) + (progn (entdel e) (setq n-weg (1+ n-weg))) + ) + (setq i (1+ i)) + ) + ) + ) + (if (setq ss (ssget "_X" '((0 . "INSERT") (2 . "GR_Traeger_*")))) + (progn + (setq i 0) + (repeat (sslength ss) + (setq erg (geruest-traeger-beschriften (ssname ss i))) + (cond ((= erg "ok") (setq n-ok (1+ n-ok))) + ((= erg "ersatz") (setq n-ersatz (1+ n-ersatz))) + (T (setq n-fehl (1+ n-fehl)))) + (setq i (1+ i)) + ) + (princ (ssg-textf "gr-tr-beschr-ergebnis" + (list (itoa (+ n-ok n-ersatz)) (itoa n-ersatz) (itoa n-fehl) (itoa n-weg)))) + ) + (princ (ssg-text "gr-tr-beschr-keine")) + ) + (ssg-end) + (princ) +) + +(defun c:GERUEST_TRAEGER ( / bg wahl) + (setq bg (geruest-baugruppe "Traeger" (geruest-traeger-daten))) + (cond + ((null bg) + (princ (ssg-textf "gr-fehler-baugruppe" (list "Traeger")))) + ((null (ssg-json-get bg "geometrie_varianten")) + (princ (ssg-textf "gr-fehler-keine-varianten" (list "Traeger")))) + ((null (setq wahl (geruest-traeger-dialog bg))) + (princ (ssg-text "gr-abgebrochen"))) + (T (geruest-traeger-einfuegen bg wahl)) + ) + (princ) +) + +(princ "\nGeruestInsert.lsp geladen. Befehle: GERUEST, GERUEST_SAEULEN, GERUEST_ROHRABHAENGER, GERUEST_WANDBEFESTIGUNG, GERUEST_TRAEGER, GERUEST_TRAEGER_BESCHRIFTEN") (princ) diff --git a/data/geruest/R2-Saeule_dummy.dwg b/data/geruest/821004200.dwg similarity index 53% rename from data/geruest/R2-Saeule_dummy.dwg rename to data/geruest/821004200.dwg index 7c50828..148b0a3 100644 Binary files a/data/geruest/R2-Saeule_dummy.dwg and b/data/geruest/821004200.dwg differ diff --git a/data/geruest/821006031.dwg b/data/geruest/821006031.dwg new file mode 100644 index 0000000..3c49e4b Binary files /dev/null and b/data/geruest/821006031.dwg differ diff --git a/data/geruest/821006036.dwg b/data/geruest/821006036.dwg new file mode 100644 index 0000000..e98cc36 Binary files /dev/null and b/data/geruest/821006036.dwg differ diff --git a/data/geruest/821006042.dwg b/data/geruest/821006042.dwg new file mode 100644 index 0000000..de1712d Binary files /dev/null and b/data/geruest/821006042.dwg differ diff --git a/data/geruest/821006043.dwg b/data/geruest/821006043.dwg new file mode 100644 index 0000000..ad9fd97 Binary files /dev/null and b/data/geruest/821006043.dwg differ diff --git a/data/geruest/821006051.dwg b/data/geruest/821006051.dwg new file mode 100644 index 0000000..cbf0558 Binary files /dev/null and b/data/geruest/821006051.dwg differ diff --git a/data/geruest/825006009.dwg b/data/geruest/825006009.dwg new file mode 100644 index 0000000..640793e Binary files /dev/null and b/data/geruest/825006009.dwg differ diff --git a/data/geruest/dummy_Muffe_1zoll.dwg b/data/geruest/dummy_Muffe_1zoll.dwg new file mode 100644 index 0000000..0e6da75 Binary files /dev/null and b/data/geruest/dummy_Muffe_1zoll.dwg differ diff --git a/data/geruest/dummy_Strebe_30x4.dwg b/data/geruest/dummy_Strebe_30x4.dwg new file mode 100644 index 0000000..b2193b0 Binary files /dev/null and b/data/geruest/dummy_Strebe_30x4.dwg differ diff --git a/data/json/geruest.json b/data/json/geruest.json index 0868ae2..cbbb946 100644 --- a/data/json/geruest.json +++ b/data/json/geruest.json @@ -5,13 +5,13 @@ "teileart": "Geruest Rohrabhaenger", "dummy": "Rohrabhaenger_dummy.dwg", "attribute": { - "Laenge": { + "LAENGE": { "typ": "number", "min": 0, "max": 6000, "einheit": "mm" }, - "Teilefarbe": { + "TEILEFARBE": { "typ": "auswahl", "optionen": [ "RAL7035", @@ -29,13 +29,13 @@ "artikel": "ROHR R 1\" 33,7 x 2,6", "sivas_id": 821006026, "farbig": true, - "menge": 1 + "stueckzahl": 1 }, { "artikel": "CAD - ROHRABH. 1\" NICHT GEQUETSCHT - MONTAGEELEMENTE KPL.", "sivas_id": 821004202, "farbig": false, - "menge": 1 + "stueckzahl": 1 } ] }, @@ -46,13 +46,13 @@ "artikel": "ROHR R 1\" 33,7 x 2,6 einseitig gequetscht", "sivas_id": 821006034, "farbig": true, - "menge": 1 + "stueckzahl": 1 }, { "artikel": "CAD - ROHRABHÄNGER 1\" EINSEITIG GEQUETSCHT - MONTAGEELEMENTE KPL.", "sivas_id": 821004203, "farbig": false, - "menge": 1 + "stueckzahl": 1 } ] }, @@ -63,13 +63,25 @@ "artikel": "ROHR R 1\" 33,7 x 2,6 beidseitig gequetscht", "sivas_id": 821006035, "farbig": true, - "menge": 1 + "stueckzahl": 1 }, { "artikel": "CAD - ROHRABHÄNGER 1\" BEIDSEITIG GEQUETSCHT - MONTAGEELEMENTE KPL.", "sivas_id": 821004204, "farbig": false, - "menge": 1 + "stueckzahl": 1 + } + ] + }, + { + "name": "T-Abhaenger mit 300 mm Flanschrohr R 1\" 33,7 x 2,6", + "dummy": "821006036.dwg", + "teile": [ + { + "artikel": "T-ABHÄNGER MIT FLANSCH-ROHR 33,7 x 2,6", + "sivas_id": 821006036, + "farbig": true, + "stueckzahl": 1 } ] } @@ -79,34 +91,37 @@ "artikel": "SPANNPRATZE ST - KH 8-11 KPL.SELBSTSICHERND", "sivas_id": 821004120, "farbig": false, - "menge": 1 + "stueckzahl": 1, + "anzahl_vorgabe": 1 }, { "artikel": "SPANNPRATZE SELBSTSICHERND KPL.", "sivas_id": 821004058, "farbig": false, - "menge": 1 + "stueckzahl": 1, + "anzahl_vorgabe": 1 }, { "name": "Schraubenset", + "anzahl_vorgabe": 1, "teile": [ { "artikel": "Scheibe DIN 125 - A 10,5 - 140 HV - St", "sivas_id": 913011010, "farbig": false, - "menge": 1 + "stueckzahl": 1 }, { "artikel": "Sechskantmutter DIN 934 - M10 - 8", "sivas_id": 912011010, "farbig": false, - "menge": 2 + "stueckzahl": 2 }, { "artikel": "Sechskantschraube DIN 933 - M10 x 30 - 8.8", "sivas_id": 911011008, "farbig": false, - "menge": 1 + "stueckzahl": 1 } ] }, @@ -114,43 +129,50 @@ "artikel": "U-BÜGEL M7 41X49 FÜR PROFILGERÜST - BG", "sivas_id": 825004049, "farbig": false, - "menge": 1 + "stueckzahl": 1, + "anzahl_vorgabe": 1 }, { "artikel": "ROHRKLAMMER 1\" KPL. VERTIKAL", "sivas_id": 825004040, "farbig": false, - "menge": 2 + "stueckzahl": 1, + "anzahl_vorgabe": 2 }, { "artikel": "WINKEL 60X60X6X300", "sivas_id": 821006043, "farbig": false, - "menge": 1 + "stueckzahl": 1, + "anzahl_vorgabe": 1 }, { "artikel": "WINKEL 60X60X6X60", "sivas_id": 821006042, "farbig": false, - "menge": 1 + "stueckzahl": 1, + "anzahl_vorgabe": 1 }, { "artikel": "ROHRKLAMMER 1\" T-VERBINDUNG KPL.", "sivas_id": 825004030, "farbig": false, - "menge": 1 + "stueckzahl": 1, + "anzahl_vorgabe": 1 }, { "artikel": "ROHRKLAMMER 1\" FLACH UND LANG, KPL.", "sivas_id": 825002021, "farbig": false, - "menge": 1 + "stueckzahl": 1, + "anzahl_vorgabe": 1 }, { "artikel": "DURCHSTECKANKER HST M12X115X20", "sivas_id": 922021003, "farbig": false, - "menge": 1 + "stueckzahl": 1, + "anzahl_vorgabe": 1 } ], "konfigurationen": [ @@ -162,17 +184,17 @@ "oben": [ { "artikel": "SPANNPRATZE SELBSTSICHERND KPL.", - "menge": 1 + "anzahl": 1 }, { "artikel": "Schraubenset", - "menge": 1 + "anzahl": 1 } ], "unten": [ { "artikel": "ROHRKLAMMER 1\" KPL. VERTIKAL", - "menge": 2 + "anzahl": 2 } ] }, @@ -184,13 +206,13 @@ "oben": [ { "artikel": "DURCHSTECKANKER HST M12X115X20", - "menge": 2 + "anzahl": 2 } ], "unten": [ { "artikel": "ROHRKLAMMER 1\" T-VERBINDUNG KPL.", - "menge": 1 + "anzahl": 1 } ] } @@ -200,15 +222,15 @@ "id": "Saeulen", "titel": "Saeulen", "teileart": "Geruest Saeule", - "dummy": "R2-Saeule_dummy.dwg", + "dummy": "821006051.dwg", "attribute": { - "Laenge": { + "LAENGE": { "typ": "number", "min": 0, "max": 6000, "einheit": "mm" }, - "Teilefarbe": { + "TEILEFARBE": { "typ": "auswahl", "optionen": [ "RAL7035", @@ -231,7 +253,7 @@ ] }, "geometrie_varianten": [ - { + { "name": "R 2\"", "teileart": "Geruest Saeule 2 Zoll", "teile": [ @@ -239,31 +261,32 @@ "artikel": "BODENSÄULE R 2\" 60,3 MIT FUSSPLATTE", "sivas_id": 821006051, "farbig": true, - "menge": 1 + "stueckzahl": 1 }, { "artikel": "CAD - BODENSÄULE R 2\" - DURCHSTECKANKER KPL.", "sivas_id": 821004201, "farbig": false, - "menge": 1 + "stueckzahl": 1 } ] }, { "name": "R 1\"", "teileart": "Geruest Saeule 1 Zoll", + "dummy": "821004200.dwg", "teile": [ { "artikel": "ROHR R 1\" 33,7 x 2,6", "sivas_id": 821006026, "farbig": true, - "menge": 1 + "stueckzahl": 1 }, { "artikel": "CAD - BODENSÄULE R 1\" - BODENPLATTE KPL.", "sivas_id": 821004200, "farbig": false, - "menge": 1 + "stueckzahl": 1 } ] } @@ -273,67 +296,78 @@ "artikel": "KLEMMRING MIT MUFFE EINFACH KPL.", "sivas_id": 825002019, "farbig": false, - "menge": 2 + "stueckzahl": 1, + "anzahl_vorgabe": 2 }, { "artikel": "KLEMMRING MIT MUFFE DOPPELT 90 Grad, KPL.", "sivas_id": 825002009, "farbig": false, - "menge": 2 + "stueckzahl": 1, + "anzahl_vorgabe": 2 }, { "artikel": "KLEMMRING MIT MUFFE DOPPELT 180 Grad, KPL.", "sivas_id": 825002008, "farbig": false, - "menge": 2 + "stueckzahl": 1, + "anzahl_vorgabe": 2 }, { "artikel": "KLEMMRING M. MUFFE DREIFACH 90 Grad/180 Grad KPL.", "sivas_id": 825002007, "farbig": false, - "menge": 2 + "stueckzahl": 1, + "anzahl_vorgabe": 2 }, { "artikel": "KLEMMRING MIT LASCHE KPL. ZUSAMMENBAUZEICHNUNG", "sivas_id": 825002020, "farbig": false, - "menge": 2 + "stueckzahl": 1, + "anzahl_vorgabe": 2 }, { "artikel": "QUERSCHELLE F. 1\" UND 2\" ROHR GESCHW./VZ. M. 4X M10X30 KPL.", "sivas_id": 825002022, "farbig": false, - "menge": 2 + "stueckzahl": 1, + "anzahl_vorgabe": 2 }, { "artikel": "KREUZSCHELLE F. 2\" UND 1\" ROHR SCHWENKBAR 360 Grad", "sivas_id": 922083009, "farbig": false, - "menge": 2 + "stueckzahl": 1, + "anzahl_vorgabe": 2 }, { "artikel": "HALTEARM FÜR BODENSÄULE R 2\" 1-ARMIG KPL.", "sivas_id": 821004051, "farbig": true, - "menge": 1 + "stueckzahl": 1, + "anzahl_vorgabe": 1 }, { "artikel": "HALTEARM FÜR BODENSÄULE R 2\" 2-ARMIG 90 Grad KPL.", "sivas_id": 821004054, "farbig": true, - "menge": 1 + "stueckzahl": 1, + "anzahl_vorgabe": 1 }, { "artikel": "HALTEARM FÜR BODENSÄULE R 2\" 2-ARMIG 180 Grad KPL.", "sivas_id": 821004052, "farbig": true, - "menge": 1 + "stueckzahl": 1, + "anzahl_vorgabe": 1 }, { "artikel": "HALTEARM FÜR BODENSÄULE R 2\" 3-ARMIG KPL.", "sivas_id": 821004053, "farbig": true, - "menge": 1 + "stueckzahl": 1, + "anzahl_vorgabe": 1 } ], "konfigurationen": [ @@ -353,7 +387,7 @@ "oben": [ { "artikel": "KLEMMRING MIT MUFFE EINFACH KPL.", - "menge": 2 + "anzahl": 2 } ], "unten": [] @@ -366,13 +400,13 @@ "oben": [ { "artikel": "KLEMMRING MIT MUFFE DOPPELT 90 Grad, KPL.", - "menge": 2 + "anzahl": 2 } ], "unten": [ { "artikel": "QUERSCHELLE F. 1\" UND 2\" ROHR GESCHW./VZ. M. 4X M10X30 KPL.", - "menge": 2 + "anzahl": 2 } ] } @@ -386,45 +420,49 @@ "geometrie_varianten": [ { "name": "WANDFLANSCH EINZELTEIL", + "dummy": "821006031.dwg", "teile": [ { "artikel": "WANDFLANSCH EINZELTEIL", "sivas_id": 821006031, "farbig": false, - "menge": 1 + "stueckzahl": 1 } ] }, { "name": "WINKEL 60X60X6X60", + "dummy": "821006042.dwg", "teile": [ { "artikel": "WINKEL 60X60X6X60", "sivas_id": 821006042, "farbig": false, - "menge": 1 + "stueckzahl": 1 } ] }, { "name": "WINKEL 60X60X6X300", + "dummy": "821006043.dwg", "teile": [ { "artikel": "WINKEL 60X60X6X300", "sivas_id": 821006043, "farbig": false, - "menge": 1 + "stueckzahl": 1 } ] }, { "name": "ROHRSCHELLE 1\" OBERTEIL", + "dummy": "825006009.dwg", "teile": [ { "artikel": "ROHRSCHELLE 1\" OBERTEIL", "sivas_id": 825006009, "farbig": false, - "menge": 1 + "stueckzahl": 1 } ] } @@ -432,29 +470,31 @@ "anbaugruppen": [ { "name": "Nylondübel 12x60 inkl. Holzschrauben", + "anzahl_vorgabe": 2, "teile": [ { "artikel": "HOLZSCHRAUB,10X70, 6KT DIN 571 VZ", "sivas_id": 911271027, "farbig": false, - "menge": 2 + "stueckzahl": 1 }, { "artikel": "NYLON - DUEBEL 12X60 (TYP S)", "sivas_id": 922013003, "farbig": false, - "menge": 2 + "stueckzahl": 1 } ] }, { "name": "HST M12X115X20", + "anzahl_vorgabe": 2, "teile": [ { "artikel": "DURCHSTECKANKER HST M12X115X20 WANDABH.", "sivas_id": 922021003, "farbig": false, - "menge": 2 + "stueckzahl": 1 } ] } @@ -466,7 +506,7 @@ "oben": [ { "artikel": "Nylondübel 12x60 inkl. Holzschrauben", - "menge": 1 + "anzahl": 2 } ], "unten": [] @@ -477,7 +517,7 @@ "oben": [ { "artikel": "HST M12X115X20", - "menge": 1 + "anzahl": 2 } ], "unten": [] diff --git a/data/json/geruest_traeger.json b/data/json/geruest_traeger.json new file mode 100644 index 0000000..42c5385 --- /dev/null +++ b/data/json/geruest_traeger.json @@ -0,0 +1,193 @@ +{ + "Rohr-Gerüst-Träger": { + "id": "Traeger", + "titel": "Traeger", + "teileart": "Geruest Traeger", + "querstrebe": { + "dummy": "dummy_Strebe_30x4.dwg", + "abstand_max": 500, + "rand_min": 100, + "rand_max": 250 + }, + "muffe": { + "dummy": "dummy_Muffe_1zoll.dwg" + }, + "beschriftung": { + "format": "%1 L=%2", + "stil": "Lan", + "ausrichtung": "BL", + "hoehe": 100 + }, + "attribute": { + "LAENGE": { + "typ": "number", + "min": 0, + "max": 6000, + "einheit": "mm" + }, + "LAENGENABZUGANFANG": { + "typ": "number", + "einheit": "mm", + "vorgabe": 35, + "aktiv": false + }, + "LAENGENABZUGENDE": { + "typ": "number", + "einheit": "mm", + "vorgabe": 35, + "aktiv": false + }, + "TEILEFARBE": { + "typ": "auswahl", + "optionen": [ + "RAL7035", + "RAL7030" + ], + "frei_erlaubt": true, + "frei_option": "Individuell" + } + }, + "geometrie_varianten": [ + { + "name": "Rohr 1\"", + "beschriftung": { + "text": "Rohr 1\"", + "layer": "D-6-BEM" + }, + "teileart": "Geruest Traeger", + "layer": "D-6", + "teile": [ + { + "artikel": "ROHR R 1\"", + "sivas_id": 821006026, + "farbig": true, + "stueckzahl": 1 + } + ] + }, + { + "name": "Rohr 1\" mit Muffe", + "beschriftung": { + "text": "Rohr 1\"", + "layer": "D-6-BEM" + }, + "mit_muffe": true, + "teileart": "Geruest Traeger", + "layer": "D-6", + "teile": [ + { + "artikel": "ROHR R 1\" mit Muffe", + "sivas_id": 825004000, + "farbig": true, + "stueckzahl": 1 + } + ] + }, + { + "name": "DRT 80 1\"", + "beschriftung": { + "text": "DRT 80", + "layer": "D-1-BEM" + }, + "teileart": "Geruest Traeger", + "layer": "D-1", + "teile": [ + { + "artikel": "DRT DOPPELROHRTRAEGER 80 1\"", + "sivas_id": 825084000, + "farbig": true, + "stueckzahl": 1 + } + ] + }, + { + "name": "DRT 80 1\" mit 2 Muffen", + "beschriftung": { + "text": "DRT 80", + "layer": "D-1-BEM" + }, + "mit_muffe": true, + "teileart": "Geruest Traeger", + "layer": "D-1", + "teile": [ + { + "artikel": "DRT DOPPELROHRTRAEGER 80 1\" mit 2 Muffen", + "sivas_id": 825084007, + "farbig": true, + "stueckzahl": 1 + } + ] + }, + { + "name": "DRT 114 1\"", + "beschriftung": { + "text": "DRT 114", + "layer": "D-3-BEM" + }, + "teileart": "Geruest Traeger", + "layer": "D-3", + "teile": [ + { + "artikel": "DRT DOPPELROHRTRAEGER 114 1\"", + "sivas_id": 825104000, + "farbig": true, + "stueckzahl": 1 + } + ] + }, + { + "name": "DRT 114 1\" mit 2 Muffen", + "beschriftung": { + "text": "DRT 114", + "layer": "D-3-BEM" + }, + "mit_muffe": true, + "teileart": "Geruest Traeger", + "layer": "D-3", + "teile": [ + { + "artikel": "DRT DOPPELROHRTRAEGER 114 1\" mit 2 Muffen", + "sivas_id": 825104007, + "farbig": true, + "stueckzahl": 1 + } + ] + }, + { + "name": "RT 80/80 1\"", + "beschriftung": { + "text": "RT 80/80", + "layer": "D-5-BEM" + }, + "teileart": "Geruest Traeger", + "layer": "D-5", + "teile": [ + { + "artikel": "RT ROHRTRAEGER 80/80 1\"", + "sivas_id": 825144000, + "farbig": true, + "stueckzahl": 1 + } + ] + }, + { + "name": "RT 80/80 1\" mit 3 Muffen", + "beschriftung": { + "text": "RT 80/80", + "layer": "D-5-BEM" + }, + "mit_muffe": true, + "teileart": "Geruest Traeger", + "layer": "D-5", + "teile": [ + { + "artikel": "RT ROHRTRAEGER 80/80 1\" mit 3 Muffen", + "sivas_id": 825144007, + "farbig": true, + "stueckzahl": 1 + } + ] + } + ] + } +} diff --git a/dcl/geruest_traeger.dcl b/dcl/geruest_traeger.dcl new file mode 100644 index 0000000..c33cfe1 --- /dev/null +++ b/dcl/geruest_traeger.dcl @@ -0,0 +1,39 @@ +// ============================================================ +// geruest_traeger.dcl - Rohr-Geruest-Traeger konfigurieren +// Verwendet von: GERUEST_TRAEGER (GeruestInsert.lsp, geruest-traeger-dialog) +// +// Die Laenge steht bewusst NICHT im Dialog - sie wird nach dem Dialog in der +// Zeichnung per Start-/Endpunkt (oder getippter Laenge) bestimmt. Aktive +// Laengenabzuege ruecken die Linie am Anfang bzw. Ende ein. +// ============================================================ + +geruest_traeger : dialog { + label = "Rohr-Geruest-Traeger konfigurieren"; + + : boxed_column { + label = "Basis"; + : popup_list { key = "variante"; label = "Variante:"; width = 56; } + : row { + : popup_list { key = "farbe"; label = "Farbe:"; width = 25; } + : edit_box { key = "farbe_frei"; label = "eigene:"; edit_width = 12; } + } + } + + : boxed_column { + label = "Laengenabzug"; + : row { + : toggle { key = "abzug_a"; label = "Laengenabzug Anfang"; width = 24; fixed_width = true; } + : edit_box { key = "abzug_a_w"; label = "mm:"; edit_width = 8; fixed_width = true; } + } + : row { + : toggle { key = "abzug_e"; label = "Laengenabzug Ende"; width = 24; fixed_width = true; } + : edit_box { key = "abzug_e_w"; label = "mm:"; edit_width = 8; fixed_width = true; } + } + } + + : toggle { key = "beschriftung"; label = "Beschriftung einfuegen"; } + + : text { key = "hinweis"; label = ""; width = 56; } + + ok_cancel; +} diff --git a/doc/json-input-lisp.md b/doc/json-input-lisp.md new file mode 100644 index 0000000..1c90257 --- /dev/null +++ b/doc/json-input-lisp.md @@ -0,0 +1,437 @@ +# JSON-Eingabe fuer den LISP-Bau + +Welche Objekte sich aus einer JSON-Datei an einer gegebenen x/y/z-Koordinate +erzeugen lassen, in welchem Format sie beschrieben werden und welche +LISP-Routine sie baut. Grundlage sind die Testdaten in `tests/testdata/*.json` +und die Treiber in `tests/test_*.lsp`, die sie lesen. + +Die zugehoerigen LISP-Routinen mit Signaturen stehen in +[lisp_methods.md](lisp_methods.md). Diese Seite ersetzt das fruehere +`tests/testdata/object_data.md` (geloescht in Commit `e147b89`, auf das Code- +Kommentare teilweise noch verweisen). + +--- + +## 0. Uebersicht + +| Objekt | Erkennungsmerkmal im JSON | Bau-Routine (Produktion) | Koordinaten-Eingang | Beispiel-Datei | +|---|---|---|---|---| +| [Kreisel (Insert)](#1-kreisel) | `"function": "insert"`, KEIN `block` | `kreisel-insert-script` | `x`,`y`,`z` = Einfuegepunkt | `kreisel_tests.json`, `mubea.json`, `hundm05.json`, `dematic_arvato.json` | +| [Kreisel (Connect)](#12-connect-start--und-endpunkt) | `"function": "connect"` | `kreisel-connect-script` | `start_*`, `end_*` | (nur `test_kreisel.lsp`, z.Z. kein Datensatz) | +| [Einzelblock](#2-einzelbloecke-separator-scanner-btmt-eingangausgang-vario-bausteine) (Separator, Scanner, BTMT, Eingang/Ausgang, Vario-Bausteine) | `"function": "insert"` + `"block"` | direktes `_.INSERT` nach `ssg-ils-block-laden` | `x`,`y`,`z` + `rotation` | `mubea.json`, `hundm05.json`, `dematic_arvato.json` | +| [VarioFoerderer Standard](#3-variofoerderer-standard-und-etage) | `test_id` beginnt mit `VF_`, `typ` fehlt oder `"standard"` | `berechne-alle-winkel` + `variofoerderer-einfuegen` + `vf-block-erstellen` | `x`,`y`,`z` = KS_EIN des AS-Elements | `mubea.json`, `foerderer_tests.json` | +| [VarioFoerderer Etage](#3-variofoerderer-standard-und-etage) | `test_id` `VF_*` + `"typ": "etage"` | `berechne-etage` + `etage-foerderanlage-einfuegen` + `vf-block-erstellen` | wie Standard | `mubea.json` | +| [Gefaellestrecke Modus 1](#4-gefaellestrecke) | `test_id` `GF_*` bzw. `"modus": 1` | `gefaellestrecke-einfuegen` (bzw. `gf-rebuild`) | `start_mm` = KS_EIN des AS-Elements | `mubea.json`, `gefaellestrecke_tests.json` | +| [Gefaellestrecke Modus 2](#42-modus-2---richtung-aus-zwei-punkten) | `"modus": 2` | wie Modus 1, `deltaL`/`hz` aus zwei Punkten | `start_mm` + `linie_start_mm`/`linie_ende_mm` | `gefaellestrecke_tests.json` | +| [Gefaellestrecke Modus 3](#43-modus-3---linienzug) | `"modus": 3` | nur ueber `test_gefaellestrecke.lsp` | `start_mm`, `ende_mm`, `bogen` | `gefaellestrecke_tests.json` | +| [VF-Linienzug-Kette (Spec)](#5-vf-linienzug-kette-spec) | Kopf mit `spec_id`, danach `glied`/`sub` | `vsp-bau-aus-spec` (Replay durch `vf-linienzug-modus`) | `start_punkt` = Kettenanfang | `hundm05.json` | +| [VF-Linienzug (Journal)](#6-vf-linienzug-eingabe-journal) | Kopf mit `test_id`, danach `typ` = `point_abs`/`real`/`string`/... | `vfl-journal-replay-start` + `vf-linienzug-modus` | erstes `point_abs` | `linienzug_tests.json`, `hm_recformat.json` | +| [Omniflo Bogen/Weiche](#71-bogen-und-weiche) | `"type": "bogen"` / `"weiche"` + `sivasnr` | `_.INSERT` der `.dxf` (Kern `omni:insert-dxf` fragt den Punkt interaktiv) | `x`,`y`, Z = `hoehe` | `omniflo_tests.json`, `omniflo_strecke_tests.json` | +| [Omniflo Gerade](#72-aluprofil-gerade) | `"type": "gerade"` + `profil` | `omni:make-gerade` | `x`,`y` -> `x_ende`,`y_ende`, Z = `hoehe` | `omniflo_strecke_tests.json` | +| [Waagrechte Foerderstrecke](#8-waagrechte-foerderstrecke-gerade---noch-ohne-bau-pfad) | `"typ": "gerade"` + `startpunkt` | **kein Bau-Pfad** (wird gemeldet und uebersprungen) | `startpunkt`, `endpunkt` | `dematic_arvato.json` | + +Nicht aus JSON baubar (es gibt keine Testdaten und keinen Treiber): Eckrad, +Geruest, TEF-Foerderer-Linie, TRO-Marker, Connections, Omniflo-Ladeschleife. +Fuer Eckrad und Geruest existiert ein skriptfaehiger Kern, siehe +[lisp_methods.md](lisp_methods.md). + +### Allgemeine Regeln + +- **Einheiten**: Laengen und Koordinaten in **mm**, Winkel in **Grad**. + Fahrtrichtung `hz`: 0 = +X (Ost), 90 = +Y (Nord), 180 = -X, 270 = -Y. +- **Z ist absolut**: `z` (bzw. Z in `start_mm`/`start_punkt`) ist die + Weltkoordinate des Einfuegepunkts, also die Einbauhoehe. +- **Dimension 2D/3D**: Gebaut wird in der aktuellen Dimension + (`ssg-ils-dim-aktuell`: `*ssg-ils-dim*` -> `DXFM_DIM`, Default 3D). + Das Feld `darstellung` in `dematic_arvato.json` ist reine Dokumentation und + wird nicht ausgewertet; `TEST_HUNDM05_2D`/`_3D` erzwingen die Dimension. +- **Flaches JSON**: Die meisten Dateien werden mit `ssg-load-json` gelesen + (zeilenweise, ein Objekt je `{`). Verschachtelte Objekte und mehrzeilige + Arrays darin funktionieren nicht - darum stehen z.B. die Glieder einer + Kette als eigene Objekte hinter ihrem Kopf-Objekt. Ausnahme: + `TEST_DEMATIC_ARVATO` liest mit dem rekursiven `ssg-json-laden`. +- **Reine Doku-Felder**: `comment`, `beschreibung`, `description`, `quelle`, + `row`, `seq`, `gruppe`, `assigned_to`, `hoehe_unbekannt`, `darstellung` + sowie alle `expect_*`/`erwartung_*`-Felder werden beim Bau nicht gelesen. + `expect_*` pruefen die Python-Tests gegen das Ergebnis. +- **Template-Felder** (nur `test_mubea.lsp`): `anzahl`, `delta_x`, `delta_y` + erzeugen aus einem Eintrag `anzahl` Kopien, jeweils um `delta_x`/`delta_y` + versetzt; `test_id` bekommt dann das Suffix `_01` ... `_nn`. Gilt fuer VF, + GF und Einzelbloecke. + +--- + +## 1. Kreisel + +Block `KREISEL_n`, gebaut von `KreiselInsert.lsp`. + +### 1.1 Insert (freier Punkt) + +```json +{ "id": "KR_Kreuz_Q1_0Grad", "function": "insert", + "x": 500, "y": 500, "z": 2500, + "abstand": 2000, "rotation": 0.0, "typ": "STANDARD" } +``` + +| Feld | Pflicht | Bedeutung | Default | +|---|---|---|---| +| `id` | ja (fuer den Treiber) | Kennung im Ergebnis; NICHT die Block-ID (die vergibt `ssg-id-generate`) | - | +| `function` | ja | `"insert"`; ohne `block` = Kreisel | - | +| `x`, `y` | ja | Einfuegepunkt. Liegt auf der Kreiselachse **einen Radius (400 mm) vor der Mitte des Antriebsrads (AN)**, also am aeussersten Punkt der AN-Seite | - | +| `z` | ja | Einbauhoehe; wird als `hoehe` uebergeben und in das Attribut `HOEHE` geschrieben | `kreisel.default_hoehe` (2000) | +| `abstand` | ja | Mittenabstand Antriebsrad - Spannrad | `kreisel.default_laenge` (2300) | +| `rotation` | nein | Drehung der Achse AN -> SP, frei (kein Raster) | 0.0 | +| `typ` | nein | `"STANDARD"` oder `"PIN"` -> Attribut `KREISELART` | `"STANDARD"` | + +Treiber: `kreisel:run-insert-test` (`test_kreisel.lsp`), `mubea:build-kreisel`, +`hundm05:bau-kreisel` (auch fuer `dematic_arvato.json`). + +### 1.2 Connect (Start- und Endpunkt) + +```json +{ "id": "K2", "function": "connect", + "start_x": 0, "start_y": 0, "start_z": 2000, + "end_x": 5000, "end_y": 0, "end_z": 2000, "typ": "STANDARD" } +``` + +`start_*` und `end_*` sind die aeussersten Punkte der AN- bzw. SP-Seite. +`abstand` (= Distanz - Kreiseldurchmesser 800) und `rotation` werden daraus +berechnet. Die Einbauhoehe kommt aus `start_z`. Nur `test_kreisel.lsp` kennt +`connect`; die anderen Treiber bauen ausschliesslich `insert`. + +--- + +## 2. Einzelbloecke (Separator, Scanner, BTMT, Eingang/Ausgang, Vario-Bausteine) + +Jeder Eintrag mit `"function": "insert"` UND `"block"` wird als einzelner +Block eingefuegt, ohne Wrapper-Block und ohne Verkettung. + +```json +{ "id": "HundM05_SCANNER_02", "function": "insert", "block": "Scanner", + "x": 4095.5, "y": 1164.1, "z": 2154, "rotation": 270 } +``` + +| Feld | Pflicht | Bedeutung | Default | +|---|---|---|---| +| `block` | ja | Blockname OHNE `_2D`/`_3D`-Suffix | - | +| `x`, `y`, `z` | ja | Einfuegepunkt = **Basispunkt der Block-DWG** (nicht KS_EIN) | - | +| `rotation` | nein | Drehung um Z | 0 | +| `id` / `test_id` | nein | Kennung im Ergebnis | `SEPARATOR_` bzw. Blockname | + +Ablauf: `ssg-ils-block-laden` loest den Namen auf `data/ils/_.dwg` +auf (3D-Fallback), danach `_.INSERT eff pt "" "" rotation` und +`ssg-id-generate`. **Fehlt die DWG, wird `Separator_SP` eingefuegt.** +Treiber: `mubea:build-separator`, `hundm05:bau-sensor` (auch Dematic). +Das Layer-Attribut `LAYER` wird dabei nicht angewendet; der Block landet auf +dem aktuellen Layer. + +### In den Testdaten verwendete Blocknamen + +| `block` | DWG in `data/ils` | Verwendet in | Hinweis | +|---|---|---|---| +| `Scanner` | `Scanner_2D/_3D.dwg` | mubea, hundm05, dematic_arvato | | +| `Separator_SP` | `Separator_SP_2D/_3D.dwg` | (Fallback) | Block von `ILS_SEPARATOR_INSERT` | +| `S-LP` | **fehlt** | mubea, hundm05, dematic_arvato | wird zu `Separator_SP` | +| `S-SP` | **fehlt** | dematic_arvato | wird zu `Separator_SP` | +| `BTMT-Beladung` | `BTMT-Beladung_2D/_3D.dwg` | mubea, hundm05 | wie `ILS_BTMT_Beladung` | +| `SC_Entladung` | `SC_Entladung_2D/_3D.dwg` | mubea, hundm05 | wie `ILS_BTMT_Entladung` | +| `Eingang` / `Ausgang` | `Eingang_*/Ausgang_*.dwg` | mubea | Symbole wie `ILS_EINGANG_INSERT`/`ILS_AUSGANG_INSERT` | +| `AS_Element_90_links` / `_rechts` | vorhanden | dematic_arvato | loses AS-Element (sonst Teil eines VF/GF) | +| `ES_Element_90_rechts`, `ES_Element_30_rechts` | vorhanden | dematic_arvato | loses ES-Element | +| `Vario_Motorstation_500mm_rechts` | vorhanden | dematic_arvato | lose Motorstation | +| `Vario_Umlenkstation_500mm_rechts` | vorhanden | dematic_arvato | lose Umlenkstation | +| `Vario_Kurve_rechts_30_TEF_aussen` | vorhanden | dematic_arvato | lose Vario-Kurve | + +Grundsaetzlich ist **jeder** Blockname aus `data/ils` erlaubt (es gibt keine +Whitelist). Die losen AS/ES-/Vario-Bausteine bilden aber keine Kette und keinen +`VF_n`/`GF_n`-Block - fuer eine zusammenhaengende Strecke Abschnitt 3-5 nutzen. + +--- + +## 3. VarioFoerderer Standard und Etage + +Block `VF_n`. Eingang sind die **Gesamtmasse** der Anlage; Winkel und +Teillaengen berechnet der Solver. + +```json +{ "test_id": "VF_Schnitt_C", "richtung": "Auf", + "deltaL": 9110, "deltaH": 474, "winkel": 21, + "gf_verteilung": "manuell", "L_GF1": 2298, "seite": "links", + "hz": 0.0, "x": 400, "y": 14120, "z": 1554 } +``` + +| Feld | Pflicht | Bedeutung | Default | +|---|---|---|---| +| `test_id` | ja | muss mit `VF_` beginnen (steuert den Dispatcher in `test_mubea.lsp`) | - | +| `typ` | nein | `"standard"` (AS/ES 90 Grad) oder `"etage"` (AS/ES 30 Grad, uebereinander) | `"standard"` | +| `richtung` | ja | `"Auf"` oder `"Ab"` | - | +| `deltaL` | ja | horizontale Gesamtlaenge inkl. AS/ES, Boegen, GF-Vor-/Nachlauf | - | +| `deltaH` | ja | Hoehendifferenz (Betrag; Vorzeichen kommt aus `richtung`) | - | +| `winkel` | nein | Bogenwinkel erzwingen, nur aus der Kandidatenliste (`vario.bogen_winkel`: 3, 6, 9, 12, 15, 18, 21, 27, 33, 39, 45, 51). Ist er nicht loesbar, wird NICHT gebaut | kleinster gueltiger Winkel | +| `L_GF1` | nein (mubea) | Vorlauf-Laenge (GF vor dem Foerderer); Nachlauf = Rest von `L_GF` | - | +| `gf_verteilung` | nein (foerderer) | `"gleichmaessig"`, `"vorne"`, `"hinten"`; in `mubea.json` nur Doku (dort zaehlt `L_GF1`) | `"gleichmaessig"` | +| `seite` | nein | `"links"`/`"rechts"` (Seite der AS/ES-Elemente) | `"links"` | +| `hz` | nein | Fahrtrichtung | 0.0 | +| `x`, `y`, `z` | ja (mubea) | Startpunkt = **KS_EIN des AS-Elements**; `z` = Einbauhoehe am Anfang | - | + +Hinweise: +- `foerderer_tests.json` hat statt `x/y/z` einen Kopf-Eintrag + `{"y_abstand": 600}`: alle Foerderer starten bei x = 0, z = 0 und werden um + `y_abstand` versetzt. Dieses Format kennt nur `test_foerderer.lsp`. +- `"Ab"` mit `deltaH` < 1 ist geometrisch unmoeglich und wird abgewiesen. +- Der Treiber schreibt nach dem Bau `vf-edit-xdata-schreiben` (Typ + `hz`), + damit der Block per Doppelklick editierbar ist. + +--- + +## 4. Gefaellestrecke + +Block `GF_n`: AS-Element -> Staustrecke (skaliert) -> Separator -> ES-Element. +Eingang ist die **horizontale Laenge**, nicht die Hoehendifferenz - die ergibt +sich aus Laenge und Neigung. + +### 4.1 Modus 1 - Werteingabe + +```json +{ "test_id": "GF_Mubea", "modus": 1, + "deltaL_mm": 9110, "hz_grad": 180.0, "winkel_grad": 2.27, + "start_mm": [9510, 12300, 2028], + "as_seite": "links", "es_seite": "links", + "as_winkel": "90", "es_winkel": "90" } +``` + +| Feld | Pflicht | Bedeutung | Default | +|---|---|---|---| +| `test_id` | ja | in `mubea.json` muss sie mit `GF_` beginnen | - | +| `modus` | nein | `1` | - | +| `deltaL_mm` | ja | horizontale Gesamtlaenge AS + Staustrecke + Separator + ES | - | +| `winkel_grad` | nein | Neigung der Staustrecke | 3.0 | +| `hz_grad` | nein | Fahrtrichtung | 0.0 | +| `start_mm` | ja | `[x, y, z]` = **KS_EIN des AS-Elements**; Z = Einbauhoehe oben | - | +| `as_seite`, `es_seite` | nein | `"links"`/`"rechts"` | `"links"` | +| `as_winkel`, `es_winkel` | nein | `"90"` oder `"30"` (als STRING) | `"90"` | + +Die Staustrecke ergibt sich aus +`L_stau = (deltaL - aus-dx - ein-dx - 300 * cos(w)) / cos(w)` +(300 = `gefaelle.separator_breite`). Eine Ziel-Hoehe gibt es nicht. + +In `gefaellestrecke_tests.json` stehen `deltaL_mm`, `winkel_grad` und +`z_start_mm` in einem gemeinsamen Kopf-Eintrag ohne `test_id` und gelten fuer +alle Faelle der Datei. + +### 4.2 Modus 2 - Richtung aus zwei Punkten + +Wie Modus 1, aber `deltaL` und `hz` werden aus `linie_start_mm` -> +`linie_ende_mm` berechnet (nur X/Y). `start_mm` bleibt der Einfuegepunkt und +ist unabhaengig von der Linie. + +```json +{ "test_id": "M2_hz000", "modus": 2, "start_mm": [0, -35000, 5000], + "linie_start_mm": [0, -35000, 0], "linie_ende_mm": [10000, -35000, 0], + "as_seite": "links", "es_seite": "links", "as_winkel": "90", "es_winkel": "90" } +``` + +### 4.3 Modus 3 - Linienzug + +In der Produktion nicht skriptbar (`gf-linienzug-modus` waehlt vorgezeichnete +LINE/ARC-Objekte per Maus). `test_gefaellestrecke.lsp` zeichnet die Leitlinie +selbst und baut sie nicht-interaktiv nach. Zwei Formen: + +```json +{ "test_id": "M3_Bogen", "modus": 3, + "start_mm": [30000, -50000, 2000], "ende_mm": [38500, -43500, 1800], + "bogen": {"winkel_grad": 90, "radius_mm": 500, "seite": "links"}, + "as_seite": "links", "es_seite": "links", "as_winkel": "90", "es_winkel": "90" } +{ "test_id": "M3_hz270", "modus": 3, "richtung": "Sued", + "start_mm": [0, -50000, 5000], + "deltaH_quelle": "M2_hz000", "deltaL_quelle": "M2_hz000", ... } +``` + +`deltaH_quelle`/`deltaL_quelle` uebernehmen die Masse eines frueher gebauten +Falls. Dieses Format ist an den Treiber gebunden. Fuer neue Anlagen besser +eine VF-Linienzug-Spec (Abschnitt 5) mit `Linie-GF`/`GF-Bogen` verwenden. + +--- + +## 5. VF-Linienzug-Kette (Spec) + +Beliebige Ketten aus GF-Linien, GF-Boegen, Vario-Kurven und VF-Einheiten, +**ohne Dialog** gebaut (`VF_SPEC_BAU`, `TEST_HUNDM05`). Eine Kette = ein +Kopf-Objekt, danach ihre Glieder (`glied`) und deren Unterknoten (`sub`) in +Baureihenfolge. Ergebnis ist ein `VF_n`-Block mit vollem `SSG_VF_EDIT`-Journal +(per Doppelklick editierbar). + +```json +{ "spec_id": "VF_spec_LZ_01", "modus": 1, + "start_punkt": [4912.2, 1230.9, 2154.0], "start_hoehe": 2154.0, + "as": 1, "as_winkel": "90", "as_seite": "links" } +{ "glied": "Linie-GF", "nr": 1, "dl": 2249.3, "hz": 90.0, + "gefaelle": "winkel", "winkel": 3.0, "ende": "nein" } +{ "glied": "Horizontal-VF", "nr": 2, "dl": 3256.9, "gf_verteilung": "einlauf", + "vf_sep_vor": 0, "vf_sep_nach": 0, "vf_ende": "nein", "vf_separator": 0 } +{ "sub": "vario-kurve", "winkel": 90, "seite": "rechts", "variante": "innen" } +{ "sub": "motorstation" } +{ "glied": "ES", "winkel": "90", "seite": "links" } +``` + +**Kopf** + +| Feld | Bedeutung | +|---|---| +| `spec_id` | Kennung der Kette | +| `modus` | `1` (nur Modus 1 hat einen Uebersetzer) | +| `start_punkt` | `[x, y, z]` Kettenanfang (KS_EIN des AS-Elements bzw. erstes Glied) | +| `start_hoehe` | Einbauhoehe am Start | +| `as` | `1` = AS-Element setzen, `0` = nicht | +| `as_winkel` / `as_seite` | `"90"`/`"30"` bzw. `"links"`/`"rechts"` | + +**Glieder** (`glied`) + +| `glied` | wichtigste Felder | +|---|---| +| `Linie-GF` | `dl`, `hz` (nur beim ersten Glied der Kette), `laenge_modus` (`endpunkt`/`hoehe`/`steigend`), `ende` (`ja-mit-es`/`ja-ohne-es`/`nein`), `sep_vor`, `sep_nach`, `gefaelle` (`hoehe`/`winkel`) + `hoehe` bzw. `winkel` | +| `GF-Bogen` | `winkel` (30/60/90), `seite` | +| `Vario-Kurve` | als eigenes Glied (Kettenanfang) vom Uebersetzer noch NICHT unterstuetzt -> Spec-Fehler; Vario-Kurven als `sub` einer VF-Einheit angeben | +| `Horizontal-VF` | `dl`, `eingang_hoehe`, `gf_verteilung` (`haelfte`/`einlauf`), `vf_sep_vor`, `vf_sep_nach`, `vf_ende` (`ja-mit-es`/`nein`/`automatisch`/`zielpunkt-ohne-es`), `vf_separator` (nur bei `vf_ende` = `nein`) + `sub`-Knoten | +| `Linie-VF` | `dl`, `hoehe`, optional `winkel_idx` + `sub`-Knoten | +| `Linie` | `dl`, `hoehe`, `segment_typ` (`GF`/`VF`), optional `winkel_idx` | +| `ES` | `winkel` (`"90"`/`"30"`), `seite` | + +**Unterknoten einer VF-Einheit** (`sub`): `horizontal` (`sep_vor`, `sep_nach`), +`vario-kurve` (`winkel`, `seite`, `variante` `aussen`/`innen`), `auf-ab` +(`richtung` `auf`/`ab`, `hoehe`), `motorstation`, `kettenende` (`es`, +`es_winkel`, `es_seite`). + +Ja/Nein-Felder (`as`, `sep_vor`, `vf_sep_vor`, `es`, ...) als `1`/`0` oder +`"ja"`/`"nein"`; mehrwertige Felder (`ende`, `vf_ende`) nur mit den genannten +Woertern. Vollstaendige Regeln +(Menue-Codes je Frame, `hz` nur einmal, Verhalten bei `winkel_idx`): +[variofoerderer.md, Abschnitt 8b](variofoerderer.md#spec-pfad-kette-aus-eingabedaten-vf_spec_bau). +Die Spec wird nicht von Hand geschrieben, sondern mit +`python lib/vf_spec_export.py ` aus einer +aufgezeichneten Kette erzeugt. Fehlerhafte Specs werden komplett abgewiesen +(`vsp-pruefen` sammelt alle Fehler, gebaut wird dann nichts). + +**Andere Objekte in derselben Datei**: Neben den Ketten darf `hundm05.json` +Kreisel (Abschnitt 1) und Einzelbloecke (Abschnitt 2) enthalten. Erkennbar +sind sie an `function`; `vsp-gruppieren` ueberspringt sie, und +`hundm05:bau-zusatzobjekte` baut sie nach den Ketten. `vf_spec_export.py` +haengt sie beim Neuerzeugen unveraendert ans Dateiende. + +--- + +## 6. VF-Linienzug (Eingabe-Journal) + +Das Rohformat hinter der Spec: die Antworten auf die Fragen des interaktiven +Linienzug-Ablaufs, in Reihenfolge. Nur fuer Regressionstests gedacht - fuer +neue Anlagen die Spec (Abschnitt 5) verwenden. + +```json +{ "test_id": "VF_hundm05_LZ_01", "anlagetyp": "linienzug", "modus": 1, ... } +{ "typ": "point_abs", "wert": [4912.2, 1230.9, 2154.0], "kommentar": "Startpunkt" } +{ "typ": "real", "wert": 2154.0, "kommentar": "Hoehe (Z)" } +{ "typ": "string", "wert": "1", "kommentar": "AS-Element? Ja" } +{ "typ": "step", "wert": "Linie-GF", "kommentar": "Glied-Marker" } +{ "typ": "point_rel", "dL": 2249.3, "hz": 90.0, "kommentar": "Endpunkt" } +{ "typ": "int", "wert": 90, "kommentar": "Vario-Kurve Winkel" } +``` + +`typ`: `point_abs` (absoluter Punkt), `point_rel` (Abstand `dL` + Richtung +`hz`, `hz` nur beim ersten Segment), `real`, `int`, `string` (Menue-Code als +String), `step` (Glied-Marker, beim Replay uebersprungen). Kopf-Feld heisst +`anlagetyp`, nicht `typ`, damit Kopf und Eingabe unterscheidbar bleiben. + +| Datei | Treiber | +|---|---| +| `linienzug_tests.json`, `linienzug_tests_stufe2.json` | `TEST_LINIENZUG`, `TEST_LINIENZUG_STUFE2` | +| `hm_recformat.json` (5 echte HundM-Ketten, erzeugt mit `lib/vf_journal_export.py`) | `TEST_HM_RECFORMAT` (in `alltests.json` abgeschaltet) | + +--- + +## 7. Omniflo + +### 7.1 Bogen und Weiche + +```json +{ "id": "821104025", "type": "bogen", "sivasnr": "821104025", + "x": 219.4, "y": 223.06, "hoehe": 2000, "drehung": 0.0 } +``` + +| Feld | Pflicht | Bedeutung | Default | +|---|---|---|---| +| `type` | ja | `"bogen"` oder `"weiche"` | - | +| `sivasnr` | ja | Sivas-Nummer = Dateiname `%DXFM_OMNIFLO%/.dxf` | - | +| `x`, `y` | ja | Einfuegepunkt (`$INSBASE` der DXF, gesetzt mit `set_einfuegepkt.py`) | - | +| `hoehe` | nein | Z des Einfuegepunkts UND Attribut `HOEHE` | 2000 | +| `drehung` | nein | Drehung + Attribut `DREHUNG` | 0 | + +Es gibt kein `z`: die Hoehe ist zugleich die Z-Koordinate. Gueltige +Sivas-Nummern: `data/json/omniflo_boegen.json`, `omniflo_weichen.json`. +Treiber: `omni:test-insert` (`test_omniflo.lsp`), `omnistr:insert-bogen` +(`test_omniflo_strecke.lsp`). + +### 7.2 Aluprofil-Gerade + +```json +{ "seq": 1, "type": "gerade", "profil": "AP110", "laenge": 1000.0, + "x": 2000.0, "y": 0.0, "x_ende": 3000.0, "y_ende": 0.0, + "drehung": 0.0, "hoehe": 2000, "id": "0003" } +``` + +`profil` = `"AP60"`, `"AP110"` oder `"APG110"`. Die Gerade laeuft von +(`x`,`y`) nach (`x_ende`,`y_ende`) auf Z = `hoehe`; Laenge und Drehung folgen +aus den Punkten (`laenge` ist nur Doku). Gebaut mit `omni:make-gerade`. + +--- + +## 8. Waagrechte Foerderstrecke (`gerade`) - noch ohne Bau-Pfad + +```json +{ "test_id": "GERADE_Arvato_01", "typ": "gerade", "laenge": 8313.39, "hz": 90.0, + "startpunkt": [2334.97, 177818.67, 0.0], "endpunkt": [2334.96, 186132.06, 0.0] } +``` + +Ein Antriebs-/Umlenkstation-Paar ohne Hoehendifferenz, so von +`lib/dxf_scan_components.py` ausgegeben (ein VarioFoerderer ist bei +`deltaH` = 0 nicht loesbar). `TEST_DEMATIC_ARVATO` meldet diese Eintraege und +ueberspringt sie. Passende Primitive waere `insert-hz-scaled-block-by-ks` +(`vf_etage.lsp`) mit `Staustrecke_SP_1000_mm`, siehe +[lisp_methods.md](lisp_methods.md). + +--- + +## 9. Testdaten-Dateien im Ueberblick + +| Datei | Inhalt | Treiber / Befehl | +|---|---|---| +| `kreisel_tests.json` | 8 Kreisel (`insert`) | `TEST_KREISEL` | +| `foerderer_tests.json` | Kopf `y_abstand` + 17 VF Standard | `TEST_FOERDERER` | +| `gefaellestrecke_tests.json` | Kopf + GF Modus 1/2/3 | `TEST_GEFAELLESTRECKE` | +| `omniflo_tests.json` | 27 Boegen, 80 Weichen | `TEST_OMNIFLO` | +| `omniflo_strecke_tests.json` | Streckenzug aus Geraden und Boegen | `TEST_OMNIFLO_STRECKE` | +| `linienzug_tests.json` / `_stufe2.json` | Eingabe-Journal je einer Kette | `TEST_LINIENZUG` / `TEST_LINIENZUG_STUFE2` | +| `hm_recformat.json` | 5 HundM-Ketten als Journal | `TEST_HM_RECFORMAT` | +| `hundm05.json` | 5 HundM-Ketten als Spec + Kreisel, Separatoren, Scanner, BTMT | `TEST_HUNDM05`, `VF_SPEC_BAU` | +| `mubea.json` | Gesamtanlage: Kreisel, VF Standard/Etage, GF-Template (20 Stueck), Separatoren, Scanner, BTMT, Eingang/Ausgang | `TEST_MUBEA` | +| `dematic_arvato.json` | Vorlage aus `dxf_scan_components.py`: Kreisel, Einzelbloecke, `gerade` (Hoehen fehlen) | `TEST_DEMATIC_ARVATO` (abgeschaltet) | +| `hundm05_linienzug_polylines.json` | Polylinien aus `extract_polylines.py` | kein Treiber (Analyse-Daten) | +| `vfl_journal_hm05_abbruch.json` | rohes Journal (`kind`/`value`) eines abgebrochenen Baus | kein Treiber | + +--- + +## 10. Eigene Anlage beschreiben - Kurzanleitung + +1. Kreisel als `insert` (Abschnitt 1): `x/y` am aeussersten Punkt der AN-Seite, + `z` = Einbauhoehe. +2. Einfache Steig-/Fall-Strecken mit fester Form als VF Standard/Etage + (Abschnitt 3) bzw. Gefaellestrecke Modus 1 (Abschnitt 4.1), Startpunkt + jeweils = KS_EIN des AS-Elements. +3. Ketten mit Kurven, mehreren VF-Einheiten oder GF-Boegen als Spec + (Abschnitt 5) - am einfachsten eine Kette einmal interaktiv bauen, mit + `lib/vf_journal_export.py` + `lib/vf_spec_export.py` als Spec exportieren + und Startpunkt/Laengen anpassen. +4. Separatoren, Scanner, BTMT-Stationen, Eingang/Ausgang als Einzelblock + (Abschnitt 2). +5. Aus Kreisel, Einzelbloecken und Specs gemischt baut derzeit nur der + HundM05-Treiber (`TEST_HUNDM05`); fuer VF Standard/Etage + GF zusammen mit + Kreiseln den Mubea-Treiber (`TEST_MUBEA`) als Vorlage nehmen. diff --git a/doc/lisp_methods.md b/doc/lisp_methods.md new file mode 100644 index 0000000..11d78d7 --- /dev/null +++ b/doc/lisp_methods.md @@ -0,0 +1,328 @@ +# LISP-Routinen zum Bau einzelner Einheiten + +Uebersicht ueber die LISP-Funktionen, die eine Einheit (Kreisel, Eckrad, +VarioFoerderer, Gefaellestrecke, Omniflo-Element, Geruest, Einzelblock) +erzeugen, mit Signatur, Parametern, Rueckgabe und Voraussetzungen. + +Zu jedem Element steht der interaktive **Befehl** (was der Anwender aufruft) +und der **skriptfaehige Kern** (ohne `getpoint`/Dialog, aus Code oder JSON +aufrufbar). Das JSON-Format, mit dem die Testtreiber diese Kerne aufrufen, +steht in [json-input-lisp.md](json-input-lisp.md); alle Befehle in +[kommandos.md](kommandos.md). + +Einheiten: mm und Grad. `hz` = Fahrtrichtung in der XY-Ebene, 0 = +X, +90 = +Y. Punkte sind Listen `(x y z)` in Weltkoordinaten. + +--- + +## 0. Uebersicht + +| Einheit | Befehl | Skriptfaehiger Kern | Ergebnis-Block | Modul | +|---|---|---|---|---| +| Kreisel | `KreiselInsert`, `KreiselConnect`, `KreiselQuick` | `kreisel-insert-script`, `kreisel-connect-script` | `KREISEL_n` | `KreiselInsert.lsp` | +| Eckrad | `ILS_Eckrad` | `ils-eckrad-insert` | `ECKRAD_n` | `KreiselInsert.lsp` | +| VF Standard | `StandardVarioFoerderer`, `FOERDERANLAGE` | `berechne-standard` + `variofoerderer-einfuegen` + `vf-block-erstellen` | `VF_n` | `vf_standard.lsp`, `vf_core.lsp` | +| VF Etage | `EtageVarioFoerderer` | `berechne-etage` + `etage-foerderanlage-einfuegen` + `vf-block-erstellen` | `VF_n` | `vf_etage.lsp`, `vf_core.lsp` | +| VF Linienzug | `LinienzugVarioFoerderer` | `vsp-bau-aus-spec` (Spec) bzw. `vfl-journal-replay-start` + `vf-linienzug-modus` | `VF_n` | `vf_linienzug.lsp`, `vf_spec.lsp` | +| Gefaellestrecke | `GEFAELLESTRECKE_EINFACH`/`_3DLINIE` | `gf-rebuild` bzw. `gefaellestrecke-einfuegen` | `GF_n` | `Gefaellestrecke.lsp` | +| Gefaellestrecke Linienzug | `GEFAELLESTRECKE_LINIENZUG` | - (nur interaktiv; Kern `gf-linienzug-einfuegen` braucht analysierte Segmente) | `GF_n` | `Gefaellestrecke.lsp` | +| Separator, Scanner | `ILS_SEPARATOR_INSERT`, `ILS_SCANNER_INSERT` | - (Schleife mit `getpoint`); Kern: `ssg-ils-block-laden` + `_.INSERT` | Einzelblock | `SSG_LIB_Commands.lsp`, `ssg_core.lsp` | +| BTMT, Eingang/Ausgang | `ILS_BTMT_*`, `ILS_EINGANG_INSERT`/`ILS_AUSGANG_INSERT` | wie Separator | Einzelblock | `SSG_LIB_Commands.lsp` | +| Omniflo Bogen/Weiche/TEF | `OMNI_APB_*`, `OMNI_W*_*`, `OMNI_TEF_*` ... | `_.INSERT` der `.dxf` (Kern `omni:insert-dxf` fragt den Punkt) | Einzelblock | `OmniModulInsert.lsp` | +| Omniflo Gerade | `OMNI_AP60`/`AP110`/`APG110` | `omni:make-gerade` | Compound-Block | `OmniModulInsert.lsp` | +| TEF-Foerderer-Linie | `TEF_FOERDERER_LINIE` | `tefl-insert-element` (ein Element) | Einzelbloecke | `TEFInsert.lsp` | +| Geruest (Saeule, Rohrabhaenger, Wandbefestigung) | `GERUEST`, `GERUEST_SAEULEN` ... | `geruest-block-bauen` | `GR__` | `GeruestInsert.lsp` | +| Geruest-Traeger | `GERUEST_TRAEGER` | `geruest-traeger-block-bauen` | `GR__` | `GeruestInsert.lsp` | + +--- + +## 1. Kreisel und Eckrad (`KreiselInsert.lsp`) + +### `kreisel-insert-script (pt abstand rotation typ hoehe)` + +Kreisel an einem freien Punkt. Ruft `draw-module` auf. + +| Parameter | Bedeutung | +|---|---| +| `pt` | Einfuegepunkt: aeusserster Punkt der AN-Seite auf der Achse (einen Radius = 400 mm vor der Mitte des Antriebsrads) | +| `abstand` | Mittenabstand Antriebsrad - Spannrad | +| `rotation` | Drehung der Achse AN -> SP in Grad | +| `typ` | `"STANDARD"` oder `"PIN"` (nil = `"STANDARD"`) -> Attribut `KREISELART` | +| `hoehe` | Einbauhoehe; nil = Z von `pt`, sonst `*kreisel-default-hoehe*` | + +Rueckgabe: Entity des `KREISEL_n`-Blocks oder nil. Setzt `ssg-start`/`ssg-end` +selbst. + +### `kreisel-connect-script (ptStart ptEnd typ hoehe)` + +Kreisel zwischen zwei aeussersten Punkten (AN-Seite, SP-Seite). Berechnet +`abstand` = Distanz - `*kreisel-durchmesser*` (800) und `rotation` aus den +Punkten. Rueckgabe wie oben. + +### `draw-module (basePoint abstand rotation attribs)` + +Gemeinsamer Bau-Kern beider Funktionen: setzt Antriebs- und Spannstation, +Tangenten, Beschriftung, fasst alles zu `KREISEL_n` zusammen, schreibt die +Attribute (`attribs` = Alist, wird mit `*kreisel-attrib-defs*` gemischt). +Direkt nur aufrufen, wenn zusaetzliche Attribute gesetzt werden muessen. + +### `ils-eckrad-insert (pt rotation attribs)` + +Eckrad als Compound-Block `ECKRAD_n`. + +| Parameter | Bedeutung | +|---|---| +| `pt` | **Radmitte** `(x y hoehe)` | +| `rotation` | Drehung in Grad | +| `attribs` | Alist mit Ueberschreibungen, z.B. `(("GERUEST_EINZELMODUL" . "1") ("GERUEST_TYP" . "Schoenenberger Geruest"))`; nil = Defaults | + +`c:ILS_Eckrad` berechnet die Radmitte aus Beruehrpunkt + Radius in Richtung +des zweiten Klicks. Rueckgabe: Entity des Blocks. + +--- + +## 2. VarioFoerderer Standard und Etage (`vf_core.lsp`, `vf_standard.lsp`, `vf_etage.lsp`) + +Ablauf in drei Schritten: **loesen** (Winkel und Teillaengen aus den +Gesamtmassen), **einfuegen** (Bausteine verketten), **Block bilden** +(`VF_n` mit Attributen). Der Dialog-Ablauf `vf-dialog-ablauf` / +`vf-dialog-berechnen-einfuegen` macht dasselbe, oeffnet aber Dialog 2 und ist +darum nicht skriptfaehig. + +### Vorbereitung + +| Funktion | Zweck | +|---|---| +| `init-bibliothek ()` | Bogen-, AS-/ES- und Stationsmasse aus den Bloecken lesen. Setzt `*lib-initialized*`. Wird von `berechne-standard` aufgerufen | +| `init-bibliothek-etage ()` | dasselbe fuer Etage, aufgerufen von `berechne-etage` | +| `vf-set-as-masse (winkel seite)` / `vf-set-es-masse (winkel seite)` | AS-/ES-Masse (`aus-dx`/`aus-dz`, `ein-dx`/`ein-dz`) fuer die Variante `"90"`/`"30"` + `"links"`/`"rechts"` setzen | + +### Loesen + +| Funktion | Rueckgabe | +|---|---| +| `berechne-standard (deltaL deltaH richtung seite)` | `(best-winkel L_GF L_VF ergebnis-liste)` | +| `berechne-alle-winkel (deltaL deltaH richtung feste-hz schon-flach)` | wie oben; `feste-hz` nil = 1600 mm feste Horizontale, `schon-flach` nil | +| `berechne-etage (deltaL deltaH richtung seite)` | wie oben, Etage-Geometrie (setzt die Gefaellebogen-Masse je `seite`) | +| `vf-winkel-solve (...)` | gemeinsamer L_GF/L_VF-Solve beider Typen (intern) | + +- `richtung` = `"Auf"`/`"Ab"`, `deltaH` als Betrag. +- `ergebnis-liste` = je Kandidatenwinkel `(winkel L_GF L_VF gueltig)`. + Kandidaten aus `vario.bogen_winkel` (3 ... 51). Einen bestimmten Winkel + erzwingen: Eintrag mit diesem `winkel` aus der Liste nehmen und + `gueltig`, `L_GF` > 0, `L_VF` > 0 pruefen (so `mubea:build-vario-one`). +- `L_GF` wird danach auf Vorlauf `L_GF1` und Nachlauf `L_GF2` aufgeteilt + (gleichmaessig / vorne / hinten / manuell, `foerderer:split-gf`). + +### Einfuegen + +``` +(variofoerderer-einfuegen deltaL deltaH richtung best-winkel L_GF1 L_GF2 L_VF startpunkt seite hz) +(etage-foerderanlage-einfuegen deltaL deltaH richtung best-winkel L_GF1 L_GF2 L_VF startpunkt seite hz) +``` + +Identische Signatur (Registry-Vertrag `*vf-typ-registry*`). `startpunkt` = +Lage von **KS_EIN des AS-Elements**, Z = Einbauhoehe am Anfang. `seite` = +`"links"`/`"rechts"`. Rueckgabe: Endpunkt (KS_AUS des ES-Elements). +`variofoerderer-einfuegen` ist aus Teilen zusammengesetzt (`vfs-as-teil`, +VF-Eingang/Koerper, `vfs-es-teil`), die auch der Linienzug nutzt. + +### Block bilden + +``` +(setq lastEnt (ssg-lastent-ohne-attribute)) ; VOR dem Einfuegen merken +...einfuegen... +(vf-block-erstellen anlage-typ seite (vf-next-number) n-scanner + hoehe-von hoehe-bis deltaH deltaL L_VF L_GF1 L_GF2 + richtung best-winkel startpunkt lastEnt hz + geruest-einzelmodul geruest-typ) +(vf-edit-xdata-schreiben ent anlage-typ hz) ; editierbar per Doppelklick +``` + +- `anlage-typ` = `"standard"`/`"etage"`; `hoehe-bis` = +deltaH bei `"Auf"`, + -deltaH bei `"Ab"`; `geruest-einzelmodul` `"0"`/`"1"` (nil = `"1"`), + `geruest-typ` einer aus `*ssg-geruest-optionen*`. +- Sammelt alle Objekte seit `lastEnt` zum Block `VF_n`, setzt Layer, Attribute, + ID (`ssg-id-generate`) und Dimension. Rueckgabe: Entity des Blocks. +- `lastEnt` immer mit `ssg-lastent-ohne-attribute` holen, nicht mit + `(entlast)` (siehe `CLAUDE.md`, ssg_core). +- Blocknummer immer ueber `vf-next-number`: eine feste Nummer fuehrt bei der + zweiten Anlage zu einer Rueckfrage von `_.-BLOCK` und einem haengenden Befehl. + +--- + +## 3. VF-Linienzug (`vf_linienzug.lsp`, `vf_spec.lsp`) + +Ketten mit Vario-Kurven, GF-Boegen und mehreren VF-Einheiten. Der Bau-Ablauf +ist ein Frage-Antwort-Assistent; jede Eingabe wird journalisiert und als XDATA +am `VF_n`-Block gespeichert. Nicht-interaktiv baut man ihn, indem man ein +Journal **abspielt**. + +| Funktion | Zweck | +|---|---| +| `vf-linienzug-modus ()` | Modus 1, frei zeichnend. Liest alle Eingaben ueber `vfl-in-point`/`-string`/`-real`/`-int` - aus der Replay-Queue, falls eine aktiv ist | +| `vf-linienzug-modus2 ()` | Modus 2, Pfad + Zielhoehe (Objektauswahl, eigenes Journal `"linienzug2"`) | +| `vfl-journal-replay-start (forward-journal)` | Journal als Antwort-Queue setzen; danach `vf-linienzug-modus` aufrufen | +| `vsp-journal (spec)` | Spec -> Journal (nil bei Spec-Fehlern, Fehler in `*vsp-fehler*`) | +| `vsp-pruefen (spec)` | alle Spec-Fehler sammeln | +| `vsp-bau-aus-spec (spec)` | eine Kette aus einer Spec bauen (headless, Replay, Ergebnis-Record mit `status`, Block, `prompts`, `glieder_soll`/`glieder_ist`) | +| `vsp-bau-liste (specs)` | mehrere Specs, je mit `vl-catch-all-apply` | +| `vsp-bau-datei (datei)` | Spec-Datei lesen (`vsp-json-laden` + `vsp-gruppieren`) und alle Ketten bauen; das ist `VF_SPEC_BAU` | +| `vsp-headless-an ()` / `vsp-headless-aus ()` | Headless-Rahmen: `*vfl-headless*`, Wizard aus, `*ssg-gui-aus*`, zaehlende Eingabe-Stubs | + +Ein Abbruch im Headless-Modus nennt die Stelle (`*vfl-headless-fehler*`), +statt auf eine Live-Eingabe zu warten. Details zum Ablauf und zu den +Journal-Regeln: [variofoerderer.md](variofoerderer.md), Abschnitte 5-8b. + +--- + +## 4. Gefaellestrecke (`Gefaellestrecke.lsp`) + +### `gf-rebuild (deltaL winkel hz-winkel as-seite es-seite startpunkt as-winkel es-winkel geruest-einzelmodul geruest-typ)` + +Der bequemste skriptfaehige Einstieg fuer Modus 1/2: setzt die AS-/ES-Masse, +berechnet die Staustrecke +`L_stau = (deltaL - aus-dx - ein-dx - separator_breite * cos(w)) / cos(w)` +und ruft `gefaellestrecke-einfuegen`. Ohne Zusammenfassung und Rueckfrage +(eigentlich fuer `GEFAELLESTRECKE_EDIT` gedacht). + +| Parameter | Bedeutung | +|---|---| +| `deltaL` | horizontale Gesamtlaenge AS + Staustrecke + Separator + ES | +| `winkel` | Neigung der Staustrecke in Grad (Default im Dialog 3.0) | +| `hz-winkel` | Fahrtrichtung | +| `as-seite`, `es-seite` | `"links"`/`"rechts"` | +| `startpunkt` | **KS_EIN des AS-Elements**, Z = Einbauhoehe oben | +| `as-winkel`, `es-winkel` | `"90"`/`"30"` als STRING (nil = `"90"`) | +| `geruest-einzelmodul`, `geruest-typ` | wie beim VF | + +Rueckgabe: `(ename deltaH deltaL)` oder nil (Staustrecke <= 0.1 mm -> `alert`). + +### `gefaellestrecke-einfuegen (L_stau winkel startpunkt as-seite es-seite hz deltaL-total as-winkel es-winkel geruest-einzelmodul geruest-typ)` + +Eigentlicher Bau: AS-Element, skalierte Staustrecke, Separator, ES-Element, +Block `GF_n` mit Attributen, XDATA `SSG_GF_EDIT` und ID. Erwartet die +fertige Staustreckenlaenge `L_stau` und passend gesetzte AS-/ES-Masse +(`vf-set-as-masse`/`vf-set-es-masse`). Rueckgabe wie `gf-rebuild`. + +### Weitere + +| Funktion | Zweck | +|---|---| +| `gf-modus12-abschluss (... dialog-bestaetigt ...)` | Abschluss Modus 1/2 mit Zusammenfassung; fragt ohne `dialog-bestaetigt` = T per Konsole nach | +| `gf-init-bibliothek ()` | AS-/ES-Masse fuer die GF lesen | +| `gf-linienzug-modus ()` | Modus 3, interaktiv (Auswahl vorhandener LINE/ARC) | +| `gf-linienzug-einfuegen (segmente grade startframe)` | Bau-Kern von Modus 3, erwartet die von `gf-analysiere-kette` aufbereiteten Segmente | + +**Falle `*lib-initialized*`**: `gefaellestrecke-einfuegen` initialisiert die +GF-Masse nur, wenn `*lib-initialized*` nil ist - dasselbe Flag setzt +`init-bibliothek` des VarioFoerderers. Wer in einem Lauf erst VF und dann GF +baut, muss `gf-init-bibliothek` einmal selbst erzwingen (so +`mubea:ensure-gf-init` in `tests/test_mubea.lsp`), sonst rechnet die GF mit +den Vario-Massen und landet auf falscher Hoehe. + +--- + +## 5. Einzelbloecke: Separator, Scanner, BTMT, Eingang/Ausgang + +| Funktion | Zweck | +|---|---| +| `ils-insert-sensor (fname)` | Befehls-Kern Separator/Scanner: fuegt in einer Schleife ein, Punkt per `getpoint`, Drehung per `pause` - **nicht skriptfaehig** | +| `ils-insert-station (rohname)` | dasselbe fuer BTMT-Beladung, SC_Entladung, Eingang, Ausgang; setzt zusaetzlich den Layer aus dem `LAYER`-Attribut | +| `ssg-ils-block-laden (blockname)` | Blockdefinition `data/ils/_.dwg` einmal je Zeichnung laden; Rueckgabe: effektiver Blockname mit Suffix oder nil | +| `ssg-ils-block-datei (blockname)` | nur den Dateipfad aufloesen (3D-Fallback) | +| `ssg-block-layer-vorgabe` | `LAYER`-Attribut eines eingefuegten Blocks als Ziel-Layer anwenden | +| `ssg-id-generate (ent)` | eindeutige `ID` vergeben (nur wenn der Block ein ID-Attribut hat) | + +Skriptfaehig bauen (Muster aus `hundm05:bau-sensor`): + +```lisp +(setq eff (ssg-ils-block-laden "Scanner")) ; nicht per (and ...) verketten! +(command "_.INSERT" eff (list x y z) "" "" rotation) +(while (and (> (getvar "CMDACTIVE") 0) (< n 5)) (command "") (setq n (1+ n))) +(ssg-id-generate (entlast)) +``` + +`ATTREQ`/`ATTDIA` vorher auf 0 setzen. Restliche Prompts gezaehlt mit Enter +beantworten, nicht ungebremst: eine Rueckfrage, die Enter ablehnt, haelt +`CMDACTIVE` sonst dauerhaft > 0. + +--- + +## 6. Omniflo (`OmniModulInsert.lsp`) + +| Funktion | Zweck | +|---|---| +| `omni:insert-dxf (sivasnr-str hoehe drehung)` | `%DXFM_OMNIFLO%/.dxf` einfuegen, `HOEHE`/`DREHUNG` setzen, Layer. **Einfuegepunkt per Maus** (`pause`); Rueckgabe: Einfuegepunkt oder nil | +| `omni:insert-from-entry (dlg-result)` | Dialog-Ergebnis von Bogen/Weiche -> `omni:insert-dxf` | +| `omni:make-gerade (blockname pt pt2 srcAttribs textHeight textGap textEnt)` | **skriptfaehig**: Aluprofil-Gerade `"AP60"`/`"AP110"`/`"APG110"` von `pt` nach `pt2` als Compound-Block mit Text. `srcAttribs` per `omni:read-src-attribs`, `textEnt` nil. Kein `ssg-start` - Aufrufer setzt OSMODE/ATTREQ/ATTDIA = 0. Rueckgabe: Entity | +| `omni:insert-block (blockname laengemax)` | Befehls-Kern der Geraden (fragt `pt`/`pt2` per `getpoint`) | +| `omni:ladeschleifen-dialog (profiltyp init-hoehe)` | Dialog Ladeschleife, danach `omni:insert-dxf` | + +Skriptfaehig fuer Boegen/Weichen: `_.INSERT` der DXF direkt mit Punkt +`(x y hoehe)` und Drehung, danach `ssg-attrib-set-on` mit `HOEHE`/`DREHUNG` +(`omni:test-insert` in `tests/test_omniflo.lsp`). Die festen TEF-/Zubehoer- +Befehle (`OMNI_TEF_*`, `OMNI_STOPPER` ...) rufen `omni:insert-dxf` mit fester +Sivas-Nummer. `OMNI_TV_*` und `OMNI_APBW_*` sind Platzhalter ohne Bau-Logik. + +--- + +## 7. TEF-Foerderer-Linie (`TEFInsert.lsp`) + +| Funktion | Zweck | +|---|---| +| `c:TEF_FOERDERER_LINIE` | Start-/Endpunkt abfragen, Linie zeichnen, 1000-mm-TEF-Geraden (`0_B10030.dxf`) aneinanderreihen; das letzte Stueck wird zurueckversetzt, damit der Foerderer genau am Linienende endet | +| `tefl-insert-element (start richtung-einheit s hz winkel)` | ein Element an Position `s` (mm ab `start`) entlang der Linie, Rz(hz)*Ry(winkel) | +| `tefl-ensure-block-loaded (dxf-pfad)` | Blockdefinition einmal laden | + +--- + +## 8. Geruest (`GeruestInsert.lsp`) + +| Funktion | Zweck | +|---|---| +| `geruest-einfuegen (bg wahl)` | Befehls-Kern Saeulen/Rohrabhaenger/Wandbefestigung: baut aus der Dialog-Auswahl `wahl` = `(variante-index oben unten laenge farbe)` die Werte (ARTINR, BEZEICHNUNG, TEILEART, LAENGE, TEILEFARBE), fragt dann den Punkt per `getpoint` | +| `geruest-block-bauen (bg variante werte pt teile-dwgs)` | **skriptfaehiger Kern**: Dummy-DWG + Teile-DWGs bei `pt`, Block `GR__`. `bg` = Baugruppe aus `geruest.json`, `werte` = Attribut-Alist, `teile-dwgs` aus `geruest-teile-dwgs` | +| `geruest-traeger-einfuegen (bg wahl)` | Befehls-Kern Traeger: Schleife Startpunkt -> Endpunkt oder Laenge | +| `geruest-traeger-block-bauen (bg layer netto strebe strebe-pfad muffe-pfad werte pt winkel)` | **skriptfaehiger Kern** Traeger: LINE (0,0,0)-(netto,0,0), Streben, Muffe, Block bei `pt` mit `winkel` (Radiant). Rueckgabe: `(blockname . entity)` | +| `geruest-traeger-laenge`, `-start`, `-streben`, `-pruefen` | reine Berechnung (Netto-Laenge nach Abzuegen, Startpunkt, Strebenverteilung, min/max) - in `tests/test_unit.lsp` getestet | +| `geruest-traeger-beschriften (blockEnt)` | Beschriftungstext aus dem fertigen Block | +| `geruest-block-abschliessen (bg attribs werte lastEnt pt rot)` | gemeinsamer Schluss aller Geruest-Bloecke (ATTDEFs, Block bilden, einfuegen, ID) | + +Fuer Geruest gibt es noch keinen JSON-Treiber. + +--- + +## 9. Einfuegeprimitiven (`ssg_ks_insert.lsp`, `vf_etage.lsp`) + +Grundbausteine, auf denen VF, GF und Linienzug aufsetzen. Alle legen **KS_EIN** +des Blocks (nicht den Block-Ursprung) auf den Startpunkt und geben den +Weltpunkt von **KS_AUS** zurueck - damit lassen sich Bausteine verketten. + +| Funktion | Zweck | +|---|---| +| `ensure-block-loaded (name)` | Block einmal ins Dokument laden | +| `extract-ks-from-block (block-obj)` | KS_EIN/KS_AUS eines eingefuegten Blocks (VLA-Objekt) lesen, Cache `*ks-cache*` je Blockname | +| `insert-block-by-ks (blockname einfuegepunkt hz)` | Block nur um Z gedreht (AS/ES-Elemente, Stationen) | +| `insert-inclined-scaled-block (blockname startpunkt laenge winkel hz)` | Block in X auf `laenge` skaliert und um `winkel` geneigt, Rz(hz)*Ry(winkel) (Staustrecken) | +| `insert-rotated-block-with-ks (blockname startpunkt winkel block-dx block-dz hz)` | Block gedreht, `block-dx`/`block-dz` nur als Fallback ohne KS_AUS (Boegen) | +| `insert-hz-scaled-block-by-ks (blockname startpunkt laenge hz-winkel vert-winkel)` | waagrechter, auf `laenge` skalierter Block (`Staustrecke_SP_1000_mm`, Massstab `laenge/1000`); Kandidat fuer die noch fehlende waagrechte Foerderstrecke. Bricht bei fehlender Blockdatei mit `(exit)` ab - vorher mit `ssg-ils-block-laden` pruefen | +| `ssg-rot-matrix-zy (chz shz cv sv)` | 4x4-Rotationsmatrix Rz(hz)*Ry(vert) aus Cosinus/Sinus beider Winkel (`ssg_core.lsp`) | + +--- + +## 10. Gemeinsame Regeln fuer eigene Treiber + +- Systemvariablen ueber `ssg-start`/`ssg-end` sichern; `OSMODE` 0, + `ATTREQ` 0, `ATTDIA` 0 setzen, sonst fragt `_.INSERT` nach Attributen. +- Module vorher laden: `(ssg-ensure "KreiselInsert")`, `"VarioFoerderer"`, + `"Gefaellestrecke"`, `"SSG_LIB_Commands"`. +- Die Dimension folgt `ssg-ils-dim-aktuell`; fuer einen festen Lauf + `*ssg-ils-dim*` (bzw. `*vsp-dim-override*` beim Spec-Bau) setzen und danach + zuruecksetzen. +- Rueckgaben nicht mit `and`/`or` verketten - die liefern in AutoLISP nur + T/nil (siehe `CLAUDE.md`, Konventionen). +- Interaktive GUI abschalten: `ssg-gui-aus` bzw. `vsp-headless-an`. +- Vorbilder: `tests/test_mubea.lsp` (Kreisel, VF Standard/Etage, GF, + Einzelbloecke), `tests/test_hundm05.lsp` (Spec-Ketten, Kreisel, + Einzelbloecke), `tests/test_omniflo_strecke.lsp` (Omniflo). diff --git a/lang/de_DE.json b/lang/de_DE.json index e9b67f9..055680d 100644 --- a/lang/de_DE.json +++ b/lang/de_DE.json @@ -1003,13 +1003,14 @@ "gr-fehler-baugruppe": "\nGeruest: Baugruppe '%1' steht nicht in der Datendatei.", "gr-fehler-keine-varianten": "\nGeruest: Baugruppe '%1' hat keine Geometrie-Varianten.", "gr-fehler-dummy": "\nGeruest: Dummy-Baugruppe nicht gefunden: %1", + "gr-fehler-dummy-variante": "\nGeruest: Dummy der Variante nicht gefunden: %1 - verwende Dummy der Baugruppe (%2)", "gr-fehler-dcl": "Dialog geruest.dcl konnte nicht geladen werden.", "gr-dlg-kopf": "%1 konfigurieren", "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-alert-menge": "Ungueltige Stueckzahl. Erlaubt ist eine ganze Zahl ab 1; ein leeres Feld uebernimmt den Vorgabewert.", + "gr-alert-menge": "Ungueltige Anzahl. Erlaubt ist eine ganze Zahl ab 1; ein leeres Feld uebernimmt den Vorgabewert.", "gr-position-oben": "oben", "gr-position-unten": "unten", "gr-konfig-keine": "", @@ -1022,6 +1023,23 @@ "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." + "gr-fehler-keine-baugruppen": "\nGeruest: Die Datendatei enthaelt keine Baugruppen.", + "gr-tr-fehler-dcl": "Dialog %1 konnte nicht geladen werden.", + "gr-tr-hinweis": "Die Laenge wird nach OK in der Zeichnung bestimmt.", + "gr-tr-alert-abzug": "Laengenabzug ungueltig. Erlaubt ist eine Zahl ab 0 (mm); ein leeres Feld gilt als 0.", + "gr-tr-prompt-start": "\nStartpunkt des Traegers waehlen : ", + "gr-tr-prompt-ende": "\nEndpunkt waehlen oder Laenge (mm) eingeben: ", + "gr-tr-prompt-richtung": "\nRichtung angeben <0>: ", + "gr-tr-eingabe-ungueltig": "\nEingabe '%1' ist weder Punkt noch Laenge.", + "gr-tr-laenge-null": "\nStrecke %1 mm ist nicht laenger als die Abzuege (%2 mm) - kein Traeger eingefuegt.", + "gr-tr-laenge-bereich": "\nTraegerlaenge %1 mm ausserhalb des erlaubten Bereichs %2 bis %3 mm - kein Traeger eingefuegt.", + "gr-tr-fertig": "\nTraeger %1 mm eingefuegt (Block %2).", + "gr-tr-ende": "\n%1 Traeger eingefuegt.", + "gr-tr-bez-abzug-anfang": "Abzug Anfang %1 mm", + "gr-tr-bez-abzug-ende": "Abzug Ende %1 mm", + "gr-tr-beschr-stil-fehlt": "\nTextstil %1 fehlt in der Zeichnung - Beschriftung mit aktuellem Stil %2.", + "gr-tr-beschr-ausrichtung": "\nUnbekannte Ausrichtung %1 fuer die Traeger-Beschriftung - verwende BC.", + "gr-tr-beschr-ergebnis": "\n%1 Traeger beschriftet (davon %2 ohne bekannte Variante), %3 nicht beschriftbar, %4 verwaiste Beschriftungen entfernt.", + "gr-tr-beschr-keine": "\nKeine Rohr-Geruest-Traeger in der Zeichnung." } } diff --git a/lang/en_GB.json b/lang/en_GB.json index bea946d..2714170 100644 --- a/lang/en_GB.json +++ b/lang/en_GB.json @@ -1003,6 +1003,7 @@ "gr-fehler-baugruppe": "\nScaffold: assembly '%1' is not in the data file.", "gr-fehler-keine-varianten": "\nScaffold: assembly '%1' has no geometry variants.", "gr-fehler-dummy": "\nScaffold: dummy assembly not found: %1", + "gr-fehler-dummy-variante": "\nScaffold: dummy of the variant not found: %1 - using dummy of the assembly (%2)", "gr-fehler-dcl": "Dialog geruest.dcl could not be loaded.", "gr-dlg-kopf": "Configure %1", "gr-alert-laenge": "Invalid length: '%1' mm. Allowed: %2 to %3 mm.", @@ -1022,6 +1023,23 @@ "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." + "gr-fehler-keine-baugruppen": "\nScaffold: the data file contains no assemblies.", + "gr-tr-fehler-dcl": "Dialog %1 could not be loaded.", + "gr-tr-hinweis": "The length is defined in the drawing after OK.", + "gr-tr-alert-abzug": "Invalid length deduction. Allowed is a number from 0 (mm); an empty field counts as 0.", + "gr-tr-prompt-start": "\nSelect start point of the beam : ", + "gr-tr-prompt-ende": "\nSelect end point or enter length (mm): ", + "gr-tr-prompt-richtung": "\nSpecify direction <0>: ", + "gr-tr-eingabe-ungueltig": "\nInput '%1' is neither a point nor a length.", + "gr-tr-laenge-null": "\nDistance %1 mm is not longer than the deductions (%2 mm) - no beam inserted.", + "gr-tr-laenge-bereich": "\nBeam length %1 mm outside the allowed range %2 to %3 mm - no beam inserted.", + "gr-tr-fertig": "\nBeam %1 mm inserted (block %2).", + "gr-tr-ende": "\n%1 beam(s) inserted.", + "gr-tr-bez-abzug-anfang": "Deduction start %1 mm", + "gr-tr-bez-abzug-ende": "Deduction end %1 mm", + "gr-tr-beschr-stil-fehlt": "\nText style %1 missing in drawing - labelling with current style %2.", + "gr-tr-beschr-ausrichtung": "\nUnknown justification %1 for beam label - using BC.", + "gr-tr-beschr-ergebnis": "\n%1 beam(s) labelled (%2 without known variant), %3 not labellable, %4 orphaned label(s) removed.", + "gr-tr-beschr-keine": "\nNo pipe scaffold beams in the drawing." } } diff --git a/lib/dxf_scan_components.py b/lib/dxf_scan_components.py index 2cb37f2..043bd20 100644 --- a/lib/dxf_scan_components.py +++ b/lib/dxf_scan_components.py @@ -44,6 +44,7 @@ import struct import sys from ezdxf.lldxf.types import BINARY_DATA, INT16, INT32, INT64, DOUBLE, BYTES +from ezdxf.lldxf.types import cast_tag_value from ezdxf.math import Matrix44, OCS sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) @@ -62,6 +63,10 @@ MAX_PAAR_ABSTAND = 60000.0 # 10 mm deckt Rundungs- und Zeichenungenauigkeiten der Fremdzeichnung ab. GERADE_MAX_DELTA_H = 10.0 +# Projektkennung in den erzeugten IDs (Kreisel__01 ...); main() +# setzt sie aus --projekt bzw. dem Namen der DXF-Datei. +PROJEKT = "HundM05" + # Toleranz fuer die Dedup-Rundung (Nachkommastellen). RUND = 2 @@ -190,6 +195,48 @@ def iter_binary_tags(mm): yield code, value +def iter_ascii_tags(mm): + """Streamt (code, value)-Tags aus einem ASCII-DXF. + + Gegenstueck zu iter_binary_tags fuer Zeichnungen, die als Text-DXF + gespeichert wurden (z.B. Dematic_arvato.dxf). Die Werte werden ueber die + ezdxf-Typtabelle in float/int gewandelt, damit lese_inserts dieselben + Typen sieht wie beim binaeren Lesen. + """ + i = 0 + n = len(mm) + while i < n: + end = mm.find(b"\n", i) + if end < 0: + break + code_raw = mm[i:end].strip() + i = end + 1 + end = mm.find(b"\n", i) + if end < 0: + end = n + value_raw = mm[i:end].rstrip(b"\r") + i = end + 1 + if not code_raw: + continue + code = int(code_raw) + value = decode_str(value_raw) + if code == 0: + value = value.strip() + else: + try: + value = cast_tag_value(code, value) + except ValueError: + pass + yield code, value + + +def iter_tags(mm): + """Binaer- oder ASCII-DXF anhand der Kopfzeile erkennen.""" + if mm[:22] == b"AutoCAD Binary DXF\r\n\x1a\x00": + return iter_binary_tags(mm) + return iter_ascii_tags(mm) + + # --------------------------------------------------------------------------- # Familien-Erkennung (Blockname -> Bauteilfamilie) # --------------------------------------------------------------------------- @@ -197,8 +244,8 @@ def iter_binary_tags(mm): # mit Wildcards - die Blocknamen der Fremdzeichnungen enthalten Umlaute und # Sonderzeichen (z.B. "3D_Schraegfoerderer_Antrieb", "TEF_Bogen_90Grad_..."). FAMILIEN = [ - ("kreisel_an", ["AN8", "AN8_*"]), - ("kreisel_sp", ["SP8", "SP8_*"]), + ("kreisel_an", ["AN8", "AN8_*", "AN8 Pinband*"]), + ("kreisel_sp", ["SP8", "SP8_*", "SP8 Pinband*"]), ("foerderer_antrieb", ["3D_TEF_Antrieb*", "3D_FF_Antrieb*", "3D_Schr*rderer_Antrieb*", "Antrieb FF"]), ("foerderer_umlenk", ["3D_TEF_Umlenk*", "3D_FF_Umlenk*", @@ -443,7 +490,7 @@ def lese_inserts(dxf_path): fh.close() raise try: - for code, value in iter_binary_tags(mm): + for code, value in iter_tags(mm): if code == 0: # Ein ATTRIB endet immer am naechsten 0-Tag. Erst hier steht # sicher fest, dass Tag UND Wert gelesen sind - in einem ATTRIB @@ -496,6 +543,19 @@ def lese_inserts(dxf_path): block_merken(aktueller_block)["kinder"].add(value) elif aktuell == "ATTRIB": attrib_tag = value + elif aktuell == "CIRCLE" and aktueller_block and code in (10, 20, 30): + # Mittelpunkt des ERSTEN Kreises der Blockdefinition merken - + # bei AN8/SP8 ist das das Rad. Die Pinband-Varianten in + # Dematic_arvato.dxf haben ihr Rad ~15 m neben dem + # Einfuegepunkt; ohne diesen Wert laege der Kreisel dort. + # Ein zweiter Kreis beginnt mit Code 10, waehrend "kreis" schon + # existiert - ab da ist der erste fertig (Code 30 ist optional). + info = block_merken(aktueller_block) + if code == 10 and "kreis" in info: + info["kreis_fertig"] = True + if not info.get("kreis_fertig"): + kreis = info.setdefault("kreis", [0.0, 0.0, 0.0]) + kreis[{10: 0, 20: 1, 30: 2}[code]] = value elif aktuell == "BLOCK" and aktueller_block and code in (10, 20, 30): block_base[aktueller_block][{10: 0, 20: 1, 30: 2}[code]] = value elif aktuell == "INSERT" and rec is not None: @@ -630,6 +690,13 @@ def sammle_vorkommen(ms_inserts, block_inserts, block_base, max_tiefe=12, xachse = mat.transform((1.0, 0.0, 0.0)) - ursprung rot = math.degrees(math.atan2(xachse.y, xachse.x)) % 360.0 fam = familie_von(rec["name"]) + # Kreisel-Stationen: Position = Radmitte, nicht Einfuegepunkt + # (siehe "kreis" in lese_inserts). Die Rotation oben bleibt die + # des Inserts. + kreis = ((block_info or {}).get(rec["name"], {}).get("kreis") + if fam in ("kreisel_an", "kreisel_sp") else None) + if kreis: + ursprung = mat.transform(tuple(kreis)) if fam: treffer.append({ "familie": fam, @@ -866,6 +933,48 @@ def zwillinge_zusammenfuehren(treffer): # Paarbildung und Schema-Abbildung # --------------------------------------------------------------------------- +# Querversatz (mm), bis zu dem AN8 und SP8 noch auf derselben Kreisel-Achse +# liegen. Beide Stationen sitzen auf der Mittellinie des Kreisels; in +# Dematic_arvato.dxf liegen echte Paare auf 0,2 mm genau auf einer Achse. +KREISEL_MAX_QUER = 100.0 + + +def kreisel_paare_bilden(an_liste, sp_liste): + """AN8 und SP8 zu Kreiseln paaren. + + Global nach Abstand (kuerzestes Paar zuerst) statt AN8 fuer AN8 in + X-Reihenfolge: dort griff sich ein frueh sortierter AN8 die naechste noch + freie SP8 eines Nachbar-Kreisels, und alle folgenden Paare rutschten mit + (Dematic_arvato: ein 33,5-m-Kreisel wurde mit der SP8 eines 3,6-m-Kreisels + 19 m daneben gepaart). Zusaetzlich muessen beide auf einer gemeinsamen + Achse liegen (Querversatz <= KREISEL_MAX_QUER). + + Rueckgabe: (paare [(an, sp, abstand)] nach AN-Position sortiert, + AN8 ohne Partner, SP8 ohne Partner) + """ + kandidaten = [] + for ia, an in enumerate(an_liste): + for isp, sp in enumerate(sp_liste): + dx = abs(sp["x"] - an["x"]) + dy = abs(sp["y"] - an["y"]) + if min(dx, dy) > KREISEL_MAX_QUER: + continue + kandidaten.append((math.hypot(dx, dy), ia, isp)) + kandidaten.sort() + an_frei = set(range(len(an_liste))) + sp_frei = set(range(len(sp_liste))) + paare = [] + for dist, ia, isp in kandidaten: + if ia in an_frei and isp in sp_frei: + an_frei.discard(ia) + sp_frei.discard(isp) + paare.append((an_liste[ia], sp_liste[isp], dist)) + paare.sort(key=lambda p: (p[0]["x"], p[0]["y"])) + return (paare, + [an_liste[i] for i in sorted(an_frei)], + [sp_liste[i] for i in sorted(sp_frei)]) + + def paare_bilden(antriebe, umlenkungen): """Gegenseitig naechste Nachbarn paaren. @@ -978,7 +1087,7 @@ def foerderer_eintrag(a, u, dist_xy, nummer, gf_winkel_min): # Die echte Hoehe liefert der 3D-Zwilling (siehe Zusammenfuehrung). if a.get("darstellung") == "2D": return { - "test_id": "GERADE_HundM05_%02d" % nummer, + "test_id": "GERADE_%s_%02d" % (PROJEKT, nummer), "typ": "gerade", "darstellung": "2D", "laenge": round(dist_xy, RUND), @@ -993,7 +1102,7 @@ def foerderer_eintrag(a, u, dist_xy, nummer, gf_winkel_min): if abs(dz) < GERADE_MAX_DELTA_H: return { - "test_id": "GERADE_HundM05_%02d" % nummer, + "test_id": "GERADE_%s_%02d" % (PROJEKT, nummer), "typ": "gerade", "darstellung": a.get("darstellung", "?"), "laenge": round(dist_xy, RUND), @@ -1008,7 +1117,7 @@ def foerderer_eintrag(a, u, dist_xy, nummer, gf_winkel_min): if dz < 0 and neigung >= gf_winkel_min: return { - "test_id": "GF_HundM05_%02d" % nummer, + "test_id": "GF_%s_%02d" % (PROJEKT, nummer), "modus": 1, "darstellung": a.get("darstellung", "?"), "deltaL_mm": round(dist_xy, RUND), @@ -1025,7 +1134,7 @@ def foerderer_eintrag(a, u, dist_xy, nummer, gf_winkel_min): } return { - "test_id": "VF_HundM05_%02d" % nummer, + "test_id": "VF_%s_%02d" % (PROJEKT, nummer), "typ": "standard", "darstellung": a.get("darstellung", "?"), "richtung": "Auf" if dz > 0 else "Ab", @@ -1116,16 +1225,12 @@ def baue_eintraege(treffer, defaults, boegen, weichen, # --- Kreisel: AN8 (Antriebsstation) + SP8 (Spannstation) -------------- durchmesser = defaults.get("kreisel", {}).get("durchmesser", 800.0) - an_liste = sorted(nach_familie.get("kreisel_an", []), key=lambda t: (t["x"], t["y"])) - sp_liste = list(nach_familie.get("kreisel_sp", [])) - for i, an in enumerate(an_liste, start=1): - if not sp_liste: - hinweise.append("AN8 bei (%.2f, %.2f) ohne SP8-Partner" % (an["x"], an["y"])) - eintraege.append(sensor_eintrag(an, bekannte_bloecke, hinweise, treffer)) - continue - sp = min(sp_liste, key=lambda s: math.hypot(s["x"] - an["x"], s["y"] - an["y"])) - sp_liste.remove(sp) - dist = math.hypot(sp["x"] - an["x"], sp["y"] - an["y"]) + kreisel_paare, an_rest, sp_liste = kreisel_paare_bilden( + nach_familie.get("kreisel_an", []), nach_familie.get("kreisel_sp", [])) + for an in an_rest: + hinweise.append("AN8 bei (%.2f, %.2f) ohne SP8-Partner" % (an["x"], an["y"])) + eintraege.append(sensor_eintrag(an, bekannte_bloecke, hinweise, treffer)) + for i, (an, sp, dist) in enumerate(kreisel_paare, start=1): rotation = math.degrees(math.atan2(sp["y"] - an["y"], sp["x"] - an["x"])) % 360.0 # AN8/SP8 sind flache 2D-Symbole auf Z=0 - die Einbauhoehe steht nicht # im DXF. Sie wird aus den 3D-Modellen in der Umgebung abgeleitet @@ -1144,19 +1249,31 @@ def baue_eintraege(treffer, defaults, boegen, weichen, hoehe_hinweis = (" ACHTUNG hoehe=%.1f ist NICHT belegt: " "AN8/SP8 sind flache 2D-Symbole auf Z=0 und " "%s." % (hoehe, grund)) + # an/sp sind Radmitten (siehe "kreis" in lese_inserts). draw-module + # (KreiselInsert.lsp) legt AN8 bei (radius, 0) und SP8 bei + # (radius + abstand, 0) in den Block: abstand ist also der + # Mittenabstand, und der Einfuegepunkt liegt einen Radius VOR der + # AN8-Mitte. Die fruehere "connect"-Ausgabe uebergab die Radmitten an + # kreisel-connect-script, das Aussenkanten erwartet - der Kreisel + # wurde dadurch an beiden Enden um einen Radius zu kurz. + radius = durchmesser / 2.0 + richtung = math.radians(rotation) eintraege.append({ - "id": "Kreisel_HundM05_%02d" % i, - "function": "connect", - "start_x": an["x"], "start_y": an["y"], "start_z": an["z"], - "end_x": sp["x"], "end_y": sp["y"], "end_z": sp["z"], + "id": "Kreisel_%s_%02d" % (PROJEKT, i), + "function": "insert", + "x": round(an["x"] - radius * math.cos(richtung), RUND) + 0.0, + "y": round(an["y"] - radius * math.sin(richtung), RUND) + 0.0, + "z": hoehe, + "abstand": round(dist, RUND), + "rotation": round(rotation, RUND) + 0.0, "typ": "STANDARD", "darstellung": an.get("darstellung", "?"), - "hoehe": hoehe, - "comment": ("aus DXF: AN8 (Layer %s) -> SP8 (Layer %s), Abstand " - "%.2f; daraus folgt produktionsseitig abstand = %.2f - " - "%.1f = %.2f und rotation = %.2f Grad." - % (an["layer"], sp["layer"], dist, dist, durchmesser, - dist - durchmesser, rotation)) + hoehe_hinweis, + "comment": ("aus DXF: Radmitte %s (Layer %s) (%.2f, %.2f) -> " + "%s (Layer %s) (%.2f, %.2f), Mittenabstand %.2f, " + "rotation %.2f Grad; x/y = AN8-Mitte minus Radius." + % (an["block"], an["layer"], an["x"], an["y"], + sp["block"], sp["layer"], sp["x"], sp["y"], dist, + rotation)) + hoehe_hinweis, }) for sp in sp_liste: hinweise.append("SP8 bei (%.2f, %.2f) ohne AN8-Partner" % (sp["x"], sp["y"])) @@ -1188,7 +1305,7 @@ def baue_eintraege(treffer, defaults, boegen, weichen, hinweise.append("Omniflo %s ohne Sivasnr: '%s' (%s)" % (typ, t["block"], grund)) eintraege.append({ - "id": sivasnr or ("%s_HundM05_%02d" % (typ.upper(), i)), + "id": sivasnr or ("%s_%s_%02d" % (typ.upper(), PROJEKT, i)), "type": typ, "sivasnr": sivasnr, "x": t["x"], "y": t["y"], "z": t["z"], @@ -1346,7 +1463,7 @@ def main(): description="Erkennt ILS-/Omniflo-Komponenten in einem fremden DXF " "und schreibt sie im JSON-Schema aus " "tests/testdata/object_data.md.") - parser.add_argument("dxf", help="Quellzeichnung (binaeres DXF)") + parser.add_argument("dxf", help="Quellzeichnung (binaeres oder ASCII-DXF)") parser.add_argument("data_dir", help="Datenverzeichnis mit json/") parser.add_argument("output", help="Ziel-JSON") parser.add_argument("--fenster", nargs=4, type=float, @@ -1354,8 +1471,13 @@ def main(): help="Nur Bauteile in diesem XY-Fenster (mm) " "ausgeben - fuer den Nachbau eines einzelnen " "Anlagenabschnitts statt der ganzen Zeichnung") + parser.add_argument("--projekt", + help="Projektkennung in den erzeugten IDs " + "(Default: Name der DXF-Datei ohne Endung)") args = parser.parse_args() dxf_path, data_dir, output_path = args.dxf, args.data_dir, args.output + global PROJEKT + PROJEKT = args.projekt or os.path.splitext(os.path.basename(dxf_path))[0] fenster = None if args.fenster: x0, y0, x1, y1 = args.fenster diff --git a/lib/export_csv.py b/lib/export_csv.py index e6e98ed..295f88a 100644 --- a/lib/export_csv.py +++ b/lib/export_csv.py @@ -319,16 +319,25 @@ def build_geruest_merkmale(block): pattern_geruest in cfg/export.cfg, GeruestInsert.lsp *geruest-attrib-defs*). ARTINR/LAENGE/BEZEICHNUNG wandern in feste CSV-Spalten (teileid/ - bezeichnung ueben in process_blocks unten), TEILEART/FARBE/LAYER bleiben - als Rest-Attribute in der Merkmale-JSON-Spalte - so wie bei Separator/ - Scanner/BTMT bereits ueblich.""" + bezeichnung ueben in process_blocks unten), TEILEART/TEILEFARBE/LAYER + bleiben als Rest-Attribute in der Merkmale-JSON-Spalte - so wie bei + Separator/Scanner/BTMT bereits ueblich. + + Die Laengenabzuege gibt es nur beim Rohr-Geruest-Traeger + (*geruest-traeger-attrib-defs*) und erscheinen darum nur, wenn der Block + sie traegt - die Merkmale der anderen GR_-Bloecke bleiben unveraendert.""" attribs = block.get("attribs", {}) - return { + merkmale = { "TeileArt": attribs.get("TEILEART", ""), - "Farbe": attribs.get("FARBE", ""), + "Farbe": attribs.get("TEILEFARBE", ""), "Laenge": attribs.get("LAENGE", ""), "Layer": attribs.get("LAYER", ""), } + for tag, name in (("LAENGENABZUGANFANG", "LaengenabzugAnfang"), + ("LAENGENABZUGENDE", "LaengenabzugEnde")): + if tag in attribs: + merkmale[name] = attribs[tag] + return merkmale def format_sensor_id(prefix, n): diff --git a/menu/SSG_LIB.cui b/menu/SSG_LIB.cui index 8299faa..369b401 100644 --- a/menu/SSG_LIB.cui +++ b/menu/SSG_LIB.cui @@ -539,7 +539,7 @@ Rohr-Geruest-Traeger - ^C^C(ssg-ensure "GeruestInsert") GERUEST + ^C^C(ssg-ensure "GeruestInsert") GERUEST_TRAEGER @@ -560,211 +560,229 @@ ^C^C(ssg-ensure "GeruestInsert") GERUEST_WANDBEFESTIGUNG - + + + Gattertraeger + ^C^C(ssg-ensure "GeruestInsert") GERUEST + + + + + Traeger beschriften + ^C^C(ssg-ensure "GeruestInsert") GERUEST_TRAEGER_BESCHRIFTEN + + + Marker einfuegen ^C^C(ssg-ensure "Tro_Edit") TRO_INSERT - + Marker bearbeiten ^C^C(ssg-ensure "Tro_Edit") TRO_EDIT - + Bearbeiten (Auto) ^C^C(ssg-ensure "SSG_LIB_Commands") SSG_BLOCKEDIT - + Verbindung einfuegen ^C^C(ssg-ensure "Connection_Edit") CONNECTION_INSERT - + Verbindung bearbeiten ^C^C(ssg-ensure "Connection_Edit") CONNECTION_EDIT - + Eingang ^C^C(ssg-ensure "SSG_LIB_Commands") ILS_EINGANG_INSERT - + Ausgang ^C^C(ssg-ensure "SSG_LIB_Commands") ILS_AUSGANG_INSERT - + SPS Schliessen ^C^C - + Export Sivas ^C^C(ssg-ensure "export") EXPORTSIVAS - + Export CSV ^C^C(ssg-ensure "export") EXPORTCSV - + Umschalten 2D und 3D ^C^C(ssg-ensure "SSG_LIB_Commands") SSG_DIM_SWITCH - + Alle Bloecke auf 2D ^C^C(ssg-ensure "SSG_LIB_Commands") SSG_DIM_ALL_2D - + Alle Bloecke auf 3D ^C^C(ssg-ensure "SSG_LIB_Commands") SSG_DIM_ALL_3D - + Sprache ^C^C(ssg-ensure "SSG_LIB_Commands") SSG_SPRACHE - + Planquadrate an/aus ^C^C(ssg-ensure "DrawPlanquadrate") PLANQUADRATE_TOGGLE - + CSV-Export ^C^C(ssg-ensure "export") CSV_EXPORT_KONFIG - + alle Tests ausfuehren und exportieren ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_run_all.lsp")) (c:SSG_RUN_ALL_TESTS_EXPORT) - + Tests ausfuehren und stehen lassen ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_run_all.lsp")) (c:SSG_RUN_ALL_TESTS_OFFEN) - + Export CSV/Sivas ALL ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_export_all.lsp")) (c:TEST_EXPORT_ALL) - + Kreisel ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_kreisel.lsp")) (c:TEST_KREISEL) - + Foerderer ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_foerderer.lsp")) (c:TEST_FOERDERER) - + Foerderer-Linienzug ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_linienzug.lsp")) (c:TEST_LINIENZUG) (c:TEST_LINIENZUG_STUFE2) - + Omniflo ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_omniflo.lsp")) (c:TEST_OMNIFLO) - + Omniflo Strecke ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_omniflo_strecke.lsp")) (c:TEST_OMNIFLO_STRECKE) - + Gefaellestrecke ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_gefaellestrecke.lsp")) (c:TEST_GEFAELLESTRECKE) - + Mubea Gesamtmodell ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_mubea.lsp")) (c:TEST_MUBEA) - + HundM05 Kreisel-Abschnitt ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_hundm05.lsp")) (c:TEST_HUNDM05) - + + + Dematic/Arvato Flowpuffer (Vorlage) + ^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_dematic_arvato.lsp")) (c:TEST_DEMATIC_ARVATO) + + + KS 2D/3D pruefen (CSV) ^C^C(load (strcat (getenv "DXFMAKRO") "/Lisp/ks_pruefung.lsp")) KS_PRUEFEN - + Kreisel bearbeiten (ssg-ensure "KreiselInsert") KreiselEdit - + Omniflo Element bearbeiten (ssg-ensure "OmniModulInsert") OMNI_EDIT - + Gefaellestrecke bearbeiten (ssg-ensure "Gefaellestrecke") GEFAELLESTRECKE_EDIT - + VarioFoerderer bearbeiten (ssg-ensure "VarioFoerderer") VARIOFOERDERER_EDIT - + TRO-Marker bearbeiten (ssg-ensure "Tro_Edit") TRO_EDIT - + Verbindung bearbeiten (ssg-ensure "Connection_Edit") CONNECTION_EDIT - + SSG Bearbeiten (Auto) (ssg-ensure "SSG_LIB_Commands") SSG_BLOCKEDIT @@ -782,10 +800,10 @@ - - - - + + + + ILS @@ -1413,249 +1431,265 @@ Gattertraeger - + + + + + + + Funktionen + + Traeger beschriften + + - + SPS - - + + Eingang - + Ausgang - + SPS Schliessen - + - + TRO Marker einfuegen - + Marker bearbeiten - + Bearbeiten (Auto) - + - + Connections Verbindung einfuegen - + Verbindung bearbeiten - + Bearbeiten (Auto) - + - + Export Export Sivas - + Export CSV - - - - - - - Konfiguration - - Umschalten 2D und 3D + + + Konfiguration - Alle Bloecke auf 2D - - - - - - Alle Bloecke auf 3D + Umschalten 2D und 3D - Sprache + Alle Bloecke auf 2D - Planquadrate an/aus + Alle Bloecke auf 3D - CSV-Export + Sprache + + Planquadrate an/aus + + + + + + CSV-Export + + + + - + Tests alle Tests ausfuehren und exportieren - + Tests ausfuehren und stehen lassen - + Export CSV/Sivas ALL - + Kreisel - + Foerderer - + Foerderer-Linienzug - + Omniflo - + Omniflo Strecke - + Gefaellestrecke - + Mubea Gesamtmodell - + HundM05 Kreisel-Abschnitt - + + + + + Dematic/Arvato Flowpuffer (Vorlage) + + KS 2D/3D pruefen (CSV) - + - + POP501 SSG_LIB Edit Context Kreisel bearbeiten - + Omniflo Element bearbeiten - + Gefaellestrecke bearbeiten - + VarioFoerderer bearbeiten - + TRO-Marker bearbeiten - + Verbindung bearbeiten - + SSG Bearbeiten (Auto) - + diff --git a/menu/SSG_LIB.mnu b/menu/SSG_LIB.mnu index ac9369e..703b508 100644 --- a/menu/SSG_LIB.mnu +++ b/menu/SSG_LIB.mnu @@ -112,11 +112,13 @@ [<-~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]^C^C(ssg-ensure "GeruestInsert") GERUEST -[Rohr-Geruest-Traeger]^C^C(ssg-ensure "GeruestInsert") GERUEST +[Rohr-Geruest-Traeger]^C^C(ssg-ensure "GeruestInsert") GERUEST_TRAEGER [Saeulen]^C^C(ssg-ensure "GeruestInsert") GERUEST_SAEULEN [Rohrabhaenger]^C^C(ssg-ensure "GeruestInsert") GERUEST_ROHRABHAENGER [Wandbefestigung]^C^C(ssg-ensure "GeruestInsert") GERUEST_WANDBEFESTIGUNG -[<-Gattertraeger]^C^C(ssg-ensure "GeruestInsert") GERUEST +[Gattertraeger]^C^C(ssg-ensure "GeruestInsert") GERUEST +[->Funktionen] +[<-<-Traeger beschriften]^C^C(ssg-ensure "GeruestInsert") GERUEST_TRAEGER_BESCHRIFTEN [->SPS] [->TRO] [Marker einfuegen]^C^C(ssg-ensure "Tro_Edit") TRO_INSERT @@ -152,6 +154,7 @@ [Gefaellestrecke]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_gefaellestrecke.lsp")) (c:TEST_GEFAELLESTRECKE) [Mubea Gesamtmodell]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_mubea.lsp")) (c:TEST_MUBEA) [HundM05 Kreisel-Abschnitt]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_hundm05.lsp")) (c:TEST_HUNDM05) +[Dematic/Arvato Flowpuffer (Vorlage)]^C^C(load (strcat (getenv "DXFMAKRO") "/tests/test_dematic_arvato.lsp")) (c:TEST_DEMATIC_ARVATO) [<-KS 2D/3D pruefen (CSV)]^C^C(load (strcat (getenv "DXFMAKRO") "/Lisp/ks_pruefung.lsp")) KS_PRUEFEN ***DOUBLECLICK diff --git a/tests/alltests.json b/tests/alltests.json index dc98cb9..f427384 100644 --- a/tests/alltests.json +++ b/tests/alltests.json @@ -10,5 +10,6 @@ { "name": "gefaellestrecke", "save": "dwg", "module": "Gefaellestrecke" }, { "name": "mubea", "save": "dxf", "module": "KreiselInsert" }, { "name": "hundm05", "save": "dxf", "module": "VarioFoerderer" }, + { "name": "dematic_arvato", "save": "dxf", "module": "VarioFoerderer", "disabled": true }, { "name": "hm_recformat", "save": "dxf", "module": "VarioFoerderer", "disabled": true } ] diff --git a/tests/test_dematic_arvato.lsp b/tests/test_dematic_arvato.lsp new file mode 100644 index 0000000..7823b7a --- /dev/null +++ b/tests/test_dematic_arvato.lsp @@ -0,0 +1,138 @@ +;; ============================================================ +;; test_dematic_arvato.lsp - Nachbau des Abschnitts Flowpuffer/Kreisel der +;; Anlage Dematic/Arvato aus der Scanner-Vorlage. +;; +;; Datenquelle: tests/testdata/dematic_arvato.json, erzeugt mit +;; python lib/dxf_scan_components.py \ +;; C:/develop/sps_skel/data/Dematic_arvato.dxf data \ +;; tests/testdata/dematic_arvato.json --projekt Arvato \ +;; --fenster -3000 159000 49000 193000 +;; +;; Die Datei ist eine VORLAGE fuer den Nachbau, kein fertiger Testfall: +;; Hoehen fehlen (flaches 2D-Schema, alles auf Z=0), Seiten/Winkel einiger +;; Bloecke sind geraten (siehe "comment" je Eintrag). +;; +;; Eintragsarten (Schema von dxf_scan_components.py): +;; "function": "insert" ohne "block" -> Kreisel (hundm05:bau-kreisel -> +;; kreisel-insert-script; x/y = Einfuegepunkt +;; einen Radius vor der AN8-Radmitte, abstand = +;; Mittenabstand AN8-SP8) +;; "function": "insert" + "block" -> Einzelblock (hundm05:bau-sensor, +;; Fallback Separator_SP bei unbekanntem Block) +;; "typ": "gerade" -> waagrechter Foerderer Antrieb->Umlenkung - +;; dafuer gibt es noch keinen Bau-Pfad, wird +;; gemeldet und uebersprungen +;; +;; Keine eigene Bau-Logik: hundm05:bau-kreisel/-sensor aus +;; tests/test_hundm05.lsp wird mitgeladen (die Datei definiert nur Funktionen). +;; +;; Aufruf in BricsCAD: +;; (load (strcat (getenv "DXFMAKRO") "/tests/test_dematic_arvato.lsp")) +;; TEST_DEMATIC_ARVATO +;; ============================================================ + +(if (not (boundp '*dematic-arvato-records*)) (setq *dematic-arvato-records* nil)) + +;; Module und die Bau-Funktionen aus test_hundm05.lsp beim LADEN dieser Datei +;; holen, nicht erst in c:TEST_DEMATIC_ARVATO: in BricsCAD werden per load +;; definierte Funktionen erst nach Rueckkehr zum Top-Level sichtbar (siehe +;; tests/test_unit.scr). +(ssg-ensure "KreiselInsert") +(ssg-ensure "VarioFoerderer") +(ssg-ensure "SSG_LIB_Commands") +(if (null (car (atoms-family 1 '("HUNDM05:BAU-SENSOR")))) + (load (strcat (getenv "DXFMAKRO") "/tests/test_hundm05.lsp"))) + +(defun dematic_arvato:datei () + (strcat (getenv "DXFMAKRO") "/tests/testdata/dematic_arvato.json")) + +;; Foerderer "gerade": noch kein Bau-Pfad - nur melden. +(defun dematic_arvato:gerade-melden (obj / tid rec) + (setq tid (ssg-val obj "test_id")) + (princ (strcat "\n [GERADE] " (if tid tid "?") + " uebersprungen - waagrechter Foerderer noch nicht unterstuetzt")) + (setq rec (vsp-rec-neu (if tid tid "(ohne id)"))) + (setq rec (vsp-set rec "kind" "gerade")) + (vsp-set (vsp-set rec "status" "uebersprungen") "fehler_text" + "kein Bau-Pfad fuer typ gerade")) + +(defun dematic_arvato:bau-eintrag (obj / kind res) + (cond + ((and (= (ssg-val obj "function") "insert") (ssg-val obj "block")) + (setq kind "sensor") + (setq res (vl-catch-all-apply 'hundm05:bau-sensor (list obj)))) + ((= (ssg-val obj "function") "insert") + (setq kind "kreisel") + (setq res (vl-catch-all-apply 'hundm05:bau-kreisel (list obj)))) + ((= (ssg-val obj "typ") "gerade") + (setq kind "gerade") + (setq res (dematic_arvato:gerade-melden obj))) + (T + (setq kind "unbekannt") + (princ "\n WARNUNG: unbekannter Eintrag uebersprungen") + (setq res (vsp-set (vsp-rec-neu "(unbekannt)") "status" "uebersprungen")))) + (if (vl-catch-all-error-p res) + (progn + (princ (strcat "\n [" (strcase kind) "] Ausnahme: " + (vl-catch-all-error-message res))) + (vsp-set (vsp-set (vsp-set (vsp-rec-neu (cond ((ssg-val obj "id")) + ((ssg-val obj "block")) + ("?"))) + "kind" kind) "status" "abbruch") + "fehler_text" (vl-catch-all-error-message res))) + res)) + +(defun c:TEST_DEMATIC_ARVATO ( / datei daten out zaehler st) + (setq datei (dematic_arvato:datei)) + (princ "\n\n================================================================") + (princ "\n TEST_DEMATIC_ARVATO - Abschnitt Flowpuffer/Kreisel (Vorlage)") + (princ (strcat "\n " datei)) + (princ "\n================================================================") + (cond + ((null (car (atoms-family 1 '("VSP-REC-NEU")))) + (princ "\n[TEST_DEMATIC_ARVATO] FEHLER: vf_spec.lsp nicht geladen" + " (VarioFoerderer laden).") + (setq *dematic-arvato-records* nil)) + ((null (findfile datei)) + (princ (strcat "\n[TEST_DEMATIC_ARVATO] FEHLER: " datei " nicht gefunden!")) + (setq *dematic-arvato-records* nil)) + (T + (setq daten (ssg-json-laden datei)) + (princ (strcat "\n " (itoa (length daten)) " Eintraege geladen")) + (ssg-start "TEST_DEMATIC_ARVATO" + '(("OSMODE") ("CECOLOR") ("ATTREQ") ("ATTDIA"))) + (setvar "OSMODE" 0) + (setvar "ATTREQ" 0) + (setvar "ATTDIA" 0) + (setq out '()) + (foreach obj daten + (setq out (cons (dematic_arvato:bau-eintrag obj) out))) + (ssg-end) + (setq *dematic-arvato-records* (reverse out)) + ;; Zusammenfassung je Status + (setq zaehler '()) + (foreach r *dematic-arvato-records* + (setq st (cond ((ssg-val r "status")) ("?"))) + (if (assoc st zaehler) + (setq zaehler (subst (cons st (1+ (cdr (assoc st zaehler)))) + (assoc st zaehler) zaehler)) + (setq zaehler (cons (cons st 1) zaehler)))) + (princ "\n================================================================") + (foreach z (reverse zaehler) + (princ (strcat "\n " (car z) ": " (itoa (cdr z))))) + (princ "\n================================================================") + (command "_.ZOOM" "_E"))) + (princ)) + +;; Ergebnis-Export (Konvention :export-results fuer test_run_all.lsp) +(defun dematic_arvato:export-results (out-dir / pfad) + (if (null *dematic-arvato-records*) + (princ "\n Keine Dematic/Arvato-Ergebnisse vorhanden.") + (progn + (vl-mkdir out-dir) + (setq pfad (strcat out-dir "/dematic_arvato_results.json")) + (vsp-results-schreiben *dematic-arvato-records* pfad))) + (princ)) + +(princ "\nTEST_DEMATIC_ARVATO geladen. Aufruf: TEST_DEMATIC_ARVATO") +(princ) diff --git a/tests/test_hundm05.lsp b/tests/test_hundm05.lsp index e579570..4ca9f40 100644 --- a/tests/test_hundm05.lsp +++ b/tests/test_hundm05.lsp @@ -138,6 +138,10 @@ (defun hundm05:bau-sensor (obj / id blockname x y z rotation pt eff n vorher ent ed attribs rec) (setq id (ssg-val obj "id")) (setq blockname (ssg-val obj "block")) + ;; Eintraege aus lib/dxf_scan_components.py haben kein "id" - die + ;; Meldungen unten setzen id per strcat zusammen und brachen dann mit + ;; "bad argument type " ab. + (if (null id) (setq id (cond (blockname) ("(ohne id)")))) (setq x (ssg-val obj "x") y (ssg-val obj "y") z (ssg-val obj "z") diff --git a/tests/test_unit.lsp b/tests/test_unit.lsp index 8991c61..7c8c66e 100644 --- a/tests/test_unit.lsp +++ b/tests/test_unit.lsp @@ -755,7 +755,7 @@ (progn (setq _daten (ssg-json-laden _datei)) ;; Drei Bereiche: Rohrabhaenger, Saeulen (1 und 2 Zoll zusammengefuehrt), - ;; Wandbefestigung + ;; Wandbefestigung. Die Traeger liegen in geruest_traeger.json. (tu-eq "json/geruest-baugruppen" 3 (length _daten)) ;; Baugruppe ueber das ASCII-Feld "id" finden (aeusserer Schluessel ;; traegt Umlaute bzw. Anfuehrungszeichen) @@ -768,16 +768,21 @@ (progn (setq _var (ssg-json-get _bg "geometrie_varianten")) (setq _anb (ssg-json-get _bg "anbaugruppen")) - (tu-eq "json/geruest-varianten" 3 (length _var)) + (tu-eq "json/geruest-varianten" 4 (length _var)) (tu-eq "json/geruest-teileart" "Geruest Rohrabhaenger" (ssg-json-get _bg "teileart")) (tu-eq "json/geruest-dummy" "Rohrabhaenger_dummy.dwg" (ssg-json-get _bg "dummy")) - ;; Sivas-ID als Zahl geparst, Laengengrenze aus dem attribute-Block + ;; Sivas-ID als Zahl geparst, Laengengrenze aus dem attribute-Block. + ;; Die Keys unter "attribute" sind die Block-Attribut-Tags und darum + ;; GROSS geschrieben (ssg-json-get unterscheidet Gross/Klein). (tu-eq "json/geruest-sivas" 821006026 (ssg-json-get (car (ssg-json-get (car _var) "teile")) "sivas_id")) (tu-eq "json/geruest-max" 6000 - (ssg-json-get (ssg-json-get (ssg-json-get _bg "attribute") "Laenge") "max")) + (ssg-json-get (ssg-json-get (ssg-json-get _bg "attribute") "LAENGE") "max")) + (tu-eq "json/geruest-farboptionen" '("RAL7035" "RAL7030") + (ssg-json-get (ssg-json-get (ssg-json-get _bg "attribute") "TEILEFARBE") + "optionen")) ) ) ) @@ -819,24 +824,24 @@ (tu-eq "geruest-id-string/str" "0_B10071" (geruest-id-string "0_B10071")) (tu-eq "geruest-id-string/nil" nil (geruest-id-string nil)) - ;; Einzelteil mit menge 2 -> Artikelnummer zweimal (ARTINR-Regel) + ;; Einzelteil mit stueckzahl 2 -> Artikelnummer zweimal (ARTINR-Regel) (setq _eintrag (ssg-json-parse - "{\"artikel\": \"X\", \"sivas_id\": 825002019, \"menge\": 2}")) - (tu-eq "geruest-artnr/menge2" '("825002019" "825002019") + "{\"artikel\": \"X\", \"sivas_id\": 825002019, \"stueckzahl\": 2}")) + (tu-eq "geruest-artnr/stueckzahl2" '("825002019" "825002019") (geruest-artikelnummern _eintrag)) (tu-eq "geruest-name/artikel" "X" (geruest-eintrag-name _eintrag)) ;; Benannte Gruppe -> teile rekursiv aufloesen, Mengen beachten (setq _gruppe (ssg-json-parse (strcat "{\"name\": \"Schraubenset\", \"teile\": [" - "{\"artikel\": \"A\", \"sivas_id\": 111, \"menge\": 1}," - "{\"artikel\": \"B\", \"sivas_id\": 222, \"menge\": 2}]}"))) + "{\"artikel\": \"A\", \"sivas_id\": 111, \"stueckzahl\": 1}," + "{\"artikel\": \"B\", \"sivas_id\": 222, \"stueckzahl\": 2}]}"))) (tu-eq "geruest-artnr/gruppe" '("111" "222" "222") (geruest-artikelnummern _gruppe)) (tu-eq "geruest-name/gruppe" "Schraubenset" (geruest-eintrag-name _gruppe)) - ;; Fehlendes menge-Feld -> 1 - (tu-eq "geruest-artnr/ohne-menge" '("333") + ;; Fehlendes stueckzahl-Feld -> 1 + (tu-eq "geruest-artnr/ohne-stueckzahl" '("333") (geruest-artikelnummern (ssg-json-parse "{\"artikel\": \"C\", \"sivas_id\": 333}"))) @@ -854,21 +859,62 @@ (tu-eq "geruest-teile-dwgs/vorhanden" (list _pfad) (geruest-teile-dwgs '("825002019" "999999999" "825002019"))) ) + ;; --- Anzahl-Vorgabe (Dialogfeld) vs. stueckzahl (wirklich geschrieben) --- + (tu-eq "geruest-anzahl-vorgabe/gruppe" 2 + (geruest-anzahl-vorgabe (ssg-json-parse + "{\"name\": \"G\", \"anzahl_vorgabe\": 2, \"teile\": [{\"sivas_id\": 1, \"stueckzahl\": 1}]}"))) + ;; stueckzahl ist KEINE Vorgabe fuer das Dialogfeld + (tu-eq "geruest-anzahl-vorgabe/stueckzahl-zaehlt-nicht" 1 + (geruest-anzahl-vorgabe (ssg-json-parse "{\"sivas_id\": 1, \"stueckzahl\": 3}"))) + (tu-eq "geruest-anzahl-vorgabe/ohne" 1 + (geruest-anzahl-vorgabe (ssg-json-parse "{\"name\": \"G\", \"teile\": []}"))) + ;; anzahl_vorgabe bestimmt nur die Vorbelegung, nicht den Satzinhalt: + ;; 3 Saetze a (1 Schraube + 1 Duebel) = 6 Nummern + (tu-eq "geruest-artikelnummern-n/anzahl-vorgabe" '("1" "2" "1" "2" "1" "2") + (geruest-artikelnummern-n (ssg-json-parse + "{\"name\": \"G\", \"anzahl_vorgabe\": 2, \"teile\": [{\"sivas_id\": 1, \"stueckzahl\": 1}, {\"sivas_id\": 2, \"stueckzahl\": 1}]}") + 3)) + ;; Einzelteil-Anbauteil: Anzahl x stueckzahl + (tu-eq "geruest-artikelnummern-n/einzelteil-stueckzahl" '("7" "7" "7" "7") + (geruest-artikelnummern-n (ssg-json-parse + "{\"artikel\": \"X\", \"sivas_id\": 7, \"stueckzahl\": 2, \"anzahl_vorgabe\": 5}") + 2)) + ;; Dummy, der zugleich Teile-DWG ist, faellt aus der Teileliste + (tu-eq "geruest-ohne-pfad/entfernt" '("b/2.dwg") + (geruest-ohne-pfad "A/1.DWG" '("a/1.dwg" "b/2.dwg"))) + (tu-eq "geruest-ohne-pfad/nil" '("a/1.dwg") + (geruest-ohne-pfad nil '("a/1.dwg"))) + ;; Dummy-Wahl: Variante gewinnt, fehlende Varianten-DWG -> Baugruppe + (setq _pfad (geruest-dummy-pfad "821006036.dwg")) + (if (and _pfad (findfile _pfad)) + (tu-eq "geruest-dummy-wahl/variante" "821006036.dwg" + (car (geruest-dummy-wahl + (ssg-json-parse "{\"dummy\": \"821006036.dwg\"}") + (ssg-json-parse "{\"dummy\": \"basis.dwg\"}")))) + ) + (tu-eq "geruest-dummy-wahl/fehlt" "basis.dwg" + (car (geruest-dummy-wahl + (ssg-json-parse "{\"dummy\": \"gibtsnicht_999.dwg\"}") + (ssg-json-parse "{\"dummy\": \"basis.dwg\"}")))) + (tu-eq "geruest-dummy-wahl/ohne" "basis.dwg" + (car (geruest-dummy-wahl + (ssg-json-parse "{\"name\": \"X\"}") + (ssg-json-parse "{\"dummy\": \"basis.dwg\"}")))) ;; --- 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)) + (tu-eq "geruest-anzahl-wert/leer" 2 (geruest-anzahl-wert "" 2)) + (tu-eq "geruest-anzahl-wert/zahl" 5 (geruest-anzahl-wert "5" 2)) + (tu-eq "geruest-anzahl-wert/null" nil (geruest-anzahl-wert "0" 2)) + (tu-eq "geruest-anzahl-wert/text" nil (geruest-anzahl-wert "abc" 2)) + (tu-eq "geruest-anzahl-wert/negativ" nil (geruest-anzahl-wert "-3" 2)) - ;; --- Stueckzahl ersetzt die JSON-Menge (Einzelteil) --- - (tu-eq "geruest-artnr-n/einzelteil" '("825002019" "825002019" "825002019") - (geruest-artikelnummern-n _eintrag 3)) + ;; --- Einzelteil: Anzahl (Dialog) x stueckzahl (JSON, hier 2) --- + (tu-eq "geruest-artnr-n/einzelteil" 6 + (length (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)) @@ -889,7 +935,7 @@ (setq _teile (ssg-json-parse (strcat "{\"name\": \"Set\", \"farbig\": true, \"teile\": [" "{\"artikel\": \"A\", \"sivas_id\": 111}," - "{\"artikel\": \"B\", \"sivas_id\": 222, \"menge\": 2}]}"))) + "{\"artikel\": \"B\", \"sivas_id\": 222, \"stueckzahl\": 2}]}"))) (tu-eq "geruest-paare/gruppe-erbt" '(("111" . T) ("222" . T) ("222" . T)) (geruest-teile-paare _teile nil)) (tu-eq "geruest-paare-n/gruppe" 6 (length (geruest-teile-paare-n _teile 2))) @@ -929,8 +975,8 @@ ;; die ganze Konfiguration verworfen) (setq _konfig (ssg-json-parse (strcat "{\"name\": \"K1\", \"oben\": [" - "{\"artikel\": \"Schraubenset\", \"menge\": 3}," - "{\"artikel\": \"unbekannt\", \"menge\": 1}]}"))) + "{\"artikel\": \"Schraubenset\", \"anzahl\": 3}," + "{\"artikel\": \"unbekannt\", \"anzahl\": 1}]}"))) (tu-eq "geruest-konfig-position" '(nil 3) (geruest-konfig-position _konfig "oben" _teile)) @@ -978,6 +1024,248 @@ (geruest-variante-wert (nth _rec (ssg-json-get _saeulen "geometrie_varianten")) _saeulen "teileart"))) + ;; Attribut-Keys GROSS - sonst findet geruest-dialog min/max und + ;; Farben nicht (Laengenfeld ohne Pruefung, leere Farbliste). + (tu-eq "geruest/saeulen-laenge-max" 6000 + (geruest-attr-feld nil _saeulen "LAENGE" "max")) + (tu-true "geruest/saeulen-farboptionen" + (geruest-attr-feld nil _saeulen "TEILEFARBE" "optionen")) + ) + ) + + ;; --- Rohr-Geruest-Traeger: reine Berechnung --- + ;; Netto-Laenge = Strecke minus beide Abzuege, nil wenn nichts uebrig + (tu-eqf "geruest-traeger-laenge/ende" 965.0 (geruest-traeger-laenge 1000.0 0.0 35.0)) + (tu-eqf "geruest-traeger-laenge/beide" 945.0 (geruest-traeger-laenge 1000.0 20.0 35.0)) + (tu-eqf "geruest-traeger-laenge/ohne" 1000.0 (geruest-traeger-laenge 1000.0 nil nil)) + (tu-nil "geruest-traeger-laenge/zu-kurz" (geruest-traeger-laenge 50.0 30.0 30.0)) + (tu-nil "geruest-traeger-laenge/genau-null" (geruest-traeger-laenge 60.0 30.0 30.0)) + ;; Startpunkt wird um den Anfangsabzug entlang der Richtung eingerueckt, + ;; Z bleibt die des Startklicks + (tu-eqf "geruest-traeger-start/0grad" '(20.0 0.0 500.0) + (geruest-traeger-start '(0.0 0.0 500.0) 0.0 20.0)) + (tu-eqf "geruest-traeger-start/90grad" '(100.0 220.0 0.0) + (geruest-traeger-start '(100.0 200.0 0.0) (/ pi 2.0) 20.0)) + (tu-eqf "geruest-traeger-start/2d-punkt" '(5.0 0.0 0.0) + (geruest-traeger-start '(5.0 0.0) 0.0 0.0)) + ;; Querstreben: Abstand max. 500, Rand 100..250 beidseitig gleich, + ;; so wenige wie moeglich + ;; Abstand genau 500, solange der Rand in 100..250 bleibt + (tu-eqf "geruest-traeger-streben/1200" '(100.0 600.0 1100.0) + (geruest-traeger-streben 1200.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-streben/1300" '(150.0 650.0 1150.0) + (geruest-traeger-streben 1300.0 500.0 100.0 250.0)) + ;; 1100 geht mit genau 500 nicht auf -> Rand 100, Abstand 450 + (tu-eqf "geruest-traeger-streben/1100" '(100.0 550.0 1000.0) + (geruest-traeger-streben 1100.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-streben/1500" '(250.0 750.0 1250.0) + (geruest-traeger-streben 1500.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-streben/1000" '(250.0 750.0) + (geruest-traeger-streben 1000.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-streben/eine" '(200.0) + (geruest-traeger-streben 400.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-streben/grenze-500" '(250.0) + (geruest-traeger-streben 500.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-streben/501" '(100.0 401.0) + (geruest-traeger-streben 501.0 500.0 100.0 250.0)) + (tu-nil "geruest-traeger-streben/zu-kurz" + (geruest-traeger-streben 150.0 500.0 100.0 250.0)) + (tu-eqf "geruest-traeger-streben/min-200" '(100.0) + (geruest-traeger-streben 200.0 500.0 100.0 250.0)) + ;; 6000 mm: 11 Felder a 500 -> 12 Streben, letzte bei 5750 + (setq _rec (geruest-traeger-streben 6000.0 500.0 100.0 250.0)) + (tu-eq "geruest-traeger-streben/6000-anzahl" 12 (length _rec)) + (tu-eqf "geruest-traeger-streben/6000-letzte" 5750.0 (last _rec)) + ;; Konfiguration: fehlende Felder -> Standard, ohne querstrebe -> nil + ;; Ergebnis (dummy laenge querlinie abstand-max rand-min rand-max) + (tu-eqf "geruest-traeger-strebe-cfg/standard" '(nil 30.0 25.0 500.0 100.0 250.0) + (geruest-traeger-strebe-cfg nil + (ssg-json-parse "{\"querstrebe\": {\"rand_min\": 100}}"))) + (tu-eqf "geruest-traeger-strebe-cfg/dummy" '("s.dwg" 30.0 25.0 500.0 100.0 250.0) + (geruest-traeger-strebe-cfg nil + (ssg-json-parse "{\"querstrebe\": {\"dummy\": \"s.dwg\"}}"))) + (tu-eqf "geruest-traeger-strebe-cfg/wert" '(nil 30.0 25.0 400.0 100.0 250.0) + (geruest-traeger-strebe-cfg nil + (ssg-json-parse "{\"querstrebe\": {\"abstand_max\": 400}}"))) + ;; Querlinien an Anfang und Ende der 30 mm langen Strebe + (tu-eqf "geruest-traeger-strebe-enden" '(235.0 265.0) + (geruest-traeger-strebe-enden 250.0 30.0)) + (tu-nil "geruest-traeger-strebe-cfg/ohne" + (geruest-traeger-strebe-cfg nil (ssg-json-parse "{\"id\": \"X\"}"))) + ;; Muffe nur bei "mit_muffe": true; Variante darf eigene Muffe mitbringen + (setq _bgtest (ssg-json-parse "{\"muffe\": {\"dummy\": \"m.dwg\"}}")) + (tu-eq "geruest-traeger-muffe-dummy/flag" "m.dwg" + (geruest-traeger-muffe-dummy + (ssg-json-parse "{\"name\": \"A\", \"mit_muffe\": true}") _bgtest)) + (tu-nil "geruest-traeger-muffe-dummy/ohne-flag" + (geruest-traeger-muffe-dummy + (ssg-json-parse "{\"name\": \"Rohr mit Muffe\"}") _bgtest)) + (tu-eq "geruest-traeger-muffe-dummy/variante" "m2.dwg" + (geruest-traeger-muffe-dummy + (ssg-json-parse + "{\"mit_muffe\": true, \"muffe\": {\"dummy\": \"m2.dwg\"}}") + _bgtest)) + (tu-nil "geruest-traeger-muffe-dummy/ohne-variante" + (geruest-traeger-muffe-dummy nil _bgtest)) + + ;; --- Traeger-Beschriftung: reine Berechnung --- + ;; Textwinkel entlang des Traegers, DIN: 90 < w <= 270 wird gedreht + (tu-eqf "geruest-text-winkel/0" 0.0 (geruest-text-winkel 0.0)) + (tu-eqf "geruest-text-winkel/45" (/ pi 4.0) (geruest-text-winkel (/ pi 4.0))) + (tu-eqf "geruest-text-winkel/90" (/ pi 2.0) (geruest-text-winkel (/ pi 2.0))) + (tu-eqf "geruest-text-winkel/91" (* 271 (/ pi 180.0)) + (geruest-text-winkel (* 91 (/ pi 180.0)))) + (tu-eqf "geruest-text-winkel/180" 0.0 (geruest-text-winkel pi)) + (tu-eqf "geruest-text-winkel/270" (/ pi 2.0) (geruest-text-winkel (* 1.5 pi))) + (tu-eqf "geruest-text-winkel/271" (* 271 (/ pi 180.0)) + (geruest-text-winkel (* 271 (/ pi 180.0)))) + ;; -90 = 270 -> gedreht auf 90 (von unten nach oben lesbar) + (tu-eqf "geruest-text-winkel/-90" (/ pi 2.0) (geruest-text-winkel (- (/ pi 2.0)))) + ;; Ausrichtungs-Codes wie TEXT "Ausrichten" -> (dxf72 dxf73) + (tu-eq "geruest-ausrichtung/TL" '(0 3) (geruest-ausrichtung-codes "TL")) + (tu-eq "geruest-ausrichtung/TC" '(1 3) (geruest-ausrichtung-codes "TC")) + (tu-eq "geruest-ausrichtung/TR" '(2 3) (geruest-ausrichtung-codes "TR")) + (tu-eq "geruest-ausrichtung/ML" '(0 2) (geruest-ausrichtung-codes "ML")) + (tu-eq "geruest-ausrichtung/MC" '(1 2) (geruest-ausrichtung-codes "MC")) + (tu-eq "geruest-ausrichtung/MR" '(2 2) (geruest-ausrichtung-codes "MR")) + (tu-eq "geruest-ausrichtung/BL" '(0 1) (geruest-ausrichtung-codes "BL")) + (tu-eq "geruest-ausrichtung/BC" '(1 1) (geruest-ausrichtung-codes "BC")) + (tu-eq "geruest-ausrichtung/BR" '(2 1) (geruest-ausrichtung-codes "BR")) + (tu-eq "geruest-ausrichtung/klein" '(1 1) (geruest-ausrichtung-codes "bc")) + (tu-nil "geruest-ausrichtung/unbekannt" (geruest-ausrichtung-codes "XY")) + (tu-nil "geruest-ausrichtung/nil" (geruest-ausrichtung-codes nil)) + ;; Textlage: Bezugspunkt auf der Linie, "links" in Leserichtung + (tu-eqf "geruest-text-lage/0-BC" '((1000.0 0.0 0.0) 0.0 1 1) + (geruest-traeger-text-lage '(0.0 0.0 0.0) 0.0 2000.0 "BC")) + (tu-eqf "geruest-text-lage/0-BL" '((0.0 0.0 0.0) 0.0 0 1) + (geruest-traeger-text-lage '(0.0 0.0 0.0) 0.0 2000.0 "BL")) + (tu-eqf "geruest-text-lage/0-BR" '((2000.0 0.0 0.0) 0.0 2 1) + (geruest-traeger-text-lage '(0.0 0.0 0.0) 0.0 2000.0 "BR")) + ;; Traeger nach links (180): Text gedreht auf 0, links = Linienende + (tu-eqf "geruest-text-lage/180-BL" '((-2000.0 0.0 0.0) 0.0 0 1) + (geruest-traeger-text-lage '(0.0 0.0 0.0) pi 2000.0 "BL")) + (tu-eqf "geruest-text-lage/180-BR" '((0.0 0.0 0.0) 0.0 2 1) + (geruest-traeger-text-lage '(0.0 0.0 0.0) pi 2000.0 "BR")) + (tu-eqf "geruest-text-lage/180-BC" '((-1000.0 0.0 0.0) 0.0 1 1) + (geruest-traeger-text-lage '(0.0 0.0 0.0) pi 2000.0 "BC")) + ;; Unbekannter Code -> BC + (tu-eqf "geruest-text-lage/ersatz" '((1000.0 0.0 0.0) 0.0 1 1) + (geruest-traeger-text-lage '(0.0 0.0 0.0) 0.0 2000.0 "XY")) + ;; Text: %1 = Variantentext, %2 = Laenge auf ganze mm + (tu-eq "geruest-text-string/beispiel" "RT 80/80 L=2650" + (geruest-traeger-text-string "%1 L=%2" "RT 80/80" 2649.5)) + (tu-eq "geruest-text-string/abrunden" "L=965 RT" + (geruest-traeger-text-string "L=%2 %1" "RT" 965.4)) + (tu-eq "geruest-text-string/mehrfach" "A A 10" + (geruest-traeger-text-string "%1 %1 %2" "A" 10.0)) + ;; Konfiguration je Feld: Variante vor Baugruppe vor Code-Standard + (setq _bgtest (ssg-json-parse + (strcat "{\"beschriftung\": {\"format\": \"%1 L=%2\", \"stil\": \"S1\"," + " \"ausrichtung\": \"BC\", \"hoehe\": 50, \"layer\": \"D-5-BEM\"}}"))) + (setq _rec (geruest-traeger-beschr-cfg + (ssg-json-parse + "{\"name\": \"RT 80/80 1 Zoll\", \"beschriftung\": {\"text\": \"RT 80/80\", \"layer\": \"D-6-BEM\"}}") + _bgtest "ERSATZ")) + (tu-eq "geruest-beschr-cfg/text" "RT 80/80" (cdr (assoc "text" _rec))) + (tu-eq "geruest-beschr-cfg/layer" "D-6-BEM" (cdr (assoc "layer" _rec))) + (tu-eq "geruest-beschr-cfg/stil" "S1" (cdr (assoc "stil" _rec))) + (tu-eqf "geruest-beschr-cfg/hoehe" 50.0 (cdr (assoc "hoehe" _rec))) + (setq _rec (geruest-traeger-beschr-cfg + (ssg-json-parse "{\"name\": \"Ohne Text\"}") _bgtest "ERSATZ")) + (tu-eq "geruest-beschr-cfg/text-name" "Ohne Text" (cdr (assoc "text" _rec))) + (tu-eq "geruest-beschr-cfg/layer-bg" "D-5-BEM" (cdr (assoc "layer" _rec))) + (setq _rec (geruest-traeger-beschr-cfg nil (ssg-json-parse "{\"id\": \"X\"}") "ERSATZ")) + (tu-eq "geruest-beschr-cfg/ersatz" "ERSATZ" (cdr (assoc "text" _rec))) + (tu-eq "geruest-beschr-cfg/std-format" "%1 L=%2" (cdr (assoc "format" _rec))) + (tu-eq "geruest-beschr-cfg/std-ausr" "BC" (cdr (assoc "ausrichtung" _rec))) + (tu-eqf "geruest-beschr-cfg/std-hoehe" 50.0 (cdr (assoc "hoehe" _rec))) + (tu-nil "geruest-beschr-cfg/std-layer" (cdr (assoc "layer" _rec))) + ;; Bereichspruefung auf der Netto-Laenge + (tu-true "geruest-traeger-pruefen/innen" (geruest-traeger-pruefen 965.0 0 6000)) + (tu-nil "geruest-traeger-pruefen/ueber" (geruest-traeger-pruefen 6001.0 0 6000)) + (tu-true "geruest-traeger-pruefen/grenze" (geruest-traeger-pruefen 6000.0 0 6000)) + (tu-true "geruest-traeger-pruefen/ohne" (geruest-traeger-pruefen 99999.0 nil nil)) + (tu-nil "geruest-traeger-pruefen/nil" (geruest-traeger-pruefen nil 0 6000)) + ;; Eingaben in mm: Komma wie Punkt, negativ/Text ungueltig + (tu-eqf "geruest-mm-zahl/ganz" 35.0 (geruest-mm-zahl "35")) + (tu-eqf "geruest-mm-zahl/komma" 17.5 (geruest-mm-zahl "17,5")) + (tu-nil "geruest-mm-zahl/text" (geruest-mm-zahl "abc")) + (tu-nil "geruest-mm-zahl/negativ" (geruest-mm-zahl "-5")) + (tu-nil "geruest-mm-zahl/leer" (geruest-mm-zahl "")) + (tu-eqf "geruest-abzug-wert/leer" 0.0 (geruest-abzug-wert "")) + (tu-nil "geruest-abzug-wert/text" (geruest-abzug-wert "x")) + (tu-eqf "geruest-tr-abzug/inaktiv" 0.0 (geruest-tr-abzug "0" "x")) + (tu-eqf "geruest-tr-abzug/aktiv" 35.0 (geruest-tr-abzug "1" "35")) + (tu-eq "geruest-mm-string/ganz" "965" (geruest-mm-string 965.0)) + (tu-eq "geruest-mm-string/dezimal" "17.5" (geruest-mm-string 17.5)) + ;; Variante darf Attribut-Felder ueberschreiben - auch mit false + (setq _bgtest (ssg-json-parse + "{\"attribute\": {\"LAENGENABZUGENDE\": {\"vorgabe\": 35, \"aktiv\": true}}}")) + (tu-eq "geruest-attr-feld/baugruppe" 35 + (geruest-attr-feld nil _bgtest "LAENGENABZUGENDE" "vorgabe")) + (tu-eq "geruest-attr-feld/variante" 50 + (geruest-attr-feld + (ssg-json-parse "{\"attribute\": {\"LAENGENABZUGENDE\": {\"vorgabe\": 50}}}") + _bgtest "LAENGENABZUGENDE" "vorgabe")) + (tu-nil "geruest-attr-feld/variante-false" + (geruest-attr-feld + (ssg-json-parse "{\"attribute\": {\"LAENGENABZUGENDE\": {\"aktiv\": false}}}") + _bgtest "LAENGENABZUGENDE" "aktiv")) + + ;; --- Rohr-Geruest-Traeger in der echten Datendatei --- + ;; Eigene Datei data/json/geruest_traeger.json: im Bereichs-Dropdown von + ;; GERUEST darf er nicht auftauchen, ueber geruest-traeger-daten muss er + ;; auffindbar sein. + (setq _rec nil) + (foreach _e (geruest-baugruppen-liste) + (if (equal (car _e) "Traeger") (setq _rec T))) + (tu-nil "geruest/traeger-nicht-im-dropdown" _rec) + (tu-true "geruest/traeger-datei" + (findfile (geruest-json-pfad "geruest_traeger.json"))) + (setq _traeger (geruest-baugruppe "Traeger" (geruest-traeger-daten))) + (tu-true "geruest/traeger-vorhanden" _traeger) + (tu-nil "geruest/traeger-nicht-in-geruest-json" + (geruest-baugruppe "Traeger" (geruest-daten))) + (if _traeger + (progn + (tu-eq "geruest/traeger-varianten-artikel" 0 + (tu-geruest-artikel-unvollstaendig + (ssg-json-get _traeger "geometrie_varianten"))) + (tu-true "geruest/traeger-layer" + (geruest-variante-wert + (car (ssg-json-get _traeger "geometrie_varianten")) + _traeger "layer")) + (tu-eq "geruest/traeger-max" 6000 + (geruest-attr-feld nil _traeger "LAENGE" "max")) + (tu-eqf "geruest/traeger-querstrebe" + '("dummy_Strebe_30x4.dwg" 30.0 25.0 500.0 100.0 250.0) + (geruest-traeger-strebe-cfg nil _traeger)) + ;; Muffen-Flag ausdruecklich je Variante: genau die "mit (2) Muffe(n)" + (setq _rec 0) + (foreach _v (ssg-json-get _traeger "geometrie_varianten") + (if (geruest-traeger-muffe-dummy _v _traeger) (setq _rec (1+ _rec)))) + (tu-eq "geruest/traeger-muffen-varianten" 4 _rec) + ;; Jede Variante definiert Beschriftungstext UND -layer selbst - der + ;; Layer ist je Traegervariante verschieden, eine Vorgabe der + ;; Baugruppe gibt es dafuer bewusst nicht. + (setq _rec 0) + (foreach _v (ssg-json-get _traeger "geometrie_varianten") + (setq _cfg (geruest-traeger-beschr-cfg _v _traeger "")) + (if (and (geruest-obj-feld _v _traeger '("beschriftung") "text") + (/= (cdr (assoc "text" _cfg)) "") + (geruest-json-pfad-get _v '("beschriftung" "layer")) + (geruest-ausrichtung-codes (cdr (assoc "ausrichtung" _cfg)))) + (setq _rec (1+ _rec)))) + (tu-eq "geruest/traeger-beschriftung-varianten" + (length (ssg-json-get _traeger "geometrie_varianten")) _rec) + ;; VARIANTE gehoert zum Traeger-Schema (Grundlage fuer das Neubeschriften) + (tu-true "geruest/traeger-attrib-variante" + (assoc "VARIANTE" *geruest-traeger-attrib-defs*)) + ;; Beide Dummy-DWGs liegen unter data/geruest + (tu-true "geruest/traeger-strebe-dwg" + (findfile (geruest-dummy-pfad "dummy_Strebe_30x4.dwg"))) + (tu-true "geruest/traeger-muffe-dwg" + (findfile (geruest-dummy-pfad "dummy_Muffe_1zoll.dwg"))) ) ) @@ -992,7 +1280,7 @@ (cons "laenge" "2000") (cons "teilefarbe" "RAL 7035 Lichtgrau") (cons "oben" (list (list (cons "artikel" "ROHRKLAMMER 1\" KPL. VERTIKAL") - (cons "menge" 2)))) + (cons "anzahl" 2)))) (cons "unten" nil))) (setq _pfad (vl-filename-mktemp "grtest" nil ".json")) (setq _f (open _pfad "w")) @@ -1012,8 +1300,8 @@ (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/oben-anzahl" 2 + (ssg-json-get (car (ssg-json-get _zurueck "oben")) "anzahl")) (tu-eq "geruest-schreiber/unten-leer" nil (ssg-json-get _zurueck "unten")) ) diff --git a/tests/testdata/dematic_arvato.json b/tests/testdata/dematic_arvato.json new file mode 100644 index 0000000..b9c122c --- /dev/null +++ b/tests/testdata/dematic_arvato.json @@ -0,0 +1,1381 @@ +[ + { + "id": "Kreisel_Arvato_03", + "function": "insert", + "x": 18737.94, + "y": 165252.93, + "z": 0.0, + "abstand": 3619.15, + "rotation": 90.0, + "typ": "STANDARD", + "darstellung": "2D", + "comment": "aus DXF: Radmitte AN8 (Layer 1ANTR-800) (18737.94, 165652.93) -> SP8 (Layer 1SP-800) (18737.94, 169272.08), Mittenabstand 3619.15, rotation 90.00 Grad; x/y = AN8-Mitte minus Radius. ACHTUNG hoehe=0.0 ist NICHT belegt: AN8/SP8 sind flache 2D-Symbole auf Z=0 und nur 0 3D-Nachbarn in 3000 mm - zu wenige fuer eine belegbare Hoehe." + }, + { + "id": "Kreisel_Arvato_04", + "function": "insert", + "x": 35394.31, + "y": 176069.0, + "z": 370.0, + "abstand": 33044.28, + "rotation": 180.0, + "typ": "STANDARD", + "darstellung": "2D", + "comment": "aus DXF: Radmitte AN8 Pinband (Layer 1ANTR-800) (34994.31, 176069.00) -> SP8 Pinband (mit Inkrementalgeber 829522024) (Layer 1SP-800) (1950.03, 176069.00), Mittenabstand 33044.28, rotation 180.00 Grad; x/y = AN8-Mitte minus Radius. ACHTUNG hoehe=370.0 ist NICHT belegt: AN8/SP8 sind flache 2D-Symbole auf Z=0 und nur 0 3D-Nachbarn in 3000 mm - zu wenige fuer eine belegbare Hoehe." + }, + { + "id": "Kreisel_Arvato_05", + "function": "insert", + "x": 36195.18, + "y": 188428.08, + "z": 0.0, + "abstand": 33470.18, + "rotation": 180.0, + "typ": "STANDARD", + "darstellung": "2D", + "comment": "aus DXF: Radmitte AN8 (Layer 1ANTR-800) (35795.18, 188428.08) -> SP8 (Layer 1SP-800) (2325.00, 188428.08), Mittenabstand 33470.18, rotation 180.00 Grad; x/y = AN8-Mitte minus Radius. ACHTUNG hoehe=0.0 ist NICHT belegt: AN8/SP8 sind flache 2D-Symbole auf Z=0 und nur 0 3D-Nachbarn in 3000 mm - zu wenige fuer eine belegbare Hoehe." + }, + { + "id": "Kreisel_Arvato_06", + "function": "insert", + "x": 42097.73, + "y": 165252.93, + "z": 0.0, + "abstand": 3619.15, + "rotation": 90.0, + "typ": "STANDARD", + "darstellung": "2D", + "comment": "aus DXF: Radmitte AN8 (Layer 1ANTR-800) (42097.73, 165652.93) -> SP8 (Layer 1SP-800) (42097.72, 169272.08), Mittenabstand 3619.15, rotation 90.00 Grad; x/y = AN8-Mitte minus Radius. ACHTUNG hoehe=0.0 ist NICHT belegt: AN8/SP8 sind flache 2D-Symbole auf Z=0 und nur 0 3D-Nachbarn in 3000 mm - zu wenige fuer eine belegbare Hoehe." + }, + { + "test_id": "GERADE_Arvato_01", + "typ": "gerade", + "darstellung": "2D", + "laenge": 8313.39, + "hz": 90.0, + "startpunkt": [2334.97, 177818.67, 0.0], + "endpunkt": [2334.96, 186132.06, 0.0], + "hoehe_unbekannt": true, + "comment": "aus DXF: Antrieb FF -> Umlenkung FF (Layer z-3 / z-3), Achse 90 Grad, Laenge 8313.39, Querversatz 0.01 (Bezugspunkt-Unterschied), Neigung 0.00 Grad; 2D-Schema - Z ist dort keine Einbauhoehe, darum waagrechte Strecke und hoehe_unbekannt=true. Einbauhoehe nur ueber den 3D-Zwilling zu bekommen." + }, + { + "test_id": "GERADE_Arvato_09", + "typ": "gerade", + "darstellung": "2D", + "laenge": 8313.39, + "hz": 90.0, + "startpunkt": [14264.99, 177818.71, 0.0], + "endpunkt": [14264.99, 186132.1, 0.0], + "hoehe_unbekannt": true, + "comment": "aus DXF: Antrieb FF -> Umlenkung FF (Layer z-3 / z-3), Achse 90 Grad, Laenge 8313.39, Querversatz 0.00 (Bezugspunkt-Unterschied), Neigung 0.00 Grad; 2D-Schema - Z ist dort keine Einbauhoehe, darum waagrechte Strecke und hoehe_unbekannt=true. Einbauhoehe nur ueber den 3D-Zwilling zu bekommen." + }, + { + "test_id": "GERADE_Arvato_10", + "typ": "gerade", + "darstellung": "2D", + "laenge": 3183.28, + "hz": 270.0, + "startpunkt": [19545.38, 174003.47, 370.0], + "endpunkt": [19545.58, 170820.19, 0.0], + "hoehe_unbekannt": true, + "comment": "aus DXF: Antrieb FF -> Umlenkung FF (Layer z-3 / z-3), Achse 270 Grad, Laenge 3183.28, Querversatz 0.20 (Bezugspunkt-Unterschied), Neigung 6.63 Grad; 2D-Schema - Z ist dort keine Einbauhoehe, darum waagrechte Strecke und hoehe_unbekannt=true. Einbauhoehe nur ueber den 3D-Zwilling zu bekommen." + }, + { + "test_id": "GERADE_Arvato_11", + "typ": "gerade", + "darstellung": "2D", + "laenge": 17664.81, + "hz": 0.0, + "startpunkt": [21626.92, 167134.77, 0.0], + "endpunkt": [39291.73, 167134.97, 0.0], + "hoehe_unbekannt": true, + "comment": "aus DXF: Antrieb FF -> Umlenkung FF (Layer z-3 / z-3), Achse 0 Grad, Laenge 17664.81, Querversatz 0.20 (Bezugspunkt-Unterschied), Neigung 0.00 Grad; 2D-Schema - Z ist dort keine Einbauhoehe, darum waagrechte Strecke und hoehe_unbekannt=true. Einbauhoehe nur ueber den 3D-Zwilling zu bekommen." + }, + { + "test_id": "GERADE_Arvato_12", + "typ": "gerade", + "darstellung": "2D", + "laenge": 3124.62, + "hz": 0.0, + "startpunkt": [21626.97, 165578.18, 0.0], + "endpunkt": [24751.59, 165578.37, 0.0], + "hoehe_unbekannt": true, + "comment": "aus DXF: Antrieb FF -> Umlenkung FF (Layer z-3 / z-3), Achse 0 Grad, Laenge 3124.62, Querversatz 0.19 (Bezugspunkt-Unterschied), Neigung 0.00 Grad; 2D-Schema - Z ist dort keine Einbauhoehe, darum waagrechte Strecke und hoehe_unbekannt=true. Einbauhoehe nur ueber den 3D-Zwilling zu bekommen." + }, + { + "test_id": "GERADE_Arvato_13", + "typ": "gerade", + "darstellung": "2D", + "laenge": 8313.39, + "hz": 90.0, + "startpunkt": [22854.62, 177818.71, 0.0], + "endpunkt": [22854.61, 186132.1, 0.0], + "hoehe_unbekannt": true, + "comment": "aus DXF: Antrieb FF -> Umlenkung FF (Layer z-3 / z-3), Achse 90 Grad, Laenge 8313.39, Querversatz 0.01 (Bezugspunkt-Unterschied), Neigung 0.00 Grad; 2D-Schema - Z ist dort keine Einbauhoehe, darum waagrechte Strecke und hoehe_unbekannt=true. Einbauhoehe nur ueber den 3D-Zwilling zu bekommen." + }, + { + "test_id": "GERADE_Arvato_14", + "typ": "gerade", + "darstellung": "2D", + "laenge": 6717.41, + "hz": 0.0, + "startpunkt": [32574.32, 165578.18, 0.0], + "endpunkt": [39291.73, 165578.37, 0.0], + "hoehe_unbekannt": true, + "comment": "aus DXF: Antrieb FF -> Umlenkung FF (Layer z-3 / z-3), Achse 0 Grad, Laenge 6717.41, Querversatz 0.19 (Bezugspunkt-Unterschied), Neigung 0.00 Grad; 2D-Schema - Z ist dort keine Einbauhoehe, darum waagrechte Strecke und hoehe_unbekannt=true. Einbauhoehe nur ueber den 3D-Zwilling zu bekommen." + }, + { + "test_id": "GERADE_Arvato_15", + "typ": "gerade", + "darstellung": "2D", + "laenge": 3706.41, + "hz": 180.0, + "startpunkt": [34650.88, 173417.91, 0.0], + "endpunkt": [30944.47, 173408.89, 0.0], + "hoehe_unbekannt": true, + "comment": "aus DXF: Antrieb FF -> Umlenkung FF (Layer z-3 / z-3), Achse 180 Grad, Laenge 3706.41, Querversatz 9.02 (Bezugspunkt-Unterschied), Neigung 0.00 Grad; 2D-Schema - Z ist dort keine Einbauhoehe, darum waagrechte Strecke und hoehe_unbekannt=true. Einbauhoehe nur ueber den 3D-Zwilling zu bekommen." + }, + { + "test_id": "GERADE_Arvato_16", + "typ": "gerade", + "darstellung": "2D", + "laenge": 3566.48, + "hz": 270.0, + "startpunkt": [41688.71, 185921.64, 0.0], + "endpunkt": [41678.01, 182355.16, 0.0], + "hoehe_unbekannt": true, + "comment": "aus DXF: Antrieb FF -> Umlenkung FF (Layer z-3 / z-3), Achse 270 Grad, Laenge 3566.48, Querversatz 10.70 (Bezugspunkt-Unterschied), Neigung 0.00 Grad; 2D-Schema - Z ist dort keine Einbauhoehe, darum waagrechte Strecke und hoehe_unbekannt=true. Einbauhoehe nur ueber den 3D-Zwilling zu bekommen." + }, + { + "function": "insert", + "block": "Vario_Motorstation_500mm_rechts", + "x": 41271.73, + "y": 171724.16, + "z": -370.0, + "rotation": 0.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Antrieb FF', Layer 'z-3', auf Makro-Block 'Vario_Motorstation_500mm_rechts' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Seite=rechts - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Vario_Umlenkstation_500mm_rechts", + "x": 30282.95, + "y": 174208.89, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Umlenkung FF', Layer 'z-3', auf Makro-Block 'Vario_Umlenkstation_500mm_rechts' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Seite=rechts - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Vario_Umlenkstation_500mm_rechts", + "x": 36190.75, + "y": 186482.16, + "z": 0.0, + "rotation": 0.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Umlenkung FF', Layer 'z-3', auf Makro-Block 'Vario_Umlenkstation_500mm_rechts' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Seite=rechts - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Vario_Kurve_rechts_30_TEF_aussen", + "x": 41377.46, + "y": 179475.63, + "z": 0.0, + "rotation": 0.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'tef30°', Layer '0', auf Makro-Block 'Vario_Kurve_rechts_30_TEF_aussen' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Lage=aussen, Seite=rechts - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Vario_Kurve_rechts_30_TEF_aussen", + "x": 35984.01, + "y": 173395.48, + "z": 0.0, + "rotation": 120.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'tef30°außen', Layer '0', auf Makro-Block 'Vario_Kurve_rechts_30_TEF_aussen' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Seite=rechts - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Vario_Kurve_rechts_30_TEF_aussen", + "x": 36210.84, + "y": 173622.64, + "z": 0.0, + "rotation": 150.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'tef30°außen', Layer '0', auf Makro-Block 'Vario_Kurve_rechts_30_TEF_aussen' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Seite=rechts - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Vario_Kurve_rechts_30_TEF_aussen", + "x": 36296.49, + "y": 173937.59, + "z": 0.0, + "rotation": 180.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'tef30°außen', Layer '0', auf Makro-Block 'Vario_Kurve_rechts_30_TEF_aussen' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Seite=rechts - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Vario_Kurve_rechts_30_TEF_aussen", + "x": 41169.03, + "y": 187345.02, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'tef30°außen', Layer '0', auf Makro-Block 'Vario_Kurve_rechts_30_TEF_aussen' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Seite=rechts - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Vario_Kurve_rechts_30_TEF_aussen", + "x": 41483.98, + "y": 187259.38, + "z": 0.0, + "rotation": 240.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'tef30°außen', Layer '0', auf Makro-Block 'Vario_Kurve_rechts_30_TEF_aussen' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Seite=rechts - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Vario_Kurve_rechts_30_TEF_aussen", + "x": 41711.14, + "y": 187032.54, + "z": 0.0, + "rotation": 210.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'tef30°außen', Layer '0', auf Makro-Block 'Vario_Kurve_rechts_30_TEF_aussen' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Seite=rechts - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Vario_Kurve_rechts_30_TEF_aussen", + "x": 41784.82, + "y": 180461.57, + "z": 0.0, + "rotation": 180.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'tef30°außen', Layer '0', auf Makro-Block 'Vario_Kurve_rechts_30_TEF_aussen' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Seite=rechts - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 2801.42, + "y": 186442.19, + "z": 0.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 4901.05, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 5801.05, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 6601.05, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 7401.05, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 8451.06, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 9251.06, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 10051.06, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 11391.05, + "y": 178054.91, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 12191.05, + "y": 178054.91, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 12991.05, + "y": 178054.91, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 14731.45, + "y": 186442.19, + "z": 0.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 15801.05, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 16601.05, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 17401.05, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 18448.56, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 19348.56, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 20148.56, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 20948.56, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 23321.08, + "y": 186442.19, + "z": 0.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 24608.56, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 25408.56, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 26908.55, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 27808.55, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 28608.55, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 29408.55, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 30958.55, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 31858.55, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 32658.55, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_links", + "x": 33458.55, + "y": 178054.88, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement links 200000217 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_links' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_rechts", + "x": 42932.74, + "y": 166073.59, + "z": 0.0, + "rotation": 180.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement rechts 200000241 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_rechts' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "AS_Element_90_rechts", + "x": 42932.74, + "y": 167630.19, + "z": 0.0, + "rotation": 180.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Ausschleuselement rechts 200000241 Draufsicht', Layer '0', auf Makro-Block 'AS_Element_90_rechts' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "ES_Element_90_rechts", + "x": 4958.03, + "y": 166992.08, + "z": 0.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Einschleuselement rechts 200000146 Seitenansicht', Layer '4-EE', auf Makro-Block 'ES_Element_90_rechts' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "ES_Element_90_rechts", + "x": 8058.82, + "y": 168049.64, + "z": 0.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Einschleuselement rechts 200000146 Seitenansicht', Layer '4-EE', auf Makro-Block 'ES_Element_90_rechts' abgebildet; ACHTUNG geraten (im DXF nicht ableitbar): Winkel=90 - bei Bedarf hier im JSON korrigieren; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "ES_Element_30_rechts", + "x": 20513.9, + "y": 187602.73, + "z": -4.49, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'Einschleuselement rechts 30° 200000688 Vorderansicht', Layer '0', auf Makro-Block 'ES_Element_30_rechts' abgebildet; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-LP", + "x": 2572.67, + "y": 175669.0, + "z": 370.0, + "rotation": 0.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-LP', Layer '5SEP-LP'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-LP", + "x": 3016.33, + "y": 188028.08, + "z": 0.0, + "rotation": 0.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-LP', Layer '5SEP-LP'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-LP", + "x": 4628.04, + "y": 176469.0, + "z": 370.0, + "rotation": 180.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-LP', Layer '5SEP-LP'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-LP", + "x": 14946.35, + "y": 188028.08, + "z": 0.0, + "rotation": 0.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-LP', Layer '5SEP-LP'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-LP", + "x": 15528.05, + "y": 176469.05, + "z": 370.0, + "rotation": 180.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-LP', Layer '5SEP-LP'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-LP", + "x": 19137.94, + "y": 168591.49, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-LP', Layer '5SEP-LP'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-LP", + "x": 23535.98, + "y": 188028.08, + "z": 0.0, + "rotation": 0.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-LP', Layer '5SEP-LP'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-LP", + "x": 24328.05, + "y": 176469.09, + "z": 370.0, + "rotation": 180.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-LP', Layer '5SEP-LP'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-LP", + "x": 34584.31, + "y": 176469.0, + "z": 370.0, + "rotation": 180.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-LP', Layer '5SEP-LP'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-LP", + "x": 35391.12, + "y": 188828.08, + "z": 0.0, + "rotation": 180.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-LP', Layer '5SEP-LP'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-LP", + "x": 41697.93, + "y": 167818.8, + "z": 0.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-LP', Layer '5SEP-LP'; Block 'S-LP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 2334.91, + "y": 186756.85, + "z": 0.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 2334.97, + "y": 176994.56, + "z": 370.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 5367.5, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 6267.5, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 7067.5, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 7867.5, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 8917.51, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 9717.51, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 10517.51, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 11857.51, + "y": 187359.45, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 12657.51, + "y": 187359.45, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 13457.51, + "y": 187359.45, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 14264.94, + "y": 186756.89, + "z": 0.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 14264.99, + "y": 176994.61, + "z": 370.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 16267.51, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 17067.51, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 17867.51, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 18915.01, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 19546.07, + "y": 170249.91, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 19546.07, + "y": 175157.16, + "z": 370.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 19680.48, + "y": 167135.46, + "z": 0.0, + "rotation": 0.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 19680.53, + "y": 165578.85, + "z": 0.0, + "rotation": 0.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 19815.01, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 20615.01, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 21415.01, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 22854.57, + "y": 186756.89, + "z": 0.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 22854.62, + "y": 176994.61, + "z": 370.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 25075.01, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 25462.17, + "y": 165578.85, + "z": 0.0, + "rotation": 0.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 25875.01, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 27375.0, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 28275.0, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 29075.0, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 29875.0, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 30851.32, + "y": 174208.89, + "z": 0.0, + "rotation": 0.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 30944.14, + "y": 165578.85, + "z": 0.0, + "rotation": 0.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 31425.0, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 31494.84, + "y": 173408.89, + "z": 0.0, + "rotation": 0.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 32325.0, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 33125.0, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 33925.0, + "y": 187359.42, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 36195.18, + "y": 187066.18, + "z": 0.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 39889.85, + "y": 167135.46, + "z": 0.0, + "rotation": 0.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 39889.9, + "y": 165578.85, + "z": 0.0, + "rotation": 0.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 41439.64, + "y": 169070.08, + "z": 0.0, + "rotation": 120.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "S-SP", + "x": 41697.72, + "y": 182892.95, + "z": 0.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'S-SP', Layer '5SEP-SP'; Block 'S-SP' ist in data/ils/ NICHT vorhanden - erzeugt im Testlauf einen Platzhalter; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Scanner", + "x": 3166.57, + "y": 188414.66, + "z": 0.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'SCAN', Layer 'ZZSCANNER', auf Makro-Block 'Scanner' abgebildet; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Scanner", + "x": 4477.8, + "y": 176855.58, + "z": 370.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'SCAN', Layer 'ZZSCANNER', auf Makro-Block 'Scanner' abgebildet; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Scanner", + "x": 15096.6, + "y": 188414.66, + "z": 0.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'SCAN', Layer 'ZZSCANNER', auf Makro-Block 'Scanner' abgebildet; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Scanner", + "x": 15377.81, + "y": 176855.63, + "z": 370.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'SCAN', Layer 'ZZSCANNER', auf Makro-Block 'Scanner' abgebildet; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Scanner", + "x": 23686.22, + "y": 188414.66, + "z": 0.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'SCAN', Layer 'ZZSCANNER', auf Makro-Block 'Scanner' abgebildet; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Scanner", + "x": 24177.81, + "y": 176855.68, + "z": 370.0, + "rotation": 90.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'SCAN', Layer 'ZZSCANNER', auf Makro-Block 'Scanner' abgebildet; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Scanner", + "x": 25618.13, + "y": 165246.94, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'SCAN', Layer 'ZZSCANNER', auf Makro-Block 'Scanner' abgebildet; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Scanner", + "x": 30966.81, + "y": 165246.94, + "z": 0.0, + "rotation": 270.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'SCAN', Layer 'ZZSCANNER', auf Makro-Block 'Scanner' abgebildet; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + }, + { + "function": "insert", + "block": "Scanner", + "x": 41298.05, + "y": 167879.95, + "z": 0.0, + "rotation": 180.0, + "darstellung": "2D", + "hoehe_unbekannt": true, + "comment": "aus DXF: Block 'SCAN', Layer 'ZZSCANNER', auf Makro-Block 'Scanner' abgebildet; 2D-Schema ohne 3D-Zwilling - z ist keine Einbauhoehe (hoehe_unbekannt)" + } +]