From 61372e91041cebc14f8a82873298fe1e600dfa3b Mon Sep 17 00:00:00 2001 From: Yuelin Wang Date: Fri, 30 Oct 2026 12:17:06 +0100 Subject: [PATCH] fix: KF_EDIT loeste den Block auf - Blocknummer jetzt ueber die Blocktabelle KF_EDIT loescht beim Neuaufbau den alten Foerderer. kf-naechste-nummer zaehlte nur die Referenzen im Modell und vergab dadurch dieselbe Nummer erneut, obwohl die Block-Definition KF_ weiter in der Blocktabelle steht. _.-BLOCK traf auf den vorhandenen Namen, die "Neu definieren?"-Abfrage verschluckte die Folge-Eingaben, der Block entstand nicht und die Einzelteile blieben lose im Modell liegen. GF_n und VF_n pruefen aus demselben Grund die Blocktabelle. - kf-freie-nummer ueberspringt jede Nummer mit vorhandener Block-Definition - kf-nummer-aus-name liest nur rein numerische Namen (KF_2D, KF_UM_26 und KF_LAENGE tragen dasselbe Praefix, atoi("2D") waere 2) - KF_EDIT nutzt zuerst die vorgewaehlte Auswahl (Doppelklick), entsel nur als Rueckfall, und akzeptiert nur echte KF_ - Unit-Tests fuer Namens-Parser und Nummernvergabe - KF_2D.dwg: ueberarbeitete Draufsicht Co-Authored-By: Claude Sonnet 5.5 --- CLAUDE.md | 2 +- Lisp/KettenFoerderer.lsp | 58 ++++++++++++++++++++++++++++++++------ data/omniflo/2D/KF_2D.dwg | Bin 11200 -> 14720 bytes tests/test_unit.lsp | 29 +++++++++++++++++++ 4 files changed, 80 insertions(+), 9 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 1e7ede1..cf52b28 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -119,7 +119,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_`) 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` | | `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`, `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`/`bm`, Zustand `*geruest-dlg-bef*`). Unter jedem Teil (auch in Sonstiges) zeigt eine Textzeile "Art.-Nr. ..." die Artikelnummer(n) (`geruest-artnr-anzeige`: Einzelteil -> `sivas_id`, Gruppe -> eindeutige Nummern der Teile; DCL kennt keine Schriftgroesse, darum normale Groesse in eigener Zeile). `anbaugruppen` ist ein Objekt mit zwei Listen: `befestigung` (Befestigungsspalte) und `sonstiges` (Teile, die weder oben noch unten sitzen, z.B. die Saeulen-Haltearme) - letztere bekommen eine eigene Zeile darunter (nur wenn nicht leer), in ARTINR/BEZEICHNUNG nach "unten" als "sonstiges" (`geruest-anbau-liste`; ein altes flaches Array gilt als `befestigung`). **Zwei Mengenfelder im JSON, bewusst verschieden benannt**: `stueckzahl` = wie oft ein Teil TATSAECHLICH geschrieben wird (je Variante bzw. je Satz/Anbauteil, fehlt = 1), `anzahl_vorgabe` = NUR die Vorbelegung des Anzahl-Feldes im Dialog (Anbaugruppen, fehlt = 1); ein Anbauteil wird Anzahl x `stueckzahl` mal geschrieben, bei einer Gruppe (`teile`) zaehlt die Anzahl die Saetze (`geruest-stueckzahl`/`geruest-anzahl-vorgabe`). In Konfigurationen (`befestigung`/`sonstiges`, dazu `winkel_platte`) heisst der gespeicherte Dialogwert `anzahl`. Aeltere Konfigurationen mit getrennten `oben`/`unten`-Listen laden weiter: deren Mengen werden zur Gesamtanzahl addiert (`geruest-konfig-befestigung`), Sonstiges-Teile darin still in die Sonstiges-Zeile umgehaengt (`geruest-konfig-sonstiges`); beim naechsten Speichern steht alles im neuen Format. Baugruppen ohne `attribute`-Block (Wandbefestigung) bekommen Laenge/Farbe gesperrt, Baugruppen ohne Anbauteile bekommen gar keine Spalten (ein leerer DCL-Cluster ist unzulaessig). Im generierten Label wird `"` zu `''` ersetzt (`geruest-dcl-label`) - fuenf Artikelnamen enthalten ein Anfuehrungszeichen, das das DCL-Label sonst vorzeitig beendet und die Datei zerstoert. **Konfigurationen**: gepflegte Bibliothek je Baugruppe unter `konfigurationen` in `geruest.json` (schreibgeschuetzt) plus eigene in `cfg/geruest_user.json` (gitignored, `geruest-user-schreiben`; getrennte Datei, damit Benutzerdaten und versionierte Bibliothek nicht kollidieren). Eigene Eintraege sind in der Liste mit `*` markiert und nur diese loeschbar. **Geaenderte Konfiguration**: beim Anwenden wird der Dialogstand als Vergleichsbasis gemerkt (`*geruest-dlg-konfig-basis*`); jede Eingabe vergleicht den aktuellen Record damit (`geruest-dlg-aenderung-pruefen`, Vergleich statt Flag - Zuruecknehmen hebt die Markierung wieder auf) und kennzeichnet den Listeneintrag mit "(geaendert)" plus Statuszeile `kstatus`. Speichern bietet bei einer gewaehlten EIGENEN Konfiguration deren Namen als Vorgabe an (Enter = ueberschreiben, `geruest-konfig-name-vorgabe`); Bibliotheks-Konfigurationen brauchen einen neuen Namen. Konfigurationen verweisen ueber den ANZEIGENAMEN auf Variante und Anbauteile; ein unbekannter Name wird gemeldet und uebersprungen, statt die Konfiguration scheitern zu lassen. Speichern/Loeschen laufen ueber `done_dialog 2`/`3` (Dialog schliessen, Aktion, neu zeigen) - `getstring` funktioniert aus einem `action_tile`-Rueckruf heraus nicht. Der Block wird - wie KREISEL_n/VF_n - SELBST zusammengebaut: Dummy-DWG aus `data/geruest` liefert nur Geometrie, das Attribut-Schema kommt aus `*geruest-attrib-defs*` (ID, ARTINR, LAENGE, BEZEICHNUNG, TEILEART, TEILEFARBE, LAYER) und wird per `ssg-attrib-merge` mit den Dummy-Tags zusammengefuehrt - sonst verschluckt `ssg-attrib-set-on` stillschweigend jeden Tag, den die Dummy-DWG nicht als ATTDEF kennt. **Teile-Geometrie**: jede ausgewaehlte Artikelnummer (Basis/oben/unten inkl. aufgeloester Gruppen), fuer die `data/geruest/.dwg` existiert, wird zusaetzlich einmal bei 0,0,0 mit in den Block eingefuegt (`geruest-teile-dwgs`, dedupliziert, fehlende DWGs still uebergangen) - reine visuelle Rueckmeldung, die Attribut-Logik bleibt davon unberuehrt. **TEILEFARBE** ist eine kommagetrennte Liste parallel zu ARTINR (z.B. `,,RAL7030,,RAL7030`): Teile mit `"farbig": true` in `geruest.json` (je Artikel; eine Gruppe vererbt es an ihre Teile, fehlend = false) bekommen die im Dialog gewaehlte Baugruppenfarbe, alle anderen einen leeren Eintrag. ARTINR und TEILEFARBE entstehen aus demselben Durchlauf (`geruest-teile-paare`/`-paare-n`, `geruest-farbliste`), sind also garantiert gleich lang. `export_sivas.py` schreibt daraus je Artikelnummer das Merkmal `{"108": ""}` in `details` (Nummer aus `cfg/export.cfg` `[Sivas] merkmal_teilefarbe`). Blockname `GR__` (Kollisionszaehler wie in OmniModulInsert). **Das `GR_`-Praefix ist nicht kosmetisch**: `SSG_BLOCKEDIT` haelt JEDEN Block mit TEILEART-Attribut fuer ein Omniflo-Element, deshalb steht in `SSG_LIB_Commands.lsp` ein `GR_*`-Zweig VOR dem Omniflo-Zweig. ARTINR listet jede Sivas-ID so oft, wie Anzahl x `stueckzahl` es vorgeben - erst Basis, dann Befestigung, dann Sonstiges; bei einer benannten Gruppe (`teile`) wird das ganze Set n-mal wiederholt, die Mengen INNERHALB des Sets bleiben dabei erhalten (`geruest-artikelnummern-n`). BEZEICHNUNG nennt Position und Menge je Teil ("Befestigung 4x ...", "sonstiges 1x ...") und verkettet mit `" + "`, NICHT mit Komma - die Artikelbezeichnungen enthalten selbst Kommas ("ROHR R 1\" 33,7 x 2,6"). Zuordnung Menue -> JSON laeuft ueber das ASCII-Feld `id`, weil die aeusseren JSON-Schluessel Umlaute bzw. Anfuehrungszeichen tragen. CSV-/Sivas-Export: `pattern_geruest = GR_*` (cfg/export.cfg), `csv:collect-export-blocks`/`ssg-id-collect-blocks` sammeln GR_-Bloecke wie BTMT eigenstaendig; `*geruest-attrib-defs*` traegt zusaetzlich ein leeres `ID`-ATTDEF (Voraussetzung fuer `ssg-id-check-all`, das nur bestehende ATTRIBs beschreiben kann). ARTINR/BEZEICHNUNG/TEILEART/LAENGE landen in export_csv.py/export_sivas.py in festen Spalten, TEILEFARBE/LAYER (+ ARTINR bei export_csv.py) im Merkmale-/details-JSON. **Attribut-Keys in `geruest.json` sind GROSS** (`LAENGE`, `TEILEFARBE`, ...) = die Block-Attribut-Tags; `ssg-json-get` unterscheidet Gross/Klein. **Rohr-Geruest-Traeger** (`GERUEST_TRAEGER`): Traeger-Baugruppen mit Linien-Logik liegen in einer EIGENEN Datei `data/json/geruest_traeger.json` (gleicher Aufbau wie geruest.json, eigener Lader/Cache `geruest-traeger-daten`, Suche `geruest-baugruppe id daten`; gemeinsamer Lader `geruest-json-laden-cache`) - sie tauchen darum nicht im Bereichs-Dropdown von GERUEST auf; dort kann spaeter auch der Gattertraeger hin. eigener Dialog aus der Vorlage `dcl/geruest_traeger.dcl` (Variante als Radiobuttons, JEDE Variante - auch die mit Muffe - mit eigenem Button, Beschriftung "Name (Artikelnummern)" ueber `geruest-varianten-label`; Varianten mit `"disabled": true` erscheinen in allen drei Geruest-Dialogen ausgegraut und nicht waehlbar (`geruest-variante-gesperrt-p`, `geruest-varianten-sperren` per `mode_tile`; Vorauswahl ueber `geruest-variante-waehlbar`/`-erste-freie-variante`; eine Konfiguration mit gesperrter Variante laesst die Auswahl unveraendert und meldet `gr-konfig-variante-gesperrt`) - bleiben aber in den Daten, damit Konfigurationen und Neubeschriften sie weiter ueber den Namen finden; eine Muffen-Checkbox gab es zwischenzeitlich, sie wurde wieder entfernt, weil die Artikelnummer der Muffen-Variante dann im Dialog nicht sichtbar war; Farbe, je eine Checkbox + Feld fuer `LAENGENABZUGANFANG`/`LAENGENABZUGENDE`, Vorgabe `vorgabe`/`aktiv` aus dem attribute-Block, je Variante ueberschreibbar via `geruest-attr-feld`); danach Einfuege-Schleife bis ESC/Enter: Startpunkt, dann Endpunkt ODER getippte Laenge (+ Richtung). Aktive Abzuege ruecken die Linie beidseitig ein (Strecke 1000, Abzug Anfang 20/Ende 35 -> Start 20 mm hinter dem Klick, Linie 945 mm); `LAENGE` = Netto-Laenge, gegen `LAENGE.min/max` geprueft. KEINE Dummy-DWG: die Geometrie ist eine per `entmake` erzeugte LINE (0,0,0)-(L,0,0) auf dem `layer` der Variante, eingefuegt mit dem Richtungswinkel (ANGBASE/ANGDIR/AUNITS dafuer kurz auf 0). **Querstreben + Muffe** als verschachtelte Dummy-DWGs im selben Block, reine Grafik ohne Export: `querstrebe.dummy` (`data/geruest/dummy_Strebe_30x4.dwg`, Basispunkt Strebenmitte, 30 mm laengs X) an jeder Strebenposition, NUR bei Varianten mit ausdruecklichem `"mit_querstrebe": true` (z.B. nicht beim `Rohr 1"`); fehlt die DWG, Rueckfall auf je eine 25-mm-Querlinie an Strebenanfang/-ende (`laenge`/`querlinie`, Standard 30/25 nur im Code). `muffe.dummy` (`dummy_Muffe_1zoll.dwg`, Basispunkt Muffenmitte) am Linienende, NUR bei Varianten mit ausdruecklichem `"mit_muffe": true` (nicht aus dem Namen geraten; bei "mit 2 Muffen" trotzdem nur eine am Ende); `querstrebe`/`muffe` per Variante ueberschreibbar (`geruest-variante-wert`). Dummys werden ohne Drehung eingefuegt (Traeger laeuft im Block entlang +X), ab dem zweiten Mal per Blockname (`geruest-dummy-einfuegen`). Verteilungs-Konfiguration `querstrebe` (`abstand`/`rand_min`/`rand_max`, Variante vor Baugruppe), Plan `geruest-traeger-streben-plan` -> (rand positionen): der Abstand zwischen zwei Streben ist IMMER genau `abstand`, beide Raender gleich (netto = k*abstand + 2*rand); gewaehlt wird die Strebenzahl, deren Rand am naechsten an `rand_min`..`rand_max` liegt (Gleichstand -> weniger Streben). Mit 500/100..250 geht das nur auf, wenn netto mod 500 = 0 oder in [200, 500) liegt (z.B. 1300 -> 150|500|500|150); sonst (1001..1199, unter 200 mm) liegt der Rand ausserhalb (1100 -> 300|500|300) - `geruest-traeger-rand-pruefen` zeigt dann eine Messagebox (ohne GUI: Konsole), der Traeger wird trotzdem eingefuegt. Schema `*geruest-traeger-attrib-defs*` = `*geruest-attrib-defs*` + die zwei Abzug-Tags; gemeinsamer Block-Schluss mit den anderen Bereichen in `geruest-block-abschliessen`. Reine Berechnung (`geruest-traeger-laenge`/`-start`/`-pruefen`, `geruest-mm-zahl`) in `tests/test_unit.lsp`. **Traeger-Beschriftung** (Checkbox im Dialog, Standard an; z.B. `RT 80/80 L=2650`): EIGENES TEXT-Objekt neben dem Block, abgeleitet NUR aus dem fertigen Block (Einfuegepunkt, DXF 50, Attribute `LAENGE` + `VARIANTE`) - darum nutzen Einfuegen und `GERUEST_TRAEGER_BESCHRIFTEN` dieselbe Funktion `geruest-traeger-beschriften`, und ein von Hand verschobener/gedrehter Traeger wird beim Neubeschriften korrekt erfasst. Verknuepfung Text -> Block per XDATA `SSG_GR_BESCHR` (1005 = Block-Handle): Neubeschriften ersetzt statt verdoppelt, der Sammelbefehl entfernt verwaiste Texte. JSON `beschriftung` (je FELD Variante vor Baugruppe, `geruest-obj-feld`): Inhalt `format` (`%1` Text, `%2` Laenge auf ganze mm) + `text` (fehlt -> Variantenname) und `layer` (je Variante verschieden, darum in JEDER Variante gepflegt - keine Baugruppen-Vorgabe; fehlt er doch, aktueller Layer); Formatierung NUR `stil`/`ausrichtung`/`hoehe` - alles andere regelt der Textstil ausserhalb, es wird kein Stil angelegt (fehlt er: Meldung, aktueller TEXTSTYLE). `ausrichtung` = Codes von TEXT "Ausrichten" (T/M/B + L/C/R), Bezugspunkt auf der Traegerlinie, L/R in LESErichtung. Textwinkel entlang des Traegers, DIN: fuer 90 < w <= 270 Grad um 180 gedreht (`geruest-text-winkel`). **Omniflo-Fahrstrecke** (`OMNI_FAHRSTRECKE`, Daten `data/json/omniflo_fahrstrecke.json`, Vorlage `dcl/fahrstrecke.dcl`): dieselbe Linien-Logik wie der Traeger, Code geteilt - `geruest-linien-starten id daten dcl-datei dlg-name` (Dialog `geruest-traeger-dialog` mit Vorlage + Dialogname als Argument, Einfuegen `geruest-traeger-einfuegen`), Beschriftung `geruest-traeger-beschriften blockEnt bg` mit der Baugruppe als Argument, Sammelbefehle je Typ ueber `geruest-linien-beschriften id daten` (Bloecke `GR__*`; verwaiste Texte werden typunabhaengig entfernt). Unterschiede stehen nur in den Daten: keine Querstreben (kein `mit_querstrebe`), Muffen-Dummy wie beim Traeger ueber den `muffe`-Block der Baugruppe (nur Varianten mit `"mit_muffe": true`), `farbig` nur bei ST/STL, Muffen-Varianten nur fuer ST/STL. Der Dialogstand bleibt je Baugruppe erhalten (`*geruest-tr-id*`; Wechsel Traeger <-> Fahrstrecke setzt ihn zurueck). Meldungstexte `gr-tr-*` nennen den Bauteilnamen aus `titel` als Platzhalter. In der Stueckliste eigene Klasse `fahrstrecke` (`GR_Fahrstrecke_*` vor dem allgemeinen `GR_*`). Noch NICHT enthalten: Edit-Pfad (GERUEST_EDIT) | -| `KettenFoerderer.lsp` | KETTENFOERDERER, KF_EDIT, KF_KS_PRUEFEN | Kettenfoerderer/Schraegfoerderer Stahl: baut aus `KF_UM_` -> `KF_LAENGE` -> `KF_AN_` (je KS_AUS auf KS_EIN) plus der Draufsicht `KF_2D` EINEN Block `KF_`. 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). **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_` -> `KF_LAENGE` -> `KF_AN_` (je KS_AUS auf KS_EIN) plus der Draufsicht `KF_2D` EINEN Block `KF_`. 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_` 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) | | `DinFormate.lsp` | DIN_FORMATE | Ersatz fuer das alte ACADSST-Makro `DINFORM.LSP`/`.DCL` (SSG2-Menue "DIN-FORMATE..."): fuegt einen DIN-Rahmen (A0-A3, A4 quer/hoch) und optional einen Schriftkopf (KOPF11, Sprachen DE/EN/FR/IT) per Dialog (`dcl/dinformate.dcl`) ein. Block-DWGs liegen flach in `cfg/rahmen/` (kopiert aus `\\infor-ln\SSG_Tools\ACADSST\KOEPFE` - nur die von DINFORM.LSP tatsaechlich referenzierten Dateien: `A0`/`A1`/`A2`/`A3`/`A4Q`/`A4H.DWG` + `Kopf11`/`Kopf11e`/`Kopf11f`/`Kopf11i.dwg`). Rahmen wird im Zeichenmassstab-Faktor eingefuegt, der Kopf im Faktor/100 (Kopf-DWG ist 100x groesser als die Rahmen-DWGs angelegt) - unveraendert aus dem Original-Massstabsschema uebernommen. Blockdefinitionen werden einmalig per `_.-INSERT ... _X 0` nachgeladen (`dinformate-block-laden`), falls noch nicht in der Zeichnung vorhanden. Lazy geladen ueber `(ssg-ensure "DinFormate")`, Menue: Werkzeuge > Din Formate. | | `DrawPlanquadrate.lsp` | DRAW_PLANQUADRATE, DELETE_PLANQUADRATE, PLANQUADRATE_TOGGLE | Zeichnet auf Z=0 ein Planquadrat-Linienraster (senkrecht/waagerecht), im Abstand/Zaehlschema der aktiven `[Planquadrate]`-Konfiguration aus `cfg/export.cfg` (dieselbe Quelle wie `lib/export_planquadrat.py` fuer den CSV-Export). Bereich kommt NICHT aus den Zeichnungs-Extents, sondern aus derselben Blockmenge wie EXPORTCSV (`csv:collect-export-blocks`, export.lsp; siehe `plq-export-bbox`): Ursprung (Index 1) UND obere Grenze kommen BEIDE aus der tatsaechlichen Bounding-Box jedes Blocks (`csv:get-bbox`, wie `bbox_columns()`/`planquadrat_column()`/`_bbox_min()` in `lib/export_csv.py` bzw. `lib/export_planquadrat.py`), NICHT aus dem reinen Einfuegepunkt - bei einem gedrehten/exzentrisch eingefuegten Block (z.B. ein um 90 Grad gedrehter Kreisel) kann das Bounding-Box-Zentrum weit vom Einfuegepunkt abweichen; Ursprung und Zellzuordnung muessen auf derselben Referenz beruhen, sonst driften sie auseinander (realer Bug, behoben). Reine Hilfsgeometrie (Kreisel-Umfahrungsboegen als eigene Linien, Texte) zaehlt bewusst nicht mit, sonst weicht das Raster vom CSV-Export ab. `x_origin`/`y_origin` ueberschreiben den Ursprung, Fallback auf EXTMIN/EXTMAX falls keine Export-Bloecke vorhanden sind bzw. `csv:get-bbox` fuer einen Block scheitert. Spalten-/Zeilenzahl wird IMMER aufgerundet auf volle Rasterschritte, damit alle senkrechten Linien auf der letzten waagerechten enden und umgekehrt - das Raster bildet immer ein geschlossenes Rechteck. Zaehlung (alphabetisch/numerisch je Achse) als Text am linken/unteren Rand. Das fertige Raster (Linien+Texte) wird per `_.-BLOCK`/`_.INSERT` zu einem einzigen Block `ZZ_PLANQUADRATE` bei (0,0,0) zusammengefasst. Layer aus `cfg/layer.cfg` (`[allgemein]` Key `planquadrate`, Fallback `ZZ_Planquadrate`). DRAW_PLANQUADRATE entfernt vor dem Neuaufbau zuerst einen evtl. vorhandenen Block (`plq-block-entfernen`: INSERTs loeschen + `_-PURGE _B`), DELETE_PLANQUADRATE ruft nur diese Entfernung auf. PLANQUADRATE_TOGGLE ("Planquadrate an/aus", Menue: Konfiguration) prueft per `tblsearch`/`ssget "X"`, ob der Block existiert, und dispatcht auf Zeichnen bzw. Loeschen. Lazy geladen ueber `(ssg-ensure "DrawPlanquadrate")`, nicht in der Core-Ladereihenfolge. Die Planquadrat-SPALTE im CSV-Export (`export_csv.py::planquadrat_column`/`resolve_origins`, `lib/export_planquadrat.py::compute_planquadrat`/`_bbox_min`) rechnet ebenfalls durchgaengig auf Bounding-Box-Basis (nicht nur Einfuegepunkt) und liefert eine SPANNE ("A/1-J/1") statt nur einer Zelle, wenn ein Element mehr als eine Zelle ueberdeckt (Kreisel, lange VF-/GF-Ketten). | diff --git a/Lisp/KettenFoerderer.lsp b/Lisp/KettenFoerderer.lsp index ba8681e..2fd9614 100644 --- a/Lisp/KettenFoerderer.lsp +++ b/Lisp/KettenFoerderer.lsp @@ -632,18 +632,51 @@ kf-insert) ;; Naechste freie KF-Nummer in der Zeichnung. -(defun kf-naechste-nummer ( / n ss i nm num) +;; Nummer aus einem Blocknamen "KF_" (rein numerisch), sonst nil. Die +;; Bausteine der Kette heissen ebenfalls "KF_...": "KF_2D" (Draufsicht), +;; "KF_UM_26", "KF_LAENGE" duerfen NICHT als Nummer gelesen werden - atoi +;; liefert fuer "2D" naemlich 2 und fuer "UM_26" 0. Reiner Rechenkern. +(defun kf-nummer-aus-name (nm / rest i ok) + (setq nm (strcase nm)) + (if (and (> (strlen nm) 3) (= (substr nm 1 3) "KF_")) + (progn + (setq rest (substr nm 4) i 1 ok T) + (while (and ok (<= i (strlen rest))) + (if (not (wcmatch (substr rest i 1) "#")) (setq ok nil)) + (setq i (1+ i))) + (if ok (atoi rest) nil)) + nil)) + +;; Erste freie Nummer ab start: ueberspringt jede Nummer, fuer die belegt-p +;; (Symbol einer Ein-Argument-Funktion) T liefert. Reiner Rechenkern, das +;; Praedikat macht die Zeichnungsabfrage austauschbar und damit testbar. +(defun kf-freie-nummer (start belegt-p / n) + (setq n start) + (while (apply belegt-p (list n)) (setq n (1+ n))) + n) + +;; Ist die Block-DEFINITION KF_ in der Zeichnung vorhanden? +(defun kf-nummer-belegt-p (n) + (not (null (tblsearch "BLOCK" (strcat "KF_" (itoa n)))))) + +;; 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 +;; Block-DEFINITION in der Blocktabelle weiter. Wird deren Nummer erneut +;; vergeben, trifft das spaetere _.-BLOCK auf einen vorhandenen Namen - die +;; "Neu definieren?"-Abfrage verschluckt die Folge-Eingaben, der Block entsteht +;; nicht, und die Einzelteile bleiben LOSE im Modell liegen (Fehlerbild "Block +;; aufgeloest"). GF_n und VF_n pruefen aus demselben Grund die Blocktabelle. +(defun kf-naechste-nummer ( / n ss i num) (setq n 0) (setq ss (ssget "X" '((0 . "INSERT") (2 . "KF_*")))) (if ss (progn (setq i 0) (while (< i (sslength ss)) - (setq nm (cdr (assoc 2 (entget (ssname ss i))))) - (setq num (atoi (substr nm 4))) - (if (> num n) (setq n num)) + (setq num (kf-nummer-aus-name (cdr (assoc 2 (entget (ssname ss i)))))) + (if (and num (> num n)) (setq n num)) (setq i (1+ i))))) - (1+ n)) + (kf-freie-nummer (1+ n) 'kf-nummer-belegt-p)) ;; ============================================================ ;; DIALOG @@ -1082,17 +1115,26 @@ (setq i (1+ i))) (cond (treffer) (0))) -(defun c:KF_EDIT ( / ent ed bname attribs daten eingabe variante hoehe +(defun c:KF_EDIT ( / ss ent ed bname attribs daten eingabe variante hoehe verbindung farbe antriebsmotor movimot steuerung geo erg laenge skala lmin lmax startpunkt drehung last-ent kf-nummer endpunkt) - (setq ent (car (entsel (ssg-text "kf-edit-waehlen")))) + ;; Erst die VORGEWAEHLTE Auswahl (Doppelklick setzt sie), nur ohne sie per + ;; Klick nachfragen - wie GEFAELLESTRECKE_EDIT/KreiselEdit. Vorher fragte + ;; KF_EDIT auch nach einem Doppelklick noch einmal nach einer Auswahl. + (setq ss (ssget "I")) + (setq ent (if (and ss (= (sslength ss) 1)) + (ssname ss 0) + (car (entsel (ssg-text "kf-edit-waehlen"))))) (if (null ent) (princ (ssg-text "kf-abgebrochen")) (progn (setq ed (entget ent)) (setq bname (cdr (assoc 2 ed))) - (if (or (/= (cdr (assoc 0 ed)) "INSERT") (not (wcmatch bname "KF_*"))) + ;; Nur echte Foerderer "KF_": die Bausteine (KF_UM_26, KF_2D, ...) + ;; tragen ebenfalls das Praefix KF_, haben aber keine Attribute - ein + ;; Neuaufbau aus ihnen waere sinnlos und wuerde sie loeschen. + (if (or (/= (cdr (assoc 0 ed)) "INSERT") (null (kf-nummer-aus-name bname))) (alert (ssg-text "kf-edit-kein-kf")) (progn (setq daten (kf-daten)) diff --git a/data/omniflo/2D/KF_2D.dwg b/data/omniflo/2D/KF_2D.dwg index 91ecdddbb8a762d0fa5927ed4bb1f2e872421396..24add3570881513a622bf75ff556f80ad4fc2ebb 100644 GIT binary patch delta 10233 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 JcmKrqe*p0ll~e!# delta 6698 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 LS;qhGe|`QNF>5Ox diff --git a/tests/test_unit.lsp b/tests/test_unit.lsp index 444f846..9c2fd50 100644 --- a/tests/test_unit.lsp +++ b/tests/test_unit.lsp @@ -2863,6 +2863,35 @@ (tu-nil "kf-antriebsart/kein-komma" (vl-string-search "," (kf-antriebsart '(("titel" . "x"))))) + ;; --- kf-nummer-aus-name: nur rein numerische Namen zaehlen --- + ;; Die Bausteine heissen ebenfalls KF_...; atoi("2D") waere 2 und wuerde + ;; die Nummernvergabe verschieben, sobald lose Teile in der Zeichnung liegen. + (tu-eq "kf-nummer/KF_1" 1 (kf-nummer-aus-name "KF_1")) + (tu-eq "kf-nummer/KF_12" 12 (kf-nummer-aus-name "KF_12")) + (tu-eq "kf-nummer/klein" 7 (kf-nummer-aus-name "kf_7")) + (tu-nil "kf-nummer/KF_2D" (kf-nummer-aus-name "KF_2D")) + (tu-nil "kf-nummer/KF_UM_26" (kf-nummer-aus-name "KF_UM_26")) + (tu-nil "kf-nummer/KF_LAENGE" (kf-nummer-aus-name "KF_LAENGE")) + (tu-nil "kf-nummer/nur-Praefix" (kf-nummer-aus-name "KF_")) + (tu-nil "kf-nummer/fremdes-Praefix" (kf-nummer-aus-name "GR_5")) + + ;; --- kf-freie-nummer: Nummern mit vorhandener Block-DEFINITION ueberspringen --- + ;; Regression "Block aufgeloest": KF_EDIT loescht den alten KF_1, die + ;; Referenzen im Modell sind weg (Start = 1), die Definition KF_1 lebt aber + ;; weiter. Wurde 1 erneut vergeben, traf _.-BLOCK auf einen vorhandenen + ;; Namen, die "Neu definieren?"-Abfrage verschluckte die Folge-Eingaben, + ;; und die Einzelteile blieben lose im Modell liegen. + (tu-eq "kf-frei/nichts-belegt" 1 + (kf-freie-nummer 1 '(lambda (n) nil))) + (tu-eq "kf-frei/Edit-Szenario" 2 + (kf-freie-nummer 1 '(lambda (n) (member n '(1))))) + (tu-eq "kf-frei/mehrere-belegt" 4 + (kf-freie-nummer 1 '(lambda (n) (member n '(1 2 3))))) + (tu-eq "kf-frei/Luecke-bleibt" 3 + (kf-freie-nummer 1 '(lambda (n) (member n '(1 2 4))))) + (tu-eq "kf-frei/Start-mitten" 5 + (kf-freie-nummer 3 '(lambda (n) (member n '(3 4))))) + ;; --- kf-drehung-gesamt: Drehwinkel fuer KF_EDIT --- ;; Regression: der Drehwinkel wird beim Bau in die Geometrie eingerechnet, ;; die Blockreferenz selbst hat IMMER Drehung 0 (ssg-block-wrap-welt).