Files
dxfmakros/Lisp
Michael StanglandClaude Sonnet 5 42309c77d2 export_csv: Separator-TYP-XDATA vom aeusseren Trennsteg statt vom geteilten Symbol lesen
Der letzte Fix markierte bauzeitig korrekt den aeusseren
Staustrecke_Separator_SP_300_mm-Trennsteg, csv:sep-proxies-erzeugen las den
Marker aber vom inneren Separator_SP-Symbol - das ist als Teil EINER
gemeinsamen DWG-Blockdefinition zwischen ALLEN Separatoren der Zeichnung
geteilt und kann keinen individuellen Marker tragen. Export sucht jetzt
zusaetzlich den aeusseren Trennsteg (eigene Instanz je Separator) und
ordnet ihn dem Symbol ueber die naechstliegende Weltposition zu.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-24 09:27:57 +02:00
..

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 (strcat (getenv "DXFMAKRO") "/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 ▸ 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, BTMT Be-/Entladung, Scanner/Separator, 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)

Ziel-Layer je Baugruppe (cfg/layer.cfg):

Für Baugruppen, die ein Makro aus Einzelteilen zusammenbaut (KREISEL_n, ECKRAD_n, VF_n, GF_n) und für die selbst gezeichnete Hilfsgeometrie (Kreisel-Tangenten, PIN-Linien, Beschriftungstexte) gibt es keine Blockdatei in data/ und damit kein LAYER-Attribut – ihr Ziel-Layer und die Layerfarbe kommen aus cfg/layer.cfg. Nur eingefügte Blöcke nehmen dagegen ihr LAYER-Attribut als Vorgabe. Alle Funktionen liefern nil, wenn nichts konfiguriert ist; der Layer bleibt dann unangetastet (kein Fehler bei fehlender cfg/layer.cfg).

Funktion Beschreibung
ssg-layer-cfg-load cfg/layer.cfg einmalig (lazy) einlesen, Rückgabe ini-Alist oder nil
ssg-layer-cfg-reset Ladezustand verwerfen (cfg in laufender Sitzung neu einlesen, Tests)
ssg-layer-cfg-eintrag ini sektion key Reine Aufl./Zerlegung "Layername, Farbe" → (layer . farbe); Farbe fehlt → "7"
ssg-layer-eintrag key Ziel-Layer einer Baugruppe: [ils_<Dimension>] → [allgemein] → nil
ssg-layer-anlegen key aktiv Layer der Baugruppe mit cfg-Farbe anlegen, optional aktivieren; Rückgabe Layername
ssg-layer-setzen ent key Blockreferenz auf den Ziel-Layer ihrer Baugruppe legen (Layer wird angelegt)
ssg-layer-farbe-fuer-name name Farbe zu einem Layernamen (Abschnitt [farben], sonst passender Baugruppen-Wert, sonst "7")
ssg-block-layer-vorgabe ent blockname attribs Layer einer nur eingefügten Referenz aus der Block-Vorgabe: LAYER-Attribut → ssg-ils-block-ebene → nichts

Die Farbe greift nur bei der Neuanlage eines Layers – ssg-make-layer lässt bestehende Layer unverändert. Layer, die eine Blockdatei aus data/ils selbst mitbringt (z. B. ILS_SP), legt BricsCAD bereits beim Einfügen des Blocks mit der Farbe aus der DWG an; dort wirkt die cfg-Farbe nicht, der Layername des Wrappers aber schon.

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)

Zentrale Debug-Schalter (default je nach Schalter)

Damit dbg*-Aufrufe dauerhaft im Code stehen bleiben können, ohne im Normalbetrieb ungeplant .dbg-Dateien zu erzeugen, gibt es eine zentrale Schalter-Sammlung (*dbg-schalter*, Symbol → 0/1). Eine Routine öffnet ihre Debug-Datei nur, wenn ihr Schalter eingeschaltet ist. Unbekannte/neue Schalter gelten implizit als 0/aus; die VF-Linienzug-Schalter unten sind bewusst mit Default 1/an vorregistriert (siehe Tabelle).

Funktion Beschreibung
dbg-schalter-on name Schalter name einschalten (1)
dbg-schalter-off name Schalter name ausschalten (0)
dbg-schalter-p name T wenn eingeschaltet, sonst nil
dbg-schalter-open name filename envvar Öffnet die Datei nur wenn Schalter=1; gibt T/nil zurück
dbg-schalter-liste Alle bekannten Schalter + Zustand ausgeben

Bekannte Schalter:

Schalter Default Modul Loggt nach
"vfl-modus1" AN (1) Linienzug Modus 1 (manuelle Eingabe, vf-linienzug-modus) DXFM_LOG/vfl_modus1.dbg
"vfl-modus2" AN (1) Linienzug Modus 2 (3D-Objekte + Ziel-Höhe, vf-linienzug-modus2) DXFM_LOG/vfl_modus2.dbg
"vfl-modus3" AN (1) Linienzug Modus 3 (Vorwärts-Nachbau, vf-linienzug-modus3) DXFM_LOG/vfl_modus3.dbg

Alle drei Modi loggen automatisch jede interaktive Eingabe, weil sie durchgehend über die gemeinsamen Wrapper vfl-in-point/-string/-real/ -int/-value/-selection gehen, die zentral in vfl-journal-record protokolliert werden – keine einzelne Aufrufstelle im Bau-Ablauf musste dafür angefasst werden. Da der Default AN ist, wird bei jedem Aufruf von FOERDERANLAGE → linienzug ohne weiteres Zutun mitgeloggt; bei Bedarf einzeln abschaltbar mit (dbg-schalter-off "vfl-modus1") usw.

;; Standardmaessig bereits an - nur zur Kontrolle:
(dbg-schalter-liste)
;; ... FOERDERANLAGE -> linienzug -> Modus 1/2/3 durchspielen ...
;; -> Datei DXFM_LOG/vfl_modus<N>.dbg enthält Session + alle Eingaben
(dbg-schalter-off "vfl-modus1")   ; bei Bedarf einzeln abschalten

Muster in einer Routine:

(setq dbg-an (dbg-schalter-open "vfl-modus3" "vfl_modus3.dbg" "DXFM_LOG"))
(if dbg-an (dbgmsg "FRAGE: ..."))
;; ... get* ...
(if dbg-an (progn (dbgmsg "ANTWORT:") (dbg 'wert) (dbgflush)))
;; am Ende:
(if dbg-an (progn (dbgreturn ergebnis) (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-ablauf in vf_standard.lsp): Dialog 1 (vfs-dialog-eingabe-basis) erfasst deltaL, deltaH, Förderrichtung (Auf/Ab), Einfügehöhe, Fahrtrichtung und Seite; anschließend berechnet berechne-standard die 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.dcl eingeben (Δ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; legt vorher fuer in VF_n/GF_n/KREISEL_n verpackte Separator_SP_2D/_3D-Sub-INSERTs (Sensor-Symbol; siehe ssg-collect-nested-inserts in ssg_core.lsp - NICHT der interne Staustrecke_Separator_SP-Trennsteg) je eine echte, temporaere Kopie an derselben Stelle an (csv:sep-proxies-erzeugen, Position/Drehung/Skalierung ueber alle Verschachtelungsebenen hinweg identisch zum Vorbild - kein kosmetischer Versatz mehr, der die geometrische Sensor-Zuordnung verfaelschen wuerde), damit sie ganz normal ueber ssg-id-check-all/csv:collect-export-blocks erfasst werden, und entfernt sie danach wieder (csv:sep-proxies-loeschen). Die verpackten AS-/ES-Schleuselemente derselben Kette bekommen stattdessen einen DAUERHAFTEN Marker (csv:asmark-aktualisieren, siehe unten); beide Wege teilen sich die Platzierungsrechnung (csv:nested-welt-records + csv:block-einfuegen-welt). Reihenfolge ist wesentlich: erst alle Kopien aus allen Wrappern erzeugen, dann ssg-id-check-all (vergibt IDs oberhalb des globalen Maximums), dann csv:sep-proxies-zuordnung-setzen und csv:sepliste-ids-setzen
csv:sep-proxies-zuordnung-setzen proxy-liste ZUORDNUNG jeder Separator-Kopie auf die ID ihres Wrapper-Blocks setzen (ein in VF_n mit ID 0010 verpackter Separator erhaelt ZUORDNUNG 0010) und die Zuordnung ueber *cs-sep-fix-by-handle* fuer cs-zuordnung-lauf festnageln - fuer verpackte Separatoren ist der Carrier bekannt und wird nicht per Boundingbox geraten. Muss NACH ssg-id-check-all laufen, da ein frisch gebauter Wrapper vorher noch keine ID hat
csv:asmark-aktualisieren AS-/ES-Kettenenden sichtbar machen: setzt an jedes verpackte AS_Element*/ES_Element* einer VF_n/GF_n-Kette ein kleines Rechteck (Block ZZ_ASES_MARKER, Groesse aus cfg/export.cfg [Marker], Layer aus cfg/layer.cfg ases_marker), das nach dem Export stehen bleibt. Unsichtbare Attribute ID/ZUORDNUNG/TYP/LFDNR/ELEMENT/ARTINR; die ID fuellt ssg-id-check-all und der naechste Export uebernimmt sie, ein AS/ES behaelt seine TeileId also ueber Laeufe hinweg. Baut bei jedem Export alle Marker neu auf (Wiederfinden ueber ZUORDNUNG+TYP+LFDNR) und entfernt verwaiste
csv:asmark-ids-setzen marker-liste Nach ssg-id-check-all: ZUORDNUNG (Wrapper-ID) am Marker nachtragen und seine ID am sepliste-Eintrag vermerken
csv:sepliste-ids-setzen proxy-liste ID jeder Kopie (Separator UND AS/ES, beide Listen zusammen) an ihren sepliste-Eintrag des Wrappers haengen; Ergebnis in *csv-sepliste-id-by-handle* (Key = Wrapper-Handle), von csv:block-to-json als Feld id je Eintrag geschrieben. Muss NACH ssg-id-check-all laufen
csv:sepliste-paare eintraege proxies Zuordnung sepliste-Eintrag -> Kopie: nur innerhalb desselben Typmarkers (AS/ES/SEP) und exklusiv (kuerzester Abstand zuerst, danach sind Eintrag und Kopie verbraucht). Damit verschiebt EIN fehlerhafter Koordinatenwert in der sepliste keine anderen Zuordnungen. Reine Rechenfunktion, getestet in tests/test_unit.lsp
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):

  1. LISP-Unit-Tests (tests/test_unit.lsp) – prüfen reine Standardfunktionen direkt in AutoLISP, ohne Zeichnung.
  2. 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_strecke und test_mubea (Gesamtmodell: Kreisel + Vario + Gefällestrecken + Separatoren aus tests/testdata/mubea.json in einer Zeichnung; über SSG_RUN_ALL_TESTS bzw. 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 – BTMT, Sensoren, Dummy-Befehle, Konfiguration und SSG_BLOCKEDIT

Sammeldatei fuer Menue-Befehle: teils Platzhalter fuer noch nicht implementierte Eintraege, teils bereits echte Funktionalitaet (BTMT Be-/Entladung, 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")

BTMT Be-/Entladestation einfuegen (real, ueber ils-insert-station):

Befehl ROH-Block Datei (3D) Beschreibung
ILS_BTMT_Beladung BTMT-Beladung BTMT-Beladung_3D.dwg Beladestation wiederholt einfuegen (ENTER/ESC beendet)
ILS_BTMT_Entladung SC_Entladung SC_Entladung_3D.dwg Entladestation wiederholt einfuegen (ENTER/ESC beendet)

Ablauf je Einfuegung: Einfuegepunkt abfragen → _.INSERT mit interaktiver Drehung (pause) → Layer aus dem LAYER-Attribut bzw. der eigenen Ebene der Blockdefinition setzen (ils-block-layer-setzen, gleiche Konvention wie omni:insert-dxf/tefl-insert-element) → ssg-id-generate, falls der Block ein ID-Attribut fuehrt.

Die Blockdefinition kommt aus ssg-ils-block-laden (aktuelle Dimension *ssg-ils-dim* → DXFM_DIM → 3D, DWG wird nur einmal pro Zeichnung gelesen); eingefuegt wird der zurueckgegebene dim-suffigierte Blockname. Fehlt die Datei der gewuenschten Dimension, greift der 3D-Fallback mit Warnung – aktuell fuer BTMT-Beladung_2D.dwg; sobald diese Datei in data/ils/ liegt, wird sie im 2D-Modus ohne Codeaenderung verwendet (SC_Entladung_2D.dwg existiert bereits).

Beide Stationen werden ueber pattern_btmt_beladung/pattern_btmt_entladung (cfg/export.cfg) von ssg-id-collect-blocks (ssg_id.lsp) UND von csv:collect-export-blocks (export.lsp) erfasst und erscheinen im Sivas-/ CSV-Export als eigene TeileArt-Zeilen ("ILS 2.0 BTMT Beladung"/"ILS 2.0 BTMT Entladung" bzw. "BT - Beladung ..."/"Abwurf fuer Klammerbuegel ..."); im Sivas-Export zusaetzlich in den Summenzaehlern "Anzahl aller BTBeladung"/ "Anzahl aller Abwurfstationen" der ILS-Automation-Zeile.

Registrierte Dummy-Befehle:

Gruppe Befehle
ILS Zusatzmodule ILS_ETF_Etagenfoerderer
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

FOERDERANLAGE ist nicht hier definiert – dieser Befehl ist in VarioFoerderer.lsp implementiert.

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)