feat: GR_-Geruest-Baugruppen in EXPORTCSV/EXPORTSIVAS aufnehmen

GR_-Bloecke werden jetzt wie BTMT eigenstaendig gesammelt (LISP: neues
pattern_geruest in cfg/export.cfg, csv:collect-export-blocks,
ssg-id-collect-blocks + ID-ATTDEF in *geruest-attrib-defs*) und in beiden
Python-Exporten als eigene TeileArt-Zeile ausgegeben: ARTINR/BEZEICHNUNG/
TEILEART/LAENGE in festen Spalten, restliche Attribute (FARBE/LAYER) im
Merkmale-/details-JSON.

Dabei zwei Bugs behoben, die beim ersten echten GR_-Export sichtbar wurden:
- csv:json-escape nutzte vl-string-subst (ersetzt nur das erste Vorkommen)
  statt des global ersetzenden ssg-str-replace-all - bei Attributwerten mit
  mehreren Anfuehrungszeichen blieb ein Zeichen unescaped und brach die
  JSON-Struktur beim Python-Parsen.
- Gradzeichen (°) in data/json/geruest.json durch "Grad" ersetzt, um das
  Encoding-Risiko (cp1252 vs. UTF-8 beim LISP-Export) zu vermeiden.
This commit is contained in:
Michael Stangl
2026-09-14 14:22:45 +02:00
parent 9e3ac1f70a
commit 1590f8f7ed
8 changed files with 128 additions and 12 deletions
+42
View File
@@ -263,6 +263,23 @@ def build_btmt_merkmale(block):
}
def build_geruest_merkmale(block):
"""Merkmale-Dict fuer einen GR_-Geruest-Baugruppenblock (siehe
pattern_geruest in cfg/export.cfg, GeruestInsert.lsp *geruest-attrib-defs*).
ARTINR/LAENGE/BEZEICHNUNG wandern in feste CSV-Spalten (teileid/
bezeichnung ueben in process_blocks unten), TEILEART/FARBE/LAYER bleiben
als Rest-Attribute in der Merkmale-JSON-Spalte - so wie bei Separator/
Scanner/BTMT bereits ueblich."""
attribs = block.get("attribs", {})
return {
"TeileArt": attribs.get("TEILEART", ""),
"Farbe": attribs.get("FARBE", ""),
"Laenge": attribs.get("LAENGE", ""),
"Layer": attribs.get("LAYER", ""),
}
def format_sensor_id(prefix, n):
"""Fallback-TeileId fuer Separator/Scanner-Zeilen im CSV-Export, falls das
Block-Attribut "ID" (noch) leer ist.
@@ -869,6 +886,7 @@ def process_blocks(blocks, lookup, dbg=None):
scanner_count = 0
btmt_beladung_count = 0
btmt_entladung_count = 0
geruest_count = 0
# Zaehlung fuer die Omniflo-Sum-Zeile (nur Bogen/Weiche/Gerade)
cnt_wk = cnt_einzel = cnt_delta = cnt_doppel = cnt_stern = 0
@@ -1141,6 +1159,30 @@ def process_blocks(blocks, lookup, dbg=None):
})
continue
# Geruest-Baugruppe (GR_<id>_<timestamp>) - eigene TeileArt-Zeile,
# analog zu BTMT: kein ANZAHL_*-Attribut an einem Carrier zaehlt sie
# mit. TeileId ist das echte ID-Attribut (ssg-id-check-all fuellt es
# vor jedem Export, siehe *geruest-attrib-defs*); ARTINR (Sivas-IDs,
# ggf. mehrere kommagetrennt) landet als eigene Spalte in den
# Merkmalen statt in "teileart" - anders als bei Kreisel/BTMT ist
# "TEILEART" hier ein FREIES Attribut (Rohrabhaenger/Saeule/...), keine
# feste Konstante.
if matches_any(bname, BLOCKPATTERNS.get("pattern_geruest", [])):
elem_nr += 1
geruest_count += 1
attribs = block.get("attribs", {})
bezeichnung = attribs.get("BEZEICHNUNG", "").strip()
items.append({
"nr": elem_nr,
"teileart": "Geruest",
"teileid": attribs.get("ID") or format_sensor_id("GR", geruest_count),
"bezeichnung": bezeichnung if bezeichnung else f"Geruest :{geruest_count}",
"planquadrat": planquadrat_column(block, planquadrat_cfg),
"merkmale": {"ARTINR": attribs.get("ARTINR", ""), **build_geruest_merkmale(block)},
**bbox_columns(block),
})
continue
if has_omniflo:
elem_nr += 1
items.append({