doc: CSV-Export-Ablauf dokumentiert (LISP-Sammlung + Python-Nachbarschaftslogik)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,287 @@
|
|||||||
|
# CSV-Export (EXPORTCSV) — Ablauf und Architektur
|
||||||
|
|
||||||
|
Beschreibt den kompletten Weg vom `EXPORTCSV`-Befehl in BricsCAD bis zur
|
||||||
|
fertigen CSV: welche Daten LISP sammelt, was Python daraus berechnet, und wie
|
||||||
|
die "Nachbarn"-Spalte zustande kommt. Stand: `Lisp/export.lsp`,
|
||||||
|
`lib/export_csv.py`, `lib/export_neighbors.py`.
|
||||||
|
|
||||||
|
## Ueberblick: zwei Phasen, eine JSON-Datei dazwischen
|
||||||
|
|
||||||
|
```
|
||||||
|
BricsCAD (LISP) Python
|
||||||
|
───────────────── ──────
|
||||||
|
c:EXPORTCSV
|
||||||
|
-> csv:run-export
|
||||||
|
-> Proxies fuer verpackte
|
||||||
|
Separatoren einfuegen
|
||||||
|
-> IDs vergeben (ssg-id-check-all)
|
||||||
|
-> Separator-/Scanner-Zuordnung
|
||||||
|
(count_sep_scan.lsp)
|
||||||
|
-> REGENALL
|
||||||
|
-> csv:collect-export-blocks export_raw.json lib/export_csv.py::main
|
||||||
|
-> je Block csv:block-to-json ───────────────> -> process_blocks
|
||||||
|
-> Nachbarschaft/
|
||||||
|
Umlauf/Zuordnung
|
||||||
|
-> CSV schreiben
|
||||||
|
```
|
||||||
|
|
||||||
|
LISP sammelt reine Zeichnungsdaten (Position, Rotation, Attribute,
|
||||||
|
Bounding-Box, Anschluss-Koordinatensysteme) und schreibt sie als flaches
|
||||||
|
JSON-Array (`results/export_raw.json`). Python bekommt keine BricsCAD-API zur
|
||||||
|
Verfuegung — es rechnet ausschliesslich mit den Zahlen aus diesem JSON. Alles,
|
||||||
|
was "Nachbar X kennt Nachbar Y" bedeutet, wird also in Python aus Geometrie
|
||||||
|
(Bounding-Box-Ueberschneidung, Distanz, Winkel) oder aus mitgelieferten
|
||||||
|
Reihenfolge-Daten (sepliste, siehe unten) rekonstruiert — LISP selbst kennt
|
||||||
|
diese Beziehungen beim Bauen der Einzelketten nicht.
|
||||||
|
|
||||||
|
## Phase 1 (LISP): `csv:run-export` — was vor dem JSON passiert
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
Kopie als top-level INSERT an derselben Weltposition ein. Die Kopie
|
||||||
|
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 `(proxy-ename . wrapper-ename)`.
|
||||||
|
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.
|
||||||
|
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.
|
||||||
|
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
|
||||||
|
`cs-zuordnung-noetig-p`) — ordnet frei platzierte Separator/Scanner-Symbole
|
||||||
|
per Bounding-Box-Ueberschneidung bzw. Distanz einem Kreisel/VF/GF zu und
|
||||||
|
schreibt `ANZAHL_SEPARATOR`/`ANZAHL_SCANNER` an den Carrier.
|
||||||
|
6. **`REGENALL`** — erzwingt eine vollstaendige Regeneration der Zeichnung.
|
||||||
|
Notwendig, weil `vla-getboundingbox` sonst in der Praxis fehlschlagen kann
|
||||||
|
(realer Fall: ein Export ohne vorheriges REGENALL lieferte fuer ALLE 75
|
||||||
|
Bloecke keine Bounding-Box). `csv:get-bbox` ruft zusaetzlich pro Objekt
|
||||||
|
`vla-update` auf, das allein reichte in diesem Fall nicht aus.
|
||||||
|
7. **`csv:collect-export-blocks`** — sammelt alle relevanten top-level
|
||||||
|
INSERTs (Kreisel, Omniflo, VF/GF-Wrapper, Separator/Scanner,
|
||||||
|
BTMT-Stationen) ueber die Blockname-Muster aus `cfg/export.cfg`
|
||||||
|
`[blockpattern]`.
|
||||||
|
8. **`csv:block-to-json`** je Fund — siehe naechster Abschnitt.
|
||||||
|
|
||||||
|
### `csv:block-to-json` — Inhalt eines JSON-Eintrags
|
||||||
|
|
||||||
|
Pro INSERT wird ein JSON-Objekt geschrieben:
|
||||||
|
|
||||||
|
| Feld | Quelle | Bemerkung |
|
||||||
|
|---|---|---|
|
||||||
|
| `block_name`, `layer`, `handle` | direkt vom INSERT | |
|
||||||
|
| `x`, `y`, `z`, `rotation` | `(assoc 10)` / `(assoc 50)` | roher Einfuegepunkt/-winkel, KEIN `vla-getboundingbox` |
|
||||||
|
| `attribs` | alle ATTRIB-Werte | |
|
||||||
|
| `insertpoint` | `csv:kos-encode` | Position+Rotation als 24-Zeichen-Base64 (Quaternion) |
|
||||||
|
| `k1`..`k4` | `csv:get-k-kos-strings` bzw. Fallbacks | Anschluss-Koordinatensysteme, siehe unten |
|
||||||
|
| `bbox` | `csv:get-bbox` (`vla-getboundingbox`) | NUR wenn `include-bbox=T` und der Aufruf gelingt; fehlt sonst komplett im JSON |
|
||||||
|
| `warnung` | `*cs-scanner-warnung-by-handle*` | strittige Scanner-Zuordnung aus `cs-zuordnung-lauf` |
|
||||||
|
| `zuordnung_fix` | `*cs-sep-fix-by-handle*` | nur bei Proxy-Kopien: die feststehende Wrapper-ID |
|
||||||
|
| `sepliste` | `ssg-sepliste-xdata-lesen` | nur bei VF_n/GF_n, siehe eigener Abschnitt |
|
||||||
|
|
||||||
|
**K1-K4 je Blocktyp:** normale ILS-Bloecke fuehren echte K1-K4-Sub-Bloecke.
|
||||||
|
Omniflo-Geraden (`AP110*`) haben keine echten K-Bloecke — K1/K2 werden aus
|
||||||
|
Laenge+Rotation synthetisiert (`csv:gerade-k-kos-strings`). VF_n/GF_n leiten
|
||||||
|
K1 (Eingang) und K2 (Ausgang) aus den eingebetteten AS_Element/ES_Element ab
|
||||||
|
(`csv:vfgf-k-kos-strings`), damit die Kreisel<->Strecke-Kollisionspruefung in
|
||||||
|
Python mit einer kleinen festen Box an der AS/ES-Position arbeiten kann statt
|
||||||
|
mit der ganzen (oft sehr langen) Wrapper-Box.
|
||||||
|
|
||||||
|
**Die `bbox` ist das einzige Feld, das `vla-getboundingbox` braucht** — alle
|
||||||
|
anderen Felder kommen aus rohen DXF-Gruppencodes oder XDATA. Ein Ausfall von
|
||||||
|
`vla-getboundingbox` zeigt sich also NICHT im ganzen JSON-Eintrag, sondern nur
|
||||||
|
im fehlenden `bbox`-Objekt. `csv:get-bbox` protokolliert einen Fehlschlag seit
|
||||||
|
Kurzem in `*csv-bbox-fail-count*`/`*csv-bbox-fail-first*`; `csv:run-export`
|
||||||
|
meldet nach dem JSON-Schreiben eine Warnung (`exp-bbox-fail`), falls das
|
||||||
|
vorkam.
|
||||||
|
|
||||||
|
### Die `sepliste` — Reihenfolge-Information einer VF_n/GF_n-Kette
|
||||||
|
|
||||||
|
Waehrend eine Gefaellestrecke (`GEFAELLESTRECKE`) oder ein VarioFoerderer
|
||||||
|
(`FOERDERANLAGE`) gebaut wird, sammelt `ssg-sepliste-sammeln`
|
||||||
|
(`Lisp/ssg_ks_insert.lsp`) alle currently frisch erzeugten `AS_Element_*`-,
|
||||||
|
`Staustrecke_Separator_SP_300_mm*`- und `ES_Element_*`-Sub-INSERTs in
|
||||||
|
Baureihenfolge und schreibt sie als XDATA (App `SSG_VF_SEP`/`SSG_GF_SEP`) auf
|
||||||
|
den fertigen Wrapper: `[(typ lfdnr x y), ...]` mit `typ` = `"AS"`/`"SEP"`/
|
||||||
|
`"ES"`, `lfdnr` 1-basiert. Das ist reine Bauabfolge, keine Weltkoordinate im
|
||||||
|
strengen Sinn — je nach Bauphase koennen einzelne Eintraege noch im
|
||||||
|
Baukoordinatensystem stehen, bevor die Kette an ihre finale Position
|
||||||
|
verschoben wurde (siehe Abschnitt "sepliste-Koordinaten" unten).
|
||||||
|
|
||||||
|
Die sepliste beschreibt NUR die Reihenfolge innerhalb einer Kette
|
||||||
|
(AS -> Sep1 -> ... -> SepN -> ES). Sie sagt nichts darueber, an welchen
|
||||||
|
Kreisel die Kette andockt — das ergibt sich erst aus der geometrischen Lage
|
||||||
|
in der fertigen Zeichnung (siehe Python-Teil).
|
||||||
|
|
||||||
|
**Wichtig:** die in einer Kette verpackten Separatoren sind KEIN doppeltes
|
||||||
|
Konzept zur sepliste — es sind dieselben physischen Symbole, nur aus zwei
|
||||||
|
Blickwinkeln: `csv:sep-proxies-erzeugen` liefert sie als echte, exportierbare
|
||||||
|
INSERT-Kopie (mit Bounding-Box und ID); die sepliste liefert dieselben
|
||||||
|
Separatoren als Eintraege mit Vorgaenger-/Nachfolger-Wissen. Python fuehrt
|
||||||
|
beides zusammen (siehe `map_separator_kette_items` unten) statt sie doppelt
|
||||||
|
zu zaehlen.
|
||||||
|
|
||||||
|
## Phase 2 (Python): `lib/export_csv.py::process_blocks`
|
||||||
|
|
||||||
|
Aufruf: `python export_csv.py <export_raw.json> <data_dir> <output.csv>`.
|
||||||
|
`main()` laedt das JSON, den Omniflo-Katalog (Boegen/Weichen) und ruft
|
||||||
|
`process_blocks(blocks, lookup)` auf, das die eigentliche Item-Liste baut.
|
||||||
|
|
||||||
|
### 1. Blöcke klassifizieren
|
||||||
|
|
||||||
|
Eine grosse Schleife über alle rohen JSON-Blöcke ordnet jeden Block anhand
|
||||||
|
der `cfg/export.cfg [blockpattern]`-Muster einer TeileArt zu (Kreisel,
|
||||||
|
Eckrad, Omniflo Bogen/Weiche/Gerade, VF-Strecke, Gefaellestrecke,
|
||||||
|
Strecke-Modul, Separator, Scanner, BTMT Be-/Entladung) und baut daraus ein
|
||||||
|
Item-dict mit `teileart`, `teileid`, `bezeichnung`, `planquadrat`,
|
||||||
|
`merkmale` und den bbox-/KOS-Spalten (`bbox_columns`). VF_n/GF_n-Items
|
||||||
|
bekommen dabei zusaetzlich `x`/`y` (Kettenanfang, fuer die Schleuselement-
|
||||||
|
Berechnung) und `sepliste` (fuer die Ketten-Nachbarschaft) durchgereicht.
|
||||||
|
|
||||||
|
Separator/Scanner-Items ohne Bounding-Box (weil `vla-getboundingbox`
|
||||||
|
scheiterte) bekommen einen Fallback: `_fallback_separator_bbox` baut aus der
|
||||||
|
festen Symbol-Groesse (`cfg/export.cfg [Boundingbox] separator_box_*`,
|
||||||
|
Default 210x150x14mm) und der bekannten Position/Rotation eine synthetische
|
||||||
|
`_bbox`, damit der Separator trotzdem an der Nachbarschaftserkennung
|
||||||
|
teilnimmt.
|
||||||
|
|
||||||
|
### 2. `compute_neighbor_ids` — die generische Bounding-Box-Nachbarschaft
|
||||||
|
|
||||||
|
Datei `lib/export_neighbors.py`. Testet drei Gruppen gegeneinander (NICHT
|
||||||
|
alle Elemente gemeinsam — das waere bei grossen Omniflo-Anlagen zu teuer bzw.
|
||||||
|
fachlich falsch):
|
||||||
|
|
||||||
|
1. Kreisel/Eckrad gegen Kreisel/Eckrad. Ein echter Kreisel wird dafuer in
|
||||||
|
eine linke und rechte Haelfte gesplittet (`kreisel_half_bboxes`) — die
|
||||||
|
Trennlinie ist die Achse durch Antriebs- und Spannstation.
|
||||||
|
2. Kreisel/Eckrad gegen Gefaellestrecke/Foerderer/Strecke-Modul. Fuer
|
||||||
|
GF/VF wird dabei NICHT die grosse Wrapper-Box getestet, sondern je eine
|
||||||
|
kleine feste Box an der AS- (K1) und ES-Position (K2) — nur dort beruehrt
|
||||||
|
eine Strecke tatsaechlich einen Kreisel.
|
||||||
|
3. Omniflo-Elemente gegen Omniflo-Elemente (Bogen/Weiche/Gerade), per
|
||||||
|
STRtree-Broad-Phase + KOS-Verfeinerung ueber K1-K4.
|
||||||
|
|
||||||
|
Ergebnis ist die rohe `Nachbarn`-Spalte fuer Kreisel/Eckrad/GF/VF (fuer
|
||||||
|
Separator/Scanner bewusst NICHT — die werden unten anders behandelt) sowie
|
||||||
|
die `Fehler`-Spalte (`compute_neighbor_errors`: "unverbunden"/"nur ein
|
||||||
|
Partner", je nach Mindestanzahl Partner pro TeileArt).
|
||||||
|
|
||||||
|
### 3. Synthetische Zeilen an den Uebergangsstellen
|
||||||
|
|
||||||
|
Drei Arten von "Zeilen ohne eigene Zeichnungsgeometrie" werden aus der
|
||||||
|
Geometrie der bereits klassifizierten Items abgeleitet:
|
||||||
|
|
||||||
|
- **`compute_kreisel_touch_switches`** — praezise Kapsel-Geometrie (nicht nur
|
||||||
|
Bounding-Box) findet jeden Punkt, an dem sich zwei echte Kreisel wirklich
|
||||||
|
beruehren, und erzeugt dort eine `"ILS Weiche"`-Zeile
|
||||||
|
(`build_kreisel_weiche_items`). Das ist der Verzweigungspunkt, an dem
|
||||||
|
Material von einem Kreisel zum anderen wechseln kann.
|
||||||
|
- **`compute_strecke_kreisel_schleus`** — an jedem Streckenende (AS/ES,
|
||||||
|
ueber K1/K2 bzw. als Fallback die Wrapper-Enden), das einen Kreisel
|
||||||
|
beruehrt (siehe Schritt 2), entsteht ein `"ILS Ausschleuselement"`
|
||||||
|
(Anfang) bzw. `"ILS Einschleuselement"` (Ende), leicht zum Kreisel hin
|
||||||
|
versetzt (`build_strecke_schleus_items`).
|
||||||
|
- **`map_separator_kette_items`** — ordnet jedem `"SEP"`-Eintrag einer
|
||||||
|
sepliste das dazu passende, bereits vorhandene Separator-Item zu (siehe
|
||||||
|
naechster Abschnitt). Erzeugt bewusst KEINE eigenen Zeilen.
|
||||||
|
|
||||||
|
### 4. Der Kreisel-Umlauf — wie Nachbarn ueber einen Kreisel hinweg entstehen
|
||||||
|
|
||||||
|
`compute_kreisel_umlauf` ist der zentrale Baustein, der Ketten-Enden,
|
||||||
|
freie Separatoren und Kreisel-Kreisel-Weichen zu einer widerspruchsfreien
|
||||||
|
Nachbarschaft zusammenfuehrt. Fuer jeden echten Kreisel werden alle Punkte
|
||||||
|
gesammelt, die dort geometrisch liegen:
|
||||||
|
|
||||||
|
- freie Separatoren, deren Bounding-Box die Kreisel-Box ueberschneidet,
|
||||||
|
- die Kreisel-Kreisel-Weichen aus Schritt 3 (in BEIDEN beteiligten
|
||||||
|
Kreisel-Umlaeufen, da eine Weiche der gemeinsame Verzweigungspunkt ist —
|
||||||
|
man kann von Kreisel A nach B und zurueck),
|
||||||
|
- die AS/ES-Enden andockender VF_n/GF_n-Ketten (aus der `sepliste`,
|
||||||
|
zugeordnet zum geometrisch naechstgelegenen Kreisel).
|
||||||
|
|
||||||
|
Alle Punkte werden nach Winkel um die Kreiselachse sortiert (Richtung ueber
|
||||||
|
das `DREHRICHTUNG`-Attribut, UZS/GUZ) — das ergibt eine geschlossene,
|
||||||
|
zirkulaere Reihenfolge. Vorgaenger/Nachfolger in dieser Reihenfolge werden
|
||||||
|
zu `Nachbarn`.
|
||||||
|
|
||||||
|
Die Matching-Box eines Kreisels ist dabei die tatsaechliche, um die Drehung
|
||||||
|
korrigierte Kapsel-Bounding-Box (nicht "Laenge entlang Welt-X" angenommen) —
|
||||||
|
ein 90 Grad gedrehter, langer Kreisel haette sonst eine falsch orientierte
|
||||||
|
(zu breite/zu schmale) Box und faengt Punkte faelschlich ein oder verpasst
|
||||||
|
echte Nachbarn.
|
||||||
|
|
||||||
|
### 5. Ketteninterne Nachbarn — `compute_sep_kette` + Aufloesung
|
||||||
|
|
||||||
|
`compute_sep_kette` liest jede `sepliste` und baut daraus einen Schluessel
|
||||||
|
je Eintrag (`"<strecke_id>#<lfdnr>"`) mit `vorgaenger`/`nachfolger`
|
||||||
|
(wieder solche Schluessel, `None` am Kettenende). Das ist eine reine
|
||||||
|
Uebersetzung der LISP-Baureihenfolge — kein Rateverfahren.
|
||||||
|
|
||||||
|
**Warum die Zeilen erst NACH allen Schluesseln aufgeloest werden:** ein
|
||||||
|
Schluessel kann auf eine Zeile zeigen, die zum Zeitpunkt seiner Vergabe noch
|
||||||
|
gar nicht existiert (eine SEP in der Mitte einer Kette hat ihre AS als
|
||||||
|
Vorgaenger, aber die AS-Zeile entsteht erst in `build_strecke_schleus_items`,
|
||||||
|
das VOR der SEP-Zuordnung laeuft). Darum sammeln
|
||||||
|
`build_strecke_schleus_items` und `map_separator_kette_items` ihre Treffer
|
||||||
|
zunaechst nur in einem gemeinsamen `sep_kette_by_key`-dict
|
||||||
|
(Schluessel -> Item), OHNE die `Nachbarn`-Spalte zu setzen.
|
||||||
|
`resolve_umlauf_und_sepliste_nachbarn` loest danach in einem einzigen Schritt
|
||||||
|
sowohl die Kreisel-Umlauf-Punkte als auch die sepliste-Schluessel gegen einen
|
||||||
|
gemeinsamen Lookup (freie Separatoren nach TeileId, Weichen nach Schluessel,
|
||||||
|
sepliste-Zeilen nach Schluessel) zu echten TeileIds auf.
|
||||||
|
|
||||||
|
**`map_separator_kette_items` erzeugt keine neuen Zeilen** (Architektur-
|
||||||
|
entscheidung, siehe Docstring in `lib/export_csv.py`): der verpackte
|
||||||
|
Separator liegt durch `csv:sep-proxies-erzeugen` (Phase 1, Schritt 1) bereits
|
||||||
|
als echtes Separator-Item vor. Eine zweite, aus der sepliste erzeugte Zeile
|
||||||
|
waere derselbe physische Separator ein zweites Mal. Die Zuordnung
|
||||||
|
sepliste-Eintrag -> vorhandenes Item laeuft ueber die Wrapper-ID (Item-
|
||||||
|
Merkmal `"Zuordnung"`, von `csv:sep-proxies-zuordnung-setzen` fest gesetzt)
|
||||||
|
plus den naechstgelegenen rohen INSERT-Punkt (Item-Felder `_x`/`_y` vs. die
|
||||||
|
sepliste-Koordinate) — beide stammen aus derselben Bauphase und liegen daher
|
||||||
|
nah beieinander, auch wenn sie nicht exakt uebereinstimmen.
|
||||||
|
|
||||||
|
### 6. Sensor-Zuordnung, Ausgabe
|
||||||
|
|
||||||
|
`compute_sensor_zuordnung` bestimmt fuer jeden Separator/Scanner ohne
|
||||||
|
bekannte (fixierte) Zuordnung das Traegerelement (Gefaellestrecke > Foerderer
|
||||||
|
> Kreiselhaelfte, per Bounding-Box). `compute_scanner_nearest_separator`
|
||||||
|
haengt jedem Scanner den raeumlich naechsten Separator an. Am Ende werden
|
||||||
|
alle Items nach TeileId sortiert und als CSV-Zeilen ausgegeben
|
||||||
|
(`format_csv_line`).
|
||||||
|
|
||||||
|
## Warum eine Zeile keine Nachbarn hat — Diagnose-Leitfaden
|
||||||
|
|
||||||
|
| Symptom | Wahrscheinlichste Ursache |
|
||||||
|
|---|---|
|
||||||
|
| ALLE Zeilen ohne Nachbarn, auch Kreisel | `bbox`-Feld fehlt im JSON komplett (`vla-getboundingbox` scheiterte global) — pruefen, ob `csv:run-export` eine `exp-bbox-fail`-Warnung ausgegeben hat |
|
||||||
|
| Nur GF/VF ohne Nachbarn, Separatoren teils auch | `sepliste` wurde nicht ans Item durchgereicht, oder die Strecke beruehrt geometrisch keinen Kreisel (`Fehler = "unverbunden"`) |
|
||||||
|
| Ein Separator/Scanner ohne Nachbarn, Rest ok | Separator ist weder Teil einer sepliste-Kette noch nah genug an einem Kreisel (freier, isoliert platzierter Sensor) — legitimer Fall, keine Fehlkonfiguration |
|
||||||
|
| Zwei sich beruehrende Kreisel haben falsche/keine Nachbarn am Beruehrpunkt | Pruefen, ob `compute_kreisel_touch_switches` ueberhaupt eine Weiche erzeugt (Toleranz `[Nachbarschaft] toleranz_mm`) und ob sie im Kreisel-Umlauf beider Kreisel auftaucht |
|
||||||
|
| `Nachbarn` enthaelt einen Wert wie `"0026#3"` statt einer TeileId | Regression im Aufloese-Schritt — ein sep_kette-Schluessel wurde nicht durch `resolve_umlauf_und_sepliste_nachbarn` ersetzt (sollte nicht mehr vorkommen, siehe Tests `test_export_kreisel_umlauf.py`) |
|
||||||
|
|
||||||
|
Das Kollisions-/Nachbarschafts-Debugprotokoll (`cfg/export.cfg`
|
||||||
|
`[Nachbarschaft] debug_log=1`) schreibt eine `.dbg`-Datei mit jeder
|
||||||
|
Klassifikation, Gruppenzuordnung und jedem Ueberschneidungstest — der
|
||||||
|
schnellste Weg, einer fehlenden Nachbarschaft auf den Grund zu gehen.
|
||||||
|
|
||||||
|
## Relevante Tests
|
||||||
|
|
||||||
|
- `tests/test_export_kreisel_umlauf.py` — Kreisel-Kreisel-Weiche im Umlauf,
|
||||||
|
rotationsunabhaengige Kreisel-Matching-Box, sepliste-Nachbarn-Aufloesung
|
||||||
|
ohne rohe Schluessel in der Ausgabe.
|
||||||
|
- `tests/test_export_separator_bbox.py` — feste Separator-Symbol-Box
|
||||||
|
(Geometrie + Fallback bei fehlender `vla-getboundingbox`-Box).
|
||||||
|
|
||||||
|
Beide laufen komplett ohne BricsCAD (reine Python-Geometrie/-Logik).
|
||||||
Reference in New Issue
Block a user