67 KiB
dxfmakros - Projektanweisungen
Projektbeschreibung
AutoLISP-Makros (SSG_LIB) fuer BricsCAD Pro zur automatisierten Erstellung von Foerderanlagen-Layouts (ILS Kreisel, Omniflo, Variofoerderer, Geruest). Ziel ist der Ersatz proprietaerer Tools (Bihler Omniflo, Bihler Geruest, Ruledesigner) durch eigene parametrische Makros.
Technologie-Stack
- CAD-System: BricsCAD Pro (AutoCAD-kompatibel)
- Hauptsprache: AutoLISP (.lsp)
- Hilfssprache: Python (Export-Skripte in
lib/) - Datenformate: DXF/DWG (Bloecke), JSON (Komponentendaten), CSV (Export)
- Dialoge: DCL (Dialog Control Language, in
dcl/) - Menu: BricsCAD .mnu/.mnl Format (in
menu/)
Projektstruktur
| Verzeichnis | Inhalt |
|---|---|
Lisp/ |
AutoLISP-Makros und Kernbibliotheken |
bin/ |
Hilfs-Skripte (setenv.bat, start_briscad.bat) |
cfg/ |
Konfigurationsdateien |
data/ |
JSON-Daten, DXF-Quellen, Block-Libraries |
data/ils/ |
DWG-Bloecke fuer ILS-Kreisel/Eckrad (AN8, SP8, AP-Typen) |
data/json/ |
Komponentendaten (Masse, Sivas-IDs); Geruest: geruest.json (GERUEST-Dialog) und geruest_traeger.json (Traeger mit Linien-Logik); omniflo_fahrstrecke.json (Omniflo-Fahrstrecke, gleiche Linien-Logik) |
data/block_libraries/ |
DXF Block-Libraries (ils_library.dxf) |
cfg/rahmen/ |
DIN-Rahmen + Schriftkopf-DWGs (A0-A4, Kopf11 DE/EN/FR/IT), kopiert aus \\infor-ln\SSG_Tools\ACADSST\KOEPFE, fuer DIN_FORMATE (DinFormate.lsp) |
dcl/ |
DCL-Dialogdefinitionen |
doc/ |
Dokumentation, Entwicklungsplan |
lib/ |
Python-Skripte (DXF-Bearbeitung, Export) |
menu/ |
BricsCAD-Menudefinition (SSG_LIB.mnu, SSG_LIB.mnl) |
tests/ |
Testskripte und Referenzdaten |
Lademechanismus (Lazy Loading via MNL)
Das Laden der LISP-Module erfolgt ueber das Menuesystem. Voraussetzung ist, dass das Menue einmalig in BricsCAD geladen wurde (siehe unten: "Menue einrichten").
- BricsCAD laedt beim Start automatisch
SSG_LIB.mnl(gleichnamige MNL-Datei zum Menue) - Die MNL laedt alle Kern- und Feature-Module direkt per
ssg-ensure. Kern-Reihenfolge:ssg_core,ssg_lang,ssg_dbg,ssg_dialog,ssg_layer,ssg_id,ssg_ks_insert(Letzteres MUSS vor Gefaellestrecke/VarioFoerderer geladen sein - beide nutzen seine Einfuegeprimitiven). Der Headless-LoaderLisp/ssg_load.lsp(fuer .scr-Testskripte) laedt dieselbe Reihenfolge. ssg-ensureueberspringt bereits geladene Module (Flag*ssg-<modul>-loaded*)
Menue einrichten (einmalig pro BricsCAD-Installation)
bin/setenv.batausfuehren (setztDXFM_LISP,DXFMAKROu.a.)- BricsCAD starten
- In der BricsCAD-Kommandozeile:
_MENULOADeingeben - Im Dialog:
menu/SSG_LIB.mnuauswaehlen und laden - BricsCAD laedt automatisch
SSG_LIB.mnlmit – alle Module werden initialisiert
Alternativ per LISP-Kommandozeile in BricsCAD:
(command "_.MENULOAD" (strcat (getenv "DXFMAKRO") "/menu/SSG_LIB.mnu"))
Das Menue bleibt dauerhaft registriert. Nach einem Update der .mnu-Datei
genuegt es, BricsCAD neu zu starten oder _MENULOAD erneut aufzurufen.
Voraussetzung
Umgebungsvariable DXFM_LISP muss gesetzt sein (via bin/setenv.bat).
Die start_briscad.bat ruft setenv.bat auf und startet BricsCAD.
Modulzuordnung im Menue
| Menue-Bereich | Modul | Befehle |
|---|---|---|
| Kreisel-Module | KreiselInsert |
KreiselInsert, KreiselConnect, KreiselEdit, ILS_Eckrad, EckradEdit, KREISEL_WEICHEN |
| TRO | Tro_Edit |
TRO_INSERT, TRO_EDIT |
| Zusatzmodul/Foerderer/Kurven | SSG_LIB_Commands |
ILS_BTMT_, ILS_ETF_, ILS_GK*_* |
| VarioFoerderer | VarioFoerderer |
FOERDERANLAGE, VARIOFOERDERER_EDIT, Vario_Kette_Merge |
| Gefaellestrecke | Gefaellestrecke |
GEFAELLESTRECKE, GEFAELLESTRECKE_EDIT |
| Omniflo (komplett) | OmniModulInsert + GeruestInsert |
OMNI_APB_, OMNI_W, OMNI_AP, OMNI_TEF, OMNI_TV_, OMNI_LADESCHLEIFE_*, OMNI_EDIT; OMNI_FAHRSTRECKE / OMNI_FAHRSTRECKE_BESCHRIFTEN (GeruestInsert, Linien-Logik wie GERUEST_TRAEGER) |
| Geruest | GeruestInsert |
GERUEST_SAEULEN, GERUEST_ROHRABHAENGER, GERUEST_WANDBEFESTIGUNG (je eigener Dialog aus der Vorlage geruest.dcl, keine Umschaltung), GERUEST_TRAEGER (eigener Dialog), GERUEST_TRAEGER_BESCHRIFTEN (Untermenue Geruest -> Funktionen -> Traeger beschriften; alle Traeger der Zeichnung neu beschriften); Gattertraeger noch Platzhalter auf GERUEST (zeigt nur einen Hinweis) |
| Connections | Connection_Edit + SSG_LIB_Commands |
CONNECTION_INSERT, CONNECTION_EDIT, ILS_EINGANG_INSERT, ILS_AUSGANG_INSERT |
| Export | export |
EXPORTSIVAS, EXPORTCSV, CALLPYTHON |
| Werkzeuge | ssg_blocktools, ssg_texttools, ssg_stueckliste, DinFormate |
SSG_BLOCK_COUNT, SSG_BLOCK_DELETE, SSG_ATTR_ERSETZEN, SSG_TEXT_ERSETZEN, SSG_TEXT_PARALLEL, SSG_TEXT_HOEHE, SSG_TEXT_STIL, SSG_FAEHNCHEN_STUECKLISTE, DIN_FORMATE |
| Konfiguration | SSG_LIB_Commands + DrawPlanquadrate |
SSG_DIM_SWITCH, SSG_SPRACHE, PLANQUADRATE_TOGGLE (DRAW_PLANQUADRATE/DELETE_PLANQUADRATE) |
| Doppelklick/Kontextmenue | SSG_LIB_Commands |
SSG_BLOCKEDIT (dispatcht Kreisel->KreiselEdit, Eckrad->EckradEdit, Omniflo->OMNI_EDIT, GF_->GEFAELLESTRECKE_EDIT, VF_->VARIOFOERDERER_EDIT) |
Entfernte Legacy-Dateien
bin/startup.scr- Entfernt (war fuer/sParameter)bin/acaddoc.lsp- Entfernt (war fuer ACAD-Pfad-basiertes Laden)bin/on_start.lsp- Entfernt; Menue wird jetzt manuell per_MENULOADeingerichtet
Detaillierte Dokumentation aller Funktionen und Befehle: siehe Lisp/README.md.
AutoLISP-Module
| Datei | Befehle | Beschreibung |
|---|---|---|
ssg_core.lsp |
- | Kernel: ssg-start/ssg-end, ssg-cfg-or, ssg-attrib-read/write, Layer/Block-Ops. Zentrale Helfer: ssg-lisp-verzeichnis/ssg-lisp-datei-pfad (Lisp-Pfad-Ermittlung), ssg-rot-matrix-zy (4x4 Rz(hz)*Ry(vert)-Rotationsmatrix, clusteruebergreifend), GUI-Aus-Schalter *ssg-gui-aus* + ssg-gui-aus/ssg-gui-an/ssg-gui-p (fuer automatische Tests: schaltet DCL-/Wizard-Dialoge ab, Module fallen auf Konsolen-/getXXX-Pfad zurueck; der Linienzug hat zusaetzlich *vfl-headless*, das die Konsolen-Rueckfrage selbst verbietet - siehe VarioFoerderer-Zeile). Layer-Zuordnung: ssg-layer-cfg-load/-reset (liest cfg/layer.cfg lazy), ssg-layer-cfg-eintrag (reine Aufloesung "Layername, Farbe" -> (layer . farbe), in tests/test_unit.lsp getestet), ssg-layer-eintrag (Suchreihenfolge [ils_<dim>] -> [allgemein]), ssg-layer-anlegen (Layer mit cfg-Farbe anlegen, optional aktivieren) und ssg-layer-setzen (Blockreferenz auf den Baugruppen-Layer legen) - fuer ZUSAMMENGEBAUTE Baugruppen; ssg-block-layer-vorgabe (LAYER-Attribut -> ssg-ils-block-ebene) fuer nur EINGEFUEGTE Bloecke. Alle liefern nil, wenn nichts konfiguriert ist, und lassen den Layer dann unangetastet. JSON: drei Parser nebeneinander, bewusst nicht zusammengelegt - ssg-load-json/ssg-parse-json-array (flaches Array von Objekten) und ssg-cfg-parse-nested (genau 1 Ebene) sind ZEILENbasiert und koennen weder echte Verschachtelung noch mehrzeilige Arrays, maskierte Anfuehrungszeichen (\") oder Kommas in Strings lesen; ihre Konsumenten (Konfiguration, Sprache, VF-Spec) sind auf dieses Verhalten eingestellt und bleiben unangetastet. Fuer alles andere ssg-json-laden/ssg-json-parse/ssg-json-get (rekursiver Descent-Parser ueber einen Positionszeiger): Objekt -> Alist, Array -> Liste, true -> T, false/null -> nil (beide nil, "Feld fehlt" ist also nicht von "Feld ist false" unterscheidbar), \uXXXX nur fuer ASCII. In tests/test_unit.lsp (tu-tests-json) gegen die echte Geruest-Datendatei getestet. Dazu ssg-join (Liste + Trennzeichen -> String). DCL-Vorlagen: ssg-dcl-vorlage-fuellen (Vorlage lesen, Markerzeilen //@NAME@ durch erzeugte Zeilen ersetzen - reiner Kern ssg-dcl-zeilen-ersetzen, getestet - und per ssg-dcl-temp-schreiben als Temp-Datei schreiben) fuer Dialoge mit fester Grundstruktur und einzelnen datenabhaengigen Bloecken. ssg-lastent-ohne-attribute: Startmarke fuer "alles Neue seit Marke per entnext einsammeln" - statt nacktem (entlast) verwenden, sonst laeuft entnext in die ATTRIBs/SEQEND eines vorherigen Attribut-Blocks und _.-BLOCK entfernt sie dort (realer Bug: per Strg+C/V kopierter GR_-Block verlor beim naechsten GERUEST seine Attribute; Test tests/test_lastent.lsp) |
ssg_ks_insert.lsp |
- | Gemeinsame KS-Extraktion + Block-Einfuegeprimitiven (von VarioFoerderer UND Gefaellestrecke genutzt, frueher in beiden dupliziert): vec-length, ks-line-axis, ks-normalize-name, ks-relativize/-absolutize, ensure-block-loaded, extract-ks-from-block[-raw] (Cache *ks-cache*), insert-block-by-ks (nur Rz), insert-inclined-scaled-block/insert-rotated-block-with-ks (Rz*Ry). MNL laedt es als Core-Modul VOR Gefaellestrecke/VarioFoerderer; vf_core/Gefaellestrecke haben einen guarded Nachlade-Load fuers isolierte Test-Laden |
ssg_lang.lsp |
- | Sprachumschaltung Deutsch/Englisch (ssg-text, ssg-textf, ssg-lang) |
ssg_dbg.lsp |
- | Debug-Logging (dbgopen, dbgmsg, dbgflush, dbgclose) |
ssg_dialog.lsp |
- | DCL-Dialog-Vorlagen und Hilfsfunktionen |
ssg_layer.lsp |
- | Layer-Verwaltung. ssg-layer-visible-p (aus ODER gefroren), ssg-layer-gefroren-p/ssg-ent-layer-gefroren-p (NUR gefroren, fuer den Export) |
ssg_input.lsp |
- | Generische Eingabe-Wrapper ssg-in-real/-int/-string/-point/-dist/-keyword/-yesno (Vorgabe in <..>, Enter = Vorgabe, optionaler Hook *ssg-in-hook* fuer Journale). Steht NEBEN den VF-spezifischen vfl-in-* (diese bleiben unveraendert). ssg-in-yesno liefert T bei Ja (anders als ssg-ques, das T fuer die nicht vorgegebene Antwort liefert); Schluesselwoerter aus ssg-text "in-yesno-keys" |
ssg_blocktools.lsp |
SSG_BLOCK_COUNT, SSG_BLOCK_DELETE, SSG_ATTR_ERSETZEN | Block-Werkzeuge fuer das aktuelle Layout (Ursprung ACADSST TOOLS.LSP/ssg.lsp): Einfuegungen zaehlen (Pick, wcmatch-Muster oder Statistik aller Bloecke), alle Einfuegungen loeschen (Rueckfrage, gesperrte Layer bleiben), Attributwerte zeichnungsweit/in Auswahl suchen+ersetzen (Vorschau mit Anzahl, Teilstring oder Gesamtwert). Wirksamer Blockname (dynamische Bloecke). Reine Kerne ssg-bt-name-passt-p, ssg-bt-zaehle-namen, ssg-bt-wert-neu in tests/test_unit.lsp getestet |
ssg_texttools.lsp |
SSG_TEXT_ERSETZEN, SSG_TEXT_PARALLEL, SSG_TEXT_HOEHE, SSG_TEXT_STIL | Text-Werkzeuge (Ursprung ACADSST CHGTEXT/PARTEXT/ALTHOEHE/ALTSTIL): Suchen/Ersetzen in TEXT, MTEXT und Block-Attributen (nutzt ssg-bt-*, daher NACH ssg_blocktools laden), Text parallel zu Linie/Polylinie/Bogen (lesbar ausgerichtet, Abstand), Hoehe/Stil aendern. Arbeitet in der XY-Ebene des Welt-KS. Reine Kerne ssg-tt-lesbarer-winkel, ssg-tt-versetzt getestet |
ssg_stueckliste.lsp |
SSG_FAEHNCHEN_STUECKLISTE | Positions-Faehnchen + Stuecklistentabelle, komplett AUS DER ZEICHNUNG - es wird keine Datei eingelesen. Der Benutzer waehlt die Elemente aus (ssg-sl-erkannte-elemente: ssget-Auswahl, Enter ohne Auswahl = alle erkannten im aktuellen Layout; der reine Filter ssg-sl-elemente-aus-ss laesst nicht erkannte Objekte still wegfallen, man darf also grosszuegig ein Fenster aufziehen). ssg-sl-klasse klassifiziert Blocknamen (KREISEL_*/ECKRAD_* -> "ILS Kreisel/Eckrad", VF_*/GF_* -> "ILS Streckengruppe", GR_* -> "Rohrgeruest", AP60*/AP110*/AP_110*/APG110* -> je eigene Omniflo-Geraden-Gruppe; wirksamer Blockname ueber ssg-bt-effname, also auch dynamische Bloecke). Jedes einzelne Element bekommt ein Faehnchen (ssg-sl-faehnchen, Rechteck/Kreis/Automation-Rahmen + Text) an der unteren linken Ecke seiner Bounding-Box (aus csv:get-bbox - liefert Zentrum+Abmessungen, NICHT Eckpunkte, die Ecke wird im Code daraus berechnet); die Tabelle (ssg-sl-tabelle-zeichnen) gruppiert GROB je Elementklasse (keine Sivas-Artikelaufloesung wie export_sivas.py) zu einer Zeile mit Anzahl + Laengensumme aus dem LAENGE-Attribut (ssg-sl-gruppieren/reiner Kern ssg-sl-gruppieren-werte), mehrere gleichartige Elemente tragen daher dieselbe Positionsnummer. Bewusst KEIN Datei-Import wie bei Walz/Bihler: dort vergab ein externes System die Positionsnummern und lieferte sie als Datei zurueck, die das CAD nur abzeichnete - bei Walz (MENU/POSITION.LSP) eine .pos/.zei aus einem Clipper-Warenwirtschaftssystem mit Koordinaten-Zuordnung, bei Bihler ITB (Walz_und_Bihler/SBCADBib/ITB-Module_ori.lsp, c:bea_pos) eine .pos mit Zuordnung ueber den Entity-Handle. Diese Umwege sind hier nicht nachgebaut (Entscheidung 2026-10): Nummern und Mengen entstehen in dieser Funktion, die Stuecklisten-AUSGABE liefern EXPORTSIVAS/EXPORTCSV aus denselben Block-Attributen. Der alte c:stueckl (Laengen-Summierung ueber hartcodierte Walz-Profillayer wie L-2=ST, A-7=AP110) hat ebenfalls kein Aequivalent im dxfmakros-Datenmodell (Block-Attribute statt Linien-Layer). Braucht csv:get-bbox/csv:get-attrib aus export.lsp und ssg-bt-effname aus ssg_blocktools.lsp - muss daher NACH beiden geladen werden (siehe ssg-ensure-Reihenfolge in SSG_LIB.mnl/ssg_load.lsp). Reine Kerne ssg-sl-klasse, ssg-sl-gruppieren-werte in tests/test_unit.lsp getestet |
KreiselInsert.lsp |
KreiselInsert, KreiselConnect, KreiselRedraw, KreiselQuick, KreiselEdit, KreiselParams, KreiselLabelSetup, KreiselLabelPos, KreiselLabelHoehe, ILS_Eckrad, EckradEdit, KREISEL_WEICHEN | ILS Kreisel und Eckrad komplett in AutoLISP. Beruehrpunkte/Weichen (kreisel-bp-*, Befehl KREISEL_WEICHEN, Menue Kreisel-Module -> Weichenpunkte ermitteln): ermittelt aus den Blockparametern (Einfuegepunkt, Drehung, ABSTAND - nicht aus der Geometrie, Modell wie draw-module) die Beruehrpunkte aller KREISEL_*/ECKRAD_* GLEICHER Hoehe (toleranz_hoehe): Rad/Rad (Kreis/Kreis) und Rad/Laufprofil (Kreis/Tangente), nur auf der freien Radseite (aeusserer Halbkreis inkl. Flanken; Eckrad ganz frei), Toleranz toleranz_beruehrung (component_defaults.json, Sektion kreisel). Kapsel-Mindestabstand < 2r - tol = KOLLISION (Ueberschneidung), Kreisel Seite an Seite (Beruehrung nur an Flanke/Profil) = KOLLISION Tangente/Tangente - beide werden gemeldet, aber nie zur Weiche. Ausgabe: Konsolenliste + Kreis-Marker (Layer kreisel_weiche/kreisel_kollision aus layer.cfg, XDATA SSG_KR_WEICHE, jeder Lauf ersetzt die alten). Der Export schreibt dieselben Punkte als kreisel_weichen.json neben export_raw.json (csv:kreisel-weichen-schreiben, export.lsp); export_csv.py uebernimmt sie (export_neighbors.load_lisp_kreisel_weichen/lisp_touch_switches, Rueckfall auf compute_kreisel_touch_switches nur ohne Datei), export_sivas.py setzt daraus Merkmal 4403 "Anzahl Weichen ILS". Reine Kerne kreisel-bp-element/-paar/-berechnen in tests/test_unit.lsp (tu-tests-kreisel-bp, u.a. mit den 11 Einfuegungen aus tests/testdata/schnittspunkte_kreisel.dxf -> 10 Beruehrungen) getestet. Eckrad: eigener Dialog eckrad_edit.dcl (eckrad-dialog, reiner Kern eckrad-dialog-werte getestet) beim Einfuegen (ILS_Eckrad, ohne GUI Konsolen-Rueckfall) und per Doppelklick (EckradEdit); kein Abstand/Typ, Drehung als freier Winkel statt fester Kreisel-Ausrichtung. ils-eckrad-insert behaelt eine vorhandene NUMMER (Edit -> gleicher Blockname ECKRAD_n). Kreiselname: draw-module setzt Bezeichnung nur noch bei leerem Wert auf die Vorgabe KreiselN (eigener Name aus KreiselEdit bleibt erhalten); Beschriftung kreisel-label-text = KREISEL n: <Name> <TYP>_<ABSTAND> (ohne Namen bzw. bei Vorgabename wie bisher KREISEL n: <TYP>_<ABSTAND>); da die Beschriftung als TEXT in der wiederverwendeten Blockdefinition KREISEL_n_TYP_ABSTAND steckt, setzt kreisel-label-aktualisieren sie dort per entmod neu (wirkt auf alle Referenzen der Definition) |
VarioFoerderer.lsp (Wrapper fuer vf_core.lsp/vf_standard.lsp/vf_etage.lsp/vf_linienzug.lsp) |
FOERDERANLAGE, VARIOFOERDERER_EDIT, VARIOFOERDERER_NACHRUESTEN, Vario_Kette_Merge, VF_SEKTION_RESTORE, VF_WIZARD_AN, VF_WIZARD_AUS | Vario-Foerderanlagen Generator (Typen Standard/Etage/Linienzug); Standard- UND Etage-Typ: Werteingabe per DCL-Dialog, inkl. der vorgelagerten Eingabemodus-Wahl 3D-Linie/Werte manuell (vfc_eingabemodus, vfc-dialog-eingabemodus-waehlen) - beide Modi laufen seitdem in denselben Basis-/Winkel-Dialog, der Konsolen-Pfad ist nur noch Rueckfall bei abgeschalteter GUI/anderer Sprache. Der Dialog-Ablauf ist zentral in vf_core (vf-dialog-ablauf/vf-dialog-berechnen-einfuegen, parametrisiert ueber Typ-Registry *vf-typ-registry* + Flag horizontal-p); vfs-standard-dialog-ablauf/vfe-etage-dialog-ablauf sind nur noch duenne Wrapper darauf. Beide nutzen dieselben Dialoge vfs-dialog-eingabe-basis/vfs-dialog-winkel-verteilung und den gemeinsamen L_GF/L_VF-Solve vf-winkel-solve (vf_core; berechne-alle-winkel/berechne-winkel-etage teilen ihn); Doppelklick auf VF_n-Block ruft VARIOFOERDERER_EDIT, das per SSG_VF_EDIT-XDATA-Marker ("standard"/"etage"/"linienzug") in den passenden Neuaufbau-Zweig dispatcht (vfe-edit-ent fuer Etage, vfl-edit-ent fuer Linienzug); Altbestand-Bloecke ohne Marker werden abgewiesen. Linienzug (Modus 1, vf-linienzug-modus) zeichnet jede interaktive Eingabe in einem Journal auf (vfl-in-point/-string/-real/-int) und schreibt es als XDATA auf den fertigen VF_n-Block; ein Abbruch (ESC) loescht dadurch nichts mehr, sondern wickelt die bis dahin gebaute Teil-Geometrie ebenso zu einem Block (vfl-modus-abbruch-sichern) - sie bleibt stehen und ist sofort per Doppelklick weiter editierbar/fortsetzbar. Der komplette Linienzug-Hauptstrang (Menue-Fragen von AS-Element bis Kettenende, inkl. GF-Bogen/Vario-Kurve/VF-Einheit-Fortsetzung) laeuft per Default ueber DCL-Dialoge statt Konsole (*vfl-wizard-mode*, Befehle VF_WIZARD_AN/VF_WIZARD_AUS): vfl-in-string/-int/-real zeigen bei aktivem Wizard-Modus generische Dialoge aus dcl/vf_linienzug_wizard.dcl (Zahl-Eingabe bzw. Mehrfachauswahl aus vfl-menu/vfl-menu-int) statt getstring/getint/getreal aufzurufen; die ORIGINALEN Konsolen-Fragen (princ + get*, de/en ueber ssg-text) bleiben unveraendert im Code (Fallback bei Fehlern via vl-catch-all-apply, siehe doc/variofoerderer.md). vfl-edit-ent liest das Journal, zeigt die Sektionen ueber einen DCL-Dialog (vfl-dlg-position, dcl/vfl_edit.dcl) an, kuerzt bei Bedarf auf eine gewaehlte Sektion (stummer Replay der behaltenen Eingaben, umgeht den Wizard-Zweig komplett) und baut danach interaktiv weiter. Linienzug Modus 2 (Pfad+Zielhoehe-Solver, vf-linienzug-modus2) nutzt dieselben Wrapper PLUS die neue Objektauswahl-Wrapper-Funktion vfl-in-selection (Pfad-Objekte als stabile Entity-Handles journalisiert, neue Journal-Art "OBJS") und schreibt sein Journal unter dem eigenen XDATA-Marker "linienzug2"; Doppelklick baut hier NICHT abschnittsweise, sondern per vollem Reset 1:1 aus dem Journal neu auf (vfl-edit-ent2, dispatcht aus vfl-edit-ent per Marker) - Modus 3 (Vorwaerts-Nachbau) bleibt bewusst reine Konsole (im Code selbst als Legacy/"wird umgebaut" markiert). Vario_Kette_Merge (in vf_linienzug.lsp) fuehrt mehrere einzeln gebaute VF_n-Bloecke ab einem gewaehlten Start-Baustein ueber die reale KS_AUS->KS_EIN-Nachbarschaft zu einem Gesamt-Block zusammen (aggregierte Attribute, Luecken-Erkennung). Die interaktiven Editier-Pfade (vfl-edit-ent Sektions-Zweig, vfl-edit-ent2) rufen den Neuaufbau BEWUSST direkt auf, NICHT ueber vl-catch-all-apply: der alte Block ist beim Neuaufbau schon per entdel weg, und ein Catch faengt den Abbruch VOR *error* ab - der Abbruch-Wickler vfl-modus-abbruch-sichern kaeme nie zum Zug und die ganze Kette bliebe als lose Einzelteile ohne Block liegen (Fehlerbild "VF-Block aufgebrochen"). Nur der nicht-interaktive Batch-Konverter vfl-konvertiere-ent behaelt den Catch (die Batch-Schleife muss weiterlaufen) und meldet den betroffenen Block. Die Abbruch-Handler setzen zusaetzlich *ssg-ils-dim* zurueck, weil der Aufrufer, der das sonst tut, nach einem Abbruch nicht mehr erreicht wird. Recovery-Pfad fuer bereits aufgebrochene Ketten: VF_SEKTION_RESTORE (vf_linienzug.lsp) setzt das Ketten-Journal aus der XDATA der losen Objekte zusammen - SSG_VF_EDIT_SEG traegt pro Entity den Journal-Abschnitt DER ITERATION, in der es entstand (indiziert mit dem ersten Glied-Index darin; eine VF-Einheit mit eingebetteten Vario-Kurven erzeugt mehrere Glieder in einer Iteration), SSG_VF_EDIT_PRE auf den Entities des ersten Gliedes die Praeambel (Startpunkt/Starthoehe/AS). Die .dbg-Dateien sind reine AUSGABE und werden von keinem Befehl gelesen (dbgopen legt sie bei jedem Lauf neu an) - Wiederherstellungs-Daten gehoeren in die XDATA. vf_konstanten.lsp (von vf_core.lsp vor den drei Submodulen geladen) sammelt alle Laengen-/Winkel-/Toleranz-Magic-Numbers aus dem VarioFoerderer-Cluster als dokumentierte *vfk-*-Konstanten. Die Migration ist abgeschlossen: vf_core/vf_standard/vf_etage/vf_linienzug referenzieren durchgaengig die zentralen Konstanten statt roher Literale (Winkel, Stations-/Separator-Laengen, Mindestlaengen, AS/ES-Fallback-Versaetze, Text-/Label-Masse) - Ausnahmen sind in vf_konstanten.lsp selbst dokumentiert (Werte mit fuehrender Config-Anbindung via ssg-cfg-or, itoa-/Integer-Kontexte, Blocknamen-Strings, die 2000.0-mm/m-Umrechnung). *vfk-gf-bogen-winkel* (30/60/90, GF-Bogen + Vario-Kurve) und *vfk-as-es-winkel* ("30"/"90", AS/ES; bewusst STRING, da 2-wertig und im Journal als STR gefuehrt) sind Beispiele; vf_linienzug hat fuer beide einen Fallback, falls vf_konstanten nicht geladen wurde. Nicht-interaktiver Betrieb: *vfl-headless* (+ vfl-headless-p/-abbruch/-notausgang, Diagnose *vfl-headless-fehler*, optionaler Antwort-Hook *vfl-headless-antwort-fn*) verbietet jede Live-Eingabe - eine erschoepfte Replay-Queue ist damit ein harter Abbruch MIT Fundstelle (Art der Eingabe, Glied- und Eingabe-Nummer) statt eines stillen Rueckfalls auf getpoint/getreal; eingebaut in vfl-in-value/-value-p/-selection/-abstand. Die Bau-Pfade melden abgewiesene Sektionen ueber vfl-meldung (sammelt nach *vfl-meldungen* + dbgmsg, alert nur bei erlaubter GUI, sonst princ) statt per blockierendem alert; reine Interaktiv-Alerts (fehlendes DCL, "nicht editierbar", "kein Journal") bleiben alert. Genutzt von tests/test_hm_recformat.lsp (Replay der echten Aufzeichnungen) und von vf_spec.lsp; Fahrplan in doc/todo/plan-vf-interactive.md. Details/Entscheidungsbaum: doc/variofoerderer.md |
vf_spec.lsp (von vf_core.lsp NACH vf_linienzug.lsp geladen) |
VF_SPEC_BAU | Linienzug-Ketten aus Eingabedaten bauen, ohne Dialog und ohne Konsolenfrage (Stufe 1 aus doc/todo/plan-vf-interactive.md). Eine Spec beschreibt eine Kette in Domaenenwerten ("links", "winkel", "aussen", ja/nein) statt in Menue-Codes; vsp-journal uebersetzt sie in ein Eingabe-Journal, vsp-bau-aus-spec spielt es per vfl-journal-replay-start durch den UNVERAENDERTEN vf-linienzug-modus (Replay-Kern vsp-bau-journal - nimmt auch ein unveraendert uebernommenes, aufgezeichnetes Journal, z.B. aus dxf_anlage_export.py). Damit traegt der fertige Block dasselbe SSG_VF_EDIT-Journal wie eine handgebaute Kette und bleibt per Doppelklick editier- und 2D/3D-konvertierbar - kein Journal-Konsument muss etwas von Specs wissen. Wichtige Regeln, die der Uebersetzer kennt: der Menue-Code haengt am Frame (erste Sektion 4 Optionen ohne GF-Bogen, danach 5), hz steht nur beim ERSTEN Segment im Journal, und erst-hor unterscheidet den horizontalen Erstkoerper einer VF-Einheit (Separator-/Endpunktfragen vorab) vom gewinkelten (keine Frage). Fehlerverhalten: fail fast - vsp-pruefen sammelt ALLE Spec-Fehler, gebaut wird dann nichts (nichts wird geraten). Ergebnis-Record je Kette mit status (executed/warnung/desync/abbruch/spec-fehler), Block/Handle/Einfuegepunkt/Attributen, prompts (muss 0 sein), glieder_soll vs. glieder_ist (Desync-Detektor, der auch bei aufgehender Queue greift) und den gesammelten meldungen. Headless-Rahmen vsp-headless-an/-aus (Save/Restore von *vfl-headless*, *vfl-wizard-mode*, *ssg-gui-aus* + zaehlende Eingabe-Stubs). Spec-Datei flach als JSON (ssg-load-json liest zeilenweise), erzeugt von lib/vf_spec_export.py. Gegenstueck/Referenz in Python: derselbe Emitter, gegen die 5 echten HundM-Ketten rundlaufgeprueft |
Gefaellestrecke.lsp |
GEFAELLESTRECKE, GEFAELLESTRECKE_EDIT | Gefaelle-Foerderanlage (AUS -> Staustrecke skaliert -> Separator -> EIN); Modus 1 per DCL-Dialog (Einfuegehoehe, Element-Winkel 30/90); Doppelklick auf GF_n-Block ruft GEFAELLESTRECKE_EDIT (Dialog vorbelegt, Neuaufbau; die Einfuegehoehe kommt aus der aktuellen Z des Einfuegepunkts statt aus XDATA/HOEHE_VON_mm, damit ein in Z verschobener Block per OK direkt auf der neuen Hoehe neu entsteht - gleiches gilt fuer die 2D/3D-Umschaltung gf-konvertiere-ent; der GF-Linienzug-Rebuild nutzt weiter die absoluten Segmentpunkte aus XDATA) |
export.lsp |
EXPORTSIVAS, EXPORTCSV, OMNI_UPDATE_ATTRIBS, CSV_EXPORT_KONFIG | JSON-Sammlung und Python-Export (Omniflo-Merkmale/Sum-Zeile via lib/export_csv.py). Gefrorene Layer: csv:collect-export-blocks laesst Bloecke auf gefrorenen Layern aus (ausgeschaltete Layer zaehlen NICHT), Schalter *csv-gefrorene-ausschliessen* (Standard an, Toggle im Dialog CSV_EXPORT_KONFIG). Die Hilfsobjekte eines gefrorenen VF_n/GF_n (Separator-/Element-Kopien, Einheiten-/AS-ES-Marker) liegen auf eigenen Layern und werden darum ueber *csv-ausschluss-enames* mit ausgelassen (gesetzt in csv:export-vorbereiten per csv:hilfskopien-ausschluss); erzeugt und nummeriert werden sie trotzdem, damit IDs stabil bleiben. DrawPlanquadrate nutzt dieselbe Sammlung und folgt dem Filter. Hoehen-Neuberechnung: erster Schritt von csv:export-vorbereiten ist csv:hoehen-aktualisieren - in Z verschobene KREISEL_*/ECKRAD_*/VF_*/GF_* bekommen ihre Hoehen-Attribute aus der Einfuege-Z neu (reiner Kern csv:hoehen-werte, getestet; Formeln/Rundung wie beim Bau: Kreisel HOEHE + MONTAGEHOEHE_m=z/1000, Eckrad HOEHE, GF/VF HOEHE_VON_mm=(fix z), MONTAGEHOEHE_m GF=z/1000 bzw. VF=(VON+BIS)/2000). HOEHE_BIS_mm = VON - (VON_alt - BIS_alt), bewusst NICHT z - DELTA_H_mm (ohne Vorzeichen -> steigende VF falsch, separat abgeschnitten -> 1 mm Abweichung); DELTA_H_mm nur Rueckfall. Nicht verschobene Bloecke (Attribut passt zu z) bleiben unangetastet, damit Bau-Werte und Hand-Korrekturen (z.B. Buehnen-Abzug aus HOEHEN_CHECK) bit-genau erhalten bleiben |
OmniModulInsert.lsp |
OMNI_LOAD, OMNI_APB_, OMNI_W*, OMNI_AP60/AP110, OMNI_TEF, OMNI_TV_, OMNI_APBW_*, OMNI_LADESCHLEIFE_AP110, OMNI_EDIT | Omniflo-Komponenten: Boegen, Weichen, Verbinder, Transferwagen, Ladeschleifen (data/json/omniflo_ladeschleifen.json, per ssg-json-laden, da die Beschreibungen Kommas enthalten; Blockdatei darf .dwg statt .dxf sein), Edit-Dialog |
SSG_LIB_Commands.lsp |
ILS_BTMT_Beladung, ILS_BTMT_Entladung, ILS_SCANNER_INSERT, ILS_SEPARATOR_INSERT, ILS_EINGANG_INSERT, ILS_AUSGANG_INSERT, ILS_ETF_, ILS_GK_*, SSG_BLOCKEDIT, SSG_DIM_SWITCH, SSG_SPRACHE | BTMT Be-/Entladung (ils-insert-station: wiederholtes Einfuegen von BTMT-Beladung bzw. SC_Entladung, Drehung interaktiv, Layer aus LAYER-Attribut, Dimension via ssg-ils-block-laden) + Sensoren (ils-insert-sensor) + Eingang-/Ausgang-Symbol (frei platzierbar wie BTMT Be-/Entladung, ueber ils-insert-station mit den Rohnamen Eingang/Ausgang, Bloecke Eingang_<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. |
GeruestInsert.lsp |
GERUEST, GERUEST_SAEULEN, GERUEST_ROHRABHAENGER, GERUEST_WANDBEFESTIGUNG, GERUEST_TRAEGER, GERUEST_TRAEGER_BESCHRIFTEN, OMNI_FAHRSTRECKE, OMNI_FAHRSTRECKE_BESCHRIFTEN | Geruest-Baugruppen aus data/json/geruest.json konfigurieren und einfuegen. Ein Dialog je Bereich: Rohrabhaenger / Saeulen / Wandbefestigung oeffnen NUR ueber ihren eigenen Menuebefehl (geruest-starten id, Dialogtitel aus dem ASCII-Feld titel); eine Umschaltung im Dialog gibt es nicht (DCL hat kein Reiter-Tile), GERUEST ohne Bereich zeigt nur einen Hinweis. Der Dialogstand beginnt je Aufruf frisch. 1"- und 2"-Saeulen liegen in EINER Baugruppe Saeulen mit gemeinsamer Anbauteil-Liste; teileart und dummy duerfen je Geometrie-Variante ueberschrieben werden (geruest-variante-wert, Variante gewinnt vor Baugruppe; fuer dummy ueber geruest-dummy-wahl: fehlt die DWG der Variante, Meldung + Rueckfall auf den Baugruppen-Dummy; ist der Dummy zugleich eine Teile-DWG, wird er per geruest-ohne-pfad nicht doppelt eingefuegt) - so liefert R 1" weiterhin TEILEART Geruest Saeule 1 Zoll und die R 2"-Varianten Geruest Saeule 2 Zoll, die Groessen bleiben im Export also unterscheidbar. Das Layout steht als Vorlage in dcl/geruest.dcl (bzw. dcl/geruest_traeger.dcl); nur die datenabhaengigen Bloecke - ein radio_button je Variante (Key v<i>, geruest-radio-key/-index), Mengenfeld + Ankreuzfeld je Anbauteil - werden zur Laufzeit an Markerzeilen //@NAME@ eingesetzt (geruest-dcl-ersetzungen -> ssg-dcl-vorlage-fuellen in ssg_core, Temp-Datei, nach unload_dialog geloescht). Grund: DCL kann Tiles weder zur Laufzeit erzeugen noch ausblenden, Radiobutton-Labels lassen sich per set_tile nicht aendern, und eine list_box kann keine Eingabefelder enthalten. Varianten bewusst als Radiobuttons statt list_box: BricsCAD zeichnet die Listenspalte nur halb so breit, und ein tabs-Wert passt nur schrift-/skalierungsabhaengig. Inhalt: Basis-Variante aus geometrie_varianten (list_box mit Einfachauswahl, Hoehe nach Variantenzahl, geruest-dcl-listenhoehe), Winkel Platte (0/90 Grad, *geruest-winkel-werte*): Ausrichtung der Anbauteile zur Basis - die Teile-DWGs aus oben/unten/sonstiges werden um diesen Winkel um Z gedreht in den Block eingefuegt, Basis und Dummy bleiben ungedreht (geruest-teile-dwgs-rot; in Konfigurationen als echter Wert winkel_platte, kein Block-Attribut), Laenge (gegen attribute.Laenge min/max geprueft, Retry-Schleife), Farbe (attribute.Teilefarbe, Freitext bei frei_option) und eine Befestigungsspalte "Befestigung oben und unten" mit allen anbaugruppen.befestigung - die Anzahl ist die GESAMTanzahl je Teil (oben und unten zusammen; Tile-Keys b<i>/bm<i>, Zustand *geruest-dlg-bef*). Unter jedem Teil (auch in Sonstiges) zeigt eine Textzeile "Art.-Nr. ..." die Artikelnummer(n) (geruest-artnr-anzeige: Einzelteil -> sivas_id, Gruppe -> eindeutige Nummern der Teile; DCL kennt keine Schriftgroesse, darum normale Groesse in eigener Zeile). anbaugruppen ist ein Objekt mit zwei Listen: befestigung (Befestigungsspalte) und sonstiges (Teile, die weder oben noch unten sitzen, z.B. die Saeulen-Haltearme) - letztere bekommen eine eigene Zeile darunter (nur wenn nicht leer), in ARTINR/BEZEICHNUNG nach "unten" als "sonstiges" (geruest-anbau-liste; ein altes flaches Array gilt als befestigung). Zwei Mengenfelder im JSON, bewusst verschieden benannt: stueckzahl = wie oft ein Teil TATSAECHLICH geschrieben wird (je Variante bzw. je Satz/Anbauteil, fehlt = 1), anzahl_vorgabe = NUR die Vorbelegung des Anzahl-Feldes im Dialog (Anbaugruppen, fehlt = 1); ein Anbauteil wird Anzahl x stueckzahl mal geschrieben, bei einer Gruppe (teile) zaehlt die Anzahl die Saetze (geruest-stueckzahl/geruest-anzahl-vorgabe). In Konfigurationen (befestigung/sonstiges, dazu winkel_platte) heisst der gespeicherte Dialogwert anzahl. Aeltere Konfigurationen mit getrennten oben/unten-Listen laden weiter: deren Mengen werden zur Gesamtanzahl addiert (geruest-konfig-befestigung), Sonstiges-Teile darin still in die Sonstiges-Zeile umgehaengt (geruest-konfig-sonstiges); beim naechsten Speichern steht alles im neuen Format. 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. Geaenderte Konfiguration: beim Anwenden wird der Dialogstand als Vergleichsbasis gemerkt (*geruest-dlg-konfig-basis*); jede Eingabe vergleicht den aktuellen Record damit (geruest-dlg-aenderung-pruefen, Vergleich statt Flag - Zuruecknehmen hebt die Markierung wieder auf) und kennzeichnet den Listeneintrag mit "(geaendert)" plus Statuszeile kstatus. Speichern bietet bei einer gewaehlten EIGENEN Konfiguration deren Namen als Vorgabe an (Enter = ueberschreiben, geruest-konfig-name-vorgabe); Bibliotheks-Konfigurationen brauchen einen neuen Namen. Konfigurationen verweisen ueber den ANZEIGENAMEN auf Variante und Anbauteile; ein unbekannter Name wird gemeldet und uebersprungen, statt die Konfiguration scheitern zu lassen. Speichern/Loeschen laufen ueber done_dialog 2/3 (Dialog schliessen, Aktion, neu zeigen) - getstring funktioniert aus einem action_tile-Rueckruf heraus nicht. Der Block wird - wie KREISEL_n/VF_n - SELBST zusammengebaut: Dummy-DWG aus data/geruest liefert nur Geometrie, das Attribut-Schema kommt aus *geruest-attrib-defs* (ID, ARTINR, LAENGE, BEZEICHNUNG, TEILEART, TEILEFARBE, LAYER) und wird per ssg-attrib-merge mit den Dummy-Tags zusammengefuehrt - sonst verschluckt ssg-attrib-set-on stillschweigend jeden Tag, den die Dummy-DWG nicht als ATTDEF kennt. Teile-Geometrie: jede ausgewaehlte Artikelnummer (Basis/oben/unten inkl. aufgeloester Gruppen), fuer die data/geruest/<artnr>.dwg existiert, wird zusaetzlich einmal bei 0,0,0 mit in den Block eingefuegt (geruest-teile-dwgs, dedupliziert, fehlende DWGs still uebergangen) - reine visuelle Rueckmeldung, die Attribut-Logik bleibt davon unberuehrt. TEILEFARBE ist eine kommagetrennte Liste parallel zu ARTINR (z.B. ,,RAL7030,,RAL7030): Teile mit "farbig": true in geruest.json (je Artikel; eine Gruppe vererbt es an ihre Teile, fehlend = false) bekommen die im Dialog gewaehlte Baugruppenfarbe, alle anderen einen leeren Eintrag. ARTINR und TEILEFARBE entstehen aus demselben Durchlauf (geruest-teile-paare/-paare-n, geruest-farbliste), sind also garantiert gleich lang. export_sivas.py schreibt daraus je Artikelnummer das Merkmal {"108": "<Farbe>"} in details (Nummer aus cfg/export.cfg [Sivas] merkmal_teilefarbe). 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 Anzahl x stueckzahl es vorgeben - erst Basis, dann Befestigung, dann Sonstiges; 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 ("Befestigung 4x ...", "sonstiges 1x ...") und verkettet mit " + ", NICHT mit Komma - die Artikelbezeichnungen enthalten selbst Kommas ("ROHR R 1" 33,7 x 2,6"). Zuordnung Menue -> JSON laeuft ueber das ASCII-Feld id, weil die aeusseren JSON-Schluessel Umlaute bzw. Anfuehrungszeichen tragen. CSV-/Sivas-Export: pattern_geruest = GR_* (cfg/export.cfg), csv:collect-export-blocks/ssg-id-collect-blocks sammeln GR_-Bloecke wie BTMT eigenstaendig; *geruest-attrib-defs* traegt zusaetzlich ein leeres ID-ATTDEF (Voraussetzung fuer ssg-id-check-all, das nur bestehende ATTRIBs beschreiben kann). ARTINR/BEZEICHNUNG/TEILEART/LAENGE landen in export_csv.py/export_sivas.py in festen Spalten, TEILEFARBE/LAYER (+ ARTINR bei export_csv.py) im Merkmale-/details-JSON. Attribut-Keys in geruest.json sind GROSS (LAENGE, TEILEFARBE, ...) = die Block-Attribut-Tags; ssg-json-get unterscheidet Gross/Klein. Rohr-Geruest-Traeger (GERUEST_TRAEGER): Traeger-Baugruppen mit Linien-Logik liegen in einer EIGENEN Datei data/json/geruest_traeger.json (gleicher Aufbau wie geruest.json, eigener Lader/Cache geruest-traeger-daten, Suche geruest-baugruppe id daten; gemeinsamer Lader geruest-json-laden-cache) - sie tauchen darum nicht im Bereichs-Dropdown von GERUEST auf; dort kann spaeter auch der Gattertraeger hin. eigener Dialog aus der Vorlage dcl/geruest_traeger.dcl (Variante als Radiobuttons, JEDE Variante - auch die mit Muffe - mit eigenem Button, Beschriftung "Name (Artikelnummern)" ueber geruest-varianten-label; Varianten mit "disabled": true erscheinen in allen drei Geruest-Dialogen ausgegraut und nicht waehlbar (geruest-variante-gesperrt-p, geruest-varianten-sperren per mode_tile; Vorauswahl ueber geruest-variante-waehlbar/-erste-freie-variante; eine Konfiguration mit gesperrter Variante laesst die Auswahl unveraendert und meldet gr-konfig-variante-gesperrt) - bleiben aber in den Daten, damit Konfigurationen und Neubeschriften sie weiter ueber den Namen finden; eine Muffen-Checkbox gab es zwischenzeitlich, sie wurde wieder entfernt, weil die Artikelnummer der Muffen-Variante dann im Dialog nicht sichtbar war; Farbe, je eine Checkbox + Feld fuer LAENGENABZUGANFANG/LAENGENABZUGENDE, Vorgabe vorgabe/aktiv aus dem attribute-Block, je Variante ueberschreibbar via geruest-attr-feld); danach Einfuege-Schleife bis ESC/Enter: Startpunkt, dann Endpunkt ODER getippte Laenge (+ Richtung). Aktive Abzuege ruecken die Linie beidseitig ein (Strecke 1000, Abzug Anfang 20/Ende 35 -> Start 20 mm hinter dem Klick, Linie 945 mm); LAENGE = Netto-Laenge, gegen LAENGE.min/max geprueft. KEINE Dummy-DWG: die Geometrie ist eine per entmake erzeugte LINE (0,0,0)-(L,0,0) auf dem layer der Variante, eingefuegt mit dem Richtungswinkel (ANGBASE/ANGDIR/AUNITS dafuer kurz auf 0). Querstreben + Muffe als verschachtelte Dummy-DWGs im selben Block, reine Grafik ohne Export: querstrebe.dummy (data/geruest/dummy_Strebe_30x4.dwg, Basispunkt Strebenmitte, 30 mm laengs X) an jeder Strebenposition, NUR bei Varianten mit ausdruecklichem "mit_querstrebe": true (z.B. nicht beim Rohr 1"); fehlt die DWG, Rueckfall auf je eine 25-mm-Querlinie an Strebenanfang/-ende (laenge/querlinie, Standard 30/25 nur im Code). muffe.dummy (dummy_Muffe_1zoll.dwg, Basispunkt Muffenmitte) am Linienende, NUR bei Varianten mit ausdruecklichem "mit_muffe": true (nicht aus dem Namen geraten; bei "mit 2 Muffen" trotzdem nur eine am Ende); querstrebe/muffe per Variante ueberschreibbar (geruest-variante-wert). Dummys werden ohne Drehung eingefuegt (Traeger laeuft im Block entlang +X), ab dem zweiten Mal per Blockname (geruest-dummy-einfuegen). Verteilungs-Konfiguration querstrebe (abstand/rand_min/rand_max, Variante vor Baugruppe), Plan geruest-traeger-streben-plan -> (rand positionen): der Abstand zwischen zwei Streben ist IMMER genau abstand, beide Raender gleich (netto = kabstand + 2rand); gewaehlt wird die Strebenzahl, deren Rand am naechsten an rand_min..rand_max liegt (Gleichstand -> weniger Streben). Mit 500/100..250 geht das nur auf, wenn netto mod 500 = 0 oder in [200, 500) liegt (z.B. 1300 -> 150 |
menu-inventory.lsp |
MenuInventory | BricsCAD Diagnostik (Menuegruppen, Plugins, Startdateien) |
DinFormate.lsp |
DIN_FORMATE | Ersatz fuer das alte ACADSST-Makro DINFORM.LSP/.DCL (SSG2-Menue "DIN-FORMATE..."): fuegt einen DIN-Rahmen (A0-A3, A4 quer/hoch) und optional einen Schriftkopf (KOPF11, Sprachen DE/EN/FR/IT) per Dialog (dcl/dinformate.dcl) ein. Block-DWGs liegen flach in cfg/rahmen/ (kopiert aus \\infor-ln\SSG_Tools\ACADSST\KOEPFE - nur die von DINFORM.LSP tatsaechlich referenzierten Dateien: A0/A1/A2/A3/A4Q/A4H.DWG + Kopf11/Kopf11e/Kopf11f/Kopf11i.dwg). Rahmen wird im Zeichenmassstab-Faktor eingefuegt, der Kopf im Faktor/100 (Kopf-DWG ist 100x groesser als die Rahmen-DWGs angelegt) - unveraendert aus dem Original-Massstabsschema uebernommen. Blockdefinitionen werden einmalig per _.-INSERT ... _X 0 nachgeladen (dinformate-block-laden), falls noch nicht in der Zeichnung vorhanden. Lazy geladen ueber (ssg-ensure "DinFormate"), Menue: Werkzeuge > Din Formate. |
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). |
DCL-Dialoge
| Datei | Verwendet von |
|---|---|
kreisel_edit.dcl |
KreiselEdit (KreiselInsert.lsp) |
eckrad_edit.dcl |
ILS_Eckrad + EckradEdit (KreiselInsert.lsp, eckrad-dialog) |
gefaellestrecke.dcl |
GEFAELLESTRECKE Modus 1 (Gefaellestrecke.lsp, gf-dialog-eingabe) |
variofoerderer.dcl |
FOERDERANLAGE Standardfall + Werteingabe (vf_standard.lsp, vfs-dialog-eingabe-basis/vfs-dialog-winkel-verteilung) |
omniflo_boegen.dcl |
OMNI_APB_* (OmniModulInsert.lsp) |
omniflo_weichen.dcl |
OMNI_W*_* (OmniModulInsert.lsp) |
omniflo_ladeschleifen.dcl |
OMNI_LADESCHLEIFE_* (OmniModulInsert.lsp, omni:ladeschleifen-dialog) |
sprache.dcl |
SSG_SPRACHE (SSG_LIB_Commands.lsp) |
tro_insert.dcl |
TRO_INSERT (Tro_Edit.lsp) |
tro_edit.dcl |
TRO_EDIT (Tro_Edit.lsp) |
geruest.dcl |
VORLAGE fuer GERUEST_SAEULEN/_ROHRABHAENGER/_WANDBEFESTIGUNG (GeruestInsert.lsp, geruest-dialog); Platzhalter //@TITEL@///@VARIANTEN@///@BEFESTIGUNG@///@SONSTIGES@ werden per ssg-dcl-vorlage-fuellen gefuellt, nicht direkt ladbar |
geruest_traeger.dcl |
VORLAGE fuer GERUEST_TRAEGER (GeruestInsert.lsp, geruest-traeger-dialog), Platzhalter //@VARIANTEN@ |
fahrstrecke.dcl |
VORLAGE fuer OMNI_FAHRSTRECKE (GeruestInsert.lsp, geruest-traeger-dialog), gleiche Tile-Keys wie geruest_traeger.dcl, Platzhalter //@VARIANTEN@ |
dinformate.dcl |
DIN_FORMATE (DinFormate.lsp) |
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 |
Menue (SSG_LIB.mnu + SSG_LIB.mnl)
SSG_LIB.mnu- Menuedefinition mit Lazy-Loading ((ssg-ensure "Modul") Befehl)SSG_LIB.mnl- Automatisch geladen mit dem Menue, definiertssg-ensureLoader***MENUGROUP=SSG_LIB- Menuegruppenname***POP1- Hauptmenue (ILS, Omniflo, SPS [TRO, Connections], Export, Konfiguration)***DOUBLECLICK- Doppelklick auf INSERT-Bloecke ruft SSG_BLOCKEDIT auf***POP501- Rechtsklick-Kontextmenue (KreiselEdit, OMNI_EDIT, SSG_BLOCKEDIT)
Umgebungsvariablen
Werden via bin/setenv.bat gesetzt. Wichtigste:
DXFMAKRO- ProjektwurzelDXFM_LISP- Pfad zu Lisp-Dateien (benoetigt fuer ssg-ensure Lazy-Loader)DXFM_BLOCKS- Pfad zu DWG-BloeckenDXFM_DATA- Datenverzeichnis (JSON, DXF)DXFM_LOG- Log-VerzeichnisDXFM_DCL- Pfad zu DCL-DialogdateienDXFM_CFG- KonfigurationsverzeichnisDXFM_LANG- Aktive Sprache (de_DE/en_GB), persistiert vonSSG_SPRACHE; Defaultde_DEDXFM_VF_SPEC- Spec-Datei fuerVF_SPEC_BAU(optional; Defaulttests/testdata/hundm05.json)
Konventionen
- AutoLISP-Funktionsnamen:
ssg-*fuer Kernfunktionen,C:BEFEHLSNAMEfuer User-Commands - AutoLISP
(and ...)/(or ...)gebenT/nilzurueck, NICHT den letzten Wert (anders als Common Lisp/Scheme). Nie einen Nicht-Boolean-Wert (String, Entity, Liste) durchand/orfaedeln - dafuerif/condverwenden. Realer Bug-Fall:(setq eff (and blockname (ssg-ils-block-laden blockname)))setzteeffaufTstatt auf den Blocknamen-String und liess ein spaeteres_.INSERT/strcatmitbad argument type <T>abstuerzen. Beim Debuggen mysterioeser Typfehler(setq .* (and/(setq .* (ordurchsuchen. - 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/90journalisieren, nicht als Auswahl-Index1/2/3- ein als Index missverstandener String"30"fuehrte real zum falschen 90-Grad-Bogen (VF-Linienzug). Gleiche Klasse wie dieand/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 inssg_ks_insert.lsp, Kern-Helfer inssg_core.lsp, Konstanten invf_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 intests/test_unit.lsp(BefehlTEST_UNIT), der komplett file- und zeichnungsfrei laeuft. Zeichnungs-/geometrieabhaengige Routinen ueber ein Szenario-Modul (tests/test_<name>.lsp+ Eintrag intests/alltests.json, ausgefuehrt vonSSG_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) mittests/test_unit.scrund fuehrtTEST_UNITaus. BricsCAD steht dafuer ueber die UmgebungsvariableBRICSCADzur Verfuegung (inbin/setenv.batgesetzt, auf die installiertebricscad.exezeigend);run_tests.batbricht 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.
- Wiederherstellungs-/Editierdaten gehoeren in XDATA/Journal, nicht in
.dbg: Alles, was zum Neuaufbau/Editieren eines Blocks noetig ist (z.B. das VF-Linienzug-Eingabe-JournalSSG_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.
- Alle Texte in Dateien ohne Umlaute (ae, oe, ue statt ae, oe, ue)
- Bloecke werden als DWG flach in
data/ils/abgelegt (Block-Libraries als DXF indata/block_libraries/). Die Dimension steckt seit dem Flach-Refactor im Dateinamen bzw. Blocknamen als Suffix_2D/_3D(z.B.AN8_2D.dwg,Vario_Bogen_auf_3_TEF_rechts_3D.dwg), damit 2D- und 3D-Variante gleichzeitig in einer Zeichnung stehen koennen. Zentrale Aufloesung:ssg-ils-blockname[-dim]/ssg-ils-block-datei[-dim]/ssg-ils-block-laden[-dim]inssg_core.lsp. Ausnahmen ohne Suffix:KS_EIN/KS_AUS/K1-K4und die Wrapper-BloeckeVF_*/GF_*/KREISEL_*/ECKRAD_*. Alle Feature-Module (Kreisel, VarioFoerderer, Gefaellestrecke) folgen der aktuellen Dimension (ssg-ils-dim-aktuell: transienter Override*ssg-ils-dim*->DXFM_DIM) mit automatischem 3D-Fallback, sodass 2D- und 3D-Aufbau funktionieren. (Die alten Unterordnerdata/ils/2Dunddata/ils/3Dbleiben uebergangsweise als Quelle der Batch-/Attribut-Tools bestehen.) - Layer-Zuordnung - zwei Quellen, klar getrennt:
- Baugruppen, die ein Makro aus Einzelteilen zusammenbaut (
KREISEL_n,ECKRAD_n,VF_n,GF_n) und die selbst gezeichnete Hilfsgeometrie (Kreisel-Tangenten, PIN-Linien, VF-/GF-/Kreisel-Beschriftung) haben keine Blockdatei indata/und damit kein Attribut - ihr Ziel-Layer und die Layerfarbe stehen incfg/layer.cfg(Abschnitte[ils_2d]/[ils_3d]/[allgemein], Key = Baugruppe, Wert =Layername, Farbnummer). Gelesen ueberssg-layer-setzen/ssg-layer-anlegen(ssg_core.lsp). - Bloecke, die nur aus
data/eingefuegt werden, tragen ihren Ziel-Layer als Block-AttributLAYER(gepflegt indata/ils/allocations_collected.jsonbzw. direkt in den DWGs); dieses Attribut gilt als Vorgabe, angewendet ueberssg-block-layer-vorgabe(Fallback: "eigene" Ebene der Blockdefinition perssg-ils-block-ebene). - Fehlt
cfg/layer.cfgoder ein Eintrag, bleibt der Layer unangetastet - kein Fehler, Verhalten wie vorher. - Die Farbe wirkt nur bei der Neuanlage des Layers (
ssg-make-layerlaesst bestehende Layer unveraendert, damit Kundenzeichnungen nicht umgefaerbt werden). Layer, die eine Blockdatei ausdata/ilsselbst mitbringt (z.B.ILS_SP/_3D_ILS_SP), legt BricsCAD schon beim Einfuegen des Blocks mit der Farbe aus der DWG an - dort greift die cfg-Farbe nicht, wohl aber der Layername des Wrappers. Fuer Layer ohne Baugruppen-Key gibt es zusaetzlich den Abschnitt[farben](Layername = Farbe), gelesen vonssg-layer-farbe-fuer-name.
- Baugruppen, die ein Makro aus Einzelteilen zusammenbaut (
- Komponentendaten (Masse, Sivas-IDs) liegen als JSON in
data/json/ - Sprache im Code und Kommentaren: Deutsch
- Benutzermeldungen sind ZWEISPRACHIG (de/en): Jede an den Anwender gerichtete Meldung (
princ/alert/getstring/getint/getreal/getkword-Prompts, auch perstrcatzusammengesetzte) laeuft ueber(ssg-text "key")bzw.(ssg-textf "key" (list args)). Die Texte stehen inlang/de_DE.jsonUNDlang/en_GB.jsonunter"texts"und muessen key-fuer-key synchron bleiben (Paritaets-Check vor dem Commit). Konventionen: Key-Prefix je Modul (vfc-vf_core,gf-Gefaellestrecke), geteilte Prompts ohne Prefix (prompt-wahl-1-2usw.) - vor dem Anlegen eines neuen Keys erst die JSONs nach dem deutschen String grep'en und wiederverwenden. Sonderzeichen wie Grad(chr 176)/ Delta(chr 916)als%n-Platzhalter uebergeben, nicht in die (ASCII-)JSON einbetten. Untranslatiert bleiben (bewusst): Bracket-Diagnosen ([vf_core]o.ae.), die VOR dem garantierten Laden vonssg_lang.lspfeuern (Bootstrap-Fallbacks am Dateikopf -ssg-textexistiert dort noch nicht), Lade-Banner (>>> ... geladen), reine====/----Trenner und reine Entwickler-Diagnosebefehle (nicht im Menue). Details und Entscheidungsbaum:doc/plan/translate.md. - Subagenten sollten von Claude nur mit Rückfrage an den Nutzer gestartet werden. Dabei sollten wenn möglich die leichteren Modelle gewählt werden, da es sich dabei in der Regel um Code recherche handelt.
Python-Bibliotheken
Standalone-Skripte (lib/) - laufen extern mit ezdxf
Abhaengigkeiten: ezdxf, Standard-Library. Benoetigen Umgebungsvariablen (DXFM_DATA, DXFM_RESULTS, DXFM_CFG).
| Skript | Beschreibung |
|---|---|
omniflo_utils.py |
Gemeinsame Hilfsfunktionen fuer Omniflo-Skripte: ROW_GROUPS (Reihen-Gruppierung), SWITCH_FILTERS (Schalter-Filter), build_row_layout, import_element_as_block, draw_cross, load_omniflo_data |
set_einfuegepkt.py |
Setzt $INSBASE (Einfuegepunkt) in Omniflo-DXF-Dateien. Schalter je Typ: --boegen, --weichen45, --weichen90, --weichenkoerper, --weichen-parallel, --delta, --dreifachweiche, --sternweiche, --show-omniflo |
set_koords.py |
Setzt Koordinatensystem-Bloecke (K1-K4) in Omniflo-DXF-Dateien. Schalter: --k1set bis --k4set, --set-all, --force, --show-omniflo, --test, --number |
export_sivas.py |
Erzeugt Sivas-Export-CSV mit Summierungszeilen (ILS Automation, Omniflo Sum). Omniflo-Elemente werden ueber das TEILEART-Attribut (Omniflo *) erkannt, ihre Sivas-Nummern kommen - wie beim Geruest - NUR aus dem kommagetrennten ARTINR-Attribut (eine Zeile je Nummer, TEF-Teile 0_B* als Omniflo TEF); KEIN Katalog-Lookup mehr (den nutzt nur noch export_csv.py fuer die Merkmale). Schreibt zusaetzlich immer <output>_zusammengefasst.csv: Zeilen, die sich nur in Nr/Anzahl/IDs unterscheiden (v.a. Geruest, je Block eine Zeile), zu einer Zeile zusammengefasst (items_zusammenfassen, Anzahl addiert, IDs vereinigt); die Hauptdatei bleibt unveraendert. ILS 2.0 Weiche (6216): eine Summenzeile (Anzahl 1), sobald kreisel_weichen.json (LISP-Export, siehe KreiselInsert.lsp) Beruehrpunkte enthaelt - je Beruehrpunkt eine Weichenkombination aus 1x aktiv (3371) + 1x passiv (3372), Zuordnung in cfg/sivas.cfg [weichen] ueber das synthetische Attribut ANZAHL_WEICHEN; dieselbe Zahl geht in Merkmal 4403 der Automation-Zeile. Aufruf: python export_sivas.py <export_raw.json> <data_dir> <output.csv> |
export_csv.py |
Erzeugt einfache Item-Liste als CSV (ohne Summierung). Aufruf: python export_csv.py <export_raw.json> <data_dir> <output.csv> |
split_block_library.py |
Teilt ils_library.dxf in einzelne DXF-Dateien pro Block auf (mit Abhaengigkeiten). Export-Prefixe: Vario_, Staustrecke_, 3D_AS, 3D_ES |
vf_journal_export.py |
Liest die Eingabe-Journale der VF_n-Bloecke (XDATA-App SSG_VF_EDIT, Marker "linienzug") aus einer Zeichnung und schreibt sie als Frage-Antwort-Protokoll im Schema von tests/testdata/linienzug_tests.json (Gegenstueck zu vfl-entry->string; kein ezdxf, reine Gruppencode-Lesung). Optional --csv <sivas_export.csv> fuer Strecken-ID und Erwartungswerte je Kette. Aufruf: python vf_journal_export.py <dxf> <output.json> [--csv ...] [--kein-startpunkt-fix] — erzeugt tests/testdata/hm_recformat.json aus data/polylines.dxf (die Aufzeichnung, aus der tests/testdata/hundm05.json als Spec abgeleitet wird) |
dxf_vf_abbild.py |
Abbild der VF_/GF_-Wrapperbloecke einer Testzeichnung: Attribute UND Bausteinzusammensetzung je Kette, Ketten ueber den Einfuegepunkt der Spec zugeordnet (der Blockname taugt nicht - die VF-Nummer beginnt in jeder Zeichnung neu bei 1). Regressionsnetz fuer Umbauten am Bau-Ablauf: --referenz vergleicht gegen tests/reference/hundm05_attribute.json (Exit 1 bei Abweichung), --schreibe-referenz frischt sie nach einem GEPRUEFTEN Lauf auf. Faengt genau das, was der Ergebnis-Record nicht sieht - eine vertauschte vfl-acc-*-Reihenfolge (Komma-Listen L_VF_m/L_GF_m/GF_WINKEL, in der Zeichnung unsichtbar, im Sivas-Export falsch) und eine falsche Blockvariante. Liest ASCII-DXF streamend; binaere DXF kann dxf_scan_components.py. Genutzt von tests/test_vf_geometrie.py |
vf_spec_export.py |
Uebersetzt ein Eingabe-Journal in eine Spec und zurueck (spec_aus_journal / journal_aus_spec) - Gegenstueck zu vsp-journal in Lisp/vf_spec.lsp, beide muessen dieselbe Tokenfolge liefern. Der Rundlauf journal_aus_spec(spec_aus_journal(tok)) == tok laeuft in tests/test_vf_spec.py gegen die 5 echten HundM-Ketten und beweist, dass die Spec das Journal verlustfrei abbildet. Schreibt das flache Spec-JSON (vom LISP-Parser lesbar). Objekte der Ziel-Datei, die keine Kette beschreiben (von Hand ergaenzte Anlagenteile mit function, z.B. Kreisel), werden beim Neuschreiben unveraendert ans Dateiende uebernommen - sonst waeren sie beim naechsten Lauf weg. Aufruf: python vf_spec_export.py <protokoll.json> <spec.json> — erzeugt tests/testdata/hundm05.json (Spec, Eingabe fuer TEST_HUNDM05/VF_SPEC_BAU) aus tests/testdata/hm_recformat.json (Aufzeichnung) |
dxf_anlage_export.py |
Gegenstueck zum Scanner: liest eine mit unseren Makros GEBAUTE (und von Hand nachbearbeitete) Anlage aus der Zeichnung zurueck in Nachbau-Testdaten - je Wrapper genau die Parameter, die auch dessen Edit-Befehl liest (KREISEL_n: Einfuegepunkt, abstand exakt aus der Blockdefinition, drehrichtung aus DREHRICHTUNG; GF_n: SSG_GF_EDIT + SEITE_AS/ES, endet die GF auf einer Kreisel-Kette, wird dessen Hoehe hoehe_ende_mm; VF_n standard/etage: mubea-Schema wie vfs-edit-basis-lesen; VF_n linienzug: Journal UNVERAENDERT als "typ": "linienzug_journal" - bewusst keine Spec, weil der Bau-Dialog neuer ist als die Spec-Grammatik; uebrige Bloecke als Einzel-Insert ohne _2D/_3D-Suffix). Reihenfolge Kreisel, VF, GF, Einzelbloecke (VF vor GF wie mubea). Aufruf: python dxf_anlage_export.py <dxf> <output.json> [--projekt NAME] — erzeugt tests/testdata/dematic_arvato.json aus Dematic_TST.dxf. Test tests/test_dxf_anlage_export.py |
dxf_scan_components.py |
Erkennt ILS-/Omniflo-Komponenten in einer fremden Projektzeichnung (ohne unsere Wrapper-Bloecke) und schreibt sie im JSON-Schema aus tests/testdata/object_data.md — Grundlage fuer Nachbau-Testfaelle wie tests/testdata/mubea.json. Liest binaere DXF im GB-Bereich streamend per mmap (ezdxf-DOM waere zu gross), loest INSERTs inkl. OCS/Extrusionsrichtung rekursiv in Weltkoordinaten auf, paart AN8/SP8 zu Kreiseln und Antrieb/Umlenkung zu Foerderern. Gefaellestrecken, die als 3D-LINE auf einem Staustrecken-Layer gezeichnet sind (GF_LINIE_LAYER, z.B. 6-SP), werden in einem zweiten Lesedurchgang zu Modus-1-Eintraegen: Start/Ende werden auf die Ketten der anschliessenden Kreisel verlaengert (gezeichnet ist nur die Staustrecke; deltaL reicht von AS- bis ES-Ursprung), AS-Seite aus dem Ausschleuselement am oberen Ende; dieses und Separatoren auf der Achse entfallen als Einzel-Insert (die GF baut beide selbst). Sitzt der AS am UNTEREN Ende, ist es ein Steigfoerderer und wird nur gemeldet (gf_eintraege_aus_linien, Test tests/test_dxf_scan_gf.py). --ohne-hoehen setzt alle Z auf 0, --kreisel-hoehe X Y Z gibt einem Kreisel seine Hoehe - die daran haengenden GF bekommen daraus start_mm.z/hoehe_ende_mm, den Winkel rechnet der Treiber (dematic_arvato:gf-geometrie: atan ueber die HORIZONTALE Laenge, ES-Laenge UND -Abfall bei der Einlaufneigung gemessen und iteriert, Staustrecke daraus - die flache ES-Laenge liess steile GF ~144 mm vor der Kette enden - NICHT gf-winkel-berechnen, dessen asin bei steilen GF wie im Showroom ~50 statt ~37 Grad liefert; dematic_arvato:gf-endhoehe-pruefen meldet warnung bei > 5 mm Abweichung von HOEHE_BIS). Flache 2D-Plaene: --flach (waagrechte Staustrecke mit AS an genau einem Ende = GF, zerstueckelte Linien werden vereint, ES am unteren Ende wird verbraucht, AS/ES-Seite ohne Namensangabe aus der Lage zur Fahrtrichtung), --kreisel-achse X1 Y1 X2 Y2 (Kreisel ohne AN8/SP8), Einzelteile auf einer Kreisel-Kette erben dessen Anwender-Hoehe (sensor_hoehen_von_kreiseln). Kreisel-Paarung mit Tausch-Nachbesserung auf minimale Abstandssumme (zwei Kreisel auf einer Achse). Genutzt fuer tests/testdata/showroom_ils.json (TEST_SHOWROOM_ILS). Aufruf: python dxf_scan_components.py <dxf> <data_dir> <output.json> [--fenster X0 Y0 X1 Y1] — ohne --fenster liefert eine Anlagenzeichnung leicht ueber 1300 Eintraege, zuviel fuer einen Testlauf |
Entwickler-Tools (lib/) - kein ezdxf, reine Text-/AutoLISP-Verarbeitung
| Skript | Beschreibung |
|---|---|
dbg2lsp.py |
Instrumentiert AutoLISP-Funktionen automatisch mit ssg_dbg.lsp (dbgf/dbg/dbgreturn/dbgopen/dbgclose). Schalter: --method (Funktion instrumentieren), --recursive (Aufrufgraph mitverfolgen), --add-open DATEI METHODE (Debug-Datei oeffnen/schliessen), --dry-run. Details: doc/dbg2lsp.md |
Typische Verarbeitungskette (Omniflo-DXF)
set_einfuegepkt.py- Einfuegepunkte berechnen und setzenset_koords.py- Koordinatensysteme (K1-K4) setzenexport_sivas.py/export_csv.py- Export aus Zeichnung
Entwicklungsstand
Siehe doc/plan/Entwicklungsplan.md fuer Meilensteinplanung und Aufwandsschaetzung.
Fertig (DONE): ILS Kreisel, ILS Eckrad, ILS Variofoerderer, ILS BTMT Be-/Entladung (Einfuegen), Omniflo Boegen, Omniflo Weichen.
Debug
Wenn Werte für das Debug in der Bricscad Konsole ausgegeben werden z.B. mit princ, soll der Benutzer dazu ermuntert werden, statt dessen die Lisp dbg Routinen zu verwenden, welche dann eine .dbg file im log folder erzeugen. Diese können ja über eine zentrale globale Sammlung von Schalter an und abgeschaltet werden, so dass die Files per default auch nicht geschrieben werden, nur wenn der passende Schalter auf 1 gesetzt wird. Damit können debug aufrufe auch im Code verbleiben. Biete diese Architektur jeden an, der viele Werte ins Bricscad Fenster raus schreibt.