This commit is contained in:
2025-07-24 17:20:41 +02:00
2 changed files with 167 additions and 13 deletions
+17 -1
View File
@@ -64,7 +64,23 @@
827072013 = STATISCHE TROLLEYABFRAGE L=700 FÜR AP110
827072111 = Traversenabfrage AP110 mit Lichttaster
827072112 = Traversenabfrage ST kpl. mit Lichttaster
827072113 = Traversenabfrage KF kpl. mit Lichttaster
829422026 = INDUKT. SENSOR LP KPL.
720002011 = Sensor Induk. M18 8mm bündig/PNP-NO/ST M12
703001017 = HILFSCHALTER MIT ZUGFEDERANSCHLUSS F. 3RV2011
725000161 = 4G1,5 mm², Steuerleitung Halogenfrei
725000163 = 7G1,5 mm², Steuerleitung Halogenfrei
725000164 = 12G1,5 mm², Steuerleitung Halogenfrei
790000057 = Sirius act Tableau 4 F "LM-GN+PDT-GE+LDT-BL+NA"
790801501 = Busverteiler kpl. ET 200SP DI+DQ, Exact -BG
790820400 = Unterverteiler 1 800x2100x400 -BG
790821106 = SCHALTSCHRANK 2000X2100X400 (HGF) -BG
829434314 = PINABFRAGE LI MECHANISCH KPL.
829534306 = Camera DM280 m. Halter (ILS) - BG
829624040 = UMLENKFLANSCH-c 800 MIT INKEMENTALGEBER KPL.
929012655 = DRUCKSCHALTER PM1-M3-G014
200000375 = Halter mit Sensor für Abfrage VARIO Finger
829434306 = INKREMENTALGEBER 800 5V KPL.
[Missing]
827072111 =
+150 -12
View File
@@ -75,7 +75,12 @@ def add_polyline(msp, points:Polyline, dxf_attribs):
pline.rgb = (255, 128, 0)
def new_dxf(plines, out_path):
""" creates a new dxf file with a polyline inside which is created by the given json file
"""
Erstellt eine neue DXF-Datei mit den gegebenen Polylinien und speichert sie unter dem angegebenen Pfad.
Args:
plines (Polylines): Die Polylinien- und Geometriedaten, die gezeichnet werden sollen.
out_path (str): Der Pfad, unter dem die neue DXF-Datei gespeichert wird.
"""
doc = ezdxf.new('R2018', setup=True)
doc.header['$INSUNITS'] = 4 # Millimeter
@@ -88,7 +93,12 @@ def new_dxf(plines, out_path):
print("Cable-Routes exported to new dxf-file")
def modify_original_dxf(plines, originaldxf):
""" adds new layer to original .dxf-file that contains cables
"""
Fügt einer bestehenden DXF-Datei einen neuen Layer mit Kabeln hinzu und speichert das Ergebnis.
Args:
plines (Polylines): Die Polylinien- und Geometriedaten, die gezeichnet werden sollen.
originaldxf (str): Pfad zur bestehenden DXF-Datei, die modifiziert werden soll.
"""
print("adding cables into original .dxf ..")
@@ -100,6 +110,13 @@ def modify_original_dxf(plines, originaldxf):
def draw_cables(plines, doc):
"""
Zeichnet alle Kabel-Polylinien in das DXF-Dokument auf einen eigenen Layer.
Args:
plines (Polylines): Die Polylinien- und Geometriedaten.
doc (ezdxf.document.Drawing): Das zu bearbeitende DXF-Dokument.
"""
msp = doc.modelspace()
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M")
cable_layer = f"cables_{timestamp}"
@@ -116,6 +133,13 @@ def draw_cables(plines, doc):
add_polyline(msp, pl, dxfattribs_cable)
def draw_racks(plines, doc):
"""
Zeichnet die Racks als 3D-Polylinien und platziert Rack-Namen als Text im DXF-Dokument.
Args:
plines (Polylines): Die Polylinien- und Geometriedaten.
doc (ezdxf.document.Drawing): Das zu bearbeitende DXF-Dokument.
"""
msp = doc.modelspace()
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M")
rack_layer = f"racks_{timestamp}"
@@ -186,6 +210,13 @@ def find_close_key(pos2sensors, x, y, tolerance=10): # !!! Toleranz nicht
return None
def draw_sensors(plines, doc):
"""
Zeichnet die Sensoren als Textobjekte an den Endpunkten der Kabel und gruppiert sie ggf. gestaffelt.
Args:
plines (Polylines): Die Polylinien- und Geometriedaten.
doc (ezdxf.document.Drawing): Das zu bearbeitende DXF-Dokument.
"""
msp = doc.modelspace()
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M")
sensor_layer = f"sensors_{timestamp}"
@@ -220,6 +251,13 @@ def draw_sensors(plines, doc):
text.set_placement(placement_info["placement"])
def draw_subdists(plines, doc):
"""
Zeichnet die Unterverteiler (Subdistributoren) als Textobjekte an den Startpunkten der Kabel.
Args:
plines (Polylines): Die Polylinien- und Geometriedaten.
doc (ezdxf.document.Drawing): Das zu bearbeitende DXF-Dokument.
"""
msp = doc.modelspace()
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M")
subdist_layer = f"subdists_{timestamp}"
@@ -297,6 +335,15 @@ def _calculate_text_placement(pt1, pt2, text_type='sensor', item_index=0, total_
}
def model_from_json(json_file):
"""
Lädt die Polylinien- und Geometriedaten aus einer JSON-Datei und gibt ein Polylines-Objekt zurück.
Args:
json_file (str): Pfad zur JSON-Datei.
Returns:
Polylines: Das geladene Polylinien-Objekt.
"""
with open(json_file, encoding='utf-8') as fh:
data = json.load(fh)
@@ -307,6 +354,15 @@ def model_from_json(json_file):
return plines
def parse_sensors_from_json(positions_json):
"""
Liest Sensordaten aus einer JSON-Datei und gibt ein Dictionary mit Sensorobjekten zurück.
Args:
positions_json (str): Pfad zur JSON-Datei mit Sensorpositionen.
Returns:
dict: Dictionary mit Sensorname als Key und Sensorobjekt als Value.
"""
with open(positions_json, encoding='utf-8') as fh:
data = json.load(fh)
@@ -321,6 +377,16 @@ def parse_sensors_from_json(positions_json):
return sensors
def get_all_artnrs(plines: Polylines, sens2cable: dict) -> set:
"""
Sammelt alle verwendeten Artikelnummern aus den Polylinien und der Sensor-Kabel-Zuordnung.
Args:
plines (Polylines): Die Polylinien- und Geometriedaten.
sens2cable (dict): Mapping von Sensor-IDs zu Kabel-Artikelnummern.
Returns:
set: Menge aller gefundenen Artikelnummern.
"""
all_artnrs = set()
for pl in plines.kabel:
if pl.s_artinr:
@@ -333,6 +399,12 @@ def get_all_artnrs(plines: Polylines, sens2cable: dict) -> set:
return all_artnrs
def mark_missings(all_artnrs):
"""
Markiert fehlende Artikelnummern im Bezeichner-Config, falls sie nicht vorhanden sind.
Args:
all_artnrs (set): Menge aller verwendeten Artikelnummern.
"""
if "Sivasnummern" not in bezeichner_cfg:
bezeichner_cfg["Sivasnummern"] = {}
if "Missing" not in bezeichner_cfg:
@@ -344,7 +416,13 @@ def mark_missings(all_artnrs):
def write_excel_from_json(plines: Polylines, sens2cable: dict, outpath: str, with_bom=True):
"""
Steuert die Erstellung der Excel-Reports (Kabelübersicht und Stückliste).
Erstellt Excel-Reports (Kabelübersicht, Fehlerlisten, Stückliste) aus den Polylinien- und Zuordnungsdaten.
Args:
plines (Polylines): Die Polylinien- und Geometriedaten.
sens2cable (dict): Mapping von Sensor-IDs zu Kabel-Artikelnummern.
outpath (str): Pfad zur Ausgabedatei.
with_bom (bool, optional): Ob zusätzlich eine Stückliste (BOM) erzeugt werden soll. Default: True.
"""
# 1. Daten aggregieren und für die Reports vorbereiten
processed_data = _process_cable_data(plines, sens2cable)
@@ -376,8 +454,14 @@ def _get_sivas_name(artnr, bezeichner_cfg):
def _process_cable_data(plines: Polylines, sens2cable: dict):
"""
Verarbeitet die Kabeldaten und aggregiert sie für die Excel-Ausgabe.
Gibt mehrere Dictionaries mit zusammengefassten Daten zurück.
Aggregiert und bereitet die Kabeldaten für die Excel-Ausgabe auf.
Args:
plines (Polylines): Die Polylinien- und Geometriedaten.
sens2cable (dict): Mapping von Sensor-IDs zu Kabel-Artikelnummern.
Returns:
dict: Verschiedene aggregierte Auswertungen für die Excel-Ausgabe.
"""
# Globale Zusammenfassungen
artnr_to_stückware_count = defaultdict(int)
@@ -431,7 +515,15 @@ def _process_cable_data(plines: Polylines, sens2cable: dict):
}
def _create_cable_list_sheet(ws, plines, sens2cable, bezeichner_cfg):
"""Erstellt das Arbeitsblatt 'Length by ID'."""
"""
Erstellt das Arbeitsblatt 'Length by ID' mit allen Kabeln und deren Längen.
Args:
ws (Worksheet): Das Excel-Arbeitsblatt.
plines (Polylines): Die Polylinien- und Geometriedaten.
sens2cable (dict): Mapping von Sensor-IDs zu Kabel-Artikelnummern.
bezeichner_cfg (ConfigParser): Konfiguration mit Artikelnummern-Namen.
"""
ws.title = "Length by ID"
ws.append(["Cable-ID", "True Length (m)", "Cable-ArtNr", "Cable-Name (short)"])
ws.column_dimensions['A'].width = 18
@@ -446,7 +538,13 @@ def _create_cable_list_sheet(ws, plines, sens2cable, bezeichner_cfg):
ws.append([pl.id, length, artnr, cable_name])
def _create_cable_summary_sheet(ws, processed_data):
"""Erstellt das Arbeitsblatt 'Cables SIVAS'."""
"""
Erstellt das Arbeitsblatt 'Cables SIVAS' mit einer Zusammenfassung der Kabel nach Artikelnummer.
Args:
ws (Worksheet): Das Excel-Arbeitsblatt.
processed_data (dict): Aggregierte Kabeldaten.
"""
ws.title = "Cables SIVAS"
ws.append(["Cable-ArtNr", "Amount (pcs)", "Cumm. Length (m)"])
ws.column_dimensions['A'].width = 20
@@ -465,7 +563,13 @@ def _create_cable_summary_sheet(ws, processed_data):
])
def _create_rack_lengths_sheet(ws, plines):
"""Erstellt das Arbeitsblatt 'Rack-Lengths'."""
"""
Erstellt das Arbeitsblatt 'Rack-Lengths' mit den Längen der Racks.
Args:
ws (Worksheet): Das Excel-Arbeitsblatt.
plines (Polylines): Die Polylinien- und Geometriedaten.
"""
ws.title = "Rack-Lengths"
ws.append(["Rack-ID", "Length (m)"])
ws.column_dimensions['A'].width = 18
@@ -479,7 +583,13 @@ def _create_rack_lengths_sheet(ws, plines):
ws.append([rackname, rack_geom.length])
def _create_error_sheets(wb, plines):
"""Erstellt die Arbeitsblätter für alle aufgetretenen Fehler."""
"""
Erstellt die Arbeitsblätter für alle aufgetretenen Fehler (Equipment-Connection, Routing, Attribute).
Args:
wb (Workbook): Das Excel-Workbook.
plines (Polylines): Die Polylinien- und Geometriedaten.
"""
# Sheet: ERR-Equipment-Connection
if plines.errors_sensors or plines.errors_dists:
ws = wb.create_sheet("ERR-Equipment-Connection")
@@ -520,7 +630,14 @@ def _create_error_sheets(wb, plines):
ws.append([sname, err_msg])
def _create_bom_workbook(outpath, processed_data, bezeichner_cfg):
"""Erstellt eine separate Excel-Arbeitsmappe für die Stückliste (BOM)."""
"""
Erstellt eine separate Excel-Arbeitsmappe für die Stückliste (BOM) und speichert sie ab.
Args:
outpath (str): Basis-Pfad für die Ausgabedatei.
processed_data (dict): Aggregierte Kabel- und Sensor-Daten.
bezeichner_cfg (ConfigParser): Konfiguration mit Artikelnummern-Namen.
"""
wb = Workbook()
# Daten aus dem verarbeiteten Dictionary extrahieren
@@ -600,6 +717,13 @@ def check_file_in_work(work_dir, filename):
return (mypath, fexists)
def copy_layers_into_dxf_by_filter(dxf_source: ezdxf.document.Drawing, dxf_target:ezdxf.document.Drawing):
"""
Kopiert bestimmte Layer (nach Filter) von einer Quell-DXF in eine Ziel-DXF.
Args:
dxf_source (ezdxf.document.Drawing): Quell-DXF-Dokument.
dxf_target (ezdxf.document.Drawing): Ziel-DXF-Dokument.
"""
msp_source = dxf_source.modelspace()
msp_target = dxf_target.modelspace()
@@ -659,8 +783,13 @@ def copy_layers_into_dxf_by_filter(dxf_source: ezdxf.document.Drawing, dxf_targe
def get_cable_artnr(section, length):
"""
Sucht in der angegebenen Config-Section die passende Kabel-Artikelnr. für die gegebene Länge.
Übergibt bei Treffer True, artnr
bei Fehler: False, errmsg
Args:
section (str): Name der Kabel-Section in der Config.
length (float): Kabellänge in Metern.
Returns:
tuple: (True, artnr) bei Erfolg, (False, Fehlermeldung) bei Fehler.
"""
# Existiert über BMK vergebene Kabel-Kennzeichnung in kabel.cfg?
if section not in cable_cfg:
@@ -676,6 +805,15 @@ def get_cable_artnr(section, length):
return False, f"Kabel länger als max. Kabellänge in Sektion '{section}'"
def map_sensor_to_cable_cfg(plines):
"""
Ordnet jedem Sensor/Kabel die passende Kabel-Artikelnummer gemäß BMK-Config und Kabel-Config zu.
Args:
plines (Polylines): Die Polylinien- und Geometriedaten.
Returns:
dict: Mapping von Kabel-IDs zu Artikelnummern.
"""
sens2cable = defaultdict(list)
mapping = config_BMK["Cable-Mapping"]