diff --git a/Lisp/README.md b/Lisp/README.md index 95e1db4..23a44c2 100644 --- a/Lisp/README.md +++ b/Lisp/README.md @@ -89,6 +89,24 @@ Grundfunktionen für Umgebungssicherung, Benutzerinteraktion, Layer, Blöcke, At | `ssg-insert-block layer farbe pfad pt xs ys rot` | Layer anlegen und Block einfügen | | `ssg-make-block pt rotation ss` | Auswahlsatz als Block definieren und gleich einfügen | +**ILS-Blockdateien laden (2D/3D):** + +| Funktion | Beschreibung | +| --- | --- | +| `ssg-ils-dim-aktuell` | Gültige Dimension: `*ssg-ils-dim*` → `DXFM_DIM` → `"3D"` | +| `ssg-ils-blockname[-dim] roh [dim]` | Effektiver Blockname mit Suffix `_2D`/`_3D` | +| `ssg-ils-block-datei[-dim] roh [dim]` | Pfad zu `data/ils/.dwg` (3D-Fallback) | +| `ssg-ils-block-laden[-dim] roh [dim]` | Blockdefinition sicherstellen; Rückgabe = effektiver Name | +| `ssg-block-refresh-reset roh` | Redefine-Sperre lösen (`nil` = alle Blöcke) → DWG wird neu gelesen | + +Jede Block-DWG wird **einmal pro Zeichnung** aus der Datei neu definiert (Redefine); +danach greift eine Sperre (`*ssg-block-refreshed*`, siehe `ssg-block-refreshed-p`/ +`-set`). Ohne sie las jede einzelne Element-Einfügung die DWG erneut von der Platte +und regenerierte alle bereits platzierten Referenzen – bei den großen 3D-Blöcken +(`AS_Element_90_links_3D` ~52 MB) waren Tests wie `TEST_MUBEA` dadurch minutenlang +beschäftigt. Wurde eine Block-DWG während der laufenden Sitzung geändert, hebt +`ssg-block-refresh-reset` die Sperre auf. + **Attribut-Operationen:** | Funktion | Beschreibung | diff --git a/Lisp/ssg_core.lsp b/Lisp/ssg_core.lsp index 76f03c2..1f27a08 100644 --- a/Lisp/ssg_core.lsp +++ b/Lisp/ssg_core.lsp @@ -370,13 +370,58 @@ (defun ssg-ils-block-datei (blockname / ) (ssg-ils-block-datei-dim blockname (ssg-ils-dim-aktuell))) +;; ------------------------------------------------------------ +;; REDEFINE-SPERRE (eine DWG-Lesung pro Block und Zeichnung) +;; ------------------------------------------------------------ +;; Merkliste der in DIESER Zeichnung schon einmal aus der DWG-Datei neu +;; definierten (redefinierten) Blocknamen. Grundlage der Sperre in +;; ssg-ils-block-laden-dim. Namen werden in Grossschreibung gehalten, weil +;; Blocknamen in CAD case-insensitiv sind. LISP-Variablen sind pro Dokument +;; eigenstaendig, die Liste gilt also automatisch je Zeichnung. +(if (not (boundp '*ssg-block-refreshed*)) (setq *ssg-block-refreshed* nil)) + +(defun ssg-block-refreshed-p (eff / ) + (and (boundp '*ssg-block-refreshed*) + (member (strcase eff) *ssg-block-refreshed*) + t)) + +(defun ssg-block-refreshed-set (eff / ) + (if (not (ssg-block-refreshed-p eff)) + (setq *ssg-block-refreshed* (cons (strcase eff) *ssg-block-refreshed*))) + eff) + +;; Sperre aufheben - erzwingt beim naechsten ssg-ils-block-laden(-dim) ein +;; erneutes Einlesen der DWG. Argument nil: ALLE Bloecke; ROH- oder effektiver +;; Blockname: nur dieser (beide Dimensionen). Noetig, wenn eine Block-DWG +;; waehrend der laufenden Sitzung geaendert wurde. +(defun ssg-block-refresh-reset (blockname / rest n2 n3 nroh) + (if (null blockname) + (setq *ssg-block-refreshed* nil) + (progn + (setq nroh (strcase blockname) + n2 (strcase (ssg-ils-blockname-dim blockname "2D")) + n3 (strcase (ssg-ils-blockname-dim blockname "3D")) + rest '()) + (foreach n *ssg-block-refreshed* + (if (not (or (= n nroh) (= n n2) (= n n3))) + (setq rest (cons n rest)))) + (setq *ssg-block-refreshed* rest))) + (princ)) + ;; Stellt sicher, dass die Block-DEFINITION zum ROH-Namen 'blockname' in einer ;; EXPLIZITEN Dimension geladen ist. Blocknamen tragen das Suffix _2D/_3D ;; (siehe ssg-ils-blockname-dim), daher koennen 2D und 3D gleichzeitig existieren. -;; WICHTIG: Ein bereits vorhandener gleichnamiger Block wird IMMER neu aus der -;; Datei definiert (Redefine), damit sein Inhalt zur aktuellen Datei passt. Sonst -;; bliebe eine veraltete Definition (z.B. mit falschem 2D/3D-Inhalt aus einer -;; frueheren Sitzung) bestehen und wuerde wiederverwendet. +;; Ein bereits vorhandener gleichnamiger Block wird EINMAL PRO ZEICHNUNG neu aus +;; der Datei definiert (Redefine), damit eine veraltete Definition aus einer +;; frueheren Sitzung (z.B. mit falschem 2D/3D-Inhalt) nicht wiederverwendet wird. +;; Jeder WEITERE Aufruf fuer denselben effektiven Blocknamen ueberspringt das +;; Neu-Definieren (siehe *ssg-block-refreshed*): Ohne diese Sperre las jede +;; einzelne Element-Einfuegung die DWG erneut von der Platte - bei den grossen +;; 3D-Bloecken (AS_Element_90_links_3D ~52 MB, ES_Element_90_links_3D ~9 MB) +;; und zusaetzlich einer Regeneration aller bereits platzierten Referenzen +;; dieses Blocks. Bei Tests mit vielen Strecken (TEST_MUBEA, Gefaellestrecke- +;; und Foerderer-Tests) fuehrte das zu minutenlangen Laufzeiten. +;; Zum erzwungenen Neu-Einlesen: ssg-block-refresh-reset. ;; Rueckgabe: effektiver (suffigierter) Blockname oder nil (Datei nicht gefunden). (defun ssg-ils-block-laden-dim (blockname dim / eff datei base temp-obj osm areq adia) (setq eff (ssg-ils-blockname-dim blockname dim)) @@ -385,12 +430,6 @@ nil (progn (setq base (vl-filename-base datei)) - ;; --- TEMP-DIAGNOSE (nach Klaerung wieder entfernen) --- - (princ (strcat "\n[SSG-DBG] roh=" blockname " dim=" dim - " eff=" eff - " existiert-vorher=" (if (tblsearch "BLOCK" eff) "JA" "NEIN") - " datei=" datei)) - ;; --- ENDE TEMP-DIAGNOSE --- ;; Sichtbar machen, wenn die Datei der gewuenschten Dimension fehlt und der ;; 3D-Fallback greift (dann traegt der Block den gewuenschten Namen, aber ;; den Inhalt der anderen Dimension = potenziell falsche Referenz). Jeder @@ -399,38 +438,36 @@ (princ (strcat "\n[SSG] WARNUNG: Blockdatei '" eff ".dwg' fehlt - Fallback auf '" base ".dwg' (Inhalt evtl. andere Dimension)."))) - (if (and (not (tblsearch "BLOCK" eff)) (= (strcase base) (strcase eff))) - ;; Neu UND Dateiname == Blockname -> COM-Weg (kein Prompt). + ;; In dieser Zeichnung bereits aus der Datei aktualisiert UND noch in der + ;; Blocktabelle vorhanden -> nichts zu tun, die Definition ist aktuell. + ;; Die tblsearch-Pruefung faengt ab, dass der Block zwischenzeitlich + ;; entfernt wurde (z.B. per PURGE) und neu geladen werden muss. + (if (and (ssg-block-refreshed-p eff) (tblsearch "BLOCK" eff)) + eff (progn - (setq temp-obj (vla-InsertBlock modelspace - (vlax-3D-point '(0 0 0)) datei 1.0 1.0 1.0 0)) - (vla-Delete temp-obj)) - ;; Sonst (Block existiert bereits ODER Dateiname != Blockname wg. - ;; 3D-Fallback) -> aus Datei (neu) definieren. "_Y" beantwortet die - ;; Redefine-Abfrage, falls der Block bereits vorhanden ist; danach die - ;; laufende Einfuegung sofort abbrechen (Definition ist bereits wirksam). - (progn - (setq areq (getvar "ATTREQ") adia (getvar "ATTDIA") osm (getvar "OSMODE")) - (setvar "ATTREQ" 0)(setvar "ATTDIA" 0)(setvar "OSMODE" 0) - (if (tblsearch "BLOCK" eff) - (command "_.-INSERT" (strcat eff "=" datei) "_Y") ; Redefine bestehender Block - (command "_.-INSERT" (strcat eff "=" datei))) ; neu (kein Redefine-Prompt) - (if (> (getvar "CMDACTIVE") 0) (command)) - (if (> (getvar "CMDACTIVE") 0) (command)) - (setvar "OSMODE" osm)(setvar "ATTREQ" areq)(setvar "ATTDIA" adia))) - ;; --- TEMP-DIAGNOSE: verschachtelte Blocknamen in eff auflisten --- - (if (tblsearch "BLOCK" eff) - (progn - (princ (strcat "\n[SSG-DBG] nested in " eff ":")) - (vl-catch-all-apply - (function (lambda () - (vlax-for e (vla-Item - (vla-get-Blocks (vla-get-ActiveDocument (vlax-get-acad-object))) - eff) - (if (= (vla-get-ObjectName e) "AcDbBlockReference") - (princ (strcat " [" (vla-get-Name e) "]"))))))))) - ;; --- ENDE TEMP-DIAGNOSE --- - eff))) + (if (and (not (tblsearch "BLOCK" eff)) (= (strcase base) (strcase eff))) + ;; Neu UND Dateiname == Blockname -> COM-Weg (kein Prompt). + (progn + (setq temp-obj (vla-InsertBlock modelspace + (vlax-3D-point '(0 0 0)) datei 1.0 1.0 1.0 0)) + (vla-Delete temp-obj)) + ;; Sonst (Block existiert bereits ODER Dateiname != Blockname wg. + ;; 3D-Fallback) -> aus Datei (neu) definieren. "_Y" beantwortet die + ;; Redefine-Abfrage, falls der Block bereits vorhanden ist; danach die + ;; laufende Einfuegung sofort abbrechen (Definition ist bereits wirksam). + (progn + (setq areq (getvar "ATTREQ") adia (getvar "ATTDIA") osm (getvar "OSMODE")) + (setvar "ATTREQ" 0)(setvar "ATTDIA" 0)(setvar "OSMODE" 0) + (if (tblsearch "BLOCK" eff) + (command "_.-INSERT" (strcat eff "=" datei) "_Y") ; Redefine bestehender Block + (command "_.-INSERT" (strcat eff "=" datei))) ; neu (kein Redefine-Prompt) + (if (> (getvar "CMDACTIVE") 0) (command)) + (if (> (getvar "CMDACTIVE") 0) (command)) + (setvar "OSMODE" osm)(setvar "ATTREQ" areq)(setvar "ATTDIA" adia))) + ;; Nur merken, wenn die Definition danach wirklich vorhanden ist - + ;; sonst wuerde ein fehlgeschlagener Ladeversuch dauerhaft gesperrt. + (if (tblsearch "BLOCK" eff) (ssg-block-refreshed-set eff)) + eff))))) ;; Wie ssg-ils-block-laden-dim, aber fuer die AKTUELLE Dimension. (defun ssg-ils-block-laden (blockname / ) diff --git a/tests/test_mubea.lsp b/tests/test_mubea.lsp index 1bcb1f0..3e71d42 100644 --- a/tests/test_mubea.lsp +++ b/tests/test_mubea.lsp @@ -113,7 +113,7 @@ ;; L_GF2 (Nachlauf) ist der Rest (L_GF_gesamt - L_GF1). (defun mubea:build-vario-one (tid richtung deltaL deltaH winkel-soll gf1-ziel seite hz startpunkt / ergebnis winkel-treffer best-winkel L_GF L_VF - gf1 gf2 hoehe-bis lastEnt ent) + gf1 gf2 hoehe-bis lastEnt ent vf-nummer) (if (or (null seite) (= seite "")) (setq seite "links")) (if (null hz) (setq hz 0.0) (setq hz (float hz))) (princ (strcat "\n [VARIO] " tid " -> deltaL=" (rtos deltaL 2 0) @@ -147,7 +147,14 @@ (setq hoehe-bis (if (= richtung "Auf") deltaH (- deltaH))) (setq lastEnt (vf-lastent-ohne-attribute)) (variofoerderer-einfuegen deltaL deltaH richtung best-winkel gf1 gf2 L_VF startpunkt seite hz) - (setq ent (vf-block-erstellen "standard" seite 1 0 0 hoehe-bis + ;; Blocknummer wie in der Produktion ueber vf-next-number holen (NICHT fest + ;; auf 1): vf-next-number ueberspringt bereits belegte VF_n-Nummern. Mit + ;; einer festen 1 traf das _.-BLOCK des ZWEITEN Foerderers auf den schon + ;; vorhandenen Namen "VF_1" -> BricsCAD fragt "Neu definieren?", verschluckt + ;; damit Basispunkt/Auswahlsatz der festen Antwortfolge, meldet "Eintrag + ;; kann nicht erkannt werden" und der Befehl bleibt offen (Haenger). + (setq vf-nummer (vf-next-number)) + (setq ent (vf-block-erstellen "standard" seite vf-nummer 0 0 hoehe-bis deltaH deltaL L_VF gf1 gf2 richtung best-winkel startpunkt lastEnt hz "0" "Schoenenberger Geruest")) (if (or (null ent) (/= (type ent) 'ENAME)) @@ -327,7 +334,7 @@ ;; > 0) - dieselbe Absicherung wie in ssg-ils-block-laden-dim (Zeilen 418f.) und ;; omni:insert-dxf (Zeile 980): nach dem INSERT verbleibende Prompts mit Leer- ;; Antworten (Enter) abfangen, bis der Befehl sauber beendet ist. -(defun mubea:build-separator-one (tid blockname pt rot / eff) +(defun mubea:build-separator-one (tid blockname pt rot / eff n) (princ (strcat "\n [SEP] " tid " '" (if blockname blockname "?") "' -> (" (rtos (car pt) 2 0) "," (rtos (cadr pt) 2 0) "," (rtos (caddr pt) 2 0) ") R=" (rtos rot 2 0))) @@ -342,7 +349,20 @@ (if eff (progn (command "_.INSERT" eff pt "" "" rot) - (while (> (getvar "CMDACTIVE") 0) (command "")) + ;; Verbleibende Prompts mit Enter abfangen - aber GEZAEHLT. Ein Prompt, der + ;; eine Leer-Antwort ablehnt ("Eintrag kann nicht erkannt werden. Bitte + ;; versuchen Sie es erneut."), haelt CMDACTIVE dauerhaft > 0: eine + ;; ungebremste (while (> CMDACTIVE 0) (command "")) Schleife laeuft dort + ;; endlos und BricsCAD haengt. Danach hart abbrechen und melden. + (setq n 0) + (while (and (> (getvar "CMDACTIVE") 0) (< n 5)) + (command "") + (setq n (1+ n))) + (if (> (getvar "CMDACTIVE") 0) + (progn + (command) + (princ (strcat "\n WARNUNG: INSERT von '" eff + "' nicht sauber beendet - abgebrochen.")))) (mubea:ent-json tid "separator" (entlast))) (progn ;; Kein Block gefunden -> sichtbaren Platzhalter zeichnen diff --git a/tests/testdata/mubea.json b/tests/testdata/mubea.json index 620aa18..4e2d457 100644 --- a/tests/testdata/mubea.json +++ b/tests/testdata/mubea.json @@ -42,7 +42,7 @@ "function": "insert", "block": "S-LP", "x": 400, - "y": -4700, + "y": -3815, "z": 1467, "rotation": 90, "assigned_to": "Kreisel_links" @@ -52,7 +52,7 @@ "function": "insert", "block": "Scanner", "x": 400, - "y": -4823, + "y": -3925, "z": 1467, "rotation": 0, "assigned_to": "Kreisel_links" @@ -94,7 +94,22 @@ "x": 400, "y": 14120, "z": 1467, - "comment": "deltaL=9110 wie die Gefaellestrecken (GF_Mubea deltaL_mm) - deltaL ist die GESAMTLAENGE des VF (Vorlauf L_GF1 + Foerderer L_VF + Nachlauf L_GF2), analog foerderer_tests.json/object_data.md 3.1; winkel=21 wird erzwungen (naechster verfuegbarer Bogenwinkel); L_GF (gesamt) und L_VF werden von berechne-alle-winkel aus deltaL/deltaH/winkel geloest, L_GF1=500 (Vorlauf-Ziel) fix, L_GF2 = L_GF_gesamt - L_GF1 (Nachlauf, Rest)" + "comment": "oberer Förderer für die Leertrolleys" + }, + { + "test_id": "VF_Schnitt_E", + "richtung": "Auf", + "deltaL": 9110, + "deltaH": 474, + "winkel": 21, + "gf_verteilung": "manuell", + "L_GF1": 2298, + "seite": "links", + "hz": 0.0, + "x": 400, + "y": -4133, + "z": 1467, + "comment": "unterer Förderer für die Teile" }, { "comment": "Separator Förderer oben", @@ -170,7 +185,7 @@ "block": "S-LP", "x": 10310, "y": -500, - "z": 1467, + "z": 1941, "rotation": 90, "assigned_to": "Kreisel_rechts" }, @@ -180,7 +195,7 @@ "block": "S-LP", "x": 10310, "y": 100, - "z": 1467, + "z": 1941, "rotation": 90, "assigned_to": "Kreisel_rechts" }, @@ -190,7 +205,7 @@ "block": "S-LP", "x": 9510, "y": -4823, - "z": 1467, + "z": 1941, "rotation": 270, "assigned_to": "GF_Mubea_20" }