Merge remote-tracking branch 'origin/master'

This commit is contained in:
2026-09-18 17:47:53 +02:00
39 changed files with 3487 additions and 824 deletions
+4
View File
@@ -190,6 +190,10 @@ data/block_libraries/ils_library.dxf
# Tool-Caches # Tool-Caches
.skylos/ .skylos/
# Eigene Geruest-Konfigurationen (vom Dialog GERUEST_* geschrieben).
# Benutzerdaten - die gepflegte Bibliothek steht in data/json/geruest.json.
cfg/geruest_user.json
# Grosse Test-Zeichnungen (Arbeitsstaende, gehoeren nicht ins Repo) # Grosse Test-Zeichnungen (Arbeitsstaende, gehoeren nicht ins Repo)
/tests/test.dwg /tests/test.dwg
/tests/test.dxf /tests/test.dxf
+4 -3
View File
@@ -75,7 +75,7 @@ Die `start_briscad.bat` ruft `setenv.bat` auf und startet BricsCAD.
| VarioFoerderer | `VarioFoerderer` | FOERDERANLAGE, VARIOFOERDERER_EDIT, Vario_Kette_Merge | | VarioFoerderer | `VarioFoerderer` | FOERDERANLAGE, VARIOFOERDERER_EDIT, Vario_Kette_Merge |
| Gefaellestrecke | `Gefaellestrecke` | GEFAELLESTRECKE, GEFAELLESTRECKE_EDIT | | Gefaellestrecke | `Gefaellestrecke` | GEFAELLESTRECKE, GEFAELLESTRECKE_EDIT |
| Omniflo (komplett) | `OmniModulInsert` | OMNI_APB_*, OMNI_W*_*, OMNI_AP*, OMNI_TEF_*, OMNI_TV_*, OMNI_EDIT | | Omniflo (komplett) | `OmniModulInsert` | OMNI_APB_*, OMNI_W*_*, OMNI_AP*, OMNI_TEF_*, OMNI_TV_*, OMNI_EDIT |
| Geruest | `GeruestInsert` | GERUEST_ROHRABHAENGER, GERUEST_SAEULE_2ZOLL, GERUEST_SAEULE_1ZOLL, GERUEST_WANDBEFESTIGUNG | | Geruest | `GeruestInsert` | GERUEST (ein Dialog fuer alle Bereiche) |
| Connections | `Connection_Edit` + `SSG_LIB_Commands` | CONNECTION_INSERT, CONNECTION_EDIT, ILS_EINGANG_INSERT, ILS_AUSGANG_INSERT | | Connections | `Connection_Edit` + `SSG_LIB_Commands` | CONNECTION_INSERT, CONNECTION_EDIT, ILS_EINGANG_INSERT, ILS_AUSGANG_INSERT |
| Export | `export` | EXPORTSIVAS, EXPORTCSV, CALLPYTHON | | Export | `export` | EXPORTSIVAS, EXPORTCSV, CALLPYTHON |
| Konfiguration | `SSG_LIB_Commands` + `DrawPlanquadrate` | SSG_DIM_SWITCH, SSG_SPRACHE, PLANQUADRATE_TOGGLE (DRAW_PLANQUADRATE/DELETE_PLANQUADRATE) | | Konfiguration | `SSG_LIB_Commands` + `DrawPlanquadrate` | SSG_DIM_SWITCH, SSG_SPRACHE, PLANQUADRATE_TOGGLE (DRAW_PLANQUADRATE/DELETE_PLANQUADRATE) |
@@ -107,7 +107,7 @@ Detaillierte Dokumentation aller Funktionen und Befehle: siehe `Lisp/README.md`.
| `OmniModulInsert.lsp` | OMNI_LOAD, OMNI_APB_*, OMNI_W*_*, OMNI_AP60/AP110, OMNI_TEF_*, OMNI_TV_*, OMNI_APBW_*, OMNI_EDIT | Omniflo-Komponenten: Boegen, Weichen, Verbinder, Transferwagen, Edit-Dialog | | `OmniModulInsert.lsp` | OMNI_LOAD, OMNI_APB_*, OMNI_W*_*, OMNI_AP60/AP110, OMNI_TEF_*, OMNI_TV_*, OMNI_APBW_*, OMNI_EDIT | Omniflo-Komponenten: Boegen, Weichen, Verbinder, Transferwagen, Edit-Dialog |
| `SSG_LIB_Commands.lsp` | ILS_BTMT_Beladung, ILS_BTMT_Entladung, ILS_SCANNER_INSERT, ILS_SEPARATOR_INSERT, ILS_EINGANG_INSERT, ILS_AUSGANG_INSERT, ILS_ETF_*, ILS_GK*_*, SSG_BLOCKEDIT, SSG_DIM_SWITCH, SSG_SPRACHE | BTMT Be-/Entladung (`ils-insert-station`: wiederholtes Einfuegen von `BTMT-Beladung` bzw. `SC_Entladung`, Drehung interaktiv, Layer aus `LAYER`-Attribut, Dimension via `ssg-ils-block-laden`) + Sensoren (`ils-insert-sensor`) + Eingang-/Ausgang-Symbol (frei platzierbar wie BTMT Be-/Entladung, ueber `ils-insert-station` mit den Rohnamen `Eingang`/`Ausgang`, Bloecke `Eingang_<DIM>.dwg`/`Ausgang_<DIM>.dwg` in data/ils) + Dummy-Befehle fuer Menue + Doppelklick/Kontextmenue-Dispatcher + Konfiguration (2D/3D, Sprache) | | `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_<DIM>.dwg`/`Ausgang_<DIM>.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_<Typ>`) 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. | | `Tro_Edit.lsp` | TRO_INSERT, TRO_EDIT | TRO-Marker (Block `TRO_SYM_<Typ>`) fuer sps_skel (lib/tro_annotate.py): TRO_INSERT fuegt eine bestehende Blockdefinition an einem gewaehlten Punkt ein und setzt ID/TYPE (bricht ab, wenn der Typ noch keine Blockdefinition in der Zeichnung hat - die entsteht nur ueber tro_annotate.py); TRO_EDIT aendert ID/TYPE eines bestehenden Markers per Dialog. Typliste aus `tro_types.lsp` (von `tro_annotate.py --emit-lisp` nach DXFM_LISP geschrieben). Der Dispatcher SSG_BLOCKEDIT leitet Blocknamen `TRO_*` zu TRO_EDIT. |
| `GeruestInsert.lsp` | GERUEST_ROHRABHAENGER, GERUEST_SAEULE_2ZOLL, GERUEST_SAEULE_1ZOLL, GERUEST_WANDBEFESTIGUNG | Geruest-Baugruppen aus `data/json/geruest.json` konfigurieren und einfuegen. Dialog (`dcl/geruest.dcl`): Basis-Variante aus `geometrie_varianten` (Dropdown), Anbauteile aus `anbaugruppen` (`list_box` mit `multiple_select` - im Projekt der erste), Laenge (gegen `attribute.Laenge` min/max geprueft, Retry-Schleife) und Farbe (`attribute.Farbe`, Freitext bei `frei_option`). Baugruppen ohne `attribute`-Block (Wandbefestigung) bekommen Laenge/Farbe gesperrt. Der Block wird - wie KREISEL_n/VF_n - SELBST zusammengebaut: Dummy-DWG aus `data/geruest` liefert nur Geometrie, das Attribut-Schema kommt aus `*geruest-attrib-defs*` (ARTINR, LAENGE, BEZEICHNUNG, TEILEART, FARBE, LAYER) und wird per `ssg-attrib-merge` mit den Dummy-Tags zusammengefuehrt - sonst verschluckt `ssg-attrib-set-on` stillschweigend jeden Tag, den die Dummy-DWG nicht als ATTDEF kennt. Blockname `GR_<id>_<ssg-timestamp>` (Kollisionszaehler wie in OmniModulInsert). **Das `GR_`-Praefix ist nicht kosmetisch**: `SSG_BLOCKEDIT` haelt JEDEN Block mit TEILEART-Attribut fuer ein Omniflo-Element, deshalb steht in `SSG_LIB_Commands.lsp` ein `GR_*`-Zweig VOR dem Omniflo-Zweig. ARTINR listet jede Sivas-ID so oft, wie `menge` es vorgibt (benannte Gruppen mit `teile` werden rekursiv aufgeloest); BEZEICHNUNG verkettet mit `" + "`, NICHT mit Komma - die Artikelbezeichnungen enthalten selbst Kommas ("ROHR R 1\" 33,7 x 2,6"). Zuordnung Menue -> JSON laeuft ueber das ASCII-Feld `id`, weil die aeusseren JSON-Schluessel Umlaute bzw. Anfuehrungszeichen tragen. CSV-/Sivas-Export: `pattern_geruest = GR_*` (cfg/export.cfg), `csv:collect-export-blocks`/`ssg-id-collect-blocks` sammeln GR_-Bloecke wie BTMT eigenstaendig; `*geruest-attrib-defs*` traegt zusaetzlich ein leeres `ID`-ATTDEF (Voraussetzung fuer `ssg-id-check-all`, das nur bestehende ATTRIBs beschreiben kann). ARTINR/BEZEICHNUNG/TEILEART/LAENGE landen in export_csv.py/export_sivas.py in festen Spalten, FARBE/LAYER (+ ARTINR bei export_csv.py) im Merkmale-/details-JSON. Noch NICHT enthalten: Edit-Pfad (GERUEST_EDIT) | | `GeruestInsert.lsp` | GERUEST | Geruest-Baugruppen aus `data/json/geruest.json` konfigurieren und einfuegen. **Ein Dialog fuer alle Bereiche**: oben ein Dropdown `bereich` (Rohrabhaenger / Saeulen / Wandbefestigung, Reihenfolge = Dateireihenfolge, Anzeigename aus dem ASCII-Feld `titel`). DCL hat **kein Reiter-Tile**, und da sich die Teilezeilen je Bereich unterscheiden, schliesst ein Bereichswechsel den Dialog (`done_dialog 4`) und die Schleife in `c:GERUEST` baut ihn neu auf; der Stand jedes Bereichs bleibt in `*geruest-zustand*` erhalten, ein versehentlicher Wechsel verwirft also nichts. 1"- und 2"-Saeulen liegen in EINER Baugruppe `Saeulen` mit gemeinsamer Anbauteil-Liste; **`teileart` und `dummy` duerfen je Geometrie-Variante ueberschrieben werden** (`geruest-variante-wert`, Variante gewinnt vor Baugruppe) - so liefert `R 1"` weiterhin TEILEART `Geruest Saeule 1 Zoll` und die `R 2"`-Varianten `Geruest Saeule 2 Zoll`, die Groessen bleiben im Export also unterscheidbar. **Die Dialogdefinition wird zur Laufzeit erzeugt** (`geruest-dcl-schreiben` -> temporaere Datei per `vl-filename-mktemp`, nach `unload_dialog` wieder geloescht) - es gibt KEINE `dcl/geruest.dcl`. Grund: je Anbauteil wird ein eigenes Mengenfeld gebraucht, und eine DCL-Liste (`list_box`) kann keine Eingabefelder enthalten; DCL kann weder Dateien einbinden noch Tiles zur Laufzeit erzeugen. Inhalt: Basis-Variante aus `geometrie_varianten` (Dropdown), Laenge (gegen `attribute.Laenge` min/max geprueft, Retry-Schleife), Farbe (`attribute.Teilefarbe`, Freitext bei `frei_option`) und **zwei Befestigungslisten** ("oben"/"unten"), die BEIDE alle `anbaugruppen` enthalten - dasselbe Teil kann oben, unten oder gleichzeitig an beiden Positionen sitzen, je mit eigener Stueckzahl (vorbelegt aus `menge`). Baugruppen ohne `attribute`-Block (Wandbefestigung) bekommen Laenge/Farbe gesperrt, Baugruppen ohne Anbauteile bekommen gar keine Spalten (ein leerer DCL-Cluster ist unzulaessig). Im generierten Label wird `"` zu `''` ersetzt (`geruest-dcl-label`) - fuenf Artikelnamen enthalten ein Anfuehrungszeichen, das das DCL-Label sonst vorzeitig beendet und die Datei zerstoert. **Konfigurationen**: gepflegte Bibliothek je Baugruppe unter `konfigurationen` in `geruest.json` (schreibgeschuetzt) plus eigene in `cfg/geruest_user.json` (gitignored, `geruest-user-schreiben`; getrennte Datei, damit Benutzerdaten und versionierte Bibliothek nicht kollidieren). Eigene Eintraege sind in der Liste mit `*` markiert und nur diese loeschbar. Konfigurationen verweisen ueber den ANZEIGENAMEN auf Variante und Anbauteile; ein unbekannter Name wird gemeldet und uebersprungen, statt die Konfiguration scheitern zu lassen. Speichern/Loeschen laufen ueber `done_dialog 2`/`3` (Dialog schliessen, Aktion, neu zeigen) - `getstring` funktioniert aus einem `action_tile`-Rueckruf heraus nicht. Der Block wird - wie KREISEL_n/VF_n - SELBST zusammengebaut: Dummy-DWG aus `data/geruest` liefert nur Geometrie, das Attribut-Schema kommt aus `*geruest-attrib-defs*` (ID, ARTINR, LAENGE, BEZEICHNUNG, TEILEART, TEILEFARBE, LAYER) und wird per `ssg-attrib-merge` mit den Dummy-Tags zusammengefuehrt - sonst verschluckt `ssg-attrib-set-on` stillschweigend jeden Tag, den die Dummy-DWG nicht als ATTDEF kennt. Blockname `GR_<id>_<ssg-timestamp>` (Kollisionszaehler wie in OmniModulInsert). **Das `GR_`-Praefix ist nicht kosmetisch**: `SSG_BLOCKEDIT` haelt JEDEN Block mit TEILEART-Attribut fuer ein Omniflo-Element, deshalb steht in `SSG_LIB_Commands.lsp` ein `GR_*`-Zweig VOR dem Omniflo-Zweig. ARTINR listet jede Sivas-ID so oft, wie die gewaehlte Stueckzahl es vorgibt - erst Basis, dann Befestigung oben, dann unten; bei einer benannten Gruppe (`teile`) wird das ganze Set n-mal wiederholt, die Mengen INNERHALB des Sets bleiben dabei erhalten (`geruest-artikelnummern-n`). BEZEICHNUNG nennt Position und Menge je Teil ("oben 2x ...") und verkettet mit `" + "`, NICHT mit Komma - die Artikelbezeichnungen enthalten selbst Kommas ("ROHR R 1\" 33,7 x 2,6"). Zuordnung Menue -> JSON laeuft ueber das ASCII-Feld `id`, weil die aeusseren JSON-Schluessel Umlaute bzw. Anfuehrungszeichen tragen. CSV-/Sivas-Export: `pattern_geruest = GR_*` (cfg/export.cfg), `csv:collect-export-blocks`/`ssg-id-collect-blocks` sammeln GR_-Bloecke wie BTMT eigenstaendig; `*geruest-attrib-defs*` traegt zusaetzlich ein leeres `ID`-ATTDEF (Voraussetzung fuer `ssg-id-check-all`, das nur bestehende ATTRIBs beschreiben kann). ARTINR/BEZEICHNUNG/TEILEART/LAENGE landen in export_csv.py/export_sivas.py in festen Spalten, TEILEFARBE/LAYER (+ ARTINR bei export_csv.py) im Merkmale-/details-JSON. Noch NICHT enthalten: Edit-Pfad (GERUEST_EDIT) |
| `menu-inventory.lsp` | MenuInventory | BricsCAD Diagnostik (Menuegruppen, Plugins, Startdateien) | | `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). | | `DrawPlanquadrate.lsp` | DRAW_PLANQUADRATE, DELETE_PLANQUADRATE, PLANQUADRATE_TOGGLE | Zeichnet auf Z=0 ein Planquadrat-Linienraster (senkrecht/waagerecht), im Abstand/Zaehlschema der aktiven `[Planquadrate]`-Konfiguration aus `cfg/export.cfg` (dieselbe Quelle wie `lib/export_planquadrat.py` fuer den CSV-Export). Bereich kommt NICHT aus den Zeichnungs-Extents, sondern aus derselben Blockmenge wie EXPORTCSV (`csv:collect-export-blocks`, export.lsp; siehe `plq-export-bbox`): Ursprung (Index 1) UND obere Grenze kommen BEIDE aus der tatsaechlichen Bounding-Box jedes Blocks (`csv:get-bbox`, wie `bbox_columns()`/`planquadrat_column()`/`_bbox_min()` in `lib/export_csv.py` bzw. `lib/export_planquadrat.py`), NICHT aus dem reinen Einfuegepunkt - bei einem gedrehten/exzentrisch eingefuegten Block (z.B. ein um 90 Grad gedrehter Kreisel) kann das Bounding-Box-Zentrum weit vom Einfuegepunkt abweichen; Ursprung und Zellzuordnung muessen auf derselben Referenz beruhen, sonst driften sie auseinander (realer Bug, behoben). Reine Hilfsgeometrie (Kreisel-Umfahrungsboegen als eigene Linien, Texte) zaehlt bewusst nicht mit, sonst weicht das Raster vom CSV-Export ab. `x_origin`/`y_origin` ueberschreiben den Ursprung, Fallback auf EXTMIN/EXTMAX falls keine Export-Bloecke vorhanden sind bzw. `csv:get-bbox` fuer einen Block scheitert. Spalten-/Zeilenzahl wird IMMER aufgerundet auf volle Rasterschritte, damit alle senkrechten Linien auf der letzten waagerechten enden und umgekehrt - das Raster bildet immer ein geschlossenes Rechteck. Zaehlung (alphabetisch/numerisch je Achse) als Text am linken/unteren Rand. Das fertige Raster (Linien+Texte) wird per `_.-BLOCK`/`_.INSERT` zu einem einzigen Block `ZZ_PLANQUADRATE` bei (0,0,0) zusammengefasst. Layer aus `cfg/layer.cfg` (`[allgemein]` Key `planquadrate`, Fallback `ZZ_Planquadrate`). DRAW_PLANQUADRATE entfernt vor dem Neuaufbau zuerst einen evtl. vorhandenen Block (`plq-block-entfernen`: INSERTs loeschen + `_-PURGE _B`), DELETE_PLANQUADRATE ruft nur diese Entfernung auf. PLANQUADRATE_TOGGLE ("Planquadrate an/aus", Menue: Konfiguration) prueft per `tblsearch`/`ssget "X"`, ob der Block existiert, und dispatcht auf Zeichnen bzw. Loeschen. Lazy geladen ueber `(ssg-ensure "DrawPlanquadrate")`, nicht in der Core-Ladereihenfolge. Die Planquadrat-SPALTE im CSV-Export (`export_csv.py::planquadrat_column`/`resolve_origins`, `lib/export_planquadrat.py::compute_planquadrat`/`_bbox_min`) rechnet ebenfalls durchgaengig auf Bounding-Box-Basis (nicht nur Einfuegepunkt) und liefert eine SPANNE ("A/1-J/1") statt nur einer Zelle, wenn ein Element mehr als eine Zelle ueberdeckt (Kreisel, lange VF-/GF-Ketten). |
@@ -123,7 +123,6 @@ Detaillierte Dokumentation aller Funktionen und Befehle: siehe `Lisp/README.md`.
| `sprache.dcl` | SSG_SPRACHE (SSG_LIB_Commands.lsp) | | `sprache.dcl` | SSG_SPRACHE (SSG_LIB_Commands.lsp) |
| `tro_insert.dcl` | TRO_INSERT (Tro_Edit.lsp) | | `tro_insert.dcl` | TRO_INSERT (Tro_Edit.lsp) |
| `tro_edit.dcl` | TRO_EDIT (Tro_Edit.lsp) | | `tro_edit.dcl` | TRO_EDIT (Tro_Edit.lsp) |
| `geruest.dcl` | Alle GERUEST_*-Befehle (GeruestInsert.lsp, `geruest-dialog`) - ein Dialog fuer alle Baugruppen, Listen zur Laufzeit gefuellt |
| `vfl_edit.dcl` | Linienzug-Editieren/Fortsetzen (vf_linienzug.lsp, `vfl-dlg-position`, aufgerufen aus `vfl-edit-ent`) | | `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` | | `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` |
@@ -155,6 +154,8 @@ Werden via `bin/setenv.bat` gesetzt. Wichtigste:
- **AutoLISP `(and ...)`/`(or ...)` geben `T`/`nil` zurueck, NICHT den letzten Wert** (anders als Common Lisp/Scheme). Nie einen Nicht-Boolean-Wert (String, Entity, Liste) durch `and`/`or` faedeln - dafuer `if`/`cond` verwenden. Realer Bug-Fall: `(setq eff (and blockname (ssg-ils-block-laden blockname)))` setzte `eff` auf `T` statt auf den Blocknamen-String und liess ein spaeteres `_.INSERT`/`strcat` mit `bad argument type <T>` abstuerzen. Beim Debuggen mysterioeser Typfehler `(setq .* (and`/`(setq .* (or` durchsuchen. - **AutoLISP `(and ...)`/`(or ...)` geben `T`/`nil` zurueck, NICHT den letzten Wert** (anders als Common Lisp/Scheme). Nie einen Nicht-Boolean-Wert (String, Entity, Liste) durch `and`/`or` faedeln - dafuer `if`/`cond` verwenden. Realer Bug-Fall: `(setq eff (and blockname (ssg-ils-block-laden blockname)))` setzte `eff` auf `T` statt auf den Blocknamen-String und liess ein spaeteres `_.INSERT`/`strcat` mit `bad argument type <T>` abstuerzen. Beim Debuggen mysterioeser Typfehler `(setq .* (and`/`(setq .* (or` durchsuchen.
- **Werte selbstbeschreibend fuehren, nicht als positionsabhaengige Indizes/Flags** - wenn ein spaeterer Konsument (Journal-Replay, Export, Edit) den Wert interpretiert. Beispiel: Winkel ueberall als ECHTER Wert `30`/`60`/`90` journalisieren, nicht als Auswahl-Index `1`/`2`/`3` - ein als Index missverstandener String `"30"` fuehrte real zum falschen 90-Grad-Bogen (VF-Linienzug). Gleiche Klasse wie die `and`/`or`-Typfalle: der Fehler faellt erst beim entfernten Konsumenten auf. - **Werte selbstbeschreibend fuehren, nicht als positionsabhaengige Indizes/Flags** - wenn ein spaeterer Konsument (Journal-Replay, Export, Edit) den Wert interpretiert. Beispiel: Winkel ueberall als ECHTER Wert `30`/`60`/`90` journalisieren, nicht als Auswahl-Index `1`/`2`/`3` - ein als Index missverstandener String `"30"` fuehrte real zum falschen 90-Grad-Bogen (VF-Linienzug). Gleiche Klasse wie die `and`/`or`-Typfalle: der Fehler faellt erst beim entfernten Konsumenten auf.
- **Wiederverwendung vor Neuschaffung bei LISP-Routinen**: Neue Funktionen moeglichst als Erweiterung/Ergaenzung bestehender Funktionen anlegen. Bevor eine NEUE Bibliothek (`.lsp`-Datei) geschaffen wird, immer zuerst pruefen, ob es bereits thematisch passende Routinen gibt, in die die Funktionalitaet gehoert (z.B. Einfuege-Primitiven in `ssg_ks_insert.lsp`, Kern-Helfer in `ssg_core.lsp`, Konstanten in `vf_konstanten.lsp`). Erst wenn nichts passt, ein neues Modul. - **Wiederverwendung vor Neuschaffung bei LISP-Routinen**: Neue Funktionen moeglichst als Erweiterung/Ergaenzung bestehender Funktionen anlegen. Bevor eine NEUE Bibliothek (`.lsp`-Datei) geschaffen wird, immer zuerst pruefen, ob es bereits thematisch passende Routinen gibt, in die die Funktionalitaet gehoert (z.B. Einfuege-Primitiven in `ssg_ks_insert.lsp`, Kern-Helfer in `ssg_core.lsp`, Konstanten in `vf_konstanten.lsp`). Erst wenn nichts passt, ein neues Modul.
- **Neue LISP-Routinen nach Moeglichkeit in die LISP-Testsuite aufnehmen**: Jede neue LISP-Routine soll moeglichst einen Test bekommen. **Vorrang haben Routinen, die OHNE Zeichnung/Dateien testbar sind** (reine Berechnung, Parser, String-/Listen-Helfer, cfg-/JSON-Aufloesung) - die gehoeren als `tu-*`-Testblock in `tests/test_unit.lsp` (Befehl `TEST_UNIT`), der komplett file- und zeichnungsfrei laeuft. Zeichnungs-/geometrieabhaengige Routinen ueber ein Szenario-Modul (`tests/test_<name>.lsp` + Eintrag in `tests/alltests.json`, ausgefuehrt von `SSG_RUN_ALL_TESTS`) abdecken. Die reinen Berechnungspfade einer solchen Routine trotzdem, wo moeglich, zusaetzlich als file-freien Unit-Test herausziehen.
- **Headless-Testlauf**: Die Unit-Tests laufen ohne GUI ueber `bin/run_tests.bat --lisp` - das startet BricsCAD im Batch-Modus (`/nologo /b`) mit `tests/test_unit.scr` und fuehrt `TEST_UNIT` aus. **BricsCAD steht dafuer ueber die Umgebungsvariable `BRICSCAD` zur Verfuegung** (in `bin/setenv.bat` gesetzt, auf die installierte `bricscad.exe` zeigend); `run_tests.bat` bricht mit Hinweis ab, wenn sie fehlt. Ein neuer file-freier Unit-Test ist damit auch ohne interaktive BricsCAD-Sitzung fahrbar. (Python-seitige Tests siehe `.venv` + `pytest tests/`.)
- **Regelmaessiges Tote-Code-Review**: Wiederkehrend auf tote/nicht mehr verwendete LISP-Routinen pruefen und aufraeumen - vorzugsweise zu Wochenbeginn, wenn in der betreffenden Woche ohnehin an den LISP-Routinen gearbeitet wird. - **Regelmaessiges Tote-Code-Review**: Wiederkehrend auf tote/nicht mehr verwendete LISP-Routinen pruefen und aufraeumen - vorzugsweise zu Wochenbeginn, wenn in der betreffenden Woche ohnehin an den LISP-Routinen gearbeitet wird.
- **Wiederherstellungs-/Editierdaten gehoeren in XDATA/Journal, nicht in `.dbg`**: Alles, was zum Neuaufbau/Editieren eines Blocks noetig ist (z.B. das VF-Linienzug-Eingabe-Journal `SSG_VF_EDIT`), wird als XDATA am Block gefuehrt. Die `.dbg`-Dateien (`ssg_dbg.lsp`) sind reine AUSGABE, werden von keinem Befehl gelesen und bei jedem Lauf neu angelegt - nie als Datenquelle nutzen. - **Wiederherstellungs-/Editierdaten gehoeren in XDATA/Journal, nicht in `.dbg`**: Alles, was zum Neuaufbau/Editieren eines Blocks noetig ist (z.B. das VF-Linienzug-Eingabe-Journal `SSG_VF_EDIT`), wird als XDATA am Block gefuehrt. Die `.dbg`-Dateien (`ssg_dbg.lsp`) sind reine AUSGABE, werden von keinem Befehl gelesen und bei jedem Lauf neu angelegt - nie als Datenquelle nutzen.
- **Editierbarkeit: festes Schema vs. Sektions-Ruecksprung**: Konstrukte mit FESTER Feldstruktur (feste Anzahl/Typen von Werten, z.B. AS/ES/GF-Bogen/Vario-Kurve im VF-Linienzug) ueber ein deklaratives Schema als einzeln editierbare Records fuehren (Einzel-Glied-Edit per Feld-Splice). Konstrukte mit VARIABLEM Dialog-Fluss (datenabhaengige Feldzahl, z.B. Horizontal-Koerper/Auf-Ab-Foerderer) NICHT per fragilem "n Felder abschneiden"-Splice editieren, sondern per Sektions-Ruecksprung: ab dem gewaehlten Glied zuruecksetzen und live neu aufbauen. Ein fester Splice ueber eine datenabhaengige Feldgrenze ist genau das Muster des Winkel-Index-Bugs. - **Editierbarkeit: festes Schema vs. Sektions-Ruecksprung**: Konstrukte mit FESTER Feldstruktur (feste Anzahl/Typen von Werten, z.B. AS/ES/GF-Bogen/Vario-Kurve im VF-Linienzug) ueber ein deklaratives Schema als einzeln editierbare Records fuehren (Einzel-Glied-Edit per Feld-Splice). Konstrukte mit VARIABLEM Dialog-Fluss (datenabhaengige Feldzahl, z.B. Horizontal-Koerper/Auf-Ab-Foerderer) NICHT per fragilem "n Felder abschneiden"-Splice editieren, sondern per Sektions-Ruecksprung: ab dem gewaehlten Glied zuruecksetzen und live neu aufbauen. Ein fester Splice ueber eine datenabhaengige Feldgrenze ist genau das Muster des Winkel-Index-Bugs.
+913 -135
View File
File diff suppressed because it is too large Load Diff
+200 -118
View File
@@ -605,59 +605,20 @@
) )
) )
;;; ============================================================ ;;; ------------------------------------------------------------
;;; ENGINE - wird sowohl von ZAEHLE_SEP_SCAN als auch automatisch ;;; Alle INSERTs der Zeichnung in Carrier-, Scanner- und Separator-
;;; vor jedem CSV-/Sivas-Export aufgerufen (siehe export.lsp, ;;; Listen einsortieren.
;;; csv:run-export). ;;; Rueckgabe: (carriers scanlist seplist)
;;; ;;; carriers = Liste von Records (ename tagScan tagSep minx miny
;;; label = Anzeigepraefix fuer die Konsolenausgabe (z.B. "EXPORTCSV"). ;;; maxx maxy cx cy id label)
;;; verbose = T -> zusaetzlich je Carrier eine Detailzeile ausgeben ;;; scanlist/seplist = Liste von (ename einfuegepunkt)
;;; (nur fuer den interaktiven Befehl sinnvoll - der ;;; Ein Carrier ohne ermittelbare Bounding-Box wird gemeldet und
;;; automatische Aufruf aus csv:run-export laesst dies aus, ;;; uebersprungen (kann sonst nicht zugeordnet werden).
;;; damit die Export-Konsole kompakt bleibt). ;;; ------------------------------------------------------------
;;; (defun cs-sammle-bloecke ( / ss i ent ed nm bb cx cy lbl carrier-id
;;; Setzt als Nebenwirkung: sk pt carriers scanlist seplist)
;;; *cs-scanner-ergaenzt* - Liste (scanner-id carrier-id
;;; distanz-mm strittig-p
;;; zweiter-carrier-id zweite-distanz-mm)
;;; je per Abstand (nicht per BBox)
;;; zugeordnetem Scanner.
;;; *cs-scanner-warnung-by-handle* - Assoc-Liste (handle . warnungstext)
;;; fuer strittige Scanner-Zuordnungen,
;;; von csv:block-to-json ausgelesen.
;;; *cs-korrekturen* - Liste (typ-wort sensor-id alt-id
;;; neu-id) je Sensor, dessen gespeicherte
;;; ZUORDNUNG nicht zur Geometrie passte
;;; und ueberschrieben wurde (Report).
;;; ============================================================
(defun cs-zuordnung-lauf (label verbose /
mss ss i ent ed nm bb cx cy lbl carrier-id
carriers grid scanlist seplist sk pt
scanCounts sepCounts n-scan-unassigned n-sep-unassigned
n-strittig n-zuordnung-fehlt alt
s sent spt res mode rec best-d second-rec second-d
vorgabe-id vorgabe-rec
scanner-id contested handle
en nm2 ns nsep newS newP okS okP eintrag)
(setq carriers nil scanlist nil seplist nil
n-zuordnung-fehlt 0 n-scan-unassigned 0 n-sep-unassigned 0 n-strittig 0)
(setq *cs-scanner-ergaenzt* nil)
(setq *cs-scanner-warnung-by-handle* nil)
(setq *cs-korrekturen* nil)
(setq *senstol* (cs-senstol))
;; Alte Markierungskreise entfernen (Idempotenz - Sensoren, die in
;; einem frueheren Lauf unzuordenbar waren, koennen inzwischen einen
;; Carrier bekommen haben oder umgekehrt).
(setq mss (ssget "_X" (list (cons 0 "CIRCLE") (cons 8 *mark-layer*))))
(if mss (command "_.ERASE" mss ""))
(vl-catch-all-apply '(lambda () (command "_.REGEN")))
(princ (ssg-textf "sens-start" (list label)))
(setq ss (ssget "_X" '((0 . "INSERT")))) (setq ss (ssget "_X" '((0 . "INSERT"))))
(if (null ss) (if ss
(princ (ssg-textf "sens-no-inserts" (list label)))
(progn (progn
(setq i 0) (setq i 0)
(while (< i (sslength ss)) (while (< i (sslength ss))
@@ -693,11 +654,70 @@
) )
(setq i (1+ i)) (setq i (1+ i))
) )
)
)
(list carriers scanlist seplist)
)
(setq grid (cs-grid-build carriers)) ;;; ------------------------------------------------------------
(setq scanCounts nil sepCounts nil) ;;; Abweichende gespeicherte ZUORDNUNG als Korrektur in
;;; *cs-korrekturen* vermerken, BEVOR sie ueberschrieben wird
;;; (Selbstheilung eingefrorener/verwaister Zuordnungen). typ-wort
;;; ist "Scanner" oder "Separator"; neu-id die geometrisch/vorgegeben
;;; ermittelte Carrier-ID.
;;; ------------------------------------------------------------
(defun cs-korrektur-vermerken (sent typ-wort neu-id / alt)
(setq alt (cs-get-att-str sent *zuordnung-tag*))
(if (and alt (/= alt "") (/= alt neu-id))
(setq *cs-korrekturen*
(cons (list typ-wort (cs-get-att-str sent "ID") alt neu-id)
*cs-korrekturen*)))
)
;; --- Scanner zuordnen (inkl. Distanz-Fallback + Strittig-Erkennung) --- ;;; ------------------------------------------------------------
;;; Strittige Distanz-Zuordnung eines Scanners protokollieren:
;;; Eintrag in *cs-scanner-ergaenzt* (immer) und - bei zu knappem
;;; Abstand zum zweitnaechsten Carrier - eine Warnung in
;;; *cs-scanner-warnung-by-handle* (von csv:block-to-json gelesen).
;;; Rueckgabe: T, wenn die Zuordnung strittig ist.
;;; ------------------------------------------------------------
(defun cs-scanner-distanz-melden (sent rec best-d second-rec second-d /
scanner-id contested handle)
(setq scanner-id (cs-get-att-str sent "ID"))
(setq contested (and second-rec
(<= (- second-d best-d) (cs-strittig-diff-mm))))
(setq *cs-scanner-ergaenzt*
(cons (list scanner-id (nth 9 rec) (fix (+ best-d 0.5))
contested
(if contested (nth 9 second-rec) nil)
(if contested (fix (+ second-d 0.5)) nil))
*cs-scanner-ergaenzt*))
(if contested
(progn
(setq handle (cdr (assoc 5 (entget sent))))
(setq *cs-scanner-warnung-by-handle*
(cons (cons handle
(strcat "Distanz-Zuordnung unsicher: "
(nth 9 rec) " (" (itoa (fix (+ best-d 0.5))) "mm) vs. "
(nth 9 second-rec) " (" (itoa (fix (+ second-d 0.5))) "mm)"))
*cs-scanner-warnung-by-handle*))
)
)
contested
)
;;; ------------------------------------------------------------
;;; Scanner den Carriern zuordnen (rein geometrisch: BBox, sonst
;;; naechster Carrier). Setzt je zugeordnetem Scanner das ZUORDNUNG-
;;; Attribut und fuellt *cs-scanner-ergaenzt*/*cs-scanner-warnung-by-
;;; handle* (Distanz-Fallback + Strittig-Report).
;;; Rueckgabe: (scanCounts n-scan-unassigned n-strittig n-zuordnung-fehlt)
;;; ------------------------------------------------------------
(defun cs-scanner-zuordnen (scanlist carriers grid /
scanCounts n-scan-unassigned n-strittig
n-zuordnung-fehlt s sent spt res mode rec
best-d second-rec second-d)
(setq scanCounts nil n-scan-unassigned 0 n-strittig 0 n-zuordnung-fehlt 0)
(foreach s scanlist (foreach s scanlist
(setq sent (car s) spt (cadr s)) (setq sent (car s) spt (cadr s))
(setq res (cs-resolve-sensor spt carriers grid)) (setq res (cs-resolve-sensor spt carriers grid))
@@ -710,56 +730,35 @@
((null rec) nil) ;; Sicherheitsnetz - tritt bei bbox/distance nicht auf ((null rec) nil) ;; Sicherheitsnetz - tritt bei bbox/distance nicht auf
(t (t
(setq scanCounts (cs-bump scanCounts (car rec))) (setq scanCounts (cs-bump scanCounts (car rec)))
;; Abweichende gespeicherte ZUORDNUNG als Korrektur vermerken, (cs-korrektur-vermerken sent "Scanner" (nth 9 rec))
;; bevor sie unten ueberschrieben wird (Selbstheilung eingefrorener
;; oder verwaister Zuordnungen).
(setq alt (cs-get-att-str sent *zuordnung-tag*))
(if (and alt (/= alt "") (/= alt (nth 9 rec)))
(setq *cs-korrekturen*
(cons (list "Scanner" (cs-get-att-str sent "ID") alt (nth 9 rec))
*cs-korrekturen*)))
(if (member mode '(bbox distance)) (if (member mode '(bbox distance))
(if (not (cs-set-att sent *zuordnung-tag* (nth 9 rec))) (if (not (cs-set-att sent *zuordnung-tag* (nth 9 rec)))
(setq n-zuordnung-fehlt (1+ n-zuordnung-fehlt)))) (setq n-zuordnung-fehlt (1+ n-zuordnung-fehlt))))
(if (eq mode 'distance) (if (eq mode 'distance)
(progn (if (cs-scanner-distanz-melden sent rec best-d second-rec second-d)
(setq scanner-id (cs-get-att-str sent "ID")) (setq n-strittig (1+ n-strittig))))
(setq contested (and second-rec
(<= (- second-d best-d) (cs-strittig-diff-mm))))
(setq *cs-scanner-ergaenzt*
(cons (list scanner-id (nth 9 rec) (fix (+ best-d 0.5))
contested
(if contested (nth 9 second-rec) nil)
(if contested (fix (+ second-d 0.5)) nil))
*cs-scanner-ergaenzt*))
(if contested
(progn
(setq n-strittig (1+ n-strittig))
(setq handle (cdr (assoc 5 (entget sent))))
(setq *cs-scanner-warnung-by-handle*
(cons (cons handle
(strcat "Distanz-Zuordnung unsicher: "
(nth 9 rec) " (" (itoa (fix (+ best-d 0.5))) "mm) vs. "
(nth 9 second-rec) " (" (itoa (fix (+ second-d 0.5))) "mm)"))
*cs-scanner-warnung-by-handle*))
)
)
)
)
) )
) )
) )
(setq *cs-scanner-ergaenzt* (reverse *cs-scanner-ergaenzt*)) (setq *cs-scanner-ergaenzt* (reverse *cs-scanner-ergaenzt*))
(list scanCounts n-scan-unassigned n-strittig n-zuordnung-fehlt)
)
;; --- Separatoren zuordnen (gleiche Logik, ohne Strittig-Report - ;;; ------------------------------------------------------------
;; der Distanz-Fallback stellt hier nur sicher, dass die ;;; Separatoren den Carriern zuordnen. Wie cs-scanner-zuordnen, aber
;; ANZAHL_SEPARATOR-Summe auch bei Sonderfaellen stimmt). --- ;;; ohne Strittig-Report; zusaetzlich hat eine festgenagelte Zuordnung
;;; (verpackter Separator, *cs-sep-fix-by-handle*) Vorrang vor der
;;; Geometrie - nur wenn der genannte Carrier nicht (mehr) existiert,
;;; wird geometrisch zugeordnet. Bei mode 'vorgabe wird der geschriebene
;;; Wert NICHT als Korrektur gemeldet (er ist die Vorgabe selbst).
;;; Rueckgabe: (sepCounts n-sep-unassigned n-zuordnung-fehlt)
;;; ------------------------------------------------------------
(defun cs-separatoren-zuordnen (seplist carriers grid /
sepCounts n-sep-unassigned n-zuordnung-fehlt
s sent spt res mode rec vorgabe-id vorgabe-rec)
(setq sepCounts nil n-sep-unassigned 0 n-zuordnung-fehlt 0)
(foreach s seplist (foreach s seplist
(setq sent (car s) spt (cadr s)) (setq sent (car s) spt (cadr s))
;; Festgenagelte Zuordnung (verpackter Separator, Carrier bekannt -
;; siehe *cs-sep-fix-by-handle*) hat Vorrang vor der Geometrie. Nur
;; wenn der genannte Carrier gar nicht (mehr) existiert, wird
;; geometrisch zugeordnet.
(setq vorgabe-id (cdr (assoc (cdr (assoc 5 (entget sent))) *cs-sep-fix-by-handle*))) (setq vorgabe-id (cdr (assoc (cdr (assoc 5 (entget sent))) *cs-sep-fix-by-handle*)))
(setq vorgabe-rec (cs-carrier-by-id vorgabe-id carriers)) (setq vorgabe-rec (cs-carrier-by-id vorgabe-id carriers))
(if vorgabe-rec (if vorgabe-rec
@@ -776,49 +775,56 @@
((null rec) nil) ;; Sicherheitsnetz - tritt bei bbox/distance nicht auf ((null rec) nil) ;; Sicherheitsnetz - tritt bei bbox/distance nicht auf
(t (t
(setq sepCounts (cs-bump sepCounts (car rec))) (setq sepCounts (cs-bump sepCounts (car rec)))
;; Abweichung nur bei geometrischer Zuordnung als Korrektur melden - (if (not (eq mode 'vorgabe))
;; bei mode 'vorgabe ist der geschriebene Wert die Vorgabe selbst, (cs-korrektur-vermerken sent "Separator" (nth 9 rec)))
;; keine Korrektur einer Fehl-Zuordnung.
(setq alt (cs-get-att-str sent *zuordnung-tag*))
(if (and (not (eq mode 'vorgabe)) alt (/= alt "") (/= alt (nth 9 rec)))
(setq *cs-korrekturen*
(cons (list "Separator" (cs-get-att-str sent "ID") alt (nth 9 rec))
*cs-korrekturen*)))
(if (member mode '(vorgabe bbox distance)) (if (member mode '(vorgabe bbox distance))
(if (not (cs-set-att sent *zuordnung-tag* (nth 9 rec))) (if (not (cs-set-att sent *zuordnung-tag* (nth 9 rec)))
(setq n-zuordnung-fehlt (1+ n-zuordnung-fehlt)))) (setq n-zuordnung-fehlt (1+ n-zuordnung-fehlt))))
) )
) )
) )
(list sepCounts n-sep-unassigned n-zuordnung-fehlt)
)
;; --- ANZAHL_SCANNER/ANZAHL_SEPARATOR je Carrier schreiben --- ;;; ------------------------------------------------------------
;;; ANZAHL_SCANNER/ANZAHL_SEPARATOR je Carrier schreiben.
;;; scanCounts/sepCounts = Assoc-Listen (carrier-ename . anzahl).
;;; ANZAHL_SEPARATOR = nur die tatsaechlich platzierten Separator-
;;; Symbole (Separator_SP/S-LP); der intern in der GF/VF-Kette
;;; gezeichnete 300-mm-Block Staustrecke_Separator_SP ist nur ein
;;; Kettenglied und wird bewusst NICHT mitgezaehlt.
;;; verbose = T -> je Carrier eine Detailzeile ausgeben.
;;; ------------------------------------------------------------
(defun cs-anzahl-schreiben (carriers scanCounts sepCounts verbose /
rec en nm2 ns nsep okS okP)
(foreach rec carriers (foreach rec carriers
(setq en (car rec)) (setq en (car rec))
(setq nm2 (cdr (assoc 2 (entget en)))) (setq nm2 (cdr (assoc 2 (entget en))))
(setq ns (cond ((assoc en scanCounts) (cdr (assoc en scanCounts))) (t 0))) (setq ns (cond ((assoc en scanCounts) (cdr (assoc en scanCounts))) (t 0)))
(setq nsep (cond ((assoc en sepCounts) (cdr (assoc en sepCounts))) (t 0))) (setq nsep (cond ((assoc en sepCounts) (cdr (assoc en sepCounts))) (t 0)))
(setq newS ns) (setq okS (cs-set-att en (nth 1 rec) (itoa ns)))
;; ANZAHL_SEPARATOR = nur die tatsaechlich platzierten Separator- (setq okP (cs-set-att en (nth 2 rec) (itoa nsep)))
;; Symbole (Separator_SP/S-LP), die diesem Carrier zugeordnet sind.
;; Der intern in der GF/VF-Kette gezeichnete 300-mm-Block
;; Staustrecke_Separator_SP ist nur ein Kettenglied (Foerder-/
;; Uebergabestueck), KEIN eigenstaendiger Separator - er wird bewusst
;; NICHT mitgezaehlt (Entscheidung: nur platzierte Symbole zaehlen).
(setq newP nsep)
(setq okS (cs-set-att en (nth 1 rec) (itoa newS)))
(setq okP (cs-set-att en (nth 2 rec) (itoa newP)))
(if verbose (if verbose
(princ (ssg-textf "sens-carrier-zeile" (princ (ssg-textf "sens-carrier-zeile"
(list nm2 (nth 9 rec) (itoa newS) (itoa newP))))) (list nm2 (nth 9 rec) (itoa ns) (itoa nsep)))))
(if (not (and okS okP)) (if (not (and okS okP))
(princ (ssg-textf "sens-carrier-fehler" (list nm2)))) (princ (ssg-textf "sens-carrier-fehler" (list nm2))))
) )
)
;; --- Zusammenfassung --- ;;; ------------------------------------------------------------
;;; Abschliessende Konsolen-Zusammenfassung ausgeben: Gesamtzahlen,
;;; per Distanz ergaenzte Scanner (*cs-scanner-ergaenzt*), strittige
;;; Zuordnungen, korrigierte ZUORDNUNGen (*cs-korrekturen*) sowie
;;; markierte unzuordenbare und fehlgeschlagene Attributschreibungen.
;;; ------------------------------------------------------------
(defun cs-bericht-ausgeben (label n-carrier n-scan n-scan-unassigned
n-sep n-sep-unassigned n-strittig
n-zuordnung-fehlt / eintrag)
(princ (ssg-textf "sens-summary" (princ (ssg-textf "sens-summary"
(list label (itoa (length carriers)) (list label (itoa n-carrier)
(itoa (length scanlist)) (itoa n-scan-unassigned) (itoa n-scan) (itoa n-scan-unassigned)
(itoa (length seplist)) (itoa n-sep-unassigned)))) (itoa n-sep) (itoa n-sep-unassigned))))
(if *cs-scanner-ergaenzt* (if *cs-scanner-ergaenzt*
(progn (progn
@@ -855,6 +861,82 @@
(if (> n-zuordnung-fehlt 0) (if (> n-zuordnung-fehlt 0)
(princ (ssg-textf "sens-zuordnung-fehlt" (list label (itoa n-zuordnung-fehlt))))) (princ (ssg-textf "sens-zuordnung-fehlt" (list label (itoa n-zuordnung-fehlt)))))
) )
;;; ============================================================
;;; ENGINE - wird sowohl von ZAEHLE_SEP_SCAN als auch automatisch
;;; vor jedem CSV-/Sivas-Export aufgerufen (siehe export.lsp,
;;; csv:run-export).
;;;
;;; label = Anzeigepraefix fuer die Konsolenausgabe (z.B. "EXPORTCSV").
;;; verbose = T -> zusaetzlich je Carrier eine Detailzeile ausgeben
;;; (nur fuer den interaktiven Befehl sinnvoll - der
;;; automatische Aufruf aus csv:run-export laesst dies aus,
;;; damit die Export-Konsole kompakt bleibt).
;;;
;;; Setzt als Nebenwirkung:
;;; *cs-scanner-ergaenzt* - Liste (scanner-id carrier-id
;;; distanz-mm strittig-p
;;; zweiter-carrier-id zweite-distanz-mm)
;;; je per Abstand (nicht per BBox)
;;; zugeordnetem Scanner.
;;; *cs-scanner-warnung-by-handle* - Assoc-Liste (handle . warnungstext)
;;; fuer strittige Scanner-Zuordnungen,
;;; von csv:block-to-json ausgelesen.
;;; *cs-korrekturen* - Liste (typ-wort sensor-id alt-id
;;; neu-id) je Sensor, dessen gespeicherte
;;; ZUORDNUNG nicht zur Geometrie passte
;;; und ueberschrieben wurde (Report).
;;; ============================================================
(defun cs-zuordnung-lauf (label verbose /
mss bloecke carriers scanlist seplist grid
scan-res sep-res scanCounts sepCounts
n-scan-unassigned n-sep-unassigned
n-strittig n-zuordnung-fehlt)
(setq *cs-scanner-ergaenzt* nil)
(setq *cs-scanner-warnung-by-handle* nil)
(setq *cs-korrekturen* nil)
(setq *senstol* (cs-senstol))
;; Alte Markierungskreise entfernen (Idempotenz - Sensoren, die in
;; einem frueheren Lauf unzuordenbar waren, koennen inzwischen einen
;; Carrier bekommen haben oder umgekehrt).
(setq mss (ssget "_X" (list (cons 0 "CIRCLE") (cons 8 *mark-layer*))))
(if mss (command "_.ERASE" mss ""))
(vl-catch-all-apply '(lambda () (command "_.REGEN")))
(princ (ssg-textf "sens-start" (list label)))
;; INSERTs in Carrier/Scanner/Separator einsortieren.
(setq bloecke (cs-sammle-bloecke))
(setq carriers (nth 0 bloecke)
scanlist (nth 1 bloecke)
seplist (nth 2 bloecke))
(if (and (null carriers) (null scanlist) (null seplist))
(princ (ssg-textf "sens-no-inserts" (list label)))
(progn
(setq grid (cs-grid-build carriers))
;; Scanner zuordnen (BBox, sonst naechster Carrier; Distanz-Fallback
;; + Strittig-Report). Zaehler kommen als Liste zurueck.
(setq scan-res (cs-scanner-zuordnen scanlist carriers grid))
(setq scanCounts (nth 0 scan-res)
n-scan-unassigned (nth 1 scan-res)
n-strittig (nth 2 scan-res)
n-zuordnung-fehlt (nth 3 scan-res))
;; Separatoren zuordnen (Vorgabe hat Vorrang vor der Geometrie).
(setq sep-res (cs-separatoren-zuordnen seplist carriers grid))
(setq sepCounts (nth 0 sep-res)
n-sep-unassigned (nth 1 sep-res)
n-zuordnung-fehlt (+ n-zuordnung-fehlt (nth 2 sep-res)))
;; ANZAHL_SCANNER/ANZAHL_SEPARATOR je Carrier schreiben und Bericht.
(cs-anzahl-schreiben carriers scanCounts sepCounts verbose)
(cs-bericht-ausgeben label (length carriers)
(length scanlist) n-scan-unassigned
(length seplist) n-sep-unassigned
n-strittig n-zuordnung-fehlt)
)
) )
(princ) (princ)
) )
+171 -79
View File
@@ -297,43 +297,76 @@
) )
;; --- Lokalen Punkt (relativ zum Block-Ursprung) unter Beruecksichtigung der ;; --- Lokalen Punkt (relativ zum Block-Ursprung) unter Beruecksichtigung der
;; Eltern-Rotation (reine Z-Drehung um den Einfuegepunkt) in Weltkoordinaten ;; VOLLEN 3D-Orientierung des Elternblocks in Weltkoordinaten umrechnen ---
;; umrechnen. Rueckgabe: (x y z) --- ;; parent-R = volle 3x3-Rotationsmatrix (mat3-* in ssg_core.lsp), nicht nur
(defun csv:local-to-world (parent-pt parent-rot-rad local-pt / c s lx ly lz pz) ;; ein Z-Drehwinkel: ein Elternblock kann selbst eine gekippte Extrusion
(setq c (cos parent-rot-rad) s (sin parent-rot-rad)) ;; tragen (z.B. ein Separator-Proxy, der aus dem geneigten Abschnitt einer
(setq lx (car local-pt) ly (cadr local-pt)) ;; VF-/GF-Kette neu zusammengesetzt wurde, siehe csv:block-to-json) - eine
;; reine Z-Drehung wuerde dessen Sub-Koordinatensysteme (K1-K4/KS_EIN/
;; KS_AUS) an einen falschen, teils gespiegelten Weltpunkt legen. Fuer einen
;; flachen Elternblock (Extrusion (0 0 1)) ist parent-R = Rz(rotation) und
;; das Ergebnis identisch zur frueheren reinen Z-Drehung. Rueckgabe: (x y z).
(defun csv:local-to-world (parent-pt parent-R local-pt / lz pz rot-pt)
(setq lz (if (caddr local-pt) (caddr local-pt) 0.0)) (setq lz (if (caddr local-pt) (caddr local-pt) 0.0))
(setq pz (if (caddr parent-pt) (caddr parent-pt) 0.0)) (setq pz (if (caddr parent-pt) (caddr parent-pt) 0.0))
(setq rot-pt (mat3-mul-vec3 parent-R (list (car local-pt) (cadr local-pt) lz)))
(list (list
(+ (car parent-pt) (- (* lx c) (* ly s))) (+ (car parent-pt) (car rot-pt))
(+ (cadr parent-pt) (+ (* lx s) (* ly c))) (+ (cadr parent-pt) (cadr rot-pt))
(+ pz lz) (+ pz (caddr rot-pt))
) )
) )
;; --- K1-K4-Koordinatensysteme eines Blocks als Positions-Strings lesen --- ;; --- K1-K4-Koordinatensysteme eines Blocks als Positions-Strings lesen ---
;; ename = Entity-Name des platzierten INSERT (Omniflo Bogen/Weiche/Gerade). ;; ename = Entity-Name des platzierten INSERT (Omniflo Bogen/Weiche/Gerade).
;; pt/rot-rad = bereits ermittelte Welt-Position/-Rotation (radiant) des ;; pt/parent-R = bereits ermittelte Welt-Position/volle 3x3-Orientierung des
;; Eltern-INSERT (siehe csv:block-to-json), keine erneute Abfrage. ;; Eltern-INSERT (siehe csv:block-to-json), keine erneute Abfrage.
;; Sucht in der Block-DEFINITION (wie omni:get-block-hoehe) nach direkten ;; Sucht in der Block-DEFINITION (wie omni:get-block-hoehe) nach direkten
;; Sub-INSERTs "K1".."K4" und rechnet deren lokale Position ueber die ;; Sub-INSERTs "K1".."K4" und rechnet deren lokale Position ueber die volle
;; Eltern-Rotation (reine Z-Drehung) in Weltkoordinaten um. Es wird nur die ;; Eltern-Orientierung in Weltkoordinaten um (csv:local-to-world). Es wird
;; POSITION kodiert (12-Zeichen-String, csv:trans-encode) - keine Rotation. ;; nur die POSITION kodiert (12-Zeichen-String, csv:trans-encode) - keine
;; Rotation.
;; Zusaetzlich werden Strecken-Koordinatensysteme KS_EIN->K1 und KS_AUS->K2 ;; Zusaetzlich werden Strecken-Koordinatensysteme KS_EIN->K1 und KS_AUS->K2
;; (inkl. der KSYS_-Varianten, siehe ks-normalize-name in vf_core.lsp) gemappt, ;; (inkl. der KSYS_-Varianten, siehe ks-normalize-name in vf_core.lsp) gemappt,
;; sofern kein echter K1/K2 am Block vorhanden ist (echte K-Bloecke haben ;; sofern kein echter K1/K2 am Block vorhanden ist (echte K-Bloecke haben
;; Vorrang: sie werden vorne angehaengt, csv:k-kos liest per assoc den ersten). ;; Vorrang: sie werden vorne angehaengt, csv:k-kos liest per assoc den ersten).
;; Rueckgabe: Assoc-Liste (("K1" . "<pos-string>") ("K2" . ...) ...), nur ;; Rueckgabe: Assoc-Liste (("K1" . "<pos-string>") ("K2" . ...) ...), nur
;; fuer tatsaechlich vorhandene K-Bloecke (nicht jedes Element hat alle 4). ;; fuer tatsaechlich vorhandene K-Bloecke (nicht jedes Element hat alle 4).
(defun csv:get-k-kos-strings (ename pt rot-rad / ed bname blk-tbl sub-ent sub-ed ;;
sub-bname sub-pt world-pt target is-real result) ;; Gruppe-10 eines Sub-INSERTs ist OCS-lokal zu SEINER EIGENEN Extrusion
;; (Gruppe 210), nicht zwingend zur flachen XY-Ebene der Elterndefinition -
;; z.B. KS_AUS von SC_Entladung (BTMT-Entladung), das schraeg in 3D sitzt.
;; mat3-ocs-matrix/mat3-mul-vec3 (ssg_core.lsp, Arbitrary Axis Algorithm)
;; wandeln das erst in echte lokale Koordinaten um, bevor csv:local-to-world
;; mit der Eltern-Rotation weiterrechnet - derselbe Fix wie bereits in
;; ssg-collect-nested-inserts-rel (dort ausfuehrlich begruendet). Ohne diese
;; Umrechnung landete K2 tief unterhalb statt oberhalb des Elements (realer
;; Bug, sichtbar in der Materialfluss-Draufsicht sps_skel/HundM05_bbs.svg).
;; Fuer Extrusion=(0 0 1) (Normalfall, z.B. KS_EIN) reduziert sich das exakt
;; auf die alte reine Uebernahme - unveraendertes Verhalten dort. Denselben
;; Grund hat parent-R als VOLLE Matrix statt eines Z-Drehwinkels: ein
;; Elternblock (z.B. ein Separator-Proxy, ueber ssg-collect-nested-inserts
;; aus dem geneigten Abschnitt einer VF-/GF-Kette neu zusammengesetzt) kann
;; selbst eine gekippte Extrusion tragen - eine reine Z-Drehung des Eltern-
;; INSERT legte dessen K1/K2 dann an einen falschen, teils gespiegelten
;; Weltpunkt (realer Bug: K1 landete beim Vorzeichen von X/Y gespiegelt weit
;; ausserhalb der eigenen Boundingbox, sichtbar als fehlendes K-Kreuz in
;; sps_skel/HundM05_bbs.svg trotz gefuelltem K1 in der CSV).
(defun csv:get-k-kos-strings (ename pt parent-R / ed bname blk-tbl sub-ent sub-ed
sub-bname sub-pt sub-extrusion sub-pt-real world-pt
target is-real result)
(setq ed (entget ename)) (setq ed (entget ename))
(setq bname (cdr (assoc 2 ed))) (setq bname (cdr (assoc 2 ed)))
(setq blk-tbl (tblsearch "BLOCK" bname)) (setq blk-tbl (tblsearch "BLOCK" bname))
(setq result nil) (setq result nil)
(if blk-tbl (if blk-tbl
(progn (progn
(setq sub-ent (entnext (cdr (assoc -2 blk-tbl)))) ;; (assoc -2) ist bereits das ERSTE Entity der Blockdefinition, NICHT
;; der BLOCK-Header - direkt hier starten, kein entnext davor (das
;; uebersprang sonst ein K1/KS_EIN, das zufaellig als erstes Entity in
;; der Blockdefinition steht - derselbe Bug wie frueher in
;; ssg-collect-nested-inserts-rel, siehe dortiger Kommentar).
(setq sub-ent (cdr (assoc -2 blk-tbl)))
(while sub-ent (while sub-ent
(setq sub-ed (entget sub-ent)) (setq sub-ed (entget sub-ent))
(if (= (cdr (assoc 0 sub-ed)) "INSERT") (if (= (cdr (assoc 0 sub-ed)) "INSERT")
@@ -352,7 +385,11 @@
(if (and target (or is-real (not (assoc target result)))) (if (and target (or is-real (not (assoc target result))))
(progn (progn
(setq sub-pt (cdr (assoc 10 sub-ed))) (setq sub-pt (cdr (assoc 10 sub-ed)))
(setq world-pt (csv:local-to-world pt rot-rad sub-pt)) (if (null (caddr sub-pt))
(setq sub-pt (list (car sub-pt) (cadr sub-pt) 0.0)))
(setq sub-extrusion (cond ((cdr (assoc 210 sub-ed))) ('(0.0 0.0 1.0))))
(setq sub-pt-real (mat3-mul-vec3 (mat3-ocs-matrix sub-extrusion) sub-pt))
(setq world-pt (csv:local-to-world pt parent-R sub-pt-real))
(setq result (cons (setq result (cons
(cons target (cons target
(csv:trans-encode (csv:trans-encode
@@ -483,14 +520,17 @@
(setq ed (entget ename)) (setq ed (entget ename))
(setq bname (cdr (assoc 2 ed))) (setq bname (cdr (assoc 2 ed)))
(setq result nil) (setq result nil)
;; Beide Enden im Wrapper-Frame holen und in Weltkoordinaten (reine ;; Beide Enden im Wrapper-Frame holen und in Weltkoordinaten umrechnen.
;; Z-Drehung) umrechnen. AS traegt das KS_EIN, ES das KS_AUS - jeweils aus ;; VF_n/GF_n-Wrapper stehen an ihrem eigenen top-level INSERT immer flach
;; dem RICHTIGEN Element (csv:vfgf-ks-loc; beide Elemente fuehren beide ;; (Extrusion (0 0 1) - siehe csv:sep-proxies-erzeugen), darum genuegt hier
;; KS-Typen). ;; weiterhin die reine Z-Drehungsmatrix (mat3-rz) statt der vollen
;; Elternorientierung, die csv:get-k-kos-strings inzwischen braucht (siehe
;; dort). AS traegt das KS_EIN, ES das KS_AUS - jeweils aus dem RICHTIGEN
;; Element (csv:vfgf-ks-loc; beide Elemente fuehren beide KS-Typen).
(setq as-loc (csv:vfgf-ks-loc bname "AS_ELEMENT*" "KS_EIN,KSYS_EIN")) (setq as-loc (csv:vfgf-ks-loc bname "AS_ELEMENT*" "KS_EIN,KSYS_EIN"))
(setq es-loc (csv:vfgf-ks-loc bname "ES_ELEMENT*" "KS_AUS,KSYS_AUS")) (setq es-loc (csv:vfgf-ks-loc bname "ES_ELEMENT*" "KS_AUS,KSYS_AUS"))
(if as-loc (setq as-world (csv:local-to-world pt rot-rad as-loc))) (if as-loc (setq as-world (csv:local-to-world pt (mat3-rz rot-rad) as-loc)))
(if es-loc (setq es-world (csv:local-to-world pt rot-rad es-loc))) (if es-loc (setq es-world (csv:local-to-world pt (mat3-rz rot-rad) es-loc)))
;; Eingang/Ausgang festlegen (siehe Richtung im Kopfkommentar). ;; Eingang/Ausgang festlegen (siehe Richtung im Kopfkommentar).
(cond (cond
;; GF: Richtung ueber die Hoehe - hoeheres Z = Eingang, tieferes Z = Ausgang. ;; GF: Richtung ueber die Hoehe - hoeheres Z = Eingang, tieferes Z = Ausgang.
@@ -609,7 +649,8 @@
;; "k1".."k4" (KOS-Strings der Koordinatensysteme im Block, siehe ;; "k1".."k4" (KOS-Strings der Koordinatensysteme im Block, siehe
;; csv:get-k-kos-strings) werden immer geschrieben, unabhaengig von ;; csv:get-k-kos-strings) werden immer geschrieben, unabhaengig von
;; include-bbox -- Kodierung ueber csv:kos-encode (siehe dort). ;; include-bbox -- Kodierung ueber csv:kos-encode (siehe dort).
(defun csv:block-to-json (ename include-bbox / ed blk-name layer pt rotation handle attribs bbox bbox-json (defun csv:block-to-json (ename include-bbox / ed blk-name layer pt rotation extrusion R-world
handle attribs bbox bbox-json
insert-quat insert-kos k-list warnung-json warnung-eintrag insert-quat insert-kos k-list warnung-json warnung-eintrag
zuordnung-fix-json zuordnung-fix-eintrag sepliste-json sepliste-app sepliste zuordnung-fix-json zuordnung-fix-eintrag sepliste-json sepliste-app sepliste
sepliste-teile sepliste-first) sepliste-teile sepliste-first)
@@ -620,12 +661,27 @@
(setq rotation (cdr (assoc 50 ed))) (setq rotation (cdr (assoc 50 ed)))
(setq handle (cdr (assoc 5 ed))) (setq handle (cdr (assoc 5 ed)))
(if (null rotation) (setq rotation 0.0)) (if (null rotation) (setq rotation 0.0))
(if (null (caddr pt)) (setq pt (list (car pt) (cadr pt) 0.0)))
;; Gruppe 10 ist OCS-lokal zur EIGENEN Extrusion dieses INSERT (Gruppe
;; 210) - fuer die allermeisten Bloecke (0 0 1) = Welt, aber ein Separator-
;; Proxy, der ueber ssg-collect-nested-inserts aus dem geneigten Abschnitt
;; einer VF-/GF-Kette neu zusammengesetzt wurde (csv:sep-proxies-erzeugen),
;; kann eine echt gekippte Extrusion tragen. Erst in echte Weltkoordinaten/
;; -orientierung umrechnen (Arbitrary Axis Algorithm, ssg_core.lsp), bevor
;; irgendetwas darauf aufbaut - sonst zeigt "x"/"y"/"z" (und darueber K1-K4,
;; siehe csv:get-k-kos-strings) einen falschen, teils spiegelverkehrten
;; Weltpunkt (realer Bug: Separator :4/6/8/10/12 in HundM05, K1 landete am
;; Vorzeichen gespiegelt weit ausserhalb der eigenen Boundingbox). Fuer
;; Extrusion=(0 0 1) reduziert sich das exakt auf die alte Uebernahme.
(setq extrusion (cond ((cdr (assoc 210 ed))) ('(0.0 0.0 1.0))))
(setq pt (mat3-mul-vec3 (mat3-ocs-matrix extrusion) pt))
(setq R-world (mat3-from-normal-rotation extrusion rotation))
(setq attribs (csv:read-attribs ename)) (setq attribs (csv:read-attribs ename))
(setq insert-quat (csv:z-angle-to-quat (* (/ rotation pi) 180.0))) (setq insert-quat (csv:z-angle-to-quat (* (/ rotation pi) 180.0)))
(setq insert-kos (csv:kos-encode (setq insert-kos (csv:kos-encode
(car pt) (cadr pt) (if (caddr pt) (caddr pt) 0.0) (car pt) (cadr pt) (if (caddr pt) (caddr pt) 0.0)
(nth 0 insert-quat) (nth 1 insert-quat) (nth 2 insert-quat) (nth 3 insert-quat))) (nth 0 insert-quat) (nth 1 insert-quat) (nth 2 insert-quat) (nth 3 insert-quat)))
(setq k-list (csv:get-k-kos-strings ename pt rotation)) (setq k-list (csv:get-k-kos-strings ename pt R-world))
;; Omniflo-Geraden fuehren keine echten K-Bloecke - K1/K2 aus der Geometrie ;; Omniflo-Geraden fuehren keine echten K-Bloecke - K1/K2 aus der Geometrie
;; (Anfang/Ende ueber Laenge + Rotation) synthetisieren, damit sie im Export ;; (Anfang/Ende ueber Laenge + Rotation) synthetisieren, damit sie im Export
;; wie echte Anschluss-Koordinatensysteme erscheinen und in der Nachbar- ;; wie echte Anschluss-Koordinatensysteme erscheinen und in der Nachbar-
@@ -1088,6 +1144,13 @@
;; ils-insert-station in SSG_LIB_Commands.lsp). Anders als Separator/ ;; ils-insert-station in SSG_LIB_Commands.lsp). Anders als Separator/
;; Scanner werden sie von KEINEM anderen Element mitgezaehlt - export_csv.py ;; Scanner werden sie von KEINEM anderen Element mitgezaehlt - export_csv.py
;; UND export_sivas.py fuehren sie beide als eigene TeileArt-Zeilen. ;; UND export_sivas.py fuehren sie beide als eigene TeileArt-Zeilen.
;; Eingang*/Ausgang* = Materialfluss-Ein-/Ausgangssymbole (siehe
;; pattern_eingang/pattern_ausgang in cfg/export.cfg, ils-insert-station in
;; SSG_LIB_Commands.lsp). Wie BTMT eigenstaendig, aber NUR fuer den
;; CSV-Export: export_csv.py gibt ihnen eigene TeileArt-Zeilen und traegt
;; sie - wie Separator/AS/ES - in die "TrackIds"-Spalte ein (auch auf
;; Kreiseln, siehe KREISEL_UMLAUF_TEILEARTEN in lib/export_neighbors.py);
;; export_sivas.py schliesst sie bewusst aus.
;; GR_* = Geruest-Baugruppen (siehe pattern_geruest in cfg/export.cfg, ;; GR_* = Geruest-Baugruppen (siehe pattern_geruest in cfg/export.cfg,
;; GeruestInsert.lsp). Wie BTMT eigenstaendig und von keinem anderen ;; GeruestInsert.lsp). Wie BTMT eigenstaendig und von keinem anderen
;; Element mitgezaehlt - export_csv.py UND export_sivas.py fuehren sie ;; Element mitgezaehlt - export_csv.py UND export_sivas.py fuehren sie
@@ -1111,6 +1174,8 @@
(wcmatch bname (export:pattern "pattern_scanner" "Scanner*")) (wcmatch bname (export:pattern "pattern_scanner" "Scanner*"))
(wcmatch bname (export:pattern "pattern_btmt_beladung" "BTMT-Beladung*")) (wcmatch bname (export:pattern "pattern_btmt_beladung" "BTMT-Beladung*"))
(wcmatch bname (export:pattern "pattern_btmt_entladung" "SC_Entladung*")) (wcmatch bname (export:pattern "pattern_btmt_entladung" "SC_Entladung*"))
(wcmatch bname (export:pattern "pattern_eingang" "Eingang*"))
(wcmatch bname (export:pattern "pattern_ausgang" "Ausgang*"))
(wcmatch bname (export:pattern "pattern_geruest" "GR_*")) (wcmatch bname (export:pattern "pattern_geruest" "GR_*"))
;; Rein numerische Namen = Omniflo SivasNr (Boegen/Weichen) ;; Rein numerische Namen = Omniflo SivasNr (Boegen/Weichen)
;; Rein LISP-intern (Python klassifiziert Omniflo stattdessen ;; Rein LISP-intern (Python klassifiziert Omniflo stattdessen
@@ -1157,16 +1222,18 @@
;; --- Gemeinsame Export-Funktion --- ;; --- Gemeinsame Export-Funktion ---
;; Sammelt relevante Bloecke, schreibt JSON, ruft Python auf. ;; Sammelt relevante Bloecke, schreibt JSON, ruft Python auf.
;; label = Anzeigename (z.B. "EXPORTCSV") ;; label = Anzeigename (z.B. "EXPORTCSV")
;; py-name = Python-Skript-Name (z.B. "export_csv.py") ;; --- Export-Vorbereitung: Zeichnungszustand fuer den Export herrichten ---
;; csv-name = Ergebnis-CSV-Name (z.B. "export.csv") ;; Aus csv:run-export ausgelagert: alle Schritte, die VOR dem Sammeln/Schreiben
;; include-bbox = T -> Bounding-Box je Block ermitteln und mit exportieren ;; den Zeichnungszustand herrichten - Separator-Proxies einpacken, IDs
;; (siehe csv:block-to-json); fuer EXPORTSIVAS nil. ;; sicherstellen, Proxy-Zuordnung setzen, Omniflo-Attribute (HOEHE/DREHUNG)
(defun csv:run-export (label py-name csv-name include-bbox / ss i ename fh out-pfad ;; aktualisieren, Separator-/Scanner-Zuordnung pruefen, global regenerieren und
py-skript ergebnis-pfad py-exe log-pfad rc first-block ;; die bbox-Fehlerdiagnose zuruecksetzen. Rueckgabe: die Liste der erzeugten
count out-dir sep-proxies) ;; Separator-Proxies (der Aufrufer entfernt sie am Ende wieder). label dient
;; nur den Meldungen.
(defun csv:export-vorbereiten (label / sep-proxies)
;; Verpackte Separator_SP-Symbole (siehe csv:sep-proxies-erzeugen) VOR der ;; Verpackte Separator_SP-Symbole (siehe csv:sep-proxies-erzeugen) VOR der
;; ID-Vergabe durch temporaere, echte Kopien ersetzen - sie werden am Ende ;; ID-Vergabe durch temporaere, echte Kopien ersetzen - sie werden am Ende
;; dieser Funktion wieder entfernt (csv:sep-proxies-loeschen). ;; von csv:run-export wieder entfernt (csv:sep-proxies-loeschen).
;; ;;
;; HINWEIS: csv:sepliste-proxies-erzeugen (echte Kopien fuer die nur noch in ;; HINWEIS: csv:sepliste-proxies-erzeugen (echte Kopien fuer die nur noch in
;; der sepliste-XDATA gefuehrten SEP/AS/ES-Eintraege der VF_n/GF_n-Wrapper) ;; der sepliste-XDATA gefuehrten SEP/AS/ES-Eintraege der VF_n/GF_n-Wrapper)
@@ -1226,6 +1293,76 @@
;; bbox-Fehlerdiagnose fuer diesen Lauf zuruecksetzen (siehe csv:get-bbox). ;; bbox-Fehlerdiagnose fuer diesen Lauf zuruecksetzen (siehe csv:get-bbox).
(setq *csv-bbox-fail-count* 0) (setq *csv-bbox-fail-count* 0)
(setq *csv-bbox-fail-first* nil) (setq *csv-bbox-fail-first* nil)
sep-proxies)
;; --- Python-Export-Skript aufrufen und Ergebnis auswerten ---------------
;; Aus csv:run-export ausgelagert: das fertige raw-JSON (out-pfad) an das
;; Python-Skript py-name uebergeben, synchron ausfuehren und den Exitcode
;; auswerten (kein stiller Fehlschlag). Arbeitet ausschliesslich ueber die
;; uebergebenen Pfade/Namen und die Umgebung (DXFM_LIB/DXFM_DATA/DXFM_LOG) -
;; keine Zeichnungs-/Sammel-Logik mehr, daher gut fuer sich testbar.
;; label/out-dir dienen nur den Meldungen bzw. dem Ergebnis-Pfad.
(defun csv:python-aufrufen (label out-dir out-pfad py-name csv-name
/ py-skript ergebnis-pfad py-exe log-pfad rc)
;; Python-Skript aufrufen. DXFM_LIB muss gesetzt sein - fehlt es
;; (z.B. weil BricsCAD nicht ueber bin/start_briscad.bat gestartet
;; wurde und die Umgebungsvariablen daher nie in diesen Prozess
;; vererbt wurden), wuerde das folgende strcat sonst mit einem
;; kryptischen Lisp-Fehler ("bad argument type <NIL>") abbrechen.
(if (null (getenv "DXFM_LIB"))
(princ (ssg-textf "exp-dxfm-lib-missing" (list label)))
(progn
(setq py-skript (strcat (getenv "DXFM_LIB") "/" py-name))
(if (findfile py-skript)
(progn
(setq ergebnis-pfad (strcat out-dir "/" (export:dwg-praefix) csv-name))
(setq py-exe (export:python-exe))
(setq log-pfad (strcat (cond ((getenv "DXFM_LOG")) (out-dir))
"/export_python.log"))
(princ (ssg-textf "exp-python-exe" (list label py-exe)))
(princ (ssg-textf "exp-calling-python" (list label)))
;; Synchron ausfuehren und Exitcode auswerten - ein stiller
;; Fehlschlag (z.B. WindowsApps-Platzhalter statt Interpreter)
;; darf nicht mehr als "Export gestartet" durchgehen.
(setq rc (export:run-python py-exe py-skript
(list out-pfad (getenv "DXFM_DATA") ergebnis-pfad)
log-pfad))
(cond
((null rc)
(princ (ssg-textf "exp-python-call-failed" (list label))))
((/= rc 0)
(princ (ssg-textf "exp-python-error" (list label rc log-pfad)))
(export:log-ausgeben log-pfad 12)
;; Exitcode 49 (Store-Platzhalter direkt) bzw. 9009 (cmd.exe:
;; "Befehl nicht gefunden") = kein brauchbarer Interpreter.
;; Gezielter Hinweis, wie er korrekt gesetzt wird.
(if (or (= rc 49) (= rc 9009)
(export:python-store-stub-p py-exe))
(princ (ssg-text "exp-python-store-hint"))
))
((not (findfile ergebnis-pfad))
(princ (ssg-textf "exp-csv-missing" (list label ergebnis-pfad log-pfad)))
(export:log-ausgeben log-pfad 12))
(t
(princ (ssg-textf "exp-export-done" (list label ergebnis-pfad))))
)
)
(princ (ssg-textf "exp-python-not-found" (list label py-skript)))
)
)
)
)
;; py-name = Python-Skript-Name (z.B. "export_csv.py")
;; csv-name = Ergebnis-CSV-Name (z.B. "export.csv")
;; include-bbox = T -> Bounding-Box je Block ermitteln und mit exportieren
;; (siehe csv:block-to-json); fuer EXPORTSIVAS nil.
(defun csv:run-export (label py-name csv-name include-bbox / ss i ename fh out-pfad
first-block count out-dir sep-proxies)
;; Zeichnungszustand fuer den Export herrichten (Proxies, IDs, Zuordnung,
;; Attribut-Update, Sep/Scan-Zaehlung, REGENALL, bbox-Reset) - ausgelagert.
;; Rueckgabe: die erzeugten Separator-Proxies (unten wieder zu entfernen).
(setq sep-proxies (csv:export-vorbereiten label))
(princ (ssg-textf "exp-collect-blocks" (list label))) (princ (ssg-textf "exp-collect-blocks" (list label)))
(setq ss (csv:collect-export-blocks)) (setq ss (csv:collect-export-blocks))
@@ -1285,53 +1422,8 @@
(list (itoa *csv-bbox-fail-count*) (list (itoa *csv-bbox-fail-count*)
(cond (*csv-bbox-fail-first*) ("?")))))) (cond (*csv-bbox-fail-first*) ("?"))))))
;; Python-Skript aufrufen. DXFM_LIB muss gesetzt sein - fehlt es ;; Python-Export-Skript aufrufen (ausgelagert, siehe oben).
;; (z.B. weil BricsCAD nicht ueber bin/start_briscad.bat gestartet (csv:python-aufrufen label out-dir out-pfad py-name csv-name)
;; wurde und die Umgebungsvariablen daher nie in diesen Prozess
;; vererbt wurden), wuerde das folgende strcat sonst mit einem
;; kryptischen Lisp-Fehler ("bad argument type <NIL>") abbrechen.
(if (null (getenv "DXFM_LIB"))
(princ (ssg-textf "exp-dxfm-lib-missing" (list label)))
(progn
(setq py-skript (strcat (getenv "DXFM_LIB") "/" py-name))
(if (findfile py-skript)
(progn
(setq ergebnis-pfad (strcat out-dir "/" (export:dwg-praefix) csv-name))
(setq py-exe (export:python-exe))
(setq log-pfad (strcat (cond ((getenv "DXFM_LOG")) (out-dir))
"/export_python.log"))
(princ (ssg-textf "exp-python-exe" (list label py-exe)))
(princ (ssg-textf "exp-calling-python" (list label)))
;; Synchron ausfuehren und Exitcode auswerten - ein stiller
;; Fehlschlag (z.B. WindowsApps-Platzhalter statt Interpreter)
;; darf nicht mehr als "Export gestartet" durchgehen.
(setq rc (export:run-python py-exe py-skript
(list out-pfad (getenv "DXFM_DATA") ergebnis-pfad)
log-pfad))
(cond
((null rc)
(princ (ssg-textf "exp-python-call-failed" (list label))))
((/= rc 0)
(princ (ssg-textf "exp-python-error" (list label rc log-pfad)))
(export:log-ausgeben log-pfad 12)
;; Exitcode 49 (Store-Platzhalter direkt) bzw. 9009 (cmd.exe:
;; "Befehl nicht gefunden") = kein brauchbarer Interpreter.
;; Gezielter Hinweis, wie er korrekt gesetzt wird.
(if (or (= rc 49) (= rc 9009)
(export:python-store-stub-p py-exe))
(princ (ssg-text "exp-python-store-hint"))
))
((not (findfile ergebnis-pfad))
(princ (ssg-textf "exp-csv-missing" (list label ergebnis-pfad log-pfad)))
(export:log-ausgeben log-pfad 12))
(t
(princ (ssg-textf "exp-export-done" (list label ergebnis-pfad))))
)
)
(princ (ssg-textf "exp-python-not-found" (list label py-skript)))
)
)
)
) )
) )
) )
+314
View File
@@ -0,0 +1,314 @@
;; ============================================================
;; list_blocks.lsp
;; Liest die (Sub-)Block-Definitionen aller DWG-Dateien in
;; data/ils aus und schreibt sie als JSON-Report
;; "blocks_collected.json" nach data/ils.
;;
;; Hintergrund: 2D- und 3D-Bloecke (z.B.
;; Vario_Bogen_ab_27_TEF_rechts_2D.dwg /
;; Vario_Bogen_ab_27_TEF_rechts_3D.dwg)
;; enthalten teilweise gleichnamige Sub-Bloecke (z.B.
;; "200000317_TEF_Bogen_vertikal_abwaerts__15"). Werden beide
;; DWGs in dieselbe Zeichnung eingefuegt, kollidieren diese
;; Definitionen (nur eine Referenz gewinnt). Dieses Skript LIEST
;; die Bloecke nur aus, damit manuell entschieden werden kann,
;; welche spaeter mit "_2D"/"_3D" umbenannt werden.
;;
;; Ausgabe:
;; - JSON data/ils/blocks_collected.json:
;; { "<Dateiname ohne .dwg>": { "dim": "2D"|"3D"|"-",
;; "blocks": ["Name", ...] }, ... }
;; - Konsolen-Report inkl. Kollisionsliste (gleicher Blockname
;; kommt sowohl in einer _2D- als auch in einer _3D-Datei vor)
;;
;; Voraussetzung: DXFM_DATA ist gesetzt (siehe bin/setenv.bat).
;;
;; Aufruf in BricsCAD:
;; (load "C:/.../Lisp/list_blocks.lsp")
;; ILS_BLOCK_LIST
;; ============================================================
(vl-load-com)
;; ------------------------------------------------------------
;; Bereits offenes Dokument mit gegebenem Pfad suchen (oder nil)
;; ------------------------------------------------------------
(defun lb-find-open-doc (dwg-pfad / docs anzahl i doc full treffer)
(setq docs (vla-get-Documents (vlax-get-acad-object))
anzahl (vla-get-Count docs)
i 0
treffer nil
dwg-pfad (strcase (vl-string-translate "\\" "/" dwg-pfad))
)
(while (and (< i anzahl) (not treffer))
(setq doc (vla-item docs i))
(setq full (strcase (vl-string-translate "\\" "/" (vla-get-FullName doc))))
(if (= full dwg-pfad) (setq treffer doc))
(setq i (1+ i))
)
treffer
)
;; ------------------------------------------------------------
;; String fuer JSON escapen (Anfuehrungszeichen/Backslash)
;; ------------------------------------------------------------
(defun lb-json-escape (s)
(if (null s) (setq s ""))
(setq s (vl-string-subst "\\\\" "\\" s))
(vl-string-subst "\\\"" "\"" s)
)
;; ------------------------------------------------------------
;; Strings mit Trennzeichen verbinden
;; ------------------------------------------------------------
(defun lb-strjoin (liste sep / erg erste s)
(setq erg "" erste T)
(foreach s liste
(if erste (setq erg s erste nil) (setq erg (strcat erg sep s)))
)
erg
)
;; ------------------------------------------------------------
;; Dimension aus dem Dateinamen ableiten ("2D"/"3D"/"-")
;; ------------------------------------------------------------
(defun lb-dim-aus-name (basisname / n)
(setq n (strcase basisname))
(cond
((wcmatch n "*_2D") "2D")
((wcmatch n "*_3D") "3D")
(t "-")
)
)
;; ------------------------------------------------------------
;; Alle echten (Sub-)Block-Definitionen des aktiven Dokuments
;; einsammeln (Symboltabelle BLOCK): keine Layouts (Model/Paper-
;; Space, "*..."), keine anonymen Bloecke (Flag 1), keine XRefs
;; (Flag 4). Alphabetisch sortiert.
;; ------------------------------------------------------------
(defun lb-collect-blocks-in-current-doc ( / def name flags ergebnis)
(setq ergebnis nil
def (tblnext "BLOCK" T)
)
(while def
(setq name (cdr (assoc 2 def))
flags (cdr (assoc 70 def))
)
(if (null flags) (setq flags 0))
(if (and name
(> (strlen name) 0)
(/= (substr name 1 1) "*") ; kein Model/Paper-Space
(= (logand flags 1) 0) ; nicht anonym
(= (logand flags 4) 0) ; kein XRef
)
(setq ergebnis (cons name ergebnis))
)
(setq def (tblnext "BLOCK"))
)
(vl-sort ergebnis (function (lambda (a b) (< (strcase a) (strcase b)))))
)
;; ------------------------------------------------------------
;; Eine DWG-Datei oeffnen (falls noetig), Bloecke einsammeln und
;; die Datei wieder schliessen (falls durch diese Funktion geoeffnet).
;; Rueckgabe: ('ok . ("Name" ...)) oder ('fehler . meldung)
;; ------------------------------------------------------------
(defun lb-collect-dwg (dwg-pfad / bereits-offen ergebnis fehler)
(setq bereits-offen (lb-find-open-doc dwg-pfad))
(setq ergebnis nil)
(setq fehler
(vl-catch-all-apply
(function
(lambda ()
(if bereits-offen
(vlax-put-property (vlax-get-acad-object) 'ActiveDocument bereits-offen)
(command "_.OPEN" dwg-pfad)
)
(setq ergebnis (lb-collect-blocks-in-current-doc))
(if (not bereits-offen) (command "_.CLOSE"))
)
)
nil
)
)
(if (vl-catch-all-error-p fehler)
(cons 'fehler (vl-catch-all-error-message fehler))
(cons 'ok ergebnis)
)
)
;; ------------------------------------------------------------
;; Liste als JSON-Array-Text formatieren (["a", "b"]), eingerueckt
;; ------------------------------------------------------------
(defun lb-list-to-json (namen einruecken / innen zeilen)
(if (null namen)
"[]"
(progn
(setq innen (strcat einruecken " "))
(setq zeilen
(mapcar
(function (lambda (nm) (strcat innen "\"" (lb-json-escape nm) "\"")))
namen
)
)
(strcat "[\n"
(car zeilen)
(apply 'strcat (mapcar (function (lambda (z) (strcat ",\n" z))) (cdr zeilen)))
"\n" einruecken "]"
)
)
)
)
;; ------------------------------------------------------------
;; Kollisionen ermitteln: Bloecke, deren Name sowohl in einer
;; _2D- als auch in einer _3D-Datei vorkommt.
;; eintraege = (("<datei>" "2D"|"3D"|"-" ("blk" ...)) ...)
;; Rueckgabe: (("blockname" ("datei-2d" ...) ("datei-3d" ...)) ...)
;; ------------------------------------------------------------
(defun lb-finde-kollisionen (eintraege / tabelle e datei dim blks blk vorhanden coll)
;; tabelle: blockname -> (liste-2d-dateien . liste-3d-dateien)
(setq tabelle nil)
(foreach e eintraege
(setq datei (nth 0 e)
dim (nth 1 e)
blks (nth 2 e)
)
(foreach blk blks
(setq vorhanden (assoc blk tabelle))
(if (not vorhanden)
(setq vorhanden (list blk nil nil)
tabelle (cons vorhanden tabelle))
)
(cond
((= dim "2D") (setq vorhanden (subst (cons datei (nth 1 vorhanden)) (nth 1 vorhanden) vorhanden)))
((= dim "3D") (setq vorhanden (subst (cons datei (nth 2 vorhanden)) (nth 2 vorhanden) vorhanden)))
)
;; aktualisierten Eintrag zurueckschreiben
(setq tabelle (subst vorhanden (assoc blk tabelle) tabelle))
)
)
;; nur Bloecke mit mind. einer 2D- UND einer 3D-Datei
(setq coll nil)
(foreach e tabelle
(if (and (nth 1 e) (nth 2 e))
(setq coll (cons e coll))
)
)
(vl-sort coll (function (lambda (a b) (< (strcase (car a)) (strcase (car b))))))
)
;; ------------------------------------------------------------
;; Hauptbefehl - verarbeitet DXFM_DATA/ils
;; ------------------------------------------------------------
(defun c:ILS_BLOCK_LIST ( / data-dir dwg-dir json-pfad dateien datei dwg-pfad basisname
dim ergebnis orig-doc anzahl-ok anzahl-fehler fehler-liste
eintraege fh erste e kollisionen k)
(setq data-dir (getenv "DXFM_DATA"))
(if (not data-dir)
(progn
(alert "DXFM_DATA ist nicht gesetzt.\nBitte zuerst bin/setenv.bat ausfuehren.")
(exit)
)
)
(setq data-dir (vl-string-translate "\\" "/" data-dir)
dwg-dir (strcat data-dir "/ils")
json-pfad (strcat dwg-dir "/blocks_collected.json")
)
(setq dwg-dir (vl-string-right-trim "/" dwg-dir))
(if (not (findfile dwg-dir))
(progn
(princ (strcat "\n[BLOCK-LIST] Verzeichnis nicht gefunden: " dwg-dir))
(exit)
)
)
(setq dateien (vl-directory-files dwg-dir "*.dwg" 1))
(if (not dateien)
(progn
(princ (strcat "\n[BLOCK-LIST] Keine DWG-Dateien in: " dwg-dir))
(exit)
)
)
(setq orig-doc (vla-get-ActiveDocument (vlax-get-acad-object))
anzahl-ok 0
anzahl-fehler 0
fehler-liste nil
eintraege nil
)
(foreach datei dateien
(setq basisname (vl-filename-base datei)
dwg-pfad (strcat dwg-dir "/" datei)
dim (lb-dim-aus-name basisname)
)
(princ (strcat "\n[BLOCK-LIST] Lese " datei " ..."))
(setq ergebnis (lb-collect-dwg dwg-pfad))
(if (equal (car ergebnis) 'fehler)
(progn
(princ (strcat "\n[BLOCK-LIST] FEHLER bei " datei ": " (cdr ergebnis)))
(setq anzahl-fehler (1+ anzahl-fehler))
(setq fehler-liste (cons datei fehler-liste))
)
(progn
(setq eintraege (cons (list basisname dim (cdr ergebnis)) eintraege))
(setq anzahl-ok (1+ anzahl-ok))
(princ (strcat " " (itoa (length (cdr ergebnis))) " Block(e)."))
)
)
)
(vlax-put-property (vlax-get-acad-object) 'ActiveDocument orig-doc)
(setq eintraege (reverse eintraege))
;; JSON schreiben
(setq fh (open json-pfad "w"))
(if (null fh)
(princ (strcat "\n[BLOCK-LIST] FEHLER: Datei kann nicht geschrieben werden: " json-pfad))
(progn
(write-line "{" fh)
(setq erste T)
(foreach e eintraege
(if (not erste) (write-line "," fh))
(write-line (strcat " \"" (lb-json-escape (nth 0 e)) "\": {") fh)
(write-line (strcat " \"dim\": \"" (nth 1 e) "\",") fh)
(write-line (strcat " \"blocks\": " (lb-list-to-json (nth 2 e) " ")) fh)
(write-line " }" fh)
(setq erste nil)
)
(write-line "}" fh)
(close fh)
(princ (strcat "\n[BLOCK-LIST] " json-pfad " geschrieben ("
(itoa anzahl-ok) " Dateien)."))
)
)
;; Kollisionen ermitteln und ausgeben
(setq kollisionen (lb-finde-kollisionen eintraege))
(princ (strcat "\n[BLOCK-LIST] Fertig: " (itoa anzahl-ok) " gelesen, "
(itoa anzahl-fehler) " Fehler, "
(itoa (length kollisionen)) " Kollision(en) (Blockname in 2D UND 3D)."))
(if kollisionen
(progn
(princ "\n[BLOCK-LIST] --- Kollisionen (Kandidaten fuer _2D/_3D-Umbenennung) ---")
(foreach k kollisionen
(princ (strcat "\n * " (car k)))
(princ (strcat "\n 2D: " (lb-strjoin (reverse (nth 1 k)) ", ")))
(princ (strcat "\n 3D: " (lb-strjoin (reverse (nth 2 k)) ", ")))
)
)
)
(if fehler-liste
(progn
(princ "\n[BLOCK-LIST] WARNUNG: Fehler bei:")
(foreach datei (reverse fehler-liste)
(princ (strcat "\n - " datei))
)
)
)
(princ)
)
(princ "\nlist_blocks.lsp geladen. Befehl: ILS_BLOCK_LIST")
(princ)
+225
View File
@@ -0,0 +1,225 @@
;; ============================================================
;; purge_blocks.lsp
;; Entfernt in allen DWG-Dateien in data/ils die nicht
;; verwendeten Block-Definitionen und Layer (-PURGE Blocks/Layer).
;;
;; Ablauf je DWG:
;; - Datei oeffnen (falls noetig)
;; - Block- und Layer-Anzahl zaehlen
;; - -PURGE Blocks und -PURGE Layer in einer Schleife ausfuehren,
;; bis nichts mehr entfernt wird (verschachtelte Bloecke bzw.
;; nur von entfernten Bloecken benutzte Layer werden erst nach
;; dem ersten Durchlauf frei)
;; - nur speichern, wenn sich die Anzahl verringert hat
;; - schliessen (falls durch dieses Skript geoeffnet)
;;
;; Es werden AUSSCHLIESSLICH unbenutzte Bloecke und Layer entfernt.
;; Andere Objekttypen (Linientypen, Textstile, ...) bleiben
;; unangetastet.
;;
;; Voraussetzung: DXFM_DATA ist gesetzt (siehe bin/setenv.bat).
;;
;; Aufruf in BricsCAD:
;; (load "C:/.../Lisp/purge_blocks.lsp")
;; ILS_BLOCK_PURGE
;; ============================================================
(vl-load-com)
;; ------------------------------------------------------------
;; Bereits offenes Dokument mit gegebenem Pfad suchen (oder nil)
;; ------------------------------------------------------------
(defun pb-find-open-doc (dwg-pfad / docs anzahl i doc full treffer)
(setq docs (vla-get-Documents (vlax-get-acad-object))
anzahl (vla-get-Count docs)
i 0
treffer nil
dwg-pfad (strcase (vl-string-translate "\\" "/" dwg-pfad))
)
(while (and (< i anzahl) (not treffer))
(setq doc (vla-item docs i))
(setq full (strcase (vl-string-translate "\\" "/" (vla-get-FullName doc))))
(if (= full dwg-pfad) (setq treffer doc))
(setq i (1+ i))
)
treffer
)
;; ------------------------------------------------------------
;; Anzahl aller Eintraege einer Symboltabelle zaehlen
;; (z.B. "BLOCK" inkl. Model/Paper-Space/anonyme, "LAYER").
;; ------------------------------------------------------------
(defun pb-count-table (tabelle / n def)
(setq n 0 def (tblnext tabelle T))
(while def
(setq n (1+ n))
(setq def (tblnext tabelle))
)
n
)
;; ------------------------------------------------------------
;; Im aktiven Dokument unbenutzte Bloecke UND Layer purgen, bis
;; stabil. Erst Bloecke, dann Layer (ein nur von einem entfernten
;; Block benutzter Layer wird erst danach frei) - daher Schleife
;; ueber beide Typen, bis sich nichts mehr aendert.
;; ------------------------------------------------------------
(defun pb-purge-current-doc ( / vorher aktuell durchlauf max-durchlauf)
(setq vorher (+ (pb-count-table "BLOCK") (pb-count-table "LAYER"))
aktuell vorher
durchlauf 1
max-durchlauf 20
)
(while (and (<= durchlauf max-durchlauf)
(progn
(command "_.-PURGE" "_B" "*" "_N") ; unbenutzte Bloecke
(command "_.-PURGE" "_LA" "*" "_N") ; unbenutzte Layer
(setq aktuell (+ (pb-count-table "BLOCK") (pb-count-table "LAYER")))
;; weiter, solange noch etwas entfernt wurde
(if (< aktuell vorher)
(progn (setq vorher aktuell) T)
nil
)
)
)
(setq durchlauf (1+ durchlauf))
)
aktuell
)
;; ------------------------------------------------------------
;; Eine DWG-Datei oeffnen (falls noetig), purgen, speichern (nur
;; bei Aenderung) und schliessen (falls hier geoeffnet).
;; Rueckgabe: ('ok blk-entfernt . lay-entfernt) oder ('fehler . meldung)
;; ------------------------------------------------------------
(defun pb-purge-dwg (dwg-pfad / bereits-offen blk-vor blk-nach lay-vor lay-nach
blk-entfernt lay-entfernt fehler)
(setq bereits-offen (pb-find-open-doc dwg-pfad))
(setq blk-entfernt 0 lay-entfernt 0)
(setq fehler
(vl-catch-all-apply
(function
(lambda ()
(if bereits-offen
(vlax-put-property (vlax-get-acad-object) 'ActiveDocument bereits-offen)
(command "_.OPEN" dwg-pfad)
)
(setq blk-vor (pb-count-table "BLOCK")
lay-vor (pb-count-table "LAYER")
)
(pb-purge-current-doc)
(setq blk-nach (pb-count-table "BLOCK")
lay-nach (pb-count-table "LAYER")
blk-entfernt (- blk-vor blk-nach)
lay-entfernt (- lay-vor lay-nach)
)
(if (or (> blk-entfernt 0) (> lay-entfernt 0))
(command "_.QSAVE")
)
(if (not bereits-offen) (command "_.CLOSE"))
)
)
nil
)
)
(if (vl-catch-all-error-p fehler)
(cons 'fehler (vl-catch-all-error-message fehler))
(list 'ok blk-entfernt lay-entfernt)
)
)
;; ------------------------------------------------------------
;; Hauptbefehl - verarbeitet DXFM_DATA/ils
;; ------------------------------------------------------------
(defun c:ILS_BLOCK_PURGE ( / data-dir dwg-dir dateien datei dwg-pfad ergebnis
orig-doc anzahl-ok anzahl-fehler summe-blk summe-lay
blk lay fehler-liste geaendert-liste)
(setq data-dir (getenv "DXFM_DATA"))
(if (not data-dir)
(progn
(alert "DXFM_DATA ist nicht gesetzt.\nBitte zuerst bin/setenv.bat ausfuehren.")
(exit)
)
)
(setq data-dir (vl-string-translate "\\" "/" data-dir)
dwg-dir (vl-string-right-trim "/" (strcat data-dir "/ils"))
)
(if (not (findfile dwg-dir))
(progn
(princ (strcat "\n[BLOCK-PURGE] Verzeichnis nicht gefunden: " dwg-dir))
(exit)
)
)
(setq dateien (vl-directory-files dwg-dir "*.dwg" 1))
(if (not dateien)
(progn
(princ (strcat "\n[BLOCK-PURGE] Keine DWG-Dateien in: " dwg-dir))
(exit)
)
)
(setq orig-doc (vla-get-ActiveDocument (vlax-get-acad-object))
anzahl-ok 0
anzahl-fehler 0
summe-blk 0
summe-lay 0
fehler-liste nil
geaendert-liste nil
)
(foreach datei dateien
(setq dwg-pfad (strcat dwg-dir "/" datei))
(princ (strcat "\n[BLOCK-PURGE] Bearbeite " datei " ..."))
(setq ergebnis (pb-purge-dwg dwg-pfad))
(if (equal (car ergebnis) 'fehler)
(progn
(princ (strcat "\n[BLOCK-PURGE] FEHLER bei " datei ": " (cdr ergebnis)))
(setq anzahl-fehler (1+ anzahl-fehler))
(setq fehler-liste (cons datei fehler-liste))
)
(progn
(setq anzahl-ok (1+ anzahl-ok)
blk (nth 1 ergebnis)
lay (nth 2 ergebnis)
)
(if (or (> blk 0) (> lay 0))
(progn
(princ (strcat " " (itoa blk) " Block(e), " (itoa lay) " Layer entfernt, gespeichert."))
(setq summe-blk (+ summe-blk blk)
summe-lay (+ summe-lay lay)
)
(setq geaendert-liste (cons (strcat datei " (B:" (itoa blk) " L:" (itoa lay) ")") geaendert-liste))
)
(princ " keine unbenutzten Bloecke/Layer.")
)
)
)
)
(vlax-put-property (vlax-get-acad-object) 'ActiveDocument orig-doc)
(princ (strcat "\n[BLOCK-PURGE] Fertig: " (itoa anzahl-ok) " Datei(en) bearbeitet, "
(itoa summe-blk) " Block(e), "
(itoa summe-lay) " Layer entfernt, "
(itoa anzahl-fehler) " Fehler."))
(if geaendert-liste
(progn
(princ "\n[BLOCK-PURGE] Geaenderte Dateien:")
(foreach datei (reverse geaendert-liste)
(princ (strcat "\n - " datei))
)
)
)
(if fehler-liste
(progn
(princ "\n[BLOCK-PURGE] WARNUNG: Fehler bei:")
(foreach datei (reverse fehler-liste)
(princ (strcat "\n - " datei))
)
)
)
(princ)
)
(princ "\npurge_blocks.lsp geladen. Befehl: ILS_BLOCK_PURGE")
(princ)
+44 -3
View File
@@ -787,7 +787,7 @@
;; und Foerderer-Tests) fuehrte das zu minutenlangen Laufzeiten. ;; und Foerderer-Tests) fuehrte das zu minutenlangen Laufzeiten.
;; Zum erzwungenen Neu-Einlesen: ssg-block-refresh-reset. ;; Zum erzwungenen Neu-Einlesen: ssg-block-refresh-reset.
;; Rueckgabe: effektiver (suffigierter) Blockname oder nil (Datei nicht gefunden). ;; Rueckgabe: effektiver (suffigierter) Blockname oder nil (Datei nicht gefunden).
(defun ssg-ils-block-laden-dim (blockname dim / eff datei base temp-obj osm areq adia) (defun ssg-ils-block-laden-dim (blockname dim / eff datei base temp-obj osm areq adia n)
(setq eff (ssg-ils-blockname-dim blockname dim)) (setq eff (ssg-ils-blockname-dim blockname dim))
(setq datei (ssg-ils-block-datei-dim blockname dim)) (setq datei (ssg-ils-block-datei-dim blockname dim))
(if (null datei) (if (null datei)
@@ -825,8 +825,22 @@
(if (tblsearch "BLOCK" eff) (if (tblsearch "BLOCK" eff)
(command "_.-INSERT" (strcat eff "=" datei) "_Y") ; Redefine bestehender Block (command "_.-INSERT" (strcat eff "=" datei) "_Y") ; Redefine bestehender Block
(command "_.-INSERT" (strcat eff "=" datei))) ; neu (kein Redefine-Prompt) (command "_.-INSERT" (strcat eff "=" datei))) ; neu (kein Redefine-Prompt)
(if (> (getvar "CMDACTIVE") 0) (command)) ;; Robust statt fixer 2 Versuche abbrechen: manche Block-DWGs (z.B.
(if (> (getvar "CMDACTIVE") 0) (command)) ;; abweichende INSUNITS) schieben eine zusaetzliche Rueckfrage ein,
;; die zwei (command)-Aufrufe nicht immer leeren. Bleibt danach ein
;; Befehl aktiv, verschluckt der NAECHSTE (command ...)-Aufruf des
;; Aufrufers (die eigentliche Platzierung, z.B. hundm05:bau-sensor/
;; ils-insert-station) dessen Argumente als Antwort auf DIESEN noch
;; offenen Prompt - Ursache eines real beobachteten kaputten Blocks
;; (fehlende Attribute) beim letzten Element eines Testlaufs.
(setq n 0)
(while (and (> (getvar "CMDACTIVE") 0) (< n 10))
(command)
(setq n (1+ n)))
(if (> (getvar "CMDACTIVE") 0)
(princ (strcat "\n[SSG] WARNUNG: Laden von '" eff
"' hat einen Befehl aktiv gelassen (CMDACTIVE > 0) -"
" nachfolgende Einfuegungen sind evtl. beschaedigt.")))
(setvar "OSMODE" osm)(setvar "ATTREQ" areq)(setvar "ATTDIA" adia))) (setvar "OSMODE" osm)(setvar "ATTREQ" areq)(setvar "ATTDIA" adia)))
;; Nur merken, wenn die Definition danach wirklich vorhanden ist - ;; Nur merken, wenn die Definition danach wirklich vorhanden ist -
;; sonst wuerde ein fehlgeschlagener Ladeversuch dauerhaft gesperrt. ;; sonst wuerde ein fehlgeschlagener Ladeversuch dauerhaft gesperrt.
@@ -1767,6 +1781,33 @@
(cdr (assoc key obj)) (cdr (assoc key obj))
) )
;; String fuer die Ausgabe in JSON maskieren (Gegenstueck zu ssg-json-string).
;; Der Backslash MUSS zuerst geprueft werden, sonst wuerde die Maskierung der
;; Anfuehrungszeichen gleich wieder mitmaskiert.
;; Hinweis: csv:json-escape in export.lsp leistet dasselbe, ist aber modul-lokal
;; am Export-Modul - bewusst nicht zusammengelegt, das waere ein eigener Schritt.
(defun ssg-json-escape (s / i c erg)
(if (null s) (setq s ""))
(setq erg "" i 1)
(while (<= i (strlen s))
(setq c (substr s i 1))
(setq erg
(strcat erg
(cond
((= c "\\") "\\\\")
((= c "\"") "\\\"")
((= c (chr 10)) "\\n")
((= c (chr 13)) "\\r")
((= c (chr 9)) "\\t")
(T c)
)
)
)
(setq i (1+ i))
)
erg
)
;; Verschachteltes JSON-Objekt parsen (1 Ebene tief) ;; Verschachteltes JSON-Objekt parsen (1 Ebene tief)
;; {"section": {"key": val, ...}, ...} ;; {"section": {"key": val, ...}, ...}
+6
View File
@@ -38,6 +38,11 @@
;; pattern_btmt_entladung in cfg/export.cfg) aus demselben Grund: sonst bleibt ;; pattern_btmt_entladung in cfg/export.cfg) aus demselben Grund: sonst bleibt
;; ihre ID trotz ATTDEF leer und sie fehlen bei der zeichnungsweiten Duplikat- ;; ihre ID trotz ATTDEF leer und sie fehlen bei der zeichnungsweiten Duplikat-
;; pruefung/Maximum-Ermittlung (ssg-id-check-all). ;; pruefung/Maximum-Ermittlung (ssg-id-check-all).
;; Eingang*/Ausgang* (Materialfluss-Ein-/Ausgangssymbole, siehe
;; pattern_eingang/pattern_ausgang in cfg/export.cfg) ebenso: die DWGs haben
;; seit dem ID-Nachtrag (allocations.json) ein leeres ID-ATTDEF, das hier
;; gefuellt werden muss, damit sie eine stabile TeileId fuer die "TrackIds"-
;; Spalte (auch auf Kreiseln) bekommen.
;; GR_* (Geruest-Baugruppen, siehe pattern_geruest in cfg/export.cfg) ebenso - ;; GR_* (Geruest-Baugruppen, siehe pattern_geruest in cfg/export.cfg) ebenso -
;; das ID-ATTDEF steht seit *geruest-attrib-defs* zur Verfuegung. ;; das ID-ATTDEF steht seit *geruest-attrib-defs* zur Verfuegung.
(defun ssg-id-collect-blocks ( / ss-all ss-out i ename ed bname) (defun ssg-id-collect-blocks ( / ss-all ss-out i ename ed bname)
@@ -57,6 +62,7 @@
(wcmatch bname "Vario*,Staustrecke*,AUS_Element*,EIN_Element*,VF_*,GF_*") (wcmatch bname "Vario*,Staustrecke*,AUS_Element*,EIN_Element*,VF_*,GF_*")
(wcmatch bname "Separator_SP*,S-LP*,Scanner*") (wcmatch bname "Separator_SP*,S-LP*,Scanner*")
(wcmatch bname "BTMT-Beladung*,SC_Entladung*") (wcmatch bname "BTMT-Beladung*,SC_Entladung*")
(wcmatch bname "Eingang*,Ausgang*")
(wcmatch bname "GR_*") (wcmatch bname "GR_*")
(wcmatch bname "#*") (wcmatch bname "#*")
) )
+20 -1
View File
@@ -413,13 +413,32 @@
(reverse liste) (reverse liste)
) )
;; Koordinate mit GENAU 2 Nachkommastellen formatieren - bewusst OHNE rtos.
;; rtos haengt an den Zeichnungs-Systemvariablen DIMZIN (Nullunterdrueckung),
;; LUPREC und LUNITS: bei dem hier verbreiteten DIMZIN=8 wuerde rtos die
;; abschliessenden Nullen wegwerfen (10.00 -> 10), und dieselben Koordinaten
;; serialisierten je nach Zeichnungsprofil unterschiedlich. Die Sepliste
;; landet als XDATA/Journal PERSISTENT am Block - die Serialisierung muss
;; darum reproduzierbar und profilunabhaengig sein. Rundung kaufmaennisch,
;; mit Uebertrag (9.999 -> 10.00), Vorzeichen bleibt erhalten.
(defun ssg-sepliste-fmt-koord (x / neg i f s)
(setq neg (< x 0.0) x (abs x))
(setq i (fix x))
(setq f (fix (+ 0.5 (* 100.0 (- x i)))))
(if (>= f 100) (progn (setq i (1+ i)) (setq f (- f 100))))
(setq s (strcat (itoa i) "."
(if (< f 10) (strcat "0" (itoa f)) (itoa f))))
(if neg (strcat "-" s) s)
)
;; Serialisiert die Sepliste zu "TYP:LFDNR:X:Y,TYP:LFDNR:X:Y,...". ;; Serialisiert die Sepliste zu "TYP:LFDNR:X:Y,TYP:LFDNR:X:Y,...".
(defun ssg-sepliste->string (liste / eintrag out first) (defun ssg-sepliste->string (liste / eintrag out first)
(setq out "" first t) (setq out "" first t)
(foreach eintrag liste (foreach eintrag liste
(setq eintrag (setq eintrag
(strcat (nth 0 eintrag) ":" (itoa (nth 1 eintrag)) ":" (strcat (nth 0 eintrag) ":" (itoa (nth 1 eintrag)) ":"
(rtos (nth 2 eintrag) 2 2) ":" (rtos (nth 3 eintrag) 2 2))) (ssg-sepliste-fmt-koord (nth 2 eintrag)) ":"
(ssg-sepliste-fmt-koord (nth 3 eintrag))))
(if first (progn (setq out eintrag) (setq first nil)) (if first (progn (setq out eintrag) (setq first nil))
(setq out (strcat out "," eintrag))) (setq out (strcat out "," eintrag)))
) )
+1 -2
View File
@@ -35,12 +35,11 @@
(load (strcat base-path "VarioFoerderer.lsp") "VarioFoerderer.lsp nicht gefunden") (load (strcat base-path "VarioFoerderer.lsp") "VarioFoerderer.lsp nicht gefunden")
(load (strcat base-path "Gefaellestrecke.lsp") "Gefaellestrecke.lsp nicht gefunden") (load (strcat base-path "Gefaellestrecke.lsp") "Gefaellestrecke.lsp nicht gefunden")
(load (strcat base-path "export.lsp") "export.lsp nicht gefunden") (load (strcat base-path "export.lsp") "export.lsp nicht gefunden")
(load (strcat base-path "tests.lsp") "tests.lsp nicht gefunden")
(load (strcat base-path "OmniModulInsert.lsp") "OmniModulInsert.lsp nicht gefunden") (load (strcat base-path "OmniModulInsert.lsp") "OmniModulInsert.lsp nicht gefunden")
(load (strcat base-path "Tro_Edit.lsp") "Tro_Edit.lsp nicht gefunden") (load (strcat base-path "Tro_Edit.lsp") "Tro_Edit.lsp nicht gefunden")
(load (strcat base-path "Connection_Edit.lsp") "Connection_Edit.lsp nicht gefunden") (load (strcat base-path "Connection_Edit.lsp") "Connection_Edit.lsp nicht gefunden")
(load (strcat base-path "SSG_LIB_Commands.lsp") "SSG_LIB_Commands.lsp nicht gefunden") (load (strcat base-path "SSG_LIB_Commands.lsp") "SSG_LIB_Commands.lsp nicht gefunden")
(prompt "\nSSG-Bibliotheken geladen: ssg_core, ssg_lang, ssg_dbg, ssg_dialog, ssg_layer, KreiselInsert, VarioFoerderer, Gefaellestrecke, export, tests, OmniModulInsert, SSG_LIB_Commands") (prompt "\nSSG-Bibliotheken geladen: ssg_core, ssg_lang, ssg_dbg, ssg_dialog, ssg_layer, KreiselInsert, VarioFoerderer, Gefaellestrecke, export, OmniModulInsert, SSG_LIB_Commands")
(princ) (princ)
) )
+5 -34
View File
@@ -138,40 +138,11 @@
;; [ sin(hz)*cos(vert) cos(hz) sin(hz)*sin(vert) ] ;; [ sin(hz)*cos(vert) cos(hz) sin(hz)*sin(vert) ]
;; [ -sin(vert) 0 cos(vert) ] ;; [ -sin(vert) 0 cos(vert) ]
(defun insert-hz-incl-scaled-block (blockname startpunkt laenge hz-winkel vert-winkel / ;; HINWEIS: insert-hz-incl-scaled-block wurde entfernt (2026-09-18) - toter Code,
rad-hz rad-vert chz shz cv sv scale ;; kein Aufrufer im Repo. Die verbleibenden drei insert-hz-*-Funktionen bleiben,
matrix block-obj endpunkt) ;; weil sie sich in der Platzierungssemantik (KS_EIN vs. Block-Ursprung) bzw. der
(setq scale (/ (float laenge) 1000.0)) ;; Skalierung+KS+Vertikale-Kombination von den ssg_ks_insert-Primitiven
(setq rad-hz (* (float hz-winkel) (/ pi 180.0))) ;; unterscheiden und ein Ersatz die Geometrie verschoebe (byte-genaue Pruefung).
(setq rad-vert (* (float vert-winkel) (/ pi 180.0)))
(setq chz (cos rad-hz) shz (sin rad-hz))
(setq cv (cos rad-vert) sv (sin rad-vert))
(setq blockname (ensure-block-loaded blockname))
(if (not (tblsearch "BLOCK" blockname))
(progn
(princ (ssg-textf "vfe-block-fehlt-abbruch" (list blockname)))
(exit)
)
)
(setq block-obj (vla-InsertBlock modelspace
(vlax-3D-point '(0 0 0)) blockname scale 1.0 1.0 0))
;; Einzelteil auf die eigene Ebene der Blockdefinition legen - wie in den
;; Zwillingen in ssg_ks_insert.lsp; hier fehlte der Aufruf bisher.
(ssg-ils-block-auf-ebene block-obj blockname)
(setq matrix (vf-rot-matrix chz shz cv sv))
(vla-TransformBy block-obj (vlax-tmatrix matrix))
(vla-Move block-obj (vlax-3D-point '(0 0 0)) (vlax-3D-point startpunkt))
(setq endpunkt (list
(+ (car startpunkt) (* laenge chz cv))
(+ (cadr startpunkt) (* laenge shz cv))
(+ (caddr startpunkt) (* (- sv) laenge))))
(princ (ssg-textf "vfe-hz-incl-status"
(list blockname (rtos laenge 2 0)
(strcat (rtos hz-winkel 2 0) (chr 176))
(strcat (rtos vert-winkel 2 0) (chr 176))
(rtos (caddr endpunkt) 2 1))))
endpunkt
)
(defun insert-hz-rotated-block-with-ks (blockname startpunkt hz-winkel vert-winkel block-dx block-dz / (defun insert-hz-rotated-block-with-ks (blockname startpunkt hz-winkel vert-winkel block-dx block-dz /
rad-hz rad-vert chz shz cv sv rad-hz rad-vert chz shz cv sv
+11
View File
@@ -193,6 +193,17 @@
;; etage-foerderanlage-einfuegen (vf_etage.lsp). ;; etage-foerderanlage-einfuegen (vf_etage.lsp).
(if (null *vfk-etage-auslauf-min-clamp*) (setq *vfk-etage-auslauf-min-clamp* 50.0)) (if (null *vfk-etage-auslauf-min-clamp*) (setq *vfk-etage-auslauf-min-clamp* 50.0))
;; Mindest-2D-Laenge (mm) eines Pfad-Segments (LINE-Laenge bzw. ARC-Sehne),
;; das der Linienzug-Modus 2/3 aus einer Objektauswahl akzeptiert. Kuerzere
;; Objekte sind Geister-/Artefakt-Elemente (z.B. ein von einer Rechteck-Auswahl
;; miterfasstes Mini-Segment) und werden VOR der Sortierung ausgeworfen, mit
;; Warnung an den Nutzer (vfl-in-segmente). Konservativ deutlich unter jedem
;; realen Bauteil (kleinstes Bauteil = Separator 300 mm; kleinster echter
;; Eck-Bogen R500/30 Grad hat eine Sehne von ~259 mm) und knapp ueber dem
;; allgemeinen Restlaengen-Clamp (100 mm), damit kein legitimes Segment
;; faelschlich verworfen wird.
(if (null *vfk-pfad-min-segmentlaenge*) (setq *vfk-pfad-min-segmentlaenge* 100.0))
;; ============================================================ ;; ============================================================
;; TEIL 4: FALLBACK-VERSATZWERTE (mm) - AS-/ES-/Gefaellebogen-Masse, falls ;; TEIL 4: FALLBACK-VERSATZWERTE (mm) - AS-/ES-/Gefaellebogen-Masse, falls
;; die Blockdefinition/das Koordinatensystem nicht extrahierbar ist ;; die Blockdefinition/das Koordinatensystem nicht extrahierbar ist
+433 -163
View File
@@ -2013,6 +2013,59 @@
(setq s (substr s (+ pos 1 (strlen sep))))) (setq s (substr s (+ pos 1 (strlen sep)))))
(reverse (cons s out))) (reverse (cons s out)))
;; --- SEGS: Segment-Essenz einer Modus-3-Kette (selbst-enthalten) ---
;; Ein SEGS-Wert ist die AUS DEN PFAD-OBJEKTEN ABGELEITETE Zahlenliste, die der
;; Modus-3-Bau ohnehin allein verwendet (gf-analysiere-kette + Start/Endpunkte
;; aus gf-sortiere-objekte). Sie ersetzt im Journal die frueheren OBJS-Handles:
;; dadurch ist die Kette im XDATA vollstaendig beschrieben und beim Doppelklick-
;; Replay unabhaengig davon, ob die urspruenglichen LINE/ARC noch existieren
;; (robuster als der OBJS-Weg von Modus 2). Siehe doc/variofoerderer.md.
;;
;; Ein Segment ist eine 9er-Liste, die die "Bau-Essenz" und die fuer die
;; AS-Trimmung/das Soll-Ende noetigen absoluten Punkte zusammenfuehrt:
;; (typ hz laenge bwinkel bseite spx spy epx epy)
;; typ "Linie"/"Bogen" (delimiter-frei, daher unquotiert serialisiert)
;; hz Eintritts-Horizontalwinkel (Grad)
;; laenge 2D-Laenge / Sehnenmass (mm)
;; bwinkel Bogenwinkel (0 bei Linie)
;; bseite "links"/"rechts"
;; spx spy Startpunkt (XY, mm) des Segments \ aus gf-sortiere-objekte,
;; epx epy Endpunkt (XY, mm) des Segments / fuer Trimmung + Soll-Ende
;;
;; Serialisierung: Segmente per ";" getrennt, Felder je Segment per "|". Beide
;; Trenner sind kollisionsfrei - die einzigen String-Felder (typ/bseite) sind
;; feste Schluesselwoerter ohne ";" oder "|", alle uebrigen Felder sind Zahlen
;; (rtos-Mode-2, wie PT/REAL). Ein leeres SEGS ("") ist eine leere Liste.
(defun vfl-seg->string (seg)
(vfl-strjoin
(list (nth 0 seg) ; typ (String)
(rtos (float (nth 1 seg)) 2 6) ; hz
(rtos (float (nth 2 seg)) 2 6) ; laenge
(rtos (float (nth 3 seg)) 2 6) ; bwinkel
(nth 4 seg) ; bseite (String)
(rtos (float (nth 5 seg)) 2 6) ; spx
(rtos (float (nth 6 seg)) 2 6) ; spy
(rtos (float (nth 7 seg)) 2 6) ; epx
(rtos (float (nth 8 seg)) 2 6)) ; epy
"|"))
(defun vfl-string->seg (s / f)
(setq f (vfl-strsplit s "|"))
(list (nth 0 f) ; typ
(atof (nth 1 f)) ; hz
(atof (nth 2 f)) ; laenge
(fix (atof (nth 3 f))) ; bwinkel (Ganzzahl, wie gf-analysiere-kette)
(nth 4 f) ; bseite
(atof (nth 5 f)) ; spx
(atof (nth 6 f)) ; spy
(atof (nth 7 f)) ; epx
(atof (nth 8 f)))) ; epy
(defun vfl-segs->string (segs)
(vfl-strjoin (mapcar 'vfl-seg->string segs) ";"))
(defun vfl-string->segs (s)
(if (= (strlen s) 0)
'()
(mapcar 'vfl-string->seg (vfl-strsplit s ";"))))
;; --- Serialisierung (tagged, selbstbeschreibend) --- ;; --- Serialisierung (tagged, selbstbeschreibend) ---
;; Zeilenformat "kind:payload", Eintraege per "\n" verkettet. getstring/getreal ;; Zeilenformat "kind:payload", Eintraege per "\n" verkettet. getstring/getreal
;; liefern nie ein Newline, daher ist "\n" ein sicherer Record-Trenner. ;; liefern nie ein Newline, daher ist "\n" ein sicherer Record-Trenner.
@@ -2020,6 +2073,8 @@
;; Entity-Handles (Hex-Strings, kollidieren nie mit dem Komma-Trenner). ;; Entity-Handles (Hex-Strings, kollidieren nie mit dem Komma-Trenner).
;; Handles bleiben - anders als Enames - ueber Sitzungen/Undo hinweg stabil ;; Handles bleiben - anders als Enames - ueber Sitzungen/Undo hinweg stabil
;; und sind damit die richtige Persistenzform fuer ssget-Ergebnisse. ;; und sind damit die richtige Persistenzform fuer ssget-Ergebnisse.
;; "SEGS" (Segment-Essenz, Modus 3): Payload = ";"-getrennte Segmente, Felder
;; per "|" (siehe vfl-segs->string oben).
(defun vfl-entry->string (e / k v) (defun vfl-entry->string (e / k v)
(setq k (car e) v (cdr e)) (setq k (car e) v (cdr e))
(cond (cond
@@ -2031,6 +2086,7 @@
((= k "STR") (strcat "STR:" v)) ((= k "STR") (strcat "STR:" v))
((= k "STEP") (strcat "STEP:" v)) ((= k "STEP") (strcat "STEP:" v))
((= k "OBJS") (strcat "OBJS:" (vfl-strjoin v ","))) ((= k "OBJS") (strcat "OBJS:" (vfl-strjoin v ",")))
((= k "SEGS") (strcat "SEGS:" (vfl-segs->string v)))
(t "NIL:"))) (t "NIL:")))
(defun vfl-string->entry (ln / p kind pay c) (defun vfl-string->entry (ln / p kind pay c)
(setq p (vl-string-search ":" ln)) (setq p (vl-string-search ":" ln))
@@ -2048,6 +2104,7 @@
((= kind "STR") (cons "STR" pay)) ((= kind "STR") (cons "STR" pay))
((= kind "STEP") (cons "STEP" pay)) ((= kind "STEP") (cons "STEP" pay))
((= kind "OBJS") (cons "OBJS" (if (> (strlen pay) 0) (vfl-strsplit pay ",") nil))) ((= kind "OBJS") (cons "OBJS" (if (> (strlen pay) 0) (vfl-strsplit pay ",") nil)))
((= kind "SEGS") (cons "SEGS" (vfl-string->segs pay)))
(t (cons "NIL" nil)))))) (t (cons "NIL" nil))))))
;; forward-journal ist bereits in Bau-Reihenfolge (nicht die umgekehrte ;; forward-journal ist bereits in Bau-Reihenfolge (nicht die umgekehrte
;; *vfl-journal*-Rohform) - gemeinsame Serialisierung fuer Ketten- UND ;; *vfl-journal*-Rohform) - gemeinsame Serialisierung fuer Ketten- UND
@@ -2086,6 +2143,21 @@
((= k "OBJS") (strcat "[" (vfl-strjoin ((= k "OBJS") (strcat "[" (vfl-strjoin
(mapcar (function (lambda (h) (strcat "\"" (vfl-json-escape h) "\""))) (mapcar (function (lambda (h) (strcat "\"" (vfl-json-escape h) "\"")))
v) ",") "]")) v) ",") "]"))
((= k "SEGS") (strcat "["
(vfl-strjoin
(mapcar
(function (lambda (seg)
(strcat "[\"" (vfl-json-escape (nth 0 seg)) "\","
(vfl-json-num (float (nth 1 seg))) ","
(vfl-json-num (float (nth 2 seg))) ","
(vfl-json-num (float (nth 3 seg))) ",\""
(vfl-json-escape (nth 4 seg)) "\","
(vfl-json-num (float (nth 5 seg))) ","
(vfl-json-num (float (nth 6 seg))) ","
(vfl-json-num (float (nth 7 seg))) ","
(vfl-json-num (float (nth 8 seg))) "]")))
v) ",")
"]"))
(t "null")) (t "null"))
"}")) "}"))
;; forward-journal (Bau-Reihenfolge) als kompakter JSON-Array-String. ;; forward-journal (Bau-Reihenfolge) als kompakter JSON-Array-String.
@@ -2133,7 +2205,7 @@
;; Rueckgabe: (marker . journal) oder nil (kein/unbekanntes Journal). ;; Rueckgabe: (marker . journal) oder nil (kein/unbekanntes Journal).
(defun vfl-journal-xdata-lesen (ent / xd) (defun vfl-journal-xdata-lesen (ent / xd)
(setq xd (vfs-xdata-lesen ent)) (setq xd (vfs-xdata-lesen ent))
(if (and xd (member (car xd) '("linienzug" "linienzug2"))) (if (and xd (member (car xd) '("linienzug" "linienzug2" "linienzug3")))
(cons (car xd) (vfl-string->journal (apply 'strcat (cdr xd)))) (cons (car xd) (vfl-string->journal (apply 'strcat (cdr xd))))
nil)) nil))
@@ -2306,6 +2378,47 @@
;; sich um einen frischen Bau oder einen Editier-Neuaufbau handelte (kein ;; sich um einen frischen Bau oder einen Editier-Neuaufbau handelte (kein
;; Sonderfall mehr noetig). Aufgerufen aus dem *error*-Handler von ;; Sonderfall mehr noetig). Aufgerufen aus dem *error*-Handler von
;; vf-linienzug-modus. ;; vf-linienzug-modus.
;; Den scharfen Abbruch-Wickel-Handler als *error* installieren - gemeinsam
;; fuer alle drei Linienzug-Modi (frueher 3x wortgleich ausgeschrieben, nur der
;; Marker-String "linienzug"/"linienzug2"/"linienzug3" unterschied sich).
;;
;; Der Handler faengt jeden Abbruch (ESC / (exit) / Laufzeitfehler) ab, wickelt
;; die bis dahin gebaute Teil-Geometrie per vfl-modus-abbruch-sichern zu einem
;; VF_n-Block mit dem vollen Journal (Marker als XDATA) und stellt *error*
;; wieder her. Details/Begruendung (dynamic scoping, *ssg-ils-dim*-Reset,
;; ssg-end) siehe die ausfuehrlichen Kommentare an der frueheren Aufrufstelle
;; in vf-linienzug-modus.
;;
;; VERTRAG (dynamic scoping): Der Aufrufer MUSS als Locals gebunden haben und
;; VOR diesem Aufruf gesetzt haben: old-error (Ruecksprung-*error*), lastEnt,
;; vfl-nummer, anzahl-gf, anzahl-vf, startpunkt, frame, as-seite, es-seite,
;; dbg-an. Sie werden vom Handler-Lambda zur ABBRUCHZEIT dynamisch aufgeloest -
;; also gegen den dann noch aktiven Modus-Frame, nicht gegen diesen Konstruktor
;; (der ist beim Abbruch laengst zurueckgekehrt). marker wird dagegen ZUR
;; INSTALL-ZEIT als String-Literal in den Lambda eingebacken (list-Konstruktion),
;; damit der Handler ihn ohne globalen Zwischenzustand selbst traegt.
(defun vfl-abbruch-handler-installieren (marker)
;; *error* auf eine LAMBDA-LISTE setzen, in die der marker als String-Literal
;; eingebaut ist (list-Konstruktion). AutoLISP ruft *error* als Funktion auf,
;; egal ob (lambda ...) oder (function (lambda ...)) - die reine Lambda-Liste
;; genuegt und vermeidet eval/function-Feinheiten. Alle uebrigen Symbole
;; (old-error, lastEnt, ...) bleiben ungequotet-frei und werden zur Aufrufzeit
;; dynamisch aufgeloest.
(setq *error*
(list 'lambda '(msg)
'(setq *error* old-error)
'(setq *ssg-ils-dim* nil)
(list 'vfl-modus-abbruch-sichern 'lastEnt 'vfl-nummer 'anzahl-gf 'anzahl-vf
'startpunkt 'frame 'as-seite 'es-seite marker)
'(if dbg-an
(progn
(dbgmsg (strcat "=== ABBRUCH: " (if msg msg "(exit)") " ==="))
(dbgreturn nil)
(dbgclose)))
'(if (and (boundp '*ssg-start-stack*) *ssg-start-stack*) (ssg-end))
'(princ)))
*error*)
(defun vfl-modus-abbruch-sichern (lastEnt vfl-nummer anzahl-gf anzahl-vf (defun vfl-modus-abbruch-sichern (lastEnt vfl-nummer anzahl-gf anzahl-vf
startpunkt frame as-seite es-seite marker / startpunkt frame as-seite es-seite marker /
e cnt vfl-ins wickel-erg grund fwd n trunc) e cnt vfl-ins wickel-erg grund fwd n trunc)
@@ -5943,6 +6056,60 @@
(vf-set-es-masse winkel seite) (vf-set-es-masse winkel seite)
seite) seite)
;; AS-Winkel (30/90) + Seite abfragen. Gemeinsame Schale fuer die frueher
;; wortgleich in vf-linienzug-modus/-modus2/-modus3 UND vfl-qc-linie-mit-gf
;; ausgeschriebene AS-Praeambel (Winkel-Frage 30/90, Seiten-Frage links/rechts,
;; vf-set-as-masse) - Gegenstueck zu vfl-frage-es-seite. Setzt *vfl-as-winkel*
;; und die AS-Masse (aus-dx/dz) fuer die Variante. Rueckgabe: Seite (links/rechts).
;;
;; seiten-headerkey ist der EINZIGE Unterschied zwischen den Aufrufstellen
;; (modus1: "vfl-aus-seite-header", modus2: "gf-seite-aus-header") und daher
;; Parameter. Die je Modus unterschiedliche VORANGEHENDE Wizard-Vorbereitung
;; (vflw-gruppe-as-impl "AS setzen?" bzw. vflw-gruppe-winkel-seite-impl) bleibt
;; bewusst an der Aufrufstelle - die abzudeckende Winkel/Seite-Frage selbst ist
;; ueberall identisch. vf-frage-element-winkel/vflw-wahl zeigen den Wizard-
;; Winkeldialog bei aktivem Wizard-Modus schon selbst.
(defun vfl-frage-as-winkel-seite (seiten-headerkey / antwort as-seite)
(princ (ssg-text "vf-winkel-aus-header"))
(princ (ssg-text "vf-winkel-90"))
(princ (ssg-text "vf-winkel-30"))
(setq *vfl-as-winkel*
(vfl-in-value "STR"
(function (lambda () (vf-frage-element-winkel "vf-winkel-aus-header"))))) ; 30/90 vor Seite
(princ (ssg-text seiten-headerkey))
(princ (ssg-text "gf-seite-links"))
(princ (ssg-text "gf-seite-rechts"))
(setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2")
(list (ssg-text "gf-seite-links") (ssg-text "gf-seite-rechts")) 1
seiten-headerkey))
(setq as-seite (if (= antwort "2") "rechts" "links"))
(vf-set-as-masse *vfl-as-winkel* as-seite) ; Masse fuer Variante
as-seite)
;; ES-Winkel (30/90) + Seite abfragen - MARKER-FREIE Schale fuer die Modi 2/3,
;; die das ES-Element als eigene Praeambel (Start/Ziel bereits fest) abfragen
;; und ihr eigenes Journal-/Masse-Schema fuehren. Unterscheidet sich bewusst
;; von vfl-frage-es-seite (Modus 1), das zusaetzlich einen Journal-Marker "ES"
;; setzt und ueber vfl-es-masse-setzen laeuft - das ist Teil des Modus-1-
;; Kettenglied-Replays und darf hier NICHT feuern. Setzt *vfl-es-winkel* und
;; die ES-Masse (ein-dx/dz). Rueckgabe: Seite (links/rechts).
(defun vfl-frage-es-winkel-seite (seiten-headerkey / antwort es-seite)
(princ (ssg-text "vf-winkel-ein-header"))
(princ (ssg-text "vf-winkel-90"))
(princ (ssg-text "vf-winkel-30"))
(setq *vfl-es-winkel*
(vfl-in-value "STR"
(function (lambda () (vf-frage-element-winkel "vf-winkel-ein-header"))))) ; 30/90 vor Seite
(princ (ssg-text seiten-headerkey))
(princ (ssg-text "gf-seite-links"))
(princ (ssg-text "gf-seite-rechts"))
(setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2")
(list (ssg-text "gf-seite-links") (ssg-text "gf-seite-rechts")) 1
seiten-headerkey))
(setq es-seite (if (= antwort "2") "rechts" "links"))
(vf-set-es-masse *vfl-es-winkel* es-seite) ; Masse fuer Variante
es-seite)
;; dialog-schon-gezeigt: T, wenn Winkel+Seite BEREITS ueber einen vorherigen ;; dialog-schon-gezeigt: T, wenn Winkel+Seite BEREITS ueber einen vorherigen
;; kombinierten Dialog (vflw-gruppe-as-impl, "ES-Element setzen?" + Winkel + ;; kombinierten Dialog (vflw-gruppe-as-impl, "ES-Element setzen?" + Winkel +
;; Seite in einem Bildschirm) abgefragt und in die Pending-Queue gelegt ;; Seite in einem Bildschirm) abgefragt und in die Pending-Queue gelegt
@@ -6317,32 +6484,11 @@
(setq lastEnt (vf-lastent-ohne-attribute)) (setq lastEnt (vf-lastent-ohne-attribute))
(setq anzahl-gf 0 anzahl-vf 0 frame nil) (setq anzahl-gf 0 anzahl-vf 0 frame nil)
(setq old-error *error*) (setq old-error *error*)
;; Abbruch-Handler: zuerst *error* zuruecksetzen (kein rekursiver ;; Scharfen Abbruch-Wickel-Handler installieren (gemeinsam fuer alle drei
;; Wiedereintritt bei einem Fehler waehrend des Wickelns), dann wickeln. ;; Modi, siehe vfl-abbruch-handler-installieren). old-error/lastEnt/vfl-nummer/
;; Laeuft der Aufruf innerhalb einer ssg-start-Sitzung (Editier-Pfad via ;; anzahl-gf/anzahl-vf/startpunkt/frame/as-seite/es-seite/dbg-an sind hier als
;; c:VARIOFOERDERER_EDIT -> vfl-edit-ent), wird diese mit ssg-end sauber ;; Locals gebunden und werden vom Handler zur Abbruchzeit dynamisch gesehen.
;; geschlossen (Undo-Gruppe, gesicherte Systemvariablen, *error* aus dem (vfl-abbruch-handler-installieren "linienzug")
;; Sitzungs-Frame) - sonst bliebe die von ssg-start geoeffnete Undo-Gruppe
;; offen.
;; *ssg-ils-dim*: der Editier-/Konverter-Pfad setzt vor dem Replay einen
;; Dimensions-Override und raeumt ihn NACH der Rueckkehr wieder ab. Bei einem
;; Abbruch kehrt der Aufruf nie dorthin zurueck (der Handler beendet den
;; Befehl), daher MUSS der Override hier zurueckgesetzt werden - sonst bliebe
;; er fuer den Rest der Sitzung stehen und alle folgenden Einfuegungen liefen
;; in der falschen Dimension.
(setq *error*
(function (lambda (msg)
(setq *error* old-error)
(setq *ssg-ils-dim* nil)
(vfl-modus-abbruch-sichern lastEnt vfl-nummer anzahl-gf anzahl-vf
startpunkt frame as-seite es-seite "linienzug")
(if dbg-an
(progn
(dbgmsg (strcat "=== ABBRUCH: " (if msg msg "(exit)") " ==="))
(dbgreturn nil)
(dbgclose)))
(if (and (boundp '*ssg-start-stack*) *ssg-start-stack*) (ssg-end))
(princ))))
(if (vfl-wizard-aktiv) (if (vfl-wizard-aktiv)
(vl-catch-all-apply 'vflw-gruppe-punkt-hoehe-impl (vl-catch-all-apply 'vflw-gruppe-punkt-hoehe-impl
@@ -6390,22 +6536,7 @@
;; tragen, wenn diesmal kein AS gewaehlt wird. ;; tragen, wenn diesmal kein AS gewaehlt wird.
(setq *vfl-as-winkel* nil *vfl-es-winkel* nil) (setq *vfl-as-winkel* nil *vfl-es-winkel* nil)
(if as-vorhanden (if as-vorhanden
(progn (setq as-seite (vfl-frage-as-winkel-seite "vfl-aus-seite-header"))
(princ (ssg-text "vf-winkel-aus-header"))
(princ (ssg-text "vf-winkel-90"))
(princ (ssg-text "vf-winkel-30"))
(setq *vfl-as-winkel*
(vfl-in-value "STR"
(function (lambda () (vf-frage-element-winkel "vf-winkel-aus-header"))))) ; 30/90 vor Seite
(princ (ssg-text "vfl-aus-seite-header"))
(princ (ssg-text "gf-seite-links"))
(princ (ssg-text "gf-seite-rechts"))
(setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2")
(list (ssg-text "gf-seite-links") (ssg-text "gf-seite-rechts")) 1
"vfl-aus-seite-header"))
(setq as-seite (if (= antwort "2") "rechts" "links"))
(vf-set-as-masse *vfl-as-winkel* as-seite) ; Masse fuer Variante
)
) )
;; ES-Seite wird erst am Kettenende abgefragt (siehe vfl-frage-es-seite), ;; ES-Seite wird erst am Kettenende abgefragt (siehe vfl-frage-es-seite),
;; da das ES-Element erst dort eingefuegt wird. ;; da das ES-Element erst dort eingefuegt wird.
@@ -8183,22 +8314,29 @@
;; und der Override sicher zurueckgesetzt wird. Rueckgabe: T bei Erfolg, sonst nil. ;; und der Override sicher zurueckgesetzt wird. Rueckgabe: T bei Erfolg, sonst nil.
;; Bekannte Grenze (nur Modus 2): das Replay von vfl-in-selection braucht die ;; Bekannte Grenze (nur Modus 2): das Replay von vfl-in-selection braucht die
;; Original-Pfadobjekte; wurden sie geloescht, schlaegt der Konverter fuer diesen ;; Original-Pfadobjekte; wurden sie geloescht, schlaegt der Konverter fuer diesen
;; Block sauber fehl (sichtbar am nicht erhoehten Batch-Zaehler). ;; Block sauber fehl (sichtbar am nicht erhoehten Batch-Zaehler). Modus 3
;; ("linienzug3") hat diese Grenze NICHT: es replayt die Segment-Essenz (SEGS)
;; aus dem Journal, nicht die Objekt-Handles - unabhaengig von den Original-LINE/ARC.
(defun vfl-konvertiere-ent (ent ziel-dim / bname xd marker journal ok) (defun vfl-konvertiere-ent (ent ziel-dim / bname xd marker journal ok)
(setq bname (cdr (assoc 2 (entget ent)))) (setq bname (cdr (assoc 2 (entget ent))))
(setq xd (vfl-journal-xdata-lesen ent)) ; (marker . journal) oder nil (setq xd (vfl-journal-xdata-lesen ent)) ; (marker . journal) oder nil
(setq marker (car xd) journal (cdr xd)) (setq marker (car xd) journal (cdr xd))
(if (and (wcmatch bname "VF_*") (if (and (wcmatch bname "VF_*")
(member marker '("linienzug" "linienzug2")) (member marker '("linienzug" "linienzug2" "linienzug3"))
journal) journal
;; Alte OBJS-"linienzug2"-Bloecke (ohne SEGS) NICHT konvertieren -
;; ihr Replay ist mit dem SEGS-Umbau nicht mehr fahrbar (harter
;; Schnitt, wie im Doppelklick-Pfad vfl-edit-ent). Sie ueberspringen
;; (nil zurueck), damit die Batch-Schleife sauber weiterlaeuft.
(or (/= marker "linienzug2") (vfl-journal-hat-segs-p journal)))
(progn (progn
(setq *ssg-ils-dim* ziel-dim) ; Zieldim erzwingen (setq *ssg-ils-dim* ziel-dim) ; Zieldim erzwingen
(entdel ent) ; alten Block weg (entdel ent) ; alten Block weg
(vfl-journal-replay-start journal) ; volle Queue, KEIN Truncate (vfl-journal-replay-start journal) ; volle Queue, KEIN Truncate
(setq ok (vl-catch-all-apply (setq ok (vl-catch-all-apply
(if (= marker "linienzug2") (cond ((= marker "linienzug2") 'vf-linienzug-modus2)
'vf-linienzug-modus2 ((= marker "linienzug3") 'vf-linienzug-modus3)
'vf-linienzug-modus))) (t 'vf-linienzug-modus))))
(setq *ssg-ils-dim* nil) ; Override zuruecksetzen (setq *ssg-ils-dim* nil) ; Override zuruecksetzen
;; Fehlgeschlagene Konvertierung NICHT stumm verschlucken: der alte Block ;; Fehlgeschlagene Konvertierung NICHT stumm verschlucken: der alte Block
;; ist per entdel schon weg, die Teil-Geometrie liegt frei in der ;; ist per entdel schon weg, die Teil-Geometrie liegt frei in der
@@ -8224,8 +8362,17 @@
;; Doppelklick kein *ssg-ils-dim*-Override aktiv ist). Wird VOR dem entdel ;; Doppelklick kein *ssg-ils-dim*-Override aktiv ist). Wird VOR dem entdel
;; gelesen; der Override greift beim anschliessenden Replay/Weiterbau. ;; gelesen; der Override greift beim anschliessenden Replay/Weiterbau.
(setq dim (ssg-dim-xdata-lesen ent)) (setq dim (ssg-dim-xdata-lesen ent))
(if (= marker "linienzug2") (cond
((= marker "linienzug2")
;; Harter Schnitt: nur SEGS-Bloecke (neue Fassung) sind per Doppelklick
;; neu aufbaubar. Alte OBJS-Bloecke (Objekt-Handles statt Segment-Essenz)
;; wuerden im Replay falsche Daten in den Bau geben - klar abweisen statt
;; zu crashen.
(if (vfl-journal-hat-segs-p journal)
(vfl-edit-ent2 ent journal) (vfl-edit-ent2 ent journal)
(alert (ssg-text "vfl-edit-m2-obsolet"))))
((= marker "linienzug3") (vfl-edit-ent3 ent journal))
(t
(progn (progn
(setq glieder (vfl-journal-glieder journal)) (setq glieder (vfl-journal-glieder journal))
(setq n (length glieder)) (setq n (length glieder))
@@ -8281,15 +8428,27 @@
;; Handler selbst (dort ebenfalls (setq *ssg-ils-dim* nil)), denn ;; Handler selbst (dort ebenfalls (setq *ssg-ils-dim* nil)), denn
;; die naechste Zeile wird dann nicht mehr erreicht. ;; die naechste Zeile wird dann nicht mehr erreicht.
(vf-linienzug-modus) (vf-linienzug-modus)
(setq *ssg-ils-dim* nil))))) (setq *ssg-ils-dim* nil))))))
(princ)) (princ))
;; Modus-2-Block per Doppelklick zuruecksetzen: voller Reset - die komplette ;; T, wenn das Journal einen SEGS-Eintrag traegt (Modus 2 NEU / Modus 3 - die
;; gespeicherte Eingabe (inkl. der gewaehlten Pfad-Objekte, siehe ;; Kette ist per Segment-Essenz selbst-enthalten). Alte Modus-2-Bloecke tragen
;; vfl-in-selection) wird 1:1 stumm abgespielt, identischer Neuaufbau. Kein ;; hier stattdessen einen OBJS-Eintrag (Objekt-Handles) - fuer die ist der
;; SEGS-Replay nicht mehr fahrbar (harter Schnitt, siehe vfl-edit-ent).
(defun vfl-journal-hat-segs-p (journal)
(and (vl-some (function (lambda (e) (equal (car e) "SEGS"))) journal) t))
;; Modus-2/3-Block per Doppelklick zuruecksetzen: voller Reset - die komplette
;; gespeicherte Eingabe wird 1:1 stumm abgespielt, identischer Neuaufbau. Kein
;; Sektions-Dialog; der Nutzer kann den neu entstandenen Block danach ganz ;; Sektions-Dialog; der Nutzer kann den neu entstandenen Block danach ganz
;; normal weiterbearbeiten oder loeschen. ;; normal weiterbearbeiten oder loeschen.
(defun vfl-edit-ent2 (ent journal / dim) ;;
;; bau-fn = die Modus-Bau-Funktion, die das Journal abspielt: 'vf-linienzug-modus2
;; fuer Marker "linienzug2" (Pfad-Objekte per OBJS-Handles) bzw.
;; 'vf-linienzug-modus3 fuer "linienzug3" (Segment-Essenz per SEGS - unabhaengig
;; von den Original-Objekten). Beide Modi haben eine flache Klassifizierungs-
;; Schleife ohne editierbare Einzelglieder, daher derselbe volle Reset.
(defun vfl-edit-ent-voll (ent journal bau-fn / dim)
;; Dimension des Bestandsblocks VOR dem entdel merken und beim Neubau als ;; Dimension des Bestandsblocks VOR dem entdel merken und beim Neubau als
;; Override erzwingen (dimensionstreuer Doppelklick-Neubau, sonst globale ;; Override erzwingen (dimensionstreuer Doppelklick-Neubau, sonst globale
;; DXFM_DIM). Danach Override zuruecksetzen. ;; DXFM_DIM). Danach Override zuruecksetzen.
@@ -8300,12 +8459,17 @@
(vfl-journal-replay-start journal) (vfl-journal-replay-start journal)
;; DIREKTER Aufruf, KEIN vl-catch-all-apply - identische Begruendung wie im ;; DIREKTER Aufruf, KEIN vl-catch-all-apply - identische Begruendung wie im
;; Sektions-Zweig von vfl-edit-ent (siehe dortiger Kommentar): der alte Block ;; Sektions-Zweig von vfl-edit-ent (siehe dortiger Kommentar): der alte Block
;; ist per entdel schon weg, bei einem Abbruch muss der *error*-Handler von ;; ist per entdel schon weg, bei einem Abbruch muss der *error*-Handler der
;; vf-linienzug-modus2 die Teil-Geometrie wickeln. Den Dim-Override raeumt im ;; Modus-Funktion die Teil-Geometrie wickeln. Den Dim-Override raeumt im
;; Abbruchfall der Handler ab. ;; Abbruchfall der Handler ab. bau-fn ist ein Funktions-SYMBOL (quoted beim
(vf-linienzug-modus2) ;; Aufruf) - ueber apply aufrufen, nicht als (bau-fn) (das riefe die Funktion
;; namens "bau-fn" auf, nicht den durchgereichten Wert).
(apply bau-fn nil)
(setq *ssg-ils-dim* nil) (setq *ssg-ils-dim* nil)
(princ)) (princ))
;; Rueckwaertskompatibler Wrapper (Modus 2). vfl-edit-ent3 fuer Modus 3.
(defun vfl-edit-ent2 (ent journal) (vfl-edit-ent-voll ent journal 'vf-linienzug-modus2))
(defun vfl-edit-ent3 (ent journal) (vfl-edit-ent-voll ent journal 'vf-linienzug-modus3))
;; ============================================================ ;; ============================================================
;; RECOVERY: Kette aus der Sektions-XDATA der Zeichnung neu aufbauen ;; RECOVERY: Kette aus der Sektions-XDATA der Zeichnung neu aufbauen
@@ -8701,14 +8865,115 @@
(foreach x liste (setq d (abs (- x w))) (if (< d bd) (setq bd d best x))) (foreach x liste (setq d (abs (- x w))) (if (< d bd) (setq bd d best x)))
best) best)
;; --- Segment-Essenz einer selektierten Pfad-Kette (Modus 3) --------------
;; Fuehrt die 5-Feld-Bau-Segmente aus gf-analysiere-kette mit den absoluten
;; Start-/Endpunkten aus gf-sortiere-objekte (kette: (ent sp ep umk)) zu der
;; selbstbeschreibenden 9-Feld-Form zusammen, die das SEGS-Journal traegt:
;; (typ hz laenge bwinkel bseite spx spy epx epy)
;; Beide Listen sind gleich lang und in derselben Reihenfolge (segmente wurde
;; genau aus kette abgeleitet). spx/spy/epx/epy sind die planaren XY der
;; Segment-Enden - sie ersetzen den spaeteren direkten kette-Zugriff (AS-
;; Trimmung am ersten Segment, Soll-Ende-Report), sodass der Bau OHNE die
;; Original-Entities auskommt und beim Replay aus dem Journal gespeist wird.
(defun vfl-kette->segmente9 (kette segmente / out i seg item sp ep)
(setq out '() i 0)
(foreach seg segmente
(setq item (nth i kette) sp (cadr item) ep (caddr item))
(setq out (cons (list (nth 0 seg) (nth 1 seg) (nth 2 seg) (nth 3 seg) (nth 4 seg)
(car sp) (cadr sp) (car ep) (cadr ep))
out))
(setq i (1+ i)))
(reverse out))
;; 2D-Laenge eines Pfad-Objekts: LINE = Abstand Start-Endpunkt, ARC = Sehne
;; (Abstand Start-Endpunkt, dasselbe Mass wie gf-analysiere-kette es fuer die
;; Segmentlaenge nutzt). Nicht-LINE/ARC liefern 0.0 (werden dadurch als "zu
;; kurz" ausgewertet und verworfen). Rein lesend, ohne Nebeneffekt.
(defun vfl-obj-2d-laenge (obj / objname sp ep)
(setq objname (vla-get-ObjectName obj))
(if (or (= objname "AcDbLine") (= objname "AcDbArc"))
(progn
(setq sp (vlax-safearray->list (vlax-variant-value (vla-get-StartPoint obj))))
(setq ep (vlax-safearray->list (vlax-variant-value (vla-get-EndPoint obj))))
(gf-abstand-2d sp ep))
0.0))
;; Aus einer Objektliste alle Elemente unter *vfk-pfad-min-segmentlaenge*
;; herauswerfen (Geister-/Artefakt-Segmente, z.B. von einer Rechteck-Auswahl
;; miterfasste Mini-Objekte). Rueckgabe: (behalten-liste . verworfen-anzahl).
;; Reine Filterung, keine Meldung - der Aufrufer entscheidet ueber die Warnung.
(defun vfl-filter-mini-objekte (obj-liste / behalten verworfen)
(setq behalten '() verworfen 0)
(foreach obj obj-liste
(if (< (vfl-obj-2d-laenge obj) *vfk-pfad-min-segmentlaenge*)
(setq verworfen (1+ verworfen))
(setq behalten (cons obj behalten))))
(cons (reverse behalten) verworfen))
;; Objektauswahl + Analyse (Live) ODER Segment-Essenz aus dem Journal (Replay).
;; Gegenstueck zu vfl-in-selection, aber persistiert NICHT die Entity-Handles,
;; sondern die abgeleitete 9-Feld-Segmentliste (Journal-Art "SEGS"). Damit ist
;; der Modus-3-Block im XDATA vollstaendig beschrieben und der Doppelklick-Neubau
;; unabhaengig davon, ob die urspruenglichen LINE/ARC noch existieren.
;; Rueckgabe: die 9-Feld-Segmentliste oder nil (Abbruch/leer). filter/startpunkt
;; wie bei der Live-Auswahl; ecke-tol wird nur im Live-Zweig fuer die
;; Eck-Vorpruefung gebraucht (Replay-Daten sind bereits geprueft eingefroren).
(defun vfl-in-segmente (filter startpunkt ecke-tol / popped ss k obj-liste kette segmente ecke-bad segs9 filter-erg verworfen)
(setq popped (if *vfl-replay-queue* (vfl-replay-pop) nil))
(if popped
;; --- Replay: fertige Segment-Essenz direkt aus dem Journal ---
(progn
(setq segs9 (car popped))
(if (null segs9)
(if (vfl-headless-p) (vfl-headless-abbruch "SEGS-leer")))
segs9)
;; --- Live: Objekte waehlen, sortieren, analysieren, dann journalisieren ---
(progn
(if (vfl-headless-p) (vfl-headless-abbruch "SEGS"))
(setq ss (ssget filter))
(if (null ss)
(progn (vfl-journal-record "NIL" nil) nil)
(progn
;; Auswahlsatz -> VLA-Objektliste (wie im frueheren vfl-in-selection).
(setq obj-liste '() k 0)
(while (< k (sslength ss))
(setq obj-liste (cons (vlax-ename->vla-object (ssname ss k)) obj-liste))
(setq k (1+ k)))
(setq obj-liste (reverse obj-liste))
;; Geister-/Mini-Objekte VOR der Sortierung ausfiltern: ein von einer
;; Rechteck-Auswahl miterfasstes winziges Segment (unter
;; *vfk-pfad-min-segmentlaenge*) wuerde sonst per Nearest-Neighbor
;; stumm in die Kette gehaengt und ein Geister-Bauteil erzeugen (realer
;; Bug: 14-mm-Bogen am Kettenende). Verworfene mit Grund melden.
(setq filter-erg (vfl-filter-mini-objekte obj-liste))
(setq obj-liste (car filter-erg) verworfen (cdr filter-erg))
(if (> verworfen 0)
(vfl-meldung (ssg-textf "vfl-m3-mini-objekte-verworfen"
(list (itoa verworfen) (rtos *vfk-pfad-min-segmentlaenge* 2 0)))))
(setq kette (if obj-liste (gf-sortiere-objekte obj-liste startpunkt) nil))
(if (null kette)
(progn (princ (ssg-text "vfl-m3-fehler-nicht-sortierbar"))
(vfl-journal-record "NIL" nil) nil)
(progn
(setq segmente (gf-analysiere-kette kette))
(setq ecke-bad (vfl2-pruefe-eckwinkel kette ecke-tol))
(if ecke-bad
(progn
(vfl-meldung (ssg-textf "vfl-vwnb-alert-eckwinkel" (list (rtos ecke-bad 2 1))))
(vfl-journal-record "NIL" nil) nil)
(progn
(setq segs9 (vfl-kette->segmente9 kette segmente))
(vfl-journal-record "SEGS" segs9)
segs9)))))))))
(defun vf-linienzug-modus3 ( / ss obj-liste startpunkt start-hoehe as-seite (defun vf-linienzug-modus3 ( / ss obj-liste startpunkt start-hoehe as-seite
es-seite kette segmente n i seg seg-ent typ hz laenge es-seite kette segmente segmente9 n i seg seg-ent typ hz laenge
bwinkel bseite frame antwort winkel deltaL bwinkel bseite frame antwort winkel deltaL
anzahl-gf anzahl-vf vfl-nummer lastEnt hoehe-bis anzahl-gf anzahl-vf vfl-nummer lastEnt hoehe-bis
ecke-bad first-line last-line letzt-winkel ecke-bad first-line last-line letzt-winkel
aus ein soll-ende ist-ende run-typ richtn best-w aus ein soll-ende ist-ende run-typ richtn best-w
kvariante L_VF ent-fp rad3 just-open kvariante L_VF ent-fp rad3 just-open
dbg-an old-error) dbg-an old-error dbg-old-error vfl-ins)
(princ "\n\n=========================================") (princ "\n\n=========================================")
(princ (ssg-text "vfl-vwnb-titel")) (princ (ssg-text "vfl-vwnb-titel"))
(princ "\n=========================================") (princ "\n=========================================")
@@ -8720,34 +8985,40 @@
;; automatisch ueber vfl-journal-record mitgeloggt (siehe dort) - hier nur ;; automatisch ueber vfl-journal-record mitgeloggt (siehe dort) - hier nur
;; noch Session-Start/-Ende + Zwischenergebnisse, die NICHT direkt aus einer ;; noch Session-Start/-Ende + Zwischenergebnisse, die NICHT direkt aus einer
;; Nutzereingabe stammen (Analyse-Ergebnis, Segment-Header, Bau-Ergebnis). ;; Nutzereingabe stammen (Analyse-Ergebnis, Segment-Header, Bau-Ergebnis).
;; *vfl-journal*/*vflw-pending* werden dabei nebenbei mitbefuellt, aber von ;; *vfl-journal* wird - wie bei Modus 1/2 - als XDATA (Marker "linienzug3")
;; Modus 3 nirgends gelesen/persistiert (kein Replay/XDATA wie bei Modus 1/2 ;; persistiert und ist damit per Doppelklick editierbar (vfl-edit-ent3, voller
;; - das ist hier bewusst NICHT mit umgebaut, nur das Logging). ;; Reset-Replay). Statt der Objekt-Handles traegt es die Segment-ESSENZ (SEGS,
;; Der *error*-Handler schliesst die Datei bei Abbruch sauber. ;; siehe vfl-in-segmente): der Block ist selbst-enthalten und der Neubau
;; unabhaengig von den urspruenglichen LINE/ARC.
;; FRUEHER *error*-Handler: schliesst nur die dbg-Datei sauber, falls waehrend
;; der Pfad-/Praeambel-Phase (noch keine Geometrie) abgebrochen wird. Der
;; eigentliche Abbruch-WICKLER (Teil-Geometrie zu einem VF_n-Block sichern)
;; wird erst nach dem Setup scharf geschaltet (siehe unten), sobald
;; vfl-nummer/lastEnt feststehen - analog Modus 2.
(setq dbg-an (dbg-schalter-open "vfl-modus3" "vfl_modus3.dbg" "DXFM_LOG")) (setq dbg-an (dbg-schalter-open "vfl-modus3" "vfl_modus3.dbg" "DXFM_LOG"))
(if dbg-an (if dbg-an
(progn (progn
(dbgf "vf-linienzug-modus3") (dbgf "vf-linienzug-modus3")
(dbgmsg "=== SESSION Modus 3 (Vorwaerts-Nachbau) START ===") (dbgmsg "=== SESSION Modus 3 (Vorwaerts-Nachbau) START ===")
(dbgflush))) (dbgflush)))
(setq old-error *error*) (setq dbg-old-error *error*)
(setq *error* (setq *error*
(function (lambda (msg) (function (lambda (msg)
(setq *error* old-error) (setq *error* dbg-old-error)
(if dbg-an (if dbg-an
(progn (progn
(dbgmsg (strcat "=== ABBRUCH: " (if msg msg "(exit)") " ===")) (dbgmsg (strcat "=== ABBRUCH: " (if msg msg "(exit)") " ==="))
(dbgreturn nil) (dbgreturn nil)
(dbgclose))) (dbgclose)))
(if old-error (old-error msg) (princ))))) (if dbg-old-error (dbg-old-error msg) (princ)))))
(vfl-gf-abhaengigkeit-sicherstellen "vfl-m3-alert-gf-modul") (vfl-gf-abhaengigkeit-sicherstellen "vfl-m3-alert-gf-modul")
;; --- 1. Pfad-Objekte waehlen (LINE/ARC) --- ;; --- 1./3. Pfad-Objekte + Segment-Essenz: siehe Schritt 3 unten. ---
(princ (ssg-text "vfl-m3-pfad-waehlen")) ;; Auswahl UND Ableitung laufen zusammen in vfl-in-segmente, weil die
(setq ss (vfl-in-selection '((0 . "LINE,ARC")))) ;; Sortierung den Startpunkt braucht (Schritt 2) - die Objektauswahl steht
(if (null ss) (progn (princ (ssg-text "vfl-m3-keine-objekte")) (exit))) ;; daher NACH der Startpunkt-/AS-Praeambel, nicht mehr davor. Journalisiert
(setq obj-liste (mapcar 'vlax-ename->vla-object ss)) ;; wird nur noch die abgeleitete Segment-Essenz (SEGS), nicht die Handles.
;; --- 2. Startpunkt (hoehere Seite) + Hoehe + AS-Seite --- ;; --- 2. Startpunkt (hoehere Seite) + Hoehe + AS-Seite ---
;; Wizard: Startpunkt+Hoehe als EIN Gruppen-Dialog (analog Modus 2). Die ;; Wizard: Startpunkt+Hoehe als EIN Gruppen-Dialog (analog Modus 2). Die
@@ -8784,24 +9055,25 @@
(setq as-seite (if (= (vfl-in-string (ssg-text "prompt-wahl-1-2")) "2") "rechts" "links")) (setq as-seite (if (= (vfl-in-string (ssg-text "prompt-wahl-1-2")) "2") "rechts" "links"))
(vf-set-as-masse *vfl-as-winkel* as-seite) ; Masse fuer Variante (vf-set-as-masse *vfl-as-winkel* as-seite) ; Masse fuer Variante
;; --- 3. Sortieren + Analysieren --- ;; --- 3. Pfad waehlen + Sortieren + Analysieren + Eck-Vorpruefung ---
(setq kette (gf-sortiere-objekte obj-liste startpunkt)) ;; vfl-in-segmente kapselt Live (ssget -> gf-sortiere-objekte ->
(if (null kette) ;; gf-analysiere-kette -> Eck-Vorpruefung -> 9-Feld-Essenz) UND Replay
(progn (princ (ssg-text "vfl-m3-fehler-nicht-sortierbar")) (exit))) ;; (fertige Essenz aus dem SEGS-Journal). Persistiert die Segment-Essenz
(setq segmente (gf-analysiere-kette kette)) ;; statt der Objekt-Handles - damit ist der Block selbst-enthalten und der
;; Doppelklick-Neubau (vfl-edit-ent3) braucht die Original-LINE/ARC nicht.
(princ (ssg-text "vfl-m3-pfad-waehlen"))
(setq segmente9 (vfl-in-segmente '((0 . "LINE,ARC")) startpunkt *vfk-eckwinkel-toleranz*))
(if (null segmente9) (progn (princ (ssg-text "vfl-m3-keine-objekte")) (exit)))
;; segmente = 5-Feld-Bausicht (typ hz laenge bwinkel bseite), abgeleitet aus
;; der 9-Feld-Essenz - der bestehende Bau unten arbeitet unveraendert damit.
(setq segmente (mapcar (function (lambda (s) (list (nth 0 s) (nth 1 s) (nth 2 s) (nth 3 s) (nth 4 s))))
segmente9))
(if dbg-an (if dbg-an
(progn (progn
(dbgmsg (strcat "ANALYSE: " (itoa (length segmente)) " Segment(e) erkannt")) (dbgmsg (strcat "ANALYSE: " (itoa (length segmente)) " Segment(e) erkannt"))
(dbg 'segmente) (dbg 'segmente)
(dbgflush))) (dbgflush)))
;; --- 4. Eck-Winkel-Vorpruefung (vor dem Bau) ---
(setq ecke-bad (vfl2-pruefe-eckwinkel kette *vfk-eckwinkel-toleranz*))
(if ecke-bad
(progn
(vfl-meldung (ssg-textf "vfl-vwnb-alert-eckwinkel" (list (rtos ecke-bad 2 1))))
(exit)))
;; --- 5. Setup --- ;; --- 5. Setup ---
(setq aus (if aus-dx aus-dx *vfk-as-es-fallback-dx*) ein (if ein-dx ein-dx *vfk-as-es-fallback-dx*)) (setq aus (if aus-dx aus-dx *vfk-as-es-fallback-dx*) ein (if ein-dx ein-dx *vfk-as-es-fallback-dx*))
(setq rad3 (* (float (ssg-cfg-or "vario" "gefaelle_winkel" 3)) (/ pi 180.0))) (setq rad3 (* (float (ssg-cfg-or "vario" "gefaelle_winkel" 3)) (/ pi 180.0)))
@@ -8811,7 +9083,19 @@
(setq vfl-nummer (vf-next-number)) (setq vfl-nummer (vf-next-number))
(setq lastEnt (vf-lastent-ohne-attribute)) (setq lastEnt (vf-lastent-ohne-attribute))
(vfl-acc-reset) (vfl-acc-reset)
(setq anzahl-gf 0 anzahl-vf 0 frame nil n (length segmente)) (setq anzahl-gf 0 anzahl-vf 0 frame nil)
;; --- Abbruch-Sicherung ab hier scharf (analog Modus 1/2) ---
;; Ab Schritt 6 kann jederzeit Geometrie entstehen. Der Wickler sichert die
;; bis zum Abbruch gebaute Teil-Geometrie als VF_n-Block mit dem vollen
;; Journal (Marker "linienzug3") - sie bleibt stehen und ist per Doppelklick
;; fortsetz-/editierbar. Loest den fruehen dbg-Hook ab (Ruecksprungziel bleibt
;; dbg-old-error) und uebernimmt das dbgclose. *ssg-ils-dim* zuruecksetzen:
;; Begruendung siehe gleichlautende Stelle in vf-linienzug-modus.
(setq old-error dbg-old-error)
;; Scharfen Abbruch-Wickel-Handler installieren (gemeinsam, siehe
;; vfl-abbruch-handler-installieren).
(vfl-abbruch-handler-installieren "linienzug3")
(setq n (length segmente))
;; erste/letzte "Linie" fuer die Trimmung bestimmen ;; erste/letzte "Linie" fuer die Trimmung bestimmen
(setq first-line -1 last-line -1 i 0) (setq first-line -1 last-line -1 i 0)
(while (< i n) (while (< i n)
@@ -8830,13 +9114,19 @@
(setq i 0) (setq i 0)
(while (< i n) (while (< i n)
(setq seg (nth i segmente) typ (car seg) hz (cadr seg)) (setq seg (nth i segmente) typ (car seg) hz (cadr seg))
(setq seg-ent (car (nth i kette))) ; echte LINE/ARC-Entity dieses Segments ;; seg-ent: KEINE Live-Entity mehr - Modus 3 baut jetzt aus der Segment-
;; Essenz (SEGS), nicht aus durchgereichten LINE/ARC. Damit entfaellt das
;; kosmetische Pfad-Segment-Highlight (vfl-seg-highlight ist nil-sicher).
;; Der Wegfall ist bewusst: die Essenz macht den Bau/Replay unabhaengig von
;; den Original-Objekten, ein Highlight haette beim Doppelklick-Neubau ohnehin
;; keine Entity mehr zum Hervorheben.
(setq seg-ent nil)
;; Wizard: alle Fragen dieses Segments in EINEM Dialog erfassen (die ;; Wizard: alle Fragen dieses Segments in EINEM Dialog erfassen (die
;; unveraenderten vfl-in-*-Aufrufe unten konsumieren die Antworten aus ;; unveraenderten vfl-in-*-Aufrufe unten konsumieren die Antworten aus
;; *vflw-pending*). Segment vorher in der Zeichnung hervorheben. Bei einem ;; *vflw-pending*). Bei einem Bogen ist Vario-Kurve nur im offenen VF-Lauf
;; Bogen ist Vario-Kurve nur im offenen VF-Lauf erlaubt - deshalb die ;; erlaubt - deshalb die Variante-Antwort nur mitschicken, wenn run-typ="VF"
;; Variante-Antwort nur mitschicken, wenn run-typ="VF" (sonst konsumiert ;; (sonst konsumiert der Aufrufer sie nicht und sie wuerde ins naechste
;; der Aufrufer sie nicht und sie wuerde ins naechste Segment lecken). ;; Segment lecken).
(if (vfl-wizard-aktiv) (if (vfl-wizard-aktiv)
(progn (progn
(vfl-seg-highlight seg-ent T) (vfl-seg-highlight seg-ent T)
@@ -8949,12 +9239,21 @@
(setq frame (vfl-insert-es-element "GF" frame (car (frame->hz-winkel frame)) (setq frame (vfl-insert-es-element "GF" frame (car (frame->hz-winkel frame))
(if letzt-winkel letzt-winkel *vfk-gefaelle-winkel*) (caddr (car frame)) es-seite))) (if letzt-winkel letzt-winkel *vfk-gefaelle-winkel*) (caddr (car frame)) es-seite)))
;; --- 8. Block + Ist-Ziel-Report --- ;; --- 8. Block + Journal-XDATA + Ist-Ziel-Report ---
(setq hoehe-bis (caddr (car frame))) (setq hoehe-bis (caddr (car frame)))
(setq vfl-ins
(vfl-block-erstellen vfl-nummer anzahl-gf anzahl-vf (caddr startpunkt) hoehe-bis (vfl-block-erstellen vfl-nummer anzahl-gf anzahl-vf (caddr startpunkt) hoehe-bis
(vfl-planar-dist startpunkt (car frame)) as-seite es-seite startpunkt lastEnt) (vfl-planar-dist startpunkt (car frame)) as-seite es-seite startpunkt lastEnt))
;; Eingabe-Journal am fertigen Block persistieren (Marker "linienzug3") ->
;; spaeter per Doppelklick editierbar (vfl-edit-ent3: voller Reset-Replay wie
;; Modus 2, aus der Segment-Essenz statt aus Objekt-Handles).
(if vfl-ins (vfl-journal-xdata-schreiben vfl-ins "linienzug3"))
(setq soll-ende (caddr (last kette))) ; Endpunkt des letzten gezeichneten Segments ;; Endpunkt des letzten gezeichneten Segments - aus der Segment-Essenz
;; (epx/epy des letzten Segments), nicht mehr aus den Original-Entities.
;; Damit stimmt der Soll-Ende-Report auch beim Doppelklick-Neubau, wenn die
;; urspruenglichen LINE/ARC laengst weg sind.
(setq soll-ende (list (nth 7 (last segmente9)) (nth 8 (last segmente9))))
(setq ist-ende (car frame)) ; ES-KS_AUS (setq ist-ende (car frame)) ; ES-KS_AUS
(princ "\n\n=========================================") (princ "\n\n=========================================")
(princ (ssg-text "vfl-vwnb-eingefuegt")) (princ (ssg-text "vfl-vwnb-eingefuegt"))
@@ -9199,7 +9498,7 @@
100.0)))) 100.0))))
(defun vf-linienzug-modus2 ( / ss k obj-liste startpunkt start-hoehe as-seite (defun vf-linienzug-modus2 ( / ss k obj-liste startpunkt start-hoehe as-seite
endpunkt end-hoehe es-seite kette segmente n i seg endpunkt end-hoehe es-seite kette segmente segmente9 n i seg
seg-ent seg-ent
typ hz laenge bwinkel bseite antwort winkel klass typ hz laenge bwinkel bseite antwort winkel klass
plan e ecke-bad vf-start vf-ende dz z-front plan e ecke-bad vf-start vf-ende dz z-front
@@ -9264,11 +9563,14 @@
(vfl-gf-abhaengigkeit-sicherstellen "vfl-m3-alert-gf-modul") (vfl-gf-abhaengigkeit-sicherstellen "vfl-m3-alert-gf-modul")
;; --- 1. Pfad-Objekte waehlen --- ;; --- 1./4. Pfad-Objekte + Segment-Essenz: siehe Schritt 4 unten. ---
(princ (ssg-text "vfl-m3-pfad-waehlen")) ;; Auswahl UND Ableitung laufen zusammen in vfl-in-segmente (Live: ssget ->
(setq ss (vfl-in-selection '((0 . "LINE,ARC")))) ;; gf-sortiere-objekte -> gf-analysiere-kette -> Eck-Vorpruefung -> SEGS;
(if (null ss) (progn (princ (ssg-text "vfl-m3-keine-objekte")) (exit))) ;; Replay: fertige Essenz aus dem Journal). Persistiert die Segment-Essenz
(setq obj-liste (mapcar 'vlax-ename->vla-object ss)) ;; statt der Objekt-Handles - der Block ist damit selbst-enthalten und der
;; Doppelklick-Neubau unabhaengig von den Original-LINE/ARC (behebt den
;; frueheren OBJS-Replay-Drift). Da vfl-in-segmente den startpunkt fuer die
;; Sortierung braucht, steht die Auswahl NACH der Startpunkt-Praeambel.
;; --- 2. Startpunkt + Z + AS-Seite --- ;; --- 2. Startpunkt + Z + AS-Seite ---
(if (vfl-wizard-aktiv) (if (vfl-wizard-aktiv)
@@ -9292,22 +9594,7 @@
;; Globals pro Lauf zuruecksetzen (siehe Kommentar in vf-linienzug-modus). ;; Globals pro Lauf zuruecksetzen (siehe Kommentar in vf-linienzug-modus).
(setq *vfl-as-winkel* nil *vfl-es-winkel* nil) (setq *vfl-as-winkel* nil *vfl-es-winkel* nil)
(if as-vorhanden (if as-vorhanden
(progn (setq as-seite (vfl-frage-as-winkel-seite "gf-seite-aus-header"))
(princ (ssg-text "vf-winkel-aus-header"))
(princ (ssg-text "vf-winkel-90"))
(princ (ssg-text "vf-winkel-30"))
(setq *vfl-as-winkel*
(vfl-in-value "STR"
(function (lambda () (vf-frage-element-winkel "vf-winkel-aus-header"))))) ; 30/90 vor Seite
(princ (ssg-text "gf-seite-aus-header"))
(princ (ssg-text "gf-seite-links"))
(princ (ssg-text "gf-seite-rechts"))
(setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2")
(list (ssg-text "gf-seite-links") (ssg-text "gf-seite-rechts")) 1
"gf-seite-aus-header"))
(setq as-seite (if (= antwort "2") "rechts" "links"))
(vf-set-as-masse *vfl-as-winkel* as-seite) ; Masse fuer Variante
)
) )
;; --- 3. Endpunkt + Z + ES-Seite (NEU in Modus 3) --- ;; --- 3. Endpunkt + Z + ES-Seite (NEU in Modus 3) ---
@@ -9330,34 +9617,19 @@
"vfl-es-setzen-frage")) "vfl-es-setzen-frage"))
(setq es-vorhanden (/= antwort "2")) (setq es-vorhanden (/= antwort "2"))
(if es-vorhanden (if es-vorhanden
(progn (setq es-seite (vfl-frage-es-winkel-seite "gf-seite-ein-header"))
(princ (ssg-text "vf-winkel-ein-header"))
(princ (ssg-text "vf-winkel-90"))
(princ (ssg-text "vf-winkel-30"))
(setq *vfl-es-winkel*
(vfl-in-value "STR"
(function (lambda () (vf-frage-element-winkel "vf-winkel-ein-header"))))) ; 30/90 vor Seite
(princ (ssg-text "gf-seite-ein-header"))
(princ (ssg-text "gf-seite-links"))
(princ (ssg-text "gf-seite-rechts"))
(setq antwort (vfl-menu (ssg-text "prompt-wahl-1-2")
(list (ssg-text "gf-seite-links") (ssg-text "gf-seite-rechts")) 1
"gf-seite-ein-header"))
(setq es-seite (if (= antwort "2") "rechts" "links"))
(vf-set-es-masse *vfl-es-winkel* es-seite) ; Masse fuer Variante
)
) )
;; --- 4. Sortieren + Analysieren + Eck-Vorpruefung --- ;; --- 4. Pfad waehlen + Sortieren + Analysieren + Eck-Vorpruefung ---
(setq kette (gf-sortiere-objekte obj-liste startpunkt)) ;; vfl-in-segmente kapselt Live + Replay und persistiert die Segment-Essenz
(if (null kette) ;; (SEGS) statt der Objekt-Handles - siehe Schritt 1 oben und Modus 3.
(progn (princ (ssg-text "vfl-m3-fehler-nicht-sortierbar")) (exit))) (princ (ssg-text "vfl-m3-pfad-waehlen"))
(setq segmente (gf-analysiere-kette kette)) (setq segmente9 (vfl-in-segmente '((0 . "LINE,ARC")) startpunkt *vfk-eckwinkel-toleranz*))
(setq ecke-bad (vfl2-pruefe-eckwinkel kette *vfk-eckwinkel-toleranz*)) (if (null segmente9) (progn (princ (ssg-text "vfl-m3-keine-objekte")) (exit)))
(if ecke-bad ;; segmente = 5-Feld-Bausicht (typ hz laenge bwinkel bseite) aus der 9-Feld-
(progn ;; Essenz - die Klassifizierungs-Schleife unten arbeitet unveraendert damit.
(vfl-meldung (ssg-textf "vfl-m3-alert-eckwinkel" (list (rtos ecke-bad 2 1)))) (setq segmente (mapcar (function (lambda (s) (list (nth 0 s) (nth 1 s) (nth 2 s) (nth 3 s) (nth 4 s))))
(exit))) segmente9))
;; --- 5. Klassifizierung (Phase A: nur speichern, nichts bauen) --- ;; --- 5. Klassifizierung (Phase A: nur speichern, nichts bauen) ---
;; Plan-Eintrag Gerade: ("Linie" hz laenge klass winkel) klass=GF/VF ;; Plan-Eintrag Gerade: ("Linie" hz laenge klass winkel) klass=GF/VF
@@ -9365,13 +9637,18 @@
(setq n (length segmente) i 0 plan '()) (setq n (length segmente) i 0 plan '())
(while (< i n) (while (< i n)
(setq seg (nth i segmente) typ (car seg) hz (cadr seg) laenge (caddr seg)) (setq seg (nth i segmente) typ (car seg) hz (cadr seg) laenge (caddr seg))
(setq seg-ent (car (nth i kette))) ; echte LINE/ARC-Entity dieses Segments ;; seg-ent: KEINE Live-Entity mehr - Modus 2 baut jetzt aus der Segment-
;; Essenz (SEGS), nicht aus durchgereichten LINE/ARC (siehe Modus 3). Damit
;; entfaellt das kosmetische Pfad-Segment-Highlight (vfl-seg-highlight ist
;; nil-sicher). Bewusst: die Essenz macht Bau/Replay unabhaengig von den
;; Original-Objekten, ein Highlight haette beim Doppelklick-Neubau ohnehin
;; keine Entity mehr.
(setq seg-ent nil)
;; Wizard: alle Fragen dieses Segments in EINEM Dialog erfassen (die ;; Wizard: alle Fragen dieses Segments in EINEM Dialog erfassen (die
;; unveraenderten vfl-menu/vfl-in-*-Aufrufe unten konsumieren die Antworten ;; unveraenderten vfl-menu/vfl-in-*-Aufrufe unten konsumieren die Antworten
;; aus *vflw-pending*). Segment vorher in der Zeichnung hervorheben. Der ;; aus *vflw-pending*). Der nach-kurve-Fall stellt keine Frage (auto-VF) ->
;; nach-kurve-Fall stellt keine Frage (auto-VF) -> kein Dialog. Ebenso ;; kein Dialog. Ebenso nach-vf bei einem Bogen: Vario-Kurve ist dann
;; nach-vf bei einem Bogen: Vario-Kurve ist dann erzwungen (eine VF- ;; erzwungen (eine VF-Einheit kann nicht in einen GF-Bogen auslaufen).
;; Einheit kann nicht in einen GF-Bogen auslaufen), auch kein Dialog.
(if (and (vfl-wizard-aktiv) (if (and (vfl-wizard-aktiv)
(not (and (= typ "Linie") nach-kurve)) (not (and (= typ "Linie") nach-kurve))
(not (and (= typ "Bogen") nach-vf))) (not (and (= typ "Bogen") nach-vf)))
@@ -9470,9 +9747,14 @@
(if (/= vf-count 1) (if (/= vf-count 1)
(progn (progn
;; Konsolen-Diagnose (fuer den Log / dbg-Nachvollzug) ...
(princ (ssg-textf "vfl-m3-vf-laeufe" (list (itoa vf-count)))) (princ (ssg-textf "vfl-m3-vf-laeufe" (list (itoa vf-count))))
(princ (ssg-text "vfl-m3-vf-lauf-noetig")) (princ (ssg-text "vfl-m3-vf-lauf-noetig"))
(princ (ssg-text "vfl-m3-vf-lauf-m3b")) (princ (ssg-text "vfl-m3-vf-lauf-m3b"))
;; ... UND als sichtbare Meldung, damit die Abweisung nicht in der
;; Konsole untergeht (vfl-meldung zeigt bei erlaubter GUI ein alert,
;; headless nur princ - blockiert also einen Batch-Lauf nicht).
(vfl-meldung (ssg-textf "vfl-m3-vf-lauf-alert" (list (itoa vf-count))))
(exit))) (exit)))
;; Traeger-Stueck = laengste VF-Gerade im Lauf (traegt die ganze Hoehe). ;; Traeger-Stueck = laengste VF-Gerade im Lauf (traegt die ganze Hoehe).
(setq carrier-idx -1 i vf-start) (setq carrier-idx -1 i vf-start)
@@ -9626,22 +9908,10 @@
;; dbg-Hook selbst) und uebernehmen das dbgclose gleich mit, sonst wuerde ;; dbg-Hook selbst) und uebernehmen das dbgclose gleich mit, sonst wuerde
;; bei einem Abbruch ab hier doppelt geschlossen. ;; bei einem Abbruch ab hier doppelt geschlossen.
(setq old-error dbg-old-error) (setq old-error dbg-old-error)
;; *ssg-ils-dim* hier zuruecksetzen - Begruendung siehe gleichlautende Stelle ;; Scharfen Abbruch-Wickel-Handler installieren (loest den fruehen dbg-Hook ab,
;; im Handler von vf-linienzug-modus (Abbruch kehrt nie zum Aufrufer zurueck, ;; Ruecksprungziel bleibt dbg-old-error via old-error). Gemeinsam fuer alle
;; der den Override abraeumen wuerde). ;; Modi - siehe vfl-abbruch-handler-installieren.
(setq *error* (vfl-abbruch-handler-installieren "linienzug2")
(function (lambda (msg)
(setq *error* old-error)
(setq *ssg-ils-dim* nil)
(vfl-modus-abbruch-sichern lastEnt vfl-nummer anzahl-gf anzahl-vf
startpunkt frame as-seite es-seite "linienzug2")
(if dbg-an
(progn
(dbgmsg (strcat "=== ABBRUCH: " (if msg msg "(exit)") " ==="))
(dbgreturn nil)
(dbgclose)))
(if (and (boundp '*ssg-start-stack*) *ssg-start-stack*) (ssg-end))
(princ))))
;; erste/letzte Gerade fuer AS/ES-Trimmung bestimmen ;; erste/letzte Gerade fuer AS/ES-Trimmung bestimmen
(setq first-line -1 last-line -1 i 0) (setq first-line -1 last-line -1 i 0)
(while (< i n) (while (< i n)
+13
View File
@@ -37,6 +37,17 @@
# in den Summierungszaehlern "Anzahl aller BTBeladung"/"Anzahl aller # in den Summierungszaehlern "Anzahl aller BTBeladung"/"Anzahl aller
# Abwurfstationen" der ILS-Automation-Zeile). # Abwurfstationen" der ILS-Automation-Zeile).
# #
# pattern_eingang/pattern_ausgang: Eingangs-/Ausgangssymbole des Material-
# flusses (siehe ils-insert-station in SSG_LIB_Commands.lsp, Bloecke
# Eingang_<DIM>/Ausgang_<DIM>). NUR fuer den CSV-Export: export_csv.py fuehrt
# sie als eigene TeileArt-Zeilen ("Eingang"/"Ausgang", Merkmale wie BTMT) UND
# als frei platzierte Umlauf-Punkte auf einem Kreisel (KREISEL_UMLAUF_TEILEARTEN
# in lib/export_neighbors.py), damit sie - wie Separator/AS/ES - in die
# "TrackIds"-Spalte gelangen. export_sivas.py schliesst sie bewusst aus (kein
# Sivas-Eintrag). Voraussetzung fuer eine stabile TeileId ist ein ID-ATTDEF in
# den DWGs (ssg-id-check-all fuellt es vor jedem Export, siehe
# ssg-id-collect-blocks in ssg_id.lsp).
#
# pattern_geruest: Geruest-Baugruppen (GeruestInsert.lsp, GR_<id>_<timestamp>). # pattern_geruest: Geruest-Baugruppen (GeruestInsert.lsp, GR_<id>_<timestamp>).
# Eigenstaendig wie BTMT, kein ANZAHL_*-Attribut an einem Carrier - export_csv.py # Eigenstaendig wie BTMT, kein ANZAHL_*-Attribut an einem Carrier - export_csv.py
# UND export_sivas.py fuehren sie als eigene TeileArt-Zeilen. # UND export_sivas.py fuehren sie als eigene TeileArt-Zeilen.
@@ -54,6 +65,8 @@ pattern_separator= Separator_SP*, S-LP*
pattern_scanner= Scanner* pattern_scanner= Scanner*
pattern_btmt_beladung= BTMT-Beladung* pattern_btmt_beladung= BTMT-Beladung*
pattern_btmt_entladung= SC_Entladung* pattern_btmt_entladung= SC_Entladung*
pattern_eingang= Eingang*
pattern_ausgang= Ausgang*
pattern_geruest= GR_* pattern_geruest= GR_*
pattern_ks_subblocks= KS_EIN, KS_AUS, K1, K2, K3, K4 pattern_ks_subblocks= KS_EIN, KS_AUS, K1, K2, K3, K4
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+14
View File
@@ -0,0 +1,14 @@
{
"Eingang_2D": {
"ID": ""
},
"Eingang_3D": {
"ID": ""
},
"Ausgang_2D": {
"ID": ""
},
"Ausgang_3D": {
"ID": ""
}
}
+104 -42
View File
@@ -1,6 +1,7 @@
{ {
"Rohrabhänger": { "Rohrabhänger": {
"id": "Rohrabhaenger", "id": "Rohrabhaenger",
"titel": "Rohrabhaenger",
"teileart": "Geruest Rohrabhaenger", "teileart": "Geruest Rohrabhaenger",
"dummy": "Rohrabhaenger_dummy.dwg", "dummy": "Rohrabhaenger_dummy.dwg",
"attribute": { "attribute": {
@@ -10,11 +11,11 @@
"max": 6000, "max": 6000,
"einheit": "mm" "einheit": "mm"
}, },
"Farbe": { "Teilefarbe": {
"typ": "auswahl", "typ": "auswahl",
"optionen": [ "optionen": [
"RAL7035", "RAL 7035 Lichtgrau",
"RAL7030" "RAL 7030 Steingrau"
], ],
"frei_erlaubt": true, "frei_erlaubt": true,
"frei_option": "Individuell" "frei_option": "Individuell"
@@ -133,50 +134,39 @@
"sivas_id": 922021003, "sivas_id": 922021003,
"menge": 1 "menge": 1
} }
]
},
"1\" Säulen": {
"id": "Saeulen_1Zoll",
"teileart": "Geruest Saeule 1 Zoll",
"dummy": "R2-Saeule_dummy.dwg",
"attribute": {
"Laenge": {
"typ": "number",
"min": 0,
"max": 6000,
"einheit": "mm"
},
"Farbe": {
"typ": "auswahl",
"optionen": [
"RAL7035",
"RAL7030"
], ],
"frei_erlaubt": true, "konfigurationen": [
"frei_option": "Individuell"
}
},
"geometrie_varianten": [
{ {
"name": "R 1\"", "name": "Abhaengung Spannpratze / Rohrklammer",
"teile": [ "variante": "ROHR R 1\" 33,7 x 2,6",
{ "laenge": 2000,
"artikel": "ROHR R 1\" 33,7 x 2,6", "teilefarbe": "RAL 7035 Lichtgrau",
"sivas_id": 821006026, "oben": [
"menge": 1 { "artikel": "SPANNPRATZE SELBSTSICHERND KPL.", "menge": 1 },
{ "artikel": "Schraubenset", "menge": 1 }
],
"unten": [
{ "artikel": "ROHRKLAMMER 1\" KPL. VERTIKAL", "menge": 2 }
]
}, },
{ {
"artikel": "CAD - BODENSÄULE R 1\" - BODENPLATTE KPL.", "name": "Abhaengung Duebel / T-Verbindung",
"sivas_id": 821004200, "variante": "ROHR R 1\" 33,7 x 2,6 einseitig gequetscht",
"menge": 1 "laenge": 1500,
} "teilefarbe": "RAL 7035 Lichtgrau",
"oben": [
{ "artikel": "DURCHSTECKANKER HST M12X115X20", "menge": 2 }
],
"unten": [
{ "artikel": "ROHRKLAMMER 1\" T-VERBINDUNG KPL.", "menge": 1 }
] ]
} }
] ]
}, },
"2\" Säulen": { "Säulen": {
"id": "Saeulen_2Zoll", "id": "Saeulen",
"teileart": "Geruest Saeule 2 Zoll", "titel": "Saeulen",
"teileart": "Geruest Saeule",
"dummy": "R2-Saeule_dummy.dwg", "dummy": "R2-Saeule_dummy.dwg",
"attribute": { "attribute": {
"Laenge": { "Laenge": {
@@ -185,11 +175,11 @@
"max": 6000, "max": 6000,
"einheit": "mm" "einheit": "mm"
}, },
"Farbe": { "Teilefarbe": {
"typ": "auswahl", "typ": "auswahl",
"optionen": [ "optionen": [
"RAL7035", "RAL 7035 Lichtgrau",
"RAL7030" "RAL 7030 Steingrau"
], ],
"frei_erlaubt": true, "frei_erlaubt": true,
"frei_option": "Individuell" "frei_option": "Individuell"
@@ -208,8 +198,25 @@
] ]
}, },
"geometrie_varianten": [ "geometrie_varianten": [
{
"name": "R 1\"",
"teileart": "Geruest Saeule 1 Zoll",
"teile": [
{
"artikel": "ROHR R 1\" 33,7 x 2,6",
"sivas_id": 821006026,
"menge": 1
},
{
"artikel": "CAD - BODENSÄULE R 1\" - BODENPLATTE KPL.",
"sivas_id": 821004200,
"menge": 1
}
]
},
{ {
"name": "R 2\"", "name": "R 2\"",
"teileart": "Geruest Saeule 2 Zoll",
"teile": [ "teile": [
{ {
"artikel": "BODENSÄULE R 2\" 60,3 MIT FUSSPLATTE", "artikel": "BODENSÄULE R 2\" 60,3 MIT FUSSPLATTE",
@@ -225,6 +232,7 @@
}, },
{ {
"name": "R 2\" Haltearm 1-ARMIG", "name": "R 2\" Haltearm 1-ARMIG",
"teileart": "Geruest Saeule 2 Zoll",
"teile": [ "teile": [
{ {
"artikel": "BODENSÄULE R 2\" 60,3 MIT FUSSPLATTE", "artikel": "BODENSÄULE R 2\" 60,3 MIT FUSSPLATTE",
@@ -245,6 +253,7 @@
}, },
{ {
"name": "R 2\" Haltearm 2-ARMIG 90 Grad", "name": "R 2\" Haltearm 2-ARMIG 90 Grad",
"teileart": "Geruest Saeule 2 Zoll",
"teile": [ "teile": [
{ {
"artikel": "BODENSÄULE R 2\" 60,3 MIT FUSSPLATTE", "artikel": "BODENSÄULE R 2\" 60,3 MIT FUSSPLATTE",
@@ -265,6 +274,7 @@
}, },
{ {
"name": "R 2\" Haltearm 2-ARMIG 180 Grad", "name": "R 2\" Haltearm 2-ARMIG 180 Grad",
"teileart": "Geruest Saeule 2 Zoll",
"teile": [ "teile": [
{ {
"artikel": "BODENSÄULE R 2\" 60,3 MIT FUSSPLATTE", "artikel": "BODENSÄULE R 2\" 60,3 MIT FUSSPLATTE",
@@ -285,6 +295,7 @@
}, },
{ {
"name": "R 2\" Haltearm 3-ARMIG", "name": "R 2\" Haltearm 3-ARMIG",
"teileart": "Geruest Saeule 2 Zoll",
"teile": [ "teile": [
{ {
"artikel": "BODENSÄULE R 2\" 60,3 MIT FUSSPLATTE", "artikel": "BODENSÄULE R 2\" 60,3 MIT FUSSPLATTE",
@@ -340,10 +351,43 @@
"sivas_id": 922083009, "sivas_id": 922083009,
"menge": 2 "menge": 2
} }
],
"konfigurationen": [
{
"name": "Bodensaeule 1 Zoll, Standardhoehe",
"variante": "R 1\"",
"laenge": 1000,
"teilefarbe": "RAL 7035 Lichtgrau",
"oben": [],
"unten": []
},
{
"name": "Bodensaeule 2 Zoll, Haltearm 1-armig",
"variante": "R 2\" Haltearm 1-ARMIG",
"laenge": 1200,
"teilefarbe": "RAL 7035 Lichtgrau",
"oben": [
{ "artikel": "KLEMMRING MIT MUFFE EINFACH KPL.", "menge": 2 }
],
"unten": []
},
{
"name": "Bodensaeule 2 Zoll, Haltearm 2-armig 90 Grad",
"variante": "R 2\" Haltearm 2-ARMIG 90 Grad",
"laenge": 1200,
"teilefarbe": "RAL 7035 Lichtgrau",
"oben": [
{ "artikel": "KLEMMRING MIT MUFFE DOPPELT 90 Grad, KPL.", "menge": 2 }
],
"unten": [
{ "artikel": "QUERSCHELLE F. 1\" UND 2\" ROHR GESCHW./VZ. M. 4X M10X30 KPL.", "menge": 2 }
]
}
] ]
}, },
"Wandbefestigung": { "Wandbefestigung": {
"id": "Wandbefestigung", "id": "Wandbefestigung",
"titel": "Wandbefestigung",
"teileart": "Geruest Wandbefestigung", "teileart": "Geruest Wandbefestigung",
"dummy": "Rohrabhaenger_dummy.dwg", "dummy": "Rohrabhaenger_dummy.dwg",
"geometrie_varianten": [ "geometrie_varianten": [
@@ -414,6 +458,24 @@
} }
] ]
} }
],
"konfigurationen": [
{
"name": "Wandflansch mit Duebeln",
"variante": "WANDFLANSCH EINZELTEIL",
"oben": [
{ "artikel": "Nylondübel 12x60 inkl. Holzschrauben", "menge": 1 }
],
"unten": []
},
{
"name": "Winkel 300 mit Schwerlastanker",
"variante": "WINKEL 60X60X6X300",
"oben": [
{ "artikel": "HST M12X115X20", "menge": 1 }
],
"unten": []
}
] ]
} }
} }
-61
View File
@@ -1,61 +0,0 @@
// ============================================================
// geruest.dcl - Konfiguration einer Geruest-Baugruppe
// Ein Dialog fuer ALLE Baugruppen (Rohrabhaenger, Saeulen, Wandbefestigung).
// Kopfzeile, Varianten-Dropdown, Farbliste und Anbauteil-Liste werden zur
// Laufzeit aus data/json/geruest.json befuellt - siehe
// Lisp/GeruestInsert.lsp, Abschnitt "DIALOG".
// Baugruppen ohne "attribute"-Block (z.B. Wandbefestigung) bekommen die
// Laengen- und Farbfelder per mode_tile gesperrt.
// ============================================================
geruest : dialog {
label = "Geruest-Baugruppe konfigurieren";
: text {
key = "kopf";
label = "";
width = 64;
}
: boxed_column {
label = "Basis";
: popup_list {
key = "variante";
label = "Variante:";
width = 56;
}
: row {
: edit_box {
key = "laenge";
label = "Laenge (mm):";
edit_width = 10;
}
: popup_list {
key = "farbe";
label = "Farbe:";
width = 18;
}
: edit_box {
key = "farbe_frei";
label = "eigene:";
edit_width = 12;
}
}
}
: boxed_column {
label = "Anbauteile (Mehrfachauswahl mit Strg)";
: list_box {
key = "anbau";
label = "";
multiple_select = true;
width = 64;
height = 12;
}
}
ok_cancel;
}
+122
View File
@@ -0,0 +1,122 @@
# TODO: Modus-2-Solver auf achsenweise Laengen-Bilanz umstellen
Status: **Vorschlag / noch nicht umgesetzt** (2026-09-18). Mit dem Entwickler zu
besprechen, bevor der Solver angefasst wird. Reiner Plan - kein Code geaendert.
## Kurzfassung
Der Modus-2-Solver (`vf-linienzug-modus2`, Phase B, `Lisp/vf_linienzug.lsp`
ca. Zeile 9915-10145) modelliert den VarioFoerderer-Lauf als EINE gerade
Bruecke. Bei einer gefalteten Kette (U-Form: hoch, quer, runter) zieht er die
festen Fussabdruecke aus der falschen Achse ab: **die erste GF wird zu lang,
die Kletterstrecke zu kurz, Motor/ES landen falsch bzw. fehlen.** Der Fix:
Laengen pro Schenkelrichtung bilanzieren statt als eine skalare Spanne.
Das ist ein **dokumentiertes v1-Naeherungs-Limit**, kein Regressionsfehler -
die Umbauten SEGS/Alert-Box/Mini-Filter haben Phase B nicht angefasst.
## Symptom (echter Testfall: results/test_modus2.dxf, logs/vfl_modus2.dbg)
U-Form-Kette, 6 Segmente, EIN VF-Lauf (Seg 2-6; die zwei Vario-Kurven zaehlen
als VF-Mitglieder):
```
^ Seg2 VF (1500 mm)
| Seg1 GF (2500 mm)
start Seg4 VF (2257 mm)
| +-----------------------+
| Seg3 Kurve Seg5 Kurve
| |
| Seg6 VF (3800 mm)
+ v
Ziel (3520, 180)
```
Ergebnis: `SOLL-ENDE=(3520, 180, 2000)`, `IST-ENDE=(3333, 2825, 2080)` ->
`dY=+2645 mm`, `dZ=+80 mm`. Die erste GF wird zu lang erzeugt; die
Kletterstrecke (Seg 6) endet ~2645 mm zu frueh; Motorstation, das
VF-Kurven-Zwischenstueck und das ES-Element sitzen falsch bzw. fehlen.
## Ursache, praezise lokalisiert
Der Solver waehlt die laengste VF-Gerade (Seg 6, 3800) als Kletterer, der die
ganze Hoehe traegt, und rechnet die geneigte Laenge skalar:
```lisp
;; vf_linienzug.lsp:9990 - Solver bekommt die SKALARE Spanne
(setq wahl3 (vfl3-waehle-winkel climber-span dH-adj feste-vf))
;; feste-vf = Umlenk + Separator (+ Motor) -- ein fester Fussabdruck
;; vf_linienzug.lsp:10070 - Kletterer bekommt br-lvf proportional
(setq this-lvf (max clamp (* br-lvf (/ (caddr seg) climber-span))))
;; br-lvf = climber-span MINUS feste-vf
;; ABER: Umlenk/GF1/Motor sitzen physisch in ANDEREN Schenkeln
;; (GF1+Umlenk am Eingang entlang hz=90, nicht in Seg6/hz=-90)
```
**Der Modellbruch:** `br-lvf` = `climber-span` minus die festen Fussabdruecke
(Umlenk 500 + Separator 300 + GF1 `gf-total` + Motor). Diese Teile liegen aber
in den ANDEREN U-Schenkeln - nicht in Seg 6. Der Solver zieht sie trotzdem von
Seg 6s 3800er-Spanne ab. Weil GF1 = "alles am Einlauf" gewaehlt war, ist
`gf-total` gross -> Seg 6 wird um genau diese Summe (~2645 mm) zu kurz. Bei
einer GERADEN Bruecke liegen alle Teile auf derselben Achse - dann stimmt die
skalare Rechnung. Bei der U-Form nicht.
Zusaetzlich: die flache Zone nimmt GENAU EIN Uebergangspaar an
(`hor-pairs = 1`, Zeile 9950), obwohl die U-Form zwei 90-Grad-Kurven plus
Kletterstrecke mischt -> daraus die Rest-dZ von +80 mm.
## Beleg: dokumentiertes Limit, kein Regressionsfehler
| Kommentar-Fundstelle | Aussage |
|---|---|
| `vf_linienzug.lsp:9073-9075` | "v1-Naeherung Laenge ... feinere Laengen-Reservierung -> spaeter" |
| `vf_linienzug.lsp:9923-9926` | Modell explizit "EIN Vario mit Horizontal-Mitte" - eine gerade Bruecke ist die Design-Annahme |
| `vf_linienzug.lsp:9948-9950` | "flache Zone hat GENAU EIN Uebergangspaar ... unabhaengig von der Zahl der Fueller/Kurven" |
Verifiziert: SEGS-Umbau, Alert-Box und Mini-Filter haben Phase B (den Solver)
nachweislich nicht veraendert. Die Abweichung ist vorbestehend.
## Vorgeschlagener Umbau in sicheren Etappen
Nach jeder Etappe eine BricsCAD-Sichtpruefung (Geometrie ist nur interaktiv
pruefbar).
1. **Regressionsnetz zuerst.** Eine bekannt-gute GERADE Modus-2-Kette
(GF...VF...GF, eine Achse) bauen und mit `lib/dxf_vf_abbild.py
--schreibe-referenz` als Referenz einfrieren. Schuetzt den funktionierenden
Normalfall, bevor die Bilanz umgebaut wird.
2. **Schenkel erkennen.** Den VF-Lauf (Seg `vf-start`..`vf-ende`) an den
Vario-Kurven in Schenkel aufteilen - jeder Schenkel eine Fahrtrichtung (hz).
Pro Schenkel: Summe der VF-Geraden-Laengen, welche festen Teile darin liegen.
Neue Datenstruktur statt der skalaren `climber-span`. (ersetzt die
climber/nonclimber-Schleife 9935-9947)
3. **Fussabdruecke dem richtigen Schenkel zuordnen** (Risiko-Kern). Umlenk/GF1
nur vom Eingangs-Schenkel abziehen, Motor/GF2/ES nur vom Ausgangs-Schenkel -
nicht pauschal von der Kletterstrecke. Reicht ein Schenkel nicht, verteilt
sich die Restneigung auf weitere Schenkel. (feste-vf/dH-adj 9978-9989,
this-lvf 10070, fill-len 10094-10104)
4. **Uebergangspaare pro Kurve** statt pauschal eins. `hor-pairs` aus der
tatsaechlichen Schenkel-/Kurvenzahl ableiten - behebt die dZ-Restabweichung.
(9950, dH-adj-Bildung 9982-9989)
5. **Verifikation.** Regressionsnetz aus Schritt 1 muss gruen bleiben (gerader
Fall unveraendert). U-Form interaktiv: erste GF korrekt kurz,
Kletterstrecke trifft den Zielpunkt, Motor + Kurven-Zwischenstueck + ES an
der richtigen Stelle. SEGS-Journal bleibt formgleich.
## Risiko-Einschaetzung
- Der GERADE Fall funktioniert heute - er ist der Normalfall, fuer den der
Solver gebaut ist. Ein Umbau der Kern-Bilanz kann ihn brechen -> Schritt 1
(Referenznetz) ist nicht optional.
- Geometrie nur interaktiv in BricsCAD pruefbar -> mehrere Runden noetig.
- Gut eingegrenzt: der Umbau bleibt in Phase B von `vf-linienzug-modus2`
(~9915-10145). Klassifizierung, SEGS-Journal, Doppelklick-Replay und die
anderen Modi bleiben unberuehrt.
## Zwischenloesung (falls der Umbau nicht sofort kommt)
Gefaltete VF-Laeufe (mehr als eine Achsenrichtung im VF-Bereich, z.B. U-Form)
erkennen und mit klarer Meldung abweisen, statt still falsche Geometrie zu
bauen - analog zur bereits vorhandenen "nur EIN VF-Lauf"-Abweisung
(`vfl-m3-vf-lauf-alert`). Macht das Limit ehrlich sichtbar, kleiner Eingriff.
+25 -5
View File
@@ -70,8 +70,8 @@ Bounding-Boxen braucht).
`vla-update` auf, das allein reichte in diesem Fall nicht aus. `vla-update` auf, das allein reichte in diesem Fall nicht aus.
7. **`csv:collect-export-blocks`** — sammelt alle relevanten top-level 7. **`csv:collect-export-blocks`** — sammelt alle relevanten top-level
INSERTs (Kreisel, Omniflo, VF/GF-Wrapper, Separator/Scanner, INSERTs (Kreisel, Omniflo, VF/GF-Wrapper, Separator/Scanner,
BTMT-Stationen) ueber die Blockname-Muster aus `cfg/export.cfg` BTMT-Stationen, Eingang/Ausgang) ueber die Blockname-Muster aus
`[blockpattern]`. `cfg/export.cfg` `[blockpattern]`.
8. **`csv:block-to-json`** je Fund — siehe naechster Abschnitt. 8. **`csv:block-to-json`** je Fund — siehe naechster Abschnitt.
### `csv:block-to-json` — Inhalt eines JSON-Eintrags ### `csv:block-to-json` — Inhalt eines JSON-Eintrags
@@ -277,7 +277,8 @@ Zu beachten:
Eine grosse Schleife über alle rohen JSON-Blöcke ordnet jeden Block anhand Eine grosse Schleife über alle rohen JSON-Blöcke ordnet jeden Block anhand
der `cfg/export.cfg [blockpattern]`-Muster einer TeileArt zu (Kreisel, der `cfg/export.cfg [blockpattern]`-Muster einer TeileArt zu (Kreisel,
Eckrad, Omniflo Bogen/Weiche/Gerade, VF-Strecke, Gefaellestrecke, Eckrad, Omniflo Bogen/Weiche/Gerade, VF-Strecke, Gefaellestrecke,
Strecke-Modul, Separator, Scanner, BTMT Be-/Entladung) und baut daraus ein Strecke-Modul, Separator, Scanner, BTMT Be-/Entladung, Eingang/Ausgang) und
baut daraus ein
Item-dict mit `teileart`, `teileid`, `bezeichnung`, `planquadrat`, Item-dict mit `teileart`, `teileid`, `bezeichnung`, `planquadrat`,
`merkmale` und den bbox-/KOS-Spalten (`bbox_columns`). VF_n/GF_n-Items `merkmale` und den bbox-/KOS-Spalten (`bbox_columns`). VF_n/GF_n-Items
bekommen dabei zusaetzlich `x`/`y` (Kettenanfang, fuer die Schleuselement- bekommen dabei zusaetzlich `x`/`y` (Kettenanfang, fuer die Schleuselement-
@@ -299,6 +300,24 @@ Default 210x150x14mm) und der bekannten Position/Rotation eine synthetische
`_bbox`, damit der Separator trotzdem an der Nachbarschaftserkennung `_bbox`, damit der Separator trotzdem an der Nachbarschaftserkennung
teilnimmt. teilnimmt.
#### Eingang/Ausgang — nur im CSV-Export
Die Materialfluss-Ein-/Ausgangssymbole (Bloecke `Eingang_<DIM>`/`Ausgang_<DIM>`,
eingefuegt ueber `ils-insert-station`, Befehle `ILS_EINGANG_INSERT`/
`ILS_AUSGANG_INSERT`) werden **nur im CSV-Export** als eigene Zeile gefuehrt
(TeileArt `"Eingang"`/`"Ausgang"`, Merkmale wie BTMT: ARTINR/Hoehe/Drehung).
`export_sivas.py` ueberspringt sie bewusst — ein Fluss-Symbol hat keinen
eigenen Sivas-Artikel.
Wie Separator/AS/ES nehmen sie als **freie Umlauf-Punkte**
(`KREISEL_UMLAUF_TEILEARTEN`) an der `"TrackIds"`-Spalte teil und tauchen so
auch auf einem Kreisel auf, auf dessen Bahn sie liegen. Voraussetzung fuer
eine ueber Exportlaeufe stabile TeileId ist ein `ID`-ATTDEF in den DWGs
(nachgetragen, dokumentiert in `data/ils/allocations.json`); `ssg-id-check-all`
fuellt es vor jedem Export, und `ssg-id-collect-blocks` (`Lisp/ssg_id.lsp`)
erfasst die Bloecke dafuer. Fehlt die ID (noch), greift wie bei BTMT eine
lauf-lokale Fallback-Nummer (`EIN-`/`AUS-`-Praefix).
### 2. `compute_neighbor_ids` — die generische Bounding-Box-Nachbarschaft ### 2. `compute_neighbor_ids` — die generische Bounding-Box-Nachbarschaft
Datei `lib/export_neighbors.py`. Testet drei Gruppen gegeneinander (NICHT Datei `lib/export_neighbors.py`. Testet drei Gruppen gegeneinander (NICHT
@@ -347,8 +366,9 @@ Weichen zu einer widerspruchsfreien Nachbarschaft zusammenfuehrt. Fuer jeden
echten Kreisel werden alle Punkte gesammelt, die dort geometrisch liegen: echten Kreisel werden alle Punkte gesammelt, die dort geometrisch liegen:
- freie Punkte (`KREISEL_UMLAUF_TEILEARTEN` in `lib/export_neighbors.py`: - freie Punkte (`KREISEL_UMLAUF_TEILEARTEN` in `lib/export_neighbors.py`:
Separator, BTMT Beladung, BTMT Entladung — alles Stationen direkt auf der Separator, BTMT Beladung, BTMT Entladung, Eingang, Ausgang — alles
Bahn/dem Track), deren Bounding-Box die Kreisel-Box ueberschneidet, Stationen/Markierungen direkt auf der Bahn/dem Track), deren Bounding-Box
die Kreisel-Box ueberschneidet,
- die Kreisel-Kreisel-Weichen aus Schritt 3 (in BEIDEN beteiligten - die Kreisel-Kreisel-Weichen aus Schritt 3 (in BEIDEN beteiligten
Kreisel-Umlaeufen, da eine Weiche der gemeinsame Verzweigungspunkt ist — Kreisel-Umlaeufen, da eine Weiche der gemeinsame Verzweigungspunkt ist —
man kann von Kreisel A nach B und zurueck), man kann von Kreisel A nach B und zurueck),
+19 -3
View File
@@ -111,6 +111,7 @@
"vfl-glied-vario": "Vario-Kurve (horizontale Kurve im VF)", "vfl-glied-vario": "Vario-Kurve (horizontale Kurve im VF)",
"vfl-glied-offen": "(offen)", "vfl-glied-offen": "(offen)",
"vfl-edit-kein-journal": "Dieser Linienzug-Block traegt kein Eingabe-Journal (Altbestand) und kann nicht schrittweise editiert werden.", "vfl-edit-kein-journal": "Dieser Linienzug-Block traegt kein Eingabe-Journal (Altbestand) und kann nicht schrittweise editiert werden.",
"vfl-edit-m2-obsolet": "Dieser Modus-2-Block wurde mit dem alten Objekt-Referenz-Journal gebaut und kann nicht mehr per Doppelklick neu aufgebaut werden. Bitte die Kette mit Modus 2 neu erzeugen (die neue Fassung speichert die Segment-Daten direkt am Block).",
"vfl-edit-kette-header": "\n\nLinienzug mit %1 Glied(ern):", "vfl-edit-kette-header": "\n\nLinienzug mit %1 Glied(ern):",
"vfl-edit-abgespielt": "\n>>> %1 Glied(er) werden abgespielt, %2 zurueckgenommen - danach interaktiv weiter.", "vfl-edit-abgespielt": "\n>>> %1 Glied(er) werden abgespielt, %2 zurueckgenommen - danach interaktiv weiter.",
"vfl-edit2-reset-info": "\n>>> Linienzug (Modus 2) wird mit den gespeicherten Angaben identisch neu aufgebaut...", "vfl-edit2-reset-info": "\n>>> Linienzug (Modus 2) wird mit den gespeicherten Angaben identisch neu aufgebaut...",
@@ -183,6 +184,8 @@
"vfl-m3-vf-laeufe": "\n\n[M3a] Gefundene VF-Laeufe: %1", "vfl-m3-vf-laeufe": "\n\n[M3a] Gefundene VF-Laeufe: %1",
"vfl-m3-vf-lauf-noetig": "\n[M3a] Genau EIN VF-Lauf noetig (GF...VF...GF; Vario-Kurven duerfen im Lauf liegen).", "vfl-m3-vf-lauf-noetig": "\n[M3a] Genau EIN VF-Lauf noetig (GF...VF...GF; Vario-Kurven duerfen im Lauf liegen).",
"vfl-m3-vf-lauf-m3b": "\n[M3a] Mehrere/keine VF-Laeufe -> M3b spaeter.", "vfl-m3-vf-lauf-m3b": "\n[M3a] Mehrere/keine VF-Laeufe -> M3b spaeter.",
"vfl-m3-vf-lauf-alert": "VF-Linienzug Modus 2 kann diese Kette nicht bauen.\n\nGefunden: %1 VarioFoerderer-Laeufe.\nModus 2 baut zurzeit nur Ketten mit GENAU EINEM VF-Lauf (GF...VF...GF; Vario-Kurven duerfen innerhalb des Laufs liegen).\n\nBitte den Pfad so aufteilen, dass nur ein zusammenhaengender VF-Bereich entsteht.",
"vfl-m3-mini-objekte-verworfen": "%1 zu kurze(s) Objekt(e) wurden aus der Auswahl ausgeschlossen (unter %2 mm 2D-Laenge).\n\nSolche Mini-Segmente sind meist Geister-/Artefakt-Elemente, die eine Rechteck-Auswahl versehentlich miterfasst - ein reales Bauteil (kleinstes: Separator 300 mm) ist nie so kurz. Sie werden ignoriert, damit sie keine Geister-Bausteine in die Kette bringen.",
"vfl-m3-vf-lauf-ohne-gerade": "\n[M3a] VF-Lauf ohne VF-Gerade - Abbruch.", "vfl-m3-vf-lauf-ohne-gerade": "\n[M3a] VF-Lauf ohne VF-Gerade - Abbruch.",
"vfl-m3-anker-vorschau-header": "\n M3a ANKER-VORSCHAU (grob geschaetzt; exakte Werte beim Bau)", "vfl-m3-anker-vorschau-header": "\n M3a ANKER-VORSCHAU (grob geschaetzt; exakte Werte beim Bau)",
"vfl-m3-start-hoehe": "\n Start-Hoehe Z: %1 mm", "vfl-m3-start-hoehe": "\n Start-Hoehe Z: %1 mm",
@@ -350,7 +353,6 @@
"vfe-gefaellebogen-links-mass": "\n Gefaellebogen links: dx=%1 dz=%2 mm", "vfe-gefaellebogen-links-mass": "\n Gefaellebogen links: dx=%1 dz=%2 mm",
"vfe-gefaellebogen-rechts-mass": "\n Gefaellebogen rechts: dx=%1 dz=%2 mm", "vfe-gefaellebogen-rechts-mass": "\n Gefaellebogen rechts: dx=%1 dz=%2 mm",
"vfe-gefaellebogen-seite-dx": "\n Gefaellebogen %1: dx=%2 mm", "vfe-gefaellebogen-seite-dx": "\n Gefaellebogen %1: dx=%2 mm",
"vfe-hz-incl-status": "\n HzIncl %1 L=%2 hz=%3 v=%4 -> Z=%5",
"vfe-hz-rot-status": "\n HzRot %1 hz=%2 v=%3 -> Z=%4", "vfe-hz-rot-status": "\n HzRot %1 hz=%2 v=%3 -> Z=%4",
"vfe-hz-rotks-status": "\n HzRotKS %1 hz=%2 -> Z=%3", "vfe-hz-rotks-status": "\n HzRotKS %1 hz=%2 -> Z=%3",
"vfe-hz-scaleks-status": "\n HzScaleKS %1 %2mm hz=%3 -> Z=%4", "vfe-hz-scaleks-status": "\n HzScaleKS %1 %2mm hz=%3 -> Z=%4",
@@ -968,7 +970,7 @@
"vfl-vwnb-typ-gerade": "\n Typ? 1 = GF (<=3 Grad) 2 = VF-Ab 3 = VF-Auf 4 = VF-Horizontal", "vfl-vwnb-typ-gerade": "\n Typ? 1 = GF (<=3 Grad) 2 = VF-Ab 3 = VF-Auf 4 = VF-Horizontal",
"vfl-vwnb-prompt-vario-winkel": "\n Vario-Winkel (3..51 Grad) [3]: ", "vfl-vwnb-prompt-vario-winkel": "\n Vario-Winkel (3..51 Grad) [3]: ",
"vfl-vwnb-kurve-nur-vf": "\n >>> Vario-Kurve nur innerhalb eines VF-Laufs zulaessig - uebersprungen.", "vfl-vwnb-kurve-nur-vf": "\n >>> Vario-Kurve nur innerhalb eines VF-Laufs zulaessig - uebersprungen.",
"vfl-vwnb-eingefuegt": "\n>>> VF-Linienzug (Modus 2, GF+VF) eingefuegt! <<<", "vfl-vwnb-eingefuegt": "\n>>> VF-Linienzug (Modus 3, GF+VF) eingefuegt! <<<",
"vfl-vwnb-soll-ende": "\n Soll-Ende (Pfad): X=%1 Y=%2", "vfl-vwnb-soll-ende": "\n Soll-Ende (Pfad): X=%1 Y=%2",
"vfl-vwnb-ist-ende": "\n Ist-Ende (ES): X=%1 Y=%2 Z=%3", "vfl-vwnb-ist-ende": "\n Ist-Ende (ES): X=%1 Y=%2 Z=%3",
"vfl-vwnb-abweichung-xy": "\n Abweichung XY: dX=%1 dY=%2 mm", "vfl-vwnb-abweichung-xy": "\n Abweichung XY: dX=%1 dY=%2 mm",
@@ -988,6 +990,20 @@
"gr-alert-laenge": "Laenge ungueltig: '%1' mm. Erlaubt: %2 bis %3 mm.", "gr-alert-laenge": "Laenge ungueltig: '%1' mm. Erlaubt: %2 bis %3 mm.",
"gr-prompt-einfuegepunkt": "\nEinfuegepunkt waehlen: ", "gr-prompt-einfuegepunkt": "\nEinfuegepunkt waehlen: ",
"gr-abgebrochen": "\nAbgebrochen.", "gr-abgebrochen": "\nAbgebrochen.",
"gr-fertig": "\nGeruest-Baugruppe '%1' eingefuegt (Block %2)." "gr-fertig": "\nGeruest-Baugruppe '%1' eingefuegt (Block %2).",
"gr-alert-menge": "Ungueltige Stueckzahl. Erlaubt ist eine ganze Zahl ab 1; ein leeres Feld uebernimmt den Vorgabewert.",
"gr-position-oben": "oben",
"gr-position-unten": "unten",
"gr-konfig-keine": "<keine>",
"gr-konfig-name": "\nName der Konfiguration: ",
"gr-konfig-gespeichert": "\nKonfiguration '%1' gespeichert.",
"gr-konfig-geloescht": "\nKonfiguration '%1' geloescht.",
"gr-konfig-loeschen-frage": "\nKonfiguration '%1' wirklich loeschen? [Ja/Nein] <Nein>: ",
"gr-konfig-nichts-gewaehlt": "\nKeine Konfiguration gewaehlt.",
"gr-konfig-bibliothek-fest": "\nKonfigurationen der Bibliothek koennen nicht geloescht werden - nur eigene (mit * markiert).",
"gr-konfig-teil-unbekannt": "\nGeruest: Anbauteil '%1' aus Konfiguration '%2' ist bei dieser Baugruppe unbekannt - uebersprungen.",
"gr-konfig-variante-unbekannt": "\nGeruest: Variante '%1' aus Konfiguration '%2' ist bei dieser Baugruppe unbekannt - Auswahl unveraendert.",
"gr-fehler-schreiben": "\nGeruest: Datei konnte nicht geschrieben werden: %1",
"gr-fehler-keine-baugruppen": "\nGeruest: Die Datendatei enthaelt keine Baugruppen."
} }
} }
+19 -3
View File
@@ -111,6 +111,7 @@
"vfl-glied-vario": "Vario curve (horizontal curve in VF)", "vfl-glied-vario": "Vario curve (horizontal curve in VF)",
"vfl-glied-offen": "(open)", "vfl-glied-offen": "(open)",
"vfl-edit-kein-journal": "This chain block carries no input journal (legacy) and cannot be edited step by step.", "vfl-edit-kein-journal": "This chain block carries no input journal (legacy) and cannot be edited step by step.",
"vfl-edit-m2-obsolet": "This mode-2 block was built with the old object-reference journal and can no longer be rebuilt by double-click. Please recreate the chain with mode 2 (the new version stores the segment data directly on the block).",
"vfl-edit-kette-header": "\n\nChain with %1 segment(s):", "vfl-edit-kette-header": "\n\nChain with %1 segment(s):",
"vfl-edit-abgespielt": "\n>>> %1 segment(s) replayed, %2 undone - then continue interactively.", "vfl-edit-abgespielt": "\n>>> %1 segment(s) replayed, %2 undone - then continue interactively.",
"vfl-edit2-reset-info": "\n>>> Rebuilding this Modus-2 chain identically from the saved inputs...", "vfl-edit2-reset-info": "\n>>> Rebuilding this Modus-2 chain identically from the saved inputs...",
@@ -183,6 +184,8 @@
"vfl-m3-vf-laeufe": "\n\n[M3a] VF runs found: %1", "vfl-m3-vf-laeufe": "\n\n[M3a] VF runs found: %1",
"vfl-m3-vf-lauf-noetig": "\n[M3a] Exactly ONE VF run required (GF...VF...GF; Vario curves may lie within the run).", "vfl-m3-vf-lauf-noetig": "\n[M3a] Exactly ONE VF run required (GF...VF...GF; Vario curves may lie within the run).",
"vfl-m3-vf-lauf-m3b": "\n[M3a] Several/no VF runs -> M3b later.", "vfl-m3-vf-lauf-m3b": "\n[M3a] Several/no VF runs -> M3b later.",
"vfl-m3-vf-lauf-alert": "VF line path mode 2 cannot build this chain.\n\nFound: %1 VarioFoerderer runs.\nMode 2 currently builds only chains with EXACTLY ONE VF run (GF...VF...GF; Vario curves may lie within the run).\n\nPlease split the path so that only one contiguous VF section remains.",
"vfl-m3-mini-objekte-verworfen": "%1 object(s) too short were excluded from the selection (below %2 mm 2D length).\n\nSuch mini segments are usually ghost/artefact elements a window selection caught by accident - a real part (smallest: separator 300 mm) is never that short. They are ignored so they cannot introduce ghost parts into the chain.",
"vfl-m3-vf-lauf-ohne-gerade": "\n[M3a] VF run without a VF straight - aborting.", "vfl-m3-vf-lauf-ohne-gerade": "\n[M3a] VF run without a VF straight - aborting.",
"vfl-m3-anker-vorschau-header": "\n M3a ANCHOR PREVIEW (rough estimate; exact values during build)", "vfl-m3-anker-vorschau-header": "\n M3a ANCHOR PREVIEW (rough estimate; exact values during build)",
"vfl-m3-start-hoehe": "\n Start height Z: %1 mm", "vfl-m3-start-hoehe": "\n Start height Z: %1 mm",
@@ -350,7 +353,6 @@
"vfe-gefaellebogen-links-mass": "\n Slope arc left: dx=%1 dz=%2 mm", "vfe-gefaellebogen-links-mass": "\n Slope arc left: dx=%1 dz=%2 mm",
"vfe-gefaellebogen-rechts-mass": "\n Slope arc right: dx=%1 dz=%2 mm", "vfe-gefaellebogen-rechts-mass": "\n Slope arc right: dx=%1 dz=%2 mm",
"vfe-gefaellebogen-seite-dx": "\n Slope arc %1: dx=%2 mm", "vfe-gefaellebogen-seite-dx": "\n Slope arc %1: dx=%2 mm",
"vfe-hz-incl-status": "\n HzIncl %1 L=%2 hz=%3 v=%4 -> Z=%5",
"vfe-hz-rot-status": "\n HzRot %1 hz=%2 v=%3 -> Z=%4", "vfe-hz-rot-status": "\n HzRot %1 hz=%2 v=%3 -> Z=%4",
"vfe-hz-rotks-status": "\n HzRotKS %1 hz=%2 -> Z=%3", "vfe-hz-rotks-status": "\n HzRotKS %1 hz=%2 -> Z=%3",
"vfe-hz-scaleks-status": "\n HzScaleKS %1 %2mm hz=%3 -> Z=%4", "vfe-hz-scaleks-status": "\n HzScaleKS %1 %2mm hz=%3 -> Z=%4",
@@ -968,7 +970,7 @@
"vfl-vwnb-typ-gerade": "\n Type? 1 = GF (<=3 deg) 2 = VF-down 3 = VF-up 4 = VF-horizontal", "vfl-vwnb-typ-gerade": "\n Type? 1 = GF (<=3 deg) 2 = VF-down 3 = VF-up 4 = VF-horizontal",
"vfl-vwnb-prompt-vario-winkel": "\n Vario angle (3..51 deg) [3]: ", "vfl-vwnb-prompt-vario-winkel": "\n Vario angle (3..51 deg) [3]: ",
"vfl-vwnb-kurve-nur-vf": "\n >>> Vario curve only allowed within a VF run - skipped.", "vfl-vwnb-kurve-nur-vf": "\n >>> Vario curve only allowed within a VF run - skipped.",
"vfl-vwnb-eingefuegt": "\n>>> VF line path (mode 2, GF+VF) inserted! <<<", "vfl-vwnb-eingefuegt": "\n>>> VF line path (mode 3, GF+VF) inserted! <<<",
"vfl-vwnb-soll-ende": "\n Target end (path): X=%1 Y=%2", "vfl-vwnb-soll-ende": "\n Target end (path): X=%1 Y=%2",
"vfl-vwnb-ist-ende": "\n Actual end (ES): X=%1 Y=%2 Z=%3", "vfl-vwnb-ist-ende": "\n Actual end (ES): X=%1 Y=%2 Z=%3",
"vfl-vwnb-abweichung-xy": "\n Deviation XY: dX=%1 dY=%2 mm", "vfl-vwnb-abweichung-xy": "\n Deviation XY: dX=%1 dY=%2 mm",
@@ -988,6 +990,20 @@
"gr-alert-laenge": "Invalid length: '%1' mm. Allowed: %2 to %3 mm.", "gr-alert-laenge": "Invalid length: '%1' mm. Allowed: %2 to %3 mm.",
"gr-prompt-einfuegepunkt": "\nSelect insertion point: ", "gr-prompt-einfuegepunkt": "\nSelect insertion point: ",
"gr-abgebrochen": "\nCancelled.", "gr-abgebrochen": "\nCancelled.",
"gr-fertig": "\nScaffold assembly '%1' inserted (block %2)." "gr-fertig": "\nScaffold assembly '%1' inserted (block %2).",
"gr-alert-menge": "Invalid quantity. Enter a whole number of 1 or more; an empty field uses the default.",
"gr-position-oben": "top",
"gr-position-unten": "bottom",
"gr-konfig-keine": "<none>",
"gr-konfig-name": "\nConfiguration name: ",
"gr-konfig-gespeichert": "\nConfiguration '%1' saved.",
"gr-konfig-geloescht": "\nConfiguration '%1' deleted.",
"gr-konfig-loeschen-frage": "\nReally delete configuration '%1'? [Ja/Nein] <Nein>: ",
"gr-konfig-nichts-gewaehlt": "\nNo configuration selected.",
"gr-konfig-bibliothek-fest": "\nLibrary configurations cannot be deleted - only your own (marked with *).",
"gr-konfig-teil-unbekannt": "\nScaffold: add-on part '%1' from configuration '%2' is unknown for this assembly - skipped.",
"gr-konfig-variante-unbekannt": "\nScaffold: variant '%1' from configuration '%2' is unknown for this assembly - selection unchanged.",
"gr-fehler-schreiben": "\nScaffold: could not write file: %1",
"gr-fehler-keine-baugruppen": "\nScaffold: the data file contains no assemblies."
} }
} }
+40
View File
@@ -886,6 +886,8 @@ def process_blocks(blocks, lookup, dbg=None):
scanner_count = 0 scanner_count = 0
btmt_beladung_count = 0 btmt_beladung_count = 0
btmt_entladung_count = 0 btmt_entladung_count = 0
eingang_count = 0
ausgang_count = 0
geruest_count = 0 geruest_count = 0
# Zaehlung fuer die Omniflo-Sum-Zeile (nur Bogen/Weiche/Gerade) # Zaehlung fuer die Omniflo-Sum-Zeile (nur Bogen/Weiche/Gerade)
@@ -1159,6 +1161,44 @@ def process_blocks(blocks, lookup, dbg=None):
}) })
continue continue
# Eingangssymbol (Materialfluss-Eingang der Anlage) - eigene
# TeileArt-Zeile, analog zu BTMT. Wie BTMT von keinem Carrier-Attribut
# mitgezaehlt; anders als BTMT NUR im CSV-Export (export_sivas.py
# ueberspringt Eingang/Ausgang). Die TeileId kommt aus dem ID-Attribut
# (ssg-id-check-all fuellt es vor jedem Export, ID-ATTDEF seit dem
# allocations.json-Nachtrag) - dieselbe TeileId, die die
# "TrackIds"-Spalte an Kreisel/Strecke referenziert (KREISEL_UMLAUF_
# TEILEARTEN in export_neighbors.py). Fallback-Nummer wie bei BTMT.
if matches_any(bname, BLOCKPATTERNS.get("pattern_eingang", [])):
elem_nr += 1
eingang_count += 1
items.append({
"nr": elem_nr,
"teileart": "Eingang",
"teileid": block.get("attribs", {}).get("ID") or format_sensor_id("EIN", eingang_count),
"bezeichnung": f"Eingang :{eingang_count}",
"planquadrat": planquadrat_column(block, planquadrat_cfg),
"merkmale": build_btmt_merkmale(block),
**bbox_columns(block),
})
continue
# Ausgangssymbol (Materialfluss-Ausgang der Anlage) - analog zum
# Eingangssymbol oben.
if matches_any(bname, BLOCKPATTERNS.get("pattern_ausgang", [])):
elem_nr += 1
ausgang_count += 1
items.append({
"nr": elem_nr,
"teileart": "Ausgang",
"teileid": block.get("attribs", {}).get("ID") or format_sensor_id("AUS", ausgang_count),
"bezeichnung": f"Ausgang :{ausgang_count}",
"planquadrat": planquadrat_column(block, planquadrat_cfg),
"merkmale": build_btmt_merkmale(block),
**bbox_columns(block),
})
continue
# Geruest-Baugruppe (GR_<id>_<timestamp>) - eigene TeileArt-Zeile, # Geruest-Baugruppe (GR_<id>_<timestamp>) - eigene TeileArt-Zeile,
# analog zu BTMT: kein ANZAHL_*-Attribut an einem Carrier zaehlt sie # analog zu BTMT: kein ANZAHL_*-Attribut an einem Carrier zaehlt sie
# mit. TeileId ist das echte ID-Attribut (ssg-id-check-all fuellt es # mit. TeileId ist das echte ID-Attribut (ssg-id-check-all fuellt es
+60 -15
View File
@@ -84,8 +84,15 @@ SENSOR_TEILEARTEN = {"ILS 2.0 Separator", "ILS 2.0 Scanner"}
# Sensoren - eigene Konstante statt Erweiterung von SENSOR_TEILEARTEN, damit # Sensoren - eigene Konstante statt Erweiterung von SENSOR_TEILEARTEN, damit
# deren Sensor-spezifische Weiterverarbeitung (z.B. compute_scanner_nearest_ # deren Sensor-spezifische Weiterverarbeitung (z.B. compute_scanner_nearest_
# separator) unberuehrt bleibt. # separator) unberuehrt bleibt.
# Eingang/Ausgang (Materialfluss-Ein-/Ausgangssymbole, siehe pattern_eingang/
# pattern_ausgang in cfg/export.cfg) werden hier wie BTMT als frei platzierte
# Umlauf-Punkte gefuehrt - so tauchen sie, wenn sie auf einer Kreiselbahn
# liegen, in dessen "TrackIds"-Spalte auf (Nutzeranforderung: wie
# Separator/AS/ES behandeln). Die TeileArt-Strings sind bewusst OHNE "ILS 2.0"-
# Praefix (siehe export_csv.py) - dieselben Strings muessen hier stehen.
KREISEL_UMLAUF_TEILEARTEN = { KREISEL_UMLAUF_TEILEARTEN = {
"ILS 2.0 Separator", "ILS 2.0 BTMT Beladung", "ILS 2.0 BTMT Entladung", "ILS Weiche", "ILS 2.0 Separator", "ILS 2.0 BTMT Beladung", "ILS 2.0 BTMT Entladung", "ILS Weiche",
"Eingang", "Ausgang",
} }
# Nur "ILS 2.0 Kreisel" (echte Kreisel-Bloecke mit AN8/Antrieb + SP8/Spann- # Nur "ILS 2.0 Kreisel" (echte Kreisel-Bloecke mit AN8/Antrieb + SP8/Spann-
@@ -1514,26 +1521,64 @@ def compute_kreisel_umlauf(items, sep_kette, tolerance_mm, durchmesser_mm=800.0,
acx = (cap["a"][0] + cap["b"][0]) / 2.0 acx = (cap["a"][0] + cap["b"][0]) / 2.0
acy = (cap["a"][1] + cap["b"][1]) / 2.0 acy = (cap["a"][1] + cap["b"][1]) / 2.0
achs_winkel = math.atan2(cap["b"][1] - cap["a"][1], cap["b"][0] - cap["a"][0]) achs_winkel = math.atan2(cap["b"][1] - cap["a"][1], cap["b"][0] - cap["a"][0])
cos_achs, sin_achs = math.cos(achs_winkel), math.sin(achs_winkel)
# Halbe Achslaenge (Mitte -> a bzw. b) und Radius der Kapsel = die
# beiden Halbachsen der Kreisel-BB im lokalen (gedrehten) Koordinaten-
# system - siehe kreisel_bounds oben, dieselbe "maximale Ausdehnung".
halblaenge = math.hypot(cap["b"][0] - cap["a"][0], cap["b"][1] - cap["a"][1]) / 2.0
radius = cap["r"]
uzs = (drehrichtung or "UZS").strip().upper() != "GUZ" uzs = (drehrichtung or "UZS").strip().upper() != "GUZ"
def _winkel(pxy): def _winkel(pxy):
# Rohwinkel relativ zur Achsrichtung, auf [0, 2*pi) normiert IN # Abfolge ueber die BB des Kreisels statt ueber einen Winkel um den
# UMLAUFRICHTUNG. Vorher (reine Vorgaenger-/Nachfolger-Ermittlung # Mittelpunkt: Punkt zuerst ins lokale (um achs_winkel gedrehte)
# ohne Startpunkt-Anspruch) wurde UZS durch reverse=True auf dem # Koordinatensystem der Kreiselachse bringen (lx = Achsrichtung,
# rohen mathematischen Winkel erreicht (= "absteigend sortieren"); # ly = quer dazu), dann auf die BB-Halbachsen normieren
# absteigend sortieren auf theta ist aequivalent zu aufsteigend # (u = lx/halblaenge, v = ly/radius). Je nachdem, welche der beiden
# sortieren auf -theta - darum wird HIER fuer UZS negiert (NICHT # Groessen (u oder v) betragsmaessig ueberwiegt, liegt der Punkt auf
# fuer GUZ, das dem unveraenderten mathematischen Winkel bereits # der rechten/linken Stirnseite (u dominiert) oder der oberen/
# aufsteigend entspricht). Ohne dieses Vorzeichen liefe der UZS- # unteren Langseite (v dominiert) der BB - dort bestimmt NUR die
# Umlauf in die falsche (zu GUZ spiegelverkehrte) Richtung - siehe # jeweils andere (die entlang der Seite laufende) Koordinate die
# Position, der ueberwiegende Anteil legt nur noch die Seite fest.
# Ein grosser, asymmetrischer Block (z.B. BTMT-Beladung/-Entladung)
# hat einen Bbox-Mittelpunkt, der deutlich weiter quer zur Achse
# absteht als der Belt-Radius - beim reinen Winkel-um-Mittelpunkt
# (frueherer Ansatz) blies das seinen Winkel unverhaeltnismaessig
# auf und liess ihn mehrere eng benachbarte Punkte in der engen
# Sortierung ueberholen, obwohl er entlang der Achse klar der
# letzte ist (realer Bug-Fall: HundM05, BTMT-Entladung "0023").
# Hier wirkt sich dieselbe Ueberhoehung nicht mehr aus, solange sie
# nicht die Seiten-Zuordnung selbst umkippt.
#
# t liegt in [0,4) und laeuft GEGEN den Uhrzeigersinn (mathematische
# Standardrichtung) einmal um die BB, beginnend bei t=0 auf der
# rechten Stirnseite in Achsrichtung (lx=halblaenge, ly=0) - das
# ist derselbe Nullpunkt wie beim frueheren "0 Grad = Achsrichtung".
# 0..1 rechte Stirnseite (ly steigend), 1..2 obere Langseite (lx
# fallend), 2..3 linke Stirnseite (ly fallend), 3..4 untere
# Langseite (lx steigend).
dx0 = pxy[1] - acx
dy0 = pxy[2] - acy
lx = dx0 * cos_achs + dy0 * sin_achs
ly = -dx0 * sin_achs + dy0 * cos_achs
u = lx / halblaenge if halblaenge > 1e-9 else 0.0
v = ly / radius if radius > 1e-9 else 0.0
if u >= abs(v):
t = v / 2.0
elif v >= abs(u):
t = 1.0 - u / 2.0
elif u <= -abs(v):
t = 2.0 - v / 2.0
else:
t = 3.0 + u / 2.0
# UZS (Uhrzeigersinn, Default) negiert t (= absteigend sortieren
# auf t ist aequivalent zu aufsteigend auf -t) - identisches Muster
# wie beim vorherigen Winkel-Ansatz, siehe
# tests/test_export_kreisel_umlauf.py::TestKreiselUmlaufStartetBeiNullGrad. # tests/test_export_kreisel_umlauf.py::TestKreiselUmlaufStartetBeiNullGrad.
# Die Normierung auf [0, 2*pi) sorgt zusaetzlich dafuer, dass t = -t if uzs else t
# Index 0 nach dem Sortieren der kleinste Winkel >= 0 Grad ist return t % 4.0
# (der gewuenschte Start bei 0 Grad).
roh = math.atan2(pxy[2] - acy, pxy[1] - acx) - achs_winkel
roh = -roh if uzs else roh
return roh % (2.0 * math.pi)
sortiert = sorted(punkte, key=lambda p: _winkel(p)) sortiert = sorted(punkte, key=lambda p: _winkel(p))
n = len(sortiert) n = len(sortiert)
+10
View File
@@ -504,6 +504,16 @@ def process_blocks(blocks, lookup):
counters["anzahl_abwurfstationen"] += 1 counters["anzahl_abwurfstationen"] += 1
continue continue
# --- Eingangs-/Ausgangssymbol (Materialfluss-Ein-/Ausgang) ---
# Bewusst NUR im CSV-Export als eigene Zeile (siehe export_csv.py) -
# hier ohne Sivas-Zeile und ohne eigenen Zaehler ueberspringen. Ein
# Eingangs-/Ausgangssymbol ist eine reine Fluss-Markierung ohne Sivas-
# Artikel; ein eigener Sivas-Eintrag wuerde die Stueckliste verfaelschen
# (Nutzeranforderung: Eingang/Ausgang nur im CSV-Export).
if matches_any(bname, BLOCKPATTERNS.get("pattern_eingang", [])) or \
matches_any(bname, BLOCKPATTERNS.get("pattern_ausgang", [])):
continue
# --- Geruest-Baugruppe --- # --- Geruest-Baugruppe ---
# Anders als BTMT/Separator je Block eine EIGENE Zeile statt gruppiert: # Anders als BTMT/Separator je Block eine EIGENE Zeile statt gruppiert:
# ARTINR (Sivas-IDs) und Laenge/Farbe koennen von Block zu Block # ARTINR (Sivas-IDs) und Laenge/Farbe koennen von Block zu Block
+1 -6
View File
@@ -110,12 +110,7 @@
[~APBW 110 R550/67,5 208x347]^C^C(ssg-ensure "OmniModulInsert") OMNI_APBW_R550_675_208 [~APBW 110 R550/67,5 208x347]^C^C(ssg-ensure "OmniModulInsert") OMNI_APBW_R550_675_208
[<-~APBW 110 R550/67,5 58x497]^C^C(ssg-ensure "OmniModulInsert") OMNI_APBW_R550_675_58 [<-~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 [<-Element bearbeiten]^C^C(ssg-ensure "OmniModulInsert") OMNI_EDIT
[->Geruest] [Geruest...]^C^C(ssg-ensure "GeruestInsert") GERUEST
[Rohrabhaenger]^C^C(ssg-ensure "GeruestInsert") GERUEST_ROHRABHAENGER
[->Saeulen]
[2 Zoll Saeulen]^C^C(ssg-ensure "GeruestInsert") GERUEST_SAEULE_2ZOLL
[<-1 Zoll Saeulen]^C^C(ssg-ensure "GeruestInsert") GERUEST_SAEULE_1ZOLL
[<-Wandbefestigung]^C^C(ssg-ensure "GeruestInsert") GERUEST_WANDBEFESTIGUNG
[->SPS] [->SPS]
[->TRO] [->TRO]
[Marker einfuegen]^C^C(ssg-ensure "Tro_Edit") TRO_INSERT [Marker einfuegen]^C^C(ssg-ensure "Tro_Edit") TRO_INSERT
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
test_export_eingang_ausgang.py - Eingangs-/Ausgangssymbole im Export.
Laeuft komplett ohne BricsCAD (reine Python-Logik).
Kundenwunsch: die Materialfluss-Ein-/Ausgangssymbole (Bloecke
Eingang_<DIM>/Ausgang_<DIM>, eingefuegt ueber ils-insert-station) sollen
- NUR im CSV-Export - als eigene Zeile erscheinen, inhaltlich aehnlich den
BTMT Be-/Entladestationen, und
- wie Separator/AS/ES in die "TrackIds" eingebaut werden (auch auf Kreiseln,
siehe test_export_kreisel_umlauf.py).
Im Sivas-Export duerfen sie NICHT auftauchen.
"""
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "lib"))
import export_csv # noqa: E402
import export_sivas # noqa: E402
def _block(block_name, teileid, artinr="12345", x=0.0, y=0.0):
return {
"block_name": block_name,
"x": x, "y": y, "z": 0.0,
"rotation": 0.0,
"attribs": {"ID": teileid, "ARTINR": artinr, "LAYER": "ILS_Flow",
"ZUORDNUNG": ""},
"_bbox": {"cx": x, "cy": y, "cz": 0.0, "dx": 200.0, "dy": 150.0},
}
class TestCsvKlassifikation:
"""export_csv.process_blocks gibt Eingang/Ausgang je eine eigene
TeileArt-Zeile ("Eingang"/"Ausgang") mit der Block-ID als TeileId."""
def test_eingang_und_ausgang_bekommen_eigene_zeile(self):
blocks = [
_block("Eingang_2D", "0050", x=0.0),
_block("Ausgang_3D", "0051", x=1000.0),
]
items = export_csv.process_blocks(blocks, lookup={})
nach_art = {it["teileart"]: it for it in items}
assert "Eingang" in nach_art, "Eingang-Zeile fehlt im CSV-Export"
assert "Ausgang" in nach_art, "Ausgang-Zeile fehlt im CSV-Export"
ein = nach_art["Eingang"]
aus = nach_art["Ausgang"]
# TeileId kommt aus dem ID-Attribut (stabil ueber Exportlaeufe).
assert ein["teileid"] == "0050"
assert aus["teileid"] == "0051"
# Merkmale wie BTMT: ARTINR als Merkmal gefuehrt.
assert ein["merkmale"].get("ARTINR") == "12345"
def test_teileid_fallback_ohne_id_attribut(self):
"""Ohne (noch) gefuelltes ID-Attribut greift der Fallback (EIN-/AUS-
Praefix), damit die Zeile trotzdem eine TeileId traegt - wie bei BTMT."""
blocks = [
{"block_name": "Eingang_2D", "x": 0.0, "y": 0.0, "z": 0.0,
"rotation": 0.0, "attribs": {},
"_bbox": {"cx": 0.0, "cy": 0.0, "cz": 0.0, "dx": 200.0, "dy": 150.0}},
]
items = export_csv.process_blocks(blocks, lookup={})
ein = next(it for it in items if it["teileart"] == "Eingang")
assert ein["teileid"].startswith("EIN-")
class TestSivasSchliesstAus:
"""export_sivas.process_blocks fuehrt Eingang/Ausgang bewusst NICHT als
eigene Zeile und zaehlt sie in keinem Summenzaehler mit."""
def test_keine_sivas_zeile_und_kein_zaehler(self):
blocks = [
_block("Eingang_2D", "0050"),
_block("Ausgang_3D", "0051"),
]
items, counters = export_sivas.process_blocks(blocks, lookup={})
arten = {it.get("teileart", "") for it in items}
assert "Eingang" not in arten
assert "Ausgang" not in arten
# Kein bestehender Zaehler darf durch die zwei Symbole hochgezaehlt
# worden sein (alle Zaehler bleiben 0, da nur Eingang/Ausgang im Satz).
assert all(v == 0 for v in counters.values()), (
f"Eingang/Ausgang haben einen Sivas-Zaehler beeinflusst: "
f"{{k: v for k, v in counters.items() if v}}")
+33
View File
@@ -64,6 +64,17 @@ def _btmt(teileart, teileid, x, y):
} }
def _flusssymbol(teileart, teileid, x, y):
"""Eingangs-/Ausgangssymbol ("Eingang"/"Ausgang") - frei auf der Bahn
platziert wie ein Separator/BTMT, siehe KREISEL_UMLAUF_TEILEARTEN. Die
TeileArt-Strings sind bewusst OHNE "ILS 2.0"-Praefix (siehe export_csv.py)."""
return {
"teileart": teileart,
"teileid": teileid,
"_bbox": {"cx": x, "cy": y, "cz": 0.0, "dx": 200.0, "dy": 150.0},
}
def _zwei_beruehrende_kreisel(): def _zwei_beruehrende_kreisel():
"""Zwei Kreisel, deren AN8-Enden sich exakt beruehren (End-an-End, wie im """Zwei Kreisel, deren AN8-Enden sich exakt beruehren (End-an-End, wie im
Mubea-Fall: 'Kreis/Kreis'-Beruehrung an der jeweiligen AN8-Kappe). Mubea-Fall: 'Kreis/Kreis'-Beruehrung an der jeweiligen AN8-Kappe).
@@ -422,3 +433,25 @@ class TestKreiselTrackidsSpalte:
write_kreisel_separatorliste_merkmale( write_kreisel_separatorliste_merkmale(
items, kreisel_umlauf, sep_kette_by_key={}, weiche_by_key={}) items, kreisel_umlauf, sep_kette_by_key={}, weiche_by_key={})
assert k["trackids"] == "0031, 0020, 0030" assert k["trackids"] == "0031, 0020, 0030"
def test_eingang_und_ausgang_zaehlen_als_freie_punkte(self):
"""Eingangs-/Ausgangssymbole ("Eingang"/"Ausgang") sind Fluss-
Markierungen AUF der Bahn wie ein Separator/BTMT - sie muessen (auf
Kundenwunsch) im Kreisel-Umlauf UND in der TrackIds-Spalte des
Kreisels auftauchen, genau wie Separator/AS/ES."""
k = _kreisel("0001", 0.0, 0.0, 0.0, abstand=1000.0)
k["merkmale"] = {}
sep = _separator("0020", 300.0, 300.0)
ein = _flusssymbol("Eingang", "0040", 900.0, 400.0)
aus = _flusssymbol("Ausgang", "0041", 1400.0, -50.0)
items = [k, sep, ein, aus]
kreisel_umlauf = compute_kreisel_umlauf(
items, sep_kette={}, tolerance_mm=200.0, durchmesser_mm=DURCHMESSER_MM)
keys = [p[0] for p in kreisel_umlauf["0001"]]
assert set(keys) == {"0020", "0040", "0041"}, (
f"Eingang/Ausgang fehlen im Kreisel-Umlauf: {keys}")
write_kreisel_separatorliste_merkmale(
items, kreisel_umlauf, sep_kette_by_key={}, weiche_by_key={})
assert k["trackids"] == "0041, 0020, 0040"
+36 -4
View File
@@ -135,7 +135,7 @@
;; jeder ueber ssg-ils-block-laden aufloesbare Blockname funktioniert. "sensor" ;; jeder ueber ssg-ils-block-laden aufloesbare Blockname funktioniert. "sensor"
;; bleibt als Sammelbegriff fuer den "kind"-Wert (= "kein Kreisel"), auch wenn ;; bleibt als Sammelbegriff fuer den "kind"-Wert (= "kein Kreisel"), auch wenn
;; inzwischen nicht mehr nur Separator/Scanner darueberlaufen. ;; inzwischen nicht mehr nur Separator/Scanner darueberlaufen.
(defun hundm05:bau-sensor (obj / id blockname x y z rotation pt eff n ent ed rec) (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 id (ssg-val obj "id"))
(setq blockname (ssg-val obj "block")) (setq blockname (ssg-val obj "block"))
(setq x (ssg-val obj "x") (setq x (ssg-val obj "x")
@@ -172,6 +172,7 @@
(princ (strcat "\n [SENSOR] " id " '" blockname "' -> (" (princ (strcat "\n [SENSOR] " id " '" blockname "' -> ("
(rtos x 2 0) ", " (rtos y 2 0) ", " (rtos z 2 0) (rtos x 2 0) ", " (rtos y 2 0) ", " (rtos z 2 0)
") R=" (rtos rotation 2 1))) ") R=" (rtos rotation 2 1)))
(setq vorher (entlast))
(command "_.INSERT" eff pt "" "" rotation) (command "_.INSERT" eff pt "" "" rotation)
;; Verbleibende Prompts mit Enter abfangen, aber GEZAEHLT - siehe ;; Verbleibende Prompts mit Enter abfangen, aber GEZAEHLT - siehe
;; mubea:build-separator-one (ein Prompt, der Leer-Antworten ;; mubea:build-separator-one (ein Prompt, der Leer-Antworten
@@ -186,14 +187,45 @@
(princ (strcat "\n WARNUNG: INSERT von '" eff (princ (strcat "\n WARNUNG: INSERT von '" eff
"' nicht sauber beendet - abgebrochen.")))) "' nicht sauber beendet - abgebrochen."))))
(setq ent (entlast)) (setq ent (entlast))
(if (atoms-family 1 '("ssg-id-generate")) (ssg-id-generate ent)) ;; Nicht blind (entlast) vertrauen: eine verschluckte Rueckfrage beim
;; Laden ODER Platzieren (siehe ssg-ils-block-laden-dim) kann eine
;; unvollstaendige/falsche Blockreferenz ohne jedes Attribut hinterlassen
;; (realer Fall: HundM05, letztes Zusatzobjekt = SC_Entladung, erste
;; Verwendung dieses Blocknamens im Testlauf). Nur eine ECHT NEUE
;; INSERT-Referenz gilt als Erfolg; alles andere wird als Fehler
;; gemeldet und die kaputte Restgeometrie sofort entfernt statt sie
;; unbemerkt in der Zeichnung zu belassen.
(cond
((or (null ent) (eq ent vorher)
(/= (cdr (assoc 0 (entget ent))) "INSERT"))
(princ (strcat "\n [SENSOR] " id " '" blockname
"' - FEHLER: INSERT hat keine gueltige neue Blockreferenz"
" erzeugt (verschluckte Rueckfrage?)."))
(if (and ent (not (eq ent vorher))) (entdel ent))
(setq rec (vsp-set (vsp-set rec "status" "abbruch") "fehler_text"
"INSERT lieferte keine gueltige neue Blockreferenz")))
(T
(setq attribs (ssg-attrib-read ent))
(setq ed (entget ent)) (setq ed (entget ent))
(princ (strcat " -> Block " (cdr (assoc 2 ed)))) (princ (strcat " -> Block " (cdr (assoc 2 ed))))
(setq rec (vsp-set rec "block_name" (cdr (assoc 2 ed)))) (setq rec (vsp-set rec "block_name" (cdr (assoc 2 ed))))
(setq rec (vsp-set rec "block_handle" (cdr (assoc 5 ed)))) (setq rec (vsp-set rec "block_handle" (cdr (assoc 5 ed))))
(setq rec (vsp-set rec "insert_point" (cdr (assoc 10 ed)))) (setq rec (vsp-set rec "insert_point" (cdr (assoc 10 ed))))
(setq rec (vsp-set rec "attribs" (ssg-attrib-read ent))) (setq rec (vsp-set rec "attribs" attribs))
(vsp-set rec "status" "executed")))))) ;; ID nur vergeben, wenn der Block ueberhaupt ein ID-Attribut fuehrt
;; (siehe ssg-id-set-and-verify in ssg_id.lsp) - sonst faellt hier
;; dieselbe "unsichtbare" Luecke auf, statt sie in der ID-Vergabe zu
;; verstecken.
(cond
((not (assoc "ID" attribs))
(princ (strcat "\n [SENSOR] " id " '" blockname
"' - WARNUNG: Block hat kein ID-Attribut (ATTDEF"
" fehlt/veraltet, ATTSYNC noetig) - keine ID vergeben."))
(setq rec (vsp-set (vsp-set rec "status" "warnung") "fehler_text"
"Block ohne ID-ATTDEF - ATTSYNC noetig")))
(T
(if (atoms-family 1 '("ssg-id-generate")) (ssg-id-generate ent))
(vsp-set rec "status" "executed"))))))))))
;; Alle Zusatzobjekte der Datei bauen. Eigener ssg-start-Rahmen, weil ;; Alle Zusatzobjekte der Datei bauen. Eigener ssg-start-Rahmen, weil
;; vsp-bau-datei seinen schon geschlossen hat; ATTREQ/ATTDIA 0 ist Pflicht - ;; vsp-bau-datei seinen schon geschlossen hat; ATTREQ/ATTDIA 0 ist Pflicht -
+419 -25
View File
@@ -19,6 +19,20 @@
;; Ergebnis: ;; Ergebnis:
;; Konsole: PASS/FAIL je Test + Zusammenfassung ;; Konsole: PASS/FAIL je Test + Zusammenfassung
;; Datei: tests/output/unit_results.txt (von run_tests.bat gelesen) ;; Datei: tests/output/unit_results.txt (von run_tests.bat gelesen)
;;
;; Ladearchitektur (EINHEITLICH):
;; Das Laden der zu testenden Module passiert AUSSCHLIESSLICH auf .scr-
;; Top-Level in tests/test_unit.scr - die Core-Module ueber ssg_load.lsp,
;; die lazy Feature-Module (GeruestInsert, count_sep_scan) per explizitem
;; load. Die Testbloecke hier laden NICHTS nach; sie pruefen nur per
;; atoms-family, ob ihr Modul vorhanden ist, und ueberspringen sich sonst
;; mit einer FAIL-Meldung.
;; Grund: In BricsCAD werden per load definierte Funktionen erst nach
;; Rueckkehr zum Top-Level sichtbar. Ein load INNERHALB von c:TEST_UNIT
;; oder eines per vl-catch-all-apply gecatchten Testblocks bindet die
;; frisch definierten Funktionen im laufenden Durchlauf NICHT (beobachtet:
;; geruest-id-string/cs-in-box blieben ungebunden). Darum gehoert alles
;; Laden vor den (c:TEST_UNIT)-Aufruf ins .scr.
;; ============================================================ ;; ============================================================
@@ -300,11 +314,13 @@
(tu-eq "csv:b64-encode-ints/0" "AAAA" (csv:b64-encode-ints '(0))) (tu-eq "csv:b64-encode-ints/0" "AAAA" (csv:b64-encode-ints '(0)))
(tu-eq "csv:trans-encode/0-0-0" "AAAAAAAAAAAA" (csv:trans-encode 0.0 0.0 0.0)) (tu-eq "csv:trans-encode/0-0-0" "AAAAAAAAAAAA" (csv:trans-encode 0.0 0.0 0.0))
;; local-to-world: reine Z-Drehung um den Elternpunkt ;; local-to-world: Eltern-Orientierung als 3x3-Rotationsmatrix (mat3-rz
;; liefert die Z-Drehmatrix aus dem Winkel - Signatur seit dem
;; Extrusions-Fix in export.lsp: parent-R statt parent-rot-rad).
(tu-eqf "csv:local-to-world/keine-drehung" (tu-eqf "csv:local-to-world/keine-drehung"
'(1.0 0.0 0.0) (csv:local-to-world '(0.0 0.0 0.0) 0.0 '(1.0 0.0 0.0))) '(1.0 0.0 0.0) (csv:local-to-world '(0.0 0.0 0.0) (mat3-rz 0.0) '(1.0 0.0 0.0)))
(tu-eqf "csv:local-to-world/90-grad" (tu-eqf "csv:local-to-world/90-grad"
'(0.0 1.0 0.0) (csv:local-to-world '(0.0 0.0 0.0) (/ pi 2.0) '(1.0 0.0 0.0))) '(0.0 1.0 0.0) (csv:local-to-world '(0.0 0.0 0.0) (mat3-rz (/ pi 2.0)) '(1.0 0.0 0.0)))
;; json-escape: Anfuehrungszeichen maskieren ;; json-escape: Anfuehrungszeichen maskieren
(tu-eq "csv:json-escape/quote" "a\\\"b" (csv:json-escape "a\"b")) (tu-eq "csv:json-escape/quote" "a\\\"b" (csv:json-escape "a\"b"))
@@ -401,6 +417,17 @@
(tu-eq "ssg-sepliste-split-feld/normal" '("SEP" "1" "10.00" "20.00") (tu-eq "ssg-sepliste-split-feld/normal" '("SEP" "1" "10.00" "20.00")
(ssg-sepliste-split-feld "SEP:1:10.00:20.00" ":")) (ssg-sepliste-split-feld "SEP:1:10.00:20.00" ":"))
;; ssg-sepliste-fmt-koord: GENAU 2 Nachkommastellen, DIMZIN-unabhaengig.
;; Ersetzt rtos (das bei DIMZIN=8 abschliessende Nullen verschluckt und die
;; XDATA-Serialisierung zeichnungsprofilabhaengig gemacht hat).
(tu-eq "sepliste-fmt/ganz" "10.00" (ssg-sepliste-fmt-koord 10.0))
(tu-eq "sepliste-fmt/eine-stelle" "30.50" (ssg-sepliste-fmt-koord 30.5))
(tu-eq "sepliste-fmt/rundung-ab" "5.01" (ssg-sepliste-fmt-koord 5.007))
(tu-eq "sepliste-fmt/uebertrag" "10.00" (ssg-sepliste-fmt-koord 9.999))
(tu-eq "sepliste-fmt/negativ" "-5.00" (ssg-sepliste-fmt-koord -5.0))
(tu-eq "sepliste-fmt/negativ-rund" "-5.01" (ssg-sepliste-fmt-koord -5.007))
(tu-eq "sepliste-fmt/null" "0.00" (ssg-sepliste-fmt-koord 0.0))
;; ssg-sepliste->string: Serialisierung eines Eintrags ;; ssg-sepliste->string: Serialisierung eines Eintrags
(tu-eq "ssg-sepliste->string/ein-eintrag" (tu-eq "ssg-sepliste->string/ein-eintrag"
"SEP:1:10.00:20.00" "SEP:1:10.00:20.00"
@@ -471,12 +498,27 @@
(tu-eq "json/escape-newline" (chr 10) (tu-eq "json/escape-newline" (chr 10)
(ssg-json-get (ssg-json-parse "{\"a\": \"\\n\"}") "a")) (ssg-json-get (ssg-json-parse "{\"a\": \"\\n\"}") "a"))
;; ssg-json-escape: Gegenstueck zum Parser
(tu-eq "json-escape/quote" "a\\\"b" (ssg-json-escape "a\"b"))
(tu-eq "json-escape/backslash" "a\\\\b" (ssg-json-escape "a\\b"))
(tu-eq "json-escape/newline" "a\\nb" (ssg-json-escape (strcat "a" (chr 10) "b")))
(tu-eq "json-escape/leer" "" (ssg-json-escape nil))
;; Rundlauf: maskieren -> parsen ergibt den Originaltext zurueck
(tu-eq "json-escape/rundlauf" "ROHRKLAMMER 1\" KPL. VERTIKAL"
(ssg-json-get
(ssg-json-parse
(strcat "{\"a\": \""
(ssg-json-escape "ROHRKLAMMER 1\" KPL. VERTIKAL") "\"}"))
"a"))
;; Echte Datendatei: der eigentliche Praxisfall ;; Echte Datendatei: der eigentliche Praxisfall
(setq _datei (strcat (getenv "DXFMAKRO") "/data/json/geruest.json")) (setq _datei (strcat (getenv "DXFMAKRO") "/data/json/geruest.json"))
(if (findfile _datei) (if (findfile _datei)
(progn (progn
(setq _daten (ssg-json-laden _datei)) (setq _daten (ssg-json-laden _datei))
(tu-eq "json/geruest-baugruppen" 4 (length _daten)) ;; Drei Bereiche: Rohrabhaenger, Saeulen (1 und 2 Zoll zusammengefuehrt),
;; Wandbefestigung
(tu-eq "json/geruest-baugruppen" 3 (length _daten))
;; Baugruppe ueber das ASCII-Feld "id" finden (aeusserer Schluessel ;; Baugruppe ueber das ASCII-Feld "id" finden (aeusserer Schluessel
;; traegt Umlaute bzw. Anfuehrungszeichen) ;; traegt Umlaute bzw. Anfuehrungszeichen)
(setq _bg nil) (setq _bg nil)
@@ -509,20 +551,15 @@
;; ------------------------------------------------------------ ;; ------------------------------------------------------------
;; TESTS: GeruestInsert.lsp - reine Hilfsfunktionen ;; TESTS: GeruestInsert.lsp - reine Hilfsfunktionen
;; ------------------------------------------------------------ ;; ------------------------------------------------------------
(defun tu-tests-geruest ( / _eintrag _gruppe) (defun tu-tests-geruest ( / _eintrag _gruppe _teile _konfig _rec _pfad _f
_zurueck _bgtest _saeulen)
(princ "\n[GeruestInsert]") (princ "\n[GeruestInsert]")
(if (and (null (atoms-family 1 '("GERUEST-ARTIKELNUMMERN"))) ;; GeruestInsert ist ein lazy Feature-Modul (nicht in der Core-Ladeliste).
(atoms-family 1 '("SSG-ENSURE"))) ;; Geladen wird es zentral in test_unit.scr auf .scr-Top-Level; hier nur
(ssg-ensure "GeruestInsert") ;; pruefen, ob es da ist (siehe Kopfkommentar zur Ladearchitektur).
)
(if (null (atoms-family 1 '("GERUEST-ARTIKELNUMMERN"))) (if (null (atoms-family 1 '("GERUEST-ARTIKELNUMMERN")))
(tu-record nil "geruest/modul" "GeruestInsert.lsp nicht geladen") (tu-record nil "geruest/modul" "GeruestInsert.lsp nicht geladen")
(progn (progn
;; Indizes aus get_tile bei multiple_select
(tu-eq "geruest-indizes/leer" '() (geruest-indizes ""))
(tu-eq "geruest-indizes/eins" '(0) (geruest-indizes "0"))
(tu-eq "geruest-indizes/drei" '(0 3 5) (geruest-indizes "0 3 5"))
(tu-eq "geruest-id-string/int" "821006026" (geruest-id-string 821006026)) (tu-eq "geruest-id-string/int" "821006026" (geruest-id-string 821006026))
(tu-eq "geruest-id-string/str" "0_B10071" (geruest-id-string "0_B10071")) (tu-eq "geruest-id-string/str" "0_B10071" (geruest-id-string "0_B10071"))
(tu-eq "geruest-id-string/nil" nil (geruest-id-string nil)) (tu-eq "geruest-id-string/nil" nil (geruest-id-string nil))
@@ -547,6 +584,247 @@
(tu-eq "geruest-artnr/ohne-menge" '("333") (tu-eq "geruest-artnr/ohne-menge" '("333")
(geruest-artikelnummern (geruest-artikelnummern
(ssg-json-parse "{\"artikel\": \"C\", \"sivas_id\": 333}"))) (ssg-json-parse "{\"artikel\": \"C\", \"sivas_id\": 333}")))
;; --- Mengenfeld ---
(tu-eq "geruest-zahl-p/ziffern" T (geruest-zahl-p "12"))
(tu-eq "geruest-zahl-p/text" nil (geruest-zahl-p "1a"))
(tu-eq "geruest-zahl-p/leer" nil (geruest-zahl-p ""))
;; Leeres Feld uebernimmt die JSON-Vorgabe, ungueltiges liefert nil
(tu-eq "geruest-menge-wert/leer" 2 (geruest-menge-wert "" 2))
(tu-eq "geruest-menge-wert/zahl" 5 (geruest-menge-wert "5" 2))
(tu-eq "geruest-menge-wert/null" nil (geruest-menge-wert "0" 2))
(tu-eq "geruest-menge-wert/text" nil (geruest-menge-wert "abc" 2))
(tu-eq "geruest-menge-wert/negativ" nil (geruest-menge-wert "-3" 2))
;; --- Stueckzahl ersetzt die JSON-Menge (Einzelteil) ---
(tu-eq "geruest-artnr-n/einzelteil" '("825002019" "825002019" "825002019")
(geruest-artikelnummern-n _eintrag 3))
;; --- Gruppe: ganzes Set n-mal, Mengen INNERHALB bleiben erhalten ---
(tu-eq "geruest-artnr-n/gruppe" '("111" "222" "222" "111" "222" "222")
(geruest-artikelnummern-n _gruppe 2))
;; --- DCL-Label: Anfuehrungszeichen wuerde die Datei zerstoeren ---
(tu-eq "geruest-dcl-label/quote" "ROHRKLAMMER 1'' KPL. VERTIKAL"
(geruest-dcl-label "ROHRKLAMMER 1\" KPL. VERTIKAL"))
(tu-eq "geruest-dcl-label/ohne" "WINKEL 60X60X6X60"
(geruest-dcl-label "WINKEL 60X60X6X60"))
;; --- Hilfslisten ---
(tu-eq "geruest-nil-liste" '(nil nil nil) (geruest-nil-liste 3))
(tu-eq "geruest-liste-setzen" '(nil 7 nil)
(geruest-liste-setzen (geruest-nil-liste 3) 1 7))
(tu-eq "geruest-alist-setzen/neu" '(("a" . 1))
(geruest-alist-setzen nil "a" 1))
(tu-eq "geruest-alist-setzen/ersetzt" '(("a" . 2))
(geruest-alist-setzen '(("a" . 1)) "a" 2))
(tu-eq "geruest-zahl-string/int" "2000" (geruest-zahl-string 2000))
(tu-eq "geruest-zahl-string/leer" "" (geruest-zahl-string nil))
;; --- Konfiguration auf Anbauteile abbilden ---
(setq _teile (list _eintrag _gruppe))
(tu-eq "geruest-name-index/treffer" 1
(geruest-name-index "Schraubenset" _teile))
(tu-eq "geruest-name-index/fehlt" nil
(geruest-name-index "gibt es nicht" _teile))
;; Bekanntes Teil wird uebernommen, unbekanntes uebersprungen (nicht
;; die ganze Konfiguration verworfen)
(setq _konfig (ssg-json-parse
(strcat "{\"name\": \"K1\", \"oben\": ["
"{\"artikel\": \"Schraubenset\", \"menge\": 3},"
"{\"artikel\": \"unbekannt\", \"menge\": 1}]}")))
(tu-eq "geruest-konfig-position" '(nil 3)
(geruest-konfig-position _konfig "oben" _teile))
;; --- Variante gewinnt vor Baugruppe (TEILEART/dummy) ---
;; Die zusammengefuehrte Saeulen-Baugruppe unterscheidet 1 Zoll und
;; 2 Zoll nur noch ueber das teileart-Feld der Variante.
(setq _bgtest (ssg-json-parse
"{\"teileart\": \"Basis\", \"dummy\": \"basis.dwg\"}"))
(tu-eq "geruest-variante-wert/variante-gewinnt" "Speziell"
(geruest-variante-wert
(ssg-json-parse "{\"teileart\": \"Speziell\"}") _bgtest "teileart"))
(tu-eq "geruest-variante-wert/rueckfall" "Basis"
(geruest-variante-wert
(ssg-json-parse "{\"name\": \"X\"}") _bgtest "teileart"))
(tu-eq "geruest-variante-wert/ohne-variante" "basis.dwg"
(geruest-variante-wert nil _bgtest "dummy"))
;; --- Zusammengefuehrte Saeulen-Baugruppe ---
(setq _saeulen nil)
(foreach _e (geruest-baugruppen-liste)
(if (equal (car _e) "Saeulen") (setq _saeulen (cdr _e))))
(tu-eq "geruest/saeulen-vorhanden" T (if _saeulen T nil))
(if _saeulen
(progn
(tu-eq "geruest/saeulen-varianten" 6
(length (ssg-json-get _saeulen "geometrie_varianten")))
(tu-eq "geruest/saeulen-anbauteile" 7
(length (ssg-json-get _saeulen "anbaugruppen")))
(tu-eq "geruest/saeulen-titel" "Saeulen" (geruest-titel _saeulen))
;; R 1" ist die erste Variante und traegt die eigene TEILEART
(tu-eq "geruest/saeulen-1zoll-teileart" "Geruest Saeule 1 Zoll"
(geruest-variante-wert
(car (ssg-json-get _saeulen "geometrie_varianten"))
_saeulen "teileart"))
)
)
;; --- Rundlauf Schreiber -> Parser ---
;; Prueft den Konfigurations-Schreiber ohne BricsCAD-Dialog. Bewusst in
;; eine temporaere Datei: ein Test darf cfg/geruest_user.json nicht
;; ueberschreiben. Die Testdaten enthalten absichtlich Anfuehrungs-
;; zeichen - genau daran wuerde eine fehlende Maskierung scheitern.
(setq _rec
(list (cons "name" "Mein \"Satz\"")
(cons "variante" "ROHR R 1\" 33,7 x 2,6")
(cons "laenge" "2000")
(cons "teilefarbe" "RAL 7035 Lichtgrau")
(cons "oben" (list (list (cons "artikel" "ROHRKLAMMER 1\" KPL. VERTIKAL")
(cons "menge" 2))))
(cons "unten" nil)))
(setq _pfad (vl-filename-mktemp "grtest" nil ".json"))
(setq _f (open _pfad "w"))
(write-line "{ \"Test\": [" _f)
(geruest-konfig-liste-schreiben _f (list _rec))
(write-line "] }" _f)
(close _f)
(setq _zurueck (car (ssg-json-get (ssg-json-laden _pfad) "Test")))
(vl-file-delete _pfad)
(tu-eq "geruest-schreiber/name" "Mein \"Satz\""
(ssg-json-get _zurueck "name"))
(tu-eq "geruest-schreiber/variante" "ROHR R 1\" 33,7 x 2,6"
(ssg-json-get _zurueck "variante"))
(tu-eq "geruest-schreiber/laenge" "2000"
(ssg-json-get _zurueck "laenge"))
(tu-eq "geruest-schreiber/farbe" "RAL 7035 Lichtgrau"
(ssg-json-get _zurueck "teilefarbe"))
(tu-eq "geruest-schreiber/oben-artikel" "ROHRKLAMMER 1\" KPL. VERTIKAL"
(ssg-json-get (car (ssg-json-get _zurueck "oben")) "artikel"))
(tu-eq "geruest-schreiber/oben-menge" 2
(ssg-json-get (car (ssg-json-get _zurueck "oben")) "menge"))
(tu-eq "geruest-schreiber/unten-leer" nil
(ssg-json-get _zurueck "unten"))
)
)
)
;; ------------------------------------------------------------
;; TESTS: count_sep_scan.lsp - geometrische Sensor-Zuordnung
;;
;; Testet die durch die Zerlegung von cs-zuordnung-lauf isolierte,
;; rein funktionale Kernlogik: Carrier-Records sind plain Listen
;; (ename tagS tagP minx miny maxx maxy cx cy id label), Sensoren
;; sind Punkte (x y). Kein ssget/entget/entmod - der ename an
;; Position 0 wird von den Geometrie-Routinen nie dereferenziert,
;; darum genuegt ein Symbol-Platzhalter. *senstol* wird fuer
;; deterministische Ergebnisse explizit gesetzt (sonst cfg-abhaengig).
;; ------------------------------------------------------------
;; Record-Konstruktor fuers Testen: baut einen Carrier-Record so, wie
;; ihn cs-sammle-bloecke liefert - nur die Box-/Zentrum-/ID-Felder
;; (Index 3-9), die die Geometrie-Routinen lesen, sind relevant; ename
;; und label sind reine Platzhalter.
;; (name minx miny maxx maxy id) -> voller Carrier-Record
(defun tu-mk-carr (name minx miny maxx maxy id)
(list name "ANZAHL_SCANNER" "ANZAHL_SEPARATOR"
minx miny maxx maxy
(/ (+ minx maxx) 2.0) (/ (+ miny maxy) 2.0)
id (strcat "L-" name)))
(defun tu-tests-sepscan ( / _senstol-alt _a _b _c _carr _grid _res)
(princ "\n[count_sep_scan]")
;; count_sep_scan ist ein lazy Feature-Modul (nicht in der Core-Ladeliste).
;; Geladen wird es zentral in test_unit.scr auf .scr-Top-Level; hier nur
;; pruefen, ob es da ist (siehe Kopfkommentar zur Ladearchitektur).
(if (null (atoms-family 1 '("CS-RESOLVE-SENSOR")))
(tu-record nil "sepscan/modul" "count_sep_scan.lsp nicht geladen")
(progn
(setq _senstol-alt (if (boundp '*senstol*) *senstol*))
(setq *senstol* 0.0)
;; --- cs-in-box: Punkt in Carrier-Box (X/Y, Z ignoriert) ---
(setq _a (tu-mk-carr "A" 0.0 0.0 100.0 100.0 "0001"))
(tu-true "cs-in-box/mitte" (cs-in-box '(50.0 50.0) _a))
(tu-true "cs-in-box/rand" (cs-in-box '(0.0 0.0) _a))
(tu-nil "cs-in-box/ausserhalb-x" (cs-in-box '(150.0 50.0) _a))
(tu-nil "cs-in-box/ausserhalb-y" (cs-in-box '(50.0 -1.0) _a))
;; --- *senstol* vergroessert die Box ---
(setq *senstol* 10.0)
(tu-true "cs-in-box/toleranz-innen" (cs-in-box '(105.0 50.0) _a))
(tu-nil "cs-in-box/toleranz-aussen" (cs-in-box '(111.0 50.0) _a))
(setq *senstol* 0.0)
;; --- cs-bump: Zaehler je ename hochzaehlen ---
(tu-eq "cs-bump/neu" '(("k" . 1)) (cs-bump nil "k"))
(tu-eq "cs-bump/erhoeht" '(("k" . 2)) (cs-bump '(("k" . 1)) "k"))
(tu-eq "cs-bump/zweiter-key"
'(("m" . 1) ("k" . 1)) (cs-bump '(("k" . 1)) "m"))
;; --- cs-carrier-by-id: Record ueber ID (Feld 9) finden ---
(setq _b (tu-mk-carr "B" 200.0 0.0 300.0 100.0 "0002"))
(setq _carr (list _a _b))
(tu-eq "cs-carrier-by-id/treffer" _b (cs-carrier-by-id "0002" _carr))
(tu-nil "cs-carrier-by-id/fehlt" (cs-carrier-by-id "9999" _carr))
(tu-nil "cs-carrier-by-id/leer" (cs-carrier-by-id "" _carr))
(tu-nil "cs-carrier-by-id/nil" (cs-carrier-by-id nil _carr))
;; --- cs-assign: BBox-Zuordnung ueber das Grid ---
;; Zwei getrennte Boxen: Punkt in genau einer -> diese.
(setq _grid (cs-grid-build _carr))
(tu-eq "cs-assign/box-a" _a (cs-assign '(50.0 50.0) _grid))
(tu-eq "cs-assign/box-b" _b (cs-assign '(250.0 50.0) _grid))
(tu-nil "cs-assign/keine-box" (cs-assign '(1000.0 1000.0) _grid))
;; Ueberlappende Boxen -> naechstes Box-Zentrum gewinnt (Tiebreak).
;; C: 0..100, D: 40..200; Punkt (45,50) liegt in beiden,
;; C-Zentrum (50,50) ist naeher als D-Zentrum (120,50).
(setq _a (tu-mk-carr "C" 0.0 0.0 100.0 100.0 "0003"))
(setq _b (tu-mk-carr "D" 40.0 0.0 200.0 100.0 "0004"))
(setq _grid (cs-grid-build (list _a _b)))
(tu-eq "cs-assign/overlap-naechstes-zentrum"
_a (cs-assign '(45.0 50.0) _grid))
;; --- cs-nearest-two: die zwei naechsten Carrier nach Zentrum ---
;; Zentren: A(50,50) B(250,50) C(50,250). Punkt (60,60):
;; naechster A, zweiter B (dx 190 < dy 190? -> Abstand B=~194, C=~190).
(setq _a (tu-mk-carr "A" 0.0 0.0 100.0 100.0 "0001"))
(setq _b (tu-mk-carr "B" 200.0 0.0 300.0 100.0 "0002"))
(setq _c (tu-mk-carr "C" 0.0 200.0 100.0 300.0 "0003"))
(setq _res (cs-nearest-two '(60.0 60.0) (list _a _b _c)))
(tu-eq "cs-nearest-two/naechster" _a (nth 0 _res))
(tu-true "cs-nearest-two/zweiter-vorhanden" (nth 2 _res))
;; Abstand zum naechsten (A-Zentrum 50,50) = sqrt(10^2+10^2) ~ 14.14
(tu-eqf "cs-nearest-two/best-dist"
(distance '(60.0 60.0) '(50.0 50.0)) (nth 1 _res))
;; Nur ein Carrier -> zweiter ist nil.
(setq _res (cs-nearest-two '(60.0 60.0) (list _a)))
(tu-eq "cs-nearest-two/einzeln-best" _a (nth 0 _res))
(tu-nil "cs-nearest-two/einzeln-kein-zweiter" (nth 2 _res))
;; --- cs-resolve-sensor: der Entscheider bbox/distance/none ---
;; Punkt in einer Box -> modus 'bbox, kein Distanzfeld.
(setq _grid (cs-grid-build (list _a _b _c)))
(setq _res (cs-resolve-sensor '(50.0 50.0) (list _a _b _c) _grid))
(tu-eq "cs-resolve/bbox-modus" 'bbox (nth 0 _res))
(tu-eq "cs-resolve/bbox-rec" _a (nth 1 _res))
(tu-nil "cs-resolve/bbox-kein-dist" (nth 2 _res))
;; Punkt ausserhalb aller Boxen, aber Carrier vorhanden -> 'distance.
(setq _res (cs-resolve-sensor '(1000.0 1000.0) (list _a _b _c) _grid))
(tu-eq "cs-resolve/distance-modus" 'distance (nth 0 _res))
(tu-true "cs-resolve/distance-rec" (nth 1 _res))
(tu-true "cs-resolve/distance-dist" (nth 2 _res))
;; Gar keine Carrier -> 'none.
(setq _grid (cs-grid-build nil))
(setq _res (cs-resolve-sensor '(50.0 50.0) nil _grid))
(tu-eq "cs-resolve/none-modus" 'none (nth 0 _res))
(tu-nil "cs-resolve/none-rec" (nth 1 _res))
;; *senstol* wieder herstellen
(setq *senstol* _senstol-alt)
) )
) )
) )
@@ -577,26 +855,142 @@
) )
;; ------------------------------------------------------------
;; TESTS: vf_linienzug.lsp - SEGS-Journal-Serialisierung (Modus 3)
;; ------------------------------------------------------------
;; Roundtrip der Segment-Essenz: die Zahlenliste, die Modus 3 aus den Pfad-
;; Objekten ableitet, muss verlustfrei durch String<->Liste und durch die
;; Journal-Eintrags-Serialisierung laufen - das ist die Grundlage dafuer, dass
;; ein Modus-3-Block per Doppelklick 1:1 aus dem XDATA-Journal nachgebaut wird.
(defun tu-tests-vfl-segs ( / _seg _segs _s _e)
(princ "\n[vf_linienzug/SEGS]")
(if (null (atoms-family 1 '("VFL-SEGS->STRING")))
(tu-record nil "vfl-segs/modul" "vf_linienzug.lsp nicht geladen (VFL-SEGS->STRING fehlt)")
(progn
;; Einzelsegment (Bogen, alle Felder gesetzt) Roundtrip
(setq _seg '("Bogen" 102.5 707.106781 90 "rechts" 1000.0 2000.0 1500.5 2500.25))
(tu-eqf "vfl-seg/roundtrip-bogen"
_seg (vfl-string->seg (vfl-seg->string _seg)))
;; Linie (bwinkel 0, bseite default) Roundtrip
(setq _seg '("Linie" 30.0 4200.0 0 "links" 0.0 0.0 3637.31 2100.0))
(tu-eqf "vfl-seg/roundtrip-linie"
_seg (vfl-string->seg (vfl-seg->string _seg)))
;; Ganze Segmentliste (Linie + Bogen + Linie) Roundtrip
(setq _segs (list '("Linie" 0.0 3000.0 0 "links" 0.0 0.0 3000.0 0.0)
'("Bogen" 0.0 707.106781 90 "rechts" 3000.0 0.0 3500.0 -500.0)
'("Linie" 270.0 2500.0 0 "links" 3500.0 -500.0 3500.0 -3000.0)))
(tu-eqf "vfl-segs/roundtrip-liste"
_segs (vfl-string->segs (vfl-segs->string _segs)))
;; Leere Liste -> "" -> leere Liste
(tu-eq "vfl-segs/leer-string" "" (vfl-segs->string '()))
(tu-nil "vfl-segs/leer-parse" (vfl-string->segs ""))
;; Einbettung als Journal-Eintrag: (SEGS . liste) durch
;; vfl-entry->string / vfl-string->entry
(setq _e (cons "SEGS" _segs))
(setq _s (vfl-entry->string _e))
(tu-true "vfl-segs/entry-praefix" (= "SEGS:" (substr _s 1 5)))
(tu-eqf "vfl-segs/entry-roundtrip"
_e (vfl-string->entry _s))
;; Delimiter-Sicherheit: die serialisierte Form enthaelt genau die
;; erwarteten Trenner und KEIN Newline (sonst zerrisse der Record-Trenner)
(tu-nil "vfl-segs/kein-newline"
(vl-string-search "\n" (vfl-segs->string _segs)))
;; vfl-journal-hat-segs-p: unterscheidet neue SEGS-Ketten von alten
;; OBJS-"linienzug2"-Bloecken (harter Schnitt beim Doppelklick).
(if (atoms-family 1 '("VFL-JOURNAL-HAT-SEGS-P"))
(progn
;; SEGS-Journal (Modus 2 neu / Modus 3): erkannt
(tu-true "vfl-hat-segs/mit-segs"
(vfl-journal-hat-segs-p
(list (cons "PT" '(0.0 0.0 0.0)) (cons "SEGS" _segs))))
;; Altes OBJS-Journal (Objekt-Handles): NICHT als SEGS erkannt
(tu-nil "vfl-hat-segs/nur-objs"
(vfl-journal-hat-segs-p
(list (cons "PT" '(0.0 0.0 0.0)) (cons "OBJS" '("A1" "A2")))))
;; Leeres Journal: nil
(tu-nil "vfl-hat-segs/leer" (vfl-journal-hat-segs-p '())))
(tu-record nil "vfl-hat-segs/modul" "VFL-JOURNAL-HAT-SEGS-P fehlt"))
)
)
)
;; ------------------------------------------------------------
;; TESTS: vf_linienzug.lsp - gemeinsamer Abbruch-Handler-Konstruktor
;; ------------------------------------------------------------
;; vfl-abbruch-handler-installieren baut *error* als Lambda-Liste, in die der
;; Marker ("linienzug"/"linienzug2"/"linienzug3") als String-Literal eingebaut
;; ist. Der wortgleiche Wickel-Handler steht damit nur noch EINMAL statt 3x.
;; Getestet wird die KONSTRUKTION (Marker an der richtigen Stelle, Struktur
;; aufrufbar) - NICHT ein echter Abbruch (der braeuchte Zeichnung/Geometrie).
;; *error* wird gesichert und wiederhergestellt, damit der Testlauf unberuehrt
;; bleibt.
(defun tu-tests-vfl-abbruch ( / _save _h _call)
(princ "\n[vf_linienzug/Abbruch-Handler]")
(if (null (atoms-family 1 '("VFL-ABBRUCH-HANDLER-INSTALLIEREN")))
(tu-record nil "vfl-abbruch/modul"
"vf_linienzug.lsp nicht geladen (VFL-ABBRUCH-HANDLER-INSTALLIEREN fehlt)")
(progn
(setq _save *error*)
(setq _h (vfl-abbruch-handler-installieren "linienzug2"))
;; Rueckgabe = das gesetzte *error* (identisch)
(tu-true "vfl-abbruch/setzt-error" (equal _h *error*))
;; Struktur: (lambda (msg) ...) - erstes Element lambda, zweites (msg)
(tu-eq "vfl-abbruch/ist-lambda" 'lambda (car _h))
(tu-eq "vfl-abbruch/arg-msg" '(msg) (cadr _h))
;; Der vfl-modus-abbruch-sichern-Aufruf im Rumpf traegt GENAU den
;; uebergebenen Marker als letztes Argument (nicht einen anderen Modus).
(setq _call (car (vl-member-if
(function (lambda (f)
(and (listp f) (eq (car f) 'vfl-modus-abbruch-sichern))))
(cddr _h))))
(tu-true "vfl-abbruch/ruft-sichern" (not (null _call)))
(tu-eq "vfl-abbruch/marker-linienzug2" "linienzug2" (last _call))
;; Anderer Marker -> anderes Literal an derselben Position
(vfl-abbruch-handler-installieren "linienzug3")
(setq _call (car (vl-member-if
(function (lambda (f)
(and (listp f) (eq (car f) 'vfl-modus-abbruch-sichern))))
(cddr *error*))))
(tu-eq "vfl-abbruch/marker-linienzug3" "linienzug3" (last _call))
;; *error* wiederherstellen - Testlauf darf nicht mit einem Wickel-Handler
;; ohne gebundene Modus-Locals zurueckbleiben.
(setq *error* _save)
)
)
)
;; ------------------------------------------------------------ ;; ------------------------------------------------------------
;; BEFEHL: TEST_UNIT ;; BEFEHL: TEST_UNIT
;; ------------------------------------------------------------ ;; ------------------------------------------------------------
;; Testblock geschuetzt aufrufen: ein harter LISP-Fehler in einem Block
;; wird als FAIL erfasst, statt c:TEST_UNIT abzubrechen und damit den
;; Report zu verhindern. Der Blockname (String) wird per read zur Funktion.
(defun tu-run-block (fn / res)
(setq res (vl-catch-all-apply (read fn) nil))
(if (vl-catch-all-error-p res)
(tu-record nil (strcat fn "/ausfuehrung")
(strcat "harter Fehler: " (vl-catch-all-error-message res))))
)
(defun c:TEST_UNIT ( / out-dir report) (defun c:TEST_UNIT ( / out-dir report)
(tu-reset) (tu-reset)
(princ "\n================================================================") (princ "\n================================================================")
(princ "\n TEST_UNIT - LISP-Standardfunktionen") (princ "\n TEST_UNIT - LISP-Standardfunktionen")
(princ "\n================================================================") (princ "\n================================================================")
(tu-tests-core) (foreach fn '("tu-tests-core" "tu-tests-lang" "tu-tests-id" "tu-tests-vfcore"
(tu-tests-lang) "tu-tests-export" "tu-tests-gf" "tu-tests-omni" "tu-tests-kreisel"
(tu-tests-id) "tu-tests-sepliste" "tu-tests-json" "tu-tests-geruest"
(tu-tests-vfcore) "tu-tests-sepscan" "tu-tests-vfl-segs" "tu-tests-vfl-abbruch")
(tu-tests-export) (tu-run-block fn))
(tu-tests-gf)
(tu-tests-omni)
(tu-tests-kreisel)
(tu-tests-sepliste)
(tu-tests-json)
(tu-tests-geruest)
(princ "\n\n----------------------------------------------------------------") (princ "\n\n----------------------------------------------------------------")
(princ (strcat "\n Gesamt: " (itoa (+ *tu-pass* *tu-fail*)) (princ (strcat "\n Gesamt: " (itoa (+ *tu-pass* *tu-fail*))
+8
View File
@@ -2,6 +2,14 @@
; Laedt SSG_LIB + die Unit-Tests, fuehrt TEST_UNIT aus und beendet BricsCAD. ; Laedt SSG_LIB + die Unit-Tests, fuehrt TEST_UNIT aus und beendet BricsCAD.
; Pfadunabhaengig ueber DXFMAKRO (von setenv.bat gesetzt). ; Pfadunabhaengig ueber DXFMAKRO (von setenv.bat gesetzt).
(load (strcat (getenv "DXFMAKRO") "/Lisp/ssg_load.lsp")) (load (strcat (getenv "DXFMAKRO") "/Lisp/ssg_load.lsp"))
; Lazy Feature-Module, die einzelne Unit-Tests brauchen, hier auf .scr-Top-
; Level laden. In BricsCAD werden per load definierte Funktionen erst nach
; Rueckkehr zum Top-Level sichtbar - ein load INNERHALB von c:TEST_UNIT
; (oder eines gecatchten Testblocks) bindet sie im selben Durchlauf nicht.
; DIES ist die einzige Ladestelle: Testet ein neuer Block ein weiteres lazy
; Modul, gehoert dessen load-Zeile HIER dazu (nicht in test_unit.lsp).
(load (strcat (getenv "DXFM_LISP") "/GeruestInsert.lsp") "GeruestInsert.lsp nicht gefunden")
(load (strcat (getenv "DXFM_LISP") "/count_sep_scan.lsp") "count_sep_scan.lsp nicht gefunden")
(load (strcat (getenv "DXFMAKRO") "/tests/test_unit.lsp")) (load (strcat (getenv "DXFMAKRO") "/tests/test_unit.lsp"))
(c:TEST_UNIT) (c:TEST_UNIT)
(command "_.QUIT") (command "_.QUIT")
+1
View File
@@ -173,6 +173,7 @@ EINGABE_ERLAUBT = {
"vfl-in-int": {"getint": 1}, "vfl-in-int": {"getint": 1},
"vfl-in-real": {"getreal": 1}, "vfl-in-real": {"getreal": 1},
"vfl-in-selection": {"ssget": 1}, "vfl-in-selection": {"ssget": 1},
"vfl-in-segmente": {"ssget": 1}, # Modus-3-Wrapper: Live-Auswahl -> Segment-Essenz (SEGS), Replay aus Journal
"vfl-winkel-live": {"getint": 1}, # livefn fuer vfl-in-value "vfl-winkel-live": {"getint": 1}, # livefn fuer vfl-in-value
# --- Wizard-Schicht (DCL); bei ssg-gui-aus greift sie nicht --- # --- Wizard-Schicht (DCL); bei ssg-gui-aus greift sie nicht ---
"vflw-zahl": {"getreal": 1}, # Fallback ohne Dialog "vflw-zahl": {"getreal": 1}, # Fallback ohne Dialog
+9 -9
View File
@@ -323,7 +323,7 @@
"comment": "Separator 3 (Nachtrag HundM05, reale Koordinate laut Anwender)", "comment": "Separator 3 (Nachtrag HundM05, reale Koordinate laut Anwender)",
"function": "insert", "function": "insert",
"block": "S-LP", "block": "S-LP",
"x": 7831.8, "x": 8549.28,
"y": 1169.5, "y": 1169.5,
"z": 2154, "z": 2154,
"rotation": 180 "rotation": 180
@@ -333,7 +333,7 @@
"comment": "Scanner zu Separator 3 (Nachtrag HundM05, reale Koordinate laut Anwender)", "comment": "Scanner zu Separator 3 (Nachtrag HundM05, reale Koordinate laut Anwender)",
"function": "insert", "function": "insert",
"block": "Scanner", "block": "Scanner",
"x": 7874.5, "x": 8598.69,
"y": 1167.7, "y": 1167.7,
"z": 2154, "z": 2154,
"rotation": 270 "rotation": 270
@@ -343,7 +343,7 @@
"comment": "Separator 4 (Nachtrag HundM05, reale Koordinate laut Anwender)", "comment": "Separator 4 (Nachtrag HundM05, reale Koordinate laut Anwender)",
"function": "insert", "function": "insert",
"block": "S-LP", "block": "S-LP",
"x": 12191.0, "x": 12619.88,
"y": 1168.2, "y": 1168.2,
"z": 2154, "z": 2154,
"rotation": 180 "rotation": 180
@@ -353,7 +353,7 @@
"comment": "Scanner zu Separator 4 (Nachtrag HundM05, reale Koordinate laut Anwender)", "comment": "Scanner zu Separator 4 (Nachtrag HundM05, reale Koordinate laut Anwender)",
"function": "insert", "function": "insert",
"block": "Scanner", "block": "Scanner",
"x": 12233.7, "x": 12669.1,
"y": 1166.4, "y": 1166.4,
"z": 2154, "z": 2154,
"rotation": 270 "rotation": 270
@@ -363,7 +363,7 @@
"comment": "Separator 5 (Nachtrag HundM05, reale Koordinate laut Anwender)", "comment": "Separator 5 (Nachtrag HundM05, reale Koordinate laut Anwender)",
"function": "insert", "function": "insert",
"block": "S-LP", "block": "S-LP",
"x": 16488.4, "x": 16785.77,
"y": 1171.7, "y": 1171.7,
"z": 2154, "z": 2154,
"rotation": 180 "rotation": 180
@@ -373,7 +373,7 @@
"comment": "Scanner zu Separator 5 (Nachtrag HundM05, reale Koordinate laut Anwender)", "comment": "Scanner zu Separator 5 (Nachtrag HundM05, reale Koordinate laut Anwender)",
"function": "insert", "function": "insert",
"block": "Scanner", "block": "Scanner",
"x": 16531.1, "x": 16839.52,
"y": 1169.8, "y": 1169.8,
"z": 2154, "z": 2154,
"rotation": 270 "rotation": 270
@@ -435,7 +435,7 @@
"block": "BTMT-Beladung", "block": "BTMT-Beladung",
"x": 5290.9, "x": 5290.9,
"y": 370.2, "y": 370.2,
"z": 0, "z": 2154,
"rotation": 179.03 "rotation": 179.03
}, },
{ {
@@ -445,7 +445,7 @@
"block": "BTMT-Beladung", "block": "BTMT-Beladung",
"x": 4261.2, "x": 4261.2,
"y": 374.2, "y": 374.2,
"z": 0, "z": 2154,
"rotation": 179.18 "rotation": 179.18
}, },
{ {
@@ -455,7 +455,7 @@
"block": "SC_Entladung", "block": "SC_Entladung",
"x": 24768.7, "x": 24768.7,
"y": 1170.3, "y": 1170.3,
"z": 0, "z": 2154,
"rotation": 0.97 "rotation": 0.97
} }
] ]