export_csv: VF_n/GF_n-Einzelelemente zu Foerdereinheit/Gefaelleeinheit gruppieren

Die Einzelteil-Proxy-Zeilen (Boegen, Streckenelemente, Umlenk-/Motorstation)
aus der letzten Session sind fuer den Normalfall zu granular. Standardmaessig
(*csv-export-alle-elemente* = nil) fasst csv:unitmark-aktualisieren sie jetzt
zu je einer "Foerdereinheit"/"Gefaelleeinheit"-Zeile pro zusammenhaengendem
VF-/GF-Abschnitt einer Kette zusammen, mit einem persistenten Rechteck-Marker
(ZZ_UNIT_MARKER, analog ZZ_ASES_MARKER) auf der Bounding-Box-Vereinigung
aller enthaltenen Elemente. Der bisherige Rohdaten-Export bleibt ueber
(csv:export-alle-elemente-an) weiterhin verfuegbar.

Einheiten-Grenzen kommen rein export-seitig aus der sepliste (SEP/AS/ES in
echter Bau-Reihenfolge) plus dem bereits vorhandenen SSG_SEP_TYP je
Separator - eine bauzeitige Markierung in vf_linienzug.lsp/vf_standard.lsp/
vf_etage.lsp waere deutlich invasiver gewesen (3 unabhaengige Baupfade ohne
gemeinsamen Einstiegspunkt). csv:sepliste-partition (Lauflaengen-Kodierung
ueber die typisierte sepliste) und csv:element-unit-zuordnung (Zuordnung der
gesammelten Elemente per Kettenrichtungs-Projektion statt der nachweislich
unzuverlaessigen csv:nested-welt-records-Listenreihenfolge) sind reine,
dateifrei getestete Funktionen.

Korrigiert nebenbei eine falsche Annahme: L_VF_m/L_GF_m zaehlen pro Segment,
nicht pro Einheit - die tatsaechliche Einheiten-Anzahl kommt nur aus der
sepliste-Typfolge.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Michael Stangl
2026-09-24 23:46:41 +02:00
co-authored by Claude Sonnet 5
parent f751a90076
commit e7643bf181
6 changed files with 943 additions and 47 deletions
+768 -36
View File
@@ -50,6 +50,21 @@
;; ueberlebt damit jeden Neustart.
(setq *export-test-override* nil)
;; --- Schalter: rohe Einzelteil-Zeilen statt gruppierter Foerdereinheit/
;; Gefaelleeinheit-Zeilen exportieren ---
;; Default nil = gruppierter Modus (csv:unitmark-aktualisieren fasst Boegen/
;; Streckenelemente/Umlenk-/Motorstation zu je einer Zeile pro
;; Foerdereinheit/Gefaelleeinheit zusammen, die vier pattern_nested_*-
;; Einzelteil-Zeilen bleiben aus dem Export ausgeschlossen). Auf T gesetzt:
;; Verhalten wie vor der Gruppierung - jedes Einzelteil bekommt seine eigene
;; Export-Zeile (csv:collect-export-blocks laesst die pattern_nested_*-
;; Klauseln dann durch). Nur per Lisp-Variable/den beiden Helfern
;; umschaltbar, kein Menuepunkt vorgesehen.
(if (not (boundp '*csv-export-alle-elemente*)) (setq *csv-export-alle-elemente* nil))
(defun csv:export-alle-elemente-p () *csv-export-alle-elemente*)
(defun csv:export-alle-elemente-an () (setq *csv-export-alle-elemente* T) (princ))
(defun csv:export-alle-elemente-aus () (setq *csv-export-alle-elemente* nil) (princ))
;; --- Feste Datei-/Skriptnamen dieses Moduls (keine Config, direkt im Code) ---
(setq *export-filenames*
@@ -655,6 +670,20 @@
)
;; --- K1/K2 eines ZZ_UNIT_MARKER lesen ---
;; Fallback fuer csv:block-to-json (siehe dort): die Werte stehen bereits
;; fertig kodiert in den ATTDEFs K1/K2 (siehe csv:unitmark-aktualisieren),
;; hier nur noch als K-Liste im Format von csv:get-k-kos-strings verpackt.
(defun csv:unitmark-k-kos-strings (ename / attribs k1 k2 result)
(setq attribs (ssg-attrib-read ename))
(setq k1 (cdr (assoc "K1" attribs)))
(setq k2 (cdr (assoc "K2" attribs)))
(setq result nil)
(if (and k1 (> (strlen k1) 0)) (setq result (cons (cons "K1" k1) result)))
(if (and k2 (> (strlen k2) 0)) (setq result (cons (cons "K2" k2) result)))
result
)
;; --- Einen INSERT-Block als JSON-Objekt schreiben ---
;; include-bbox = T -> zusaetzlich Bounding-Box (Mitte + Ausdehnung) ermitteln
;; und als "bbox"-Objekt anhaengen (nur fuer EXPORTCSV, nicht fuer EXPORTSIVAS).
@@ -718,6 +747,14 @@
(wcmatch (strcase blk-name) "VF_*,GF_*"))
(setq k-list (csv:vfgf-k-kos-strings ename pt rotation))
)
;; ZZ_UNIT_MARKER (Foerdereinheit/Gefaelleeinheit): K1/K2 stehen bereits
;; fertig kodiert als eigene ATTDEFs am Marker (csv:unitmark-aktualisieren
;; berechnet sie einmal bei der Marker-Erzeugung aus dem ersten/letzten
;; zugeordneten Element) - keine erneute KS-Suche noetig.
(if (and (null k-list)
(wcmatch (strcase blk-name) "ZZ_UNIT_MARKER*"))
(setq k-list (csv:unitmark-k-kos-strings ename))
)
(setq bbox-json "")
(if include-bbox
(progn
@@ -1475,6 +1512,325 @@
gesetzt
)
;; ===========================================================================
;; Foerdereinheit/Gefaelleeinheit-Gruppierung (ZZ_UNIT_MARKER)
;; ===========================================================================
;; Fasst die Einzelbausteine einer VF_n/GF_n-Kette (Boegen, Strecken, Umlenk-/
;; Motorstation, siehe csv:element-proxies-erzeugen) wieder zu Einheiten
;; zusammen: abwechselnd Foerdereinheit (VF) / Gefaelleeinheit (GF), je EINE
;; Zeile mit K1 vom ersten, K2 vom letzten enthaltenen Element. Laeuft rein
;; export-seitig (kein Bau-Code-Eingriff) - Einheiten-Grenzen kommen aus der
;; sepliste (SEP/AS/ES in echter Bau-Reihenfolge, XDATA-lfdnr) plus dem
;; bereits vorhandenen SSG_SEP_TYP je Separator (VF/GF), NICHT aus der
;; unzuverlaessigen csv:nested-welt-records-Listenreihenfolge (siehe
;; Kommentar dort - fuer einen einzelnen Wrapper doppelt umgekehrt) und NICHT
;; aus L_VF_m/L_GF_m (die zaehlen pro Segment, nicht pro Einheit - ein VF-
;; Einheit kann mehrere L_VF_m-Eintraege haben, GF1/GF2-Rampen einer VF-
;; Einheit landen im selben L_GF_m wie eigenstaendige GF-Stuecke, siehe
;; vfl-block-erstellen-Kommentar in vf_linienzug.lsp).
;; --- Sepliste-Eintraege um strecke-typ und verlaessliche Weltposition
;; anreichern ---
;; sepliste = ((typ lfdnr x y) ...) wie ssg-sepliste-xdata-lesen liefert
;; (XDATA-Ebene - deren x/y sind der ROHE Gruppencode-10-Punkt
;; des Bau-Primitivs und koennen gespiegelt/verschoben sein,
;; siehe die sepliste-x/y-Bereinigung im JSON-Export; hier NICHT
;; als Positionsquelle verwendet, nur zur Paarung mit dem echten
;; Proxy per csv:sepliste-paare, wie an anderer Stelle bereits
;; etabliert).
;; sep-proxies-eigene = NUR die sep-proxies-Records (siehe
;; csv:sep-proxies-erzeugen) DIESES Wrappers, in
;; Erzeugungsreihenfolge.
;; asmarks-eigene = NUR die asmark-Records (marker-ename wrapper-ename
;; typ lfdnr) DIESES Wrappers, aus csv:asmark-aktualisieren.
;; Rueckgabe: ((typ lfdnr strecke-typ welt-pt) ...) in lfdnr-Reihenfolge.
;; strecke-typ = nil fuer AS/ES (wird von csv:sepliste-partition vom
;; Nachbarn geerbt) bzw. fuer einen SEP-Eintrag ohne gefundenen Partner.
;; welt-pt = (x y z) der ECHTEN, verlaesslichen Proxy-/Marker-Position (SEP
;; ueber sep-proxies-eigene, AS/ES ueber den asmark-Entity-Einfuegepunkt),
;; oder nil, falls kein Partner gefunden wurde.
(defun csv:sepliste-mit-strecke-typ (sepliste sep-proxies-eigene asmarks-eigene
/ proxies paare paar-by-lfdnr paar erg e
strecke-typ welt-pt m)
(setq proxies
(mapcar (function (lambda (r) (list (nth 2 r) (car (nth 3 r)) (cadr (nth 3 r)))))
sep-proxies-eigene))
(setq paare (csv:sepliste-paare sepliste proxies))
(setq paar-by-lfdnr nil)
(foreach paar paare
(setq paar-by-lfdnr (cons (cons (nth 0 paar) (nth 1 paar)) paar-by-lfdnr)))
(setq erg nil)
(foreach e sepliste
(setq strecke-typ nil welt-pt nil)
(cond
((= (nth 0 e) "SEP")
(setq paar (assoc (nth 1 e) paar-by-lfdnr))
(if paar
(progn
(setq strecke-typ (nth 4 (nth (cdr paar) sep-proxies-eigene)))
(setq welt-pt (nth 3 (nth (cdr paar) sep-proxies-eigene)))
)
)
)
((member (nth 0 e) (list "AS" "ES"))
(setq m nil)
(foreach kand asmarks-eigene
(if (and (null m) (= (nth 2 kand) (nth 0 e))
(= (nth 3 kand) (itoa (nth 1 e))))
(setq m kand)))
(if m (setq welt-pt (cdr (assoc 10 (entget (nth 0 m))))))
)
)
(setq erg (cons (list (nth 0 e) (nth 1 e) strecke-typ welt-pt) erg))
)
(reverse erg)
)
;; --- Ersten SEP-typ in der Liste finden (Vorschau fuer AS) ---
;; Eine Kette beginnt immer mit einer Foerdereinheit (AS haengt am ersten
;; SEP-Eintrag, falls vorhanden) - Default "VF", falls die Kette gar keinen
;; Separator hat (reine Foerdereinheit ohne jeden Zwischenhalt).
(defun csv:sepliste-erster-sep-typ (sepliste-typisiert / e)
(setq e (car sepliste-typisiert))
(cond
((null e) "VF")
((and (= (nth 0 e) "SEP") (nth 2 e)) (nth 2 e))
(t (csv:sepliste-erster-sep-typ (cdr sepliste-typisiert)))
)
)
;; --- Sepliste (mit strecke-typ) in Einheiten-Spannen partitionieren ---
;; sepliste-typisiert = ((typ lfdnr strecke-typ welt-pt) ...) in lfdnr-
;; Reihenfolge (Rueckgabe von csv:sepliste-mit-strecke-typ).
;;
;; Lauflaengen-Kodierung ueber den EFFEKTIVEN Typ jedes Eintrags, EIN
;; Vorwaerts-Durchlauf mit einer einzigen mitgefuehrten Zustandsvariable
;; "aktueller-typ" (das strecke-typ des zuletzt gesehenen SEP-Eintrags):
;; - AS: effektiver Typ = das strecke-typ des ERSTEN SEP-Eintrags der
;; ganzen Kette (Vorschau, csv:sepliste-erster-sep-typ) - eine Kette
;; beginnt immer mit einer Foerdereinheit.
;; - SEP: effektiver Typ = sein eigenes strecke-typ (faellt es aus - alter
;; Bestand ohne Marker -, bleibt der zuletzt bekannte Typ stehen).
;; "aktueller-typ" wird auf diesen Wert aktualisiert.
;; - ES: effektiver Typ = "aktueller-typ" (der zuletzt gesehene SEP,
;; also die Einheit, die gerade offen ist).
;; Eine neue Einheit beginnt, sobald sich der effektive Typ gegenueber dem
;; Vorgaenger aendert; gleich bleibender Typ (inkl. zwei aufeinanderfolgender
;; SEP-Eintraege desselben Typs, die GEMEINSAM eine einzige Einheit
;; bracketieren - HundM05-Beispiel: SEP3=GF,SEP4=GF sind EINE Gefaelleeinheit)
;; haengt an die offene Einheit an. Kein Sonderfall-Code fuer "erster/zweiter
;; SEP dieses Typs" noetig - die Lauflaengen-Regel deckt beides ab.
;;
;; Rueckgabe: ((einheit-typ (lfdnr ...)) ...) in Kettenreihenfolge.
;; Reine Rechenfunktion (keine Zeichnung, keine Datei) - getestet in
;; tests/test_unit.lsp.
(defun csv:sepliste-partition (sepliste-typisiert / erg e eff aktueller-typ
offen-typ offen-lfdnrs)
(setq aktueller-typ (csv:sepliste-erster-sep-typ sepliste-typisiert))
(setq erg nil offen-typ nil offen-lfdnrs nil)
(foreach e sepliste-typisiert
(cond
((= (nth 0 e) "SEP")
(setq aktueller-typ (cond ((nth 2 e)) (aktueller-typ)))
(setq eff aktueller-typ))
(t (setq eff aktueller-typ))
)
(if (and offen-typ (/= eff offen-typ))
(progn
(setq erg (cons (list offen-typ (reverse offen-lfdnrs)) erg))
(setq offen-typ nil offen-lfdnrs nil)
)
)
(setq offen-typ eff)
(setq offen-lfdnrs (cons (nth 1 e) offen-lfdnrs))
)
(if offen-typ
(setq erg (cons (list offen-typ (reverse offen-lfdnrs)) erg)))
(reverse erg)
)
;; --- Weltposition eines sepliste-lfdnr aus der typisierten Liste holen ---
(defun csv:sepliste-typisiert-pos (lfdnr sepliste-typisiert / treffer)
(setq treffer nil)
(foreach e sepliste-typisiert
(if (and (null treffer) (= (nth 1 e) lfdnr)) (setq treffer e)))
(if treffer (nth 3 treffer))
)
;; --- 2D-Skalarprodukt zweier Vektoren (x y) ---
(defun csv:dot2 (a b) (+ (* (car a) (car b)) (* (cadr a) (cadr b))))
;; --- Elemente den Einheiten-Spannen zuordnen ---
;; unit-spans = Rueckgabe von csv:sepliste-partition:
;; ((einheit-typ (lfdnr ...)) ...).
;; sepliste-typisiert = Rueckgabe von csv:sepliste-mit-strecke-typ (liefert
;; die Weltposition jedes Rand-lfdnr).
;; element-proxies-eigene = NUR die element-proxies-Records (siehe
;; csv:element-proxies-erzeugen) DIESES Wrappers,
;; (proxy-ename wrapper-ename teileart-key (x y z)).
;;
;; Vorgehen: fuer jede Spanne den ERSTEN und LETZTEN Rand-lfdnr in eine
;; Weltposition aufloesen (Start-/Endpunkt der Spanne entlang der Kette).
;; Kettenrichtung = Einheitsvektor vom allerersten zum allerletzten
;; Rand-lfdnr der GESAMTEN sepliste (AS->ES). Jedes Element wird per
;; Skalarprodukt auf diese Richtung projiziert (t-Wert) und der Spanne
;; zugeordnet, deren [t-start,t-ende]-Intervall am naechsten liegt (bei
;; eindeutiger Enthaltung: diese Spanne; sonst die mit dem kleinsten
;; Abstand zum naeheren Rand - kein Element bleibt unzugeordnet).
;;
;; Rueckgabe: Liste parallel zu unit-spans, je Spanne eine Liste
;; zugeordneter element-proxies-Records.
(defun csv:element-unit-zuordnung (unit-spans sepliste-typisiert
element-proxies-eigene / grenzen span
lfdnrs start-pos ende-pos richtung
erste-pos letzte-pos laenge t-werte
erg elem t-elem beste-i beste-abstand i
t-start t-ende abstand)
;; Kettenrichtung ueber den allerersten/allerletzten Rand-lfdnr der
;; gesamten sepliste bestimmen (nicht nur der ersten/letzten Spanne - bei
;; einer Kette mit nur einer Spanne sind das dieselben Werte).
(setq erste-pos nil letzte-pos nil)
(foreach e sepliste-typisiert
(if (and (null erste-pos) (nth 3 e)) (setq erste-pos (nth 3 e))))
(foreach e (reverse sepliste-typisiert)
(if (and (null letzte-pos) (nth 3 e)) (setq letzte-pos (nth 3 e))))
(if (or (null erste-pos) (null letzte-pos)
(< (distance erste-pos letzte-pos) 1e-6))
;; Entartete Kette (kein Rand-Punkt aufloesbar, oder AS=ES-Position) -
;; keine sinnvolle Projektion moeglich, jede Spanne bekommt keine
;; Elemente zugeordnet (Aufrufer behandelt das wie "keine Elemente").
(setq richtung '(1.0 0.0))
(progn
(setq richtung (list (- (car letzte-pos) (car erste-pos))
(- (cadr letzte-pos) (cadr erste-pos))))
(setq laenge (sqrt (csv:dot2 richtung richtung)))
(if (> laenge 1e-6)
(setq richtung (list (/ (car richtung) laenge) (/ (cadr richtung) laenge)))
(setq richtung '(1.0 0.0))
)
)
)
;; Je Spanne: [t-start, t-ende] aus dem ersten/letzten Rand-lfdnr.
(setq grenzen nil)
(foreach span unit-spans
(setq lfdnrs (cadr span))
(setq start-pos (csv:sepliste-typisiert-pos (car lfdnrs) sepliste-typisiert))
(setq ende-pos (csv:sepliste-typisiert-pos (last lfdnrs) sepliste-typisiert))
(setq t-start
(if start-pos
(csv:dot2 (list (- (car start-pos) (car erste-pos))
(- (cadr start-pos) (cadr erste-pos))) richtung)
0.0))
(setq t-ende
(if ende-pos
(csv:dot2 (list (- (car ende-pos) (car erste-pos))
(- (cadr ende-pos) (cadr erste-pos))) richtung)
0.0))
(setq grenzen (cons (list (min t-start t-ende) (max t-start t-ende)) grenzen))
)
(setq grenzen (reverse grenzen))
;; Je Element: t-Wert projizieren, naechstliegende Spanne waehlen. Jeder
;; Eintrag wird zunaechst als (t-elem . elem) gefuehrt, damit die Spanne
;; am Ende NACH t SORTIERT werden kann - das liefert "erstes"/"letztes"
;; Element einer Spanne entlang der Kettenrichtung (fuer K1/K2 der
;; Einheiten-Zeile, siehe csv:unitmark-aktualisieren), ohne auf die
;; unzuverlaessige csv:nested-welt-records-Reihenfolge angewiesen zu sein.
(setq erg nil)
(foreach span unit-spans (setq erg (cons nil erg)))
(setq erg (reverse erg))
(foreach elem element-proxies-eigene
(setq t-elem
(csv:dot2 (list (- (car (nth 3 elem)) (car erste-pos))
(- (cadr (nth 3 elem)) (cadr erste-pos))) richtung))
(setq beste-i 0 beste-abstand nil i 0)
(foreach g grenzen
(setq abstand
(cond
((and (>= t-elem (car g)) (<= t-elem (cadr g))) 0.0)
((< t-elem (car g)) (- (car g) t-elem))
(t (- t-elem (cadr g)))
))
(if (or (null beste-abstand) (< abstand beste-abstand))
(progn (setq beste-abstand abstand) (setq beste-i i)))
(setq i (1+ i))
)
(setq erg (csv:liste-setzen erg beste-i (cons (cons t-elem elem) (nth beste-i erg))))
)
;; Je Spanne nach t sortieren, dann den t-Wert wieder abstreifen.
(mapcar
(function (lambda (paare)
(mapcar 'cdr (vl-sort paare (function (lambda (a b) (< (car a) (car b))))))))
erg)
)
;; --- Element einer Liste an Index i ersetzen (0-basiert) ---
;; Kleiner Hilfsbaustein, da AutoLISP kein eingebautes "setf (nth i lst)" hat.
(defun csv:liste-setzen (lst i wert / j out)
(setq out nil j 0)
(foreach e lst
(setq out (cons (if (= j i) wert e) out))
(setq j (1+ j))
)
(reverse out)
)
;; --- Bounding-Box-Vereinigung mehrerer Entities ---
;; Verallgemeinert csv:get-bbox (siehe dort) von einer Entity auf eine Liste:
;; min/max ueber alle vla-getboundingbox-Ergebnisse, gleiche Fehler-
;; behandlung/Diagnose-Zaehler (*csv-bbox-fail-count*), gleiche Rueckgabeform
;; (cx cy cz dx dy dz). Entities, deren Box nicht ermittelt werden kann,
;; werden uebersprungen (nicht die ganze Vereinigung verworfen) - eine
;; teilweise Union ist besser als gar keine.
(defun csv:get-bbox-union (enames / obj res ename minpt maxpt gmin gmax dz min-hoehe)
(setq gmin nil gmax nil)
(foreach ename enames
(setq obj (vlax-ename->vla-object ename))
(vl-catch-all-apply 'vla-update (list obj))
(setq res
(vl-catch-all-apply
'(lambda ( / ll ur)
(vla-getboundingbox obj 'll 'ur)
(list (csv:pt->list ll) (csv:pt->list ur)))))
(if (vl-catch-all-error-p res)
(progn
(setq *csv-bbox-fail-count* (1+ (cond (*csv-bbox-fail-count*) (0))))
(if (null *csv-bbox-fail-first*)
(setq *csv-bbox-fail-first*
(strcat "(" (cond ((cdr (assoc 2 (entget ename)))) ("?")) ") "
(vl-catch-all-error-message res))))
)
(progn
(setq minpt (car res))
(setq maxpt (cadr res))
(setq gmin
(if gmin
(mapcar 'min gmin minpt)
minpt))
(setq gmax
(if gmax
(mapcar 'max gmax maxpt)
maxpt))
)
)
)
(if (and gmin gmax)
(progn
(setq dz (- (caddr gmax) (caddr gmin)))
(setq min-hoehe (csv:bb-minimum-height-mm))
(if (< dz min-hoehe) (setq dz min-hoehe))
(list
(/ (+ (car gmin) (car gmax)) 2.0)
(/ (+ (cadr gmin) (cadr gmax)) 2.0)
(/ (+ (caddr gmin) (caddr gmax)) 2.0)
(- (car gmax) (car gmin))
(- (cadr gmax) (cadr gmin))
dz
)
)
nil
)
)
;; ===========================================================================
;; AS-/ES-Marker (ZZ_ASES_MARKER)
;; ===========================================================================
@@ -1790,6 +2146,376 @@
(princ)
)
;; ===========================================================================
;; Foerdereinheit-/Gefaelleeinheit-Marker (ZZ_UNIT_MARKER)
;; ===========================================================================
;; Ein Rechteck, das der Export je Einheiten-Spanne (siehe
;; csv:sepliste-partition/csv:element-unit-zuordnung weiter oben) setzt und
;; dort DAUERHAFT stehen bleibt - analog zu ZZ_ASES_MARKER, aber auf die
;; Bounding-Box-Vereinigung der enthaltenen Elemente skaliert statt fest
;; quadratisch. Persistiert wie ZZ_ASES_MARKER ueber Exportlaeufe hinweg
;; (Wiederfinden ueber ZUORDNUNG+TYP+LFDNR), damit die TeileId stabil bleibt.
;;
;; Nur aktiv, wenn *csv-export-alle-elemente* NICHT gesetzt ist (siehe
;; Schalter weiter unten) - im Rohdaten-Modus werden keine neuen Marker
;; angelegt, ein Aufraeum-Durchlauf entfernt aber IMMER verwaiste Marker
;; (auch im Rohdaten-Modus - sonst blieben bei einem Wechsel gruppiert->roh
;; alte Marker als Karteileichen stehen).
(defun csv:unitmark-blockname () "ZZ_UNIT_MARKER")
;; Attribut-Schema. Alle ATTDEFs unsichtbar (wie *csv-asmark-attrib-defs*).
;; ID - fuellt ssg-id-check-all, ueber Exportlaeufe stabil
;; ZUORDNUNG - ID des VF_n/GF_n-Wrappers (Schluessel zum Wiederfinden)
;; TYP - "VF" (Foerdereinheit) oder "GF" (Gefaelleeinheit)
;; LFDNR - 1-basierte Einheiten-Nummer INNERHALB des Wrappers (NICHT
;; der sepliste-lfdnr - eine frische Zaehlung je Einheit)
;; LAENGE_m - Summe der LAENGE/A-Attribute aller enthaltenen
;; Streckenelement-Mitglieder (Meter, als String)
;; ANZAHL_BOEGEN - Anzahl der enthaltenen Mitglieder mit TeileArt "BOGEN"
;; K1/K2 - KS_EIN des ERSTEN bzw. KS_AUS des LETZTEN Mitglieds der
;; Spanne, bereits als csv:trans-encode-String (wie K1-K4
;; ueberall sonst im Export) - NICHT als rtos-Text: rtos
;; haengt an DIMZIN/LUPREC/LUNITS und ist fuer persistente
;; ATTDEF-Werte ungeeignet (siehe rtos-Hinweis in anderen
;; sepliste-Serialisierungen, ssg_ks_insert.lsp). Der
;; fertig kodierte String wird unveraendert zurueckgelesen,
;; kein Decode/Re-encode-Rundlauf noetig.
(setq *csv-unitmark-attrib-defs*
'(("ID" "") ("ZUORDNUNG" "") ("TYP" "") ("LFDNR" "")
("LAENGE_m" "") ("ANZAHL_BOEGEN" "") ("K1" "") ("K2" ""))
)
;; --- Blockdefinition des Markers bei Bedarf anlegen ---
;; 1x1-Einheitsquadrat auf Layer "0" (wie csv:asmark-blockdef-sicherstellen) -
;; wird beim Einfuegen ueber sx/sy auf die tatsaechliche Bounding-Box-
;; Ausdehnung der Einheit skaliert (csv:block-einfuegen-welt), anders als der
;; feste ZZ_ASES_MARKER. Keine eigene DWG (reine Hilfsgeometrie).
;; Rueckgabe: Blockname oder nil.
(defun csv:unitmark-blockdef-sicherstellen ( / name lastEnt ss e typ)
(setq name (csv:unitmark-blockname))
(if (tblsearch "BLOCK" name)
name
(progn
(setq lastEnt (entlast))
(entmake (list '(0 . "LWPOLYLINE") '(100 . "AcDbEntity") '(8 . "0")
'(100 . "AcDbPolyline") '(90 . 4) '(70 . 1)
(cons 10 (list -0.5 -0.5))
(cons 10 (list 0.5 -0.5))
(cons 10 (list 0.5 0.5))
(cons 10 (list -0.5 0.5))))
(ssg-attrib-make-defs *csv-unitmark-attrib-defs* 0.05)
(setq ss (ssadd))
(setq e (if lastEnt (entnext lastEnt) (entnext)))
(while e
(setq typ (cdr (assoc 0 (entget e))))
(if (member typ '("LWPOLYLINE" "ATTDEF")) (ssadd e ss))
(setq e (entnext e))
)
(command "_.-BLOCK" name (list 0.0 0.0 0.0) ss "")
(if (tblsearch "BLOCK" name)
(progn (princ (ssg-textf "exp-unitmark-blockdef" (list name))) name)
(progn (princ (ssg-textf "exp-unitmark-blockdef-fehler" (list name))) nil)
)
)
)
)
;; --- Vorhandene Marker der Zeichnung einsammeln ---
;; Rueckgabe: Liste von (ename zuordnung typ lfdnr id), analog
;; csv:asmark-sammeln.
(defun csv:unitmark-sammeln ( / ss i ename attribs out)
(setq ss (ssget "X" (list (cons 0 "INSERT") (cons 2 (csv:unitmark-blockname)))))
(if ss
(progn
(setq i 0)
(while (setq ename (ssname ss i))
(setq attribs (ssg-attrib-read ename))
(setq out
(cons (list ename
(cond ((cdr (assoc "ZUORDNUNG" attribs))) (""))
(cond ((cdr (assoc "TYP" attribs))) (""))
(cond ((cdr (assoc "LFDNR" attribs))) (""))
(cond ((cdr (assoc "ID" attribs))) ("")))
out))
(setq i (1+ i))
)
)
)
out
)
;; --- Bestehenden Marker ueber (ZUORDNUNG, TYP, LFDNR) wiederfinden ---
(defun csv:unitmark-finden (bestand zuordnung typ lfdnr / m treffer)
(foreach m bestand
(if (and (null treffer)
(= (nth 1 m) zuordnung)
(= (nth 2 m) typ)
(= (nth 3 m) lfdnr))
(setq treffer m)
)
)
treffer
)
;; --- Laenge (m) eines Streckenelement-Mitglieds aus seinem LAENGE/A-
;; Attribut lesen; 0.0 falls keins vorhanden ---
(defun csv:unitmark-elem-laenge-mm (proxy-ename / attribs wert)
(setq attribs (ssg-attrib-read proxy-ename))
(setq wert (cond ((cdr (assoc "LAENGE" attribs))) ((cdr (assoc "A" attribs))) (nil)))
(if wert (atof wert) 0.0)
)
;; --- K1/K2 eines Mitglieds lesen, bereits als csv:trans-encode-String ---
;; Dieselbe KS_EIN/KS_AUS-Suche wie csv:get-k-kos-strings (nur EINE Ebene
;; tief in der Proxy-Blockdefinition, echte K1-K4 haben Vorrang). ks-ziel =
;; "K1" oder "K2" (Ziel-Slot wie dort).
;; Rueckgabe: 12-Zeichen Base64-String oder nil, falls das Mitglied kein
;; passendes KS traegt (z.B. das nie verwendete Vario_Kurve_*).
(defun csv:unitmark-elem-ks (proxy-ename ks-ziel / ed pt rotation extrusion
R-world psx psy psz blk-tbl sub-ent sub-ed
sub-bname sub-pt sub-extrusion sub-pt-real
is-real target treffer)
(setq ed (entget proxy-ename))
(setq pt (cdr (assoc 10 ed)))
(if (null (caddr pt)) (setq pt (list (car pt) (cadr pt) 0.0)))
(setq rotation (cond ((cdr (assoc 50 ed))) (0.0)))
(setq extrusion (cond ((cdr (assoc 210 ed))) ('(0.0 0.0 1.0))))
(setq pt (mat3-mul-vec3 (mat3-ocs-matrix extrusion) pt))
(setq R-world (mat3-from-normal-rotation extrusion rotation))
(setq psx (cond ((cdr (assoc 41 ed))) (1.0)))
(setq psy (cond ((cdr (assoc 42 ed))) (1.0)))
(setq psz (cond ((cdr (assoc 43 ed))) (1.0)))
(setq blk-tbl (tblsearch "BLOCK" (cdr (assoc 2 ed))))
(setq treffer nil)
(if blk-tbl
(progn
(setq sub-ent (cdr (assoc -2 blk-tbl)))
(while sub-ent
(setq sub-ed (entget sub-ent))
(if (= (cdr (assoc 0 sub-ed)) "INSERT")
(progn
(setq sub-bname (strcase (cdr (assoc 2 sub-ed))))
(setq is-real (wcmatch sub-bname "K1,K2,K3,K4"))
(setq target
(cond
(is-real sub-bname)
((or (= sub-bname "KS_EIN") (= sub-bname "KSYS_EIN")) "K1")
((or (= sub-bname "KS_AUS") (= sub-bname "KSYS_AUS")) "K2")
(t nil)))
(if (and target (= target ks-ziel) (or is-real (null treffer)))
(progn
(setq sub-pt (cdr (assoc 10 sub-ed)))
(if (null (caddr sub-pt))
(setq sub-pt (list (car sub-pt) (cadr sub-pt) 0.0)))
(setq sub-extrusion (cond ((cdr (assoc 210 sub-ed))) ('(0.0 0.0 1.0))))
(setq sub-pt-real (mat3-mul-vec3 (mat3-ocs-matrix sub-extrusion) sub-pt))
(setq sub-pt-real (list (* psx (car sub-pt-real))
(* psy (cadr sub-pt-real))
(* psz (caddr sub-pt-real))))
(setq sub-pt-real (csv:local-to-world pt R-world sub-pt-real))
(setq treffer
(csv:trans-encode (car sub-pt-real) (cadr sub-pt-real) (caddr sub-pt-real)))
)
)
)
)
(setq sub-ent (entnext sub-ent))
)
)
)
treffer
)
;; --- Alle Foerdereinheit-/Gefaelleeinheit-Marker der Zeichnung neu
;; aufbauen ---
;; Laeuft VOR ssg-id-check-all (csv:export-vorbereiten), analog
;; csv:asmark-aktualisieren. sep-proxies/element-proxies/asmarks = die drei
;; bereits erzeugten Proxy-/Marker-Listen dieses Exportlaufs.
;;
;; Ist *csv-export-alle-elemente* gesetzt (Rohdaten-Modus): KEINE neuen
;; Marker anlegen, nur den kompletten vorhandenen Bestand aufraeumen (sonst
;; blieben bei einem Wechsel gruppiert->roh alte Marker als Karteileichen
;; stehen).
;;
;; Rueckgabe: Liste von Records (marker-ename wrapper-ename typ lfdnr) fuer
;; csv:unitmark-ids-setzen.
(defun csv:unitmark-aktualisieren (sep-proxies element-proxies asmarks
/ blockname bestand ms wrapper-enames
rec wrapper ed bname wrapper-id app
sepliste sepliste-typisiert sep-eigene
elem-eigene asmarks-eigene unit-spans
zuordnungen span span-i typ lfdnrs
zugeordnet alt alt-id mark bbox-enames
bbox laenge-mm anzahl-boegen elem
lfdnr-erstes lfdnr-letztes k1 k2
ergebnis neu uebernommen verwaist)
(setq neu 0 uebernommen 0 verwaist 0)
(setq bestand (csv:unitmark-sammeln))
(if (not (csv:export-alle-elemente-p))
(progn
(setq blockname (csv:unitmark-blockdef-sicherstellen))
(if blockname
(progn
(setq ms (vla-get-ModelSpace (vla-get-ActiveDocument (vlax-get-acad-object))))
;; Wrapper einsammeln (jeder nur einmal) - Vereinigung ueber alle
;; drei Proxy-Listen, damit auch ein Wrapper OHNE Boegen/Strecken
;; (nur AS-Separator-ES, reine Einzel-Foerdereinheit) erfasst wird.
(setq wrapper-enames nil)
(foreach rec sep-proxies
(if (not (member (nth 1 rec) wrapper-enames))
(setq wrapper-enames (cons (nth 1 rec) wrapper-enames))))
(foreach rec element-proxies
(if (not (member (nth 1 rec) wrapper-enames))
(setq wrapper-enames (cons (nth 1 rec) wrapper-enames))))
(foreach rec asmarks
(if (not (member (nth 1 rec) wrapper-enames))
(setq wrapper-enames (cons (nth 1 rec) wrapper-enames))))
(foreach wrapper (reverse wrapper-enames)
(setq ed (entget wrapper))
(setq bname (cdr (assoc 2 ed)))
(setq wrapper-id (cond ((cdr (assoc "ID" (ssg-attrib-read wrapper)))) ("")))
(setq app
(cond
((wcmatch (strcase bname) "VF_*") "SSG_VF_SEP")
((wcmatch (strcase bname) "GF_*") "SSG_GF_SEP")
(t nil)))
(setq sepliste
(if (and app (car (atoms-family 1 '("SSG-SEPLISTE-XDATA-LESEN"))))
(ssg-sepliste-xdata-lesen wrapper app)))
(if sepliste
(progn
(setq sep-eigene nil)
(foreach rec sep-proxies
(if (eq (nth 1 rec) wrapper) (setq sep-eigene (cons rec sep-eigene))))
(setq sep-eigene (reverse sep-eigene))
(setq elem-eigene nil)
(foreach rec element-proxies
(if (eq (nth 1 rec) wrapper) (setq elem-eigene (cons rec elem-eigene))))
(setq elem-eigene (reverse elem-eigene))
(setq asmarks-eigene nil)
(foreach rec asmarks
(if (eq (nth 1 rec) wrapper) (setq asmarks-eigene (cons rec asmarks-eigene))))
(setq asmarks-eigene (reverse asmarks-eigene))
(setq sepliste-typisiert
(csv:sepliste-mit-strecke-typ sepliste sep-eigene asmarks-eigene))
(setq unit-spans (csv:sepliste-partition sepliste-typisiert))
(setq zuordnungen
(csv:element-unit-zuordnung unit-spans sepliste-typisiert elem-eigene))
(setq span-i 0)
(foreach span unit-spans
(setq typ (car span))
(setq lfdnrs (cadr span))
;; zugeordnet ist bereits NACH KETTENRICHTUNG SORTIERT
;; (csv:element-unit-zuordnung) - das erste Element liefert
;; K1, das letzte K2.
(setq zugeordnet (nth span-i zuordnungen))
;; Bounding-Box-Vereinigung ueber alle zugeordneten
;; Element-Proxies. Ist eine Spanne leer (z.B. eine ganz
;; kurze Gefaelleeinheit ohne eigenes Bogen-/Strecken-
;; Element zwischen zwei direkt benachbarten Separatoren),
;; entsteht fuer sie kein Marker - eine Einheit ohne jedes
;; zugeordnete Bauteil hat keine sinnvolle Geometrie.
(setq bbox-enames nil)
(foreach elem zugeordnet (setq bbox-enames (cons (nth 0 elem) bbox-enames)))
(setq bbox (if bbox-enames (csv:get-bbox-union bbox-enames)))
(if bbox
(progn
;; Laenge/Anzahl Boegen aus den zugeordneten Elementen.
(setq laenge-mm 0.0 anzahl-boegen 0)
(foreach elem zugeordnet
(if (= (nth 2 elem) "STRECKENELEMENT")
(setq laenge-mm (+ laenge-mm (csv:unitmark-elem-laenge-mm (nth 0 elem)))))
(if (= (nth 2 elem) "BOGEN")
(setq anzahl-boegen (1+ anzahl-boegen)))
)
;; K1/K2: eigenes KS_EIN des ERSTEN, KS_AUS des
;; LETZTEN zugeordneten Elements (Kettenrichtungs-
;; Sortierung, siehe oben) - fehlt bei einem Element
;; das passende KS (z.B. das nie verwendete
;; Vario_Kurve_*), bleibt K1/K2 leer, kein Abbruch.
(setq k1 (csv:unitmark-elem-ks (nth 0 (car zugeordnet)) "K1"))
(setq k2 (csv:unitmark-elem-ks (nth 0 (last zugeordnet)) "K2"))
(setq mark
(csv:block-einfuegen-welt ms blockname
(list (nth 0 bbox) (nth 1 bbox) (nth 2 bbox))
(mat3-rz 0.0)
(max (nth 3 bbox) 1.0) (max (nth 4 bbox) 1.0) 1.0
bname))
(if mark
(progn
(setq alt (csv:unitmark-finden bestand wrapper-id typ (itoa (1+ span-i))))
(setq alt-id "")
(if alt
(progn
(setq alt-id (nth 4 alt))
(entdel (nth 0 alt))
(setq bestand (vl-remove alt bestand))
(setq uebernommen (1+ uebernommen))
)
(setq neu (1+ neu))
)
(ssg-attrib-set-on mark
(list (cons "ID" alt-id)
(cons "ZUORDNUNG" wrapper-id)
(cons "TYP" typ)
(cons "LFDNR" (itoa (1+ span-i)))
(cons "LAENGE_m" (rtos (/ laenge-mm 1000.0) 2 3))
(cons "ANZAHL_BOEGEN" (itoa anzahl-boegen))
(cons "K1" (cond (k1) ("")))
(cons "K2" (cond (k2) ("")))
))
(ssg-layer-setzen mark "ases_marker")
(setq ergebnis (cons (list mark wrapper typ (1+ span-i)) ergebnis))
)
)
)
)
(setq span-i (1+ span-i))
)
)
)
)
)
)
)
)
;; Rest des Bestands (auch im Rohdaten-Modus, wo bestand nie durch obige
;; Schleife verkleinert wurde): keine Kette/Einheit mehr dahinter.
(foreach m bestand
(entdel (nth 0 m))
(setq verwaist (1+ verwaist))
)
(if (or (> neu 0) (> uebernommen 0) (> verwaist 0))
(princ (ssg-textf "exp-unitmark-bilanz"
(list (itoa neu) (itoa uebernommen) (itoa verwaist)))))
(reverse ergebnis)
)
;; --- Nach der ID-Vergabe: ZUORDNUNG bereits gesetzt (anders als bei
;; ZZ_ASES_MARKER, siehe csv:unitmark-aktualisieren) - hier bleibt nur die
;; Bilanz-Meldung uebrig. Kein sepliste-Bezug (Einheiten sind nicht Teil der
;; sepliste selbst). ---
(defun csv:unitmark-ids-setzen (marker-liste / rec mark id gesetzt ohne-id)
(setq gesetzt 0 ohne-id 0)
(foreach rec marker-liste
(setq mark (nth 0 rec))
(setq id (cdr (assoc "ID" (ssg-attrib-read mark))))
(if (and id (> (strlen id) 0))
(setq gesetzt (1+ gesetzt))
(setq ohne-id (1+ ohne-id))
)
)
(if (> gesetzt 0)
(princ (ssg-textf "exp-unitmark-ids" (list (itoa gesetzt)))))
gesetzt
)
;; --- Relevante Bloecke sammeln ---
;; Filtert INSERT-Entities auf exportierbare Bloecke:
@@ -1828,7 +2554,7 @@
;; Element mitgezaehlt - export_csv.py UND export_sivas.py fuehren sie
;; beide als eigene TeileArt-Zeilen.
;; Gibt Auswahlsatz zurueck oder nil.
(defun csv:collect-export-blocks ( / ss-all ss-out i ename ed bname)
(defun csv:collect-export-blocks ( / ss-all ss-out i ename ed bname ist-nested-element)
(setq ss-all (ssget "X" (list (cons 0 "INSERT"))))
(if (null ss-all) (setq ss-out nil)
(progn
@@ -1837,40 +2563,40 @@
(while (setq ename (ssname ss-all i))
(setq ed (entget ename))
(setq bname (cdr (assoc 2 ed)))
(if (or
(wcmatch bname (export:pattern "pattern_kreisel" "KR_*,KREISEL_*,ECKRAD_*"))
(wcmatch (strcase bname) (export:pattern "pattern_gerade" "AP110*,AP_110*"))
(wcmatch bname (export:pattern "pattern_strecke"
"Vario*,Staustrecke*,AUS_Element*,EIN_Element*,VF_*,GF_*"))
(wcmatch bname (export:pattern "pattern_ases_marker" "ZZ_ASES_MARKER*"))
(wcmatch bname (export:pattern "pattern_separator" "Separator_SP*,S-LP*"))
(wcmatch bname (export:pattern "pattern_scanner" "Scanner*"))
(wcmatch bname (export:pattern "pattern_btmt_beladung" "BTMT-Beladung*"))
(wcmatch bname (export:pattern "pattern_btmt_entladung" "SC_Entladung*"))
(wcmatch bname (export:pattern "pattern_eingang" "Eingang*"))
(wcmatch bname (export:pattern "pattern_ausgang" "Ausgang*"))
(wcmatch bname (export:pattern "pattern_geruest" "GR_*"))
;; Uebrige verpackte VF_n/GF_n-Bausteine (Boegen, gerade
;; Foerderer-/Gefaellestrecken-Segmente, Umlenk-/Motorstation) -
;; siehe csv:element-proxies-erzeugen. pattern_strecke oben
;; wuerde die meisten davon schon per Praefix (Vario*/
;; Staustrecke*) miterfassen, NUR Gefaellebogen_* nicht - die
;; vier expliziten Klauseln schliessen diese Luecke und machen
;; die Zugehoerigkeit klar. Kein Duplikate-Risiko: eine ssadd-
;; Selektion kann dieselbe Entity nicht zweimal enthalten, egal
;; wie viele Klauseln hier matchen.
(wcmatch bname (export:pattern "pattern_nested_bogen"
"Gefaellebogen_*,Vario_Bogen_*,Vario_Kurve_*"))
(wcmatch bname (export:pattern "pattern_nested_streckenelement"
"Staustrecke_SP_1000_mm*"))
(wcmatch bname (export:pattern "pattern_nested_umlenkstation" "Vario_Umlenkstation_*"))
(wcmatch bname (export:pattern "pattern_nested_motorstation" "Vario_Motorstation_*"))
;; Rein numerische Namen = Omniflo SivasNr (Boegen/Weichen)
;; Rein LISP-intern (Python klassifiziert Omniflo stattdessen
;; ueber exakten Katalog-Match), daher fest im Code statt in
;; cfg/export.cfg.
(wcmatch bname "#*")
)
;; Uebrige verpackte VF_n/GF_n-Bausteine (Boegen, gerade Foerderer-/
;; Gefaellestrecken-Segmente, Umlenk-/Motorstation, siehe
;; csv:pattern-nested-element/csv:element-proxies-erzeugen): NUR im
;; Rohdaten-Modus (*csv-export-alle-elemente* = T) als eigene Export-
;; Zeile gesammelt. Im Default (gruppierter Modus, nil) fasst
;; csv:unitmark-aktualisieren sie zu Foerdereinheit-/Gefaelleeinheit-
;; Zeilen zusammen (pattern_unit_marker unten) - die Proxy-Kopien
;; selbst werden dann trotz Treffer auf pattern_strecke (Vario*/
;; Staustrecke* per Praefix) explizit AUSGESCHLOSSEN, damit sie nicht
;; doppelt (einzeln UND als Teil der Einheiten-Zeile) erscheinen.
(setq ist-nested-element
(and (not (csv:export-alle-elemente-p))
(wcmatch bname (csv:pattern-nested-element))))
(if (and (not ist-nested-element)
(or
(wcmatch bname (export:pattern "pattern_kreisel" "KR_*,KREISEL_*,ECKRAD_*"))
(wcmatch (strcase bname) (export:pattern "pattern_gerade" "AP110*,AP_110*"))
(wcmatch bname (export:pattern "pattern_strecke"
"Vario*,Staustrecke*,AUS_Element*,EIN_Element*,VF_*,GF_*"))
(wcmatch bname (export:pattern "pattern_ases_marker" "ZZ_ASES_MARKER*"))
(wcmatch bname (export:pattern "pattern_separator" "Separator_SP*,S-LP*"))
(wcmatch bname (export:pattern "pattern_scanner" "Scanner*"))
(wcmatch bname (export:pattern "pattern_btmt_beladung" "BTMT-Beladung*"))
(wcmatch bname (export:pattern "pattern_btmt_entladung" "SC_Entladung*"))
(wcmatch bname (export:pattern "pattern_eingang" "Eingang*"))
(wcmatch bname (export:pattern "pattern_ausgang" "Ausgang*"))
(wcmatch bname (export:pattern "pattern_geruest" "GR_*"))
(wcmatch bname (export:pattern "pattern_unit_marker" "ZZ_UNIT_MARKER*"))
;; Rein numerische Namen = Omniflo SivasNr (Boegen/Weichen)
;; Rein LISP-intern (Python klassifiziert Omniflo stattdessen
;; ueber exakten Katalog-Match), daher fest im Code statt in
;; cfg/export.cfg.
(wcmatch bname "#*")
))
(ssadd ename ss-out)
)
(setq i (1+ i))
@@ -1918,7 +2644,7 @@
;; die bbox-Fehlerdiagnose zuruecksetzen. Rueckgabe: die Liste ALLER erzeugten
;; Kopien - Separatoren UND AS/ES-Schleuselemente (der Aufrufer entfernt sie am
;; Ende wieder). label dient nur den Meldungen.
(defun csv:export-vorbereiten (label / sep-proxies asmarks element-proxies)
(defun csv:export-vorbereiten (label / sep-proxies asmarks element-proxies unitmarks)
;; Verpackte Separator_SP-Symbole (siehe csv:sep-proxies-erzeugen) VOR der
;; ID-Vergabe durch temporaere, echte Kopien ersetzen - sie werden am Ende
;; von csv:run-export wieder entfernt (csv:sep-proxies-loeschen).
@@ -1935,6 +2661,11 @@
;; dessen Lauf mit nummeriert werden (siehe ssg-id-collect-blocks,
;; ssg_id.lsp).
(setq element-proxies (csv:element-proxies-erzeugen))
;; Foerdereinheit-/Gefaelleeinheit-Marker (ZZ_UNIT_MARKER) aus den drei
;; obigen Proxy-/Marker-Listen bauen - MUSS nach ihnen (braucht ihre
;; Weltpositionen) und VOR ssg-id-check-all stehen (frische Marker werden
;; dort mit nummeriert), siehe csv:unitmark-aktualisieren.
(setq unitmarks (csv:unitmark-aktualisieren sep-proxies element-proxies asmarks))
;; 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
@@ -1956,6 +2687,7 @@
(setq *csv-sepliste-id-by-handle* nil)
(csv:asmark-ids-setzen asmarks)
(csv:sepliste-ids-setzen sep-proxies)
(csv:unitmark-ids-setzen unitmarks)
;; Vor dem Export HOEHE/DREHUNG aller Omniflo-Elemente aktualisieren
(princ (ssg-textf "exp-update-hoehe-drehung" (list label)))
(omni:update-all-attribs)
+4
View File
@@ -72,6 +72,10 @@
;; und eigene JSON-Zeile) und behalten sie ueber Laeufe hinweg,
;; weil der Marker in der Zeichnung stehen bleibt.
(wcmatch bname "ZZ_ASES_MARKER*")
;; ZZ_UNIT_MARKER: die Foerdereinheit-/Gefaelleeinheit-Marker
;; (siehe csv:unitmark-aktualisieren, export.lsp) - aus
;; demselben Grund wie ZZ_ASES_MARKER.
(wcmatch bname "ZZ_UNIT_MARKER*")
(wcmatch bname "Separator_SP*,S-LP*,Scanner*")
(wcmatch bname "BTMT-Beladung*,SC_Entladung*")
(wcmatch bname "Eingang*,Ausgang*")
+26 -5
View File
@@ -76,16 +76,36 @@
# Gefaellestrecken-Segmente, Umlenk-/Motorstation). Wie pattern_schleuselement
# liegen sie NICHT als eigener top-level INSERT vor, sondern verpackt in der
# Wrapper-Blockdefinition - die Muster dienen der SUCHE dort
# (csv:nested-welt-records). Exportiert wird eine TEMPORAERE Kopie des
# jeweiligen Original-Blocks (csv:element-proxies-erzeugen, wie bei
# Separator/pattern_separator), die nach dem Export wieder geloescht wird.
# Vario_Kurve_* ist laut Code-Kommentar (vf_linienzug.lsp) aktuell nirgends
# im Projekt tatsaechlich eingefuegt - nur zur Vollstaendigkeit gelistet.
# (csv:nested-welt-records). Eine TEMPORAERE Kopie des jeweiligen Original-
# Blocks wird IMMER erzeugt (csv:element-proxies-erzeugen, wie bei Separator/
# pattern_separator - liefert die Positionsdaten fuer die Gruppierung unten)
# und nach dem Export IMMER wieder geloescht; als EIGENE Export-Zeile
# erscheinen sie aber NUR, wenn *csv-export-alle-elemente* (export.lsp) auf T
# gesetzt ist. Im Default (nil, gruppierter Modus) fasst
# csv:unitmark-aktualisieren sie stattdessen zu Foerdereinheit-/
# Gefaelleeinheit-Zeilen zusammen (siehe pattern_unit_marker). Vario_Kurve_*
# ist laut Code-Kommentar (vf_linienzug.lsp) als "nirgends im Projekt
# tatsaechlich eingefuegt" dokumentiert - in der Praxis kommt es doch vor
# (siehe reale HundM05-Exporte), das Muster bleibt darum wirksam.
# Staustrecke_SP_1000_mm wird beim Bau per Laenge NICHT-UNIFORM skaliert
# (insert-inclined-scaled-block) - csv:get-k-kos-strings beruecksichtigt das
# beim Lesen von K1/K2 (sx/sy/sz-Parameter). export_sivas.py schliesst diese
# vier Muster wie Separator/Scanner aus (Stueckzahl steckt schon in den
# GF_Bogen_*/VF_Bogen_*/ANZAHL_VF/ANZAHL_GF-Attributen des Wrappers).
#
# pattern_unit_marker: der Marker, den der Export je zusammenhaengendem
# Foerderer-Abschnitt ("Foerdereinheit") bzw. Gefaelle-Abschnitt
# ("Gefaelleeinheit") einer VF_n/GF_n-Kette setzt (ZZ_UNIT_MARKER, ein
# Rechteck auf der Bounding-Box-Vereinigung aller enthaltenen Boegen/
# Streckenelemente/Umlenk-/Motorstation-Mitglieder, csv:unitmark-
# aktualisieren in Lisp/export.lsp). Bleibt wie ZZ_ASES_MARKER dauerhaft in
# der Zeichnung stehen und behaelt seine ID ueber Exportlaeufe hinweg
# (Attribute ID/ZUORDNUNG/TYP/LFDNR/LAENGE_m/ANZAHL_BOEGEN/K1/K2, alle
# unsichtbar). Nur aktiv im gruppierten Modus (*csv-export-alle-elemente* =
# nil) - im Rohdaten-Modus werden keine neuen Marker angelegt, verwaiste
# Marker (z.B. nach einem Wechsel gruppiert->roh) werden aber immer
# aufgeraeumt. export_sivas.py schliesst dieses Muster wie die vier
# pattern_nested_*-Muster aus (Stueckzahl steckt in ANZAHL_VF/ANZAHL_GF).
# ============================================================
[blockpattern]
@@ -109,6 +129,7 @@ pattern_nested_bogen= Gefaellebogen_*, Vario_Bogen_*, Vario_Kurve_*
pattern_nested_streckenelement= Staustrecke_SP_1000_mm*
pattern_nested_umlenkstation= Vario_Umlenkstation_*
pattern_nested_motorstation= Vario_Motorstation_*
pattern_unit_marker= ZZ_UNIT_MARKER*
pattern_ks_subblocks= KS_EIN, KS_AUS, K1, K2, K3, K4
[python]
+54
View File
@@ -272,6 +272,24 @@ def build_element_merkmale(block):
}
def build_einheit_merkmale(block):
"""Merkmale-Dict fuer eine Foerdereinheit-/Gefaelleeinheit-Zeile (ZZ_UNIT_
MARKER, siehe csv:unitmark-aktualisieren in export.lsp und
pattern_unit_marker in cfg/export.cfg).
LAENGE_m/ANZAHL_BOEGEN kommen direkt aus den ATTDEFs des Markers - dort
von Lisp bereits vorberechnet (Summe der LAENGE/A-Attribute aller
zugeordneten Streckenelement-Mitglieder bzw. Anzahl der Bogen-Mitglieder).
Python hat im gruppierten Modus KEINEN Zugriff auf die Einzelteile selbst
(die werden dort gar nicht erst exportiert), daher keine eigene
Neuberechnung hier."""
attribs = block.get("attribs", {})
return {
"Laenge_m": attribs.get("LAENGE_m", ""),
"Anzahl_Boegen": attribs.get("ANZAHL_BOEGEN", ""),
}
def build_btmt_merkmale(block):
"""Merkmale-Dict fuer eine eigenstaendige BTMT-Beladung/SC_Entladung-Station
(siehe pattern_btmt_beladung/pattern_btmt_entladung in cfg/export.cfg).
@@ -948,6 +966,8 @@ def process_blocks(blocks, lookup, dbg=None):
geruest_count = 0
ausschleus_count = 0
einschleus_count = 0
foerdereinheit_count = 0
gefaelleeinheit_count = 0
# Zaehlung fuer die Omniflo-Sum-Zeile (nur Bogen/Weiche/Gerade)
cnt_wk = cnt_einzel = cnt_delta = cnt_doppel = cnt_stern = 0
@@ -1013,6 +1033,40 @@ def process_blocks(blocks, lookup, dbg=None):
})
continue
# ZZ_UNIT_MARKER (siehe cfg/export.cfg pattern_unit_marker sowie
# csv:unitmark-aktualisieren in Lisp/export.lsp): der persistente
# Marker, den Lisp je zusammenhaengendem Foerderer-/Gefaelle-Abschnitt
# einer VF_n/GF_n-Kette setzt (gruppierter Modus, Default). TYP
# entscheidet VF->Foerdereinheit / GF->Gefaelleeinheit, analog zur
# AS/ES-Klassifikation oben. K1/K2 brauchen keine Sonderbehandlung -
# bbox_columns() liest sie generisch aus dem JSON-Row (csv:unitmark-
# k-kos-strings, export.lsp).
if matches_any(bname, BLOCKPATTERNS.get("pattern_unit_marker", [])):
attribs = block.get("attribs", {})
typ = attribs.get("TYP", "")
teileart = {"VF": "ILS 2.0 Foerdereinheit", "GF": "ILS 2.0 Gefaelleeinheit"}.get(typ)
if teileart is None:
print(f"[export_csv] WARNUNG: {bname} ohne gueltiges TYP-Attribut "
f"(TYP={typ!r}) - uebersprungen")
continue
elem_nr += 1
if typ == "VF":
foerdereinheit_count += 1
zaehler_n = foerdereinheit_count
else:
gefaelleeinheit_count += 1
zaehler_n = gefaelleeinheit_count
items.append({
"nr": elem_nr,
"teileart": teileart,
"teileid": attribs.get("ID") or format_sensor_id("EINH", elem_nr),
"bezeichnung": f"{teileart} :{zaehler_n}",
"planquadrat": planquadrat_column(block, planquadrat_cfg),
"merkmale": build_einheit_merkmale(block),
**bbox_columns(block),
})
continue
# Omniflo Bogen oder Weiche
if bname in lookup:
typ, eintrag = lookup[bname]
+10 -6
View File
@@ -413,18 +413,22 @@ SKIP_BLOCKS = set(BLOCKPATTERNS.get("pattern_ks_subblocks", ["K1", "K2", "K3", "
#
# Dieselbe Ueberlegung gilt fuer die ausgepackten VF_n/GF_n-Sub-Komponenten
# (Boegen, gerade Foerderer-/Gefaellestrecken-Segmente, Umlenk-/Motorstation -
# siehe csv:element-proxies-erzeugen, export.lsp): ihre Stueckzahl steckt
# bereits in den GF_Bogen_*/VF_Bogen_*/ANZAHL_VF/ANZAHL_GF-Attributen des
# Wrappers (*strecke-attr-gruppe*, ssg_core.lsp) - ein eigener Sivas-Eintrag
# wuerde ebenso doppelt zaehlen. Muss VOR der pattern_strecke_module-Pruefung
# unten greifen, sonst faengt deren generisches "ILS 2.0 Strecke"-Sammeln
# diese Kopien zuerst ab.
# siehe csv:element-proxies-erzeugen, export.lsp; nur im Rohdaten-Modus
# *csv-export-alle-elemente*=T ueberhaupt in export_raw.json vorhanden) sowie
# fuer die gruppierten Foerdereinheit-/Gefaelleeinheit-Marker (ZZ_UNIT_MARKER,
# csv:unitmark-aktualisieren, Default-Modus): ihre Stueckzahl steckt bereits
# in den GF_Bogen_*/VF_Bogen_*/ANZAHL_VF/ANZAHL_GF-Attributen des Wrappers
# (*strecke-attr-gruppe*, ssg_core.lsp) - ein eigener Sivas-Eintrag wuerde
# ebenso doppelt zaehlen. Muss VOR der pattern_strecke_module-Pruefung unten
# greifen, sonst faengt deren generisches "ILS 2.0 Strecke"-Sammeln diese
# Kopien zuerst ab.
SEP_SCANNER_PATTERNS = (
BLOCKPATTERNS.get("pattern_separator", []) + BLOCKPATTERNS.get("pattern_scanner", [])
+ BLOCKPATTERNS.get("pattern_nested_bogen", [])
+ BLOCKPATTERNS.get("pattern_nested_streckenelement", [])
+ BLOCKPATTERNS.get("pattern_nested_umlenkstation", [])
+ BLOCKPATTERNS.get("pattern_nested_motorstation", [])
+ BLOCKPATTERNS.get("pattern_unit_marker", [])
)
# Sivas-Nummern und Bezeichnungen je ILS-Elementtyp
+81
View File
@@ -410,6 +410,87 @@
(csv:element-teileart "Vario_Motorstation_500mm_rechts"))
(tu-eq "csv:element-teileart/unbekannt" nil
(csv:element-teileart "AS_Element_90_links"))
;; --- csv:sepliste-partition: sepliste in Foerdereinheit-/Gefaelleeinheit-
;; Spannen aufteilen (Lauflaengen-Kodierung ueber den effektiven Typ) -----
;; Reine Rechenfunktion auf (typ lfdnr strecke-typ welt-pt)-Tupeln, hier
;; ohne Zeichnung testbar (welt-pt wird von der Partitionierung selbst
;; nicht gebraucht, darum ueberall nil).
;; HundM05 VF_1-Beispiel (echt verifiziert): AS, SEP2=VF, SEP3=GF, SEP4=GF,
;; SEP5=VF, ES -> 3 Einheiten VF,GF,VF. SEP3+SEP4 bracketen GEMEINSAM eine
;; einzige Gefaelleeinheit (nicht zwei separate).
(setq _sep-hundm05
'(("AS" 1 nil nil) ("SEP" 2 "VF" nil) ("SEP" 3 "GF" nil)
("SEP" 4 "GF" nil) ("SEP" 5 "VF" nil) ("ES" 6 nil nil)))
(tu-eq "csv:sepliste-partition/hundm05"
'(("VF" (1 2)) ("GF" (3 4)) ("VF" (5 6)))
(csv:sepliste-partition _sep-hundm05))
;; Minimalfall: nur AS-SEP(VF)-ES, kein Typwechsel -> 1 Foerdereinheit ueber
;; die ganze Kette.
(setq _sep-minimal '(("AS" 1 nil nil) ("SEP" 2 "VF" nil) ("ES" 3 nil nil)))
(tu-eq "csv:sepliste-partition/minimal"
'(("VF" (1 2 3)))
(csv:sepliste-partition _sep-minimal))
;; Einzel-GF-Fall: die Gefaelleeinheit wird nur von EINEM SEP (nicht zwei)
;; UND dem nachfolgenden ES bracketiert.
(setq _sep-einzel-gf
'(("AS" 1 nil nil) ("SEP" 2 "VF" nil) ("SEP" 3 "GF" nil) ("ES" 4 nil nil)))
(tu-eq "csv:sepliste-partition/einzel-gf"
'(("VF" (1 2)) ("GF" (3 4)))
(csv:sepliste-partition _sep-einzel-gf))
;; 3+ gleichtypige SEP-Eintraege hintereinander muessen weiterhin EINE
;; Einheit ergeben (nicht mehrere) - Lauflaengen-Regel, kein Sonderfall.
(setq _sep-dreifach-gf
'(("AS" 1 nil nil) ("SEP" 2 "VF" nil) ("SEP" 3 "GF" nil)
("SEP" 4 "GF" nil) ("SEP" 5 "GF" nil) ("SEP" 6 "VF" nil) ("ES" 7 nil nil)))
(tu-eq "csv:sepliste-partition/dreifach-gf"
'(("VF" (1 2)) ("GF" (3 4 5)) ("VF" (6 7)))
(csv:sepliste-partition _sep-dreifach-gf))
;; Kette beginnend mit GF (AS erbt den Typ des ersten SEP-Eintrags, hier GF
;; statt des sonstigen Default "VF").
(setq _sep-startet-gf
'(("AS" 1 nil nil) ("SEP" 2 "GF" nil) ("ES" 3 nil nil)))
(tu-eq "csv:sepliste-partition/startet-gf"
'(("GF" (1 2 3)))
(csv:sepliste-partition _sep-startet-gf))
;; --- csv:dot2: 2D-Skalarprodukt ---
(tu-eqf "csv:dot2/achsen" 0.0 (csv:dot2 '(1.0 0.0) '(0.0 1.0)))
(tu-eqf "csv:dot2/parallel" 6.0 (csv:dot2 '(2.0 0.0) '(3.0 0.0)))
;; --- csv:element-unit-zuordnung: Elemente per Kettenrichtungs-Projektion
;; den Einheiten-Spannen zuordnen und INNERHALB jeder Spanne nach der
;; Projektion sortieren -----------------------------------------------
;; Gerade Kette AS(0,0) -SEP2(VF, 10,0)- SEP3(GF, 20,0) -ES(30,0)- entlang
;; der X-Achse. unit-spans: VF=[1,2], GF=[2,3] (SEP2 gehoert zu beiden
;; Spannen als Grenzmarker, siehe csv:sepliste-partition).
(setq _sep-proj
'(("AS" 1 nil (0.0 0.0 0.0)) ("SEP" 2 "VF" (10.0 0.0 0.0))
("SEP" 3 "GF" (20.0 0.0 0.0)) ("ES" 4 nil (30.0 0.0 0.0))))
(setq _spans-proj (csv:sepliste-partition _sep-proj))
(tu-eq "csv:sepliste-partition/proj-spans" '(("VF" (1 2)) ("GF" (3 4))) _spans-proj)
;; Zwei Elemente: eines bei x=5 (klar in der VF-Spanne [0,10]), eines bei
;; x=17 (in der Luecke zwischen den Spannen-Grenzpunkten [10,20], aber
;; naeher an der GF-Grenze 20 als an der VF-Grenze 10 - eindeutig GF, kein
;; Gleichstand wie bei x=15 in der Luecken-Mitte).
(setq _elem-vf (list 'PROXY1 'WRAPPER "STRECKENELEMENT" '(5.0 0.0 0.0)))
(setq _elem-gf (list 'PROXY2 'WRAPPER "STRECKENELEMENT" '(17.0 0.0 0.0)))
(setq _zuord (csv:element-unit-zuordnung _spans-proj _sep-proj (list _elem-vf _elem-gf)))
(tu-eq "csv:element-unit-zuordnung/vf-treffer" (list _elem-vf) (nth 0 _zuord))
(tu-eq "csv:element-unit-zuordnung/gf-treffer" (list _elem-gf) (nth 1 _zuord))
;; Sortierung innerhalb einer Spanne: zwei Elemente in der VF-Spanne,
;; absichtlich in "falscher" Reihenfolge uebergeben - das Ergebnis muss
;; nach x sortiert sein (frueheres x zuerst).
(setq _elem-a (list 'PROXY_A 'WRAPPER "BOGEN" '(8.0 0.0 0.0)))
(setq _elem-b (list 'PROXY_B 'WRAPPER "BOGEN" '(2.0 0.0 0.0)))
(setq _zuord2 (csv:element-unit-zuordnung _spans-proj _sep-proj (list _elem-a _elem-b)))
(tu-eq "csv:element-unit-zuordnung/sortiert" (list _elem-b _elem-a) (nth 0 _zuord2))
)