[ADD] Kreisel-Split (links/rechts) bei Kollisionspruefung + Separator/Scanner-Zuordnung
- export_neighbors.py: kreisel_half_bboxes() teilt die Kreisel-BBox entlang der Achse durch Antriebs-/Spannstation (AN8/SP8) in eine linke und eine rechte Haelfte (je ca. 400mm breit, siehe [Kreisel] durchmesser_mm in cfg/export.cfg, gespiegelt von component_defaults.json). compute_neighbor_ids ersetzt jeden Kreisel-Eintrag in der Kollisionsgruppe durch dieses Paar (TeileId-L/-R) statt einer einzelnen BBox - Nachbarn einer Gefaellestrecke/ eines Foerderers zeigen jetzt, welche Haelfte tatsaechlich beruehrt. - compute_sensor_zuordnung(): ordnet jeden Separator/Scanner per BBox- Ueberschneidung seiner Gefaellestrecke, seinem Foerderer oder einer Kreiselhaelfte zu (Prioritaet in dieser Reihenfolge), ersetzt die ZUORDNUNG-Attribut-Merkmale im CSV-Export bei Treffer. - compute_scanner_nearest_separator(): sucht zusaetzlich, unabhaengig von der Zuordnung, zu jedem Scanner den raeumlich naechstgelegenen Separator. - ssg_id.lsp: ssg-id-collect-blocks erfasst jetzt auch Separator_SP/S-LP/ Scanner-Bloecke, damit ssg-id-check-all ihr (neues) ID-Attribut vor jedem Export befuellt; export_csv.py bevorzugt dieses echte ID-Attribut als TeileId und faellt nur bei leerem Attribut auf eine laufende SEP-/SCN- Ersatznummer zurueck. - data/ils/Scanner_2D.dwg, Scanner_3D.dwg, Separator_SP_2D.dwg: ID-ATTDEF ergaenzt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+73
-18
@@ -38,7 +38,20 @@ Fehler = "unverbunden" (keine Partner, aber mindestens einer noetig),
|
||||
(Mindestanzahl erreicht bzw. TeileArt wird nicht geprueft). Kreisel/Eckrad/
|
||||
Omniflo-Elemente brauchen mindestens einen Partner, Gefaellestrecke/
|
||||
Foerderer/Strecke-Modul mindestens zwei (siehe export_neighbors.py
|
||||
MIN_PARTNER).
|
||||
MIN_PARTNER). Ein Kreisel wird dabei NICHT als eine, sondern als zwei
|
||||
BBoxen (links/rechts, geteilt entlang der Achse durch Antriebs-/
|
||||
Spannstation, siehe export_neighbors.kreisel_half_bboxes) gegen
|
||||
Gefaellestrecke/Foerderer getestet - deren "Nachbarn"-Spalte zeigt darum
|
||||
"<KreiselTeileId>-L" bzw. "-R" statt nur der Kreisel-TeileId.
|
||||
Separator/Scanner-Zeilen bekommen zusaetzlich ein berechnetes "Zuordnung"-
|
||||
Merkmal (Gefaellestrecke > Foerderer > Kreiselhaelfte, per BBox-Ueber-
|
||||
schneidung - siehe export_neighbors.compute_sensor_zuordnung), das die
|
||||
ATTRIB-Zuordnung (falls das Attribut ZUORDNUNG in der Zeichnung von Hand
|
||||
gesetzt wurde) ersetzt, sofern ein Treffer gefunden wird. Scanner-Zeilen
|
||||
bekommen zusaetzlich ein "Naechster Separator"-Merkmal (raeumlich naechst-
|
||||
gelegener Separator, euklidischer Abstand der BBox-Mittelpunkte - eigene,
|
||||
von der Zuordnung unabhaengige Suche, siehe export_neighbors.compute_
|
||||
scanner_nearest_separator).
|
||||
Position, Boundingbox, Planquadrat, Nachbarn und Fehler sind nur fuer
|
||||
EXPORTCSV vorhanden (nicht EXPORTSIVAS) - siehe csv:run-export. Insertpoint/
|
||||
K1-K4 stehen zwar in export_raw.json fuer jeden Export zur Verfuegung (siehe
|
||||
@@ -55,15 +68,20 @@ from export_planquadrat import load_planquadrat_config, resolve_origins, compute
|
||||
from export_neighbors import (
|
||||
load_neighbor_tolerance_mm,
|
||||
load_omniflo_cell_size_mm,
|
||||
load_kreisel_durchmesser_mm,
|
||||
load_collision_debug_target,
|
||||
compute_neighbor_ids,
|
||||
compute_neighbor_errors,
|
||||
compute_sensor_zuordnung,
|
||||
compute_scanner_nearest_separator,
|
||||
SENSOR_TEILEARTEN,
|
||||
)
|
||||
|
||||
BLOCKPATTERNS = load_patterns()
|
||||
PLANQUADRAT_CFG = load_planquadrat_config()
|
||||
NEIGHBOR_TOLERANCE_MM = load_neighbor_tolerance_mm()
|
||||
NEIGHBOR_OMNIFLO_CELL_SIZE_MM = load_omniflo_cell_size_mm()
|
||||
KREISEL_DURCHMESSER_MM = load_kreisel_durchmesser_mm()
|
||||
NEIGHBOR_DEBUG_TARGET = load_collision_debug_target()
|
||||
|
||||
|
||||
@@ -191,9 +209,9 @@ def build_gerade_merkmale(block):
|
||||
def build_sensor_merkmale(block):
|
||||
"""Merkmale-Dict fuer einen eigenstaendigen Separator_SP/S-LP- oder
|
||||
Scanner-Block (siehe pattern_separator/pattern_scanner in cfg/export.cfg).
|
||||
Diese Bloecke haben (anders als Kreisel/GF/VF) kein ID-Attribut -
|
||||
ZUORDNUNG/ARTINR/LAYER sind die einzigen ATTDEFs (siehe ils-insert-sensor/
|
||||
mubea:build-separator-one)."""
|
||||
ZUORDNUNG/ARTINR/LAYER (+ neuerdings ID) sind die ATTDEFs (siehe
|
||||
ils-insert-sensor/mubea:build-separator-one, ssg-id-collect-blocks in
|
||||
ssg_id.lsp)."""
|
||||
attribs = block.get("attribs", {})
|
||||
return {
|
||||
"Zuordnung": attribs.get("ZUORDNUNG", ""),
|
||||
@@ -204,13 +222,17 @@ def build_sensor_merkmale(block):
|
||||
|
||||
|
||||
def format_sensor_id(prefix, n):
|
||||
"""Erzeugt eine laufende TeileId fuer Separator/Scanner-Zeilen im CSV-Export.
|
||||
"""Fallback-TeileId fuer Separator/Scanner-Zeilen im CSV-Export, falls das
|
||||
Block-Attribut "ID" (noch) leer ist.
|
||||
|
||||
Diese Bloecke haben KEIN persistiertes ID-Attribut in der Zeichnung
|
||||
(anders als Kreisel/GF/VF, deren "ID" von ssg-id-generate/ssg-id-check-all
|
||||
beim Insert bzw. vor jedem Export vergeben wird) - der Praefix macht das
|
||||
sichtbar: eine rein fuer diesen Exportlauf erzeugte, NICHT ueber mehrere
|
||||
Exportlaeufe hinweg stabile laufende Nummer, keine echte Block-ID.
|
||||
Separator_SP/Scanner haben seit kurzem ein eigenes ID-ATTDEF (siehe
|
||||
ssg-id-collect-blocks in ssg_id.lsp) - normalerweise wird also das echte,
|
||||
ueber ssg-id-generate/ssg-id-check-all vergebene "ID"-Attribut als
|
||||
TeileId verwendet, genau wie bei Kreisel/GF/VF. Nur wenn das (noch)
|
||||
fehlt (z.B. bei aelteren, noch nicht neu exportierten Bloecken), erzeugt
|
||||
diese Funktion eine rein fuer DIESEN Exportlauf gueltige, NICHT ueber
|
||||
mehrere Exportlaeufe hinweg stabile Ersatznummer - der Praefix macht
|
||||
sichtbar, dass es keine echte, persistierte Block-ID ist.
|
||||
"""
|
||||
return f"{prefix}-{n:04d}"
|
||||
|
||||
@@ -489,6 +511,15 @@ def process_blocks(blocks, lookup, dbg=None):
|
||||
"bezeichnung": f"Kreisel :{kreisel_count}",
|
||||
"planquadrat": planquadrat_column(block, planquadrat_cfg),
|
||||
"merkmale": build_kreisel_merkmale(block),
|
||||
# x/y/rotation/attribs (roh, nicht nur die Merkmale) werden
|
||||
# von export_neighbors.kreisel_half_bboxes gebraucht, um die
|
||||
# Links/Rechts-Trennung ueber AN8/SP8/ABSTAND nachzurechnen -
|
||||
# bbox_columns() liefert nur die fertige Gesamt-BBox, keine
|
||||
# Rohdaten dafuer.
|
||||
"x": block.get("x", 0.0),
|
||||
"y": block.get("y", 0.0),
|
||||
"rotation": block.get("rotation", 0.0),
|
||||
"attribs": block.get("attribs", {}),
|
||||
**bbox_columns(block),
|
||||
})
|
||||
continue
|
||||
@@ -508,18 +539,23 @@ def process_blocks(blocks, lookup, dbg=None):
|
||||
})
|
||||
continue
|
||||
|
||||
# Separator (Separator_SP/S-LP) - eigene TeileArt-Zeile mit laufender
|
||||
# ID (siehe format_sensor_id; kein echtes ID-Attribut am Block, anders
|
||||
# als Kreisel/GF/VF). EXPORTSIVAS schliesst denselben Blockpattern
|
||||
# bewusst aus (siehe export_sivas.py) - dort zaehlt bereits
|
||||
# ANZAHL_SEPARATOR am zugehoerigen Kreisel/GF/VF.
|
||||
# Separator (Separator_SP/S-LP) - eigene TeileArt-Zeile. TeileId: das
|
||||
# echte ID-Attribut, falls vorhanden (Separator_SP/Scanner haben seit
|
||||
# kurzem ein eigenes ID-ATTDEF, siehe ssg-id-collect-blocks in
|
||||
# ssg_id.lsp - ssg-id-check-all fuellt es vor jedem Export). Fallback
|
||||
# format_sensor_id (laufende SEP-/SCN-Nummer NUR fuer diesen Export-
|
||||
# lauf) fuer aeltere Bloecke, deren ID-Attribut noch leer ist (z.B.
|
||||
# vor dem naechsten Insert/ssg-id-check-all-Durchlauf).
|
||||
# EXPORTSIVAS schliesst denselben Blockpattern bewusst aus (siehe
|
||||
# export_sivas.py) - dort zaehlt bereits ANZAHL_SEPARATOR am
|
||||
# zugehoerigen Kreisel/GF/VF.
|
||||
if matches_any(bname, BLOCKPATTERNS.get("pattern_separator", [])):
|
||||
elem_nr += 1
|
||||
separator_count += 1
|
||||
items.append({
|
||||
"nr": elem_nr,
|
||||
"teileart": "ILS 2.0 Separator",
|
||||
"teileid": format_sensor_id("SEP", separator_count),
|
||||
"teileid": block.get("attribs", {}).get("ID") or format_sensor_id("SEP", separator_count),
|
||||
"bezeichnung": f"Separator :{separator_count}",
|
||||
"planquadrat": planquadrat_column(block, planquadrat_cfg),
|
||||
"merkmale": build_sensor_merkmale(block),
|
||||
@@ -534,7 +570,7 @@ def process_blocks(blocks, lookup, dbg=None):
|
||||
items.append({
|
||||
"nr": elem_nr,
|
||||
"teileart": "ILS 2.0 Scanner",
|
||||
"teileid": format_sensor_id("SCN", scanner_count),
|
||||
"teileid": block.get("attribs", {}).get("ID") or format_sensor_id("SCN", scanner_count),
|
||||
"bezeichnung": f"Scanner :{scanner_count}",
|
||||
"planquadrat": planquadrat_column(block, planquadrat_cfg),
|
||||
"merkmale": build_sensor_merkmale(block),
|
||||
@@ -563,12 +599,31 @@ def process_blocks(blocks, lookup, dbg=None):
|
||||
})
|
||||
|
||||
neighbor_ids = compute_neighbor_ids(
|
||||
items, NEIGHBOR_TOLERANCE_MM, NEIGHBOR_OMNIFLO_CELL_SIZE_MM, dbg=dbg)
|
||||
items, NEIGHBOR_TOLERANCE_MM, NEIGHBOR_OMNIFLO_CELL_SIZE_MM,
|
||||
kreisel_durchmesser_mm=KREISEL_DURCHMESSER_MM, dbg=dbg)
|
||||
neighbor_errors = compute_neighbor_errors(items, neighbor_ids)
|
||||
for item, nachbarn, fehler in zip(items, neighbor_ids, neighbor_errors):
|
||||
item["nachbarn"] = nachbarn
|
||||
item["fehler"] = fehler
|
||||
|
||||
# Separator/Scanner: Zuordnung zu Gefaellestrecke/Foerderer/Kreiselhaelfte
|
||||
# per BBox-Ueberschneidung berechnen (Prioritaet siehe compute_sensor_
|
||||
# zuordnung) - ersetzt die von Hand gesetzte ZUORDNUNG-Attribut-Merkmale
|
||||
# nur, wenn tatsaechlich ein Treffer gefunden wurde.
|
||||
sensor_zuordnung = compute_sensor_zuordnung(
|
||||
items, NEIGHBOR_TOLERANCE_MM, kreisel_durchmesser_mm=KREISEL_DURCHMESSER_MM, dbg=dbg)
|
||||
for item, zuordnung in zip(items, sensor_zuordnung):
|
||||
if item.get("teileart", "") in SENSOR_TEILEARTEN and zuordnung:
|
||||
item["merkmale"]["Zuordnung"] = zuordnung
|
||||
|
||||
# NACH der Separator-Zuordnung: zu jedem Scanner den raeumlich naechst-
|
||||
# gelegenen Separator suchen (eigene, unabhaengige Suche - siehe
|
||||
# compute_scanner_nearest_separator).
|
||||
nearest_separator = compute_scanner_nearest_separator(items, dbg=dbg)
|
||||
for item, separator_id in zip(items, nearest_separator):
|
||||
if item.get("teileart", "") == "ILS 2.0 Scanner" and separator_id:
|
||||
item["merkmale"]["Naechster Separator"] = separator_id
|
||||
|
||||
return items
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user