This commit is contained in:
Simon Hensch committed 2026-11-02 09:56:08 +01:00
commit afc5dce13a
10 files changed
+470 -337

No files matched your search

+2 -1
View File
@@ -121,7 +121,7 @@ Detaillierte Dokumentation aller Funktionen und Befehle: siehe `Lisp/README.md`.
| `Jam_Edit.lsp` | JAM_EDIT | Staubereich `JAM_AREA` aus sps_skel (`tro_annotate.py --anlage`, Name `Jam<NNNN>_<k>`) bearbeiten: Foerderer und laufende Nummer per Dialog `jam_edit.dcl`, Knopf zurueck auf den abgeleiteten Wert (`AUTO`), Warnung bei doppeltem Namen. `TRO`/`ENTRY` sind stabiler Schluessel und werden nicht geaendert. SSG_BLOCKEDIT leitet `JAM_*` hierher; in die SCL kommt die Aenderung ueber `tro_annotate --read-jams` | | `Jam_Edit.lsp` | JAM_EDIT | Staubereich `JAM_AREA` aus sps_skel (`tro_annotate.py --anlage`, Name `Jam<NNNN>_<k>`) bearbeiten: Foerderer und laufende Nummer per Dialog `jam_edit.dcl`, Knopf zurueck auf den abgeleiteten Wert (`AUTO`), Warnung bei doppeltem Namen. `TRO`/`ENTRY` sind stabiler Schluessel und werden nicht geaendert. SSG_BLOCKEDIT leitet `JAM_*` hierher; in die SCL kommt die Aenderung ueber `tro_annotate --read-jams` |
| `TEFInsert.lsp` | TEF_FOERDERER_LINIE | Linie zwischen zwei Punkten zeichnen und mit 1000-mm-TEF-Geraden (`0_B10030`) belegen; Rest ueber ein zurueckversetztes Element, damit der Foerderer exakt am Linienende endet; geneigte Linien per Rz*Ry wie `ssg_ks_insert.lsp` | | `TEFInsert.lsp` | TEF_FOERDERER_LINIE | Linie zwischen zwei Punkten zeichnen und mit 1000-mm-TEF-Geraden (`0_B10030`) belegen; Rest ueber ein zurueckversetztes Element, damit der Foerderer exakt am Linienende endet; geneigte Linien per Rz*Ry wie `ssg_ks_insert.lsp` |
| `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` (Radiobuttons, siehe oben), **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 VergleichsbasLine truncated | `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` (Radiobuttons, siehe oben), **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 VergleichsbasLine truncated
| `KettenFoerderer.lsp` | KETTENFOERDERER, KF_EDIT, KF_KS_PRUEFEN | Kettenfoerderer/Schraegfoerderer Stahl: baut aus `KF_UM_<winkel>` -> `KF_LAENGE` -> `KF_AN_<winkel>` (je KS_AUS auf KS_EIN) plus der Draufsicht `KF_2D` EINEN Block `KF_<n>`. Daten `data/json/kettenfoerderer.json`, Dialog `dcl/kettenfoerderer.dcl`. **Geometrie**: die Bausteine sind in der XY-Ebene gezeichnet (Hoehe = Y), werden aber AUFGERICHTET eingebaut `Rz(basis)*Rx(90)*Rz(eigen)` (`kf-matrix-aufrecht`) - die Hoehe laeuft in **Z**; dafuer `insert-block-ks-matrix` (freie 3x3-Rotation) in ssg_ks_insert.lsp. Die Neigung (26/30 Grad) ist KEIN Rechenparameter: sie steckt in den Varianten-Bloecken, der Einbauwinkel jedes Folgebausteins wird zur Laufzeit aus der RICHTUNG des KS_AUS seines Vorgaengers gemessen (`ks-xy-winkel`/`kf-hz-folge`) - keine gepflegte Winkeltabelle. `KF_LAENGE` wird in X skaliert, die Einbauhoehe H bestimmt seine Laenge ueber die Hoehenbilanz (`kf-laenge-berechnen`). `KF_2D` liegt DECKUNGSGLEICH mit der Kette (KS_EIN/KS_AUS auf denen von Umlenkung/Antrieb), ist flach gezeichnet, nur um die Steigung geneigt und auf die Schraeglaenge skaliert. **Attribut-Schema** `*kf-attrib-defs-liste*` ist EIGENSTAENDIG und bewusst NICHT von `ssg-strecke-attrib-defs` abgeleitet (sonst ~24 geerbte VF-Tags am Block); die Reihenfolge folgt dem Sivas-Merkmalstamm. FOERDERERTYP = Bauart im Klartext, WINKEL = nackte Zahl 26/30 (der Klartext mit Gradzeichen entsteht erst ueber `[kf_typ_anzeige]` in cfg/sivas.cfg, damit Attributwerte ASCII bleiben); kein TEILEART (Festwert `KF_TEILEART` im Export). Alle Auswahllisten stehen mit ihren Sivas-Optionstexten in der JSON. **Das `KF_`-Praefix ist wie `GR_` nicht kosmetisch**: `SSG_BLOCKEDIT` haelt JEDEN Block mit TEILEART-Attribut fuer ein Omniflo-Element, darum steht ein `KF_*`-Zweig VOR dem Omniflo-Zweig (Doppelklick -> KF_EDIT, der die vorgewaehlte Auswahl nutzt und nur echte `KF_<n>` zulaesst - die Bausteine `KF_UM_26`, `KF_2D` usw. tragen dasselbe Praefix). **Die Blocknummer wird ueber die Blocktabelle vergeben** (`kf-naechste-nummer` ueberspringt jede Nummer mit vorhandener Block-DEFINITION, wie bei `GF_n`/`VF_n`): nur ueber die Referenzen im Modell zu zaehlen war ein realer Fehler - `KF_EDIT` loescht den alten Block, die Definition lebt aber weiter, `_.-BLOCK` traf auf den vorhandenen Namen, die "Neu definieren?"-Abfrage verschluckte die Folge-Eingaben, und die Einzelteile blieben lose liegen ("Block aufgeloest"). **Sivas-Export** (`[kettenfoerderer]` in cfg/sivas.cfg, `build_kettenfoerderer_details` in export_sivas.py): baugleiche Foerderer werden zu EINER Zeile zusammengefasst (`kettenfoerderer_signature`), die Gruppengroesse traegt Merkmal 353 (entsteht erst aus der Gruppierung, daher Code); Farbe (354) und Antriebsmotor (355) sind ZWEI Merkmale, und jedes merkmalbildende Attribut steht auch in der Signatur, sonst verschmelzen unterschiedlich ausgefuehrte Foerderer. Reine Kerne in `tests/test_unit.lsp` (`tu-tests-kf`); das Modul wird in der MNL vorgeladen, Laufzeit-Messung ueber den dbg-Schalter `kf-zeit` | | `KettenFoerderer.lsp` | KETTENFOERDERER, KF_EDIT, KF_KS_PRUEFEN | Kettenfoerderer/Schraegfoerderer Stahl: baut aus `KF_UM_<winkel>` -> `KF_LAENGE` -> `KF_AN_<winkel>` (je KS_AUS auf KS_EIN) plus der Draufsicht `KF_2D` EINEN Block `KF_<n>`. Daten `data/json/kettenfoerderer.json`, Dialog `dcl/kettenfoerderer.dcl`. **Geometrie**: SEITENANSICHT in der XY-Ebene (Hoehe = Y), ungedreht und nicht aufgerichtet, Einfuegepunkt = KS_EIN der Umlenkung; jeder Baustein bekommt nur den aus den KS gemessenen Verkettungswinkel (`insert-block-by-ks`/`insert-block-ks-skaliert`). Die Neigung (26/30 Grad) ist KEIN Rechenparameter: sie steckt in den Varianten-Bloecken, der Einbauwinkel jedes Folgebausteins wird zur Laufzeit aus der RICHTUNG des KS_AUS seines Vorgaengers gemessen (`ks-xy-winkel`/`kf-hz-folge`) - keine gepflegte Winkeltabelle. `KF_LAENGE` wird in X skaliert, die Einbauhoehe H bestimmt seine Laenge ueber die Hoehenbilanz (`kf-laenge-berechnen`). **Hauptblock + Hilfsblock**: `KF_<n>` traegt Beschriftung (`KF_1 26 Grad H=..mm L_horizontal=..mm`, Layer `kettenfoerderer_beschriftung`) und ein Hoehenmass (ActiveX `AddDimRotated`, scheitert es, wird ohne Mass gebaut) und ist der EINZIGE exportierte Block. Die Draufsicht `KF_2D` ist ein getrennter Hilfsblock `KF2D_<n>` (kein Objekt, nur zum Anschliessen von Schienen): flach, auf die PROJIZIERTE Laenge skaliert (`kf-waagerecht-laenge`), beim ersten Einfuegen `ansicht_2d.versatz_y` (-1000) mm unter dem Hauptblock, danach frei verschieb- und loeschbar, gleiche Beschriftung. Beide Bloecke tragen denselben Schluessel als XDATA `SSG_KF_EDIT` (`kf-xdata-key`); KF_EDIT findet damit den - auch verschobenen - Hilfsblock (`kf-hilfsblock-finden`), behaelt dessen Lage und erzeugt ihn bei jedem Edit neu (auch wenn er geloescht war). Der Hilfsblock passt nicht aufs Export-Muster `KF_*`; `export_sivas.py` nimmt ausserdem nur `KF_<Ziffern>`. Eine von Hand gesetzte Drehung des Hauptblocks (DXF 50) wird beim Edit wieder aufgesetzt. **Attribut-Schema** `*kf-attrib-defs-liste*` ist EIGENSTAENDIG und bewusst NICHT von `ssg-strecke-attrib-defs` abgeleitet (sonst ~24 geerbte VF-Tags am Block); die Reihenfolge folgt dem Sivas-Merkmalstamm. FOERDERERTYP = Bauart im Klartext, WINKEL = nackte Zahl 26/30 (der Klartext mit Gradzeichen entsteht erst ueber `[kf_typ_anzeige]` in cfg/sivas.cfg, damit Attributwerte ASCII bleiben); kein TEILEART (Festwert `KF_TEILEART` im Export). `L_KF_mm` ist die WAAGERECHTE Laenge des ganzen Foerderers (der Draufsicht `KF_2D`, `kf-waagerecht-laenge`), nicht die Laenge des schraegen Mittelstuecks; sie geht NICHT in die Sivas-Zeile (Spalte Laenge bleibt leer) - Sivas berechnet ZL, EL und Kettenlaenge selbst, uebernommen wird nur die Hoehe (360). Alle Auswahllisten stehen mit ihren Sivas-Optionstexten in der JSON. **Das `KF_`-Praefix ist wie `GR_` nicht kosmetisch**: `SSG_BLOCKEDIT` haelt JEDEN Block mit TEILEART-Attribut fuer ein Omniflo-Element, darum steht ein `KF_*`-Zweig VOR dem Omniflo-Zweig (Doppelklick -> KF_EDIT, der die vorgewaehlte Auswahl nutzt und nur echte `KF_<n>` zulaesst - die Bausteine `KF_UM_26`, `KF_2D` usw. tragen dasselbe Praefix). **Die Blocknummer wird ueber die Blocktabelle vergeben** (`kf-naechste-nummer` ueberspringt jede Nummer mit vorhandener Block-DEFINITION, wie bei `GF_n`/`VF_n`): nur ueber die Referenzen im Modell zu zaehlen war ein realer Fehler - `KF_EDIT` loescht den alten Block, die Definition lebt aber weiter, `_.-BLOCK` traf auf den vorhandenen Namen, die "Neu definieren?"-Abfrage verschluckte die Folge-Eingaben, und die Einzelteile blieben lose liegen ("Block aufgeloest"). **Sivas-Export** (`[kettenfoerderer]` in cfg/sivas.cfg, `build_kettenfoerderer_details` in export_sivas.py): baugleiche Foerderer werden zu EINER Zeile zusammengefasst (`kettenfoerderer_signature`), die Gruppengroesse traegt Merkmal 353 (entsteht erst aus der Gruppierung, daher Code); Farbe (354) und Antriebsmotor (355) sind ZWEI Merkmale, und jedes merkmalbildende Attribut steht auch in der Signatur, sonst verschmelzen unterschiedlich ausgefuehrte Foerderer. Reine Kerne in `tests/test_unit.lsp` (`tu-tests-kf`); das Modul wird in der MNL vorgeladen, Laufzeit-Messung ueber den dbg-Schalter `kf-zeit` |
| `menu-inventory.lsp` | MenuInventory | BricsCAD Diagnostik (Menuegruppen, Plugins, Startdateien) | | `menu-inventory.lsp` | MenuInventory | BricsCAD Diagnostik (Menuegruppen, Plugins, Startdateien) |
| `LagerInsert.lsp` | LG_GESCHOSS, LG_GESCHOSS_AKTIV, LG_GESCHOSS_KOPIEREN, LG_BEREICH, LG_REIHE, LG_GANG, LG_EDIT, LG_REGALE, LAGER_EXPORT, LAGER_HAND_YAML | Lagerplanung GOH-Buehnenlager -> RFQ-YAML (Phase 0+1: Mengen, 2D; Konzept/Schema/YAML-Zuordnung in `doc/plan/lager_rfq.md`). Bloecke `LG_GESCHOSS` (Kopfsymbol, XDATA `SSG_LG` `kontur` -> Belagskontur), `LG_BEREICH` (Kontur + Parameter, fuellt sich selbst), `LG_REIHE_<L>_<T>`/`LG_GANG_<L>_<B>_<BELAG>` (Definition je Geometrie wiederverwendet, Werte in den Attributen der Referenz; Kinder tragen XDATA `bereich`; der Belag steht im Gang-Blocknamen, weil die Definition seine Schraffur enthaelt - Muster aus `lager.json belag_schraffur`, per `vla-AddHatch` auf Layer 0/VONBLOCK, `hallenboden` ohne; BricsCAD-`iso.pat` kennt kein `NET`, Gitterrost = `GRID`). Alle tragen `ID`, `GESCHOSS`, `LAYER`. Blockdefinitionen per `entmake` (BLOCK...ENDBLK), Einfuegen per `vla-InsertBlock` in WCS - kein `_.-BLOCK`/`_.INSERT`. **Attribut-Tags haben gemischte Schreibweise** (`HOEHE_BELAG_mm`) - beim Setzen ueber `vla-get-TagString` daher case-insensitiv zuordnen (`lg-wert-fuer-tag`), sonst bleiben diese Attribute still leer (realer Fehler beim Bau). Layer je Geschoss = Basis aus `cfg/layer.cfg [lager]` + `_G<nr>`; `LG_GESCHOSS_AKTIV` friert die uebrigen (2D-Grundrisse liegen uebereinander), aktives Geschoss als `vlax-ldata` (Dictionary `SSG_LAGER`). Geschosszuordnung `lg-geschoss-fuer-punkt-kern`: eine Kontur -> eindeutig, mehrere nur ueber Z unterscheidbar, wenn die Konturen auf verschiedenen Hoehen liegen (3D), sonst nil. Jede Kontur gehoert genau einem Block (vergebene Kontur wird kopiert). Auto-Fuellung `lg-fuellung-berechnen` (reiner Kern, Muster Reihe|Gang|Reihe Ruecken an Ruecken bzw. Reihe|Gang, nur volle Module, Streifen an Unter-/Mittel-/Oberkante gegen die Kontur geschnitten, Quergaenge ausgerichtet und nicht am Reihenende); Bereichs-Edit = Neuaufbau aller Kinder. **Randgaenge** `RANDGANG_N/O/S/W_mm` (Nord = +Y Welt) laufen entlang der echten Konturkanten (`lg-randgang-streifen`, Himmelsrichtung der Aussennormale ueber `lg-kompass`, auch L-Form; Ecken: N/S besitzen sie - konvex O/W kuerzen, einspringend N/S verlaengern) und werden wie manuelle Gaenge (`LG_GANG`, `QUELLE=manuell`, Kern `lg-gang-einfuegen`; per Linie oder Option `Rechteck` = Flaeche mit dem echten BricsCAD-Befehl RECHTECK gezeichnet, inkl. aller seiner Optionen (`lg-rechteck-zeichnen`: `_.RECTANG` + `(command pause)`-Schleife bei CMDECHO 1, Polylinie danach ueber `lg-rechteck-ecken-aus-poly` gelesen und geloescht; nur 4 Ecken ohne Boegen, sonst Meldung), Breite/Lage aus dem Rechteck ueber `lg-gang-aus-ecken`, Schleife `lg-linien-schleife` mit `rechteck-fn`) als **Sperrflaechen** aus Reihen/Haupt-/Quergaengen ausgeschnitten (`ssg-poly-band-x`). **Von Hand Gesetztes loest KEINE Neuberechnung aus**: `LG_REIHE`/`LG_GANG` (`lg-manuell-einfuegen`) und das Bearbeiten einer Reihe/eines Gangs (`lg-linie-neu-aufbauen-sperre`) schneiden nur die eigene Flaeche aus den rangniedrigeren Objekten heraus (`lg-sperre-anwenden`, reiner Kern `lg-linie-zuschnitt`; Rang `lg-quelle-rang`: auto < auto_geaendert < manuell) - frueher fuellte ein manueller Gang den ganzen Bereich neu, dabei kamen geloeschte Objekte zurueck und geaenderte wurden ersetzt. Die ausdrueckliche Neuberechnung (`lg-bereich-fuellen`, Bereich bearbeiten) entfernt nur `QUELLE=auto`; `manuell`/`auto_geaendert`-Kinder und unverknuepfte manuelle Objekte in der Kontur (`lg-manuelle-in-kontur`) bleiben und sperren ihre Flaeche. `lg-blockdef` ergaenzt bei Altbestand fehlende ATTDEFs (Neudefinition + `_.ATTSYNC`). `LAGER_EXPORT` schreibt `<dwg>_lager_raw.json` (eigener JSON-Schreiber `lg-json-*`; Geschosse, Bereiche, Reihen, Gaenge) und ruft `lib/lager_yaml_export.py` ueber `export:run-python`. Befehl `LG_REGALE` (Menue Lager -> Regale zaehlen) zaehlt die Regalreihen `LG_REIHE_*` (Anzahl, Laenge, Laenge x Haengeebenen je Geschoss aus den Attributen GESCHOSS/LAENGE/HAENGEEBENEN, reine Kerne `lg-reihe-eintrag`/`lg-reihen-auswerten`). **Der parametrische BricsCAD-Block `goh_rack` wird nicht mehr unterstuetzt** (veraltet, 2026-10-30 aus Zaehlung, Export und Geschoss-Kopie entfernt). Vorgaben aus `data/json/lager.json`. Dialoge `dcl/lager.dcl` ueber den generischen Treiber `lg-dialog` (Feldlisten `*lg-felder-*`, Konsolen-Rueckfall `lg-konsole`, ohne GUI Vorgaben). **Geschoss kopieren** (`LG_GESCHOSS_KOPIEREN`, Dialog `lg_geschoss_kopie`, Kern `lg-geschosse-kopieren`): Quelle (Vorgabe oberstes Geschoss, Erweitern G3 -> G4), Anzahl, Geschosshoehe je Kopie, Belag, Versatz X/Y je Kopie (0 = deckungsgleich; 3D-Zeichnung zusaetzlich +Z). Kein BricsCAD-KOPIEREN (XDATA-Verweise zeigten aufs Original): jedes Objekt entsteht ueber die normalen Einfuegefunktionen neu (`lg-geschoss-anlegen`, `lg-bereich-block-anlegen` = Bereich ohne Fuellung, `lg-linie-einfuegen`), Kinder 1:1 inkl. `auto_geaendert`/manuell, kein Neufuellen; Belaege gleich dem Quell-Geschossbelag folgen dem neuLine truncated | `LagerInsert.lsp` | LG_GESCHOSS, LG_GESCHOSS_AKTIV, LG_GESCHOSS_KOPIEREN, LG_BEREICH, LG_REIHE, LG_GANG, LG_EDIT, LG_REGALE, LAGER_EXPORT, LAGER_HAND_YAML | Lagerplanung GOH-Buehnenlager -> RFQ-YAML (Phase 0+1: Mengen, 2D; Konzept/Schema/YAML-Zuordnung in `doc/plan/lager_rfq.md`). Bloecke `LG_GESCHOSS` (Kopfsymbol, XDATA `SSG_LG` `kontur` -> Belagskontur), `LG_BEREICH` (Kontur + Parameter, fuellt sich selbst), `LG_REIHE_<L>_<T>`/`LG_GANG_<L>_<B>_<BELAG>` (Definition je Geometrie wiederverwendet, Werte in den Attributen der Referenz; Kinder tragen XDATA `bereich`; der Belag steht im Gang-Blocknamen, weil die Definition seine Schraffur enthaelt - Muster aus `lager.json belag_schraffur`, per `vla-AddHatch` auf Layer 0/VONBLOCK, `hallenboden` ohne; BricsCAD-`iso.pat` kennt kein `NET`, Gitterrost = `GRID`). Alle tragen `ID`, `GESCHOSS`, `LAYER`. Blockdefinitionen per `entmake` (BLOCK...ENDBLK), Einfuegen per `vla-InsertBlock` in WCS - kein `_.-BLOCK`/`_.INSERT`. **Attribut-Tags haben gemischte Schreibweise** (`HOEHE_BELAG_mm`) - beim Setzen ueber `vla-get-TagString` daher case-insensitiv zuordnen (`lg-wert-fuer-tag`), sonst bleiben diese Attribute still leer (realer Fehler beim Bau). Layer je Geschoss = Basis aus `cfg/layer.cfg [lager]` + `_G<nr>`; `LG_GESCHOSS_AKTIV` friert die uebrigen (2D-Grundrisse liegen uebereinander), aktives Geschoss als `vlax-ldata` (Dictionary `SSG_LAGER`). Geschosszuordnung `lg-geschoss-fuer-punkt-kern`: eine Kontur -> eindeutig, mehrere nur ueber Z unterscheidbar, wenn die Konturen auf verschiedenen Hoehen liegen (3D), sonst nil. Jede Kontur gehoert genau einem Block (vergebene Kontur wird kopiert). Auto-Fuellung `lg-fuellung-berechnen` (reiner Kern, Muster Reihe|Gang|Reihe Ruecken an Ruecken bzw. Reihe|Gang, nur volle Module, Streifen an Unter-/Mittel-/Oberkante gegen die Kontur geschnitten, Quergaenge ausgerichtet und nicht am Reihenende); Bereichs-Edit = Neuaufbau aller Kinder. **Randgaenge** `RANDGANG_N/O/S/W_mm` (Nord = +Y Welt) laufen entlang der echten Konturkanten (`lg-randgang-streifen`, Himmelsrichtung der Aussennormale ueber `lg-kompass`, auch L-Form; Ecken: N/S besitzen sie - konvex O/W kuerzen, einspringend N/S verlaengern) und werden wie manuelle Gaenge (`LG_GANG`, `QUELLE=manuell`, Kern `lg-gang-einfuegen`; per Linie oder Option `Rechteck` = Flaeche mit dem echten BricsCAD-Befehl RECHTECK gezeichnet, inkl. aller seiner Optionen (`lg-rechteck-zeichnen`: `_.RECTANG` + `(command pause)`-Schleife bei CMDECHO 1, Polylinie danach ueber `lg-rechteck-ecken-aus-poly` gelesen und geloescht; nur 4 Ecken ohne Boegen, sonst Meldung), Breite/Lage aus dem Rechteck ueber `lg-gang-aus-ecken`, Schleife `lg-linien-schleife` mit `rechteck-fn`) als **Sperrflaechen** aus Reihen/Haupt-/Quergaengen ausgeschnitten (`ssg-poly-band-x`). **Von Hand Gesetztes loest KEINE Neuberechnung aus**: `LG_REIHE`/`LG_GANG` (`lg-manuell-einfuegen`) und das Bearbeiten einer Reihe/eines Gangs (`lg-linie-neu-aufbauen-sperre`) schneiden nur die eigene Flaeche aus den rangniedrigeren Objekten heraus (`lg-sperre-anwenden`, reiner Kern `lg-linie-zuschnitt`; Rang `lg-quelle-rang`: auto < auto_geaendert < manuell) - frueher fuellte ein manueller Gang den ganzen Bereich neu, dabei kamen geloeschte Objekte zurueck und geaenderte wurden ersetzt. Die ausdrueckliche Neuberechnung (`lg-bereich-fuellen`, Bereich bearbeiten) entfernt nur `QUELLE=auto`; `manuell`/`auto_geaendert`-Kinder und unverknuepfte manuelle Objekte in der Kontur (`lg-manuelle-in-kontur`) bleiben und sperren ihre Flaeche. `lg-blockdef` ergaenzt bei Altbestand fehlende ATTDEFs (Neudefinition + `_.ATTSYNC`). `LAGER_EXPORT` schreibt `<dwg>_lager_raw.json` (eigener JSON-Schreiber `lg-json-*`; Geschosse, Bereiche, Reihen, Gaenge) und ruft `lib/lager_yaml_export.py` ueber `export:run-python`. Befehl `LG_REGALE` (Menue Lager -> Regale zaehlen) zaehlt die Regalreihen `LG_REIHE_*` (Anzahl, Laenge, Laenge x Haengeebenen je Geschoss aus den Attributen GESCHOSS/LAENGE/HAENGEEBENEN, reine Kerne `lg-reihe-eintrag`/`lg-reihen-auswerten`). **Der parametrische BricsCAD-Block `goh_rack` wird nicht mehr unterstuetzt** (veraltet, 2026-10-30 aus Zaehlung, Export und Geschoss-Kopie entfernt). Vorgaben aus `data/json/lager.json`. Dialoge `dcl/lager.dcl` ueber den generischen Treiber `lg-dialog` (Feldlisten `*lg-felder-*`, Konsolen-Rueckfall `lg-konsole`, ohne GUI Vorgaben). **Geschoss kopieren** (`LG_GESCHOSS_KOPIEREN`, Dialog `lg_geschoss_kopie`, Kern `lg-geschosse-kopieren`): Quelle (Vorgabe oberstes Geschoss, Erweitern G3 -> G4), Anzahl, Geschosshoehe je Kopie, Belag, Versatz X/Y je Kopie (0 = deckungsgleich; 3D-Zeichnung zusaetzlich +Z). Kein BricsCAD-KOPIEREN (XDATA-Verweise zeigten aufs Original): jedes Objekt entsteht ueber die normalen Einfuegefunktionen neu (`lg-geschoss-anlegen`, `lg-bereich-block-anlegen` = Bereich ohne Fuellung, `lg-linie-einfuegen`), Kinder 1:1 inkl. `auto_geaendert`/manuell, kein Neufuellen; Belaege gleich dem Quell-Geschossbelag folgen dem neuLine truncated
| `LagerPruefung.lsp` | LG_REGELN | Regelprofile der Lagerplanung: `cfg/regelwerk/<LAND>.json` laden, `erbt_von` aufloesen (`lg-profil-mischen`), je Regel den STRENGSTEN Wert aller zutreffenden Quellen (`lg-regel-aufloesen`: `art` min/max/wert, `bedingung` gegen einen Kontext - fehlt der Schluessel, konservativ einbezogen -, `tabelle` + `tabellen_schluessel`) mit Protokoll. Jede Pruefregel der Lagerplanung wird genau hier implementiert (Python rendert nur). Alle Startwerte `verifiziert: false` | | `LagerPruefung.lsp` | LG_REGELN | Regelprofile der Lagerplanung: `cfg/regelwerk/<LAND>.json` laden, `erbt_von` aufloesen (`lg-profil-mischen`), je Regel den STRENGSTEN Wert aller zutreffenden Quellen (`lg-regel-aufloesen`: `art` min/max/wert, `bedingung` gegen einen Kontext - fehlt der Schluessel, konservativ einbezogen -, `tabelle` + `tabellen_schluessel`) mit Protokoll. Jede Pruefregel der Lagerplanung wird genau hier implementiert (Python rendert nur). Alle Startwerte `verifiziert: false` |
@@ -219,6 +219,7 @@ Abhaengigkeiten: `ezdxf`, Standard-Library. Benoetigen Umgebungsvariablen (`DXFM
| `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` | | `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) | | `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_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` |
| (`io_zuordnung_pruefen.py`) | **Nicht hier**: die Zuordnung E-Geraete (Sensoren/Aktoren/Motoren) -> Anlagenbauteile (Separator, AS, Kreisel AN8/SP8, Vario-Stationen, Scanner) liegt im Kabeltool-Repo `kabellaengen`: Pruefskript `lib/io_zuordnung_pruefen.py` (`bin\io_zuordnung_pruefen.bat`), Konverter alter `io`-Symbole `lib/altZuNeu.py` (`bin\altZuNeu.bat`), gemeinsames Regelwerk `cfg/io_regeln.json`, Doku `doc/Anwenderdoku_altZuNeu.md`. Die dort erkannten Mechanik-Bloecke sind die dxfmakros-Bloecke (`Separator_SP*`, `AS_Element*`, `AN8_*`/`SP8_*`, `Vario_Motorstation*`/`Vario_Umlenkstation*`, `Scanner*`) - wer sie umbenennt, muss `bauteile` in `io_regeln.json` mitziehen |
| `lager_yaml_export.py` | Lagerplanung: `<dwg>_lager_raw.json` (LAGER_EXPORT) -> `<dwg>_drawing.yaml` (Overlay im Schema der RFQ-Hand-YAML, nur Zeichnungsfelder, plus `_provenance`/`_conflicts`/`_warnings`) und optional `--nachweis` HTML. `--hand <yaml>` gleicht ab (Hand gewinnt; Konflikte bei > 1 % bzw. 0,005). Aggregiert nur, Regeln leben in LISP. Braucht `pyyaml`. Test `tests/test_lager_yaml.py`. Aufruf: `python lager_yaml_export.py <raw.json> <out.yaml> [--hand ...] [--nachweis ...]` | | `lager_yaml_export.py` | Lagerplanung: `<dwg>_lager_raw.json` (LAGER_EXPORT) -> `<dwg>_drawing.yaml` (Overlay im Schema der RFQ-Hand-YAML, nur Zeichnungsfelder, plus `_provenance`/`_conflicts`/`_warnings`) und optional `--nachweis` HTML. `--hand <yaml>` gleicht ab (Hand gewinnt; Konflikte bei > 1 % bzw. 0,005). Aggregiert nur, Regeln leben in LISP. Braucht `pyyaml`. Test `tests/test_lager_yaml.py`. Aufruf: `python lager_yaml_export.py <raw.json> <out.yaml> [--hand ...] [--nachweis ...]` |
| `dxf_scan_components.py` | Erkennt ILS-/Omniflo-Komponenten in einer **fremden** Projektzeichnung (ohne unsere Wrapper-Bloecke) und schreibt sie im JSON-Schema aus `doc/json-input-lisp.md` (Nachfolger des geloeschten `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 | | `dxf_scan_components.py` | Erkennt ILS-/Omniflo-Komponenten in einer **fremden** Projektzeichnung (ohne unsere Wrapper-Bloecke) und schreibt sie im JSON-Schema aus `doc/json-input-lisp.md` (Nachfolger des geloeschten `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 |
+357 -258
View File
@@ -26,6 +26,14 @@
;; insert-block-by-ks/insert-block-ks-skaliert, die Rz*Ry-Primitiven des ;; insert-block-by-ks/insert-block-ks-skaliert, die Rz*Ry-Primitiven des
;; VarioFoerderers werden hier nicht gebraucht. ;; VarioFoerderers werden hier nicht gebraucht.
;; ;;
;; HAUPT- UND HILFSBLOCK: KF_<n> ist die Seitenansicht (ungedreht, Einfuege-
;; punkt = KS_EIN der Umlenkung) mit Beschriftung und Hoehenmass; nur dieser
;; Block wird exportiert. Die Draufsicht KF_2D ist ein getrennter Hilfsblock
;; KF2D_<n> (zum Anschliessen von Schienen), der aus der projizierten Laenge
;; entsteht, beim ersten Mal 1000 mm unter dem Hauptblock liegt, frei
;; verschiebbar und loeschbar ist und per Doppelklick auf KF_<n> neu erzeugt
;; wird. Beide tragen denselben Schluessel in der XDATA (SSG_KF_EDIT).
;;
;; Die Einbauhoehe H (Abstand KS_EIN der Umlenkung bis KS_AUS des Antriebs auf ;; Die Einbauhoehe H (Abstand KS_EIN der Umlenkung bis KS_AUS des Antriebs auf
;; der Y-Achse) bestimmt die Laenge des Zwischenstuecks ueber die ;; der Y-Achse) bestimmt die Laenge des Zwischenstuecks ueber die
;; Hoehenbilanz, siehe kf-laenge-berechnen. ;; Hoehenbilanz, siehe kf-laenge-berechnen.
@@ -223,7 +231,8 @@
;; STANDARD_STEUERUNG - Merkmal 361, "1"/"0" ueber Transform ja_nein. ;; STANDARD_STEUERUNG - Merkmal 361, "1"/"0" ueber Transform ja_nein.
;; Steht direkt hinter WINKEL, wie im Merkmalstamm. ;; Steht direkt hinter WINKEL, wie im Merkmalstamm.
;; H_EINBAUHOEHE_mm - Merkmal 360 ;; H_EINBAUHOEHE_mm - Merkmal 360
;; L_KF_mm - Laenge des skalierten Zwischenstuecks, CSV-Spalte ;; L_KF_mm - WAAGERECHTE Laenge des ganzen Foerderers (der Draufsicht
;; KF_2D), CSV-Spalte "Laenge"
;; FARBE - Merkmal 354 ;; FARBE - Merkmal 354
;; ANTRIEBSMOTOR - Merkmal 355 (eigenes Merkmal, nicht Teil von 354) ;; ANTRIEBSMOTOR - Merkmal 355 (eigenes Merkmal, nicht Teil von 354)
;; ANTRIEBSART - Merkmal 357, FEST "1.1 kW" (Wert aus der JSON, ;; ANTRIEBSART - Merkmal 357, FEST "1.1 kW" (Wert aus der JSON,
@@ -327,27 +336,6 @@
(if (and (null treffer) (findfile k)) (setq treffer (findfile k)))) (if (and (null treffer) (findfile k)) (setq treffer (findfile k))))
treffer) treffer)
;; ============================================================
;; AUFRICHTUNG: die Baugruppe steht im Modell
;; ============================================================
;; Die Bausteine sind in der XY-Ebene gezeichnet (X = Fahrtrichtung,
;; Y = Hoehe) - eine Seitenansicht. Eingebaut werden sie AUFGERICHTET: um
;; 90 Grad um die Fahrtrichtungsachse gekippt, sodass die Hoehe nach Z
;; wandert. Von oben sieht man dann die Draufsicht KF_2D, von der Seite die
;; Kette; beide bilden EIN Objekt ohne Versatz.
;;
;; Gesamtdrehung je Baustein: Rz(basis) * Rx(90) * Rz(eigen)
;; Rz(eigen) richtet den Baustein in seiner EIGENEN Zeichenebene aus
;; (der aus den KS gemessene Verkettungswinkel, s. kf-hz-folge)
;; Rx(90) richtet die Ebene auf - Y_lokal wird zu Z_welt
;; Rz(basis) dreht die fertige Baugruppe in der Draufsicht
;; Die Reihenfolge ist nicht beliebig: Rz(eigen) muss VOR dem Aufrichten
;; wirken (es ist eine Drehung in der Blockebene), Rz(basis) danach (es ist
;; eine Drehung im Modell).
(defun kf-matrix-aufrecht (basis-hz eigen-hz)
(mat3-mul-mat3 (ks-rot-z basis-hz)
(mat3-mul-mat3 (ks-rot-x 90.0) (ks-rot-z eigen-hz))))
;; ============================================================ ;; ============================================================
;; GEOMETRIE EINER VARIANTE MESSEN ;; GEOMETRIE EINER VARIANTE MESSEN
;; ============================================================ ;; ============================================================
@@ -433,156 +421,261 @@
(list laenge skala nil))))))) (list laenge skala nil)))))))
;; ============================================================ ;; ============================================================
;; KETTE BAUEN (aufgerichtet, 3D) ;; KETTE BAUEN (Seitenansicht)
;; ============================================================ ;; ============================================================
;; Fuegt die drei Bausteine ab startpunkt verkettet ein - AUFGERICHTET, die ;; Fuegt die drei Bausteine ab startpunkt verkettet ein. Die Kette ist eine
;; Hoehe laeuft also in Z (siehe kf-matrix-aufrecht). basis-hz dreht die ;; SEITENANSICHT: sie liegt flach in der XY-Ebene (X = Fahrtrichtung,
;; fertige Baugruppe in der Draufsicht. ;; Y = Hoehe), so wie die Bausteine gezeichnet sind. Es wird nichts
;; aufgerichtet und nichts gedreht; jeder Baustein bekommt nur den aus den KS
;; gemessenen Verkettungswinkel (kf-hz-folge).
;; (Zwischenzeitlich wurde die Kette aufgerichtet in Z eingebaut - nach
;; Abstimmung wieder zurueckgenommen; die Draufsicht ist jetzt ein EIGENER
;; Hilfsblock, siehe kf-hilfsblock-bauen.)
;; Rueckgabe: Weltpunkt des KS_AUS der Antriebsstation (Kettenende). ;; Rueckgabe: Weltpunkt des KS_AUS der Antriebsstation (Kettenende).
(defun kf-kette-bauen (variante geo skala startpunkt basis-hz / pt) (defun kf-kette-bauen (variante geo skala startpunkt / pt)
(setq pt startpunkt) (setq pt startpunkt)
(setq pt (insert-block-ks-matrix (setq pt (insert-block-by-ks
(ssg-json-get variante "block_umlenkung") (ssg-json-get variante "block_umlenkung") pt
pt (cdr (assoc "hz-um" geo))))
(kf-matrix-aufrecht basis-hz (cdr (assoc "hz-um" geo))) (setq pt (insert-block-ks-skaliert
1.0)) (ssg-json-get variante "block_laenge") pt
(setq pt (insert-block-ks-matrix (cdr (assoc "hz-l" geo)) skala))
(ssg-json-get variante "block_laenge") (setq pt (insert-block-by-ks
pt (ssg-json-get variante "block_antrieb") pt
(kf-matrix-aufrecht basis-hz (cdr (assoc "hz-l" geo))) (cdr (assoc "hz-an" geo))))
skala))
(setq pt (insert-block-ks-matrix
(ssg-json-get variante "block_antrieb")
pt
(kf-matrix-aufrecht basis-hz (cdr (assoc "hz-an" geo)))
1.0))
pt) pt)
;; ------------------------------------------------------------ ;; ------------------------------------------------------------
;; DRAUFSICHT (KF_2D) - deckungsgleich mit der Kette ;; MASSE AUS START- UND ENDPUNKT (reine Rechenkerne)
;; ------------------------------------------------------------ ;; ------------------------------------------------------------
;; KF_2D gehoert in DIESELBE Lage wie die Kette, nicht daneben: sein KS_EIN ;; Horizontalanteil von p1 nach p2, auf die Kettenrichtung (basis-hz, Grad)
;; faellt mit KS_EIN der Umlenkung zusammen, sein KS_AUS mit KS_AUS des ;; projiziert und nicht die blosse X-Differenz - sonst stimmt es bei einem von
;; Antriebs. Dadurch ist die Baugruppe EIN Koerper - von oben sieht man die ;; Hand gedrehten Block nicht. Die Hoehe (Y) geht NICHT ein.
;; Draufsicht, von der Seite die Kette. (defun kf-horiz-projektion (p1 p2 basis-hz / rad dx dy)
;;
;; Zwischenzeitlich lagen beide Ansichten 1000 mm uebereinander (versatz_y).
;; Das war eine Hilfskonstruktion, solange die Kette flach in der XY-Ebene
;; lag; seit sie aufgerichtet eingebaut wird, ist sie unnoetig.
;;
;; KF_2D ist selbst flach gezeichnet und wird darum NICHT aufgerichtet,
;; sondern nur um die Steigung GENEIGT (Rz(basis) * Ry(-winkel)) und auf die
;; SCHRAEGLAENGE skaliert. In der Draufsicht erscheint es dadurch genau mit
;; der Grundrisslaenge - die Verkuerzung uebernimmt die Projektion, sie muss
;; nicht gerechnet werden.
;; Neigungswinkel und Schraeglaenge aus Start- und Endpunkt der Kette.
;; Rueckgabe: (schraeglaenge winkel-grad), oder nil bei Laenge 0.
;; Reiner Rechenkern - der eigentliche Pruefpunkt der 3D-Umstellung.
(defun kf-neigung-aus-punkten (p1 p2 basis-hz / rad dx dy dz horiz laenge)
(setq rad (* (float basis-hz) (/ pi 180.0))) (setq rad (* (float basis-hz) (/ pi 180.0)))
(setq dx (- (car p2) (car p1)) (setq dx (- (car p2) (car p1))
dy (- (cadr p2) (cadr p1)) dy (- (cadr p2) (cadr p1)))
dz (- (cond ((caddr p2)) (0.0)) (cond ((caddr p1)) (0.0)))) (+ (* dx (cos rad)) (* dy (sin rad))))
;; Horizontalanteil auf die Kettenrichtung projiziert (nicht die blosse
;; X-Differenz) - sonst stimmt es bei gedreht eingefuegter Kette nicht.
(setq horiz (+ (* dx (cos rad)) (* dy (sin rad))))
(setq laenge (sqrt (+ (* horiz horiz) (* dz dz))))
(if (< laenge 1e-6)
nil
(list laenge (/ (* 180.0 (atan dz horiz)) pi))))
;; Draufsicht einfuegen - deckungsgleich mit der Kette. ;; WAAGERECHTE (projizierte) Laenge der Kette = Laenge der Draufsicht KF_2D.
;; kette-start/kette-ende = KS_EIN der Umlenkung bzw. KS_AUS des Antriebs. ;; Das ist der Wert im Attribut L_KF_mm. Immer positiv.
;; Rueckgabe: die verwendete Schraeglaenge (mm), sonst nil. (defun kf-waagerecht-laenge (p1 p2 basis-hz)
(defun kf-ansicht-2d-bauen (daten kette-start kette-ende basis-hz / (abs (kf-horiz-projektion p1 p2 basis-hz)))
cfg bname masse laenge winkel ks ein-dx skala R)
;; Neigungswinkel der Variante als nackte Ganzzahl ("26"/"30"), bewusst ueber
;; itoa/fix statt rtos: rtos haengt an DIMZIN/LUPREC/LUNITS der Zeichnung. Der
;; Wert steht im Attribut WINKEL und ist Schluessel der Tabelle
;; [kf_typ_anzeige] im Sivas-Export - er muss profilunabhaengig sein.
(defun kf-winkel-text (variante)
(itoa (fix (+ 0.5 (cond ((kf-zahl (ssg-json-get variante "winkel"))) (0.0))))))
;; Punkt um versatz-y in Y verschoben (Z bleibt, fehlendes Z = 0).
(defun kf-versatzpunkt (p versatz-y)
(list (car p) (+ (cadr p) (float versatz-y)) (cond ((caddr p)) (0.0))))
;; Versatz des Hilfsblocks (Draufsicht) gegen den Hauptblock beim ERSTEN
;; Einfuegen, in mm (Y). Steht in der JSON, nicht im Code; Vorgabe -1000.
;; (Pruefung ueber if, nicht ueber and/or: and liefert T statt der Zahl.)
(defun kf-hilfsblock-versatz (daten / cfg wert)
(setq cfg (ssg-json-get daten "ansicht_2d")) (setq cfg (ssg-json-get daten "ansicht_2d"))
(setq bname (if cfg (ssg-json-get cfg "block"))) (setq wert (if cfg (kf-zahl (ssg-json-get cfg "versatz_y"))))
(if (or (null bname) (= bname "")) (cond (wert) (-1000.0)))
nil
;; ------------------------------------------------------------
;; BESCHRIFTUNG UND HOEHENMASS
;; ------------------------------------------------------------
;; Beschriftung wie bei VF_/GF_ (gf-make-label): ein TEXT, Hoehe 100, auf
;; einem eigenen Layer aus cfg/layer.cfg. Haupt- und Hilfsblock tragen
;; dieselbe Zeile: "KF_1 26 Grad-Zeichen H=1500mm L_horizontal=4951mm".
(defun kf-label-text (nummer winkel-str hoehe waagerecht)
(strcat "KF_" (itoa nummer) " " winkel-str (chr 176)
" H=" (itoa (fix (+ 0.5 (float hoehe)))) "mm"
" L_horizontal=" (itoa (fix (+ 0.5 (float waagerecht)))) "mm"))
;; Layer der Beschriftung (Baugruppe "kettenfoerderer_beschriftung" in
;; cfg/layer.cfg), sonst Hartwert - wie bei GF_/VF_.
(defun kf-beschriftungs-layer ( / lay)
(setq lay (ssg-layer-anlegen "kettenfoerderer_beschriftung" nil))
(if (null lay)
(progn (progn
(setq masse (kf-neigung-aus-punkten kette-start kette-ende basis-hz)) (setq lay "KF_Beschriftung")
(if (null masse) (ssg-make-layer lay "7" nil)))
(progn (princ (ssg-text "kf-2d-laenge-null")) nil) lay)
(progn
(setq laenge (car masse) winkel (cadr masse)) (defun kf-label-zeichnen (txt pt lay)
(if (null (kf-block-laden bname)) (entmake
(progn (princ (ssg-textf "kf-fehler-block-datei" (list bname))) nil) (list '(0 . "TEXT")
(progn (cons 10 pt)
(setq ks (ks-block-messen bname)) (cons 11 pt)
(setq ein-dx (car (cond ((ks-versatz ks)) ('(0.0 0.0 0.0))))) '(40 . 100.0)
(setq skala (kf-skala-berechnen laenge ein-dx)) (cons 1 txt)
(if (null skala) '(7 . "Standard")
(progn (princ (ssg-textf "kf-2d-ks-fehlt" (list bname))) nil) (cons 8 lay)
(progn '(72 . 1)
;; Ry(-winkel) hebt die flache Scheibe um die Steigung an; '(73 . 2)))
;; das Vorzeichen folgt der Rechte-Hand-Regel (positives dz (princ))
;; = Drehung gegen den Uhrzeigersinn um die Y-Achse).
(setq R (mat3-mul-mat3 (ks-rot-z basis-hz) ;; Senkrechtes Hoehenmass zwischen p-unten und p-oben, rechts neben dem
(ks-rot-y (- winkel)))) ;; Kettenende. Echtes DIMENSION-Objekt per ActiveX (AddDimRotated, 90 Grad) -
(insert-block-ks-matrix bname kette-start R skala) ;; kein Befehlsaufruf, der bei einem Fehler im Befehl haengen bleiben koennte.
(princ (ssg-textf "kf-2d-gebaut" ;; Jeder Schritt laeuft unter vl-catch-all-apply: scheitert das Mass, geht der
(list bname (rtos laenge 2 0) (rtos winkel 2 1)))) ;; Bau OHNE Mass weiter und meldet es (Hinweis, kein Abbruch). Textgroesse und
laenge))))))))) ;; Pfeile werden gesetzt, damit das Mass neben der 100-mm-Beschriftung lesbar
;; ist; fehlt eine Eigenschaft, bleibt der Wert der Bemassungsvariablen.
(defun kf-hoehenmass-zeichnen (p-unten p-oben lay / obj)
(setq obj (vl-catch-all-apply 'vla-AddDimRotated
(list modelspace
(vlax-3d-point p-unten)
(vlax-3d-point p-oben)
(vlax-3d-point (list (+ (car p-oben) 400.0)
(/ (+ (cadr p-unten) (cadr p-oben)) 2.0)
(caddr p-oben)))
(/ pi 2.0))))
(if (vl-catch-all-error-p obj)
(progn (princ (ssg-text "kf-bemassung-fehlt")) nil)
(progn
(foreach prop (list (list 'vla-put-TextHeight 100.0)
(list 'vla-put-ArrowheadSize 50.0)
(list 'vla-put-PrimaryUnitsPrecision 0)
(list 'vla-put-Layer lay))
(vl-catch-all-apply (car prop) (cons obj (cdr prop))))
T)))
;; Alles seit last-ent Entstandene als Auswahlsatz (fuer _.-BLOCK).
(defun kf-sammeln (last-ent / ss ent)
(setq ss (ssadd))
(setq ent (if last-ent (entnext last-ent) (entnext)))
(while ent
(ssadd ent ss)
(setq ent (entnext ent)))
ss)
;; Referenz um rot-rad (Bogenmass) um ihren Einfuegepunkt drehen. Der Block
;; wird immer ungedreht gebaut (ssg-block-wrap-welt fuegt mit Drehung 0 ein);
;; eine von Hand vorgenommene Drehung wird so beim Neuaufbau wieder aufgesetzt.
(defun kf-rotation-setzen (ent rot-rad / ed)
(if (and ent rot-rad (/= rot-rad 0.0))
(progn
(setq ed (entget ent))
(if (assoc 50 ed)
(entmod (subst (cons 50 rot-rad) (assoc 50 ed) ed)))))
ent)
;; ============================================================ ;; ============================================================
;; DREHWINKEL DES BLOCKS (XDATA) ;; SCHLUESSEL ZWISCHEN HAUPT- UND HILFSBLOCK (XDATA)
;; ============================================================ ;; ============================================================
;; Der Drehwinkel der Baugruppe wird beim Bau in die GEOMETRIE eingerechnet ;; Haupt- (KF_<n>) und Hilfsblock (KF2D_<n>) tragen denselben Schluessel als
;; (kf-matrix-aufrecht), nicht ueber die Drehung der Blockreferenz: ;; XDATA. Der Name taugt dafuer nicht: die Nummer wechselt bei JEDEM Neuaufbau
;; ssg-block-wrap-welt fuegt den fertigen Block immer mit Drehung 0 ein. Die ;; (KF_EDIT vergibt eine neue). Mit dem Schluessel findet KF_EDIT den
;; Referenz traegt damit nirgends mehr, wie die Kette gedreht wurde - KF_EDIT ;; Hilfsblock auch dann, wenn der Benutzer ihn verschoben hat, und erzeugt ihn
;; wuerde sie beim Neuaufbau ungedreht neu bauen. Darum steht der Winkel als ;; an seiner jetzigen Stelle neu (Projektregel: Editierdaten gehoeren in
;; XDATA am Block (Projektregel: Editierdaten gehoeren in XDATA, nicht in ;; XDATA, nicht in .dbg).
;; .dbg). Eine spaetere Drehung der Referenz von Hand kommt ueber
;; Gruppencode 50 hinzu und wird addiert.
(setq *kf-xdata-app* "SSG_KF_EDIT") (setq *kf-xdata-app* "SSG_KF_EDIT")
;; Gesamtdrehung = gebauter Winkel (XDATA, Grad) + Drehung der Referenz (defun kf-neuer-key () (ssg-timestamp))
;; (Gruppencode 50, Bogenmass), auf -180..180 Grad normalisiert. Reiner
;; Rechenkern; fehlende Werte zaehlen als 0 (Altbestand ohne XDATA).
(defun kf-drehung-gesamt (xdata-grad dxf50-rad / g r)
(setq g (cond (xdata-grad (float xdata-grad)) (0.0)))
(setq r (cond (dxf50-rad (float dxf50-rad)) (0.0)))
(ks-winkel-normalisieren (+ g (/ (* 180.0 r) pi))))
(defun kf-xdata-schreiben (ent drehung-grad / eintrag) (defun kf-xdata-schreiben (ent key / eintrag)
(regapp *kf-xdata-app*) (regapp *kf-xdata-app*)
(setq eintrag (list *kf-xdata-app* (setq eintrag (list *kf-xdata-app*
(cons 1000 "kf-edit") (cons 1000 "kf-edit")
(cons 1040 (float drehung-grad)))) (cons 1000 key)))
(entmod (append (entget ent) (list (list -3 eintrag)))) (entmod (append (entget ent) (list (list -3 eintrag))))
ent) ent)
;; Gebauter Drehwinkel (Grad) aus der XDATA, nil wenn keine vorhanden ist. ;; Schluessel aus einer entget-Liste mit XDATA (reiner Kern, testbar): das
(defun kf-xdata-lesen (ent / xd daten paar) ;; ZWEITE 1000-Feld; nil bei fehlender oder Altbestands-XDATA (nur Marker).
(setq xd (entget ent (list *kf-xdata-app*))) (defun kf-key-aus-xdata (xd / daten teile)
(setq daten (cdr (assoc -3 xd))) (setq daten (cdr (assoc -3 xd)) teile '())
(if daten (if daten
(foreach p (cdr (car daten))
(if (= (car p) 1000) (setq teile (append teile (list (cdr p)))))))
(if (>= (length teile) 2) (cadr teile) nil))
(defun kf-xdata-key (ent)
(kf-key-aus-xdata (entget ent (list *kf-xdata-app*))))
;; Hilfsblock (KF2D_*) mit diesem Schluessel suchen; nil, wenn es keinen gibt
;; (nie gebaut oder vom Benutzer geloescht).
(defun kf-hilfsblock-finden (key / ss i e treffer)
(setq treffer nil)
(if key
(progn (progn
(setq paar (assoc 1040 (cdr (car daten)))) (regapp *kf-xdata-app*)
(if paar (cdr paar) nil)) (setq ss (ssget "X" (list '(0 . "INSERT") '(2 . "KF2D_*")
nil)) (list -3 (list *kf-xdata-app*)))))
(if ss
(progn
(setq i 0)
(while (and (null treffer) (< i (sslength ss)))
(setq e (ssname ss i))
(if (equal (kf-xdata-key e) key) (setq treffer e))
(setq i (1+ i)))))))
treffer)
;; ============================================================
;; HILFSBLOCK KF2D_<n> (Draufsicht)
;; ============================================================
;; Die Draufsicht KF_2D ist KEIN Teil des Foerderers, sondern ein
;; Hilfsblock, an dem der Benutzer Schienen anschliesst. Darum:
;; - eigener Block KF2D_<n> (nicht KF_*): die Export-Muster erfassen ihn nicht,
;; exportiert wird nur der Hauptblock KF_<n>
;; - flach in der XY-Ebene, auf die PROJIZIERTE Laenge des Foerderers skaliert
;; - unabhaengig verschiebbar; loescht der Benutzer ihn, bleibt der Hauptblock
;; unberuehrt, und KF_EDIT erzeugt ihn bei Bedarf neu
;; - traegt dieselbe Beschriftung wie der Hauptblock
;; Rueckgabe: die Referenz, sonst nil.
(defun kf-hilfsblock-bauen (daten nummer key winkel-str hoehe waagerecht
einfuegepunkt rot-rad /
cfg bname ks ein-dx skala last-ent lay hname ss ins)
(setq cfg (ssg-json-get daten "ansicht_2d"))
(setq bname (if cfg (ssg-json-get cfg "block")))
(cond
((or (null bname) (= bname "")) nil)
((null (kf-block-laden bname))
(princ (ssg-textf "kf-fehler-block-datei" (list bname)))
nil)
(t
(setq ks (ks-block-messen bname))
(setq ein-dx (car (cond ((ks-versatz ks)) ('(0.0 0.0 0.0)))))
(setq skala (kf-skala-berechnen waagerecht ein-dx))
(if (null skala)
(progn (princ (ssg-textf "kf-2d-ks-fehlt" (list bname))) nil)
(progn
(setq hname (strcat "KF2D_" (itoa nummer)))
(setq last-ent (ssg-lastent-ohne-attribute))
(insert-block-ks-skaliert bname einfuegepunkt 0.0 skala)
(setq lay (kf-beschriftungs-layer))
(kf-label-zeichnen
(kf-label-text nummer winkel-str hoehe waagerecht)
(list (+ (car einfuegepunkt) (/ waagerecht 2.0))
(+ (cadr einfuegepunkt) 250.0)
(cond ((caddr einfuegepunkt)) (0.0)))
lay)
(setq ss (kf-sammeln last-ent))
(ssg-block-wrap-welt hname einfuegepunkt ss)
(setq ins (entlast))
(ssg-layer-setzen ins (cond ((ssg-json-get daten "layer")) ("kettenfoerderer")))
(kf-rotation-setzen ins rot-rad)
(kf-xdata-schreiben ins key)
(princ (ssg-textf "kf-2d-gebaut" (list hname (rtos waagerecht 2 0))))
ins)))))
;; ============================================================ ;; ============================================================
;; BLOCK ABSCHLIESSEN ;; BLOCK ABSCHLIESSEN
;; ============================================================ ;; ============================================================
;; Sammelt alle seit last-ent erzeugten Objekte, legt die ATTDEFs an, fasst ;; Beschriftung und Hoehenmass zeichnen, alle seit last-ent erzeugten Objekte
;; alles zu KF_<n> zusammen und beschriftet den fertigen Block. ;; sammeln, ATTDEFs anlegen und alles zu KF_<n> zusammenfassen. Vorbild:
;; Vorbild: gf-block-erstellen (Gefaellestrecke.lsp). ;; gf-block-erstellen (Gefaellestrecke.lsp).
(defun kf-block-erstellen (variante geo hoehe laenge verbindung ;; waagerecht = projizierte Laenge (Attribut L_KF_mm), key = Schluessel zum
startpunkt last-ent kf-nummer ;; Hilfsblock, rot-rad = von Hand gesetzte Drehung der Referenz (0 beim ersten
farbe antriebsmotor movimot steuerung drehung / ;; Einfuegen).
kf-bname kf-ss ent kf-insert winkel-str) (defun kf-block-erstellen (variante geo hoehe waagerecht verbindung
startpunkt endpunkt last-ent kf-nummer
farbe antriebsmotor movimot steuerung key rot-rad /
kf-bname kf-ss kf-insert winkel-str lay z)
(setq kf-bname (strcat "KF_" (itoa kf-nummer))) (setq kf-bname (strcat "KF_" (itoa kf-nummer)))
;; Winkel als nackte Ganzzahl ("26"/"30") - bewusst ueber itoa/fix statt (setq winkel-str (kf-winkel-text variante))
;; rtos: rtos haengt an DIMZIN/LUPREC/LUNITS der Zeichnung und liefert je (setq z (cond ((caddr startpunkt)) (0.0)))
;; nach Zeichnungsprofil unterschiedliche Schreibweisen. Dieser Wert landet
;; als Attribut WINKEL am Block und wird vom Sivas-Export als Schluessel der
;; Tabelle [kf_typ_anzeige] verwendet - er muss profilunabhaengig sein
;; (gleiche Ueberlegung wie bei ssg-sepliste-fmt-koord).
(setq winkel-str (itoa (fix (+ 0.5 (kf-zahl (ssg-json-get variante "winkel"))))))
;; ATTDEFs (unsichtbar, am Startpunkt) nach dem KF-Schema ;; ATTDEFs (unsichtbar, am Startpunkt) nach dem KF-Schema
(foreach def (kf-attrib-defs) (foreach def (kf-attrib-defs)
(entmake (entmake
@@ -596,18 +689,20 @@
'(70 . 1) '(70 . 1)
'(72 . 0) '(72 . 0)
'(74 . 0)))) '(74 . 0))))
;; Alle Entities seit last-ent einsammeln ;; Beschriftung unter der Kette, Hoehenmass rechts neben dem Kettenende
(setq kf-ss (ssadd)) (setq lay (kf-beschriftungs-layer))
(setq ent (if last-ent (entnext last-ent) (entnext))) (kf-label-zeichnen
(while ent (kf-label-text kf-nummer winkel-str hoehe waagerecht)
(ssadd ent kf-ss) (list (/ (+ (car startpunkt) (car endpunkt)) 2.0) (- (cadr startpunkt) 300.0) z)
(setq ent (entnext ent))) lay)
(kf-hoehenmass-zeichnen (list (car endpunkt) (cadr startpunkt) z)
(list (car endpunkt) (cadr endpunkt) z)
lay)
(setq kf-ss (kf-sammeln last-ent))
;; Block definieren und einfuegen (OSMODE/ELEVATION/BKS werden dabei sauber ;; Block definieren und einfuegen (OSMODE/ELEVATION/BKS werden dabei sauber
;; abgeschaltet, siehe ssg-block-wrap-welt) ;; abgeschaltet, siehe ssg-block-wrap-welt)
(ssg-block-wrap-welt kf-bname startpunkt kf-ss) (ssg-block-wrap-welt kf-bname startpunkt kf-ss)
(setq kf-insert (entlast)) (setq kf-insert (entlast))
;; Gebauten Drehwinkel festhalten, damit KF_EDIT ihn wiederfindet
(kf-xdata-schreiben kf-insert drehung)
(ssg-layer-setzen kf-insert (cond ((ssg-json-get variante "layer")) (ssg-layer-setzen kf-insert (cond ((ssg-json-get variante "layer"))
((ssg-json-get (kf-daten) "layer")) ((ssg-json-get (kf-daten) "layer"))
("kettenfoerderer"))) ("kettenfoerderer")))
@@ -620,7 +715,9 @@
("Schraegfoerderer Stahl"))) ("Schraegfoerderer Stahl")))
(cons "WINKEL" winkel-str) (cons "WINKEL" winkel-str)
(cons "H_EINBAUHOEHE_mm" (itoa (fix (+ 0.5 (float hoehe))))) (cons "H_EINBAUHOEHE_mm" (itoa (fix (+ 0.5 (float hoehe)))))
(cons "L_KF_mm" (itoa (fix (+ 0.5 (float laenge))))) ;; WAAGERECHTE (projizierte) Laenge des ganzen Foerderers, nicht die
;; Laenge des schraegen Mittelstuecks
(cons "L_KF_mm" (itoa (fix (+ 0.5 (float waagerecht)))))
(cons "FARBE" (cond (farbe) ("RAL 7035"))) (cons "FARBE" (cond (farbe) ("RAL 7035")))
(cons "ANTRIEBSMOTOR" (cond (antriebsmotor) ("Europa 50 Hz"))) (cons "ANTRIEBSMOTOR" (cond (antriebsmotor) ("Europa 50 Hz")))
(cons "ANTRIEBSART" (kf-antriebsart (kf-daten))) (cons "ANTRIEBSART" (kf-antriebsart (kf-daten)))
@@ -629,6 +726,9 @@
(cons "VERBINDUNGSELEMENTE" (cond (verbindung) ("Nein"))) (cons "VERBINDUNGSELEMENTE" (cond (verbindung) ("Nein")))
(cons "ARTINR" (cond ((ssg-json-get variante "sivas_id")) (""))))) (cons "ARTINR" (cond ((ssg-json-get variante "sivas_id")) ("")))))
(ssg-id-generate kf-insert) (ssg-id-generate kf-insert)
;; Reihenfolge: erst Drehung (entmod der Referenz), ZULETZT die XDATA
(kf-rotation-setzen kf-insert rot-rad)
(kf-xdata-schreiben kf-insert key)
kf-insert) kf-insert)
;; Naechste freie KF-Nummer in der Zeichnung. ;; Naechste freie KF-Nummer in der Zeichnung.
@@ -655,9 +755,10 @@
(while (apply belegt-p (list n)) (setq n (1+ n))) (while (apply belegt-p (list n)) (setq n (1+ n)))
n) n)
;; Ist die Block-DEFINITION KF_<n> in der Zeichnung vorhanden? ;; Ist die Block-DEFINITION KF_<n> ODER KF2D_<n> in der Zeichnung vorhanden?
(defun kf-nummer-belegt-p (n) (defun kf-nummer-belegt-p (n)
(not (null (tblsearch "BLOCK" (strcat "KF_" (itoa n)))))) (not (null (or (tblsearch "BLOCK" (strcat "KF_" (itoa n)))
(tblsearch "BLOCK" (strcat "KF2D_" (itoa n)))))))
;; Naechste freie KF-Nummer. Bewusst NICHT nur ueber die Referenzen im Modell: ;; Naechste freie KF-Nummer. Bewusst NICHT nur ueber die Referenzen im Modell:
;; loescht man einen Foerderer (oder baut KF_EDIT ihn neu auf), lebt seine ;; loescht man einen Foerderer (oder baut KF_EDIT ihn neu auf), lebt seine
@@ -756,8 +857,8 @@
;; zwei Punkte in der Zeichnung gewaehlt (z.B. Oberkante Zufuehrung und ;; zwei Punkte in der Zeichnung gewaehlt (z.B. Oberkante Zufuehrung und
;; Oberkante Abgabe) und ihr Hoehenunterschied uebernommen. ;; Oberkante Abgabe) und ihr Hoehenunterschied uebernommen.
;; ;;
;; Gemessen wird die Differenz in Z: die Baugruppe steht aufgerichtet im Modell ;; Gemessen wird die Differenz in Z (die Hoehe der Punkte in der Zeichnung); die
;; (siehe kf-matrix-aufrecht), ihre Hoehe ist also die Hoehe der Zeichnung. ;; Differenz in Y wird nur mit ausgegeben.
;; Damit nachvollziehbar bleibt, was gemessen wurde, nennt die Meldung BEIDE ;; Damit nachvollziehbar bleibt, was gemessen wurde, nennt die Meldung BEIDE
;; Differenzen (dZ und dY): wer in einer DRAUFSICHT pickt, sieht sofort, dass ;; Differenzen (dZ und dY): wer in einer DRAUFSICHT pickt, sieht sofort, dass
;; dort dZ = 0 ist, und kann es melden. ;; dort dZ = 0 ist, und kann es melden.
@@ -981,72 +1082,91 @@
(kf-auswahl-wert movimots *kf-dlg-movimot* "0") (kf-auswahl-wert movimots *kf-dlg-movimot* "0")
(kf-auswahl-wert steuerungen *kf-dlg-steuerung* "0"))) (kf-auswahl-wert steuerungen *kf-dlg-steuerung* "0")))
;; ============================================================
;; KF_BAUEN - gemeinsamer Bau fuer Befehl und Edit
;; ============================================================
;; Baut Hauptblock KF_<n> (Seitenansicht mit Beschriftung + Hoehenmass) und
;; danach den Hilfsblock KF2D_<n> (Draufsicht). Beide Bloecke werden ungedreht
;; gebaut; eine Drehung (Edit eines von Hand gedrehten Blocks) setzt
;; kf-rotation-setzen danach wieder auf.
;; eingabe = Ergebnis von kf-dialog/kf-konsole (Variante, Hoehe, ...)
;; startpunkt = KS_EIN der Umlenkstation (Einfuegepunkt Hauptblock)
;; rot-rad = Drehung des Hauptblocks, hilfs-pt/hilfs-rot = Lage des Hilfsblocks
;; key = Schluessel Haupt <-> Hilfsblock (nil -> neuer)
;; Rueckgabe: Liste (Blockname Hoehe Laenge) bei Erfolg, sonst nil (die
;; Fehlermeldung ist dann schon ausgegeben, die Zeichnung unveraendert).
(defun kf-bauen (daten eingabe startpunkt rot-rad hilfs-pt hilfs-rot key
loeschen /
variante hoehe verbindung farbe antriebsmotor movimot steuerung
geo erg laenge skala lmin lmax endpunkt waagerecht
last-ent kf-nummer winkel-str)
(setq variante (nth 0 eingabe)
hoehe (nth 1 eingabe)
verbindung (nth 2 eingabe)
farbe (nth 3 eingabe)
antriebsmotor (nth 4 eingabe)
movimot (nth 5 eingabe)
steuerung (nth 6 eingabe))
(setq lmin (kf-zahl (ssg-json-get (ssg-json-get daten "laenge") "min"))
lmax (kf-zahl (ssg-json-get (ssg-json-get daten "laenge") "max")))
(setq geo (kf-geometrie-messen variante))
(setq erg (kf-laenge-fuer-hoehe geo hoehe lmin lmax))
(cond
((caddr erg)
(if (ssg-gui-p) (alert (ssg-text (caddr erg))))
(princ (ssg-text (caddr erg)))
nil)
(t
(setq laenge (car erg) skala (cadr erg))
(ssg-start "Kettenfoerderer" '(("OSMODE") ("ATTREQ") ("ATTDIA")))
(setvar "OSMODE" 0)
(setvar "ATTREQ" 0)
(setvar "ATTDIA" 0)
;; Edit: alte Bloecke (Liste von Enames) erst jetzt loeschen
(foreach e loeschen (if e (entdel e)))
(if (null key) (setq key (kf-neuer-key)))
(setq last-ent (ssg-lastent-ohne-attribute))
(setq kf-nummer (kf-naechste-nummer))
(setq winkel-str (kf-winkel-text variante))
(setq endpunkt (kf-kette-bauen variante geo skala startpunkt))
(setq waagerecht (kf-waagerecht-laenge startpunkt endpunkt 0.0))
(kf-block-erstellen variante geo hoehe waagerecht verbindung
startpunkt endpunkt last-ent kf-nummer
farbe antriebsmotor movimot steuerung key rot-rad)
(kf-hilfsblock-bauen daten kf-nummer key winkel-str hoehe waagerecht
hilfs-pt hilfs-rot)
(ssg-end)
(list (strcat "KF_" (itoa kf-nummer)) hoehe laenge))))
;; ============================================================ ;; ============================================================
;; C:KETTENFOERDERER - Hauptbefehl ;; C:KETTENFOERDERER - Hauptbefehl
;; ============================================================ ;; ============================================================
(defun c:KETTENFOERDERER ( / daten eingabe variante hoehe verbindung (defun c:KETTENFOERDERER ( / daten eingabe startpunkt erg t0)
farbe antriebsmotor movimot steuerung
geo erg laenge skala endpunkt
lmin lmax startpunkt drehung last-ent kf-nummer ent
t0)
;; Laufzeit des Dialogaufbaus protokollieren (Schalter "kf-zeit", siehe ;; Laufzeit des Dialogaufbaus protokollieren (Schalter "kf-zeit", siehe
;; ssg_dbg.lsp). Hintergrund: der Dialog erschien beim ersten Menueklick ;; ssg_dbg.lsp): unterscheidet Lazy-Laden, JSON-Lesen und Dialogaufbau.
;; spuerbar verzoegert. Statt zu raten, wird die Zeit bis zum Dialog
;; gemessen - so laesst sich unterscheiden, ob das Lazy-Laden des Moduls,
;; das Lesen der JSON oder der Dialogaufbau selbst die Zeit kostet.
(setq t0 (getvar "MILLISECS")) (setq t0 (getvar "MILLISECS"))
(setq daten (kf-daten)) (setq daten (kf-daten))
(kf-zeit "Daten geladen" t0) (kf-zeit "Daten geladen" t0)
(if (null daten) (if (null daten)
(princ (ssg-text "kf-fehler-daten-leer")) (princ (ssg-text "kf-fehler-daten-leer"))
(progn (progn
(setq lmin (kf-zahl (ssg-json-get (ssg-json-get daten "laenge") "min"))
lmax (kf-zahl (ssg-json-get (ssg-json-get daten "laenge") "max")))
(kf-zeit "vor Dialog" t0) (kf-zeit "vor Dialog" t0)
(setq eingabe (if (ssg-gui-p) (kf-dialog daten) (kf-konsole daten))) (setq eingabe (if (ssg-gui-p) (kf-dialog daten) (kf-konsole daten)))
(if (null eingabe) (if (null eingabe)
(princ (ssg-text "kf-abgebrochen")) (princ (ssg-text "kf-abgebrochen"))
(progn (progn
(setq variante (nth 0 eingabe) (setq startpunkt (getpoint (ssg-text "kf-prompt-startpunkt")))
hoehe (nth 1 eingabe) (if (null startpunkt)
verbindung (nth 2 eingabe) (princ (ssg-text "kf-abgebrochen"))
farbe (nth 3 eingabe)
antriebsmotor (nth 4 eingabe)
movimot (nth 5 eingabe)
steuerung (nth 6 eingabe))
(setq geo (kf-geometrie-messen variante))
(setq erg (kf-laenge-fuer-hoehe geo hoehe lmin lmax))
(if (caddr erg)
(progn (progn
(if (ssg-gui-p) (alert (ssg-text (caddr erg)))) ;; Hilfsblock beim ersten Mal 1000 mm unter dem Hauptblock
(princ (ssg-text (caddr erg)))) (setq erg (kf-bauen daten eingabe startpunkt 0.0
(progn (kf-versatzpunkt startpunkt (kf-hilfsblock-versatz daten))
(setq laenge (car erg) skala (cadr erg)) 0.0 nil nil))
(setq startpunkt (getpoint (ssg-text "kf-prompt-startpunkt"))) (if erg
(if (null startpunkt) (princ (ssg-textf "kf-fertig"
(princ (ssg-text "kf-abgebrochen")) (list (car erg) (rtos (float (cadr erg)) 2 0)
(progn (rtos (caddr erg) 2 0)))))))))))
(setq drehung (getangle startpunkt (ssg-text "kf-prompt-drehung")))
(setq drehung (if drehung (/ (* 180.0 drehung) pi) 0.0))
(ssg-start "Kettenfoerderer" '(("OSMODE") ("ATTREQ") ("ATTDIA")))
(setvar "OSMODE" 0)
(setvar "ATTREQ" 0)
(setvar "ATTDIA" 0)
(setq last-ent (ssg-lastent-ohne-attribute))
(setq kf-nummer (kf-naechste-nummer))
;; Kette und Draufsicht liegen DECKUNGSGLEICH - beide
;; beginnen am gewaehlten Punkt. Die Draufsicht gehoert mit
;; in dieselbe Baugruppe, darum VOR kf-block-erstellen.
(setq endpunkt (kf-kette-bauen variante geo skala startpunkt drehung))
(kf-ansicht-2d-bauen daten startpunkt endpunkt drehung)
(setq ent (kf-block-erstellen variante geo hoehe laenge verbindung
startpunkt last-ent kf-nummer farbe antriebsmotor movimot
steuerung drehung))
(ssg-end)
(princ (ssg-textf "kf-fertig"
(list (strcat "KF_" (itoa kf-nummer))
(rtos (float hoehe) 2 0)
(rtos laenge 2 0))))))))))))
(princ)) (princ))
;; ============================================================ ;; ============================================================
@@ -1056,10 +1176,11 @@
;; an SEINER Stelle neu auf. Vorbild: GEFAELLESTRECKE_EDIT. ;; an SEINER Stelle neu auf. Vorbild: GEFAELLESTRECKE_EDIT.
;; ;;
;; Einfuegepunkt und Drehung kommen aus dem Block selbst (Gruppencode 10 und ;; Einfuegepunkt und Drehung kommen aus dem Block selbst (Gruppencode 10 und
;; 50), nicht aus XDATA: der Kettenfoerderer hat keine Aufbau-Historie, die ;; 50), alles Uebrige steht in den Attributen. Ein von Hand verschobener oder
;; ueber diese beiden Werte hinausgeht - alles Uebrige steht in den ;; gedrehter Block wird dadurch an seiner NEUEN Lage neu gebaut. Die XDATA
;; Attributen. Ein von Hand verschobener oder gedrehter Block wird dadurch ;; traegt nur den Schluessel zum Hilfsblock KF2D_<n>: er wird mit dem Hauptblock
;; korrekt an seiner NEUEN Lage neu gebaut. ;; neu erzeugt (auch wenn der Benutzer ihn geloescht hatte) und behaelt die
;; Lage, an die der Benutzer ihn zuletzt gelegt hat.
;; ;;
;; Der alte Block wird erst geloescht, wenn der Benutzer den Dialog mit OK ;; Der alte Block wird erst geloescht, wenn der Benutzer den Dialog mit OK
;; bestaetigt hat - ein Abbruch laesst die Zeichnung unveraendert. ;; bestaetigt hat - ein Abbruch laesst die Zeichnung unveraendert.
@@ -1115,13 +1236,10 @@
(setq i (1+ i))) (setq i (1+ i)))
(cond (treffer) (0))) (cond (treffer) (0)))
(defun c:KF_EDIT ( / ss ent ed bname attribs daten eingabe variante hoehe (defun c:KF_EDIT ( / ss ent ed bname attribs daten eingabe startpunkt
verbindung farbe antriebsmotor movimot steuerung rot-rad key hilfs hilfs-pt hilfs-rot erg)
geo erg laenge skala lmin lmax startpunkt drehung
last-ent kf-nummer endpunkt)
;; Erst die VORGEWAEHLTE Auswahl (Doppelklick setzt sie), nur ohne sie per ;; Erst die VORGEWAEHLTE Auswahl (Doppelklick setzt sie), nur ohne sie per
;; Klick nachfragen - wie GEFAELLESTRECKE_EDIT/KreiselEdit. Vorher fragte ;; Klick nachfragen - wie GEFAELLESTRECKE_EDIT/KreiselEdit.
;; KF_EDIT auch nach einem Doppelklick noch einmal nach einer Auswahl.
(setq ss (ssget "I")) (setq ss (ssget "I"))
(setq ent (if (and ss (= (sslength ss) 1)) (setq ent (if (and ss (= (sslength ss) 1))
(ssname ss 0) (ssname ss 0)
@@ -1132,8 +1250,7 @@
(setq ed (entget ent)) (setq ed (entget ent))
(setq bname (cdr (assoc 2 ed))) (setq bname (cdr (assoc 2 ed)))
;; Nur echte Foerderer "KF_<n>": die Bausteine (KF_UM_26, KF_2D, ...) ;; Nur echte Foerderer "KF_<n>": die Bausteine (KF_UM_26, KF_2D, ...)
;; tragen ebenfalls das Praefix KF_, haben aber keine Attribute - ein ;; tragen ebenfalls das Praefix KF_, haben aber keine Attribute.
;; Neuaufbau aus ihnen waere sinnlos und wuerde sie loeschen.
(if (or (/= (cdr (assoc 0 ed)) "INSERT") (null (kf-nummer-aus-name bname))) (if (or (/= (cdr (assoc 0 ed)) "INSERT") (null (kf-nummer-aus-name bname)))
(alert (ssg-text "kf-edit-kein-kf")) (alert (ssg-text "kf-edit-kein-kf"))
(progn (progn
@@ -1141,53 +1258,35 @@
(if (null daten) (if (null daten)
(princ (ssg-text "kf-fehler-daten-leer")) (princ (ssg-text "kf-fehler-daten-leer"))
(progn (progn
(setq attribs (ssg-attrib-read ent)) (setq attribs (ssg-attrib-read ent))
(setq startpunkt (cdr (assoc 10 ed))) (setq startpunkt (cdr (assoc 10 ed)))
;; Der gebaute Winkel steckt in der XDATA, eine nachtraegliche (setq rot-rad (cond ((cdr (assoc 50 ed))) (0.0)))
;; Drehung der Referenz in Gruppencode 50 - beides zaehlt. ;; Schluessel zum Hilfsblock; Altbestand ohne Schluessel -> nil,
(setq drehung (kf-drehung-gesamt (kf-xdata-lesen ent) ;; dann gibt es keinen auffindbaren Hilfsblock und ein neuer
(cdr (assoc 50 ed)))) ;; entsteht an der Standardposition.
(setq key (kf-xdata-key ent))
(setq hilfs (kf-hilfsblock-finden key))
(if hilfs
(setq hilfs-pt (cdr (assoc 10 (entget hilfs)))
hilfs-rot (cond ((cdr (assoc 50 (entget hilfs)))) (0.0)))
(setq hilfs-pt (kf-versatzpunkt startpunkt
(kf-hilfsblock-versatz daten))
hilfs-rot 0.0))
(kf-dlg-aus-attributen daten attribs) (kf-dlg-aus-attributen daten attribs)
(setq lmin (kf-zahl (ssg-json-get (ssg-json-get daten "laenge") "min"))
lmax (kf-zahl (ssg-json-get (ssg-json-get daten "laenge") "max")))
(setq eingabe (if (ssg-gui-p) (kf-dialog daten) (kf-konsole daten))) (setq eingabe (if (ssg-gui-p) (kf-dialog daten) (kf-konsole daten)))
(if (null eingabe) (if (null eingabe)
(princ (ssg-text "kf-abgebrochen")) (princ (ssg-text "kf-abgebrochen"))
(progn (progn
(setq variante (nth 0 eingabe) ;; Der Hilfsblock wird bei JEDEM Edit neu erzeugt, damit er
hoehe (nth 1 eingabe) ;; zur neuen Hoehe/Laenge passt - an der Stelle, an der der
verbindung (nth 2 eingabe) ;; Benutzer ihn zuletzt hingelegt hat.
farbe (nth 3 eingabe) (setq erg
antriebsmotor (nth 4 eingabe) (kf-bauen daten eingabe startpunkt rot-rad hilfs-pt hilfs-rot
movimot (nth 5 eingabe) (cond (key) (kf-neuer-key))
steuerung (nth 6 eingabe)) (list ent hilfs)))
(setq geo (kf-geometrie-messen variante)) (if erg
(setq erg (kf-laenge-fuer-hoehe geo hoehe lmin lmax)) (princ (ssg-textf "kf-edit-fertig"
(if (caddr erg) (list bname (car erg)))))))))))))
(progn
(if (ssg-gui-p) (alert (ssg-text (caddr erg))))
(princ (ssg-text (caddr erg))))
(progn
(setq laenge (car erg) skala (cadr erg))
(ssg-start "Kettenfoerderer Edit"
'(("OSMODE") ("ATTREQ") ("ATTDIA")))
(setvar "OSMODE" 0)
(setvar "ATTREQ" 0)
(setvar "ATTDIA" 0)
;; Erst jetzt loeschen - ein Abbruch oben laesst die
;; Zeichnung unveraendert.
(entdel ent)
(setq last-ent (ssg-lastent-ohne-attribute))
(setq kf-nummer (kf-naechste-nummer))
(setq endpunkt (kf-kette-bauen variante geo skala
startpunkt drehung))
(kf-ansicht-2d-bauen daten startpunkt endpunkt drehung)
(kf-block-erstellen variante geo hoehe laenge verbindung
startpunkt last-ent kf-nummer farbe antriebsmotor
movimot steuerung drehung)
(ssg-end)
(princ (ssg-textf "kf-edit-fertig"
(list bname (strcat "KF_" (itoa kf-nummer)))))))))))))))
(princ)) (princ))
;; ============================================================ ;; ============================================================
+6
View File
@@ -462,6 +462,12 @@
) )
) )
;; Hilfsblock (Draufsicht) des Kettenfoerderers: kein eigener Edit - er wird
;; mit dem Hauptblock KF_<n> neu erzeugt (Doppelklick dort).
((wcmatch bname "KF2D_*")
(princ (ssg-text "cmd-kf2d-hilfsblock"))
)
;; Omniflo-Bloecke -> OMNI_EDIT ;; Omniflo-Bloecke -> OMNI_EDIT
;; Erkennung: TEILEART-Attribut vorhanden, AP110-Blockname oder rein numerisch ;; Erkennung: TEILEART-Attribut vorhanden, AP110-Blockname oder rein numerisch
((progn ((progn
+16 -4
View File
@@ -85,10 +85,21 @@
;; Realer Fehler, den das behebt: die KS-Messung eines KF-Bausteins lief ins ;; Realer Fehler, den das behebt: die KS-Messung eines KF-Bausteins lief ins
;; Leere und meldete "KS_EIN/KS_AUS fehlt", obwohl beide KS im Block vorhanden ;; Leere und meldete "KS_EIN/KS_AUS fehlt", obwohl beide KS im Block vorhanden
;; waren - gesucht wurde nur ein Blockname, den es nicht gibt. ;; waren - gesucht wurde nur ein Blockname, den es nicht gibt.
;;
;; Zweiter Sonderfall - Namen, die SELBST auf _2D/_3D enden (z.B. die
;; Kettenfoerderer-Draufsicht "KF_2D"): ssg-ils-blockname laesst solche Namen
;; unveraendert (eff = blockname), die Bedingung "eff existiert nicht" kann
;; dann nie zutreffen, und der Block ging in den ILS-Ladepfad. Der suchte
;; data/ils/KF_2D.dwg und meldete "Block-Datei nicht gefunden", obwohl der
;; Block in der Zeichnung vorhanden war. Massgeblich ist darum, ob es fuer den
;; Namen ueberhaupt eine ILS-Datei gibt: ohne Datei ist es kein ILS-Block und
;; die vorhandene Definition wird genommen. Echte ILS-Bloecke haben eine Datei
;; und laufen unveraendert durch den Ladepfad (Aktualisierung aus der Datei).
(defun ensure-block-loaded (blockname / eff) (defun ensure-block-loaded (blockname / eff)
(setq eff (ssg-ils-blockname blockname)) (setq eff (ssg-ils-blockname blockname))
(if (and (not (tblsearch "BLOCK" eff)) (if (and (tblsearch "BLOCK" blockname)
(tblsearch "BLOCK" blockname)) (or (= eff blockname) (not (tblsearch "BLOCK" eff)))
(null (ssg-ils-block-datei blockname)))
blockname blockname
(progn (progn
(if (not (ssg-ils-block-laden blockname)) (if (not (ssg-ils-block-laden blockname))
@@ -558,8 +569,9 @@
;; Bewusst matrixbasiert statt mit einem weiteren Winkelparameter: die ;; Bewusst matrixbasiert statt mit einem weiteren Winkelparameter: die
;; Aufrichtung ist nur EIN Anwendungsfall, und jeder weitere haette eine ;; Aufrichtung ist nur EIN Anwendungsfall, und jeder weitere haette eine
;; weitere Spezialfunktion gebraucht. Den Aufrufern bleibt freigestellt, ;; weitere Spezialfunktion gebraucht. Den Aufrufern bleibt freigestellt,
;; welche Drehungen sie kombinieren (beim Kettenfoerderer ;; welche Drehungen sie kombinieren. (Der Kettenfoerderer hat sie kurz fuer
;; Rz(basis) * Rx(90) * Rz(eigen), siehe kf-matrix-aufrecht). ;; Rz(basis) * Rx(90) * Rz(eigen) genutzt, ist inzwischen aber wieder eine flache
;; Seitenansicht - aktuell ohne Aufrufer.)
;; Elementare Rotationsmatrizen (3x3, Winkel in GRAD). ;; Elementare Rotationsmatrizen (3x3, Winkel in GRAD).
(defun ks-rot-x (grad / r c s) (defun ks-rot-x (grad / r c s)
+2
View File
@@ -174,6 +174,8 @@ planquadrate = ZZ_Planquadrate, 8
# ZUSAMMENGEBAUTE Baugruppe ohne eigene Blockdatei, der Layer muss darum hier # ZUSAMMENGEBAUTE Baugruppe ohne eigene Blockdatei, der Layer muss darum hier
# stehen. Die drei Bausteine darin behalten ihre eigenen Layer. # stehen. Die drei Bausteine darin behalten ihre eigenen Layer.
kettenfoerderer = Omni_Kettenfoerderer, 4 kettenfoerderer = Omni_Kettenfoerderer, 4
; Beschriftung (KF_n ... H=, L projiziert=) und Hoehenmass, auf Haupt- und Hilfsblock
kettenfoerderer_beschriftung = KF_Beschriftung, 7
# ============================================================ # ============================================================
+2 -2
View File
@@ -9,8 +9,8 @@
"einbauhoehe": { "vorgabe": 1500, "min": 300, "max": 8000 }, "einbauhoehe": { "vorgabe": 1500, "min": 300, "max": 8000 },
"laenge": { "min": 100, "max": 20000 }, "laenge": { "min": 100, "max": 20000 },
"_hinweis_ansicht": "KF_2D liegt DECKUNGSGLEICH mit der Kette (KS_EIN auf KS_EIN der Umlenkung, KS_AUS auf KS_AUS des Antriebs) - die Baugruppe ist EIN Koerper. Den frueheren Versatz (versatz_y) gibt es nicht mehr, seit die Kette aufgerichtet eingebaut wird.", "_hinweis_ansicht": "KF_2D ist ein eigener Hilfsblock KF2D_<n> (Draufsicht zum Anschliessen von Schienen), kein Teil des Foerderers. Er wird auf die projizierte Laenge skaliert und beim ERSTEN Einfuegen versatz_y mm (Y) gegen den Hauptblock versetzt; danach liegt er, wo der Benutzer ihn hinlegt.",
"ansicht_2d": { "block": "KF_2D" }, "ansicht_2d": { "block": "KF_2D", "versatz_y": -1000 },
"_hinweis_antriebsart": "Merkmal 357 ist beim Kettenfoerderer FEST: die Liste hat genau EINEN Eintrag, der im Dialog als Dropdown erscheint und zwingend gewaehlt ist. Weitere Antriebsarten (z.B. 0.55 kW) waeren einfach eine weitere Zeile.", "_hinweis_antriebsart": "Merkmal 357 ist beim Kettenfoerderer FEST: die Liste hat genau EINEN Eintrag, der im Dialog als Dropdown erscheint und zwingend gewaehlt ist. Weitere Antriebsarten (z.B. 0.55 kW) waeren einfach eine weitere Zeile.",
"antriebsart": [ "antriebsart": [
+4 -4
View File
@@ -1295,11 +1295,11 @@
"kf-prompt-messpunkt-2": "\nZweiten Messpunkt waehlen (Hoehenbezug oben): ", "kf-prompt-messpunkt-2": "\nZweiten Messpunkt waehlen (Hoehenbezug oben): ",
"kf-messung-ergebnis": "\n>>> Gemessen: Hoehendifferenz dZ = %1 mm (zur Kontrolle dY = %2 mm).", "kf-messung-ergebnis": "\n>>> Gemessen: Hoehendifferenz dZ = %1 mm (zur Kontrolle dY = %2 mm).",
"kf-konsole-hoehe-hinweis": "\n(0 eingeben, um die Hoehe aus zwei Punkten zu messen)", "kf-konsole-hoehe-hinweis": "\n(0 eingeben, um die Hoehe aus zwei Punkten zu messen)",
"kf-2d-gebaut": "\n>>> Draufsicht %1 eingefuegt: Schraeglaenge %2 mm, Neigung %3 Grad.", "kf-2d-gebaut": "\n>>> Hilfsblock %1 (Draufsicht) eingefuegt: KF waagerechte Laenge %2 mm.",
"kf-2d-laenge-null": "\nDraufsicht uebersprungen: die Kette hat keine messbare Grundrisslaenge.", "kf-bemassung-fehlt": "\nHinweis: Das Hoehenmass konnte nicht gezeichnet werden - der Foerderer wurde trotzdem gebaut.",
"cmd-kf2d-hilfsblock": "\nHilfsblock (Draufsicht): kein eigener Dialog - Doppelklick auf den Hauptblock KF_<n> erzeugt ihn neu.",
"kf-2d-ks-fehlt": "\nDraufsicht uebersprungen: %1 hat kein messbares KS_EIN/KS_AUS.", "kf-2d-ks-fehlt": "\nDraufsicht uebersprungen: %1 hat kein messbares KS_EIN/KS_AUS.",
"kf-prompt-startpunkt": "\nEinfuegepunkt waehlen (KS_EIN der Draufsicht; die Seitenansicht entsteht darueber): ", "kf-prompt-startpunkt": "\nEinfuegepunkt waehlen (KS_EIN der Umlenkstation): ",
"kf-prompt-drehung": "\nDrehwinkel der Kette: ",
"kf-abgebrochen": "\nAbgebrochen.", "kf-abgebrochen": "\nAbgebrochen.",
"kf-fertig": "\n>>> %1 erstellt: Einbauhoehe %2 mm, Zwischenstueck %3 mm.", "kf-fertig": "\n>>> %1 erstellt: Einbauhoehe %2 mm, Zwischenstueck %3 mm.",
"kf-konsole-variante-header": "\n\nSchraegfoerderer Stahl Typ waehlen:", "kf-konsole-variante-header": "\n\nSchraegfoerderer Stahl Typ waehlen:",
+4 -4
View File
@@ -1295,11 +1295,11 @@
"kf-prompt-messpunkt-2": "\nSelect second measuring point (upper reference): ", "kf-prompt-messpunkt-2": "\nSelect second measuring point (upper reference): ",
"kf-messung-ergebnis": "\n>>> Measured: height difference dZ = %1 mm (for reference dY = %2 mm).", "kf-messung-ergebnis": "\n>>> Measured: height difference dZ = %1 mm (for reference dY = %2 mm).",
"kf-konsole-hoehe-hinweis": "\n(enter 0 to measure the height from two points)", "kf-konsole-hoehe-hinweis": "\n(enter 0 to measure the height from two points)",
"kf-2d-gebaut": "\n>>> Plan view %1 inserted: slope length %2 mm, inclination %3 deg.", "kf-2d-gebaut": "\n>>> Helper block %1 (plan view) inserted: KF horizontal length %2 mm.",
"kf-2d-laenge-null": "\nPlan view skipped: the chain has no measurable plan length.", "kf-bemassung-fehlt": "\nNote: the height dimension could not be drawn - the conveyor was built anyway.",
"cmd-kf2d-hilfsblock": "\nHelper block (plan view): no dialog of its own - double-click the main block KF_<n> to regenerate it.",
"kf-2d-ks-fehlt": "\nPlan view skipped: %1 has no measurable KS_EIN/KS_AUS.", "kf-2d-ks-fehlt": "\nPlan view skipped: %1 has no measurable KS_EIN/KS_AUS.",
"kf-prompt-startpunkt": "\nSelect insertion point (KS_EIN of the plan view; the side view is built above it): ", "kf-prompt-startpunkt": "\nSelect insertion point (KS_EIN of the return station): ",
"kf-prompt-drehung": "\nRotation angle of the chain: ",
"kf-abgebrochen": "\nCancelled.", "kf-abgebrochen": "\nCancelled.",
"kf-fertig": "\n>>> %1 created: installation height %2 mm, intermediate section %3 mm.", "kf-fertig": "\n>>> %1 created: installation height %2 mm, intermediate section %3 mm.",
"kf-konsole-variante-header": "\n\nSelect inclined steel conveyor type:", "kf-konsole-variante-header": "\n\nSelect inclined steel conveyor type:",
+12 -4
View File
@@ -313,8 +313,8 @@ def kettenfoerderer_signature(block):
Merkmale wuerden schon hier dieselbe Gruppe ergeben. Sonst wuerde dort Merkmale wuerden schon hier dieselbe Gruppe ergeben. Sonst wuerde dort
"anzahl" addiert, waehrend 353 unveraendert stehen bliebe. "anzahl" addiert, waehrend 353 unveraendert stehen bliebe.
Die Laenge des Zwischenstuecks (L_KF_mm) ist eine FOLGE der Einbauhoehe und Die waagerechte Laenge (L_KF_mm) ist eine FOLGE der Einbauhoehe (und des
steht darum nicht eigens im Schluessel. Winkels) und steht darum nicht eigens im Schluessel.
""" """
attribs = block.get("attribs", {}) attribs = block.get("attribs", {})
return ( return (
@@ -702,7 +702,11 @@ def process_blocks(blocks, anzahl_weichen_ils=0):
# 359/360/391) gingen dabei verloren. Anders als das Geruest werden # 359/360/391) gingen dabei verloren. Anders als das Geruest werden
# BAUGLEICHE Foerderer zu einer Zeile zusammengefasst, weil Merkmal # BAUGLEICHE Foerderer zu einer Zeile zusammengefasst, weil Merkmal
# 353 genau diese Stueckzahl verlangt. # 353 genau diese Stueckzahl verlangt.
if matches_any(bname, BLOCKPATTERNS.get("pattern_kettenfoerderer", [])): # Nur echte Foerderer "KF_<n>": das Muster KF_* erfasst sonst auch
# lose Bausteine (KF_UM_26, KF_2D). Der Hilfsblock KF2D_<n> passt nicht
# aufs Muster und wird nie exportiert.
if (matches_any(bname, BLOCKPATTERNS.get("pattern_kettenfoerderer", []))
and re.fullmatch(r"KF_\d+", bname, re.IGNORECASE)):
sig = kettenfoerderer_signature(block) sig = kettenfoerderer_signature(block)
if sig not in kettenfoerderer_groups: if sig not in kettenfoerderer_groups:
kettenfoerderer_groups[sig] = {"block": block, "ids": [], "anzahl": 0} kettenfoerderer_groups[sig] = {"block": block, "ids": [], "anzahl": 0}
@@ -996,7 +1000,11 @@ def process_blocks(blocks, anzahl_weichen_ils=0):
"bezeichnung": attribs.get("Bezeichnung", ""), "bezeichnung": attribs.get("Bezeichnung", ""),
"dispgruppe": 20, "dispgruppe": 20,
"anzahl": gruppe["anzahl"], "anzahl": gruppe["anzahl"],
"laenge_mm": attribs.get("L_KF_mm", ""), # Bewusst LEER: Sivas berechnet ZL, EL und die Kettenlaenge selbst, aus
# dem Kettenfoerderer wird nur die Hoehe (360) uebernommen. L_KF_mm
# (waagerechte Laenge des Foerderers) steht als Attribut am Block,
# geht aber nicht in die Sivas-Zeile.
"laenge_mm": "",
"ids": gruppe["ids"], "ids": gruppe["ids"],
"details": build_kettenfoerderer_details(block, gruppe["anzahl"]), "details": build_kettenfoerderer_details(block, gruppe["anzahl"]),
}) })
+65 -60
View File
@@ -2892,66 +2892,71 @@
(tu-eq "kf-frei/Start-mitten" 5 (tu-eq "kf-frei/Start-mitten" 5
(kf-freie-nummer 3 '(lambda (n) (member n '(3 4))))) (kf-freie-nummer 3 '(lambda (n) (member n '(3 4)))))
;; --- kf-drehung-gesamt: Drehwinkel fuer KF_EDIT --- ;; --- kf-label-text: Beschriftung von Haupt- UND Hilfsblock ---
;; Regression: der Drehwinkel wird beim Bau in die Geometrie eingerechnet, ;; Eine Zeile wie bei VF_/GF_: Nummer, Winkel mit Gradzeichen, Hoehe,
;; die Blockreferenz selbst hat IMMER Drehung 0 (ssg-block-wrap-welt). ;; projizierte Laenge (ganze mm)
;; KF_EDIT las nur Gruppencode 50 und baute eine mit 90 Grad gebaute (tu-eq "kf-label/normal"
;; Kette ungedreht neu. Der gebaute Winkel steht jetzt als XDATA. (strcat "KF_1 26" (chr 176) " H=1500mm L_horizontal=4951mm")
(tu-eqf "kf-drehung/nur-xdata" 90.0 (kf-drehung-gesamt 90.0 0.0)) (kf-label-text 1 "26" 1500.0 4951.4))
;; Referenz nachtraeglich von Hand um 90 Grad gedreht (Gruppencode 50 in ;; rundet kaufmaennisch auf ganze mm, nimmt auch Ganzzahlen
;; Bogenmass) -> beides addiert sich (tu-eq "kf-label/rundet-auf"
(tu-eqf "kf-drehung/xdata-plus-referenz" 180.0 (strcat "KF_12 30" (chr 176) " H=2000mm L_horizontal=3001mm")
(kf-drehung-gesamt 90.0 (/ pi 2.0))) (kf-label-text 12 "30" 2000 3000.5))
;; Altbestand ohne XDATA: nur die Referenzdrehung (bisheriges Verhalten)
(tu-eqf "kf-drehung/ohne-xdata" 45.0 (kf-drehung-gesamt nil (/ pi 4.0)))
;; nichts bekannt -> 0, kein Absturz und kein nil
(tu-eqf "kf-drehung/nichts" 0.0 (kf-drehung-gesamt nil nil))
(tu-true "kf-drehung/ist-zahl" (numberp (kf-drehung-gesamt nil nil)))
;; Summe ueber 180 Grad wird auf -180..180 gefaltet (270 = -90)
(tu-eqf "kf-drehung/normalisiert" -90.0
(kf-drehung-gesamt 180.0 (/ pi 2.0)))
;; ganze Zahl aus der XDATA (nicht real) darf nicht abstuerzen
(tu-eqf "kf-drehung/ganzzahl" 30.0 (kf-drehung-gesamt 30 0.0))
;; --- kf-neigung-aus-punkten: Kern der 3D-Aufrichtung --- ;; --- kf-winkel-text: Neigung als nackte Ganzzahl (Attribut WINKEL) ---
;; Liefert (Schraeglaenge Neigungswinkel) aus Ketten-Start und -Ende. (tu-eq "kf-winkel-text/real" "26" (kf-winkel-text '(("winkel" . 26.0))))
;; Die Kette wird AUFGERICHTET eingebaut, die Hoehe laeuft also in Z. (tu-eq "kf-winkel-text/string" "30" (kf-winkel-text '(("winkel" . "30"))))
;; 3-4-5-Dreieck: 3000 waagerecht, 4000 hoch -> 5000 schraeg (tu-eq "kf-winkel-text/fehlt" "0" (kf-winkel-text '(("name" . "x"))))
(tu-eqf "kf-neigung/3-4-5-laenge" 5000.0
(car (kf-neigung-aus-punkten '(0.0 0.0 0.0) '(3000.0 0.0 4000.0) 0.0)))
;; 45 Grad: gleiche Betraege waagerecht und hoch
(tu-eqf "kf-neigung/45-grad" 45.0
(cadr (kf-neigung-aus-punkten '(0.0 0.0 0.0) '(1000.0 0.0 1000.0) 0.0)))
;; waagerecht -> Winkel 0, Laenge = Horizontalabstand
(tu-eqf "kf-neigung/waagerecht" '(2000.0 0.0)
(kf-neigung-aus-punkten '(0.0 0.0 0.0) '(2000.0 0.0 0.0) 0.0))
;; Der Horizontalanteil wird auf die KETTENRICHTUNG projiziert, nicht
;; auf die Welt-X-Achse: bei 90 Grad Basisdrehung laeuft die Kette in Y.
;; Ohne Projektion kaeme hier 0 heraus und der Winkel waere 90 statt 45.
(tu-eqf "kf-neigung/gedreht-90" '(1414.213562 45.0)
(kf-neigung-aus-punkten '(0.0 0.0 0.0) '(0.0 1000.0 1000.0) 90.0))
;; fallende Kette -> negativer Winkel
(tu-eqf "kf-neigung/fallend" -45.0
(cadr (kf-neigung-aus-punkten '(0.0 0.0 1000.0) '(1000.0 0.0 0.0) 0.0)))
;; Start = Ende -> nil statt Division durch 0
(tu-nil "kf-neigung/nulllaenge"
(kf-neigung-aus-punkten '(5.0 5.0 5.0) '(5.0 5.0 5.0) 0.0))
;; --- kf-matrix-aufrecht: Y_lokal wird zu Z_welt --- ;; --- kf-versatzpunkt: Hilfsblock beim ersten Einfuegen unter dem Hauptblock ---
;; Das ist die Kernaussage der Aufrichtung. Geprueft wird sie an den (tu-eqf "kf-versatzpunkt/minus-1000" '(100.0 -980.0 5.0)
;; Bildern der Einheitsvektoren, nicht an den Matrixeintraegen - so (kf-versatzpunkt '(100.0 20.0 5.0) -1000))
;; bleibt der Test lesbar und unabhaengig von der Schreibweise. ;; 2D-Punkt ohne Z -> Z = 0, kein Absturz
(tu-eqf "kf-matrix/Y-lokal-wird-Z-welt" '(0.0 0.0 1.0) (tu-eqf "kf-versatzpunkt/ohne-z" '(0.0 -1000.0 0.0)
(mat3-mul-vec3 (kf-matrix-aufrecht 0.0 0.0) '(0.0 1.0 0.0))) (kf-versatzpunkt '(0.0 0.0) -1000.0))
;; Die Fahrtrichtung (X) bleibt bei Basisdrehung 0 die Welt-X-Achse
(tu-eqf "kf-matrix/X-bleibt-X" '(1.0 0.0 0.0) ;; --- kf-hilfsblock-versatz: Wert steht in der JSON, Vorgabe -1000 ---
(mat3-mul-vec3 (kf-matrix-aufrecht 0.0 0.0) '(1.0 0.0 0.0))) (tu-eqf "kf-versatz/aus-daten" -500.0
;; Basisdrehung 90 Grad dreht die Fahrtrichtung in die Welt-Y-Achse, (kf-hilfsblock-versatz '(("ansicht_2d" ("block" . "KF_2D") ("versatz_y" . -500)))))
;; die Hoehe bleibt dabei in Z (tu-eqf "kf-versatz/ohne-feld" -1000.0
(tu-eqf "kf-matrix/basis-90-dreht-X-nach-Y" '(0.0 1.0 0.0) (kf-hilfsblock-versatz '(("ansicht_2d" ("block" . "KF_2D")))))
(mat3-mul-vec3 (kf-matrix-aufrecht 90.0 0.0) '(1.0 0.0 0.0))) (tu-eqf "kf-versatz/ohne-ansicht" -1000.0
(tu-eqf "kf-matrix/basis-90-haelt-Z" '(0.0 0.0 1.0) (kf-hilfsblock-versatz '(("id" . "x"))))
(mat3-mul-vec3 (kf-matrix-aufrecht 90.0 0.0) '(0.0 1.0 0.0))) ;; Zahl, nicht T (and/or-Typfalle)
(tu-true "kf-versatz/ist-zahl"
(numberp (kf-hilfsblock-versatz '(("id" . "x")))))
;; --- kf-key-aus-xdata: Schluessel Haupt- <-> Hilfsblock ---
(tu-eq "kf-key/aus-xdata" "20261030120000"
(kf-key-aus-xdata
'((-3 ("SSG_KF_EDIT" (1000 . "kf-edit") (1000 . "20261030120000"))))))
;; Altbestand: nur der Marker, kein Schluessel -> nil (-> neuer Schluessel)
(tu-nil "kf-key/nur-marker"
(kf-key-aus-xdata '((-3 ("SSG_KF_EDIT" (1000 . "kf-edit"))))))
(tu-nil "kf-key/keine-xdata" (kf-key-aus-xdata '((0 . "INSERT"))))
(tu-nil "kf-key/nil" (kf-key-aus-xdata nil))
;; --- kf-waagerecht-laenge: Wert des Attributs L_KF_mm ---
;; Waagerechte Laenge des GANZEN Foerderers (der Draufsicht KF_2D), also
;; ohne die Steigung - nicht die Laenge des schraegen Mittelstuecks.
;; 3-4-5: 3000 waagerecht, 4000 hoch -> waagerecht bleibt 3000 (nicht 5000)
(tu-eqf "kf-waagerecht/ohne-steigung" 3000.0
(kf-waagerecht-laenge '(0.0 0.0 0.0) '(3000.0 0.0 4000.0) 0.0))
;; nur Hoehe, keine waagerechte Strecke -> 0
(tu-eqf "kf-waagerecht/nur-hoehe" 0.0
(kf-waagerecht-laenge '(0.0 0.0 0.0) '(0.0 0.0 1500.0) 0.0))
;; auf die KETTENRICHTUNG projiziert: bei 90 Grad Basisdrehung laeuft die
;; Kette in Y, die Welt-X-Differenz waere 0
(tu-eqf "kf-waagerecht/gedreht-90" 1000.0
(kf-waagerecht-laenge '(0.0 0.0 0.0) '(0.0 1000.0 1000.0) 90.0))
(tu-eqf "kf-waagerecht/gedreht-45" 1000.0
(kf-waagerecht-laenge '(0.0 0.0 0.0) '(707.10678 707.10678 500.0) 45.0))
;; fallende Kette / Ende "hinter" dem Start: Betrag, nie negativ
(tu-eqf "kf-waagerecht/immer-positiv" 2000.0
(kf-waagerecht-laenge '(2000.0 0.0 0.0) '(0.0 0.0 800.0) 0.0))
;; muss eine Zahl sein (Typfalle: nie T/nil)
(tu-true "kf-waagerecht/ist-zahl"
(numberp (kf-waagerecht-laenge '(0.0 0.0 0.0) '(10.0 0.0 0.0) 0.0)))
;; --- Standard-Variante: steht in den DATEN, nicht im Code --- ;; --- Standard-Variante: steht in den DATEN, nicht im Code ---
(tu-true "kf-variante-standard-p/markiert" (tu-true "kf-variante-standard-p/markiert"
@@ -2970,8 +2975,8 @@
(kf-standard-index '((("winkel" . 26)) (("winkel" . 30))))) (kf-standard-index '((("winkel" . 26)) (("winkel" . 30)))))
;; --- kf-punkt-differenzen: zweite Hoehen-Eingabeart (2 Punkte) --- ;; --- kf-punkt-differenzen: zweite Hoehen-Eingabeart (2 Punkte) ---
;; Gemessen wird die Differenz in Z - die Baugruppe steht aufgerichtet im ;; Gemessen wird die Differenz in Z (Hoehe der gepickten Punkte).
;; Modell. Zurueckgegeben wird (dz dy): die HOEHE zuerst, dY nur zur ;; Zurueckgegeben wird (dz dy): die HOEHE zuerst, dY nur zur
;; Kontrolle (wer in einer Draufsicht pickt, sieht dz = 0). ;; Kontrolle (wer in einer Draufsicht pickt, sieht dz = 0).
(tu-eqf "kf-punkt-diff/dz" '(1500.0 0.0) (tu-eqf "kf-punkt-diff/dz" '(1500.0 0.0)
(kf-punkt-differenzen '(0.0 0.0 0.0) '(2000.0 0.0 1500.0))) (kf-punkt-differenzen '(0.0 0.0 0.0) '(2000.0 0.0 1500.0)))