Merge origin/master: Sensor-Zuordnung + Connections zusammenfuehren

Konflikt in lang/de_DE.json und lang/en_GB.json aufgeloest: beide Seiten
haben nur Schluessel ergaenzt (con-*/cmd-connection-* hier, sens-* aus
origin). Beide Saetze uebernommen, Einrueckung von origin (2 Leerzeichen)
beibehalten, tro-edit-fb mit UID-Anzeige uebernommen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-24 12:13:09 +02:00
8 changed files with 1947 additions and 1609 deletions
+16 -9
View File
@@ -993,7 +993,7 @@
aktuell-frame nach-aus-pt aktuell-frame nach-aus-pt
gf-nummer last-ent gf-nummer last-ent
L-gf-str delta-l-gf laengen-liste first-l l-item L-gf-str delta-l-gf laengen-liste first-l l-item
bL90 bL60 bL30 bR90 bR60 bR30 n-bogen-gf bL90 bL60 bL30 bR90 bR60 bR30
ein-hz-as rad-h-as rad-v-as xu-entry ein-hz-as rad-h-as rad-v-as xu-entry
sep-endpunkt rad-h-es rad-v-es xu-exit sep-frame sep-endpunkt rad-h-es rad-v-es xu-exit sep-frame
es-aus-frame es-aus-frame
@@ -1312,8 +1312,6 @@
) )
) )
) )
(setq n-bogen-gf (+ bL90 bL60 bL30 bR90 bR60 bR30))
;; delta-l-gf bereits vor der Zusammenfassung berechnet ;; delta-l-gf bereits vor der Zusammenfassung berechnet
;; Block mit allen Attributen erstellen. Geruest fuer Einzelmodul/ ;; Block mit allen Attributen erstellen. Geruest fuer Einzelmodul/
@@ -1327,7 +1325,9 @@
delta-l-gf delta-l-gf
L-gf-str L-gf-str
grade grade
(1+ n-bogen-gf) ;; n-separator/n-scanner = 0: interne Ketten-Separatoren zaehlen nicht;
;; die echte Anzahl setzt der Export aus platzierten Symbolen.
0
0 0
bL90 bL60 bL30 bL90 bL60 bL30
bR90 bR60 bR30 bR90 bR60 bR30
@@ -1386,7 +1386,7 @@
;; L-first-seg: Laenge der ersten geraden Staustrecke in mm. ;; L-first-seg: Laenge der ersten geraden Staustrecke in mm.
;; Beschriftung: Mitte der ersten Geraden, 400mm senkrecht rechts versetzt. ;; Beschriftung: Mitte der ersten Geraden, 400mm senkrecht rechts versetzt.
(defun gf-make-label (gf-nummer hoehe-von hoehe-bis deltaH deltaL (defun gf-make-label (gf-nummer hoehe-von hoehe-bis deltaH deltaL
L-gf-str winkel n-separator nach-aus-pt entry-hz L-first-seg / L-gf-str winkel nach-aus-pt entry-hz L-first-seg /
label-txt label-pt chz-l shz-l halb basis-winkel) label-txt label-pt chz-l shz-l halb basis-winkel)
(setq chz-l (cos (* entry-hz (/ pi 180.0)))) (setq chz-l (cos (* entry-hz (/ pi 180.0))))
(setq shz-l (sin (* entry-hz (/ pi 180.0)))) (setq shz-l (sin (* entry-hz (/ pi 180.0))))
@@ -1401,8 +1401,7 @@
" " delta-sym "H=" (itoa (fix (abs deltaH))) "mm" " " delta-sym "H=" (itoa (fix (abs deltaH))) "mm"
" " delta-sym "L=" (itoa (fix deltaL)) "mm" " " delta-sym "L=" (itoa (fix deltaL)) "mm"
" L=[" L-gf-str "]m" " L=[" L-gf-str "]m"
" W=" (rtos (float winkel) 2 2) grad-zeichen " W=" (rtos (float winkel) 2 2) grad-zeichen))
" Sep=" (itoa n-separator)))
;; Basiswinkel: Text immer lesbar auf 0 Grad (X-Achse, links->rechts) oder ;; Basiswinkel: Text immer lesbar auf 0 Grad (X-Achse, links->rechts) oder
;; 90 Grad (Y-Achse) einrasten - unabhaengig von der tatsaechlichen ;; 90 Grad (Y-Achse) einrasten - unabhaengig von der tatsaechlichen
;; Fahrtrichtung (nie 180/270, damit der Text nie auf dem Kopf steht). ;; Fahrtrichtung (nie 180/270, damit der Text nie auf dem Kopf steht).
@@ -1439,7 +1438,7 @@
(if (null geruest-typ) (setq geruest-typ (ssg-geruest-idx-to-typ (ssg-geruest-typ-to-idx nil)))) (if (null geruest-typ) (setq geruest-typ (ssg-geruest-idx-to-typ (ssg-geruest-typ-to-idx nil))))
;; Beschriftung: Mitte der ersten Geraden, 400mm senkrecht versetzt ;; Beschriftung: Mitte der ersten Geraden, 400mm senkrecht versetzt
(gf-make-label gf-nummer hoehe-von hoehe-bis deltaH deltaL (gf-make-label gf-nummer hoehe-von hoehe-bis deltaH deltaL
L-gf-str winkel n-separator nach-aus-pt entry-hz L-first-seg) L-gf-str winkel nach-aus-pt entry-hz L-first-seg)
;; TYP: mit Bogen -> "Streckengruppe"; ohne Bogen (einsegmentig) -> ;; TYP: mit Bogen -> "Streckengruppe"; ohne Bogen (einsegmentig) ->
;; "Gefaellestrecke" (reduzierter Attributsatz, siehe ssg-strecke-attrib-defs). ;; "Gefaellestrecke" (reduzierter Attributsatz, siehe ssg-strecke-attrib-defs).
(setq typ-str (setq typ-str
@@ -1518,6 +1517,12 @@
(cons "GF_Bogen_R_90" (itoa bogen-R90)) (cons "GF_Bogen_R_90" (itoa bogen-R90))
(cons "GF_Bogen_R_60" (itoa bogen-R60)) (cons "GF_Bogen_R_60" (itoa bogen-R60))
(cons "GF_Bogen_R_30" (itoa bogen-R30)) (cons "GF_Bogen_R_30" (itoa bogen-R30))
;; ANZAHL_SEPARATOR/ANZAHL_SCANNER werden beim Bau mit 0 vorbelegt
;; (n-separator/n-scanner werden von den Aufrufern als 0 uebergeben):
;; der intern gezeichnete Ketten-Separator Staustrecke_Separator_SP zaehlt
;; NICHT als Separator. Die tatsaechliche Anzahl ermittelt der Export aus
;; den platzierten Separator_SP/S-LP-/Scanner-Symbolen und schreibt sie
;; hier hinein (cs-zuordnung-lauf in count_sep_scan.lsp).
(cons "ANZAHL_SEPARATOR" (itoa n-separator)) (cons "ANZAHL_SEPARATOR" (itoa n-separator))
(cons "ANZAHL_SCANNER" (itoa n-scanner)) (cons "ANZAHL_SCANNER" (itoa n-scanner))
(cons "GERUEST_EINZELMODUL" geruest-einzelmodul) (cons "GERUEST_EINZELMODUL" geruest-einzelmodul)
@@ -1637,7 +1642,9 @@
(float deltaL-total) (float deltaL-total)
L-gf-str L-gf-str
winkel winkel
1 0 ;; n-separator/n-scanner = 0: der interne Ketten-Separator zaehlt nicht;
;; die echte Anzahl setzt der Export aus platzierten Symbolen (s.o.).
0 0
0 0 0 0 0 0 0 0 0 0 0 0
startpunkt last-ent nach-aus-pt (float hz) (float L_stau) startpunkt last-ent nach-aus-pt (float hz) (float L_stau)
es-seite geruest-einzelmodul geruest-typ)) es-seite geruest-einzelmodul geruest-typ))
+412 -202
View File
@@ -1,63 +1,90 @@
;;; ============================================================ ;;; ============================================================
;;; count_sep_scan.lsp - Eigenstaendiges Zaehl-Werkzeug ;;; count_sep_scan.lsp - Separator-/Scanner-Zuordnung
;;; ------------------------------------------------------------ ;;; ------------------------------------------------------------
;;; Zaehlt die manuell platzierten Bloecke "Scanner" und ;;; Zaehlt die manuell platzierten Bloecke "Scanner" und
;;; "Separator_SP" je ILS-Block (Kreisel / VarioFoerderer / ;;; "Separator_SP" (Muster siehe cfg/export.cfg [blockpattern]
;;; Gefaellestrecke) und schreibt die Anzahlen in die Attribute ;;; pattern_scanner/pattern_separator) je Carrier (Kreisel/Eckrad,
;;; des jeweiligen ILS-Blocks. Anschliessend QSAVE. ;;; VarioFoerderer-Streckengruppe VF_*, Gefaellestrecke GF_* -
;;; Muster pattern_kreisel/pattern_variofoerderer/
;;; pattern_gefaellestrecke) und schreibt die Anzahlen in die
;;; Attribute ANZAHL_SCANNER/ANZAHL_SEPARATOR des jeweiligen
;;; Carrier-Blocks.
;;; ;;;
;;; Zuordnung eines Sensors zu einem ILS-Block (Z wird ignoriert): ;;; WICHTIG: Gezaehlt werden ausschliesslich die eigenstaendig
;;; - Scanner: der Einfuegepunkt (X/Y) muss in der Bounding-Box ;;; platzierten Sensor-Symbole. Der intern in der GF/VF-Kette
;;; des ILS-Blocks liegen. Die 3D-Geometrie des Scanners kann ;;; gezeichnete 300-mm-Block Staustrecke_Separator_SP ist nur ein
;;; Ketten-/Uebergabestueck (kein eigenstaendiger Separator) und wird
;;; NICHT mitgezaehlt - wer einen Separator gezaehlt haben will, setzt
;;; ein Separator_SP/S-LP-Symbol.
;;;
;;; Zuordnung eines Sensors zu einem Carrier (Z wird ignoriert):
;;; 0. Traegt der Sensor bereits ein ZUORDNUNG-Attribut, wird das
;;; respektiert (nicht ueberschrieben) - er zaehlt mit, wenn die
;;; dort stehende ID zu einem bekannten Carrier passt.
;;; 1. Scanner: der Einfuegepunkt (X/Y) muss in der Bounding-Box
;;; des Carriers liegen. Die 3D-Geometrie des Scanners kann
;;; weit vom Einfuegepunkt entfernt sein, daher zaehlt hier der ;;; weit vom Einfuegepunkt entfernt sein, daher zaehlt hier der
;;; Einfuegepunkt (dort klickt der Benutzer auf den Block). ;;; Einfuegepunkt (dort klickt der Benutzer auf den Block).
;;; - Separator: der geometrische Mittelpunkt (Zentrum der eigenen ;;; 2. Separator: der geometrische Mittelpunkt (Zentrum der eigenen
;;; Bounding-Box, unabhaengig vom $INSBASE) muss in der ;;; Bounding-Box, unabhaengig vom $INSBASE) muss in der
;;; Bounding-Box des ILS-Blocks liegen. ;;; Bounding-Box des Carriers liegen.
;;; - Liegt der Punkt in mehreren Boxen (Ueberlappung), wird ;;; 3. Liegt der Punkt in mehreren Boxen (Ueberlappung), wird der
;;; der ILS-Block mit dem naechstgelegenen Box-Zentrum gewaehlt. ;;; Carrier mit dem naechstgelegenen Box-Zentrum gewaehlt.
;;; - Ein optionaler Toleranzrand (*senstol*) vergroessert die ;;; 4. Liegt der Punkt in KEINER Carrier-Box (Scanner koennen
;;; durchaus ausserhalb der Kreisel-BBox montiert sein), wird der
;;; raeumlich naechstgelegene Carrier ueber alle Kreisel/Strecken
;;; hinweg gewaehlt (cs-nearest-two). Ist der Abstand zum
;;; zweitnaechsten Carrier fast gleich gross (Differenz <=
;;; cs-strittig-diff-mm, siehe cfg/export.cfg [Sensorzuordnung]
;;; strittig_diff_mm), gilt die Zuordnung als "strittig": sie
;;; wird trotzdem geschrieben, aber zusaetzlich in
;;; *cs-scanner-ergaenzt* (Report-Liste) und
;;; *cs-scanner-warnung-by-handle* (Key = Entity-Handle, Wert =
;;; Warnungstext) vermerkt. csv:block-to-json (export.lsp) liest
;;; *cs-scanner-warnung-by-handle* aus und haengt den Text als
;;; "warnung"-Feld an den JSON-Block des betroffenen Scanners an
;;; - export_csv.py gibt ihn in der CSV-Spalte "Warnungen" aus.
;;; *cs-scanner-ergaenzt* sammelt ALLE per Abstand (nicht per
;;; BBox) zugeordneten Scanner, nicht nur die strittigen -
;;; cs-zuordnung-lauf gibt diese Liste auf der Konsole aus.
;;; 5. Ein optionaler Toleranzrand (cs-senstol, siehe
;;; cfg/export.cfg [Sensorzuordnung] senstol_mm) vergroessert die
;;; 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
;;; "ID") in das Sensor-Attribut ZUORDNUNG geschrieben.
;;; 7. Sensoren, zu denen ueberhaupt kein Carrier existiert (leere
;;; Zeichnung), werden mit einem Kreis auf dem Layer *mark-layer*
;;; markiert (zentriert auf den Sensor).
;;; ;;;
;;; Zusaetzlich: ;;; cs-zuordnung-lauf ist die wiederverwendbare Engine (siehe unten):
;;; - Bei zugeordneten Sensoren wird die Bezeichnung des ILS-Blocks ;;; sie wird sowohl vom interaktiven Befehl ZAEHLE_SEP_SCAN als auch
;;; (Attribut "Bezeichnung", Fallback Blockname) in das Sensor- ;;; automatisch vor jedem CSV-/Sivas-Export aufgerufen (Lisp/export.lsp,
;;; Attribut *zuordnung-tag* geschrieben. ;;; csv:run-export) - dort OHNE QSAVE und ohne die ausfuehrliche
;;; - Nicht zugeordnete Sensoren werden mit einem Kreis auf dem ;;; Detailzeile je Carrier (verbose=nil), damit die Konsolenausgabe
;;; Layer *mark-layer* markiert (zentriert auf den Sensor). ;;; beim normalen Export kompakt bleibt.
;;; - Der Befehl ist wiederholbar: alte Markierungskreise werden
;;; zu Beginn entfernt.
;;; ;;;
;;; Aufruf: ZAEHLE_SEP_SCAN ;;; Aufruf (interaktiv): ZAEHLE_SEP_SCAN
;;; Laden: per APPLOAD oder (load "count_sep_scan") ;;; Laden: per APPLOAD oder (ssg-ensure "count_sep_scan")
;;; ============================================================ ;;; ============================================================
(vl-load-com) (vl-load-com)
;;; ------------------------------------------------------------ ;;; ------------------------------------------------------------
;;; KONFIGURATION (bei Bedarf anpassen) ;;; KONFIGURATION
;;; ------------------------------------------------------------ ;;; ------------------------------------------------------------
;; Toleranzrand um die ILS-Block-Box in Zeichnungseinheiten (mm). ;; Toleranzrand um die Carrier-Box in Zeichnungseinheiten (mm), aus
;; 0.0 = exakte Box. Groesserer Wert = Sensoren knapp ausserhalb ;; cfg/export.cfg [Sensorzuordnung] senstol_mm (Default 0.0 = exakte
;; werden noch dem ILS-Block zugerechnet. ;; Box). Wird von cs-zuordnung-lauf bei jedem Lauf frisch gelesen und
;; hier nur mit einem Startwert vorbelegt, damit cs-in-box/cs-grid-add
;; auch ausserhalb eines Laufs referenzierbar bleiben.
(if (null *senstol*) (setq *senstol* 0.0)) (if (null *senstol*) (setq *senstol* 0.0))
;; Blockname-Muster (wcmatch, Grossschreibung) fuer die manuell ;; Attribut-Tag im Scanner-/Separator-Block, in das die ID des
;; platzierten Sensoren. ;; zugeordneten Carriers geschrieben wird.
(setq *scanner-pattern* "SCANNER,SCANNER_*") (setq *zuordnung-tag* "ZUORDNUNG")
(setq *separator-pattern* "SEPARATOR_SP,SEPARATOR_SP_*")
;; Blockname-Muster fuer die INTERN im ILS-Block (in der Kette) ;; Markierung von Sensoren ohne jeglichen Carrier: Kreis auf eigenem Layer.
;; gezeichneten Separatoren. Diese bilden die Basis fuer
;; ANZAHL_SEPARATOR und werden bei jedem Lauf frisch gezaehlt.
(setq *intern-separator-pattern* "STAUSTRECKE_SEPARATOR_SP*")
;; Attribut-Tag im Scanner-/Separator-Block, in das die Bezeichnung
;; des zugeordneten ILS-Blocks geschrieben wird (Gross/Klein egal).
(setq *zuordnung-tag* "zuordnung")
;; Markierung nicht zugeordneter Sensoren: 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
;; Kreisradius = Faktor * halbe XY-Diagonale der Sensor-Box, ;; Kreisradius = Faktor * halbe XY-Diagonale der Sensor-Box,
@@ -65,19 +92,32 @@
(if (null *mark-radius-factor*) (setq *mark-radius-factor* 1.5)) (if (null *mark-radius-factor*) (setq *mark-radius-factor* 1.5))
(if (null *mark-radius-min*) (setq *mark-radius-min* 500.0)) (if (null *mark-radius-min*) (setq *mark-radius-min* 500.0))
;; --- Toleranzrand (mm) aus cfg/export.cfg [Sensorzuordnung] senstol_mm ---
(defun cs-senstol ()
(atof (export:cfg "Sensorzuordnung" "senstol_mm" "0"))
)
;; --- "Strittig"-Schwelle (mm) aus cfg/export.cfg [Sensorzuordnung] ---
;; Differenz zwischen dem Abstand zum naechsten und zweitnaechsten
;; Carrier, unterhalb derer eine Distanz-Zuordnung als unsicher gilt.
(defun cs-strittig-diff-mm ()
(atof (export:cfg "Sensorzuordnung" "strittig_diff_mm" "500"))
)
;;; ------------------------------------------------------------ ;;; ------------------------------------------------------------
;;; ILS-Block-Erkennung und zugehoerige Attribut-Tags ;;; Carrier-/Sensor-Erkennung ueber die gemeinsamen Muster aus
;;; Alle ILS-Bloecke (Kreisel / VF / GF) verwenden einheitlich ;;; cfg/export.cfg [blockpattern] (export:pattern, siehe export.lsp) -
;;; ANZAHL_SCANNER und ANZAHL_SEPARATOR. ;;; dieselben Muster wie csv:collect-export-blocks/export_csv.py.
;;; Rueckgabe: (scanner-tag separator-tag) oder nil
;;; ------------------------------------------------------------ ;;; ------------------------------------------------------------
(defun cs-carrier-tags (nm / up)
;; --- Ist nm ein Kreisel/Eckrad-, VarioFoerderer- oder Gefaellestrecke-
;; Compound-Block? Alle drei fuehren einheitlich die Attribute
;; ANZAHL_SCANNER/ANZAHL_SEPARATOR. ---
(defun cs-carrier-p (nm / up)
(setq up (strcase nm)) (setq up (strcase nm))
(if (or (wcmatch up "KREISEL_*") (or (wcmatch up (export:pattern "pattern_kreisel" "KR_*,KREISEL_*,ECKRAD_*"))
(wcmatch up "VF_*") (wcmatch up (export:pattern "pattern_variofoerderer" "VF_*"))
(wcmatch up "GF_*")) (wcmatch up (export:pattern "pattern_gefaellestrecke" "GF_*"))
(list "ANZAHL_SCANNER" "ANZAHL_SEPARATOR")
nil
) )
) )
@@ -88,22 +128,8 @@
(defun cs-sensor-kind (nm / up) (defun cs-sensor-kind (nm / up)
(setq up (strcase nm)) (setq up (strcase nm))
(cond (cond
((wcmatch up *scanner-pattern*) 'scanner) ((wcmatch up (strcase (export:pattern "pattern_scanner" "Scanner*"))) 'scanner)
((wcmatch up *separator-pattern*) 'separator) ((wcmatch up (strcase (export:pattern "pattern_separator" "Separator_SP*,S-LP*"))) 'separator)
(t nil)
)
)
;;; ------------------------------------------------------------
;;; Punkt-Rueckgabe von vla-getboundingbox in eine Liste wandeln.
;;; BricsCAD liefert ein Safearray, AutoCAD ein Variant mit
;;; Safearray - beide Faelle werden hier abgedeckt.
;;; ------------------------------------------------------------
(defun cs-pt->list (v)
(cond
((= (type v) 'variant) (vlax-safearray->list (vlax-variant-value v)))
((= (type v) 'safearray) (vlax-safearray->list v))
((listp v) v)
(t nil) (t nil)
) )
) )
@@ -116,6 +142,15 @@
;;; Rueckgabe: (minx miny maxx maxy) oder nil bei Fehler. ;;; Rueckgabe: (minx miny maxx maxy) oder nil bei Fehler.
;;; *cs-last-error* enthaelt bei Fehler den echten COM-Fehlertext. ;;; *cs-last-error* enthaelt bei Fehler den echten COM-Fehlertext.
;;; ------------------------------------------------------------ ;;; ------------------------------------------------------------
(defun cs-pt->list (v)
(cond
((= (type v) 'variant) (vlax-safearray->list (vlax-variant-value v)))
((= (type v) 'safearray) (vlax-safearray->list v))
((listp v) v)
(t nil)
)
)
(defun cs-bbox (ent / obj res) (defun cs-bbox (ent / obj res)
(setq *cs-last-error* nil) (setq *cs-last-error* nil)
(setq obj (vlax-ename->vla-object ent)) (setq obj (vlax-ename->vla-object ent))
@@ -157,8 +192,8 @@
) )
;;; ------------------------------------------------------------ ;;; ------------------------------------------------------------
;;; Liegt Punkt pt (X/Y) in ILS-Block-Record rec (mit Toleranz)? ;;; Liegt Punkt pt (X/Y) in Carrier-Record rec (mit Toleranz)?
;;; rec = (ename tagScan tagSep minx miny maxx maxy cx cy) ;;; rec = (ename tagScan tagSep minx miny maxx maxy cx cy id)
;;; ------------------------------------------------------------ ;;; ------------------------------------------------------------
(defun cs-in-box (pt rec / x y) (defun cs-in-box (pt rec / x y)
(setq x (car pt) y (cadr pt)) (setq x (car pt) y (cadr pt))
@@ -246,13 +281,13 @@
) )
;;; ------------------------------------------------------------ ;;; ------------------------------------------------------------
;;; Sensor-Punkt einem ILS-Block zuordnen. ;;; Sensor-Punkt einem Carrier per BBox zuordnen (Schritt 1-3 oben).
;;; Rueckgabe: ILS-Block-Record oder nil (keine Zuordnung). ;;; Rueckgabe: Carrier-Record oder nil (kein BBox-Treffer).
;;; Bei mehreren Treffern: naechstes Box-Zentrum (X/Y). ;;; Bei mehreren Treffern: naechstes Box-Zentrum (X/Y).
;;; grid = Vorsortierung aus cs-grid-build (statt aller Carrier ;;; grid = Vorsortierung aus cs-grid-build (statt aller Carrier
;;; wird nur die Kandidatenliste der eigenen Zelle geprueft). ;;; wird nur die Kandidatenliste der eigenen Zelle geprueft).
;;; ------------------------------------------------------------ ;;; ------------------------------------------------------------
(defun cs-assign (pt grid / matches best bestd d) (defun cs-assign (pt grid / matches best bestd d rec)
(setq matches nil) (setq matches nil)
(foreach rec (cs-grid-candidates grid pt) (foreach rec (cs-grid-candidates grid pt)
(if (cs-in-box pt rec) (setq matches (cons rec matches)))) (if (cs-in-box pt rec) (setq matches (cons rec matches))))
@@ -270,6 +305,61 @@
) )
) )
;;; ------------------------------------------------------------
;;; Die beiden dem Punkt pt (X/Y) naechstgelegenen Carrier, sortiert
;;; nach Abstand zum Box-Mittelpunkt (gleicher Massstab wie der
;;; Ueberlappungs-Tiebreak in cs-assign). carriers darf nicht leer
;;; sein - siehe cs-resolve-sensor.
;;; Rueckgabe: (best-rec best-dist second-rec-oder-nil second-dist-oder-nil)
;;; ------------------------------------------------------------
(defun cs-nearest-two (pt carriers / paare sortiert rec)
(setq paare nil)
(foreach rec carriers
(setq paare (cons (cons (distance (list (car pt) (cadr pt))
(list (nth 7 rec) (nth 8 rec)))
rec)
paare))
)
(setq sortiert (vl-sort paare (function (lambda (a b) (< (car a) (car b))))))
(list
(cdr (nth 0 sortiert))
(car (nth 0 sortiert))
(if (cdr sortiert) (cdr (nth 1 sortiert)) nil)
(if (cdr sortiert) (car (nth 1 sortiert)) nil)
)
)
;;; --- Carrier-Record mit gegebener ID aus carriers heraussuchen ---
(defun cs-find-carrier-by-id (carriers id / found rec)
(setq found nil)
(foreach rec carriers
(if (and (not found) (= (nth 9 rec) id)) (setq found rec))
)
found
)
;;; ------------------------------------------------------------
;;; Ordnet einen Sensor-Punkt (Scanner oder Separator) einem Carrier
;;; zu (Reihenfolge siehe Datei-Kopfkommentar Schritte 0/1-3/4).
;;; Rueckgabe: (modus rec best-d second-rec second-d)
;;; modus = 'manual | 'bbox | 'distance | 'none (keine Carrier vorhanden)
;;; best-d/second-rec/second-d sind nur bei modus='distance gesetzt.
;;; ------------------------------------------------------------
(defun cs-resolve-sensor (sent spt carriers grid / existing rec nearest)
(setq existing (cs-get-att-str sent *zuordnung-tag*))
(cond
((and existing (/= existing ""))
(list 'manual (cs-find-carrier-by-id carriers existing) nil nil nil))
((setq rec (cs-assign spt grid))
(list 'bbox rec nil nil nil))
((null carriers)
(list 'none nil nil nil nil))
(t
(setq nearest (cs-nearest-two spt carriers))
(list 'distance (nth 0 nearest) (nth 1 nearest) (nth 2 nearest) (nth 3 nearest)))
)
)
;;; ------------------------------------------------------------ ;;; ------------------------------------------------------------
;;; Zaehler in einer Assoc-Liste (Schluessel = ename) erhoehen. ;;; Zaehler in einer Assoc-Liste (Schluessel = ename) erhoehen.
;;; ------------------------------------------------------------ ;;; ------------------------------------------------------------
@@ -309,35 +399,6 @@
ok ok
) )
;;; ------------------------------------------------------------
;;; Zaehlt rekursiv, wie oft ein Block (Muster pat) in der
;;; Definition des Blocks bname verwendet wird - inklusive
;;; verschachtelter Unterbloecke. Damit wird die interne Anzahl
;;; der Kette-Separatoren aus der Geometrie ermittelt.
;;; ------------------------------------------------------------
(defun cs-count-nested (bname pat / e ed nm n stop)
(setq n 0 stop nil)
(setq e (tblobjname "BLOCK" bname))
(if e
(progn
(setq e (entnext e))
(while (and e (not stop))
(setq ed (entget e))
(cond
((= (cdr (assoc 0 ed)) "ENDBLK") (setq stop t))
((= (cdr (assoc 0 ed)) "INSERT")
(setq nm (cdr (assoc 2 ed)))
(if (wcmatch (strcase nm) pat)
(setq n (1+ n))
(setq n (+ n (cs-count-nested nm pat)))))
)
(if (not stop) (setq e (entnext e)))
)
)
)
n
)
;;; ------------------------------------------------------------ ;;; ------------------------------------------------------------
;;; Attributwert (String) an einem INSERT lesen. "" wenn nicht ;;; Attributwert (String) an einem INSERT lesen. "" wenn nicht
;;; vorhanden. Tag ohne Gross/Klein-Unterscheidung. ;;; vorhanden. Tag ohne Gross/Klein-Unterscheidung.
@@ -363,8 +424,9 @@
) )
;;; ------------------------------------------------------------ ;;; ------------------------------------------------------------
;;; Bezeichnung eines ILS-Blocks ermitteln: Attribut "Bezeichnung", ;;; Bezeichnung eines Carriers ermitteln: Attribut "Bezeichnung",
;;; Fallback auf den Blocknamen. ;;; Fallback auf den Blocknamen. Nur fuer Konsolen-/Fehlermeldungen -
;;; die eigentliche Verknuepfung (ZUORDNUNG) laeuft ueber die ID.
;;; ------------------------------------------------------------ ;;; ------------------------------------------------------------
(defun cs-carrier-label (ent nm / bez) (defun cs-carrier-label (ent nm / bez)
(setq bez (cs-get-att-str ent "Bezeichnung")) (setq bez (cs-get-att-str ent "Bezeichnung"))
@@ -395,7 +457,7 @@
) )
;;; ------------------------------------------------------------ ;;; ------------------------------------------------------------
;;; Nicht zugeordneten Sensor mit einem Kreis markieren, zentriert ;;; Sensor ohne jeglichen Carrier mit einem Kreis markieren, zentriert
;;; auf die Sensor-Geometrie. Radius aus der XY-Box des Sensors ;;; auf die Sensor-Geometrie. Radius aus der XY-Box des Sensors
;;; (Faktor), mindestens *mark-radius-min*. ;;; (Faktor), mindestens *mark-radius-min*.
;;; ------------------------------------------------------------ ;;; ------------------------------------------------------------
@@ -421,64 +483,166 @@
(cons 40 r))) (cons 40 r)))
) )
;;; ============================================================ ;;; ------------------------------------------------------------
;;; HAUPTBEFEHL ;;; Schnelltest: Ist ein vollstaendiger cs-zuordnung-lauf ueberhaupt
;;; ============================================================ ;;; noetig? Der eigentliche Lauf ist teuer (vla-getboundingbox + Regen
(defun c:ZAEHLE_SEP_SCAN ( / ss i ent ed nm tags bb cx cy label ;;; je Carrier UND je Sensor). Diese Vorpruefung kommt ohne jede
carriers grid scanlist seplist kind pt ;;; Bounding-Box aus - sie liest nur Attribute (entget).
scanCounts sepCounts ;;;
rec en nm2 ns nsep internSep newS newP ;;; Rueckgabe T (Lauf noetig), wenn EINE der Bedingungen zutrifft:
s sent spt ;;; a) Ein Scanner-/Separator-Symbol hat ein leeres ZUORDNUNG-Attribut
n-scan-unassigned n-sep-unassigned ;;; (noch nicht zugeordnet) oder ein ZUORDNUNG, das auf keinen
n-zuordnung-fehlt mss ;;; (mehr) existierenden Carrier zeigt.
okS okP) ;;; b) An einem Carrier passt ANZAHL_SCANNER nicht zur Anzahl der ihm
(setq carriers nil scanlist nil seplist nil n-zuordnung-fehlt 0) ;;; per ZUORDNUNG zugeschriebenen Scanner-Symbole.
;;; c) An einem Carrier passt ANZAHL_SEPARATOR nicht zur Anzahl der ihm
;; Alte Markierungskreise entfernen (Idempotenz) ;;; per ZUORDNUNG zugeschriebenen Separator-Symbole (nur platzierte
(setq mss (ssget "_X" (list (cons 0 "CIRCLE") (cons 8 *mark-layer*)))) ;;; Symbole zaehlen - dieselbe Regel wie newP im eigentlichen Lauf;
(if mss (command "_.ERASE" mss "")) ;;; der interne Staustrecke_Separator_SP zaehlt nicht mit).
;;; Sonst nil - die gespeicherten Attribute sind konsistent, der teure
;; Vor der Extents-Berechnung einmal regenerieren, damit ;;; Lauf wird uebersprungen.
;; vla-getboundingbox verlaessliche Werte liefert. ;;;
(vl-catch-all-apply '(lambda () (command "_.REGEN"))) ;;; Die Zuordnung Symbol->Carrier wird hier billig ueber das bereits
;;; gesetzte ZUORDNUNG-Attribut rekonstruiert (Carrier-ID), OHNE die
;; --- 1. Alle INSERTs sammeln und klassifizieren --- ;;; teure Boundingbox-/Distanz-Rechnung des eigentlichen Laufs. Dadurch
;;; erkennt der Test - anders als eine reine Gesamtsummen-Pruefung - auch
;;; das Verschieben eines Sensors von einem Carrier zu einem anderen
;;; (die Per-Carrier-Zahl kippt, ZUORDNUNG zeigt aber noch auf den alten).
;;; ------------------------------------------------------------
(defun cs-zuordnung-noetig-p ( / ss i ent ed nm sk zuord id
carriers carrier-ids sensors s
sepByCarrier scanByCarrier
noetig expScan expSep rec)
(setq noetig nil carriers nil carrier-ids nil sensors nil
sepByCarrier nil scanByCarrier nil)
(setq ss (ssget "_X" '((0 . "INSERT")))) (setq ss (ssget "_X" '((0 . "INSERT"))))
(if (null ss) (if (null ss)
(progn (princ "\n>>> Keine Bloecke in der Zeichnung gefunden.") (princ)) nil ;; keine Bloecke -> nichts zu tun
(progn
;; Pass 1: Carrier (ename . id) und Sensor-Symbole (kind . ZUORDNUNG)
;; sammeln - die ssget-Reihenfolge ist beliebig, daher erst sammeln,
;; dann auswerten.
(setq i 0)
(while (< i (sslength ss))
(setq ent (ssname ss i) ed (entget ent) nm (cdr (assoc 2 ed)))
(cond
((cs-carrier-p nm)
(setq id (cs-get-att-str ent "ID"))
(setq carriers (cons (cons ent id) carriers))
(setq carrier-ids (cons id carrier-ids)))
(t
(setq sk (cs-sensor-kind nm))
(if sk
(setq sensors
(cons (cons sk (cs-get-att-str ent *zuordnung-tag*)) sensors))))
)
(setq i (1+ i))
)
;; Pass 2: Symbole ueber ZUORDNUNG je Carrier-ID zaehlen. Leeres
;; ZUORDNUNG oder ZUORDNUNG auf einen nicht (mehr) existierenden
;; Carrier -> Lauf noetig.
(foreach s sensors
(setq sk (car s) zuord (cdr s))
(cond
((or (null zuord) (= zuord "")) (setq noetig T))
((not (member zuord carrier-ids)) (setq noetig T))
((eq sk 'scanner) (setq scanByCarrier (cs-bump scanByCarrier zuord)))
(t (setq sepByCarrier (cs-bump sepByCarrier zuord)))
)
)
;; Pass 3: je Carrier Soll (= zugeschriebene Symbole) gegen die
;; gespeicherten Attribute pruefen.
(if (not noetig)
(foreach rec carriers
(setq id (cdr rec)) ;; rec = (ename . carrier-id)
(setq expScan (cond ((assoc id scanByCarrier) (cdr (assoc id scanByCarrier))) (t 0)))
(setq expSep (cond ((assoc id sepByCarrier) (cdr (assoc id sepByCarrier))) (t 0)))
(if (or (/= (atoi (cs-get-att-str (car rec) "ANZAHL_SCANNER")) expScan)
(/= (atoi (cs-get-att-str (car rec) "ANZAHL_SEPARATOR")) expSep))
(setq noetig T))
)
)
noetig
)
)
)
;;; ============================================================
;;; ENGINE - wird sowohl von ZAEHLE_SEP_SCAN als auch automatisch
;;; vor jedem CSV-/Sivas-Export aufgerufen (siehe export.lsp,
;;; csv:run-export).
;;;
;;; label = Anzeigepraefix fuer die Konsolenausgabe (z.B. "EXPORTCSV").
;;; verbose = T -> zusaetzlich je Carrier eine Detailzeile ausgeben
;;; (nur fuer den interaktiven Befehl sinnvoll - der
;;; automatische Aufruf aus csv:run-export laesst dies aus,
;;; damit die Export-Konsole kompakt bleibt).
;;;
;;; Setzt als Nebenwirkung:
;;; *cs-scanner-ergaenzt* - Liste (scanner-id carrier-id
;;; distanz-mm strittig-p
;;; zweiter-carrier-id zweite-distanz-mm)
;;; je per Abstand (nicht per BBox)
;;; zugeordnetem Scanner.
;;; *cs-scanner-warnung-by-handle* - Assoc-Liste (handle . warnungstext)
;;; fuer strittige Scanner-Zuordnungen,
;;; von csv:block-to-json ausgelesen.
;;; ============================================================
(defun cs-zuordnung-lauf (label verbose /
mss ss i ent ed nm bb cx cy lbl carrier-id
carriers grid scanlist seplist sk pt
scanCounts sepCounts n-scan-unassigned n-sep-unassigned
n-strittig n-zuordnung-fehlt
s sent spt res mode rec best-d second-rec second-d
scanner-id contested handle
en nm2 ns nsep newS newP okS okP eintrag)
(setq carriers nil scanlist nil seplist nil
n-zuordnung-fehlt 0 n-scan-unassigned 0 n-sep-unassigned 0 n-strittig 0)
(setq *cs-scanner-ergaenzt* nil)
(setq *cs-scanner-warnung-by-handle* nil)
(setq *senstol* (cs-senstol))
;; Alte Markierungskreise entfernen (Idempotenz - Sensoren, die in
;; einem frueheren Lauf unzuordenbar waren, koennen inzwischen einen
;; Carrier bekommen haben oder umgekehrt).
(setq mss (ssget "_X" (list (cons 0 "CIRCLE") (cons 8 *mark-layer*))))
(if mss (command "_.ERASE" mss ""))
(vl-catch-all-apply '(lambda () (command "_.REGEN")))
(princ (ssg-textf "sens-start" (list label)))
(setq ss (ssget "_X" '((0 . "INSERT"))))
(if (null ss)
(princ (ssg-textf "sens-no-inserts" (list label)))
(progn (progn
(setq i 0) (setq i 0)
(while (< i (sslength ss)) (while (< i (sslength ss))
(setq ent (ssname ss i)) (setq ent (ssname ss i))
(setq ed (entget ent)) (setq ed (entget ent))
(setq nm (cdr (assoc 2 ed))) (setq nm (cdr (assoc 2 ed)))
(setq tags (cs-carrier-tags nm))
(cond (cond
;; --- ILS-Block --- ((cs-carrier-p nm)
(tags
(setq bb (cs-bbox ent)) (setq bb (cs-bbox ent))
(if bb (if bb
(progn (progn
(setq cx (/ (+ (nth 0 bb) (nth 2 bb)) 2.0)) (setq cx (/ (+ (nth 0 bb) (nth 2 bb)) 2.0))
(setq cy (/ (+ (nth 1 bb) (nth 3 bb)) 2.0)) (setq cy (/ (+ (nth 1 bb) (nth 3 bb)) 2.0))
(setq label (cs-carrier-label ent nm)) (setq lbl (cs-carrier-label ent nm))
(setq carrier-id (cs-get-att-str ent "ID"))
(setq carriers (setq carriers
(cons (list ent (car tags) (cadr tags) (cons (list ent "ANZAHL_SCANNER" "ANZAHL_SEPARATOR"
(nth 0 bb) (nth 1 bb) (nth 2 bb) (nth 3 bb) (nth 0 bb) (nth 1 bb) (nth 2 bb) (nth 3 bb)
cx cy label) cx cy carrier-id lbl)
carriers))) carriers)))
(princ (strcat "\n! Bounding-Box fehlgeschlagen fuer ILS-Block '" nm "'" (princ (ssg-textf "sens-bbox-fehler" (list nm)))
(if *cs-last-error*
(strcat " (" *cs-last-error* ")") "")))
) )
) )
;; --- Sensor (Entity + Referenzpunkt merken) ---
(t (t
(setq kind (cs-sensor-kind nm)) (setq sk (cs-sensor-kind nm))
(if kind (if sk
(progn (progn
(setq pt (cs-sensor-point ent ed kind)) (setq pt (cs-sensor-point ent ed sk))
(if (eq kind 'scanner) (if (eq sk 'scanner)
(setq scanlist (cons (list ent pt) scanlist)) (setq scanlist (cons (list ent pt) scanlist))
(setq seplist (cons (list ent pt) seplist))))) (setq seplist (cons (list ent pt) seplist)))))
) )
@@ -486,99 +650,145 @@
(setq i (1+ i)) (setq i (1+ i))
) )
;; --- 2. Sensoren zuordnen: zuordnung schreiben bzw. markieren ---
;; Vorsortierung: Carrier einmalig in ein raeumliches Grid einsortieren,
;; damit cs-assign je Sensor nur die Kandidaten der eigenen Zelle statt
;; aller Carrier prueft (siehe cs-grid-build).
(setq grid (cs-grid-build carriers)) (setq grid (cs-grid-build carriers))
(setq scanCounts nil sepCounts nil (setq scanCounts nil sepCounts nil)
n-scan-unassigned 0 n-sep-unassigned 0)
;; --- Scanner zuordnen (inkl. Distanz-Fallback + Strittig-Erkennung) ---
(foreach s scanlist (foreach s scanlist
(setq sent (car s) spt (cadr s)) (setq sent (car s) spt (cadr s))
(setq rec (cs-assign spt grid)) (setq res (cs-resolve-sensor sent spt carriers grid))
(if rec (setq mode (nth 0 res) rec (nth 1 res)
(progn best-d (nth 2 res) second-rec (nth 3 res) second-d (nth 4 res))
(cond
((eq mode 'none)
(setq n-scan-unassigned (1+ n-scan-unassigned))
(cs-mark-unassigned sent))
((null rec) nil) ;; manuell zugeordnet, ID unbekannt -> unangetastet
(t
(setq scanCounts (cs-bump scanCounts (car rec))) (setq scanCounts (cs-bump scanCounts (car rec)))
(if (member mode '(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))))
(if (eq mode 'distance)
(progn (progn
(setq n-scan-unassigned (1+ n-scan-unassigned)) (setq scanner-id (cs-get-att-str sent "ID"))
(cs-set-att sent *zuordnung-tag* "") (setq contested (and second-rec
(cs-mark-unassigned sent)))) (<= (- second-d best-d) (cs-strittig-diff-mm))))
(setq *cs-scanner-ergaenzt*
(cons (list scanner-id (nth 9 rec) (fix (+ best-d 0.5))
contested
(if contested (nth 9 second-rec) nil)
(if contested (fix (+ second-d 0.5)) nil))
*cs-scanner-ergaenzt*))
(if contested
(progn
(setq n-strittig (1+ n-strittig))
(setq handle (cdr (assoc 5 (entget sent))))
(setq *cs-scanner-warnung-by-handle*
(cons (cons handle
(strcat "Distanz-Zuordnung unsicher: "
(nth 9 rec) " (" (itoa (fix (+ best-d 0.5))) "mm) vs. "
(nth 9 second-rec) " (" (itoa (fix (+ second-d 0.5))) "mm)"))
*cs-scanner-warnung-by-handle*))
)
)
)
)
)
)
)
(setq *cs-scanner-ergaenzt* (reverse *cs-scanner-ergaenzt*))
;; --- Separatoren zuordnen (gleiche Logik, ohne Strittig-Report -
;; der Distanz-Fallback stellt hier nur sicher, dass die
;; 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 rec (cs-assign spt grid)) (setq res (cs-resolve-sensor sent spt carriers grid))
(if rec (setq mode (nth 0 res) rec (nth 1 res))
(progn (cond
((eq mode 'none)
(setq n-sep-unassigned (1+ n-sep-unassigned))
(cs-mark-unassigned sent))
((null rec) nil)
(t
(setq sepCounts (cs-bump sepCounts (car rec))) (setq sepCounts (cs-bump sepCounts (car rec)))
(if (member mode '(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))))
(progn )
(setq n-sep-unassigned (1+ n-sep-unassigned)) )
(cs-set-att sent *zuordnung-tag* "") )
(cs-mark-unassigned sent))))
;; --- 3. ANZAHL-Attribute setzen und Bericht ausgeben --- ;; --- ANZAHL_SCANNER/ANZAHL_SEPARATOR je Carrier schreiben ---
(princ "\n============================================")
(princ "\n Sensor-Zaehlung je ILS-Block")
(princ "\n============================================")
(if (null carriers)
(princ "\n (Keine ILS-Bloecke KREISEL_/VF_/GF_ gefunden)")
(foreach rec carriers (foreach rec carriers
(setq en (car rec)) (setq en (car rec))
(setq nm2 (cdr (assoc 2 (entget en)))) (setq nm2 (cdr (assoc 2 (entget en))))
(setq ns (cond ((assoc en scanCounts) (cdr (assoc en scanCounts))) (t 0))) (setq ns (cond ((assoc en scanCounts) (cdr (assoc en scanCounts))) (t 0)))
(setq nsep (cond ((assoc en sepCounts) (cdr (assoc en sepCounts))) (t 0))) (setq nsep (cond ((assoc en sepCounts) (cdr (assoc en sepCounts))) (t 0)))
;; interne Kette-Separatoren frisch aus der Blockdefinition zaehlen
(setq internSep (cs-count-nested nm2 *intern-separator-pattern*))
;; Scanner = nur neu zugeordnete
;; Separator = interne Kette-Separatoren + neu zugeordnete
(setq newS ns) (setq newS ns)
(setq newP (+ internSep nsep)) ;; ANZAHL_SEPARATOR = nur die tatsaechlich platzierten Separator-
;; gefundene Anzahlen melden (mit Bezeichnung/Label) ;; Symbole (Separator_SP/S-LP), die diesem Carrier zugeordnet sind.
(princ (strcat "\n " nm2 " [" (nth 9 rec) "]" ;; Der intern in der GF/VF-Kette gezeichnete 300-mm-Block
": Scanner(neu)=" (itoa ns) ;; Staustrecke_Separator_SP ist nur ein Kettenglied (Foerder-/
", Separator intern=" (itoa internSep) ;; Uebergabestueck), KEIN eigenstaendiger Separator - er wird bewusst
" + manuell=" (itoa nsep))) ;; NICHT mitgezaehlt (Entscheidung: nur platzierte Symbole zaehlen).
;; Werte schreiben und Ergebnis bestaetigen (setq newP nsep)
(setq okS (cs-set-att en (nth 1 rec) (itoa newS))) (setq okS (cs-set-att en (nth 1 rec) (itoa newS)))
(setq okP (cs-set-att en (nth 2 rec) (itoa newP))) (setq okP (cs-set-att en (nth 2 rec) (itoa newP)))
(if (and okS okP) (if verbose
(princ (strcat "\n -> geschrieben: " (princ (ssg-textf "sens-carrier-zeile"
"ANZAHL_SCANNER=" (itoa newS) (list nm2 (nth 9 rec) (itoa newS) (itoa newP)))))
", ANZAHL_SEPARATOR=" (itoa newP))) (if (not (and okS okP))
(princ (strcat "\n -> FEHLER beim Schreiben: " (princ (ssg-textf "sens-carrier-fehler" (list nm2))))
(if okS "" "ANZAHL_SCANNER fehlt ")
(if okP "" "ANZAHL_SEPARATOR fehlt"))))
) )
)
(princ (strcat "\n--------------------------------------------"
"\n ILS-Bloecke gesamt: " (itoa (length carriers))
"\n Scanner gesamt: " (itoa (length scanlist))
" (nicht zugeordnet: " (itoa n-scan-unassigned) ")"
"\n Separator gesamt: " (itoa (length seplist))
" (nicht zugeordnet: " (itoa n-sep-unassigned) ")"))
(if (> (+ n-scan-unassigned n-sep-unassigned) 0)
(princ (strcat "\n " (itoa (+ n-scan-unassigned n-sep-unassigned))
" nicht zugeordnete(r) Sensor(en) mit Kreis markiert"
" (Layer '" *mark-layer* "').")))
(if (> n-zuordnung-fehlt 0)
(princ (strcat "\n WARNUNG: " (itoa n-zuordnung-fehlt)
" zugeordnete(r) Sensor(en) ohne Attribut '"
*zuordnung-tag* "' - Bezeichnung nicht schreibbar.")))
;; --- 4. Zeichnung speichern --- ;; --- Zusammenfassung ---
(princ (ssg-textf "sens-summary"
(list label (itoa (length carriers))
(itoa (length scanlist)) (itoa n-scan-unassigned)
(itoa (length seplist)) (itoa n-sep-unassigned))))
(if *cs-scanner-ergaenzt*
(progn
(princ (ssg-textf "sens-ergaenzt-header"
(list label (itoa (length *cs-scanner-ergaenzt*)))))
(foreach eintrag *cs-scanner-ergaenzt*
(princ (ssg-textf "sens-ergaenzt-zeile"
(list (nth 0 eintrag) (nth 1 eintrag) (itoa (nth 2 eintrag)))))
(if (nth 3 eintrag)
(princ (ssg-textf "sens-strittig-suffix"
(list (nth 4 eintrag) (itoa (nth 5 eintrag))))))
)
)
)
(if (> n-strittig 0)
(princ (ssg-textf "sens-strittig-summary" (list label (itoa n-strittig)))))
(if (> (+ n-scan-unassigned n-sep-unassigned) 0)
(princ (ssg-textf "sens-unassigned-marked"
(list label (itoa (+ n-scan-unassigned n-sep-unassigned)) *mark-layer*))))
(if (> n-zuordnung-fehlt 0)
(princ (ssg-textf "sens-zuordnung-fehlt" (list label (itoa n-zuordnung-fehlt)))))
)
)
(princ)
)
;;; ============================================================
;;; BEFEHL: ZAEHLE_SEP_SCAN - interaktiver Aufruf mit vollem Bericht
;;; (verbose) und abschliessendem QSAVE. Der automatische Aufruf vor
;;; jedem CSV-/Sivas-Export (csv:run-export in export.lsp) nutzt
;;; dieselbe Engine (cs-zuordnung-lauf) direkt, ohne QSAVE.
;;; ============================================================
(defun c:ZAEHLE_SEP_SCAN ()
(cs-zuordnung-lauf "ZAEHLE_SEP_SCAN" T)
(princ "\n--------------------------------------------") (princ "\n--------------------------------------------")
(princ "\n Speichere Zeichnung (QSAVE) ...") (princ "\n Speichere Zeichnung (QSAVE) ...")
(command "_.QSAVE") (command "_.QSAVE")
(princ "\n>>> Fertig.") (princ "\n>>> Fertig.")
(princ) (princ)
) )
)
(princ)
)
(princ "\n>>> count_sep_scan.lsp geladen. Befehl: ZAEHLE_SEP_SCAN") (princ "\n>>> count_sep_scan.lsp geladen. Befehl: ZAEHLE_SEP_SCAN")
(princ) (princ)
+36 -1
View File
@@ -463,7 +463,7 @@
;; 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) insert-quat insert-kos k-list warnung-json warnung-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)))
@@ -506,6 +506,17 @@
) )
) )
) )
;; Warnung fuer strittige Scanner-Zuordnungen anhaengen (siehe
;; cs-zuordnung-lauf in count_sep_scan.lsp, das *cs-scanner-warnung-
;; by-handle* vor csv:collect-export-blocks befuellt). Referenziert
;; die Variable direkt statt ueber atoms-family: ein in AutoLISP noch
;; nie gesetztes globales Symbol wertet beim Lesen zu nil aus (kein
;; Fehler), genau wie *export-test-override* in diesem Modul.
(setq warnung-json "")
(setq warnung-eintrag (assoc handle *cs-scanner-warnung-by-handle*))
(if warnung-eintrag
(setq warnung-json (strcat ",\"warnung\":\"" (csv:json-escape (cdr warnung-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) "\""
@@ -521,6 +532,7 @@
",\"k3\":\"" (csv:k-kos k-list "K3") "\"" ",\"k3\":\"" (csv:k-kos k-list "K3") "\""
",\"k4\":\"" (csv:k-kos k-list "K4") "\"" ",\"k4\":\"" (csv:k-kos k-list "K4") "\""
bbox-json bbox-json
warnung-json
"}" "}"
) )
) )
@@ -615,6 +627,29 @@
;; 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)
;; Vor dem Export: Separator-/Scanner-Zuordnung pruefen und
;; ANZAHL_SEPARATOR/ANZAHL_SCANNER an Kreisel/Streckengruppe/
;; Gefaellestrecke aktualisieren (siehe count_sep_scan.lsp,
;; cs-zuordnung-lauf). Scanner ausserhalb jeder Carrier-Boundingbox
;; werden dabei per Abstand zugeordnet (ZUORDNUNG-Attribut = Carrier-
;; ID) und - bei mehrdeutigem Abstand - als "strittig" markiert;
;; *cs-scanner-warnung-by-handle* wird von csv:block-to-json unten
;; ausgelesen und haengt den Warnungstext an den betroffenen Scanner-
;; Block im JSON an. verbose=nil haelt die Export-Konsole kompakt
;; (die ausfuehrliche Detailzeile je Carrier gibt es nur beim
;; interaktiven Befehl ZAEHLE_SEP_SCAN).
;;
;; Der Lauf ist teuer (Boundingbox+Regen je Carrier und Sensor),
;; darum vorher der billige Schnelltest cs-zuordnung-noetig-p (nur
;; Attribute lesen, keine Boundingboxen): nur bei leeren ZUORDNUNG-
;; Eintraegen oder abweichenden Summen wird tatsaechlich neu gerechnet.
(ssg-ensure "count_sep_scan")
(if (atoms-family 1 '("cs-zuordnung-lauf"))
(if (cs-zuordnung-noetig-p)
(cs-zuordnung-lauf label nil)
(princ (ssg-textf "sens-skip" (list label)))
)
)
(princ (ssg-textf "exp-collect-blocks" (list label))) (princ (ssg-textf "exp-collect-blocks" (list label)))
(setq ss (csv:collect-export-blocks)) (setq ss (csv:collect-export-blocks))
+21
View File
@@ -157,3 +157,24 @@ bb_minimum_height_mm=1
# Linie durch beide Stationsmitten) - genau diese Linie trennt links/rechts. # Linie durch beide Stationsmitten) - genau diese Linie trennt links/rechts.
durchmesser_mm=800 durchmesser_mm=800
[Sensorzuordnung]
# Zuordnung von manuell platzierten Separator_SP/Scanner-Bloecken zu einem
# Kreisel/Eckrad, einer VarioFoerderer-Streckengruppe (VF_*) oder einer
# Gefaellestrecke (GF_*) - siehe Lisp/count_sep_scan.lsp (cs-zuordnung-lauf).
# Laeuft automatisch vor jedem CSV-/Sivas-Export (Lisp/export.lsp,
# csv:run-export) und schreibt ANZAHL_SEPARATOR/ANZAHL_SCANNER am
# Kreisel/VF/GF sowie ZUORDNUNG (= ID des zugeordneten Elements) am Sensor.
# Toleranzrand (mm) um die Carrier-Boundingbox: Sensoren knapp ausserhalb
# gelten noch als "innerhalb". 0 = exakte Box.
senstol_mm=0
# Scanner (und Separatoren) ausserhalb JEDER Carrier-Boundingbox ohne
# vorhandenes ZUORDNUNG-Attribut werden per Abstand dem naechstgelegenen
# Kreisel/VF/GF zugeschlagen. Liegt der Abstand zum zweitnaechsten
# Kandidaten hoechstens strittig_diff_mm ueber dem des naechsten, gilt die
# Zuordnung als unsicher ("strittig"): sie wird trotzdem geschrieben, aber
# als Warnung im CSV-Export (Spalte "Warnungen") und in der BricsCAD-Konsole
# ausgegeben. Wert in mm.
strittig_diff_mm=500
+13
View File
@@ -512,6 +512,19 @@
"exp-csv-missing": "\n[%1] FEHLER: Python meldete Erfolg, aber die CSV fehlt: %2. Log: %3", "exp-csv-missing": "\n[%1] FEHLER: Python meldete Erfolg, aber die CSV fehlt: %2. Log: %3",
"exp-omni-no-inserts": "\n[OMNI_UPDATE] Keine INSERT-Bloecke in der Zeichnung.", "exp-omni-no-inserts": "\n[OMNI_UPDATE] Keine INSERT-Bloecke in der Zeichnung.",
"exp-omni-updated": "\n[OMNI_UPDATE] %1 Elemente aktualisiert (HOEHE, DREHUNG).", "exp-omni-updated": "\n[OMNI_UPDATE] %1 Elemente aktualisiert (HOEHE, DREHUNG).",
"sens-skip": "\n[%1] Separator-/Scanner-Zuordnung aktuell (ZUORDNUNG gesetzt, Summen stimmen) - Neuberechnung uebersprungen.",
"sens-start": "\n[%1] Pruefe Separator-/Scanner-Zuordnung (ANZAHL_SEPARATOR/ANZAHL_SCANNER)...",
"sens-no-inserts": "\n[%1] Keine Bloecke in der Zeichnung gefunden - Sensor-Zuordnung uebersprungen.",
"sens-bbox-fehler": "\n ! Bounding-Box fehlgeschlagen fuer ILS-Block '%1'",
"sens-carrier-zeile": "\n %1 [%2]: ANZAHL_SCANNER=%3, ANZAHL_SEPARATOR=%4 (nur platzierte Symbole)",
"sens-carrier-fehler": "\n FEHLER beim Schreiben der Anzahl-Attribute an %1",
"sens-summary": "\n[%1] %2 Kreisel/Streckengruppen/Gefaellestrecken aktualisiert, %3 Scanner gesamt (%4 nicht zuordenbar), %5 Separator gesamt (%6 nicht zuordenbar).",
"sens-ergaenzt-header": "\n[%1] %2 Scanner ausserhalb jeder Kreisel-/Strecken-Boundingbox per Abstand automatisch zugeordnet:",
"sens-ergaenzt-zeile": "\n Scanner %1 -> %2 (%3mm)",
"sens-strittig-suffix": " STRITTIG: auch nah an %1 (%2mm)",
"sens-strittig-summary": "\n[%1] WARNUNG: %2 der obigen Zuordnungen sind strittig (Abstand zu zwei Kandidaten fast gleich) - siehe Spalte 'Warnungen' im CSV-Export.",
"sens-unassigned-marked": "\n[%1] %2 Sensor(en) ohne jeglichen Kreisel/Strecke markiert (Layer '%3').",
"sens-zuordnung-fehlt": "\n[%1] WARNUNG: %2 zugeordnete(r) Sensor(en) ohne Attribut 'ZUORDNUNG' - Wert nicht schreibbar.",
"id-new-assigned": "\n[ID] Neue ID zugewiesen: %1", "id-new-assigned": "\n[ID] Neue ID zugewiesen: %1",
"id-invalid-block": "\n[ID] FEHLER: Kein gueltiger INSERT-Block!", "id-invalid-block": "\n[ID] FEHLER: Kein gueltiger INSERT-Block!",
"id-check-no-blocks": "\n[IDSCHECK] Keine relevanten Bloecke gefunden.", "id-check-no-blocks": "\n[IDSCHECK] Keine relevanten Bloecke gefunden.",
+13
View File
@@ -512,6 +512,19 @@
"exp-csv-missing": "\n[%1] ERROR: Python reported success but the CSV is missing: %2. Log: %3", "exp-csv-missing": "\n[%1] ERROR: Python reported success but the CSV is missing: %2. Log: %3",
"exp-omni-no-inserts": "\n[OMNI_UPDATE] No INSERT blocks in the drawing.", "exp-omni-no-inserts": "\n[OMNI_UPDATE] No INSERT blocks in the drawing.",
"exp-omni-updated": "\n[OMNI_UPDATE] %1 elements updated (HEIGHT, ROTATION).", "exp-omni-updated": "\n[OMNI_UPDATE] %1 elements updated (HEIGHT, ROTATION).",
"sens-skip": "\n[%1] Separator/scanner assignment up to date (ZUORDNUNG set, sums match) - recomputation skipped.",
"sens-start": "\n[%1] Checking separator/scanner assignment (ANZAHL_SEPARATOR/ANZAHL_SCANNER)...",
"sens-no-inserts": "\n[%1] No blocks found in the drawing - sensor assignment skipped.",
"sens-bbox-fehler": "\n ! Bounding box failed for ILS block '%1'",
"sens-carrier-zeile": "\n %1 [%2]: ANZAHL_SCANNER=%3, ANZAHL_SEPARATOR=%4 (placed symbols only)",
"sens-carrier-fehler": "\n ERROR writing the count attributes on %1",
"sens-summary": "\n[%1] %2 Kreisel/Streckengruppen/Gefaellestrecken updated, %3 scanners total (%4 unassignable), %5 separators total (%6 unassignable).",
"sens-ergaenzt-header": "\n[%1] %2 scanners outside every Kreisel/track bounding box auto-assigned by distance:",
"sens-ergaenzt-zeile": "\n Scanner %1 -> %2 (%3mm)",
"sens-strittig-suffix": " CONTESTED: also close to %1 (%2mm)",
"sens-strittig-summary": "\n[%1] WARNING: %2 of the above assignments are contested (distance to two candidates nearly equal) - see column 'Warnungen' in the CSV export.",
"sens-unassigned-marked": "\n[%1] %2 sensor(s) without any Kreisel/track marked (layer '%3').",
"sens-zuordnung-fehlt": "\n[%1] WARNING: %2 assigned sensor(s) without a 'ZUORDNUNG' attribute - value not writable.",
"id-new-assigned": "\n[ID] New ID assigned: %1", "id-new-assigned": "\n[ID] New ID assigned: %1",
"id-invalid-block": "\n[ID] ERROR: Not a valid INSERT block!", "id-invalid-block": "\n[ID] ERROR: Not a valid INSERT block!",
"id-check-no-blocks": "\n[IDSCHECK] No relevant blocks found.", "id-check-no-blocks": "\n[IDSCHECK] No relevant blocks found.",
+39
View File
@@ -27,6 +27,13 @@ Warnungen = Spalte fuer Auffaelligkeiten. Aktuell: eine Strecke sollte genau
zwei Kreisel (Anfang + Ende) beruehren - beruehrt ihre (in x/y gepruefte) zwei Kreisel (Anfang + Ende) beruehren - beruehrt ihre (in x/y gepruefte)
Bounding-Box mehr, steht hier ein Hinweis und es wird zusaetzlich eine Bounding-Box mehr, steht hier ein Hinweis und es wird zusaetzlich eine
Konsolen-WARNUNG ausgegeben (mit exakten 3D-BBoxen sollte das nicht auftreten). Konsolen-WARNUNG ausgegeben (mit exakten 3D-BBoxen sollte das nicht auftreten).
Scanner-Zeilen bekommen dieselbe Spalte fuer strittige Distanz-Zuordnungen:
liegt ein Scanner ausserhalb jeder Kreisel-/Streckengruppen-/Gefaellestrecke-
Boundingbox und ist der Abstand zum naechsten und zweitnaechsten Carrier fast
gleich gross, schreibt Lisp/count_sep_scan.lsp (cs-zuordnung-lauf, vor dem
Export automatisch aufgerufen) einen Warnungstext mit den IDs beider
Kandidaten in das "warnung"-Feld des Scanner-Blocks im Roh-JSON (siehe
csv:block-to-json in export.lsp) - hier unveraendert uebernommen.
Aufruf: Aufruf:
python export_csv.py <export_raw.json> <data_dir> <output.csv> python export_csv.py <export_raw.json> <data_dir> <output.csv>
@@ -84,6 +91,15 @@ EXPORTCSV vorhanden (nicht EXPORTSIVAS) - siehe csv:run-export. Insertpoint/
K1-K4 stehen zwar in export_raw.json fuer jeden Export zur Verfuegung (siehe K1-K4 stehen zwar in export_raw.json fuer jeden Export zur Verfuegung (siehe
csv:block-to-json), werden aber ebenfalls nur von diesem Skript (EXPORTCSV) csv:block-to-json), werden aber ebenfalls nur von diesem Skript (EXPORTCSV)
als CSV-Spalten ausgegeben. als CSV-Spalten ausgegeben.
Reihenfolge/Elementnummer: die Zeilen werden am Ende aufsteigend nach
TeileId sortiert (numerische IDs) und die Elementnummer (erste Spalte)
danach neu von 1 an vergeben (siehe teileid_sort_key in process_blocks) -
wurde kein Element geloescht, stimmen Elementnummer und TeileId dadurch
ueberein, geloeschte Elemente hinterlassen Luecken in der TeileId-Folge.
Zeilen ohne numerische TeileId (Omniflo-Sum-Zeile, SEP-/SCN-Fallback-IDs
ohne ID-Attribut) behalten stabil ihre bisherige Reihenfolge und stehen
danach.
""" """
import json import json
@@ -756,6 +772,13 @@ def process_blocks(blocks, lookup, dbg=None):
"bezeichnung": f"Scanner :{scanner_count}", "bezeichnung": f"Scanner :{scanner_count}",
"planquadrat": planquadrat_column(block, planquadrat_cfg), "planquadrat": planquadrat_column(block, planquadrat_cfg),
"merkmale": build_sensor_merkmale(block), "merkmale": build_sensor_merkmale(block),
# "warnung" kommt von cs-zuordnung-lauf (Lisp/count_sep_scan.lsp),
# das vor dem Export strittige Distanz-Zuordnungen (Scanner
# ausserhalb jeder Kreisel-/Strecken-Boundingbox, Abstand zu zwei
# Kandidaten fast gleich) in *cs-scanner-warnung-by-handle*
# sammelt - csv:block-to-json (export.lsp) haengt den Text als
# "warnung"-Feld an den betroffenen Scanner-Block im JSON an.
"warnung": block.get("warnung", ""),
**bbox_columns(block), **bbox_columns(block),
}) })
continue continue
@@ -816,6 +839,22 @@ def process_blocks(blocks, lookup, dbg=None):
elem_nr = build_kreisel_weiche_items(items, planquadrat_cfg, elem_nr, dbg=dbg) elem_nr = build_kreisel_weiche_items(items, planquadrat_cfg, elem_nr, dbg=dbg)
build_strecke_schleus_items(items, planquadrat_cfg, elem_nr, dbg=dbg) build_strecke_schleus_items(items, planquadrat_cfg, elem_nr, dbg=dbg)
# Nach dem Sammelheimen aller Zeilen: sortiert nach TeileId ausgeben, damit
# Elementnummer (erste Spalte) und TeileId nach Moeglichkeit uebereinstimmen
# (siehe format_csv_line/Elementnummer-Neuvergabe unten). Numerische IDs
# zuerst aufsteigend; nicht-numerische IDs (z.B. "autogenerated_of_json"
# der Omniflo-Sum-Zeile oder SEP-/SCN-Fallback-IDs ohne ID-Attribut)
# behalten stabil ihre bisherige Reihenfolge und stehen danach. Wurde kein
# Element geloescht, ergibt sich dadurch Elementnummer == TeileId.
def teileid_sort_key(indexed_item):
index, item = indexed_item
tid = str(item.get("teileid", "")).strip()
return (0, int(tid)) if tid.isdigit() else (1, index)
items = [item for _, item in sorted(enumerate(items), key=teileid_sort_key)]
for i, item in enumerate(items, start=1):
item["nr"] = i
return items return items
+1 -1
View File
@@ -135,7 +135,7 @@
[Verbindung bearbeiten]^C^C(ssg-ensure "Connection_Edit") CONNECTION_EDIT [Verbindung bearbeiten]^C^C(ssg-ensure "Connection_Edit") CONNECTION_EDIT
[<-Bearbeiten (Auto)]^C^C(ssg-ensure "SSG_LIB_Commands") SSG_BLOCKEDIT [<-Bearbeiten (Auto)]^C^C(ssg-ensure "SSG_LIB_Commands") SSG_BLOCKEDIT
[->Export] [->Export]
[Export Sivas]^C^C(ssg-ensure "count_sep_scan") ZAEHLE_SEP_SCAN (ssg-ensure "export") EXPORTSIVAS [Export Sivas]^C^C(ssg-ensure "export") EXPORTSIVAS
[<-Export CSV]^C^C(ssg-ensure "export") EXPORTCSV [<-Export CSV]^C^C(ssg-ensure "export") EXPORTCSV
[->Konfiguration] [->Konfiguration]
[Umschalten 2D und 3D]^C^C(ssg-ensure "SSG_LIB_Commands") SSG_DIM_SWITCH [Umschalten 2D und 3D]^C^C(ssg-ensure "SSG_LIB_Commands") SSG_DIM_SWITCH