From d454ad4149f4c52afa60afa1de87129e754d331f Mon Sep 17 00:00:00 2001 From: Michael Stangl Date: Wed, 13 May 2026 23:24:20 +0200 Subject: [PATCH] Python Funktion um aus der durch die Lisproutine exportieren CSV eine Sivas TOS Datei mit enthaltenem json zu machen --- lib/export_csv.py | 393 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 393 insertions(+) create mode 100644 lib/export_csv.py diff --git a/lib/export_csv.py b/lib/export_csv.py new file mode 100644 index 0000000..b1e7a3f --- /dev/null +++ b/lib/export_csv.py @@ -0,0 +1,393 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +export_csv.py - Erzeugt CSV aus den von LISP gesammelten Block-Daten. + +Aufruf: + python export_csv.py + +Argumente: + export_raw.json - JSON-Datei mit allen INSERT-Entities aus der Zeichnung + data_dir - Pfad zum data/-Verzeichnis (mit json/ Unterordner) + output.csv - Ziel-CSV-Datei +""" + +import json +import sys +import os +import uuid + + +def load_json(path): + with open(path, "r", encoding="utf-8") as f: + return json.load(f) + + +def build_lookup(boegen, weichen): + """Erzeugt ein Dict: SivasNr (als String) -> (typ, eintrag)""" + lookup = {} + for b in boegen: + key = str(b["Sivasnr"]) + lookup[key] = ("bogen", b) + for w in weichen: + key = str(w["Sivasnr"]) + lookup[key] = ("weiche", w) + return lookup + + +def generate_shape_id(): + return "shape_" + str(uuid.uuid4()) + + +def classify_weiche(eintrag): + """Bestimmt den Weichen-Subtyp fuer die Omniflo Sum Zaehlung.""" + wt = eintrag.get("WeichenTyp", "") + profil = eintrag.get("ProfilTyp", "") + if "WEICHENKOERPER" in profil.upper(): + return "weichenkoerper" + if wt == "Einzelweiche": + return "einzelweiche" + if wt == "Doppelweiche": + return "doppelweiche" + if wt == "Dreiwegeweiche": + return "dreiwegeweiche" + if wt == "Deltaweiche" or "DELTA" in profil.upper(): + return "deltaweiche" + if wt == "Dreifachweiche": + return "deltaweiche" + if wt == "Sternweiche" or "STAR" in profil.upper(): + return "sternweiche" + return "einzelweiche" + + +def build_bogen_merkmale(block, eintrag): + """Merkmale-JSON fuer einen Omniflo Bogen.""" + return { + "Länge in Meter": "0", + "Kurvenwinkel": eintrag.get("KurvenWinkel", 0), + "Radius": eintrag.get("Radius", 0), + "Höhe": "2000", + "Drehung": block.get("rotation", 0.0), + "SivasNummer": str(eintrag.get("Sivasnr", "")) + } + + +def build_weiche_merkmale(block, eintrag): + """Merkmale-JSON fuer eine Omniflo Weiche.""" + wt = eintrag.get("WeichenTyp", "Einzelweiche") + return { + "Länge in Meter links": None, + "Länge in Meter rechts": None, + "Länge in Meter gerade": None, + "Weichentyp": wt, + "Richtung": str(eintrag.get("KurvenRichtung", "")), + "Weichenwinkel": eintrag.get("KurvenWinkel", 0), + "Höhe": "2000", + "Drehung": block.get("rotation", 0.0), + "Antrieb Kurve": eintrag.get("SivasnrTEF") is not None, + "SivasNummer": str(eintrag.get("Sivasnr", "")) + } + + +def build_gerade_merkmale(block): + """Merkmale-JSON fuer eine Omniflo Gerade (AP110).""" + return { + "Anzahl der Separatoren": "0", + "Länge in Meter": "2", + "Winkel": "0", + "Anzahl der Scanner": 0, + "Höhe oben": "2000", + "Höhe unten": "2000", + "Drehung": block.get("rotation", 0.0), + "SivasNummer": "" + } + + +def build_kreisel_merkmale(block): + """Merkmale-JSON fuer einen ILS Kreisel.""" + return { + "Abstand (Kreiselachse A - Kreiselachse) in Meter": "3", + "Anzahl der Separatoren": "2", + "Kreiselart": "Standard", + "Anzahl der Scanner": "0", + "Anzahl der Rampen": "0", + "Höhe in m": "2", + "Drehrichtung": "UZS", + "Drehung": block.get("rotation", 0.0) + } + + +# --- Bekannte Blocknamen fuer nicht-Omniflo Elemente --- +KREISEL_BLOCKS = {"AN8", "SP8"} + +VARIO_BLOCKS = { + "AUS_Element_links", "EIN_Element_links", + "Staustrecke_SP_1000_mm", "Staustrecke_Separator_SP_300_mm", + "Vario_Spannstation_SP_500mm", "Vario_Motorstation_SP_500mm", +} + +# Koordinatensystem-Bloecke (ignorieren) +SKIP_BLOCKS = {"K1", "K2", "K3", "K4", "KS_EIN", "KS_AUS"} + + +def process_blocks(blocks, lookup): + """ + Verarbeitet alle Bloecke und erzeugt: + - items: Liste der CSV-Einzelzeilen + - counters: Zaehler fuer Summierungszeilen + """ + items = [] + counters = { + # Omniflo Sum + "anzahl_boegen": 0, + "anzahl_weichenkoerper": 0, + "anzahl_einzelweichen": 0, + "anzahl_deltaweichen": 0, + "anzahl_doppel_dreiwegeweichen": 0, + "anzahl_sternweichen": 0, + "gesamtlaenge_ap110": 0.0, + "gesamtlaenge_ap60": 0.0, + # ILS Automation + "anzahl_kreisel": 0, + "anzahl_kreisel_pinband": 0, + "sep_kreisel_pin": 0, + "sep_kreisel_ohne_pin": 0, + "anzahl_verbindungskreisel": 0, + "laengen_kreisel": 0.0, + "anzahl_weichen_ils": 0, + "anzahl_eckraeder": 0, + "anzahl_gefaellestrecken": 0, + "anzahl_variofoerderer": 0, + "anzahl_streckengruppen": 0, + "laengen_gefaellestrecken": 0.0, + "anzahl_motoren_vario": 0, + "laengen_variofoerderer": 0.0, + "anzahl_etf": 0, + "laengen_etf": 0.0, + "anzahl_separatoren": 0, + "anzahl_scanner": 0, + "anzahl_btbeladung": 0, + "anzahl_abwurfstationen": 0, + } + + elem_nr = 0 + bogen_count = 0 + weiche_count = {} + gerade_count = 0 + kreisel_count = 0 + + for block in blocks: + bname = block.get("block_name", "") + + # Koordinatensystem-Bloecke ueberspringen + if bname in SKIP_BLOCKS: + continue + + # Omniflo Bogen oder Weiche? + if bname in lookup: + typ, eintrag = lookup[bname] + elem_nr += 1 + shape_id = generate_shape_id() + + if typ == "bogen": + bogen_count += 1 + items.append({ + "nr": elem_nr, + "teileart": "Omniflo Kurve", + "teileid": shape_id, + "bezeichnung": f"OFBogen :{bogen_count}", + "anzahl": 1, + "merkmale": build_bogen_merkmale(block, eintrag) + }) + counters["anzahl_boegen"] += 1 + + elif typ == "weiche": + subtyp = classify_weiche(eintrag) + wt = eintrag.get("WeichenTyp", "Einzelweiche") + weiche_count[wt] = weiche_count.get(wt, 0) + 1 + + items.append({ + "nr": elem_nr, + "teileart": "Omniflo Weiche", + "teileid": shape_id, + "bezeichnung": f"OFWeiche :{weiche_count[wt]}", + "anzahl": 1, + "merkmale": build_weiche_merkmale(block, eintrag) + }) + + if subtyp == "einzelweiche": + counters["anzahl_einzelweichen"] += 1 + elif subtyp == "doppelweiche" or subtyp == "dreiwegeweiche": + counters["anzahl_doppel_dreiwegeweichen"] += 1 + elif subtyp == "deltaweiche": + counters["anzahl_deltaweichen"] += 1 + elif subtyp == "sternweiche": + counters["anzahl_sternweichen"] += 1 + elif subtyp == "weichenkoerper": + counters["anzahl_weichenkoerper"] += 1 + + continue + + # Omniflo Gerade (AP110) + if "AP110" in bname.upper() or "AP_110" in bname.upper(): + elem_nr += 1 + gerade_count += 1 + items.append({ + "nr": elem_nr, + "teileart": "Omniflo Gerade", + "teileid": generate_shape_id(), + "bezeichnung": f"OFGerade :{gerade_count}", + "anzahl": 1, + "merkmale": build_gerade_merkmale(block) + }) + counters["gesamtlaenge_ap110"] += 2000.0 + continue + + # ILS Kreisel + if bname in KREISEL_BLOCKS: + elem_nr += 1 + kreisel_count += 1 + items.append({ + "nr": elem_nr, + "teileart": "ILS 2.0 Kreisel", + "teileid": generate_shape_id(), + "bezeichnung": f"Kreisel :{kreisel_count}", + "anzahl": 1, + "merkmale": build_kreisel_merkmale(block) + }) + counters["anzahl_kreisel"] += 1 + counters["sep_kreisel_ohne_pin"] += 2 + counters["laengen_kreisel"] += 3.0 + counters["anzahl_separatoren"] += 2 + if counters["anzahl_streckengruppen"] == 0: + counters["anzahl_streckengruppen"] = 1 + continue + + # VarioFoerderer-Bloecke + is_vario = False + for vb in VARIO_BLOCKS: + if vb in bname: + is_vario = True + break + if is_vario: + counters["anzahl_variofoerderer"] += 1 + continue + + return items, counters + + +def build_ils_automation_row(elem_nr, counters): + """Erzeugt die ILS 2.0 Automation Summierungszeile.""" + merkmale = { + "Anzahl Kreisel": counters["anzahl_kreisel"], + "Anzahl Kreisel mit Pinband": counters["anzahl_kreisel_pinband"], + "Anzahl aller Separatoren in Kreisel mit PIN": counters["sep_kreisel_pin"], + "Anzahl aller Separatoren in Kreisel ohne PIN": counters["sep_kreisel_ohne_pin"], + "Anzahl Verbindungskreisel": counters["anzahl_verbindungskreisel"], + "Längen aller Kreisel": counters["laengen_kreisel"], + "Anzahl der Weichen": counters["anzahl_weichen_ils"], + "Anzahl der Eckräder": counters["anzahl_eckraeder"], + "Anzahl der einzelnen Gefällestrecken": counters["anzahl_gefaellestrecken"], + "Anzahl der einzelnen Varioförderer": counters["anzahl_variofoerderer"], + "Anzahl der Streckengruppen": counters["anzahl_streckengruppen"], + "Längen aller Gefällestrecken": counters["laengen_gefaellestrecken"], + "Anzahl der Motoren Varioförderer": counters["anzahl_motoren_vario"], + "Längen aller Varioförderer": counters["laengen_variofoerderer"], + "Anzahl ETFs": counters["anzahl_etf"], + "Längen aller ETFs": counters["laengen_etf"], + "Anzahl aller Separatoren": counters["anzahl_separatoren"], + "Anzahl aller Scanner": counters["anzahl_scanner"], + "Anzahl aller BTBeladung": counters["anzahl_btbeladung"], + "Anzahl aller Abwurfstationen": counters["anzahl_abwurfstationen"], + } + return { + "nr": elem_nr, + "teileart": "ILS 2.0 Automation", + "teileid": "autogenerated_ils_automation_json", + "bezeichnung": "Automation ILS structgen", + "anzahl": 1, + "merkmale": merkmale, + } + + +def build_omniflo_sum_row(elem_nr, counters): + """Erzeugt die Omniflo Sum Summierungszeile.""" + merkmale = { + "Anzahl Bögen": counters["anzahl_boegen"], + "Anzahl Weichengrundkörper": counters["anzahl_weichenkoerper"], + "Anzahl Einwegweichen": counters["anzahl_einzelweichen"], + "Anzahl Deltaweichen": counters["anzahl_deltaweichen"], + "Anzahl Doppelweichen und Dreiwegeweichen": counters["anzahl_doppel_dreiwegeweichen"], + "Anzahl Sternweichen": counters["anzahl_sternweichen"], + "Gesamtlänge AP110": counters["gesamtlaenge_ap110"], + "Gesamtlänge AP60": counters["gesamtlaenge_ap60"], + } + return { + "nr": elem_nr, + "teileart": "Omniflo Sum", + "teileid": "autogenerated_of_json", + "bezeichnung": "Omniflo sum", + "anzahl": 1, + "merkmale": merkmale, + } + + +def format_csv_line(item): + """Formatiert eine CSV-Zeile im geforderten Format.""" + merkmale_json = json.dumps(item["merkmale"], ensure_ascii=False) + return '{nr};"{teileart}";"{teileid}";"{bezeichnung}";{anzahl};{merkmale}'.format( + nr=item["nr"], + teileart=item["teileart"], + teileid=item["teileid"], + bezeichnung=item["bezeichnung"], + anzahl=item["anzahl"], + merkmale=merkmale_json, + ) + + +def main(): + if len(sys.argv) < 4: + print("Aufruf: python export_csv.py ") + sys.exit(1) + + raw_json_path = sys.argv[1] + data_dir = sys.argv[2] + output_csv = sys.argv[3] + + # Daten laden + blocks = load_json(raw_json_path) + boegen_path = os.path.join(data_dir, "json", "omniflo_boegen.json") + weichen_path = os.path.join(data_dir, "json", "omniflo_weichen.json") + + boegen = load_json(boegen_path) if os.path.exists(boegen_path) else [] + weichen = load_json(weichen_path) if os.path.exists(weichen_path) else [] + + lookup = build_lookup(boegen, weichen) + + print(f"[export_csv] {len(blocks)} Bloecke geladen, " + f"{len(boegen)} Boegen, {len(weichen)} Weichen im Katalog.") + + # Bloecke verarbeiten + items, counters = process_blocks(blocks, lookup) + + # Summierungszeilen anhaengen + next_nr = (items[-1]["nr"] + 1) if items else 1 + + ils_row = build_ils_automation_row(next_nr, counters) + items.append(ils_row) + + of_row = build_omniflo_sum_row(next_nr + 1, counters) + items.append(of_row) + + # CSV schreiben + header = "Elementnummer;TeileArt;TeileId;Bezeichnung;Anzahl;Merkmale" + with open(output_csv, "w", encoding="utf-8") as f: + f.write(header + "\n") + for item in items: + f.write(format_csv_line(item) + "\n") + + print(f"[export_csv] CSV geschrieben: {output_csv} ({len(items)} Zeilen)") + + +if __name__ == "__main__": + main()