Merge origin/master: i18n-System (de/en) + 30-Grad-AS/ES-Elemente zusammenfuehren
Lokaler Stand (Modus-3 Randwert-Solver, 30/90-AS/ES, Kreisel-Attribute) mit dem Remote-Stand (Internationalisierung ssg-text, neue Bloecke, Sivas-Export) vereint. Konfliktaufloesung: - vf_linienzug.lsp: Nicht-interaktive -block-Kerne (fuer Pfad-Modi) behalten, interaktive Abfragen (GF-Bogen, Vario-Kurve, ES-Seite) auf ssg-text umgestellt. Kein doppelter defun mehr. - Gefaellestrecke.lsp: 30/90-Winkelabfrage mit den i18n-Seiten-Prompts kombiniert; gefaellestrecke-einfuegen-Aufruf mit as-/es-winkel. - vf_core.lsp: vf-frage-element-winkel nimmt jetzt einen i18n-Header-Key (vf-winkel-aus-header / vf-winkel-ein-header) statt festem Text. - lang/de_DE.json + en_GB.json: neue Keys vf-winkel-aus/ein-header, vf-winkel-90/30; gf-seite-Header ohne irrefuehrendes "_90_". - export_sivas.py: ANZAHL_SEPARATOR/ANZAHL_SCANNER mit N_*-Fallback plus Remote-Umsortierung; doppelte "Kreiselart"-Zeile entfernt. - AS_Element_30_links/rechts.dwg + Separator_SP.dwg (3D): Remote-Version (Attribut-Stand) uebernommen. Die KS_AUS-Geometrie-Korrektur der 30-Grad- AS-Elemente wird danach in BricsCAD nachgezogen. Offen (Folge-Commit): die Modus-Treiber in vf_linienzug.lsp (vf-linienzug-modus / -modus2 / -modus3) sind noch nicht i18n (ca. 110 Strings). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+13
-4
@@ -52,6 +52,15 @@ def build_lookup(boegen, weichen):
|
||||
return lookup
|
||||
|
||||
|
||||
def format_sivasnr(sivasnr):
|
||||
"""
|
||||
Wandelt einen "+"-separierten Sivasnr-Wert (Rohformat aus den JSON-Katalogen,
|
||||
dort weiterhin benoetigt fuer Dateinamen-Referenzen) in eine reine
|
||||
kommaseparierte Liste fuer die Ausgabe (CSV/Block-Attribut) um.
|
||||
"""
|
||||
return ", ".join(p.strip() for p in str(sivasnr).split("+") if p.strip())
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Merkmale-Builder
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -80,7 +89,7 @@ def build_bogen_merkmale(block, eintrag):
|
||||
"Radius": float(eintrag.get("Radius", 0)),
|
||||
"Höhe": get_hoehe(block),
|
||||
"Drehung": get_drehung(block),
|
||||
"SivasNummer": str(eintrag.get("Sivasnr", ""))
|
||||
"SivasNummer": format_sivasnr(eintrag.get("Sivasnr", ""))
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +102,7 @@ def build_weiche_merkmale(block, eintrag):
|
||||
"Höhe": get_hoehe(block),
|
||||
"Drehung": get_drehung(block),
|
||||
"Antrieb Kurve": eintrag.get("SivasnrTEF") is not None,
|
||||
"SivasNummer": str(eintrag.get("Sivasnr", ""))
|
||||
"SivasNummer": format_sivasnr(eintrag.get("Sivasnr", ""))
|
||||
}
|
||||
|
||||
|
||||
@@ -305,7 +314,7 @@ def process_blocks(blocks, lookup):
|
||||
vf_count += 1
|
||||
items.append({
|
||||
"nr": elem_nr,
|
||||
"teileart": "VarioFoerderer",
|
||||
"teileart": "ILS 2.0 Strecke",
|
||||
"teileid": block.get("attribs", {}).get("ID", "0000"),
|
||||
"bezeichnung": f"VarioFoerderer :{vf_count}",
|
||||
"merkmale": build_variofoerderer_merkmale(block),
|
||||
@@ -318,7 +327,7 @@ def process_blocks(blocks, lookup):
|
||||
gf_count += 1
|
||||
items.append({
|
||||
"nr": elem_nr,
|
||||
"teileart": "Gefaellestrecke",
|
||||
"teileart": "ILS 2.0 Gefaellestrecke",
|
||||
"teileid": block.get("attribs", {}).get("ID", "0000"),
|
||||
"bezeichnung": f"Gefaellestrecke :{gf_count}",
|
||||
"merkmale": build_variofoerderer_merkmale(block),
|
||||
|
||||
+195
-18
@@ -4,7 +4,7 @@
|
||||
export_sivas.py - Erzeugt Sivas-Export-CSV mit gruppierten Zeilen und Summierungszeilen.
|
||||
|
||||
Neues CSV-Format:
|
||||
Nr;TeileArt;SivasNummer;Dispgruppe;Anzahl;Laenge[mm];Hoehe[m];details
|
||||
Nr;TeileArt;SivasNummer;Dispogruppe;Anzahl;Laenge[mm];Hoehe[m];details
|
||||
|
||||
- Omniflo Bogen/Weiche: gruppiert nach SivasNummer, Anzahl summiert
|
||||
- Omniflo Gerade: gruppiert nach ARTINR-Attribut, Laenge[mm] summiert
|
||||
@@ -83,6 +83,15 @@ def get_id(block):
|
||||
return block.get("attribs", {}).get("ID", "")
|
||||
|
||||
|
||||
def format_sivasnr(sivasnr):
|
||||
"""
|
||||
Wandelt einen "+"-separierten Sivasnr-Wert (Rohformat aus den JSON-Katalogen,
|
||||
dort weiterhin benoetigt fuer Dateinamen-Referenzen) in eine reine
|
||||
kommaseparierte Liste fuer die Ausgabe (CSV/Block-Attribut) um.
|
||||
"""
|
||||
return ", ".join(p.strip() for p in str(sivasnr).split("+") if p.strip())
|
||||
|
||||
|
||||
def format_hoehe_m(hoehe_mm_str):
|
||||
"""Konvertiert mm-String nach m mit deutschem Dezimaltrennzeichen."""
|
||||
try:
|
||||
@@ -94,6 +103,113 @@ def format_hoehe_m(hoehe_mm_str):
|
||||
return ""
|
||||
|
||||
|
||||
def build_gefaellestrecke_details(block):
|
||||
"""Merkmale-Dict fuer eine einzelne ILS Gefaellestrecke (GF_N-Block)."""
|
||||
attribs = block.get("attribs", {})
|
||||
|
||||
def mm_to_m(key):
|
||||
try:
|
||||
return str(round(float(attribs.get(key, "0")) / 1000.0, 2))
|
||||
except (ValueError, TypeError):
|
||||
return "0"
|
||||
|
||||
return {
|
||||
"IDs": [get_id(block)],
|
||||
"Bezeichnung": attribs.get("Bezeichnung", block.get("block_name", "")),
|
||||
"Hoehe oben": mm_to_m("HOEHE_VON_mm"),
|
||||
"Hoehe unten": mm_to_m("HOEHE_BIS_mm"),
|
||||
"Laenge in Meter": attribs.get("L_GF_m", ""),
|
||||
"Winkel": attribs.get("GF_WINKEL", ""),
|
||||
"Anzahl der Separatoren": attribs.get("ANZAHL_SEPARATOR", "0"),
|
||||
"Anzahl der Scanner": attribs.get("ANZAHL_SCANNER", "0"),
|
||||
"Typ": attribs.get("TYP", "Gefaellestrecke"),
|
||||
}
|
||||
|
||||
|
||||
def gefaellestrecke_signature(block):
|
||||
"""
|
||||
Schluessel zur Gruppierung gleichartiger Gefaellestrecken: Bloecke mit
|
||||
identischen Massen (Hoehe, Laenge, Winkel, Separatoren, Scanner, Typ)
|
||||
landen in derselben Sivas-Zeile - nur IDs/Bezeichnung unterscheiden sich.
|
||||
"""
|
||||
attribs = block.get("attribs", {})
|
||||
return (
|
||||
attribs.get("HOEHE_VON_mm", "0"),
|
||||
attribs.get("HOEHE_BIS_mm", "0"),
|
||||
attribs.get("L_GF_m", ""),
|
||||
attribs.get("GF_WINKEL", ""),
|
||||
attribs.get("ANZAHL_SEPARATOR", "0"),
|
||||
attribs.get("ANZAHL_SCANNER", "0"),
|
||||
attribs.get("TYP", "Gefaellestrecke"),
|
||||
)
|
||||
|
||||
|
||||
def parse_float_list(csv_str):
|
||||
"""Kommagetrennten Attribut-String (z.B. L_GF_m) in Liste von floats."""
|
||||
out = []
|
||||
for part in str(csv_str or "").split(","):
|
||||
part = part.strip()
|
||||
if not part:
|
||||
continue
|
||||
try:
|
||||
out.append(float(part))
|
||||
except ValueError:
|
||||
pass
|
||||
return out
|
||||
|
||||
|
||||
def parse_str_list(csv_str):
|
||||
"""Kommagetrennten Attribut-String (z.B. MOTORSEITE) in Liste von Strings."""
|
||||
return [p.strip() for p in str(csv_str or "").split(",") if p.strip()]
|
||||
|
||||
|
||||
def build_variofoerderer_details(block):
|
||||
"""
|
||||
Merkmale-Dict fuer eine VF_N-Streckengruppe (VarioFoerderer, ggf.
|
||||
mehrsegmentig aus Gefaelle- und angetriebenen Foerderer-Teilen).
|
||||
"""
|
||||
attribs = block.get("attribs", {})
|
||||
|
||||
def to_int(key):
|
||||
try:
|
||||
return int(attribs.get(key, "0") or "0")
|
||||
except (ValueError, TypeError):
|
||||
return 0
|
||||
|
||||
return {
|
||||
"IDs": [get_id(block)],
|
||||
"Anzahl Foerdergruppen": to_int("ANZAHL_VF"),
|
||||
"Anzahl Gefaellegruppen": to_int("ANZAHL_GF"),
|
||||
"Hoehe in m": attribs.get("MONTAGEHOEHE_m", "0"),
|
||||
"GruppenteileGefaelle": {
|
||||
"AnzahlGeraden": to_int("ANZAHL_GF"),
|
||||
"Laenge in Meter": parse_float_list(attribs.get("L_GF_m", "")),
|
||||
"Winkel": parse_float_list(attribs.get("GF_WINKEL", "")),
|
||||
"AnzahlKurvenLinks90": to_int("GF_Bogen_L_90"),
|
||||
"AnzahlKurvenLinks60": to_int("GF_Bogen_L_60"),
|
||||
"AnzahlKurvenLinks30": to_int("GF_Bogen_L_30"),
|
||||
"AnzahlKurvenRechts90": to_int("GF_Bogen_R_90"),
|
||||
"AnzahlKurvenRechts60": to_int("GF_Bogen_R_60"),
|
||||
"AnzahlKurvenRechts30": to_int("GF_Bogen_R_30"),
|
||||
},
|
||||
"GruppenteileAngetrieben": {
|
||||
"MotorSeite": parse_str_list(attribs.get("MOTORSEITE", "")),
|
||||
"AnzahlGeraden": to_int("ANZAHL_VF"),
|
||||
"Laenge in Meter": parse_float_list(attribs.get("L_VF_m", "")),
|
||||
"Winkel": parse_float_list(attribs.get("VF_WINKEL", "")),
|
||||
"Foerderrichtung": parse_str_list(attribs.get("ANTRIEBFAHRTRICHTUNG", "")),
|
||||
"AnzahlKurvenAussen90": to_int("VF_Bogen_A_90"),
|
||||
"AnzahlKurvenAussen60": to_int("VF_Bogen_A_60"),
|
||||
"AnzahlKurvenAussen30": to_int("VF_Bogen_A_30"),
|
||||
"AnzahlKurvenInnen90": to_int("VF_Bogen_I_90"),
|
||||
"AnzahlKurvenInnen60": to_int("VF_Bogen_I_60"),
|
||||
"AnzahlKurvenInnen30": to_int("VF_Bogen_I_30"),
|
||||
},
|
||||
"Anzahl der Separatoren": to_int("ANZAHL_SEPARATOR"),
|
||||
"Anzahl der Scanner": to_int("ANZAHL_SCANNER"),
|
||||
}
|
||||
|
||||
|
||||
def build_kreisel_details(block):
|
||||
"""Merkmale-Dict fuer einen ILS Kreisel."""
|
||||
attribs = block.get("attribs", {})
|
||||
@@ -108,12 +224,12 @@ def build_kreisel_details(block):
|
||||
except (ValueError, TypeError):
|
||||
hoehe_m = "0"
|
||||
return {
|
||||
"Hoehe in m": hoehe_m,
|
||||
"Kreiselart": attribs.get("KREISELART", "STANDARD"),
|
||||
"Abstand (Kreiselachse A - Kreiselachse) in Meter": abstand_m,
|
||||
"Anzahl der Separatoren": attribs.get("ANZAHL_SEPARATOR", attribs.get("N_SEPARATOREN", "2")),
|
||||
"Kreiselart": attribs.get("KREISELART", "STANDARD"),
|
||||
"Anzahl der Scanner": attribs.get("ANZAHL_SCANNER", attribs.get("N_SCANNER", "0")),
|
||||
"Anzahl der Rampen": attribs.get("N_RAMPEN", "0"),
|
||||
"Hoehe in m": hoehe_m,
|
||||
"Drehrichtung": attribs.get("DREHRICHTUNG", "UZS"),
|
||||
"Drehung": block.get("rotation", 0.0),
|
||||
}
|
||||
@@ -123,13 +239,20 @@ def build_kreisel_details(block):
|
||||
SKIP_BLOCKS = set(BLOCKPATTERNS.get("pattern_ks_subblocks", ["K1", "K2", "K3", "K4", "KS_EIN", "KS_AUS"]))
|
||||
|
||||
# Sivas-Nummern und Bezeichnungen je ILS-Elementtyp
|
||||
# Hinweis: "ils_kurve", "bt_beladung" und "bg_entladung" sind aktuell nur
|
||||
# Typnamen-Platzhalter - die zugehoerigen Menuebefehle sind noch Dummies
|
||||
# (siehe Lisp/README.md), es gibt noch keine Block-Erkennung dafuer.
|
||||
ILS_SIVASNR = {
|
||||
"kreisel": ("6200", "ILS 2.0 Kreisel"),
|
||||
"eckrad": ("6210", "ILS 2.0 Eckrad"),
|
||||
"zusatzmodul":("6215", "ILS 2.0 Zusatzmodul"),
|
||||
"ils_weiche": ("6216", "ILS 2.0 Weichen"),
|
||||
"strecke": ("6220", "ILS 2.0 Strecke - Module"),
|
||||
"automation": ("6269", "ILS 2.0 Automation und Pneumatik - Gesamtanlage"),
|
||||
"kreisel": ("6200", "ILS 2.0 Kreisel"),
|
||||
"eckrad": ("6210", "ILS 2.0 Eckrad"),
|
||||
"zusatzmodul": ("6215", "ILS 2.0 Zusatzmodul"),
|
||||
"ils_weiche": ("6216", "ILS 2.0 Weiche"),
|
||||
"ils_kurve": ("", "ILS 2.0 Kurve"),
|
||||
"strecke": ("6220", "ILS 2.0 Strecke"),
|
||||
"gefaellestrecke": ("6220", "ILS 2.0 Gefaellestrecke"),
|
||||
"bt_beladung": ("", "BT - Beladung"),
|
||||
"bg_entladung": ("", "BG - Entladung"),
|
||||
"automation": ("6269", "ILS 2.0 Automation und Pneumatik - Gesamtanlage"),
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +271,9 @@ def process_blocks(blocks, lookup):
|
||||
gerade_groups = {}
|
||||
kreisel_list = [] # Liste von blocks
|
||||
eckrad_ids = [] # IDs aller Eckrad-Bloecke
|
||||
strecke_ids = [] # IDs aller Strecke-Module (VF_*, GF_*, Vario/Staustrecke)
|
||||
strecke_ids = [] # IDs loser Strecke-Elemente (Vario/Staustrecke ausserhalb VF_N)
|
||||
vario_list = [] # Liste von VarioFoerderer-Compound-Bloecken (VF_N), je einer Zeile
|
||||
gefaelle_list = [] # Liste von Gefaellestrecke-Bloecken (GF_*), je einer Zeile
|
||||
|
||||
counters = {
|
||||
"anzahl_boegen": 0,
|
||||
@@ -190,7 +315,7 @@ def process_blocks(blocks, lookup):
|
||||
# --- Omniflo Bogen oder Weiche (erkannt via Katalog) ---
|
||||
if bname in lookup:
|
||||
typ, eintrag = lookup[bname]
|
||||
sivasnr = str(eintrag.get("Sivasnr", bname))
|
||||
sivasnr = format_sivasnr(eintrag.get("Sivasnr", bname))
|
||||
hoehe = get_hoehe_mm(block)
|
||||
|
||||
if typ == "bogen":
|
||||
@@ -270,10 +395,12 @@ def process_blocks(blocks, lookup):
|
||||
continue
|
||||
|
||||
# --- VarioFoerderer Compound-Block (VF_N) ---
|
||||
# Bekommt immer eine eigene Detail-Zeile (vario_list) statt der
|
||||
# generischen IDs-Gruppierung - siehe build_variofoerderer_details.
|
||||
if matches_any(bname, BLOCKPATTERNS.get("pattern_variofoerderer", [])):
|
||||
attribs = block.get("attribs", {})
|
||||
counters["anzahl_variofoerderer"] += 1
|
||||
strecke_ids.append(get_id(block))
|
||||
vario_list.append(block)
|
||||
try:
|
||||
counters["laengen_variofoerderer"] += float(attribs.get("L_VF_m", "0") or "0")
|
||||
except (ValueError, TypeError):
|
||||
@@ -291,11 +418,11 @@ def process_blocks(blocks, lookup):
|
||||
continue
|
||||
|
||||
# --- Gefaellestrecke Compound-Block (GF_N) ---
|
||||
# Eigener Typ "ILS 2.0 Gefaellestrecke" - NICHT als Variofoerderer zaehlen.
|
||||
if matches_any(bname, BLOCKPATTERNS.get("pattern_gefaellestrecke", [])):
|
||||
attribs = block.get("attribs", {})
|
||||
counters["anzahl_variofoerderer"] += 1
|
||||
counters["anzahl_gefaellestrecken"] += 1
|
||||
strecke_ids.append(get_id(block))
|
||||
gefaelle_list.append(block)
|
||||
try:
|
||||
counters["laengen_gefaellestrecken"] += float(attribs.get("L_GF_m", "0") or "0")
|
||||
except (ValueError, TypeError):
|
||||
@@ -395,7 +522,10 @@ def process_blocks(blocks, lookup):
|
||||
})
|
||||
nr += 1
|
||||
|
||||
if counters["anzahl_variofoerderer"] > 0:
|
||||
# Lose Strecke-Elemente (Vario/Staustrecke ausserhalb eines VF_N-Blocks)
|
||||
# als eine gruppierte Zeile; VF_N-Compound-Bloecke bekommen unten je eine
|
||||
# eigene Detail-Zeile.
|
||||
if strecke_ids:
|
||||
sivasnr, teileart = ILS_SIVASNR["strecke"]
|
||||
items.append({
|
||||
"nr": nr,
|
||||
@@ -403,17 +533,64 @@ def process_blocks(blocks, lookup):
|
||||
"sivasnr": sivasnr,
|
||||
"sivasnr_quoted": True,
|
||||
"dispgruppe": 20,
|
||||
"anzahl": counters["anzahl_variofoerderer"],
|
||||
"anzahl": len(strecke_ids),
|
||||
"laenge_mm": "",
|
||||
"hoehe_m": "",
|
||||
"details": {"IDs": strecke_ids},
|
||||
})
|
||||
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:
|
||||
sivasnr, teileart = ILS_SIVASNR["strecke"]
|
||||
items.append({
|
||||
"nr": nr,
|
||||
"teileart": teileart,
|
||||
"sivasnr": sivasnr,
|
||||
"sivasnr_quoted": True,
|
||||
"dispgruppe": 20,
|
||||
"anzahl": 1,
|
||||
"laenge_mm": "",
|
||||
"hoehe_m": "",
|
||||
"details": build_variofoerderer_details(block),
|
||||
})
|
||||
nr += 1
|
||||
|
||||
# Gleichartige Gefaellestrecken (identische Masse) zu einer Zeile
|
||||
# zusammenfassen - nur IDs/Bezeichnung bleiben je Block unterschiedlich.
|
||||
gefaelle_groups = {}
|
||||
for block in gefaelle_list:
|
||||
sig = gefaellestrecke_signature(block)
|
||||
if sig not in gefaelle_groups:
|
||||
gefaelle_groups[sig] = {"ids": [], "bezeichnungen": [], "block": block}
|
||||
gefaelle_groups[sig]["ids"].append(get_id(block))
|
||||
gefaelle_groups[sig]["bezeichnungen"].append(
|
||||
block.get("attribs", {}).get("Bezeichnung", block.get("block_name", "")))
|
||||
|
||||
for group in gefaelle_groups.values():
|
||||
sivasnr, teileart = ILS_SIVASNR["gefaellestrecke"]
|
||||
details = build_gefaellestrecke_details(group["block"])
|
||||
details["IDs"] = group["ids"]
|
||||
details["Bezeichnung"] = group["bezeichnungen"]
|
||||
items.append({
|
||||
"nr": nr,
|
||||
"teileart": teileart,
|
||||
"sivasnr": sivasnr,
|
||||
"sivasnr_quoted": True,
|
||||
"dispgruppe": 20,
|
||||
"anzahl": len(group["ids"]),
|
||||
"laenge_mm": "",
|
||||
"hoehe_m": "",
|
||||
"details": details,
|
||||
})
|
||||
nr += 1
|
||||
|
||||
# ILS Automation Summierungszeile (nur wenn ILS-Elemente vorhanden)
|
||||
has_ils = (counters["anzahl_kreisel"] > 0
|
||||
or counters["anzahl_eckraeder"] > 0
|
||||
or counters["anzahl_variofoerderer"] > 0)
|
||||
or counters["anzahl_variofoerderer"] > 0
|
||||
or counters["anzahl_gefaellestrecken"] > 0)
|
||||
if has_ils:
|
||||
automation_details = {
|
||||
"Anzahl Kreisel": counters["anzahl_kreisel"],
|
||||
@@ -492,7 +669,7 @@ def main():
|
||||
|
||||
items, _ = process_blocks(blocks, lookup)
|
||||
|
||||
header = "Nr;TeileArt;SivasNummer;Dispgruppe;Anzahl;Laenge[mm];Hoehe[m];details"
|
||||
header = "Nr;TeileArt;SivasNummer;Dispogruppe;Anzahl;Laenge[mm];Hoehe[m];details"
|
||||
with open(output_csv, "w", encoding="utf-8") as f:
|
||||
f.write(header + "\n")
|
||||
for item in items:
|
||||
|
||||
+67
-8
@@ -39,13 +39,14 @@ Umgebungsvariablen:
|
||||
DXFM_RESULTS - Pfad zum results/-Verzeichnis
|
||||
|
||||
Aufruf:
|
||||
python lib/set_attributs.py [--number SIVASNR] [--dry-run]
|
||||
python lib/set_attributs.py [--number SIVASNR] [--dry-run] [--type 2D|3D]
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import configparser
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
import ezdxf
|
||||
@@ -64,6 +65,7 @@ def load_config(cfg_path):
|
||||
"bogen": [("BESCHR", "ProfilTyp"), ("ARTINR", "Sivasnr"), ...],
|
||||
"weiche": [("BESCHR", "ProfilTyp"), ...],
|
||||
"layer": {"bogen": "OF Boegen A-2", "weiche_90": "Weiche 90 Grad A-2", ...},
|
||||
"container": {"0_BG071090": "0_B10071, 0_B10090, 0_B10090", ...},
|
||||
}
|
||||
"""
|
||||
config = configparser.ConfigParser()
|
||||
@@ -81,6 +83,13 @@ def load_config(cfg_path):
|
||||
val = val[1:-1]
|
||||
layer_map[key] = val
|
||||
result["layer"] = layer_map
|
||||
elif section == "container":
|
||||
# Container-Sektion: 0_BGxxxxxx -> bereinigte, kommaseparierte Liste
|
||||
container_map = {}
|
||||
for key, val in config.items(section):
|
||||
parts = [p.strip() for p in val.split(",") if p.strip()]
|
||||
container_map[key] = ", ".join(parts)
|
||||
result["container"] = container_map
|
||||
else:
|
||||
attrs = []
|
||||
for tag, source in config.items(section):
|
||||
@@ -131,12 +140,41 @@ def get_layer_for_entry(typ, json_entry, layer_map):
|
||||
return ""
|
||||
|
||||
|
||||
def resolve_value(source, json_entry, sivasnr):
|
||||
CONTAINER_PATTERN = re.compile(r"0_BG\d+")
|
||||
|
||||
|
||||
def resolve_container_refs(text, container_map):
|
||||
"""
|
||||
Ersetzt jedes Vorkommen von 0_BGxxxxxx in text durch die zugehoerige
|
||||
kommaseparierte Liste aus [container]. Kommt das Muster mehrfach vor,
|
||||
wird jedes Vorkommen einzeln ersetzt. Unbekannte 0_BG-Werte bleiben
|
||||
unveraendert stehen.
|
||||
|
||||
Anschliessend werden verbleibende "+"-Trenner (z.B. zwischen Artikelnr.
|
||||
und 0_BG-Referenz) durch ", " ersetzt, damit SivasNummer ausschliesslich
|
||||
eine kommaseparierte Liste ist.
|
||||
"""
|
||||
if container_map and "0_BG" in text:
|
||||
text = CONTAINER_PATTERN.sub(
|
||||
lambda m: container_map.get(m.group(0), m.group(0)), text
|
||||
)
|
||||
if "+" in text:
|
||||
parts = [p.strip() for p in text.split("+") if p.strip()]
|
||||
text = ", ".join(parts)
|
||||
return text
|
||||
|
||||
|
||||
def resolve_value(source, json_entry, sivasnr, container_map=None):
|
||||
"""
|
||||
Loest einen Quell-Ausdruck aus der Config auf:
|
||||
- "fester Text" -> fester Text
|
||||
- {sivasnr} -> SivasNr-Wert
|
||||
- Feldname -> Wert aus json_entry
|
||||
|
||||
Wird das Feld "Sivasnr" aufgeloest, werden enthaltene 0_BGxxxxxx-Muster
|
||||
anhand von [container] durch die dort hinterlegte kommaseparierte Liste
|
||||
ersetzt und verbleibende "+"-Trenner durch ", " ersetzt, sodass das
|
||||
Ergebnis ausschliesslich eine kommaseparierte Liste ist (keine "+" mehr).
|
||||
"""
|
||||
source = source.strip()
|
||||
|
||||
@@ -152,7 +190,10 @@ def resolve_value(source, json_entry, sivasnr):
|
||||
val = json_entry.get(source)
|
||||
if val is None:
|
||||
return ""
|
||||
return str(val)
|
||||
val = str(val)
|
||||
if source == "Sivasnr":
|
||||
val = resolve_container_refs(val, container_map)
|
||||
return val
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -225,7 +266,7 @@ def wrap_entities_in_block(doc, block_name):
|
||||
|
||||
|
||||
def add_attdefs_to_dxf(dxf_path, output_path, attr_defs, json_entry, sivasnr,
|
||||
layer_value=""):
|
||||
layer_value="", container_map=None):
|
||||
"""
|
||||
Oeffnet eine DXF-Datei, fuegt ATTDEF-Entities in den Modelspace ein
|
||||
und speichert das Ergebnis.
|
||||
@@ -233,6 +274,7 @@ def add_attdefs_to_dxf(dxf_path, output_path, attr_defs, json_entry, sivasnr,
|
||||
attr_defs: Liste von (TAG, source_expression) aus der Config
|
||||
json_entry: Dict mit den JSON-Daten fuer dieses Element
|
||||
layer_value: Layer-Name aus [layer]-Sektion (wird als LAYER-ATTDEF gesetzt)
|
||||
container_map: Dict aus [container]-Sektion fuer 0_BG-Aufloesung in Sivasnr
|
||||
"""
|
||||
doc = ezdxf.readfile(dxf_path)
|
||||
msp = doc.modelspace()
|
||||
@@ -265,7 +307,7 @@ def add_attdefs_to_dxf(dxf_path, output_path, attr_defs, json_entry, sivasnr,
|
||||
y_pos = y_start
|
||||
count = 0
|
||||
for tag, source in all_defs:
|
||||
default_value = resolve_value(source, json_entry, sivasnr)
|
||||
default_value = resolve_value(source, json_entry, sivasnr, container_map)
|
||||
prompt = tag
|
||||
|
||||
msp.add_attdef(
|
||||
@@ -296,6 +338,7 @@ def process_all(data_dir, cfg_path, results_dir, single_nr=None, dry_run=False):
|
||||
# Config laden
|
||||
attr_config = load_config(cfg_path)
|
||||
layer_map = attr_config.pop("layer", {})
|
||||
container_map = attr_config.pop("container", {})
|
||||
print(f"[set_attributs] Config geladen: {list(attr_config.keys())}")
|
||||
if layer_map:
|
||||
print(f" [layer] {len(layer_map)} Zuordnungen: "
|
||||
@@ -319,7 +362,18 @@ def process_all(data_dir, cfg_path, results_dir, single_nr=None, dry_run=False):
|
||||
out_dir = os.path.join(results_dir, "omniflo")
|
||||
os.makedirs(out_dir, exist_ok=True)
|
||||
|
||||
omniflo_dir = os.environ.get("DXFM_OMNIFLO") or os.path.join(data_dir, "omniflo")
|
||||
dxfm_dim = os.environ.get("DXFM_DIM")
|
||||
if dxfm_dim:
|
||||
omniflo_dir = os.path.join(data_dir, "omniflo", dxfm_dim)
|
||||
else:
|
||||
omniflo_dir = os.environ.get("DXFM_OMNIFLO") or os.path.join(data_dir, "omniflo")
|
||||
if not os.path.isdir(omniflo_dir) or not any(
|
||||
f.lower().endswith(".dxf") for f in os.listdir(omniflo_dir)
|
||||
):
|
||||
print(f"[set_attributs] FEHLER: Im Quellverzeichnis '{omniflo_dir}' "
|
||||
f"wurden keine DXF-Dateien gefunden. eventuell DXFM_DIM=2D in Umgebung setzen")
|
||||
sys.exit(1)
|
||||
|
||||
processed = 0
|
||||
skipped = 0
|
||||
errors = 0
|
||||
@@ -348,7 +402,7 @@ def process_all(data_dir, cfg_path, results_dir, single_nr=None, dry_run=False):
|
||||
output_path = os.path.join(out_dir, dxf_name)
|
||||
|
||||
if dry_run:
|
||||
values = {tag: resolve_value(src, entry, sivasnr)
|
||||
values = {tag: resolve_value(src, entry, sivasnr, container_map)
|
||||
for tag, src in attr_defs}
|
||||
if layer_value:
|
||||
values["LAYER"] = layer_value
|
||||
@@ -359,7 +413,8 @@ def process_all(data_dir, cfg_path, results_dir, single_nr=None, dry_run=False):
|
||||
try:
|
||||
count = add_attdefs_to_dxf(dxf_path, output_path, attr_defs,
|
||||
entry, sivasnr,
|
||||
layer_value=layer_value)
|
||||
layer_value=layer_value,
|
||||
container_map=container_map)
|
||||
processed += 1
|
||||
print(f" OK: {sivasnr} ({typ}) -> {count} Attribute -> {output_path}")
|
||||
except Exception as e:
|
||||
@@ -379,9 +434,13 @@ def main():
|
||||
help="Nur diese SivasNr verarbeiten")
|
||||
parser.add_argument("--dry-run", "-d", action="store_true",
|
||||
help="Nur anzeigen, nicht schreiben")
|
||||
parser.add_argument("--type", "-t", choices=["2D", "3D"], default="2D",
|
||||
help="Ueberschreibt DXFM_DIM und damit das "
|
||||
"Quellverzeichnis data/omniflo/<type> (Default: 2D)")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Umgebungsvariablen
|
||||
os.environ["DXFM_DIM"] = args.type
|
||||
data_dir = os.environ.get("DXFM_DATA")
|
||||
cfg_dir = os.environ.get("DXFM_CFG")
|
||||
results_dir = os.environ.get("DXFM_RESULTS")
|
||||
|
||||
Reference in New Issue
Block a user