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
+49
View File
@@ -288,6 +288,19 @@ def build_eckrad_details(block):
}
def build_geruest_details(block):
"""Merkmale-Dict fuer eine GR_-Geruest-Baugruppe (siehe pattern_geruest in
cfg/export.cfg, GeruestInsert.lsp *geruest-attrib-defs*). ARTINR/
BEZEICHNUNG/TEILEART/LAENGE wandern in feste Sivas-Spalten (sivasnr/
bezeichnung/teileart/laenge_mm), FARBE/LAYER bleiben als Rest-Attribute
hier."""
attribs = block.get("attribs", {})
return {
"Farbe": attribs.get("FARBE", ""),
"Layer": attribs.get("LAYER", ""),
}
# --- Bekannte Bloecke ignorieren ---
SKIP_BLOCKS = set(BLOCKPATTERNS.get("pattern_ks_subblocks", ["K1", "K2", "K3", "K4", "KS_EIN", "KS_AUS"]))
@@ -339,6 +352,7 @@ def process_blocks(blocks, lookup):
btmt_beladung_bezeichnungen = []
btmt_entladung_ids = [] # IDs aller SC_Entladung*-Bloecke (eine gruppierte Zeile)
btmt_entladung_bezeichnungen = []
geruest_list = [] # Liste von GR_-Geruest-Baugruppenbloecken, je einer Zeile
counters = {
"anzahl_boegen": 0,
@@ -369,6 +383,7 @@ def process_blocks(blocks, lookup):
"anzahl_scanner": 0,
"anzahl_btbeladung": 0,
"anzahl_abwurfstationen": 0,
"anzahl_geruest": 0,
}
for block in blocks:
@@ -398,6 +413,18 @@ def process_blocks(blocks, lookup):
counters["anzahl_abwurfstationen"] += 1
continue
# --- Geruest-Baugruppe ---
# Anders als BTMT/Separator je Block eine EIGENE Zeile statt gruppiert:
# ARTINR (Sivas-IDs) und Laenge/Farbe koennen von Block zu Block
# unterschiedlich sein (freie Basis-Variante+Anbauteile-Auswahl je
# GERUEST_*-Aufruf), eine Gruppierung wuerde das verschleiern - wie
# bei VF_N/Kreisel oben, die aus demselben Grund ebenfalls einzeln
# bleiben.
if matches_any(bname, BLOCKPATTERNS.get("pattern_geruest", [])):
geruest_list.append(block)
counters["anzahl_geruest"] += 1
continue
# --- Omniflo Bogen oder Weiche (erkannt via Katalog) ---
if bname in lookup:
typ, eintrag = lookup[bname]
@@ -702,6 +729,28 @@ def process_blocks(blocks, lookup):
})
nr += 1
# Geruest-Baugruppen (GR_<id>_<timestamp>) - je Block eine eigene Zeile
# (siehe Kommentar bei geruest_list oben). Sivasnr kommt direkt aus dem
# ARTINR-Attribut - kann bereits mehrere, kommagetrennte Sivas-IDs
# enthalten (Baugruppe aus mehreren Einzelteilen, siehe ssg-join in
# GeruestInsert.lsp), anders als bei den uebrigen Teilearten also KEIN
# fester Eintrag aus ILS_SIVASNR.
for block in geruest_list:
attribs = block.get("attribs", {})
items.append({
"nr": nr,
"teileart": attribs.get("TEILEART", "Geruest"),
"sivasnr": attribs.get("ARTINR", ""),
"sivasnr_quoted": True,
"bezeichnung": attribs.get("BEZEICHNUNG", ""),
"dispgruppe": 20,
"anzahl": 1,
"laenge_mm": attribs.get("LAENGE", ""),
"ids": [get_id(block)],
"details": build_geruest_details(block),
})
nr += 1
# VarioFoerderer-Compound-Bloecke (VF_N) - je Block eine eigene Zeile mit
# vollstaendigen Gefaelle-/Angetrieben-Teilgruppen (immer TYP "Streckengruppe").
for block in vario_list: