[FIX] Eindeutige IDs und korrekte ZUORDNUNG der verpackten Separatoren
Beim CSV-Export von Mubea trugen Gefaellestrecke/VF/Kreisel und die aus ihnen erzeugten Separator-Kopien dieselbe ID (26 Dubletten 0001-0026), und die ZUORDNUNG der Separatoren zeigte auf den falschen Carrier. ssg-id-check-all: neue Phase 0 ermittelt das globale Maximum der bereits vergebenen IDs, BEVOR die erste neue ID vergeben wird (ausgelagert nach ssg-id-max-in-ss, das auch ssg-id-max nutzt). Bisher wurde max-id erst waehrend Phase 1 mitgezogen (Start 0); da (ssget "X" ...) in BricsCAD die zuletzt erzeugten Entities zuerst liefert, standen die noch ID-losen Separator-Kopien ganz vorne und bekamen 0001, 0002, ... - genau die IDs der weiter hinten liegenden Wrapper-Bloecke. Phase 2 konnte das nicht heilen, weil frisch vergebene IDs nicht in id-map landen. ssg-collect-nested-inserts: fuehrt Position, Z-Drehung und Skalierung jetzt ueber alle Verschachtelungsebenen mit (Records statt nackter Entity-Namen). Vorher gab die Rekursion Entities tieferer Ebenen mit ihrer ROH-Position aus der Zwischen-Blockdefinition zurueck - zwei Separatoren an derselben lokalen Stelle in zwei verschieden platzierten Zwischenbloecken landeten dadurch auf exakt derselben Weltposition. csv:sep-proxies-erzeugen: die Kopie steht jetzt exakt auf Position/Drehung/ Skalierung ihres verpackten Vorbilds; der kosmetische Versatz von 500 mm ist weg (er kippte Separatoren am Kettenende in die Boundingbox des Nachbar- Carriers). Der vla-InsertBlock-Aufruf ist gekapselt, damit ein Sonderfall nur diese eine Kopie ausfallen laesst statt den ganzen Export abzubrechen. csv:sep-proxies-zuordnung-setzen (neu, laeuft NACH ssg-id-check-all, da ein frisch gebauter Wrapper vorher keine ID hat): fuer einen VERPACKTEN Separator ist der Carrier bekannt - es ist der Wrapper, in dem er steckt. ZUORDNUNG wird daher auf die Wrapper-ID gesetzt (Separator in VF 0010 -> ZUORDNUNG 0010) und ueber *cs-sep-fix-by-handle* festgenagelt: cs-zuordnung-lauf uebernimmt sie unveraendert statt sie geometrisch neu zu raten, und csv:block-to-json reicht sie als "zuordnung_fix" an export_csv.py durch. Ohne das ueberschrieb compute_sensor_zuordnung (Prioritaet GF > Foerderer > Kreiselhaelfte) den bekannten Wert - in der Mubea-Zeichnung landeten 26 in VF/GF verpackte Separatoren so bei einer Kreiselhaelfte. export_csv.py: respektiert "zuordnung_fix" und ersetzt eine vorhandene ZUORDNUNG nicht mehr durch "nicht zugeordnet" - nur ein echter Treffer ueberschreibt, wie im Docstring von compute_sensor_zuordnung beschrieben. tests/test_export_ids.py (neu): prueft je tests/output/*_export.csv, dass jede TeileId hoechstens einmal vorkommt und jede Sensor-Zuordnung auf eine existierende TeileId zeigt. Gegen die fehlerhafte Mubea-CSV schlaegt der Test mit allen 26 Dubletten an. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+2
-1
@@ -395,7 +395,8 @@ Sammelt INSERT-Blöcke aus der Zeichnung und exportiert sie als JSON oder ruft P
|
|||||||
| `c:EXPORTSIVAS` | Alle Blöcke als JSON sammeln und `export_sivas.py` aufrufen (Sivas-CSV) |
|
| `c:EXPORTSIVAS` | Alle Blöcke als JSON sammeln und `export_sivas.py` aufrufen (Sivas-CSV) |
|
||||||
| `c:EXPORTCSV` | Alle Blöcke als JSON sammeln und `export_csv.py` aufrufen (einfache Item-Liste, inkl. Omniflo Bogen/Weiche/Gerade und abschließender "Omniflo Sum"-Zeile) |
|
| `c:EXPORTCSV` | Alle Blöcke als JSON sammeln und `export_csv.py` aufrufen (einfache Item-Liste, inkl. Omniflo Bogen/Weiche/Gerade und abschließender "Omniflo Sum"-Zeile) |
|
||||||
| `csv:collect-export-blocks` | Alle INSERT-Blöcke der Zeichnung als JSON-Array sammeln |
|
| `csv:collect-export-blocks` | Alle INSERT-Blöcke der Zeichnung als JSON-Array sammeln |
|
||||||
| `csv:run-export py-skript json-datei output-csv` | Python-Skript mit gesammelten Daten aufrufen; legt vorher fuer in VF_n/GF_n/KREISEL_n **verpackte** Separator_SP_2D/_3D-Sub-INSERTs (Sensor-Symbol; siehe `ssg-collect-nested-inserts` in `ssg_core.lsp` - NICHT der interne Staustrecke_Separator_SP-Trennsteg) je eine echte, temporaere Kopie daneben an (`csv:sep-proxies-erzeugen`), damit sie ganz normal ueber `ssg-id-check-all`/`csv:collect-export-blocks` erfasst werden, und entfernt sie danach wieder (`csv:sep-proxies-loeschen`) |
|
| `csv:run-export py-skript json-datei output-csv` | Python-Skript mit gesammelten Daten aufrufen; legt vorher fuer in VF_n/GF_n/KREISEL_n **verpackte** Separator_SP_2D/_3D-Sub-INSERTs (Sensor-Symbol; siehe `ssg-collect-nested-inserts` in `ssg_core.lsp` - NICHT der interne Staustrecke_Separator_SP-Trennsteg) je eine echte, temporaere Kopie **an derselben Stelle** an (`csv:sep-proxies-erzeugen`, Position/Drehung/Skalierung ueber alle Verschachtelungsebenen hinweg identisch zum Vorbild - kein kosmetischer Versatz mehr, der die geometrische Sensor-Zuordnung verfaelschen wuerde), damit sie ganz normal ueber `ssg-id-check-all`/`csv:collect-export-blocks` erfasst werden, und entfernt sie danach wieder (`csv:sep-proxies-loeschen`). Reihenfolge ist wesentlich: **erst alle** Kopien aus allen Wrappern erzeugen, **dann** `ssg-id-check-all` (vergibt IDs oberhalb des globalen Maximums), **dann** `csv:sep-proxies-zuordnung-setzen` |
|
||||||
|
| `csv:sep-proxies-zuordnung-setzen proxy-liste` | ZUORDNUNG jeder Separator-Kopie auf die **ID ihres Wrapper-Blocks** setzen (ein in VF_n mit ID 0010 verpackter Separator erhaelt ZUORDNUNG 0010) und die Zuordnung ueber `*cs-sep-fix-by-handle*` fuer `cs-zuordnung-lauf` festnageln - fuer verpackte Separatoren ist der Carrier bekannt und wird nicht per Boundingbox geraten. Muss NACH `ssg-id-check-all` laufen, da ein frisch gebauter Wrapper vorher noch keine ID hat |
|
||||||
| `csv:read-attribs ename` | Attribute eines INSERT als JSON-Objekt lesen |
|
| `csv:read-attribs ename` | Attribute eines INSERT als JSON-Objekt lesen |
|
||||||
| `c:OMNI_UPDATE_ATTRIBS` | HOEHE/DREHUNG aller Omniflo-Elemente aus der Zeichnung aktualisieren |
|
| `c:OMNI_UPDATE_ATTRIBS` | HOEHE/DREHUNG aller Omniflo-Elemente aus der Zeichnung aktualisieren |
|
||||||
|
|
||||||
|
|||||||
+55
-4
@@ -60,6 +60,16 @@
|
|||||||
;;; Box, damit Sensoren knapp am Rand noch erfasst werden.
|
;;; Box, damit Sensoren knapp am Rand noch erfasst werden.
|
||||||
;;; 6. Bei zugeordneten Sensoren wird die ID des Carriers (Attribut
|
;;; 6. Bei zugeordneten Sensoren wird die ID des Carriers (Attribut
|
||||||
;;; "ID") in das Sensor-Attribut ZUORDNUNG geschrieben.
|
;;; "ID") in das Sensor-Attribut ZUORDNUNG geschrieben.
|
||||||
|
;;; 6a. AUSNAHME zu 1-5: Separatoren, deren Entity-Handle in
|
||||||
|
;;; *cs-sep-fix-by-handle* steht, werden NICHT geometrisch
|
||||||
|
;;; zugeordnet, sondern dem dort genannten Carrier. Das sind die
|
||||||
|
;;; temporaeren Kopien der in VF_n/GF_n/KREISEL_n VERPACKTEN
|
||||||
|
;;; Separator_SP-Symbole (csv:sep-proxies-erzeugen/-zuordnung-
|
||||||
|
;;; setzen, export.lsp): fuer sie ist der Carrier bekannt - es ist
|
||||||
|
;;; der Wrapper-Block, in dem sie stecken -, und Raten per
|
||||||
|
;;; Boundingbox waere schlechter als die bekannte Wahrheit (am
|
||||||
|
;;; Kettenende ueberlappen sich Carrier-Boxen). Existiert der
|
||||||
|
;;; genannte Carrier nicht (mehr), greifen wieder 1-5.
|
||||||
;;; 7. Sensoren, zu denen ueberhaupt kein Carrier existiert (leere
|
;;; 7. Sensoren, zu denen ueberhaupt kein Carrier existiert (leere
|
||||||
;;; Zeichnung), werden mit einem Kreis auf dem Layer *mark-layer*
|
;;; Zeichnung), werden mit einem Kreis auf dem Layer *mark-layer*
|
||||||
;;; markiert (zentriert auf den Sensor).
|
;;; markiert (zentriert auf den Sensor).
|
||||||
@@ -92,6 +102,17 @@
|
|||||||
;; zugeordneten Carriers geschrieben wird.
|
;; zugeordneten Carriers geschrieben wird.
|
||||||
(setq *zuordnung-tag* "ZUORDNUNG")
|
(setq *zuordnung-tag* "ZUORDNUNG")
|
||||||
|
|
||||||
|
;; Festgenagelte (nicht geometrisch zu ermittelnde) Separator-Zuordnungen:
|
||||||
|
;; Assoc-Liste (entity-handle . carrier-id). Wird von csv:sep-proxies-
|
||||||
|
;; zuordnung-setzen (export.lsp) fuer die temporaeren Kopien der in
|
||||||
|
;; VF_n/GF_n/KREISEL_n VERPACKTEN Separator_SP-Symbole gefuellt - fuer die ist
|
||||||
|
;; der Carrier bekannt (es ist der Wrapper, in dem sie stecken) und muss
|
||||||
|
;; nicht ueber Boundingbox/Distanz geraten werden. Bei ueberlappenden
|
||||||
|
;; Carrier-Boxen (Kettenende an einem Kreisel) wuerde die Geometrie sonst
|
||||||
|
;; leicht den Nachbarn treffen. Leer = alles rein geometrisch (Normalfall
|
||||||
|
;; beim interaktiven ZAEHLE_SEP_SCAN).
|
||||||
|
(if (null *cs-sep-fix-by-handle*) (setq *cs-sep-fix-by-handle* nil))
|
||||||
|
|
||||||
;; Markierung von Sensoren ohne jeglichen Carrier: Kreis auf eigenem Layer.
|
;; Markierung von Sensoren ohne jeglichen Carrier: Kreis auf eigenem Layer.
|
||||||
(setq *mark-layer* "S_SENSOR_UNZUGEORDNET")
|
(setq *mark-layer* "S_SENSOR_UNZUGEORDNET")
|
||||||
(setq *mark-color* 2) ;; 2 = Gelb
|
(setq *mark-color* 2) ;; 2 = Gelb
|
||||||
@@ -362,6 +383,21 @@
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
;;; ------------------------------------------------------------
|
||||||
|
;;; Carrier-Record ueber seine ID finden (siehe *cs-sep-fix-by-handle*).
|
||||||
|
;;; carriers = Liste von Records (ename tagScan tagSep minx miny maxx
|
||||||
|
;;; maxy cx cy id label), id an Position 9.
|
||||||
|
;;; Rueckgabe: Record oder nil, wenn kein Carrier diese ID traegt.
|
||||||
|
;;; ------------------------------------------------------------
|
||||||
|
(defun cs-carrier-by-id (id carriers / rec treffer)
|
||||||
|
(if (and id (/= id ""))
|
||||||
|
(foreach rec carriers
|
||||||
|
(if (and (null treffer) (= (nth 9 rec) id))
|
||||||
|
(setq treffer rec)))
|
||||||
|
)
|
||||||
|
treffer
|
||||||
|
)
|
||||||
|
|
||||||
;;; ------------------------------------------------------------
|
;;; ------------------------------------------------------------
|
||||||
;;; Zaehler in einer Assoc-Liste (Schluessel = ename) erhoehen.
|
;;; Zaehler in einer Assoc-Liste (Schluessel = ename) erhoehen.
|
||||||
;;; ------------------------------------------------------------
|
;;; ------------------------------------------------------------
|
||||||
@@ -600,6 +636,7 @@
|
|||||||
scanCounts sepCounts n-scan-unassigned n-sep-unassigned
|
scanCounts sepCounts n-scan-unassigned n-sep-unassigned
|
||||||
n-strittig n-zuordnung-fehlt alt
|
n-strittig n-zuordnung-fehlt alt
|
||||||
s sent spt res mode rec best-d second-rec second-d
|
s sent spt res mode rec best-d second-rec second-d
|
||||||
|
vorgabe-id vorgabe-rec
|
||||||
scanner-id contested handle
|
scanner-id contested handle
|
||||||
en nm2 ns nsep newS newP okS okP eintrag)
|
en nm2 ns nsep newS newP okS okP eintrag)
|
||||||
(setq carriers nil scanlist nil seplist nil
|
(setq carriers nil scanlist nil seplist nil
|
||||||
@@ -719,8 +756,19 @@
|
|||||||
;; ANZAHL_SEPARATOR-Summe auch bei Sonderfaellen stimmt). ---
|
;; ANZAHL_SEPARATOR-Summe auch bei Sonderfaellen stimmt). ---
|
||||||
(foreach s seplist
|
(foreach s seplist
|
||||||
(setq sent (car s) spt (cadr s))
|
(setq sent (car s) spt (cadr s))
|
||||||
(setq res (cs-resolve-sensor spt carriers grid))
|
;; Festgenagelte Zuordnung (verpackter Separator, Carrier bekannt -
|
||||||
(setq mode (nth 0 res) rec (nth 1 res))
|
;; siehe *cs-sep-fix-by-handle*) hat Vorrang vor der Geometrie. Nur
|
||||||
|
;; wenn der genannte Carrier gar nicht (mehr) existiert, wird
|
||||||
|
;; geometrisch zugeordnet.
|
||||||
|
(setq vorgabe-id (cdr (assoc (cdr (assoc 5 (entget sent))) *cs-sep-fix-by-handle*)))
|
||||||
|
(setq vorgabe-rec (cs-carrier-by-id vorgabe-id carriers))
|
||||||
|
(if vorgabe-rec
|
||||||
|
(setq mode 'vorgabe rec vorgabe-rec)
|
||||||
|
(progn
|
||||||
|
(setq res (cs-resolve-sensor spt carriers grid))
|
||||||
|
(setq mode (nth 0 res) rec (nth 1 res))
|
||||||
|
)
|
||||||
|
)
|
||||||
(cond
|
(cond
|
||||||
((eq mode 'none)
|
((eq mode 'none)
|
||||||
(setq n-sep-unassigned (1+ n-sep-unassigned))
|
(setq n-sep-unassigned (1+ n-sep-unassigned))
|
||||||
@@ -728,12 +776,15 @@
|
|||||||
((null rec) nil) ;; Sicherheitsnetz - tritt bei bbox/distance nicht auf
|
((null rec) nil) ;; Sicherheitsnetz - tritt bei bbox/distance nicht auf
|
||||||
(t
|
(t
|
||||||
(setq sepCounts (cs-bump sepCounts (car rec)))
|
(setq sepCounts (cs-bump sepCounts (car rec)))
|
||||||
|
;; Abweichung nur bei geometrischer Zuordnung als Korrektur melden -
|
||||||
|
;; bei mode 'vorgabe ist der geschriebene Wert die Vorgabe selbst,
|
||||||
|
;; keine Korrektur einer Fehl-Zuordnung.
|
||||||
(setq alt (cs-get-att-str sent *zuordnung-tag*))
|
(setq alt (cs-get-att-str sent *zuordnung-tag*))
|
||||||
(if (and alt (/= alt "") (/= alt (nth 9 rec)))
|
(if (and (not (eq mode 'vorgabe)) alt (/= alt "") (/= alt (nth 9 rec)))
|
||||||
(setq *cs-korrekturen*
|
(setq *cs-korrekturen*
|
||||||
(cons (list "Separator" (cs-get-att-str sent "ID") alt (nth 9 rec))
|
(cons (list "Separator" (cs-get-att-str sent "ID") alt (nth 9 rec))
|
||||||
*cs-korrekturen*)))
|
*cs-korrekturen*)))
|
||||||
(if (member mode '(bbox distance))
|
(if (member mode '(vorgabe bbox distance))
|
||||||
(if (not (cs-set-att sent *zuordnung-tag* (nth 9 rec)))
|
(if (not (cs-set-att sent *zuordnung-tag* (nth 9 rec)))
|
||||||
(setq n-zuordnung-fehlt (1+ n-zuordnung-fehlt))))
|
(setq n-zuordnung-fehlt (1+ n-zuordnung-fehlt))))
|
||||||
)
|
)
|
||||||
|
|||||||
+142
-30
@@ -463,7 +463,8 @@
|
|||||||
;; csv:get-k-kos-strings) werden immer geschrieben, unabhaengig von
|
;; csv:get-k-kos-strings) werden immer geschrieben, unabhaengig von
|
||||||
;; include-bbox -- Kodierung ueber csv:kos-encode (siehe dort).
|
;; include-bbox -- Kodierung ueber csv:kos-encode (siehe dort).
|
||||||
(defun csv:block-to-json (ename include-bbox / ed blk-name layer pt rotation handle attribs bbox bbox-json
|
(defun csv:block-to-json (ename include-bbox / ed blk-name layer pt rotation handle attribs bbox bbox-json
|
||||||
insert-quat insert-kos k-list warnung-json warnung-eintrag)
|
insert-quat insert-kos k-list warnung-json warnung-eintrag
|
||||||
|
zuordnung-fix-json zuordnung-fix-eintrag)
|
||||||
(setq ed (entget ename))
|
(setq ed (entget ename))
|
||||||
(setq blk-name (cdr (assoc 2 ed)))
|
(setq blk-name (cdr (assoc 2 ed)))
|
||||||
(setq layer (cdr (assoc 8 ed)))
|
(setq layer (cdr (assoc 8 ed)))
|
||||||
@@ -517,6 +518,22 @@
|
|||||||
(if warnung-eintrag
|
(if warnung-eintrag
|
||||||
(setq warnung-json (strcat ",\"warnung\":\"" (csv:json-escape (cdr warnung-eintrag)) "\""))
|
(setq warnung-json (strcat ",\"warnung\":\"" (csv:json-escape (cdr warnung-eintrag)) "\""))
|
||||||
)
|
)
|
||||||
|
;; Bekannte (nicht geratene) Separator-Zuordnung durchreichen: fuer die
|
||||||
|
;; temporaeren Kopien VERPACKTER Separator_SP-Symbole steht der Carrier fest
|
||||||
|
;; (der Wrapper-Block, in dem das Symbol steckt) - siehe
|
||||||
|
;; csv:sep-proxies-zuordnung-setzen und *cs-sep-fix-by-handle*. export_csv.py
|
||||||
|
;; berechnet die Zuordnung sonst ein zweites Mal per Boundingbox-
|
||||||
|
;; Ueberschneidung (compute_sensor_zuordnung, Prioritaet GF > VF > Kreisel-
|
||||||
|
;; haelfte) und wuerde den bekannten Wert damit ueberschreiben - z.B. auf
|
||||||
|
;; eine Gefaellestrecke, in deren Box der Separator zufaellig hineinragt,
|
||||||
|
;; obwohl er in einem VF-Block verpackt ist. Mit diesem Feld uebernimmt
|
||||||
|
;; export_csv.py den Wert unveraendert.
|
||||||
|
(setq zuordnung-fix-json "")
|
||||||
|
(setq zuordnung-fix-eintrag (assoc handle *cs-sep-fix-by-handle*))
|
||||||
|
(if zuordnung-fix-eintrag
|
||||||
|
(setq zuordnung-fix-json
|
||||||
|
(strcat ",\"zuordnung_fix\":\"" (csv:json-escape (cdr zuordnung-fix-eintrag)) "\""))
|
||||||
|
)
|
||||||
(strcat
|
(strcat
|
||||||
" {\"block_name\":\"" (csv:json-escape blk-name) "\""
|
" {\"block_name\":\"" (csv:json-escape blk-name) "\""
|
||||||
",\"layer\":\"" (csv:json-escape layer) "\""
|
",\"layer\":\"" (csv:json-escape layer) "\""
|
||||||
@@ -533,6 +550,7 @@
|
|||||||
",\"k4\":\"" (csv:k-kos k-list "K4") "\""
|
",\"k4\":\"" (csv:k-kos k-list "K4") "\""
|
||||||
bbox-json
|
bbox-json
|
||||||
warnung-json
|
warnung-json
|
||||||
|
zuordnung-fix-json
|
||||||
"}"
|
"}"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -561,25 +579,38 @@
|
|||||||
(strcat (export:pattern "pattern_kreisel" "KR_*,KREISEL_*,ECKRAD_*") ",VF_*,GF_*")
|
(strcat (export:pattern "pattern_kreisel" "KR_*,KREISEL_*,ECKRAD_*") ",VF_*,GF_*")
|
||||||
)
|
)
|
||||||
|
|
||||||
;; --- Versatz (mm, in Blockrichtung +X) der temporaeren Separator-Kopien ---
|
;; --- Skalierungsfaktor absichern ---
|
||||||
;; "Einfach ausserhalb" der Original-Geometrie, damit sie nicht exakt auf
|
;; vla-InsertBlock verweigert den Faktor 0 (entartete Blockreferenz). Der
|
||||||
;; ihr verpacktes Vorbild zu liegen kommen (rein kosmetisch/zur Unter-
|
;; kann nur aus einer fehlerhaften Blockdefinition kommen; 1.0 ist dann die
|
||||||
;; scheidbarkeit waehrend der - sehr kurzen - Lebensdauer der Kopie).
|
;; brauchbarste Annahme - die Kopie steht wenigstens an der richtigen Stelle.
|
||||||
(setq *csv-sep-proxy-versatz-mm* 500.0)
|
(defun csv:skalierung-oder-eins (f)
|
||||||
|
(if (or (null f) (equal f 0.0 1e-12)) 1.0 f)
|
||||||
|
)
|
||||||
|
|
||||||
;; --- Fuer jedes verpackte Separator_SP-Symbol eine ECHTE, temporaere Kopie
|
;; --- Fuer jedes verpackte Separator_SP-Symbol eine ECHTE, temporaere Kopie
|
||||||
;; daneben in den Modellraum einfuegen ---
|
;; an DERSELBEN Stelle in den Modellraum einfuegen ---
|
||||||
;; Separator_SP-Sensor-Symbole, die beim Zusammenbau von VF_n/GF_n/
|
;; Separator_SP-Sensor-Symbole, die beim Zusammenbau von VF_n/GF_n/
|
||||||
;; KREISEL_n in die Compound-Blockdefinition "verpackt" wurden (siehe
|
;; KREISEL_n in die Compound-Blockdefinition "verpackt" wurden (siehe
|
||||||
;; ssg-collect-nested-inserts), sind fuer (ssget "X" ...) unsichtbar - und
|
;; ssg-collect-nested-inserts), sind fuer (ssget "X" ...) unsichtbar - und
|
||||||
;; damit weder fuer ssg-id-check-all (eigene ID) noch csv:collect-export-
|
;; damit weder fuer ssg-id-check-all (eigene ID) noch csv:collect-export-
|
||||||
;; blocks (JSON-Zeile) erreichbar. Statt beide Sammel-Funktionen dafuer
|
;; blocks (JSON-Zeile) erreichbar. Statt beide Sammel-Funktionen dafuer
|
||||||
;; anzupassen, wird hier VOR der ID-Vergabe (csv:run-export) fuer jeden Fund
|
;; anzupassen, wird hier VOR der ID-Vergabe (csv:run-export) fuer jeden Fund
|
||||||
;; eine ECHTE Kopie desselben Blocks an seiner Weltposition (+ Versatz in
|
;; eine ECHTE Kopie desselben Blocks eingefuegt.
|
||||||
;; Blockrichtung, damit sie nicht exakt auf dem Original liegt) eingefuegt.
|
|
||||||
;; Diese Kopie ist ein normaler, top-level INSERT und durchlaeuft ID-
|
;; Diese Kopie ist ein normaler, top-level INSERT und durchlaeuft ID-
|
||||||
;; Vergabe/Export danach unveraendert ueber die bestehenden Funktionen -
|
;; Vergabe/Export danach unveraendert ueber die bestehenden Funktionen -
|
||||||
;; deren Blockname-Muster (Separator_SP*) trifft ja bereits zu.
|
;; deren Blockname-Muster (Separator_SP*) trifft ja bereits zu.
|
||||||
|
;;
|
||||||
|
;; Die Kopie steht bewusst EXAKT auf der Weltposition/-drehung/-skalierung
|
||||||
|
;; ihres verpackten Vorbilds (frueher: +500 mm Versatz in Blockrichtung, rein
|
||||||
|
;; kosmetisch). Der Versatz war schaedlich: die geometrische Sensor-Zuordnung
|
||||||
|
;; in count_sep_scan.lsp arbeitet ueber die Carrier-Boundingbox, und ein um
|
||||||
|
;; 500 mm verschobener Separator kippt am Kettenende in die Box des Nachbar-
|
||||||
|
;; Carriers - die Kopie soll ihr Vorbild in der Zeichenebene 1:1
|
||||||
|
;; repraesentieren. Die Platzierung der Ebenen zwischen Wrapper und Symbol
|
||||||
|
;; steckt in den Records von ssg-collect-nested-inserts (Position/Drehung/
|
||||||
|
;; Skalierung relativ zur Wrapper-Definition); hier kommt nur noch die
|
||||||
|
;; Platzierung des top-level INSERT darauf.
|
||||||
|
;;
|
||||||
;; Einfuegen ueber vla-InsertBlock (ActiveX) statt "command _.INSERT": das
|
;; Einfuegen ueber vla-InsertBlock (ActiveX) statt "command _.INSERT": das
|
||||||
;; legt fuer einen Block mit ATTDEFs automatisch ATTRIB-Entities mit den
|
;; legt fuer einen Block mit ATTDEFs automatisch ATTRIB-Entities mit den
|
||||||
;; Vorgabewerten an (wie TEFInsert.lsp/tefl-insert-element, ssg_ks_insert.lsp
|
;; Vorgabewerten an (wie TEFInsert.lsp/tefl-insert-element, ssg_ks_insert.lsp
|
||||||
@@ -593,11 +624,15 @@
|
|||||||
;; koennten in dieser Sitzung noch gar nicht geladen worden sein, falls die
|
;; koennten in dieser Sitzung noch gar nicht geladen worden sein, falls die
|
||||||
;; Zeichnung mit bereits vorhandenen VF_n/GF_n-Bloecken geoeffnet und direkt
|
;; Zeichnung mit bereits vorhandenen VF_n/GF_n-Bloecken geoeffnet und direkt
|
||||||
;; exportiert wird, ohne vorher FOERDERANLAGE/GEFAELLESTRECKE aufzurufen.
|
;; exportiert wird, ohne vorher FOERDERANLAGE/GEFAELLESTRECKE aufzurufen.
|
||||||
;; Rueckgabe: Liste der neu eingefuegten Entity-Namen (fuer csv:sep-proxies-
|
;;
|
||||||
;; loeschen nach dem Export).
|
;; Rueckgabe: Liste von Paaren (proxy-ename . wrapper-ename) - der Wrapper
|
||||||
|
;; wird mitgefuehrt, weil erst NACH der ID-Vergabe daraus die ZUORDNUNG der
|
||||||
|
;; Kopie gesetzt werden kann (csv:sep-proxies-zuordnung-setzen). Die
|
||||||
|
;; proxy-enames dienen ausserdem csv:sep-proxies-loeschen nach dem Export.
|
||||||
(defun csv:sep-proxies-erzeugen ( / ms ss-all i ename ed bname nested pt rotation
|
(defun csv:sep-proxies-erzeugen ( / ms ss-all i ename ed bname nested pt rotation
|
||||||
nested-ent nested-ed nested-bname nested-rotation
|
psx psy psz c si rec nested-ent nested-bname
|
||||||
local-pt world-pt block-obj proxy-liste)
|
local-pt nested-rot lx ly lz world-pt
|
||||||
|
block-obj proxy-liste)
|
||||||
(setq ss-all (ssget "X" (list (cons 0 "INSERT"))))
|
(setq ss-all (ssget "X" (list (cons 0 "INSERT"))))
|
||||||
(if ss-all
|
(if ss-all
|
||||||
(progn
|
(progn
|
||||||
@@ -614,23 +649,45 @@
|
|||||||
(if nested
|
(if nested
|
||||||
(progn
|
(progn
|
||||||
(setq pt (cdr (assoc 10 ed)))
|
(setq pt (cdr (assoc 10 ed)))
|
||||||
(setq rotation (cdr (assoc 50 ed)))
|
(setq rotation (cond ((cdr (assoc 50 ed))) (0.0)))
|
||||||
(if (null rotation) (setq rotation 0.0))
|
(setq psx (cond ((cdr (assoc 41 ed))) (1.0)))
|
||||||
(foreach nested-ent nested
|
(setq psy (cond ((cdr (assoc 42 ed))) (1.0)))
|
||||||
(setq nested-ed (entget nested-ent))
|
(setq psz (cond ((cdr (assoc 43 ed))) (1.0)))
|
||||||
(setq nested-bname (cdr (assoc 2 nested-ed)))
|
(setq c (cos rotation) si (sin rotation))
|
||||||
(setq local-pt (cdr (assoc 10 nested-ed)))
|
(foreach rec nested
|
||||||
(setq nested-rotation (cdr (assoc 50 nested-ed)))
|
(setq nested-ent (nth 0 rec))
|
||||||
(if (null nested-rotation) (setq nested-rotation 0.0))
|
(setq local-pt (nth 1 rec))
|
||||||
(setq world-pt (csv:local-to-world pt rotation local-pt))
|
(setq nested-rot (nth 2 rec))
|
||||||
(setq world-pt (list (+ (car world-pt) *csv-sep-proxy-versatz-mm*)
|
(setq nested-bname (cdr (assoc 2 (entget nested-ent))))
|
||||||
(cadr world-pt)
|
(setq lx (* psx (car local-pt))
|
||||||
(caddr world-pt)))
|
ly (* psy (cadr local-pt))
|
||||||
|
lz (* psz (caddr local-pt)))
|
||||||
|
(setq world-pt
|
||||||
|
(list (+ (car pt) (- (* lx c) (* ly si)))
|
||||||
|
(+ (cadr pt) (+ (* lx si) (* ly c)))
|
||||||
|
(+ (cond ((caddr pt)) (0.0)) lz)))
|
||||||
;; Rotation fuer vla-InsertBlock in RADIANT (ActiveX-Konvention,
|
;; Rotation fuer vla-InsertBlock in RADIANT (ActiveX-Konvention,
|
||||||
;; anders als der Grad-Winkel von "command _.INSERT").
|
;; anders als der Grad-Winkel von "command _.INSERT").
|
||||||
(setq block-obj (vla-InsertBlock ms (vlax-3D-point world-pt)
|
;; Gekapselt, weil hier - anders als beim frueheren festen
|
||||||
nested-bname 1.0 1.0 1.0 (+ rotation nested-rotation)))
|
;; 1.0/1.0/1.0 - die tatsaechlichen Skalierungsfaktoren
|
||||||
(setq proxy-liste (cons (vlax-vla-object->ename block-obj) proxy-liste))
|
;; durchgereicht werden: ein einzelner Sonderfall (entartete
|
||||||
|
;; Skalierung, gespiegelter Zwischenblock) darf nicht den
|
||||||
|
;; kompletten Export der Zeichnung abbrechen, sondern nur diese
|
||||||
|
;; eine Kopie ausfallen lassen - mit Meldung, welcher Block.
|
||||||
|
(setq block-obj
|
||||||
|
(vl-catch-all-apply 'vla-InsertBlock
|
||||||
|
(list ms (vlax-3D-point world-pt) nested-bname
|
||||||
|
(csv:skalierung-oder-eins (* psx (nth 3 rec)))
|
||||||
|
(csv:skalierung-oder-eins (* psy (nth 4 rec)))
|
||||||
|
(csv:skalierung-oder-eins (* psz (nth 5 rec)))
|
||||||
|
(+ rotation nested-rot))))
|
||||||
|
(if (vl-catch-all-error-p block-obj)
|
||||||
|
(princ (ssg-textf "exp-sep-proxy-fehler"
|
||||||
|
(list nested-bname bname
|
||||||
|
(vl-catch-all-error-message block-obj))))
|
||||||
|
(setq proxy-liste
|
||||||
|
(cons (cons (vlax-vla-object->ename block-obj) ename) proxy-liste))
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -641,10 +698,56 @@
|
|||||||
proxy-liste
|
proxy-liste
|
||||||
)
|
)
|
||||||
|
|
||||||
|
;; --- ZUORDNUNG der temporaeren Separator-Kopien aus ihrem Wrapper setzen ---
|
||||||
|
;; MUSS nach ssg-id-check-all laufen: erst dann steht die ID des Wrapper-
|
||||||
|
;; Blocks (VF_n/GF_n/KREISEL_n) fest - ein frisch gebauter Wrapper kann beim
|
||||||
|
;; Erzeugen der Kopien noch gar keine ID haben.
|
||||||
|
;;
|
||||||
|
;; Die Zuordnung eines VERPACKTEN Separators ist keine Schaetzung, sondern
|
||||||
|
;; bekannt: er steckt in genau diesem Wrapper. Ein Separator, der in einem
|
||||||
|
;; VF-Block mit ID 0010 verpackt ist, bekommt also ZUORDNUNG = 0010. Die
|
||||||
|
;; geometrische Zuordnung in count_sep_scan.lsp (Carrier-Boundingbox bzw.
|
||||||
|
;; naechster Carrier) wuerde diesen Wert bei jedem Lauf neu berechnen und ggf.
|
||||||
|
;; ueberschreiben - bei ueberlappenden Boxen (Kettenende an einem Kreisel)
|
||||||
|
;; landet der Separator dort leicht beim Nachbarn. Darum wird die bekannte
|
||||||
|
;; Zuordnung ueber *cs-sep-fix-by-handle* (Key = Entity-Handle der Kopie)
|
||||||
|
;; festgenagelt; cs-zuordnung-lauf uebernimmt sie unveraendert und zaehlt den
|
||||||
|
;; Separator beim richtigen Carrier.
|
||||||
|
;; Rueckgabe: Anzahl festgenagelter Kopien.
|
||||||
|
(defun csv:sep-proxies-zuordnung-setzen (proxy-liste / paar proxy wrapper wrapper-id
|
||||||
|
handle gesetzt ohne-id)
|
||||||
|
(setq gesetzt 0 ohne-id 0)
|
||||||
|
(setq *cs-sep-fix-by-handle* nil)
|
||||||
|
(foreach paar proxy-liste
|
||||||
|
(setq proxy (car paar))
|
||||||
|
(setq wrapper (cdr paar))
|
||||||
|
(setq wrapper-id (cdr (assoc "ID" (ssg-attrib-read wrapper))))
|
||||||
|
(if (and wrapper-id (> (strlen wrapper-id) 0))
|
||||||
|
(progn
|
||||||
|
(ssg-attrib-set-on proxy (list (cons "ZUORDNUNG" wrapper-id)))
|
||||||
|
(setq handle (cdr (assoc 5 (entget proxy))))
|
||||||
|
(setq *cs-sep-fix-by-handle*
|
||||||
|
(cons (cons handle wrapper-id) *cs-sep-fix-by-handle*))
|
||||||
|
(setq gesetzt (1+ gesetzt))
|
||||||
|
)
|
||||||
|
;; Wrapper ohne ID-Attribut (Altbestand ohne ID-ATTDEF): keine bekannte
|
||||||
|
;; Zuordnung - die Kopie faellt auf die geometrische Zuordnung zurueck.
|
||||||
|
(setq ohne-id (1+ ohne-id))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(if (> gesetzt 0)
|
||||||
|
(princ (ssg-textf "exp-sep-zuordnung" (list (itoa gesetzt)))))
|
||||||
|
(if (> ohne-id 0)
|
||||||
|
(princ (ssg-textf "exp-sep-zuordnung-ohne-id" (list (itoa ohne-id)))))
|
||||||
|
gesetzt
|
||||||
|
)
|
||||||
|
|
||||||
;; --- Temporaere Separator_SP-Kopien (csv:sep-proxies-erzeugen) nach dem
|
;; --- Temporaere Separator_SP-Kopien (csv:sep-proxies-erzeugen) nach dem
|
||||||
;; Export wieder entfernen ---
|
;; Export wieder entfernen ---
|
||||||
|
;; proxy-liste = Paare (proxy-ename . wrapper-ename), nur der car wird geloescht.
|
||||||
(defun csv:sep-proxies-loeschen (proxy-liste)
|
(defun csv:sep-proxies-loeschen (proxy-liste)
|
||||||
(foreach ent proxy-liste (entdel ent))
|
(foreach paar proxy-liste (entdel (car paar)))
|
||||||
|
(setq *cs-sep-fix-by-handle* nil)
|
||||||
(princ)
|
(princ)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -736,9 +839,18 @@
|
|||||||
;; ID-Vergabe durch temporaere, echte Kopien ersetzen - sie werden am Ende
|
;; ID-Vergabe durch temporaere, echte Kopien ersetzen - sie werden am Ende
|
||||||
;; dieser Funktion wieder entfernt (csv:sep-proxies-loeschen).
|
;; dieser Funktion wieder entfernt (csv:sep-proxies-loeschen).
|
||||||
(setq sep-proxies (csv:sep-proxies-erzeugen))
|
(setq sep-proxies (csv:sep-proxies-erzeugen))
|
||||||
;; Vor dem Export: IDs aller Bloecke sicherstellen und Duplikate korrigieren
|
;; Vor dem Export: IDs aller Bloecke sicherstellen und Duplikate korrigieren.
|
||||||
|
;; Erst hier - also erst nachdem ALLE Kopien aus allen Wrapper-Bloecken
|
||||||
|
;; stehen - wird ueberhaupt eine ID vergeben. ssg-id-check-all ermittelt
|
||||||
|
;; dabei zuerst das globale Maximum der bereits vergebenen IDs (Phase 0)
|
||||||
|
;; und nummeriert die noch ID-losen Kopien erst danach - sonst bekaemen sie
|
||||||
|
;; IDs, die die Wrapper (VF_n/GF_n/Kreisel) selbst schon tragen.
|
||||||
(princ (ssg-textf "exp-check-ids" (list label)))
|
(princ (ssg-textf "exp-check-ids" (list label)))
|
||||||
(ssg-id-check-all)
|
(ssg-id-check-all)
|
||||||
|
;; Jetzt steht die ID jedes Wrappers fest: die bekannte (nicht geratene)
|
||||||
|
;; ZUORDNUNG der verpackten Separatoren auf die Wrapper-ID setzen und fuer
|
||||||
|
;; cs-zuordnung-lauf festnageln.
|
||||||
|
(csv:sep-proxies-zuordnung-setzen sep-proxies)
|
||||||
;; Vor dem Export HOEHE/DREHUNG aller Omniflo-Elemente aktualisieren
|
;; Vor dem Export HOEHE/DREHUNG aller Omniflo-Elemente aktualisieren
|
||||||
(princ (ssg-textf "exp-update-hoehe-drehung" (list label)))
|
(princ (ssg-textf "exp-update-hoehe-drehung" (list label)))
|
||||||
(omni:update-all-attribs)
|
(omni:update-all-attribs)
|
||||||
|
|||||||
+56
-10
@@ -761,26 +761,72 @@
|
|||||||
;; die durchlaeuft ID-Vergabe/Export danach ganz normal ueber die
|
;; die durchlaeuft ID-Vergabe/Export danach ganz normal ueber die
|
||||||
;; unveraenderten ssg-id-check-all/csv:collect-export-blocks. Rekursiv,
|
;; unveraenderten ssg-id-check-all/csv:collect-export-blocks. Rekursiv,
|
||||||
;; falls ein Treffer selbst nochmal in einem weiteren Sub-Compound-Block
|
;; falls ein Treffer selbst nochmal in einem weiteren Sub-Compound-Block
|
||||||
;; steckt. Rueckgabe: Liste von Entity-Namen (aus der Blockdefinition, kein
|
;; steckt.
|
||||||
;; Selection-Set - sie sind nicht Space-resident und daher fuer ssadd nicht
|
;;
|
||||||
;; sicher verwendbar), oder nil. Die gefundenen Entity-Namen bleiben ueber
|
;; Rueckgabe: Liste von Treffer-Records, oder nil. Ein Record ist
|
||||||
;; entget normal lesbar (Blockname/Einfuegepunkt/Rotation), sollten aber
|
;; (ename (x y z) rot-rad sx sy sz)
|
||||||
;; NICHT direkt exportiert werden - eine eigene Bounding-Box
|
;; mit der Platzierung des Treffers RELATIV zur abgefragten Blockdefinition
|
||||||
;; (vla-getboundingbox) liefert fuer sie keine sinnvollen Weltkoordinaten,
|
;; bname - ueber alle Verschachtelungsebenen hinweg aufsummiert (Position,
|
||||||
;; da sie nicht Space-resident sind.
|
;; Z-Drehung und Skalierung). Der Aufrufer muss also nur noch die Platzierung
|
||||||
(defun ssg-collect-nested-inserts (bname pattern / blk-tbl sub-ent sub-ed sub-bname result)
|
;; des top-level INSERT daraufrechnen. Der rohe Einfuegepunkt aus
|
||||||
|
;; (entget ename) ist bei mehr als einer Ebene NICHT verwendbar - er gilt nur
|
||||||
|
;; innerhalb der unmittelbaren Zwischen-Blockdefinition.
|
||||||
|
;;
|
||||||
|
;; Kein Selection-Set: die Entities sind nicht Space-resident und damit fuer
|
||||||
|
;; ssadd nicht sicher verwendbar. Sie bleiben ueber entget normal lesbar
|
||||||
|
;; (Blockname, Attribute), sollten aber NICHT direkt exportiert werden - eine
|
||||||
|
;; eigene Bounding-Box (vla-getboundingbox) liefert fuer sie keine sinnvollen
|
||||||
|
;; Weltkoordinaten.
|
||||||
|
(defun ssg-collect-nested-inserts (bname pattern)
|
||||||
|
(ssg-collect-nested-inserts-rel bname pattern '(0.0 0.0 0.0) 0.0 1.0 1.0 1.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
;; --- Rekursions-Rumpf mit mitgefuehrter Platzierung ---
|
||||||
|
;; basis-pt/basis-rot/bsx/bsy/bsz = Platzierung der gerade durchsuchten
|
||||||
|
;; Blockdefinition im Koordinatensystem des URSPRUENGLICH abgefragten Blocks.
|
||||||
|
;; Auf jeder Ebene wird der Einfuegepunkt des Sub-INSERT erst mit der
|
||||||
|
;; Skalierung, dann mit der Drehung der Ebene darueber verrechnet und
|
||||||
|
;; aufaddiert - genau das fehlte frueher: die Rekursion gab die Entities
|
||||||
|
;; tieferer Ebenen mit ihrer ROH-Position aus der jeweiligen Zwischen-
|
||||||
|
;; Blockdefinition zurueck. Zwei Separatoren, die in zwei verschieden
|
||||||
|
;; platzierten Zwischenbloecken an derselben lokalen Stelle sitzen, landeten
|
||||||
|
;; dadurch auf exakt derselben Weltposition (und damit beim falschen Carrier).
|
||||||
|
;;
|
||||||
|
;; Modellannahme wie im uebrigen Export (csv:local-to-world): reine
|
||||||
|
;; Z-Drehung, keine OCS-/Extrusionsrichtung. Fuer die Wrapper-Ketten
|
||||||
|
;; VF_n/GF_n/KREISEL_n trifft das zu.
|
||||||
|
(defun ssg-collect-nested-inserts-rel (bname pattern basis-pt basis-rot bsx bsy bsz
|
||||||
|
/ blk-tbl sub-ent sub-ed sub-bname loc-pt
|
||||||
|
rot sx sy sz c si lx ly lz wx wy wz result)
|
||||||
(setq blk-tbl (tblsearch "BLOCK" bname))
|
(setq blk-tbl (tblsearch "BLOCK" bname))
|
||||||
(if blk-tbl
|
(if blk-tbl
|
||||||
(progn
|
(progn
|
||||||
|
(setq c (cos basis-rot) si (sin basis-rot))
|
||||||
(setq sub-ent (entnext (cdr (assoc -2 blk-tbl))))
|
(setq sub-ent (entnext (cdr (assoc -2 blk-tbl))))
|
||||||
(while sub-ent
|
(while sub-ent
|
||||||
(setq sub-ed (entget sub-ent))
|
(setq sub-ed (entget sub-ent))
|
||||||
(if (= (cdr (assoc 0 sub-ed)) "INSERT")
|
(if (= (cdr (assoc 0 sub-ed)) "INSERT")
|
||||||
(progn
|
(progn
|
||||||
(setq sub-bname (cdr (assoc 2 sub-ed)))
|
(setq sub-bname (cdr (assoc 2 sub-ed)))
|
||||||
|
(setq loc-pt (cdr (assoc 10 sub-ed)))
|
||||||
|
(setq rot (cond ((cdr (assoc 50 sub-ed))) (0.0)))
|
||||||
|
(setq sx (cond ((cdr (assoc 41 sub-ed))) (1.0)))
|
||||||
|
(setq sy (cond ((cdr (assoc 42 sub-ed))) (1.0)))
|
||||||
|
(setq sz (cond ((cdr (assoc 43 sub-ed))) (1.0)))
|
||||||
|
(setq lx (* bsx (car loc-pt))
|
||||||
|
ly (* bsy (cadr loc-pt))
|
||||||
|
lz (* bsz (cond ((caddr loc-pt)) (0.0))))
|
||||||
|
(setq wx (+ (car basis-pt) (- (* lx c) (* ly si)))
|
||||||
|
wy (+ (cadr basis-pt) (+ (* lx si) (* ly c)))
|
||||||
|
wz (+ (caddr basis-pt) lz))
|
||||||
(if (wcmatch sub-bname pattern)
|
(if (wcmatch sub-bname pattern)
|
||||||
(setq result (cons sub-ent result))
|
(setq result (cons (list sub-ent (list wx wy wz) (+ basis-rot rot)
|
||||||
(setq result (append result (ssg-collect-nested-inserts sub-bname pattern)))
|
(* bsx sx) (* bsy sy) (* bsz sz))
|
||||||
|
result))
|
||||||
|
(setq result (append result
|
||||||
|
(ssg-collect-nested-inserts-rel sub-bname pattern
|
||||||
|
(list wx wy wz) (+ basis-rot rot)
|
||||||
|
(* bsx sx) (* bsy sy) (* bsz sz))))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
+40
-8
@@ -62,13 +62,17 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
;; --- Hoechste ID in der Zeichnung ermitteln ---
|
;; --- Hoechste bereits vergebene ID in einem Auswahlsatz ermitteln ---
|
||||||
;; Durchsucht alle exportierbaren Bloecke nach dem Attribut "ID".
|
;; ss = Auswahlsatz (i.d.R. aus ssg-id-collect-blocks) oder nil.
|
||||||
;; Rueckgabe: Integer (hoechste ID) oder 0 wenn keine gefunden.
|
;; Rueckgabe: Integer (hoechste ID) oder 0 wenn keine gefunden.
|
||||||
(defun ssg-id-max ( / ss i ename attribs id-val id-num max-id)
|
;;
|
||||||
(dbgf "ssg-id-max")
|
;; Bewusst als eigene Funktion: ssg-id-check-all MUSS diesen Wert ueber den
|
||||||
|
;; GESAMTEN Auswahlsatz kennen, BEVOR es die erste neue ID vergibt (siehe
|
||||||
|
;; Phase 0 dort) - sonst haengt die Neuvergabe von der (beliebigen)
|
||||||
|
;; ssget-Reihenfolge ab und vergibt IDs, die weiter hinten im Auswahlsatz
|
||||||
|
;; schon belegt sind.
|
||||||
|
(defun ssg-id-max-in-ss (ss / i ename attribs id-val id-num max-id)
|
||||||
(setq max-id 0)
|
(setq max-id 0)
|
||||||
(setq ss (ssg-id-collect-blocks))
|
|
||||||
(if ss
|
(if ss
|
||||||
(progn
|
(progn
|
||||||
(setq i 0)
|
(setq i 0)
|
||||||
@@ -87,6 +91,16 @@
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
max-id
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
;; --- Hoechste ID in der Zeichnung ermitteln ---
|
||||||
|
;; Durchsucht alle exportierbaren Bloecke nach dem Attribut "ID".
|
||||||
|
;; Rueckgabe: Integer (hoechste ID) oder 0 wenn keine gefunden.
|
||||||
|
(defun ssg-id-max ( / max-id)
|
||||||
|
(dbgf "ssg-id-max")
|
||||||
|
(setq max-id (ssg-id-max-in-ss (ssg-id-collect-blocks)))
|
||||||
(dbgreturn max-id)
|
(dbgreturn max-id)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -156,10 +170,26 @@
|
|||||||
0
|
0
|
||||||
)
|
)
|
||||||
(progn
|
(progn
|
||||||
;; Phase 1: Alle IDs sammeln und hoechste ID ermitteln
|
;; Phase 0: Hoechste BEREITS vergebene ID ueber den GESAMTEN Auswahlsatz
|
||||||
|
;; ermitteln - zwingend VOR der ersten Neuvergabe in Phase 1.
|
||||||
|
;;
|
||||||
|
;; Frueher wurde max-id erst waehrend Phase 1 mitgefuehrt (Start 0) und
|
||||||
|
;; eine fehlende ID sofort mit max-id+1 belegt. Die Reihenfolge von
|
||||||
|
;; (ssget "X" ...) ist aber beliebig - in BricsCAD kommen die zuletzt
|
||||||
|
;; erzeugten Entities zuerst. Genau das trifft die temporaeren
|
||||||
|
;; Separator-Kopien aus csv:sep-proxies-erzeugen (export.lsp): sie haben
|
||||||
|
;; noch keine ID, stehen ganz vorne und bekamen daher 0001, 0002, ... -
|
||||||
|
;; also exakt die IDs, die die weiter hinten liegenden Kreisel/VF_n/GF_n
|
||||||
|
;; laengst tragen. Phase 2 konnte das nicht heilen, weil die frisch
|
||||||
|
;; vergebenen IDs nicht in id-map landen (und auch nicht muessen: sie
|
||||||
|
;; liegen jetzt garantiert oberhalb jeder vorhandenen ID).
|
||||||
|
(dbgp "=== Phase 0: hoechste vorhandene ID ermitteln ===")
|
||||||
|
(setq max-id (ssg-id-max-in-ss ss))
|
||||||
|
(dbgp (strcat "Phase 0: hoechste vorhandene ID = " (itoa max-id)))
|
||||||
|
|
||||||
|
;; Phase 1: Alle IDs sammeln, Bloecke ohne ID neu nummerieren
|
||||||
(dbgp "=== Phase 1: IDs sammeln ===")
|
(dbgp "=== Phase 1: IDs sammeln ===")
|
||||||
(setq id-map nil)
|
(setq id-map nil)
|
||||||
(setq max-id 0)
|
|
||||||
(setq i 0)
|
(setq i 0)
|
||||||
(while (setq ename (ssname ss i))
|
(while (setq ename (ssname ss i))
|
||||||
(setq attribs (ssg-attrib-read ename))
|
(setq attribs (ssg-attrib-read ename))
|
||||||
@@ -171,7 +201,9 @@
|
|||||||
(if (and id-val (> (strlen id-val) 0))
|
(if (and id-val (> (strlen id-val) 0))
|
||||||
(progn
|
(progn
|
||||||
(setq id-num (atoi id-val))
|
(setq id-num (atoi id-val))
|
||||||
(if (> id-num max-id) (setq max-id id-num))
|
;; max-id NICHT mehr hier nachziehen - es steht seit Phase 0 auf
|
||||||
|
;; dem globalen Maximum und dient ab jetzt nur noch als Zaehler
|
||||||
|
;; fuer die Neuvergabe.
|
||||||
;; In Map eintragen: (id-num . (ename1 ename2 ...))
|
;; In Map eintragen: (id-num . (ename1 ename2 ...))
|
||||||
(setq entry (assoc id-num id-map))
|
(setq entry (assoc id-num id-map))
|
||||||
(if entry
|
(if entry
|
||||||
|
|||||||
@@ -567,6 +567,9 @@
|
|||||||
"core-dwg-not-found": "\nFEHLER: Block nicht gefunden: %1",
|
"core-dwg-not-found": "\nFEHLER: Block nicht gefunden: %1",
|
||||||
"exp-cfg-not-found": "\n[export] WARNUNG: %1 nicht gefunden/leer, verwende Vorgaben.",
|
"exp-cfg-not-found": "\n[export] WARNUNG: %1 nicht gefunden/leer, verwende Vorgaben.",
|
||||||
"exp-check-ids": "\n[%1] Pruefe und vergebe IDs...",
|
"exp-check-ids": "\n[%1] Pruefe und vergebe IDs...",
|
||||||
|
"exp-sep-proxy-fehler": "\n[EXPORT] WARNUNG: Kopie des verpackten Separators '%1' aus Block '%2' fehlgeschlagen (%3) - dieser Separator fehlt im Export.",
|
||||||
|
"exp-sep-zuordnung": "\n[%1] verpackte Separator-Kopie(n): ZUORDNUNG aus der ID des Wrapper-Blocks gesetzt.",
|
||||||
|
"exp-sep-zuordnung-ohne-id": "\n[%1] verpackte Separator-Kopie(n) ohne Wrapper-ID - ZUORDNUNG wird geometrisch bestimmt.",
|
||||||
"exp-update-hoehe-drehung": "\n[%1] Aktualisiere Hoehe/Drehung...",
|
"exp-update-hoehe-drehung": "\n[%1] Aktualisiere Hoehe/Drehung...",
|
||||||
"exp-collect-blocks": "\n[%1] Sammle relevante Bloecke...",
|
"exp-collect-blocks": "\n[%1] Sammle relevante Bloecke...",
|
||||||
"exp-no-blocks-found": "\n[%1] Keine relevanten Bloecke gefunden.",
|
"exp-no-blocks-found": "\n[%1] Keine relevanten Bloecke gefunden.",
|
||||||
|
|||||||
@@ -567,6 +567,9 @@
|
|||||||
"core-dwg-not-found": "\nERROR: Block not found: %1",
|
"core-dwg-not-found": "\nERROR: Block not found: %1",
|
||||||
"exp-cfg-not-found": "\n[export] WARNING: %1 not found/empty, using defaults.",
|
"exp-cfg-not-found": "\n[export] WARNING: %1 not found/empty, using defaults.",
|
||||||
"exp-check-ids": "\n[%1] Checking and assigning IDs...",
|
"exp-check-ids": "\n[%1] Checking and assigning IDs...",
|
||||||
|
"exp-sep-proxy-fehler": "\n[EXPORT] WARNING: copying the packed separator '%1' out of block '%2' failed (%3) - this separator is missing from the export.",
|
||||||
|
"exp-sep-zuordnung": "\n[%1] packed separator copy/copies: ZUORDNUNG set from the wrapper block ID.",
|
||||||
|
"exp-sep-zuordnung-ohne-id": "\n[%1] packed separator copy/copies without a wrapper ID - ZUORDNUNG determined geometrically.",
|
||||||
"exp-update-hoehe-drehung": "\n[%1] Updating height/rotation...",
|
"exp-update-hoehe-drehung": "\n[%1] Updating height/rotation...",
|
||||||
"exp-collect-blocks": "\n[%1] Collecting relevant blocks...",
|
"exp-collect-blocks": "\n[%1] Collecting relevant blocks...",
|
||||||
"exp-no-blocks-found": "\n[%1] No relevant blocks found.",
|
"exp-no-blocks-found": "\n[%1] No relevant blocks found.",
|
||||||
|
|||||||
+27
-4
@@ -282,10 +282,15 @@ def build_sensor_merkmale(block):
|
|||||||
Scanner-Block (siehe pattern_separator/pattern_scanner in cfg/export.cfg).
|
Scanner-Block (siehe pattern_separator/pattern_scanner in cfg/export.cfg).
|
||||||
ZUORDNUNG/ARTINR/LAYER (+ neuerdings ID) sind die ATTDEFs (siehe
|
ZUORDNUNG/ARTINR/LAYER (+ neuerdings ID) sind die ATTDEFs (siehe
|
||||||
ils-insert-sensor/mubea:build-separator-one, ssg-id-collect-blocks in
|
ils-insert-sensor/mubea:build-separator-one, ssg-id-collect-blocks in
|
||||||
ssg_id.lsp)."""
|
ssg_id.lsp).
|
||||||
|
|
||||||
|
"zuordnung_fix" (csv:block-to-json) hat Vorrang vor dem ZUORDNUNG-Attribut:
|
||||||
|
es steht an den temporaeren Kopien der in VF_n/GF_n/KREISEL_n VERPACKTEN
|
||||||
|
Separator_SP-Symbole und traegt die ID des Wrapper-Blocks, in dem das
|
||||||
|
Symbol steckt - eine bekannte, keine geratene Zuordnung."""
|
||||||
attribs = block.get("attribs", {})
|
attribs = block.get("attribs", {})
|
||||||
return {
|
return {
|
||||||
"Zuordnung": attribs.get("ZUORDNUNG", ""),
|
"Zuordnung": block.get("zuordnung_fix") or attribs.get("ZUORDNUNG", ""),
|
||||||
"ARTINR": attribs.get("ARTINR", ""),
|
"ARTINR": attribs.get("ARTINR", ""),
|
||||||
"Höhe": get_hoehe(block),
|
"Höhe": get_hoehe(block),
|
||||||
"Drehung": get_drehung(block),
|
"Drehung": get_drehung(block),
|
||||||
@@ -757,6 +762,10 @@ def process_blocks(blocks, lookup, dbg=None):
|
|||||||
"bezeichnung": f"Separator :{separator_count}",
|
"bezeichnung": f"Separator :{separator_count}",
|
||||||
"planquadrat": planquadrat_column(block, planquadrat_cfg),
|
"planquadrat": planquadrat_column(block, planquadrat_cfg),
|
||||||
"merkmale": build_sensor_merkmale(block),
|
"merkmale": build_sensor_merkmale(block),
|
||||||
|
# Privates Feld (nicht in der CSV): markiert eine BEKANNTE
|
||||||
|
# Zuordnung, die compute_sensor_zuordnung unten nicht
|
||||||
|
# ueberschreiben darf - siehe build_sensor_merkmale.
|
||||||
|
"_zuordnung_fix": bool(block.get("zuordnung_fix")),
|
||||||
**bbox_columns(block),
|
**bbox_columns(block),
|
||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
@@ -819,8 +828,22 @@ def process_blocks(blocks, lookup, dbg=None):
|
|||||||
sensor_zuordnung = compute_sensor_zuordnung(
|
sensor_zuordnung = compute_sensor_zuordnung(
|
||||||
items, NEIGHBOR_TOLERANCE_MM, kreisel_durchmesser_mm=KREISEL_DURCHMESSER_MM, dbg=dbg)
|
items, NEIGHBOR_TOLERANCE_MM, kreisel_durchmesser_mm=KREISEL_DURCHMESSER_MM, dbg=dbg)
|
||||||
for item, zuordnung in zip(items, sensor_zuordnung):
|
for item, zuordnung in zip(items, sensor_zuordnung):
|
||||||
if item.get("teileart", "") in SENSOR_TEILEARTEN and zuordnung:
|
if item.get("teileart", "") not in SENSOR_TEILEARTEN or not zuordnung:
|
||||||
item["merkmale"]["Zuordnung"] = zuordnung
|
continue
|
||||||
|
# Bekannte Zuordnung (verpackter Separator, Carrier = Wrapper-Block,
|
||||||
|
# siehe "_zuordnung_fix"/build_sensor_merkmale) nicht ueberschreiben:
|
||||||
|
# die BBox-Suche hier priorisiert GF > Foerderer > Kreiselhaelfte und
|
||||||
|
# wuerde einen in einem VF-Block verpackten Separator an eine
|
||||||
|
# Gefaellestrecke haengen, in deren Box er zufaellig hineinragt.
|
||||||
|
if item.get("_zuordnung_fix"):
|
||||||
|
continue
|
||||||
|
# Kein Treffer ("nicht zugeordnet") ersetzt einen vorhandenen
|
||||||
|
# ZUORDNUNG-Attributwert nicht - sonst geht die in der Zeichnung
|
||||||
|
# gesetzte Zuordnung verloren (siehe Docstring von
|
||||||
|
# compute_sensor_zuordnung: ersetzen nur bei echtem Treffer).
|
||||||
|
if zuordnung == "nicht zugeordnet" and item["merkmale"].get("Zuordnung"):
|
||||||
|
continue
|
||||||
|
item["merkmale"]["Zuordnung"] = zuordnung
|
||||||
|
|
||||||
# NACH der Separator-Zuordnung: zu jedem Scanner den raeumlich naechst-
|
# NACH der Separator-Zuordnung: zu jedem Scanner den raeumlich naechst-
|
||||||
# gelegenen Separator suchen (eigene, unabhaengige Suche - siehe
|
# gelegenen Separator suchen (eigene, unabhaengige Suche - siehe
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
test_export_ids.py - Validiert IDs und Sensor-Zuordnung im CSV-Export.
|
||||||
|
|
||||||
|
Prueft jede von TEST_EXPORT_ALL (test_export_all.lsp) geschriebene
|
||||||
|
Datei tests/output/<basis>_export.csv:
|
||||||
|
|
||||||
|
1. Jede TeileId kommt HOECHSTENS EINMAL vor. Eine ID ist in der
|
||||||
|
Zeichnung eindeutig - Gefaellestrecke/VF/Kreisel und der aus
|
||||||
|
ihnen erzeugte Separator duerfen sich keine ID teilen.
|
||||||
|
(Regression: ssg-id-check-all zog max-id frueher erst waehrend
|
||||||
|
des Durchlaufs mit; die temporaeren Separator-Kopien aus
|
||||||
|
csv:sep-proxies-erzeugen stehen in der ssget-Reihenfolge ganz
|
||||||
|
vorne und bekamen dadurch 0001, 0002, ... - also genau die IDs
|
||||||
|
der weiter hinten liegenden Wrapper-Bloecke.)
|
||||||
|
|
||||||
|
2. Die Zuordnung jedes Separators/Scanners zeigt auf eine TeileId,
|
||||||
|
die es in derselben Datei auch gibt (bzw. auf eine Kreisel-
|
||||||
|
haelfte "<TeileId>-L"/"-R"). Eine Zuordnung auf eine nicht
|
||||||
|
existierende ID ist ein verwaister Verweis.
|
||||||
|
|
||||||
|
Beide Pruefungen brauchen keine Zeichnung und kein ezdxf - sie lesen
|
||||||
|
nur die fertige CSV. Ohne Export-CSV in tests/output wird der Test
|
||||||
|
uebersprungen (TEST_EXPORT_ALL in BricsCAD ausfuehren).
|
||||||
|
"""
|
||||||
|
|
||||||
|
import csv
|
||||||
|
import glob
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
# TeileArten, die einen Sensor beschreiben (siehe SENSOR_TEILEARTEN in
|
||||||
|
# lib/export_neighbors.py).
|
||||||
|
SENSOR_TEILEARTEN = {"ILS 2.0 Separator", "ILS 2.0 Scanner"}
|
||||||
|
|
||||||
|
# Zuordnungs-Wert, den lib/export_neighbors.py setzt, wenn die Bounding-Box
|
||||||
|
# des Sensors keinen Carrier trifft - kein verwaister Verweis, sondern die
|
||||||
|
# ausdrueckliche Aussage "keiner".
|
||||||
|
ZUORDNUNG_KEINE = "nicht zugeordnet"
|
||||||
|
|
||||||
|
# Platzhalter-TeileId fuer Bloecke ohne ID-Attribut (aeltere Exporte). Nicht
|
||||||
|
# als ID zu werten und daher von der Eindeutigkeitspruefung ausgenommen.
|
||||||
|
TEILEID_LEER = {"", "0000"}
|
||||||
|
|
||||||
|
|
||||||
|
def _output_dir():
|
||||||
|
return os.getenv("DXFM_TESTOUT",
|
||||||
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), "output"))
|
||||||
|
|
||||||
|
|
||||||
|
def _read_export_csv(path):
|
||||||
|
"""Liest eine Export-CSV als Liste von Dicts (Spaltenname -> Wert).
|
||||||
|
|
||||||
|
AutoLISP/Python schreiben die Datei je nach Lauf in UTF-8 oder in der
|
||||||
|
ANSI-Codepage des Systems - wie conftest._load_json erst UTF-8 versuchen,
|
||||||
|
dann cp1252.
|
||||||
|
"""
|
||||||
|
for encoding in ("utf-8-sig", "cp1252"):
|
||||||
|
try:
|
||||||
|
with open(path, "r", encoding=encoding, newline="") as f:
|
||||||
|
return list(csv.DictReader(f, delimiter=";"))
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
continue
|
||||||
|
pytest.fail(f"{os.path.basename(path)}: weder als UTF-8 noch als cp1252 lesbar")
|
||||||
|
|
||||||
|
|
||||||
|
def _export_csv_dateien():
|
||||||
|
return sorted(glob.glob(os.path.join(_output_dir(), "*_export.csv")))
|
||||||
|
|
||||||
|
|
||||||
|
def _merkmale(row):
|
||||||
|
"""Merkmale-Spalte (JSON) einer CSV-Zeile als Dict, {} wenn unlesbar."""
|
||||||
|
rohtext = (row.get("Merkmale") or "").strip()
|
||||||
|
if not rohtext:
|
||||||
|
return {}
|
||||||
|
try:
|
||||||
|
wert = json.loads(rohtext)
|
||||||
|
except ValueError:
|
||||||
|
return {}
|
||||||
|
return wert if isinstance(wert, dict) else {}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(params=_export_csv_dateien(),
|
||||||
|
ids=lambda p: os.path.basename(p))
|
||||||
|
def export_csv(request):
|
||||||
|
"""Je gefundene tests/output/*_export.csv ein eigener Testlauf."""
|
||||||
|
rows = _read_export_csv(request.param)
|
||||||
|
if not rows:
|
||||||
|
pytest.skip(f"{os.path.basename(request.param)} enthaelt keine Datenzeilen")
|
||||||
|
if "TeileId" not in rows[0]:
|
||||||
|
pytest.skip(f"{os.path.basename(request.param)} hat kein TeileId-Feld "
|
||||||
|
"(Export aus einer aelteren Spaltenversion)")
|
||||||
|
return os.path.basename(request.param), rows
|
||||||
|
|
||||||
|
|
||||||
|
if not _export_csv_dateien():
|
||||||
|
pytest.skip("Keine tests/output/*_export.csv vorhanden - "
|
||||||
|
"TEST_EXPORT_ALL in BricsCAD ausfuehren",
|
||||||
|
allow_module_level=True)
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 1. Eindeutigkeit der TeileId
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
def test_teileid_eindeutig(export_csv):
|
||||||
|
"""Keine TeileId darf zweimal vorkommen (Platzhalter ausgenommen)."""
|
||||||
|
name, rows = export_csv
|
||||||
|
|
||||||
|
belegung = {}
|
||||||
|
for row in rows:
|
||||||
|
teileid = (row.get("TeileId") or "").strip()
|
||||||
|
if teileid in TEILEID_LEER:
|
||||||
|
continue
|
||||||
|
belegung.setdefault(teileid, []).append(
|
||||||
|
f'{(row.get("TeileArt") or "").strip()} "{(row.get("Bezeichnung") or "").strip()}"')
|
||||||
|
|
||||||
|
doppelt = {tid: wer for tid, wer in belegung.items() if len(wer) > 1}
|
||||||
|
assert not doppelt, (
|
||||||
|
f"{name}: {len(doppelt)} TeileId(s) mehrfach vergeben - eine ID ist je "
|
||||||
|
"Zeichnungsobjekt eindeutig:\n" +
|
||||||
|
"\n".join(f" {tid}: " + ", ".join(wer) for tid, wer in sorted(doppelt.items()))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 2. Zuordnung der Sensoren zeigt auf eine existierende TeileId
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
def test_sensor_zuordnung_existiert(export_csv):
|
||||||
|
"""Jede Sensor-Zuordnung verweist auf eine TeileId derselben Datei."""
|
||||||
|
name, rows = export_csv
|
||||||
|
|
||||||
|
bekannte_ids = {(row.get("TeileId") or "").strip() for row in rows}
|
||||||
|
bekannte_ids -= TEILEID_LEER
|
||||||
|
|
||||||
|
verwaist = []
|
||||||
|
for row in rows:
|
||||||
|
if (row.get("TeileArt") or "").strip() not in SENSOR_TEILEARTEN:
|
||||||
|
continue
|
||||||
|
zuordnung = str(_merkmale(row).get("Zuordnung", "")).strip()
|
||||||
|
if not zuordnung or zuordnung == ZUORDNUNG_KEINE:
|
||||||
|
continue
|
||||||
|
# Kreiselhaelften werden als "<TeileId>-L"/"-R" notiert (siehe
|
||||||
|
# compute_sensor_zuordnung in lib/export_neighbors.py).
|
||||||
|
carrier_id = zuordnung[:-2] if zuordnung[-2:] in ("-L", "-R") else zuordnung
|
||||||
|
if carrier_id not in bekannte_ids:
|
||||||
|
verwaist.append(
|
||||||
|
f' {(row.get("TeileArt") or "").strip()} '
|
||||||
|
f'{(row.get("TeileId") or "").strip()} '
|
||||||
|
f'("{(row.get("Bezeichnung") or "").strip()}") -> {zuordnung}')
|
||||||
|
|
||||||
|
assert not verwaist, (
|
||||||
|
f"{name}: {len(verwaist)} Sensor(en) mit Zuordnung auf eine nicht "
|
||||||
|
"vorhandene TeileId:\n" + "\n".join(verwaist)
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user