The TRO markers written by sps_skel (lib/tro_annotate.py) are blocks TRO_SYM_<type> carrying ID and TYPE as attributes. This wires them into the existing SSG_LIB mechanisms rather than adding new ones. The double-click hook and the EATTEDIT override already exist: ***DOUBLECLICK routes [INSERT] to SSG_BLOCKEDIT, and c:EATTEDIT is undefined and forwarded to the same dispatcher. So all that was missing was a branch: Blockname TRO_* -> c:TRO_EDIT (Lisp/Tro_Edit.lsp) -> dcl/tro_edit.dcl Tro_Edit.lsp follows the conventions of the other edit commands: DCL path from DXFM_DCL, ssg-start/ssg-end around the command, ssg-attrib-read and ssg-attrib-set-on for the attributes, all user-visible text via ssg-text/ ssg-textf with keys in lang/de_DE.json and en_GB.json. Implied selection is read before ssg-start, since ssg-start clears it - the same note Gefaellestrecke.lsp carries. TYPE is a picklist rather than free text so it cannot drift from the type catalogue. The list comes from Lisp/tro_types.lsp, generated on the sps_skel side by "tro_annotate.py --emit-lisp" into DXFM_LISP. If that file is absent the dialog offers the block's current type and still works. Loading is lazy: the module is not preloaded in the MNL. The menu macros and the dispatcher pull it with (ssg-ensure "Tro_Edit") on first use, and the dispatcher falls back to native EATTEDIT if the module cannot be found. ssg_load.lsp loads it for the non-menu path. Menu: SSG_LIB > TRO with "Marker bearbeiten" and "Bearbeiten (Auto)", plus an entry in the POP501 edit context menu. The dialog changes attributes only. The marker shape belongs to the block definition of the type and FB_BLOCK is derived from it, so both follow on the next tro_annotate.py run; the dialog states this and the command prints a reminder when the type was changed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Lisp – AutoLISP-Bibliotheken (SSG_LIB)
AutoLISP-Routinen für BricsCAD Pro zur automatisierten Erstellung von Förderanlagen-Layouts. Destilliert aus den historischen ACADSST-Makros (Schönenberger Systeme GmbH) und erweitert für die SSG_LIB-Befehle.
Menue einrichten und Module laden
Module werden automatisch ueber SSG_LIB.mnl geladen, sobald das Menue
einmalig in BricsCAD registriert wurde:
Einmalige Einrichtung (BricsCAD-Kommandozeile):
_MENULOAD → menu/SSG_LIB.mnu auswaehlen
BricsCAD laedt daraufhin automatisch SSG_LIB.mnl, welches alle Module
per ssg-ensure initialisiert (Kern: ssg_core/lang/dbg/dialog/layer, dann
alle Feature-Module). Details: siehe ../CLAUDE.md Abschnitt "Lademechanismus".
Manuell laden (Notfall / Entwicklung):
(load "C:/10-Develop/gitrepos/dxfmakros/Lisp/ssg_load.lsp")
ssg_load.lsp laedt alle Module in der richtigen Reihenfolge ohne Menueabhaengigkeit.
Status: noch offene Menueintraege (im Menue ausgegraut)
Die folgenden Menueintraege rufen aktuell nur eine Dummy-Funktion auf (Meldung
[DUMMY], keine Geometrie). Sie sind in menu/SSG_LIB.mnu mit ~ ausgegraut
und werden erst bei echter Implementierung wieder aktiviert. Der ~-Marker steht
am Label-Anfang, bei Submenue-Schliessern hinter dem <- ([<-~Label]).
| Menuebereich | Eintraege | Definiert in |
|---|---|---|
| ILS ▸ Zusatzmodul | BTMT-Beladung, BTMT-Entladung | SSG_LIB_Commands.lsp |
| ILS ▸ Foerderer | Durch TEF angetriebene Strecke; Kurven 90/60/30 links/rechts angetrieben Aussen + Innen (12) | SSG_LIB_Commands.lsp |
| ILS ▸ Gefaellestrecke | Kurven 90/60/30 links/rechts Gefaellestrecke (6) | SSG_LIB_Commands.lsp |
| Omniflo ▸ TEF Elemente | UmlenkR/L, AntriebL/R, TEF Gerade, KettenFoerderer (6) | OmniModulInsert.lsp |
| Omniflo ▸ Verbinder | alle OMNI_TV_* (18) |
OmniModulInsert.lsp |
| Omniflo ▸ Boegen fuer Weichen | alle OMNI_APBW_* (14) |
OmniModulInsert.lsp |
Aktiv (implementiert) sind u.a.: Kreisel, Eckrad, VarioFoerderer, EtageVarioFoerderer, Gefaellestrecke, Omniflo Boegen (APB), Aluprofil-Geraden (AP), alle Weichen (W90/W45/WP/WK/WKomb), Export (Sivas/CSV) und die Tests.
Dateien
ssg_core.lsp – Kernbibliothek
Grundfunktionen für Umgebungssicherung, Benutzerinteraktion, Layer, Blöcke, Attribute und JSON-Konfiguration. Muss als erstes geladen werden.
Umgebungssicherung:
| Funktion | Beschreibung |
|---|---|
ssg-start titel vars |
Systemvariablen sichern, CMDECHO aus, Undo-Gruppe öffnen |
ssg-end |
Systemvariablen wiederherstellen, Undo-Gruppe schließen |
ssg-errhan em |
Fehlerhandler – räumt auf und ruft ssg-end auf |
Benutzer-Abfragen:
| Funktion | Beschreibung |
|---|---|
ssg-ques text defa |
Ja/Nein-Abfrage; defa=T → Default Ja, nil → Default Nein |
ssg-emsg text |
Fehlermeldung mit optionalem Signalton (ssg-bell) |
Layer-Hilfsfunktionen:
| Funktion | Beschreibung |
|---|---|
ssg-get-layer text |
Layer eines angeklickten Objektes ermitteln |
ssg-layer-ss name typ |
Auswahlsatz aller Objekte eines Layers (optional nach Typ filtern) |
ssg-make-layer name farbe aktiv |
Layer anlegen (falls nicht vorhanden) und optional aktivieren |
ssg-ss-foreach ss func |
Iteration über Auswahlsatz – func für jeden Entity-Name |
ssg-ss-redraw ss modus |
Auswahlsatz hervorheben (3), invertieren (4) oder normal (0) |
ssg-ss-add src dest |
Auswahlsatz src zu dest hinzufügen (in-place, mit Highlight) |
Block-Operationen:
| Funktion | Beschreibung |
|---|---|
ssg-timestamp |
Zeitstempel als YYYYMMDDHHMMSS-String erzeugen |
ssg-make-blockname pt |
Eindeutigen Blocknamen aus Einfügepunkt + Zeitstempel erzeugen |
ssg-insert-block layer farbe pfad pt xs ys rot |
Layer anlegen und Block einfügen |
ssg-make-block pt rotation ss |
Auswahlsatz als Block definieren und gleich einfügen |
ILS-Blockdateien laden (2D/3D):
| Funktion | Beschreibung |
|---|---|
ssg-ils-dim-aktuell |
Gültige Dimension: *ssg-ils-dim* → DXFM_DIM → "3D" |
ssg-ils-blockname[-dim] roh [dim] |
Effektiver Blockname mit Suffix _2D/_3D |
ssg-ils-block-datei[-dim] roh [dim] |
Pfad zu data/ils/<blockname>.dwg (3D-Fallback) |
ssg-ils-block-laden[-dim] roh [dim] |
Blockdefinition sicherstellen; Rückgabe = effektiver Name |
ssg-block-refresh-reset roh |
Redefine-Sperre lösen (nil = alle Blöcke) → DWG wird neu gelesen |
Jede Block-DWG wird einmal pro Zeichnung aus der Datei neu definiert (Redefine);
danach greift eine Sperre (*ssg-block-refreshed*, siehe ssg-block-refreshed-p/
-set). Ohne sie las jede einzelne Element-Einfügung die DWG erneut von der Platte
und regenerierte alle bereits platzierten Referenzen – bei den großen 3D-Blöcken
(AS_Element_90_links_3D ~52 MB) waren Tests wie TEST_MUBEA dadurch minutenlang
beschäftigt. Wurde eine Block-DWG während der laufenden Sitzung geändert, hebt
ssg-block-refresh-reset die Sperre auf.
Attribut-Operationen:
| Funktion | Beschreibung |
|---|---|
ssg-attrib-set tag wert ersetzen |
Ein Attribut der letzten Einfügung setzen oder anfügen |
ssg-attrib-set-many alist |
Mehrere Attribute der letzten Einfügung auf einmal setzen |
ssg-attrib-set-on ent alist |
Attribute eines bestimmten INSERT-Entity setzen (nicht entlast) |
ssg-attrib-read ent |
Alle Attribute eines INSERT-Entity als Assoziationsliste lesen |
ssg-attrib-read-dwg pfad |
Block temporär einfügen, ATTRIBs lesen, Block wieder löschen |
ssg-attrib-defaults defs |
Default-Attribute aus Definitionsliste ((TAG DEFAULT) ...) erzeugen |
ssg-attrib-merge given defs |
Übergebene Attribute mit Defaults zusammenführen |
ssg-attrib-make-defs defs hoehe |
Unsichtbare ATTDEF-Entities erzeugen (für Block-Definition) |
ssg-attrib-alist-to-defs alist |
Assoziationsliste (TAG . Wert) in ATTDEF-Definitionsliste umwandeln |
ssg-attrib-by-handle handle tag wert ersetzen |
Attribut eines Blocks anhand seines Entity-Handles suchen und ändern |
Dialog-Hilfsfunktionen:
| Funktion | Beschreibung |
|---|---|
ssg-dialog-run dcl id init-fn accept-fn |
Generischer DCL-Dialog: laden, initialisieren, starten, entladen |
Zoom-Hilfsfunktionen:
| Funktion | Beschreibung |
|---|---|
ssg-zoom-save |
Aktuelle View-Größe merken |
ssg-zoom-restore |
Auf gemerkte View-Größe zurückschalten (nur wenn verändert) |
JSON-Konfigurationssystem:
Liest data/json/component_defaults.json beim Laden über ssg-load-config. Die Config wird in der globalen Variable *ssg-config* gehalten.
| Funktion | Beschreibung |
|---|---|
ssg-load-config |
Config aus $DXFM_DATA/json/component_defaults.json laden |
ssg-cfg section key |
Wert aus Config lesen, nil wenn nicht vorhanden |
ssg-cfg-or section key default |
Wert aus Config lesen, default als Fallback |
Interne Parser-Funktionen (nicht direkt aufrufen):
ssg-read-file-lines, ssg-cfg-trim, ssg-cfg-split-comma,
ssg-cfg-parse-value, ssg-cfg-parse-array, ssg-cfg-parse-kv, ssg-cfg-parse-nested
ssg_lang.lsp – Sprachumschaltung (Deutsch/Englisch)
Zentrale Textverwaltung fuer benutzersichtbare Meldungen. Kern-Modul, wird direkt nach ssg_core geladen. Wird schrittweise auf weitere Module ausgerollt (siehe doc/translate.md).
| Funktion | Beschreibung |
|---|---|
ssg-text key |
Liefert Text zu key in *ssg-lang*, Fallback de_DE, dann Key selbst |
ssg-textf key werte |
Wie ssg-text, ersetzt Platzhalter %1 %2 ... durch werte (Liste) |
Sprache umschalten: Befehl SSG_SPRACHE (Menue → Konfiguration → Sprache) setzt *ssg-lang*
live und persistiert via (setenv "DXFM_LANG" ...). Default ohne DXFM_LANG ist "de_DE".
ssg_dbg.lsp – Debug-Logging
Dateibasiertes Debug-Log mit gepuffertem Schreiben. dbgf/dbg/dbgmsg puffern im Speicher, dbgreturn flusht auf die Platte (open/append/close).
| Funktion | Beschreibung |
|---|---|
dbgopen filename envvar |
Debug-Datei öffnen (Verzeichnis via Umgebungsvariable, z.B. "DXFM_LOG") |
dbgclose |
Puffer schreiben und Datei schließen |
dbgon |
Debug-Ausgabe aktivieren |
dbgoff |
Debug-Ausgabe deaktivieren (verhindert Dateianlage wenn vor dbgopen) |
dbgf funcname |
Funktionseintritt loggen, Einrückung erhöhen |
dbgreturn retval |
Funktionsaustritt loggen, Puffer flushen, Rückgabewert durchreichen |
dbg varname |
Variable oder Wert loggen: (dbg 'x) oder (dbg wert) |
dbgmsg text |
Freitext-Nachricht loggen |
dbgp text |
Alias für dbgmsg |
dbgflush |
Puffer sofort schreiben (ohne dbgreturn, z.B. in Startup-Code) |
Beispiel:
(dbgopen "test.dbg" "DXFM_LOG")
(dbgf "meine-funktion")
(dbg 'x)
(dbgreturn ergebnis) ; schreibt Puffer auf Platte
(dbgclose)
ssg_dialog.lsp – Dialog-Pattern für Modul-Einfüge-Makros
Kapselt das wiederkehrende DCL-Muster: Dialog mit Vorschaubild, Radio-Auswahl, Block einfügen, Attribute setzen. Setzt ssg_core.lsp voraus.
| Funktion | Beschreibung |
|---|---|
ssg-dia-show-slide tile lib name |
Vorschau-Slide im image-Tile anzeigen (Hintergrundfarbe aus Config: infrastruktur/dialog_preview_bg_color) |
ssg-dia-update-preview tile idx lib liste |
Vorschau anhand Index-String aus Radio-/List-Tile aktualisieren |
ssg-modul-dialog dcl id sel-tile start-val prev-tile lib slides extra-init extra-read |
Vollständiger Modul-Dialog: lädt DCL, setzt Tiles, verknüpft Vorschau, liefert gewählten Index |
ssg-insert-modul layer farbe pfad attribs rotation |
Standardsequenz: Layer anlegen → Punkt abfragen → Block einfügen → Attribute setzen |
Die Datei enthält außerdem ein auskommentiertes Template für neue c:BEFEHL-Definitionen nach dem SSG-Muster.
ssg_layer.lsp – Interaktive Layer-Verwaltung
Benutzerfreundliche BricsCAD-Befehle für Layer-Operationen. Alle Befehle sichern die Umgebung über ssg-start/ssg-end. Setzt ssg_core.lsp voraus.
Hilfsfunktionen:
| Funktion | Beschreibung |
|---|---|
ssg-layer-list-all |
Alle Layernamen als Liste |
ssg-layer-info-all |
Alle Layer als Assoziationslisten (Name, Farbe, Linientyp, Flags) |
ssg-layer-visible-p name |
Sichtbarkeitsstatus prüfen (T = sichtbar) |
ssg-layer-safe-p name gruppe |
Prüfen ob Layer ausgeschaltet werden darf (nicht aktiver Layer) |
ssg-layer-group name |
Gruppen-Kennung aus Layernamen extrahieren (Position 2–3) |
ssg-layer-rename-group name neu |
Neuen Layernamen mit geänderter Gruppen-Kennung berechnen |
ssg-layer-group-move alt neu |
Alle Objekte einer Layergruppe auf neue Gruppen-Kennung verschieben |
BricsCAD-Befehle:
| Befehl | Beschreibung |
|---|---|
c:SSG-LayerSet |
Layer des angeklickten Objektes als aktiven Layer setzen |
c:SSG-LayerInfo |
Layer des angeklickten Objektes anzeigen |
c:SSG-LayerMove |
Auswahlsatz auf Layer eines anderen Objektes verschieben |
c:SSG-LayerCopy |
Auswahlsatz kopieren und Kopie auf Ziel-Layer legen |
c:SSG-LayerMoveType |
Alle Objekte des gleichen Typs auf dem gleichen Layer verschieben |
c:SSG-LayerOff |
Layer durch Anklicken ausschalten (Schleife bis Enter) |
c:SSG-LayerFreeze |
Layer durch Anklicken einfrieren (Schleife bis Enter) |
c:SSG-FreezeAll |
Alle Layer außer einem einfrieren |
c:SSG-ThawAll |
Alle Layer auftauen |
c:SSG-LayerOnAll |
Alle ausgeschalteten Layer einschalten |
c:SSG-LayerDelete |
Layer mit Sicherheitsabfrage löschen (Objekte + Layer) |
ssg_load.lsp – Laderoutine
Lädt alle Module in der richtigen Reihenfolge. Ermittelt den Pfad automatisch über findfile. Ruft direkt nach ssg_core.lsp die Funktion ssg-load-config auf, damit die JSON-Konfiguration für alle nachfolgenden Module verfügbar ist.
VarioFoerderer.lsp – Vario-Förderer
Kompatibilitäts-Wrapper, der vf_core.lsp lädt (Plugin-Dispatcher-Architektur; lädt seinerseits vf_standard.lsp, vf_etage.lsp, vf_linienzug.lsp). Parametrischer Generator für ILS Vario-Förderanlagen. Liest Block-Geometrie aus einer DWG-Bibliothek (DXFM_BLOCKS). Alle Konstanten (Abstände, Winkel, Skalierung) werden via ssg-cfg-or aus component_defaults.json (Sektion vario) gelesen.
Befehl: c:FOERDERANLAGE / c:VarioFoerderer – wählt zuerst den Anlagen-Typ (Standard/Etage/Linienzug):
- Typ „standard“ + Werteingabe: läuft über den DCL-Dialog
dcl/variofoerderer.dcl(zwei Dialoge,vfs-standard-dialog-ablaufinvf_standard.lsp): Dialog 1 (vfs-dialog-eingabe-basis) erfasst deltaL, deltaH, Förderrichtung (Auf/Ab), Einfügehöhe, Fahrtrichtung und Seite; anschließend berechnetberechne-standarddie gültigen Winkel-Varianten, und Dialog 2 (vfs-dialog-winkel-verteilung) lässt Winkel und L_GF-Verteilung (vorne/hinten) wählen. Startpunkt (X/Y) wird wie bei der Gefällestrecke vorher in der Zeichnung gepickt. - Typ „standard“ + 3D-Linie, sowie Typ „etage“ und „linienzug“ bleiben vollständig interaktiv (Kommandozeile), unverändert gegenüber dem bisherigen Ablauf.
Bearbeiten (c:VARIOFOERDERER_EDIT): Doppelklick auf einen VF_n-Block – bzw.
Rechtsklick-Kontextmenü/SSG_BLOCKEDIT – öffnet dieselben Dialoge, vorbelegt mit den
aktuellen Parametern, und baut die Strecke bei OK am selben Startpunkt neu auf (neue
VF_n-Nummer). Da VF_n-Blöcke auch von Etage- und Linienzug-Anlagen erzeugt werden
(deren komplexere Geometrie ein einfacher Neuaufbau zerstören würde), markiert
vfs-xdata-schreiben nur per Standard-Dialog erstellte Blöcke mit XDATA (App
SSG_VF_EDIT, Fahrtrichtung hz); Blöcke ohne diese Markierung weist
c:VARIOFOERDERER_EDIT mit einer Meldung ab, statt sie zu verändern. Winkel und L_GF1
werden aus den Sivas-Attributen (VF_WINKEL, L_GF_m) vorbelegt (Verteilung stets als
„Eigene Werte“ mit dem exakten aktuellen L_GF1).
Wichtige interne Funktionen (werden auch von Gefaellestrecke.lsp genutzt):
| Funktion | Beschreibung |
|---|---|
init-bibliothek |
Block-Bibliothek aus DWG laden und Geometriedaten extrahieren |
ensure-block-loaded name |
Block aus Bibliothek in aktives Dokument laden (einmalig) |
insert-block-by-ks name ks-nr pt |
Block anhand Koordinatensystem-Punkt einfügen |
insert-inclined-scaled-block name laenge winkel pt |
Geneigten, skalierten Block einfügen |
insert-rotated-block-with-ks name rot pt |
Rotierten Block mit KS-Offset einfügen |
get-line-start-end-points ename |
Start- und Endpunkt einer LINE-Entity ermitteln |
punkt-differenz p1 p2 |
Differenzvektor zweier Punkte |
Gefaellestrecke.lsp – Gefällestrecken-Generator
Erzeugt eine einfache Förderanlage mit festem Gefällewinkel:
AUS-Element → Staustrecke (skaliert) → Separator → EIN-Element
Setzt VarioFoerderer.lsp voraus (nutzt dessen init-bibliothek, insert-block-by-ks und weitere Hilfsfunktionen). Alle Defaults (default_delta_l, default_winkel, separator_breite) kommen aus Config-Sektion gefaelle.
Befehl: c:GEFAELLESTRECKE
Drei Modi:
- Modus 1: Startpunkt (X/Y) picken, restliche Parameter über den DCL-Dialog
dcl/gefaellestrecke.dcleingeben (ΔL, Einfügehöhe Z, Gefällewinkel, Fahrtrichtung, AUS-/EIN-Element je Winkel 30/90 und Seite links/rechts). Die Einfügehöhe aus dem Dialog setzt die Z-Koordinate des Startpunkts. - Modus 2: Bestehende 3D-Linie wählen (ΔL und Fahrtrichtung werden daraus berechnet), restliche Parameter interaktiv über die Kommandozeile.
- Modus 3: Linienzug aus LINE-/ARC-Objekten; ARC-Segmente werden durch Gefällebogen-Blöcke ersetzt, Winkel aus ΔH und Geometrie berechnet.
Dialog-Hilfsfunktionen: gf-dialog-eingabe (lädt/liest den DCL-Dialog, liefert
(deltaL winkel hz-winkel as-seite es-seite einfuegehoehe as-winkel es-winkel dim geruest-einzelmodul geruest-typ) oder nil bei Abbruch) und gf-modus12-abschluss
(gemeinsamer Abschluss für Modus 1+2: setzt die Element-Masse per
vf-set-as-masse/vf-set-es-masse, berechnet die Staustreckenlänge,
zeigt die Zusammenfassung und ruft gefaellestrecke-einfuegen).
Die AUS-/EIN-Element-Varianten (30/90 Grad, links/rechts) liegen als DWG-Blöcke
AS_Element_<30|90>_<links|rechts> bzw. ES_Element_… flach in data/ils/ (Dimension
im Dateinamen als Suffix _2D/_3D, aufgelöst über ssg-ils-block-datei); ihre Maße
werden direkt aus dem Block gemessen (vf-element-masse), es gibt dafür keine JSON-Tabelle.
Bearbeiten (c:GEFAELLESTRECKE_EDIT): Doppelklick auf einen Gefällestrecken-Block
(GF_n) – bzw. Rechtsklick-Kontextmenü/SSG_BLOCKEDIT – öffnet denselben DCL-Dialog,
vorbelegt mit den aktuellen Parametern, und baut die Strecke bei OK am selben Startpunkt
neu auf. Damit Fahrtrichtung und Element-Winkel (30/90) verlustfrei rekonstruiert werden,
schreibt gefaellestrecke-einfuegen diese beim Erstellen als XDATA (App SSG_GF_EDIT,
Helfer gf-xdata-schreiben/gf-xdata-lesen) auf das GF_n-INSERT – bewusst nicht in
den Sivas-Attributsatz, damit der Export unverändert bleibt. Ältere Blöcke ohne XDATA
werden aus den Attributen vorbelegt (Fahrtrichtung 0°, Element-Winkel 90° als Vorgabe).
Der Neuaufbau läuft über gf-rebuild (nicht-interaktiv, ohne Konsolen-Rückfrage).
export.lsp – Export-Funktionen
Sammelt INSERT-Blöcke aus der Zeichnung und exportiert sie als JSON oder ruft Python-Skripte für den Sivas-Export auf.
| Befehl / Funktion | Beschreibung |
|---|---|
c:EXPORTSIVAS |
Alle Blöcke als JSON sammeln und export_sivas.py aufrufen (Sivas-CSV) |
c:EXPORTCSV |
Alle Blöcke als JSON sammeln und export_csv.py aufrufen (einfache Item-Liste, inkl. Omniflo Bogen/Weiche/Gerade und abschließender "Omniflo Sum"-Zeile) |
csv:collect-export-blocks |
Alle INSERT-Blöcke der Zeichnung als JSON-Array sammeln |
csv:run-export py-skript json-datei output-csv |
Python-Skript mit gesammelten Daten aufrufen |
csv:read-attribs ename |
Attribute eines INSERT als JSON-Objekt lesen |
c:OMNI_UPDATE_ATTRIBS |
HOEHE/DREHUNG aller Omniflo-Elemente aus der Zeichnung aktualisieren |
Der Omniflo-CSV-Export (Bogen/Weiche/Gerade, inkl. Merkmale-Aufbau und Sum-Zeile) läuft
seit Entfernung von c:OMNI_EXPORT ausschließlich über c:EXPORTCSV → lib/export_csv.py
(build_bogen_merkmale, build_weiche_merkmale, build_gerade_merkmale, build_omni_sum_merkmale).
Ausgabeordner: c:EXPORTSIVAS/c:EXPORTCSV schreiben immer nach DXFM_RESULTS.
TEST_EXPORT_ALL (siehe tests/test_export_all.lsp) leitet waehrend des Testlaufs stattdessen
nach DXFM_TESTOUT um, über die reine Lisp-Sitzungsvariable *export-test-override* (wird von
csv:run-export zusaetzlich zu DXFM_RESULTS abgefragt und danach garantiert wieder auf nil
zurueckgesetzt, auch bei einem Fehler, ueber vl-catch-all-apply).
Wichtig: Der Umweg über *export-test-override* ist bewusst gewaehlt und kein
(setenv "DXFM_RESULTS" ...). In BricsCAD/AutoCAD schreibt (setenv ...) nicht nur in den
Windows-Prozess, sondern dauerhaft in die Profil-Registry der Anwendung – der Wert überlebt
damit jeden Neustart und jedes setenv.bat, bis er erneut per (setenv) überschrieben wird.
Ein einziger (setenv "DXFM_RESULTS" ...)-Aufruf (z. B. ein frueherer, unvollstaendiger
Testlauf) würde DXFM_RESULTS für alle künftigen Sitzungen unwiderruflich auf den Testordner
umbiegen. Falls das bereits passiert ist, hilft nur ein manuelles
(setenv "DXFM_RESULTS" "<richtiger Pfad>") in BricsCAD, um den Profil-Wert zu korrigieren.
tests.lsp – Testbefehle
Testbefehle für die Python-Integration. Export-Funktionen wurden in export.lsp ausgelagert.
| Befehl | Beschreibung |
|---|---|
c:CALLPYTHON |
testpycall.py via _.SHELL aufrufen und Rückgabe einlesen (Integrationstest LISP↔Python) |
Tests
Zwei Test-Ebenen, beide gesteuert über bin\run_tests.bat (Details und
Verzeichnisstruktur: ../tests/README.md):
- LISP-Unit-Tests (
tests/test_unit.lsp) – prüfen reine Standardfunktionen direkt in AutoLISP, ohne Zeichnung. - Zeichnungsbasierte Integrationstests (
tests/test_*.lsp+tests/test_*.py) – erzeugen Blöcke in BricsCAD und validieren das Ergebnis anschließend mit pytest/ezdxf. Module:test_kreisel,test_foerderer,test_linienzug,test_gefaellestrecke,test_omniflo,test_omniflo_streckeundtest_mubea(Gesamtmodell: Kreisel + Vario + Gefällestrecken + Separatoren austests/testdata/mubea.jsonin einer Zeichnung; überSSG_RUN_ALL_TESTSbzw. Menü „Tests → Mubea Gesamtmodell").
Welche JSON-Angaben pro Element-Typ nötig sind (abgeleitet aus den
Produktions-Routinen, nicht aus den Test-Wrappern), dokumentiert
../tests/testdata/object_data.md –
inkl. der Unterscheidung scriptbar vs. nur-interaktiv.
LISP-Unit-Tests (test_unit.lsp)
Testen reine Hilfsfunktionen – solche, die nur aus ihren Argumenten ein
Ergebnis berechnen und keine Zeichnungsdatenbank, Auswahlsätze, Dialoge oder
Blockdateien brauchen (String-, Zahl-, Vektor-, Listen- und Alist-Helfer).
Abgedeckt werden Funktionen aus ssg_core, ssg_lang, ssg_id, vf_core,
export, Gefaellestrecke, OmniModulInsert und KreiselInsert.
Aufruf über die Kommandozeile (startet BricsCAD headless):
bin\run_tests.bat --lisp
run_tests.bat --lisp löscht das alte Ergebnis, startet BricsCAD mit
tests/test_unit.scr (lädt ssg_load.lsp + test_unit.lsp, ruft TEST_UNIT
auf und beendet BricsCAD), gibt danach tests/output/unit_results.txt aus und
liefert Exit-Code 1, wenn ein Test fehlschlägt oder kein Report entsteht.
Aufruf direkt in BricsCAD (SSG_LIB geladen):
(load (strcat (getenv "DXFMAKRO") "/tests/test_unit.lsp"))
TEST_UNIT
Aufbau eines Testfalls. test_unit.lsp enthält ein Mini-Framework: jeder
Testfall ruft eine Funktion auf und vergleicht das Ergebnis gegen einen
erwarteten Wert. Die Assertion-Helfer zählen Treffer/Fehler mit und schreiben
je eine PASS/FAIL-Zeile:
| Helfer | Vergleich |
|---|---|
tu-eq name erwartet ist |
exakt (equal) – Strings, Ganzzahlen, Listen davon |
tu-eqf name erwartet ist |
numerisch mit Toleranz 1e-6 – Fliesskomma, auch verschachtelte Listen |
tu-true name ist |
Ergebnis ist nicht nil |
tu-nil name ist |
Ergebnis ist nil |
Beispiel – Formatierung einer ID und ein Vektor-Kreuzprodukt:
(tu-eq "ssg-id-format/1" "0001" (ssg-id-format 1))
(tu-eqf "vec3-cross/x-cross-y" '(0.0 0.0 1.0) (vec3-cross '(1.0 0.0 0.0) '(0.0 1.0 0.0)))
Die Testfälle sind in tu-tests-<modul>-Funktionen gruppiert; c:TEST_UNIT
ruft alle nacheinander auf, gibt eine Zusammenfassung
(Gesamt / PASS / FAIL) aus und schreibt den Report unit_results.txt mit
einer abschließenden Zeile RESULT: OK bzw. RESULT: FAIL (die
run_tests.bat auswertet).
Neuen Testfall ergänzen: in der passenden tu-tests-<modul>-Funktion eine
tu-eq/tu-eqf/tu-true/tu-nil-Zeile hinzufügen. Für ein neues Modul eine
eigene tu-tests-<modul>-Funktion anlegen und in c:TEST_UNIT aufrufen.
Getestet werden sollten nur Funktionen ohne Zeichnungs-/Dialog-Umfeld – alles
mit ssget/entget/entmake/command/vla-*/entsel/DCL gehört in die
zeichnungsbasierten Integrationstests.
KreiselInsert.lsp – ILS Kreisel und Eckrad
AutoLISP-Implementierung fuer ILS Kreisel und Eckrad. Enthaelt alle Befehle fuer Einfuegen, Verbinden, Neuzeichnen und Bearbeiten.
| Befehl | Beschreibung |
|---|---|
c:KreiselInsert |
Kreisel-Block an Punkt einfuegen (mit Dialog fuer Parameter) |
c:KreiselConnect |
Zwei Punkte waehlen, Kreisel dazwischen einfuegen |
c:KreiselRedraw |
Bestehenden Kreisel mit neuen Parametern neu zeichnen |
c:KreiselQuick |
Schnelleinfuegung mit Standardwerten |
c:KreiselEdit |
Kreisel-Attribute ueber DCL-Dialog bearbeiten |
c:KreiselParams |
Aktuelle Kreisel-Parameter anzeigen |
c:KreiselLabelSetup |
Schriftart, Hoehe und Farbe fuer Kreisel-Beschriftung konfigurieren |
c:KreiselLabelPos |
Label-Offset (dx/dy) setzen |
c:KreiselLabelHoehe |
Label-Hoehe aendern |
c:ILS_Eckrad |
Eckrad an Tangentenpunkt einfuegen |
OmniModulInsert.lsp – Omniflo-Komponenten
AutoLISP-Implementierung fuer alle Omniflo-Komponenten: Boegen, Weichen, Verbinder, Transferwagen. Liest JSON-Daten aus omniflo_boegen.json und omniflo_weichen.json. Alle Konstanten via ssg-cfg-or aus Config.
| Befehlsgruppe | Befehle | Beschreibung |
|---|---|---|
| Daten | OMNI_LOAD, OMNI_INFO_BOGEN, OMNI_INFO_WEICHE |
JSON-Daten laden und abfragen |
| Boegen | OMNI_APB_630_90, OMNI_APB_550_675, OMNI_APB_630_45, OMNI_APB_550_225, OMNI_APB_650_180 |
Omniflo-Boegen ueber DCL-Dialog einfuegen |
| Weichen | OMNI_W90_*, OMNI_W45_*, OMNI_WP_*, OMNI_WK_*, OMNI_WKomb_* |
Omniflo-Weichen (Einfach/Doppel/Dreiwege, Delta, Stern) |
| Geraden | OMNI_AP60, OMNI_AP110, OMNI_APG110 |
Omniflo-Geradenstuecke |
| TEF ⚠️ | OMNI_TEF_UmlenkR/L, OMNI_TEF_AntriebL/R, OMNI_TEF_Gerade, OMNI_KettenFoerderer |
Dummy (noch nicht implementiert, im Menue ausgegraut) |
| Transferwagen ⚠️ | OMNI_TV_* |
Dummy (noch nicht implementiert, im Menue ausgegraut) |
| Bogenweichen ⚠️ | OMNI_APBW_* |
Dummy (noch nicht implementiert, im Menue ausgegraut) |
| Bearbeiten | OMNI_EDIT |
Omniflo-Block selektieren und Attribute bearbeiten |
SSG_LIB_Commands.lsp – Dummy-Befehle, Sensoren, Konfiguration und SSG_BLOCKEDIT
Sammeldatei fuer Menue-Befehle: teils Platzhalter fuer noch nicht implementierte
Eintraege, teils bereits echte Funktionalitaet (Sensoren, Konfiguration,
Doppelklick-Dispatcher). Alle noch offenen Dummy-Menueintraege sind in
menu/SSG_LIB.mnu mit ~ ausgegraut (siehe Abschnitt
"Status: noch offene Menueintraege").
Sensoren einfuegen (real, ueber ils-insert-sensor → wiederholtes _.INSERT
mit interaktivem Einfuegepunkt/Rotation, Blockdatei via ssg-ils-block-datei):
| Befehl | Block | Beschreibung |
|---|---|---|
ILS_SCANNER_INSERT |
Scanner |
Scanner wiederholt einfuegen (ENTER/ESC beendet) |
ILS_SEPARATOR_INSERT |
Separator_SP |
Separator wiederholt einfuegen (Menue „Separator einfuegen") |
Registrierte Dummy-Befehle:
| Gruppe | Befehle |
|---|---|
| ILS Zusatzmodule | ILS_BTMT_Beladung, ILS_BTMT_Entladung, ILS_ETF_Etagenfoerderer, ILS_TEF_Strecke |
| ILS Kurven angetrieben Aussen | ILS_K90LA, ILS_K90RA, ILS_K60LA, ILS_K60RA, ILS_K30LA, ILS_K30RA |
| ILS Kurven angetrieben Innen | ILS_K90LI, ILS_K90RI, ILS_K60LI, ILS_K60RI, ILS_K30LI, ILS_K30RI |
| ILS Gefaellestrecken-Kurven | ILS_Gefaellestrecke, ILS_GK90L, ILS_GK90R, ILS_GK60L, ILS_GK60R, ILS_GK30L, ILS_GK30R |
FOERDERANLAGEist nicht hier definiert – dieser Befehl ist inVarioFoerderer.lspimplementiert.
Konfigurationsbefehle (Menue → Konfiguration):
| Befehl | Beschreibung |
|---|---|
SSG_DIM_SWITCH |
Schaltet zwischen 2D/3D-Bloecken um (setzt DXFM_DIM, DXFM_BLOCKS, DXFM_OMNIFLO) |
SSG_SPRACHE |
Oeffnet Dialog sprache.dcl, setzt *ssg-lang* und persistiert via DXFM_LANG (siehe ssg_lang.lsp) |
SSG_BLOCKEDIT – Doppelklick/Kontextmenue-Dispatcher:
Erkennt den Blocktyp des selektierten INSERT-Blocks und ruft den passenden Bearbeitungsbefehl auf:
| Blockmuster | Aktion |
|---|---|
KREISEL_* |
KreiselEdit |
ECKRAD_* |
BEDIT (Block-Editor) |
| Omniflo (TEILEART-Attribut, AP110*, numerisch) | OMNI_EDIT |
| Sonstige | BEDIT (Block-Editor) |