This commit is contained in:
2025-07-24 09:45:18 +02:00
6 changed files with 97956 additions and 51 deletions
+1 -1
View File
@@ -49,7 +49,7 @@ if not exist "%PROJECT_WORK%\%JSON_TODRAW%" (
) )
echo. echo.
echo === Writing Output Files === echo === Writing Output Files ===
call draw_dxf.bat --filename %JSON_TODRAW% --new %DXF_RES% -x %EXCEL_RES% -l call draw_dxf.bat --filename %JSON_TODRAW% --new %DXF_RES% -x %EXCEL_RES%
if not exist "%PROJECT_WORK%\%EXCEL_RES%" ( if not exist "%PROJECT_WORK%\%EXCEL_RES%" (
@echo -failed- draw_dxf @echo -failed- draw_dxf
pause pause
+29 -14
View File
@@ -1,5 +1,8 @@
[Sivasnummern] [Sivasnummern]
725000015 = 4G1,5mm², Steuerleitung 200000248 = Lichttaster LP+SP als Staumelder
610554001 = Sensorhalter für WT18-3P420 oder -BG
610554002 = CPC Sensorhalter mit Reflextaster P1NH601 -BG
720002003 = SENSOR INDUK. M18x1, SA=12MM N-BÜ/NO, KABEL-150MM
722001300 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 1m 722001300 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 1m
722001301 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 2m 722001301 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 2m
722001302 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 3m 722001302 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 3m
@@ -9,6 +12,7 @@
722001306 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 20m 722001306 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 20m
722001307 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 25m 722001307 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 25m
722001308 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 2,5m 722001308 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 2,5m
722001309 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 30m
722001310 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 35m 722001310 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 35m
722001311 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 40m 722001311 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 40m
722001312 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 50m 722001312 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 50m
@@ -33,7 +37,8 @@
722001357 = Verb.-Leitung M12 St-0°/ M12 Bu-0° PUR UL/CSA 30m 722001357 = Verb.-Leitung M12 St-0°/ M12 Bu-0° PUR UL/CSA 30m
722001358 = Verb.-Leitung M12 St-0°/ M12 Bu-0° PUR UL/CSA 2,5m 722001358 = Verb.-Leitung M12 St-0°/ M12 Bu-0° PUR UL/CSA 2,5m
722001359 = Verb.-Leitung M12 St-0°/ M12 Bu-0° PUR UL/CSA 40m 722001359 = Verb.-Leitung M12 St-0°/ M12 Bu-0° PUR UL/CSA 40m
726001062 = Patchkabel Cat6A 4P26 S/FTP 2xRJ45 0,25m grün 723000013 = ADERLEITUNG H07V-K 6MM² GELB/GRÜN (RING a`100m)
725000015 = 4G1,5mm², Steuerleitung
726001040 = Patchkabel Cat6A 4P26 S/FTP 2xRJ45 0,5m grün 726001040 = Patchkabel Cat6A 4P26 S/FTP 2xRJ45 0,5m grün
726001041 = Patchkabel Cat6A 4P26 S/FTP 2xRJ45 1,0m grün 726001041 = Patchkabel Cat6A 4P26 S/FTP 2xRJ45 1,0m grün
726001042 = Patchkabel Cat6A 4P26 S/FTP 2xRJ45 1,5m grün 726001042 = Patchkabel Cat6A 4P26 S/FTP 2xRJ45 1,5m grün
@@ -54,20 +59,30 @@
726001057 = Patchkabel Cat6A 4P26 S/FTP 2xRJ45 65m grün 726001057 = Patchkabel Cat6A 4P26 S/FTP 2xRJ45 65m grün
726001060 = Patchkabel Cat6A 4P26 S/FTP 2xRJ45 85m grün 726001060 = Patchkabel Cat6A 4P26 S/FTP 2xRJ45 85m grün
726001061 = Patchkabel Cat6A 4P26 S/FTP 2xRJ45 95m grün 726001061 = Patchkabel Cat6A 4P26 S/FTP 2xRJ45 95m grün
722001309 = Verb.-Leitung M12 St-0°/ M8 Bu-0° PUR UL/CSA 30m 726001062 = Patchkabel Cat6A 4P26 S/FTP 2xRJ45 0,25m grün
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
790902001 = E-Teile für SEW Motor ASE1/HAN10ES - BG 790902001 = E-Teile für SEW Motor ASE1/HAN10ES - BG
200000248 = Lichttaster LP+SP als Staumelder 827072013 = STATISCHE TROLLEYABFRAGE L=700 FÜR AP110
723000013 = ADERLEITUNG H07V-K 6MM² GELB/GRÜN (RING a`100m) 827072111 = Traversenabfrage AP110 mit Lichttaster
827072112 = Traversenabfrage ST kpl. mit Lichttaster
[Missing] [Missing]
8270720111 = 827072111 =
8270720112 = 827072112 =
8270720113 = 827072113 =
720000011 = 720000011 =
829422026 =
720002011 =
703001017 =
725000161 =
725000163 =
725000164 =
790000057 =
790801501 =
790820400 =
790821106 =
829434314 =
829534306 =
829624040 =
929012655 =
+1
View File
@@ -83,6 +83,7 @@ def new_dxf(plines, out_path):
out_path (str): Der Pfad, unter dem die neue DXF-Datei gespeichert wird. out_path (str): Der Pfad, unter dem die neue DXF-Datei gespeichert wird.
""" """
doc = ezdxf.new('R2018', setup=True) doc = ezdxf.new('R2018', setup=True)
doc.header['$INSUNITS'] = 4 # Millimeter
draw_cables(plines, doc) draw_cables(plines, doc)
draw_sensors(plines, doc) draw_sensors(plines, doc)
draw_subdists(plines, doc) draw_subdists(plines, doc)
+52 -35
View File
@@ -7,11 +7,8 @@ import sys
from pathlib import Path from pathlib import Path
import ezdxf import ezdxf
import ezdxf.filemanagement
from ezdxf.addons import iterdxf from ezdxf.addons import iterdxf
from shapely.geometry import Point from shapely.geometry import Point
# Fix import for DXFStructureError
from ezdxf.lldxf.const import DXFStructureError from ezdxf.lldxf.const import DXFStructureError
@@ -70,6 +67,38 @@ def get_attributes_of_insert(d_insert: dict, d_pos: dict) -> tuple[dict, str, st
ld = d_insert ld = d_insert
typ = 'unknown' typ = 'unknown'
# die neueren Bläcke heissen nicht IO, sondern haben einen Namen
if "IO" in d_insert:
pos = d_pos["IO"]
typ = get_type_of_name_cfg(d_insert["IO"])
# neuer Block: Enthält alles was nötig is
if "ARTINR" in d_insert and "SPS" in d_insert:
id_ = d_insert["IO"]+"@"+d_insert["SPS"]
ld["pos"] = (pos[0], pos[1])
if "REALE_POSITION" in d_insert and d_insert["REALE_POSITION"] == 'x':
pos = d_pos["REALE_POSITION"]
else:
id_ = d_insert["IO"]
# Sensoren werden später gemerged mit den anderen Blöcken des Rahmens mit A,B,C, usw
if "SPS" in d_insert and typ != "Sensor":
id_ = id_+"@"+d_insert["SPS"]
if "REALE_POSITION" in d_insert and d_insert["REALE_POSITION"] == 'x':
pos = d_pos["REALE_POSITION"]
# Hoehe und Breite von "x" addieren, um Mittelpunkt zu finden
breite_marker = config.getfloat("GetPos-Geom-Sensor", "Breite")
hoehe_marker = config.getfloat("GetPos-Geom-Sensor", "Hoehe")
midx = pos[0] + breite_marker * 0.5
midy = pos[1] + hoehe_marker * 0.5
ld["pos"] = (round(midx, 1), round(midy, 1))
else:
ld["pos"] = (pos[0], pos[1])
# die neueren Bläcke heissen nicht IO, sondern haben einen Namen # die neueren Bläcke heissen nicht IO, sondern haben einen Namen
if "NAME" in d_insert: if "NAME" in d_insert:
typ = get_type_of_name_cfg(d_insert["NAME"]) typ = get_type_of_name_cfg(d_insert["NAME"])
@@ -77,28 +106,7 @@ def get_attributes_of_insert(d_insert: dict, d_pos: dict) -> tuple[dict, str, st
pos = d_pos["NAME"] pos = d_pos["NAME"]
ld["pos"] = (pos[0], pos[1]) ld["pos"] = (pos[0], pos[1])
if "IO" in d_insert: if "B" in d_insert and "IO" not in d_insert:
attr_text = d_insert["IO"]
typ = get_type_of_name_cfg(attr_text)
id_ = d_insert["IO"]
# Sensoren werden später gemerged mit den anderen Blöcken des Rahmens mit A,B,C, usw
if "SPS" in d_insert and typ != "Sensor":
id_ = id_+"@"+d_insert["SPS"]
pos = d_pos["IO"]
if "REALE_POSITION" in d_insert and d_insert["REALE_POSITION"] == 'x':
pos = d_pos["REALE_POSITION"]
# Hoehe und Breite von "x" addieren, um Mittelpunkt zu finden
breite_marker = config.getfloat("GetPos-Geom-Sensor", "Breite")
hoehe_marker = config.getfloat("GetPos-Geom-Sensor", "Hoehe")
midx = pos[0] + breite_marker * 0.5
midy = pos[1] + hoehe_marker * 0.5
ld["pos"] = (round(midx, 1), round(midy, 1))
else:
ld["pos"] = (pos[0], pos[1])
if "B" in d_insert:
attr_text = d_insert["B"] attr_text = d_insert["B"]
typ = get_type_of_name_cfg(attr_text) typ = get_type_of_name_cfg(attr_text)
id_ = attr_text id_ = attr_text
@@ -217,11 +225,14 @@ def extract_input_positions(insert_iterable) -> tuple[dict, dict, dict, dict]:
ld, id_, typ = get_attributes_of_insert(insert, pos) ld, id_, typ = get_attributes_of_insert(insert, pos)
if typ == "Sensor": if typ == "Sensor":
# wenn NAME enthalten ist, dann ist das ein eindeutiger Bezeichner # wenn NAME enthalten ist, z.B. bei Erdungslayouts, dann ist das ein eindeutiger Bezeichner
if "NAME" in ld: if "NAME" in ld:
all_sensors[id_] = ld all_sensors[id_] = ld
# neuer Block der alle nötigen Infos enthält
elif "IO" in ld and "ARTINR" in ld :
all_sensors[id_] = ld
# alle anderen Sachen werden dann aus mehreren Rahmen zusammen gesammelt
else: else:
# alle anderen Sachen werden dann aus mehreren Rahmen zusammen gesammelt
wp.add_block(id_, ld) wp.add_block(id_, ld)
elif typ == "Kabel": elif typ == "Kabel":
@@ -362,9 +373,13 @@ def create_mappings(positions: dict) -> tuple[dict, dict]:
# Pfad zur Karte splitten. Dieser hat z.B. den Inhalt "=AH01+UH02-KF1FDI7" # Pfad zur Karte splitten. Dieser hat z.B. den Inhalt "=AH01+UH02-KF1FDI7"
matches = re.findall(r'[^\-+=]+', unterverteiler_pfad.lstrip('=')) matches = re.findall(r'[^\-+=]+', unterverteiler_pfad.lstrip('='))
if matches: if matches:
anlage = matches[0] if len(matches) == 3:
verteiler = matches[1] anlage = matches[0]
karte = matches[2] verteiler = matches[1]
karte = matches[2]
else:
warnings[sensorname] = f"Ungültiger Pfad in Kennzeichnung: {unterverteiler_pfad}: +, - oder = fehlen an entsprechender Stelle"
else: else:
warnings[sensorname] = f"Ungültiger Pfad in Kennzeichnung: {unterverteiler_pfad}" warnings[sensorname] = f"Ungültiger Pfad in Kennzeichnung: {unterverteiler_pfad}"
continue continue
@@ -414,7 +429,7 @@ def get_subdistributor_positions_iter(dxf_path, dist2sensors: dict) -> dict:
if entity_layer != layer_name: if entity_layer != layer_name:
continue continue
for distname in all_distributors: for distname in all_distributors:
if f"-{distname}" in entity_text: if f"+{distname}" in entity_text:
ret[distname] = (round(insert_point[0], 1), round(insert_point[1], 1)) ret[distname] = (round(insert_point[0], 1), round(insert_point[1], 1))
return ret return ret
@@ -651,6 +666,11 @@ def check_environment_var(env_str: str) -> Path:
print(f"Umgebungsvariable {env_str} ist nicht gesetzt oder leer.") print(f"Umgebungsvariable {env_str} ist nicht gesetzt oder leer.")
exit() exit()
def get_input_positions_combined(source: str, use_iter: bool) -> tuple[dict, dict, dict, dict]:
if use_iter:
return get_input_positions_iter(source)
else:
return get_input_positions(source)
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser(description='fetches the x/y positions from a dxf file', prog='getpositions') parser = argparse.ArgumentParser(description='fetches the x/y positions from a dxf file', prog='getpositions')
@@ -717,10 +737,7 @@ if __name__ == '__main__':
if args.sensors: if args.sensors:
# Sensoren auslesen # Sensoren auslesen
if use_iter: res_sens, res_schaltschrank_elemente, res_double, missing_attribs = get_input_positions_combined(dxf_path if use_iter else msp, use_iter)
res_sens, res_schaltschrank_elemente, res_double, missing_attribs = get_input_positions_iter(dxf_path)
else:
res_sens, res_schaltschrank_elemente, res_double, missing_attribs = get_input_positions(msp)
if args.errors and len(res_double) > 0: if args.errors and len(res_double) > 0:
print("Duplicate blocks found. Writing errors-file.") print("Duplicate blocks found. Writing errors-file.")
+31 -1
View File
@@ -14,6 +14,10 @@ import shapely
# Globale Variable, die in main aufgerufen wird und steuert ob Graphen in unittests gezeichnet werden # Globale Variable, die in main aufgerufen wird und steuert ob Graphen in unittests gezeichnet werden
draw = False draw = False
class PointSorter: class PointSorter:
''' Klasse, die Punkte sortiert.
Die Punkte werden in der Liste self.points gespeichert.
Die Punkte werden sortiert nach x- und y-Koordinate.
'''
def __init__(self): def __init__(self):
self.points = [] self.points = []
@@ -34,6 +38,9 @@ def to_json(d, pretty: bool = True) -> str:
return json.dumps(d, indent=2 if pretty else None, default=str) #ensure_ascii false für darstellung von "ue" return json.dumps(d, indent=2 if pretty else None, default=str) #ensure_ascii false für darstellung von "ue"
class NodeIDs(): class NodeIDs():
''' Klasse, die Punkte verwaltet und NodeIDs zu Punkten zuordnet.
Die NodeIDs sind ganze Zahlen, die die Position der Punkte in der Liste self.points repräsentieren.
'''
def __init__(self, points=[]): def __init__(self, points=[]):
self._counter = 0 self._counter = 0
self._cord2id = dict() self._cord2id = dict()
@@ -41,6 +48,8 @@ class NodeIDs():
self.add_points(points) self.add_points(points)
def add_point(self, point:Point): def add_point(self, point:Point):
''' Fügt den Punkt unter einer neuen NodeId hinzu.
'''
if self.point_exists(point): if self.point_exists(point):
return True return True
self._counter += 1 self._counter += 1
@@ -78,6 +87,9 @@ class NodeIDs():
return len(self._cord2id.keys()) return len(self._cord2id.keys())
def get_points(self, nids:list[int]) -> list[Point]: def get_points(self, nids:list[int]) -> list[Point]:
''' Gibt zu einer Liste von NodeIDs die zugehörigen Punkte zurück.
Die Punkte werden in der gleichen Reihenfolge wie die NodeIDs zurückgegeben.
'''
ret = list() ret = list()
for n in nids: for n in nids:
c = self.get_point(n) c = self.get_point(n)
@@ -87,6 +99,9 @@ class NodeIDs():
def show(self): def show(self):
return self._id2cord return self._id2cord
class RackIDs(): class RackIDs():
''' Klasse, die Racks verwaltet und Rack-Racks und Rack-Equipment miteinander verbindet.
Die Racks werden als Linien in den STR-Baum eingefügt.
'''
def __init__(self, tol_snap = 200.0): def __init__(self, tol_snap = 200.0):
self._point2rack = dict() self._point2rack = dict()
self._rack2begend = dict() self._rack2begend = dict()
@@ -96,12 +111,18 @@ class RackIDs():
self._rack_tree = None self._rack_tree = None
def add_rack(self, beg:Point, end:Point, name:str): def add_rack(self, beg:Point, end:Point, name:str):
''' Fügt einen Rack hinzu.
Fügt die Anfangs- und Endpunkte des Racks zu den Racks hinzu.
'''
self.add_point_to_rack(beg, name) self.add_point_to_rack(beg, name)
self.add_point_to_rack(end, name) self.add_point_to_rack(end, name)
self._rack2begend[name] = (beg, end) # Anfangs und Endpunkte zu Rack Namen merken self._rack2begend[name] = (beg, end) # Anfangs und Endpunkte zu Rack Namen merken
def add_racks(self, racks:dict): def add_racks(self, racks:dict):
''' Fügt Racks hinzu.
Fügt die Anfangs- und Endpunkte der Racks zu den Racks hinzu.
'''
for name,v in racks.items(): for name,v in racks.items():
if len(v) == 2: if len(v) == 2:
self.add_rack(v[0], v[1], name) self.add_rack(v[0], v[1], name)
@@ -151,6 +172,9 @@ class RackIDs():
return ret_sorted return ret_sorted
def _build_rack_strtree(self): def _build_rack_strtree(self):
''' Erzeugt einen STR-Baum aus den Racks.
Die Racks werden als Linien in den STR-Baum eingefügt.
'''
self._rack_lines = [] self._rack_lines = []
self._rack_map = {} self._rack_map = {}
for r_name, pts in self.get_racks_borders().items(): for r_name, pts in self.get_racks_borders().items():
@@ -160,6 +184,9 @@ class RackIDs():
self._rack_tree = STRtree(self._rack_lines) self._rack_tree = STRtree(self._rack_lines)
def join_racks_str(self): def join_racks_str(self):
''' Verbindet Racks miteinander.
Die Racks werden als Linien in den STR-Baum eingefügt.
'''
if self._rack_tree is None: if self._rack_tree is None:
self._build_rack_strtree() self._build_rack_strtree()
@@ -192,7 +219,10 @@ class RackIDs():
connrackname = f"c-{rnames[l2]}-{rnames[l1]}" connrackname = f"c-{rnames[l2]}-{rnames[l1]}"
self.add_rack(pt, snap_point, connrackname) self.add_rack(pt, snap_point, connrackname)
def rack_is_horizontal(self, name): def rack_is_horizontal(self, name):
''' Gibt True zurück, wenn der Rack horizontal ist.
False, wenn der Rack vertikal ist.
'''
[pa, pe] = self._rack2begend[name] [pa, pe] = self._rack2begend[name]
if pa.y == pe.y: if pa.y == pe.y:
return True return True
+97842
View File
File diff suppressed because it is too large Load Diff