From 2f982bf5f431819d600ad495cc3a7075cef60238 Mon Sep 17 00:00:00 2001 From: lertlmaier Date: Mon, 23 Jun 2025 11:55:23 +0200 Subject: [PATCH] =?UTF-8?q?drawdxf=20sammelt=20ALLE=20Atikelnummern=20(Kab?= =?UTF-8?q?el,=20Motoren,=20Sensoren),=20pr=C3=BCft=20lokale=20bezeichner.?= =?UTF-8?q?cfg,=20ob=20f=C3=BCr=20alle=20artikelnummern=20ein=20bezeichner?= =?UTF-8?q?=20vorhanden.=20=20Falls=20kein=20bezeichner=20vorhanden=20wird?= =?UTF-8?q?=20bezeichner.cfg=20um=20Block=20[Missing]=20erg=C3=A4nzt,=20un?= =?UTF-8?q?d=20SIVAS=20Datenabfrage=20daf=C3=BCr=20gestartet.=20Scahlter?= =?UTF-8?q?=20--local=20zur=20vermeidung=20der=20Abfrage=20verf=C3=BCgbar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cfg/bezeichner.cfg | 132 ++++++++++++++++++++------------------- lib/drawdxf.py | 111 +++++++++++++++----------------- lib/updateconfignames.py | 39 ++++++------ 3 files changed, 138 insertions(+), 144 deletions(-) diff --git a/cfg/bezeichner.cfg b/cfg/bezeichner.cfg index 9825509..f7b0b2b 100644 --- a/cfg/bezeichner.cfg +++ b/cfg/bezeichner.cfg @@ -1,64 +1,70 @@ -# Artikelnummern zu welchen Bezeichner fehlt (-> ggf. Abfrage starten!) -# Art-numbers to which no name is available (-> start query if needed!) -[Missing] - - -# Bezeichner jeder Verbindungsleitung (gekürzt) -# Name of every cable (shortened) [Sivasnummern] -725000015=4G1,5mm², Steuerleitung -722001300=M12 St-0°/ M8 Bu-0° 1m -722001301=M12 St-0°/ M8 Bu-0° 2m -722001302=M12 St-0°/ M8 Bu-0° 3m -722001303=M12 St-0°/ M8 Bu-0° 5m -722001304=M12 St-0°/ M8 Bu-0° 10m -722001305=M12 St-0°/ M8 Bu-0° 15m -722001306=M12 St-0°/ M8 Bu-0° 20m -722001307=M12 St-0°/ M8 Bu-0° 25m -722001308=M12 St-0°/ M8 Bu-0° 2,5m -722001309=M12 St-0°/ M8 Bu-0° 30m -722001310=M12 St-0°/ M8 Bu-0° 35m -722001311=M12 St-0°/ M8 Bu-0° 40m -722001312=M12 St-0°/ M8 Bu-0° 50m -722001330=M12 St-0°/ M12 Bu-90° 1m -722001331=M12 St-0°/ M12 Bu-90° 2m -722001332=M12 St-0°/ M12 Bu-90° 3m -722001333=M12 St-0°/ M12 Bu-90° 5m -722001334=M12 St-0°/ M12 Bu-90° 10m -722001335=M12 St-0°/ M12 Bu-90° 15m -722001336=M12 St-0°/ M12 Bu-90° 20m -722001337=M12 St-0°/ M12 Bu-90° 25m -722001338=M12 St-0°/ M12 Bu-90° 2,5 -722001339=M12 St-0°/ M12 Bu-90° 30m -722001340=M12 St-0°/ M12 Bu-90° 35m -722001341=M12 St-0°/ M12 Bu-90° 40m -722001342=M12 St-0°/ M12 Bu-90° 50m -722001352=M12 St-0°/ M12 Bu-0° 3m -722001353=M12 St-0°/ M12 Bu-0° 5m -722001354=M12 St-0°/ M12 Bu-0° 10m -722001355=M12 St-0°/ M12 Bu-0° 15m -722001356=M12 St-0°/ M12 Bu-0° 20m -722001357=M12 St-0°/ M12 Bu-0° 30m -722001358=M12 St-0°/ M12 Bu-0° 2,5m -722001359=M12 St-0°/ M12 Bu-0° 40m -726001062=Patchkabel 2xRJ45 0,25m -726001040=Patchkabel 2xRJ45 0,5m -726001041=Patchkabel 2xRJ45 1,0m -726001042=Patchkabel 2xRJ45 1,5m -726001043=Patchkabel 2xRJ45 2,0m -726001044=Patchkabel 2xRJ45 3,0m -726001045=Patchkabel 2xRJ45 5,0m -726001046=Patchkabel 2xRJ45 10m -726001047=Patchkabel 2xRJ45 15m -726001048=Patchkabel 2xRJ45 20m -726001049=Patchkabel 2xRJ45 25m -726001050=Patchkabel 2xRJ45 30m -726001051=Patchkabel 2xRJ45 35m -726001052=Patchkabel 2xRJ45 40m -726001053=Patchkabel 2xRJ45 45m -726001054=Patchkabel 2xRJ45 50m -726001055=Patchkabel 2xRJ45 55m -726001056=Patchkabel 2xRJ45 60m -726001057=Patchkabel 2xRJ45 65m -726001060=Patchkabel 2xRJ45 85m -726001061=Patchkabel 2xRJ45 95m +725000015 = 4G1,5mm², Steuerleitung +722001300 = M12 St-0°/ M8 Bu-0° 1m +722001301 = M12 St-0°/ M8 Bu-0° 2m +722001302 = M12 St-0°/ M8 Bu-0° 3m +722001303 = M12 St-0°/ M8 Bu-0° 5m +722001304 = M12 St-0°/ M8 Bu-0° 10m +722001305 = M12 St-0°/ M8 Bu-0° 15m +722001306 = M12 St-0°/ M8 Bu-0° 20m +722001307 = M12 St-0°/ M8 Bu-0° 25m +722001308 = M12 St-0°/ M8 Bu-0° 2,5m +722001309 = M12 St-0°/ M8 Bu-0° 30m +722001310 = M12 St-0°/ M8 Bu-0° 35m +722001311 = M12 St-0°/ M8 Bu-0° 40m +722001312 = M12 St-0°/ M8 Bu-0° 50m +722001330 = M12 St-0°/ M12 Bu-90° 1m +722001331 = M12 St-0°/ M12 Bu-90° 2m +722001332 = M12 St-0°/ M12 Bu-90° 3m +722001333 = M12 St-0°/ M12 Bu-90° 5m +722001334 = M12 St-0°/ M12 Bu-90° 10m +722001335 = M12 St-0°/ M12 Bu-90° 15m +722001336 = M12 St-0°/ M12 Bu-90° 20m +722001337 = M12 St-0°/ M12 Bu-90° 25m +722001338 = M12 St-0°/ M12 Bu-90° 2,5 +722001339 = M12 St-0°/ M12 Bu-90° 30m +722001340 = M12 St-0°/ M12 Bu-90° 35m +722001341 = M12 St-0°/ M12 Bu-90° 40m +722001342 = M12 St-0°/ M12 Bu-90° 50m +722001352 = M12 St-0°/ M12 Bu-0° 3m +722001353 = M12 St-0°/ M12 Bu-0° 5m +722001354 = M12 St-0°/ M12 Bu-0° 10m +722001355 = M12 St-0°/ M12 Bu-0° 15m +722001356 = M12 St-0°/ M12 Bu-0° 20m +722001357 = M12 St-0°/ M12 Bu-0° 30m +722001358 = M12 St-0°/ M12 Bu-0° 2,5m +722001359 = M12 St-0°/ M12 Bu-0° 40m +726001062 = Patchkabel 2xRJ45 0,25m +726001040 = Patchkabel 2xRJ45 0,5m +726001041 = Patchkabel 2xRJ45 1,0m +726001042 = Patchkabel 2xRJ45 1,5m +726001043 = Patchkabel 2xRJ45 2,0m +726001044 = Patchkabel 2xRJ45 3,0m +726001045 = Patchkabel 2xRJ45 5,0m +726001046 = Patchkabel 2xRJ45 10m +726001047 = Patchkabel 2xRJ45 15m +726001048 = Patchkabel 2xRJ45 20m +726001049 = Patchkabel 2xRJ45 25m +726001050 = Patchkabel 2xRJ45 30m +726001051 = Patchkabel 2xRJ45 35m +726001052 = Patchkabel 2xRJ45 40m +726001053 = Patchkabel 2xRJ45 45m +726001054 = Patchkabel 2xRJ45 50m +726001055 = Patchkabel 2xRJ45 55m +726001056 = Patchkabel 2xRJ45 60m +726001057 = Patchkabel 2xRJ45 65m +726001060 = Patchkabel 2xRJ45 85m +726001061 = Patchkabel 2xRJ45 95m +720002003 = SENSOR INDUK. M18x1, SA=12MM N-BÜ/NO, KABEL-150MM +610554001 = Sensorhalter für WT18-3P420 oder -BG +610554002 = CPC Sensorhalter mit Reflextaster P1NH601 -BG +827072112 = Traversenabfrage ST kpl. mit Lichttaster +827072111 = Traversenabfrage AP110 mit Lichttaster +827072013 = STATISCHE TROLLEYABFRAGE L=700 FÜR AP110 + +[Missing] +720000011 = +8270720111 = +8270720112 = +8270720113 = + diff --git a/lib/drawdxf.py b/lib/drawdxf.py index d652bda..565376b 100644 --- a/lib/drawdxf.py +++ b/lib/drawdxf.py @@ -10,7 +10,7 @@ from openpyxl import Workbook import math from collections import defaultdict import configparser -from updateconfignames import get_sivas_teilestamm, read_bezeichner_from_csv +import updateconfignames as uc @dataclass @@ -266,15 +266,36 @@ def parse_sensors_from_json(positions_json): sensors[name] = sensor return sensors -def export_excel(plines, out_path): +def export_excel(plines, sens2cable, out_path): # Hier für Excel Export print("creating excel file with cable information ..") - write_excel_from_json(plines, out_path) + write_excel_from_json(plines, sens2cable, out_path) print("done") -def write_excel_from_json(plines:Polylines, outpath:str): +def get_all_artnrs(plines: Polylines, sens2cable: dict) -> set: + all_artnrs = set() + for pl in plines.kabel: + if pl.s_artinr: + all_artnrs.add(pl.s_artinr) + + for cablelist in sens2cable.values(): + for artnr in cablelist: + if isinstance(artnr, str) and artnr.isdigit(): + all_artnrs.add(artnr) + return all_artnrs + +def mark_missings(all_artnrs): + if "Sivasnummern" not in bezeichner_cfg: + bezeichner_cfg["Sivasnummern"] = {} + if "Missing" not in bezeichner_cfg: + bezeichner_cfg["Missing"] = {} + + for artnr in all_artnrs: + if artnr not in bezeichner_cfg["Sivasnummern"]: + bezeichner_cfg["Missing"][artnr] = "" + +def write_excel_from_json(plines:Polylines, sens2cable: dict, outpath:str): wb = Workbook() - sens2cable = map_sensor_to_cable_cfg(plines) # Dicts für Anzahl bzw kummulierte Länge count_summary = defaultdict(int) length_summary = defaultdict(float) @@ -290,11 +311,11 @@ def write_excel_from_json(plines:Polylines, outpath:str): ws1.column_dimensions['D'].width = 25 for pl in plines.kabel: - length = pl.length /1000 # Umrechnung von mm in m + length = round(pl.length /1000 , 1) # Umrechnung von mm in m for artnr in sens2cable[pl.id]: cable_name = "" if artnr.isdigit(): - cable_name = cable_cfg["Sivasnummern"][artnr] + cable_name = bezeichner_cfg["Sivasnummern"][artnr] ws1.append([pl.id, length, artnr, cable_name]) if "MA" in pl.id: @@ -465,53 +486,10 @@ def get_cable_artnr(section, length): return True, entries[str(l)] return False, f"Kabel länger als max. Kabellänge in Sektion '{section}'" - -def map_sensor_to_cable(plines): - sens2cable = defaultdict(list) - - for pl in plines.kabel: - sensor_name = pl.id.split('-')[-1] - cable_length = pl.length/1000 - sensor_artinr = pl.s_artinr - name_prefix = sensor_name[0:2] - - sections = [] #Default - - if name_prefix == "MA": - sections = ["MA"] # Sektion für Motor-Kabel - - elif name_prefix == "MB": - sections = ["WD_Q"] # Sektion für Ventil-Kabel - - elif name_prefix == "QM": - sections = ["WD_Q"] # Sektion für Ventil-Kabel - - elif sensor_name.startswith("B"): # Sensor Name beginnt mit B -> Sensor jeglicher Art - if name_prefix == "BX": # Wenn "BX", dann Scanner - sections = ["WF_B", "WD_I"] # Sektion für Scanner Patch-Kabel und Sektion für Standard-Sensorkabel zur Spannungsversorgung - cable_length = max(0.0, cable_length -4.0) # 4 Meter abziehen, da bereits 5m Kabelschwanz an Scanner dran - elif sensor_artinr == 829422026: - sections = ["WD_I-829422026"] - elif sensor_artinr == 720002003: - sections = ["WD_I-720002003"] - else: - sections = ["WD_I"] - - if not sections: - sens2cable[pl.id].append ("Kein Kabeltyp zugewiesen") - continue - - for section in sections: - cable_artnr = get_cable_artnr(section, cable_length) - if cable_artnr is None: - sens2cable[pl.id].append(f"Kabel länger als max. Kabellänge") - else: - sens2cable[pl.id].append(cable_artnr) - - return sens2cable def map_sensor_to_cable_cfg(plines): sens2cable = defaultdict(list) + mapping = config_BMK["Cable-Mapping"] for pl in plines.kabel: @@ -540,10 +518,7 @@ def map_sensor_to_cable_cfg(plines): # Kabel-Atikelnummer innerhalb der Sektion der kabel.cfg bestimmen for section in sections: sucess, result = get_cable_artnr(section, cable_length) - if not sucess: - sens2cable[pl.id].append(result) - else: - sens2cable[pl.id].append(result) + sens2cable[pl.id].append(result) return sens2cable @@ -588,10 +563,10 @@ if __name__ == '__main__': config_BMK.optionxform = lambda option: option # preserve case for letters config_BMK.read(os.path.join(config_dir, "BMK.cfg")) - names_cfg = configparser.ConfigParser() - names_cfg.optionxform = str #Keys case-sensitive + bezeichner_cfg = configparser.ConfigParser() + bezeichner_cfg.optionxform = str #Keys case-sensitive with open(os.path.join(config_dir, "bezeichner.cfg"), encoding="utf-8") as f: - names_cfg.read_file(f) + bezeichner_cfg.read_file(f) dxf_file = args.dxf @@ -626,8 +601,20 @@ if __name__ == '__main__': res_pos = new_dxf(plines, out_path) if args.excel: - excel_path = os.path.join(work_dir, args.excel) - export_excel(plines, excel_path) + # 1. Kabelzuordnung + sens2cable = map_sensor_to_cable_cfg(plines) - - + # 2. verwendete Artikelnummer (Sensoren + Kabel) + all_artnrs = get_all_artnrs(plines, sens2cable) + + # 3. Fehlende Bezeichner in bezeichner.cfg markieren + mark_missings(all_artnrs) + + # 4. Bezeichner ggf. aus SIVAS abrufen + if args.local == False: + config_path=os.path.join(config_dir, "bezeichner.cfg") + uc.update_bezeichner(bezeichner_cfg, config_path) + + # 5. Excel schreiben + excel_path = os.path.join(work_dir, args.excel) + export_excel(plines, sens2cable, excel_path) \ No newline at end of file diff --git a/lib/updateconfignames.py b/lib/updateconfignames.py index b15df6b..aab5adc 100644 --- a/lib/updateconfignames.py +++ b/lib/updateconfignames.py @@ -3,22 +3,21 @@ import configparser import os import subprocess import csv +import pandas as pd -def get_sivas_teilestamm(sivas_ids, out_dir=None, overwrite=True): +def get_sivas_teilestamm(sivas_ids, overwrite=True, out_dir= os.environ.get('PROJECT_DATA')): """ Generiert für jede angegebene SIVAS-Teilenummer eine CSV-Datei, in der die Teilestamm-Informationen gespeichert sind """ sivas_teilestamm_exe = os.environ.get('SIVAS_TEILESTAMM') + if not os.path.isfile(sivas_teilestamm_exe): raise FileNotFoundError( f"Programmpfad '{sivas_teilestamm_exe}' nicht erreichbar. " f"Bitte Zugriff und Variable SIVAS_TEILESTAMM in setenv.bat prüfen." ) - if out_dir is None: - out_dir = os.environ.get('PROJECT_DATA') - if not os.path.isdir(out_dir): raise NotADirectoryError( f"Ausgabeverzeichnis '{out_dir}' existiert nicht. " @@ -51,10 +50,6 @@ def get_sivas_teilestamm(sivas_ids, out_dir=None, overwrite=True): rows = list(csv_data) if len(rows) != 2: raise ValueError(f"SIVAS-Teilenummer '{id}' existiert nicht oder liefert keine gültige CSV.") - # Rückgabe des Bezeichners (zweite Zeile, zweite Spalte) - yield id, rows[1][1].strip() - - def read_bezeichner_from_csv(teilenr): """ @@ -64,28 +59,32 @@ def read_bezeichner_from_csv(teilenr): :return: Bezeichner-String, falls erfolgreich. Wirft Exception bei Fehler. """ csv_dir = os.environ.get("PROJECT_DATA", ".") - csv_path = os.path.join(csv_dir, f"{teilenr}.csv") if not os.path.isfile(csv_path): raise FileNotFoundError(f"CSV für Teilenummer '{teilenr}' nicht gefunden unter {csv_path}") - with open(csv_path, encoding="utf-8", newline="") as f: - reader = csv.reader(f, delimiter=";") - rows = list(reader) - if len(rows) != 2: - raise ValueError(f"Ungültiges Format oder keine Daten in CSV für '{teilenr}'") + try: + df = pd.read_csv(csv_path, sep=";", encoding="utf-8") + except Exception as e: + raise ValueError(f"Fehler beim Einlesen der CSV: {e}") - bezeichner = rows[1][1].strip() - return bezeichner + if df.shape[0] != 1: + raise ValueError(f"Ungültiger Zeileninhalt für '{teilenr}' – erwartet genau eine Datenzeile.") + + if "Bezeichnung1" not in df.columns: + raise ValueError(f"Spalte 'Bezeichnung1' nicht gefunden in CSV für '{teilenr}'.") + + return str(df["Bezeichnung1"].iloc[0]).strip() -def update_bezeichner(names_config): +def update_bezeichner(names_config, config_path): if "Missing" not in names_config or not names_config["Missing"]: print("Keine fehlenden Einträge in [Missing]. Keine Abfrage angestoßen.") return missing_ids = list(names_config["Missing"].keys()) + resolved_ids = [] print(f"Starte SIVAS-Abfrage für {len(missing_ids)} fehlende Teilenummer(n)...") @@ -93,20 +92,22 @@ def update_bezeichner(names_config): get_sivas_teilestamm(missing_ids) except Exception as e: print(f"Fehler bei der SIVAS-Abfrage: {e}") - return updated = 0 + + for id in missing_ids: try: bezeichner = read_bezeichner_from_csv(id) if "Sivasnummern" not in names_config: names_config["Sivasnummern"] = {} names_config["Sivasnummern"][id] = bezeichner + resolved_ids.append(id) updated += 1 except Exception as e: print(f"WARNUNG: Bezeichner für {id} konnte nicht gelesen werden: {e}") - for id in missing_ids: + for id in resolved_ids: names_config["Missing"].pop(id, None) if "Missing" in names_config and not names_config["Missing"]: