export: jeden Wert nur an einer Stelle setzen (ZUORDNUNG, sepliste-IDs, Typen, K1/K2)

- ZUORDNUNG: ssg-id-check-all laeuft jetzt ZUERST, danach sind die
  Wrapper-IDs endgueltig - Separator-Kopien, AS/ES- und Einheiten-Marker
  bekommen ZUORDNUNG direkt beim Erzeugen. Entfallen:
  csv:sep-proxies-zuordnung-setzen, csv:asmark-ids-setzen,
  csv:unitmark-ids-setzen. zuordnung_fix/*cs-sep-fix-by-handle* bleibt
  (count_sep_scan rechnet eigenstaendige Sensoren bewusst neu).
- sepliste-IDs: Zuordnung ueber die KS-Kettenfolge je Typ
  (csv:sepliste-kette-paare) statt ueber die teils gespiegelten
  sepliste-Koordinaten (csv:sepliste-paare, csv:sepliste-ids-setzen
  entfallen). Die Kette wird je Lauf einmal berechnet (csv:kette-von);
  die ID liest csv:block-to-json erst beim Schreiben. Behebt vertauschte
  TrackIds/Nachbarn in HundM05 VF_1 (0031/0029).
- Element-Klassifizierung: eine Tabelle csv:element-familien (cfg-Muster)
  fuer Suche und Klassifizierung; csv:proxy-typ entfaellt.
- Einheiten-Marker: K1/K2 nur noch in *csv-baustein-by-handle*
  (csv:geometrie-merken), K1/K2-Attribute und
  csv:unitmark-k-kos-strings entfallen.

Geprueft: HundM05-Export alt/neu headless verglichen - einzige Abweichung
ist die korrigierte Reihenfolge in VF_1. LISP-Unit-Tests 303/303.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Samer Ayadi
2026-09-28 12:10:16 +02:00
co-authored by Claude Opus 5.5
parent e67f7cc1aa
commit 1e6b152d72
10 changed files with 416 additions and 642 deletions
+48 -47
View File
@@ -43,7 +43,14 @@ Datei: `Lisp/export.lsp`. `c:EXPORTCSV` ruft `csv:run-export` mit
`include-bbox=T` auf (im Unterschied zu `c:EXPORTSIVAS`, das keine
Bounding-Boxen braucht).
1. **`csv:sep-proxies-erzeugen`** — Separator_SP-Symbole, die beim
Jeder Wert wird dabei genau EINMAL gesetzt; die Reihenfolge sorgt dafuer,
dass er beim Setzen schon feststeht (`csv:export-vorbereiten`):
1. **`ssg-id-check-all`** — ZUERST, noch bevor ein Stellvertreter existiert:
vergibt fehlende IDs und korrigiert Duplikate. Danach ist die ID jedes
Wrappers (`VF_n`/`GF_n`/`KREISEL_n`) endgueltig, die folgenden Schritte
koennen `ZUORDNUNG` direkt beim Erzeugen setzen.
2. **`csv:sep-proxies-erzeugen`** — Separator_SP-Symbole, die beim
Zusammenbau von `KREISEL_n`/`ECKRAD_n`/`VF_n`/`GF_n` in die
Compound-Blockdefinition verpackt wurden, sind fuer `(ssget "X" ...)`
unsichtbar. Diese Funktion fuegt fuer jeden Fund eine ECHTE, temporaere
@@ -51,32 +58,30 @@ Bounding-Boxen braucht).
durchlaeuft danach ID-Vergabe und Export ganz normal ueber den freien-
Separator-Pfad (Blockname-Muster `Separator_SP*` trifft zu) und bekommt
dadurch eine echte `vla-getboundingbox`-Box, die das verpackte Original nie
haben koennte. Rueckgabe: Liste von Records
`(proxy-ename wrapper-ename typ (x y z))`.
1b. **`csv:asmark-aktualisieren`** — die AS-/ES-Enden derselben Kette
haben koennte. Dabei bekommt die Kopie `ZUORDNUNG = Wrapper-ID` (bekannt,
nicht geraten) und wird in `*cs-sep-fix-by-handle*` als "bekannt"
vermerkt: `count_sep_scan.lsp` und `export_csv.py` (Feld `zuordnung_fix`)
berechnen die Zuordnung EIGENSTAENDIGER Sensoren bei jedem Lauf
geometrisch neu und nehmen verpackte Separatoren damit davon aus.
Rueckgabe: Liste von Records `(proxy-ename wrapper-ename "SEP" (x y z)
trennsteg-ent)`.
2b. **`csv:asmark-aktualisieren`** — die AS-/ES-Enden derselben Kette
(`AS_Element*`/`ES_Element*`, Muster `pattern_schleuselement`) bekommen
KEINE Kopie des Elements, sondern einen eigenen (ebenfalls temporaeren) Marker — siehe eigener
Abschnitt "Der AS/ES-Marker" unten. Beide Wege teilen sich die
Platzierungsrechnung (`csv:nested-welt-records` liefert die Weltlage jedes
verpackten Fundes, `csv:block-einfuegen-welt` setzt ein Objekt dorthin).
2. **`ssg-id-check-all`** — vergibt fehlende IDs (inkl. der frisch erzeugten
Proxies) und korrigiert Duplikate. Muss NACH den Proxies laufen, sonst
wuerden diese Kopien numerisch nie erfasst. `ssg-id-collect-blocks`
(`ssg_id.lsp`) erfasst dafuer auch `AS_Element*`/`ES_Element*`.
3. **`csv:sep-proxies-zuordnung-setzen`** — jetzt steht die ID jedes Wrappers
fest: die Proxy-Kopie bekommt `ZUORDNUNG = Wrapper-ID` fest zugewiesen
(nicht geraten) und wird zusaetzlich in `*cs-sep-fix-by-handle*` vermerkt,
damit `count_sep_scan.lsp` diesen Wert spaeter nicht per Geometrie
ueberschreibt. Nur fuer die Separator-Kopien — AS/ES sind keine Sensoren.
3b. **`csv:asmark-ids-setzen` + `csv:sepliste-ids-setzen`** — ebenfalls erst
jetzt moeglich: jedes Bauteil wird seinem `sepliste`-Eintrag zugeordnet und
seine ID unter `*csv-sepliste-id-by-handle*` (Key = Wrapper-Handle)
abgelegt; `csv:block-to-json` schreibt sie als Feld `id` in den Eintrag.
AS/ES laufen ueber die Marker (die ihre ID schon kennen, die Zuordnung
steht im Marker-Attribut `LFDNR`), SEP ueber die temporaeren Kopien und
`csv:sepliste-paare` — siehe eigener Abschnitt unten. `csv:asmark-ids-setzen`
traegt zusaetzlich die `ZUORDNUNG` (Wrapper-ID) am Marker nach; ohne sie
faende der naechste Export den Marker nicht wieder.
KEINE Kopie des Elements, sondern einen eigenen (ebenfalls temporaeren)
Marker, mit `ZUORDNUNG` direkt beim Erzeugen — siehe eigener Abschnitt
"Der AS/ES-Marker" unten.
2c. **`csv:element-proxies-erzeugen`** — nur Modus `all_elements`: Kopien der
uebrigen Kettenbausteine (Boegen, Streckenelemente, Umlenk-/Motorstation).
3. **`csv:ketten-verarbeiten`** — je `VF_n`/`GF_n` die KS-geordnete Kette
(`csv:kette-von`, einmal je Lauf berechnet) auswerten: Einheiten-Proxies
(Modus `group_vf_gf`), IDs in Kettenfolge, Separator-Typ VF/GF
(`csv:kette-sep-typen`) und die Stellvertreter der `sepliste`-Eintraege
(`csv:sepliste-kette-paare`, siehe eigener Abschnitt unten). Alles aus
DERSELBEN Kette - Einheiten-BB, Separator-Typ und TrackIds passen damit
zusammen.
3b. **`ssg-id-check-all`** (zweiter Lauf) — Restnummerierung (z.B. Kreisel-
Separator ohne Kette) und Duplikat-Korrektur unter den neuen
Stellvertretern; danach `csv:res-ids-schreiben` (reservierte IDs).
4. **`omni:update-all-attribs`** — Hoehe/Drehung aller Omniflo-Elemente vor
dem Export aktualisieren.
5. **`count_sep_scan.lsp::cs-zuordnung-lauf`** (nur bei Bedarf, siehe
@@ -229,8 +234,9 @@ zu zaehlen.
### Das Feld `id` je sepliste-Eintrag
Seit `csv:sepliste-ids-setzen` traegt JEDER Eintrag zusaetzlich die `id` des
realen Bauteils, das ihn in der Zeichnung vertritt:
JEDER Eintrag traegt zusaetzlich die `id` des realen Bauteils, das ihn in der
Zeichnung vertritt (zugeordnet in `csv:ketten-verarbeiten`, die ID selbst
liest `csv:block-to-json` erst beim Schreiben vom Stellvertreter):
```json
{"typ": "AS", "lfdnr": 1, "x": 5070.1, "y": 1199.2, "id": "0031"}
@@ -286,26 +292,21 @@ jeder, der keinem Kettenende mehr entspricht.
Im Export ist der Marker eine eigene Zeile (`pattern_ases_marker` in
`cfg/export.cfg`) und liefert die `id` des AS-/ES-Eintrags in der `sepliste`.
**Zuordnung Eintrag -> Kopie (`csv:sepliste-paare`).** Verglichen wird nur
innerhalb desselben Typmarkers (`AS`/`ES`/`SEP`) und EXKLUSIV: alle Paare
eines Typs werden nach Abstand sortiert, das kuerzeste zuerst genommen, danach
sind Eintrag UND Kopie verbraucht. Zwei Gruende gegen das naheliegende "je
Eintrag die naechste Kopie":
**Zuordnung Eintrag -> Bauteil (`csv:sepliste-kette-paare`).** Ueber die
REIHENFOLGE je Typ: das k-te `AS`/`SEP`/`ES` der sepliste (nach `lfdnr`) ist
das k-te `AS`/`SEP`/`ES` der KS-geordneten Kette (`csv:kette-von`). Die
sepliste entsteht beim Bau per `entnext`-Wanderung in Baureihenfolge, und
gebaut wird in Foerderrichtung - dieselbe Folge, die die KS-Kette
rekonstruiert. Die x/y-Koordinaten der sepliste werden dafuer NICHT benutzt.
- waehlen zwei Eintraege dieselbe Kopie, bleibt eine Kopie ohne Eintrag und
ein Eintrag ohne ID (genau dieses Muster verursacht in Python die
Separatoren ohne `Nachbarn`/`TrackIds`);
- EIN fehlerhafter Koordinatenwert in der sepliste (bekannter Fall: gespiegeltes
`x` bei `lfdnr=2`) verschiebt so keine anderen Zuordnungen mehr — alle
uebrigen Eintraege finden ihre Kopie ueber den kurzen Abstand zuerst, der
fehlerhafte bekommt den Rest zugeteilt. Liegt dieser Rest weiter als
`csv:sepliste-abstand-warnung-mm` (500 mm) entfernt, meldet der Export das
(`exp-sepliste-abstand`), ohne die Zuordnung zu verwerfen.
Die Sortierung ist eine strikte Totalordnung (Abstand, dann `lfdnr`, dann
Index) — `vl-sort` wirft Elemente weg, die unter der Vergleichsfunktion gleich
sind, zwei Paare mit identischem Abstand wuerden sonst zu einem verschmelzen.
Getestet in `tests/test_unit.lsp` (`csv:sepliste-paare/*`, zeichnungsfrei).
Frueher lief die Zuordnung ueber den kuerzesten Abstand zwischen sepliste-
Koordinate und Kopie (`csv:sepliste-paare`). Die sepliste-Koordinaten sind
aber teils gespiegelt (bekannter Fall: `x` bei `lfdnr=2`) - das vertauschte
real zwei Separatoren von HundM05 `VF_1` in TrackIds und Nachbarn. Passen
sepliste und Kette in der Anzahl nicht zusammen, meldet der Export das
(`exp-sepliste-ohne-proxy`); die ueberzaehligen Eintraege bleiben ohne `id`.
Getestet in `tests/test_unit.lsp` (`csv:sepliste-kette-paare/*`,
zeichnungsfrei).
## Phase 2 (Python): `lib/export_csv.py::process_blocks`