From 73fa459dda27c09d5f5cb8db7607165b16fcdbee Mon Sep 17 00:00:00 2001 From: mistangl Date: Tue, 29 Apr 2025 18:34:05 +0200 Subject: [PATCH] Koordinaten der Unterverteiler, Sensoren und Kabelpritsche aus .dxf Datei festgestellt. Bisher Ausgabe nur in Konsole. --- .vscode/launch.json | 21 +++++- .vscode/settings.json | 6 ++ bin/getpositions.bat | 2 +- lib/getpositions.py | 164 +++++++++++++++++++++++++++--------------- lib/model.py | 66 +++++++++++++++++ pyrightconfig.json | 5 ++ 6 files changed, 203 insertions(+), 61 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 lib/model.py create mode 100644 pyrightconfig.json diff --git a/.vscode/launch.json b/.vscode/launch.json index 6213d67..e27b563 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -21,7 +21,22 @@ "justMyCode": true }, { - "name": "use filename and default", + "name": "use easy.dwg", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "justMyCode": true, + "args": [ + "--filename", + "easy.dxf", + "-s", + "-d", + "-r" + ], + }, + { + "name": "use Steuerungstestlayout", "type": "debugpy", "request": "launch", "program": "${file}", @@ -30,8 +45,8 @@ "args": [ "--filename", "ST_6300_Steuerungstestlayout1_neueBloecke.dxf", - "-i", - "-o", + "-s", + "-d", "-r" ], } diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..cfcf5d6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "python.languageServer": "Pylance", + "cSpell.words": [ + "ezdxf" + ] +} \ No newline at end of file diff --git a/bin/getpositions.bat b/bin/getpositions.bat index d17e900..77d27d6 100644 --- a/bin/getpositions.bat +++ b/bin/getpositions.bat @@ -1,4 +1,4 @@ @echo off CALL manage_interpreter.bat activate_interpreter -python %PROJECT_LIB%\getpositions.py +python %PROJECT_LIB%\getpositions.py %* CALL manage_interpreter.bat deactivate_interpreter diff --git a/lib/getpositions.py b/lib/getpositions.py index 1cf8ec4..840be4d 100644 --- a/lib/getpositions.py +++ b/lib/getpositions.py @@ -1,9 +1,14 @@ import argparse import configparser -import ezdxf +#import ezdxf +import ezdxf.document from ezdxf.math import Matrix44 import os import sys +import math +import json +import re + """ Dieses Programm: @@ -49,53 +54,71 @@ def get_world_position_of_attribute(insert, attrib): # Transformation aufbauen: Skalierung -> Rotation -> Translation m = ( Matrix44.scale(xscale, yscale, 1) @ - Matrix44.z_rotate(rotation_deg * 3.141592653589793 / 180) @ + Matrix44.z_rotate(rotation_deg * math.pi / 180) @ Matrix44.translate(block_pos.x, block_pos.y, block_pos.z) ) # Lokale Position transformieren return m.transform(local_pos) -def get_input_positions(doc): +def merge_two_dicts(x, y): + z = x.copy() + z.update(y) + return z + +def get_input_positions(msp: ezdxf.document.Drawing.modelspace): """hole alle Positionen der Eingänge """ - msp = doc.modelspace() + allIds = dict() + SpecialKeys = ["MB", # Separator + "MA", # Motor + "BG", # Stausensor + "FC" # Motorschutzschalter + ] + # Über alle Blockreferenzen (INSERT) im Modelspace laufen for insert in msp.query('INSERT'): if len(insert.attribs) == 0: continue # Überspringe Blöcke ohne Attribute + id = "" + ld = dict() for attrib in insert.attribs: - # Position des Blocks ist attrib.dxf.insert - print(f"Attribut Name: {attrib.dxf.tag}, Wert: {attrib.dxf.text}") + if len(insert.attribs) == 0: + continue # Überspringe Blöcke ohne Attribute + #print(f"Attribut Name: {attrib.dxf.tag}, Wert: {attrib.dxf.text}") + ld[attrib.dxf.tag] = attrib.dxf.text + if attrib.dxf.tag == "IO": + id = attrib.dxf.text + # print(f"-- coord io {id}--: {attrib.dxf.insert}") # position des Blocks + if attrib.dxf.tag == "B": + for spec in SpecialKeys: + if spec in attrib.dxf.text: + id = attrib.dxf.text + #print(f"-- coord {attrib.dxf.text} --: {attrib.dxf.insert}") if attrib.dxf.tag == "REALE_POSITION" and attrib.dxf.text == "x": - print(f"-- coord --: {attrib.dxf.insert}") - if attrib.dxf.tag == "B" and "MB" in attrib.dxf.text: - print(f"-- coord --: {attrib.dxf.insert}") - if attrib.dxf.tag == "B" and "BG" in attrib.dxf.text: - print(f"-- coord --: {attrib.dxf.insert}") - # attrib.layer + #print(f"-- coord real --: {attrib.dxf.insert}") world_pos = get_world_position_of_attribute(insert, attrib) + #print(f"-- world --: {world_pos}") + ld["block_pos"] = attrib.dxf.insert + ld["world_pos"] = world_pos - # # Attribute abrufen (als Liste von ATTRIB-Objekten) - # attribs = {attrib.dxf.tag: attrib.dxf.text for attrib in insert.attribs} - - # # Hier z.B. filtern: Blocke, die ein "NAME"-Attribut haben - # if "NAME" in attribs: - # print(f"Blockname: {insert.dxf.name}") - # print(f"NAME-Attribut: {attribs['NAME']}") - - # # Zugriff auf weitere Attribute - # # z.B. "ID", "TYP" usw. - # if "ID" in attribs: - # print(f"ID: {attribs['ID']}") + if not id == "": + if not id in allIds: + allIds[id] = ld + else: + allIds[id] = merge_two_dicts(allIds[id], ld) + return allIds - # msp = doc.modelspace() +def get_subdistributor_positions(msp): + """hole alle Positionen der Unterverteiler + """ # # Über alle Texte laufen - # for text in msp.query('TEXT'): + # for text in msp.query('MTEXT'): # print(f"Inhalt: {text.dxf.text}") # print(f"Layer: {text.dxf.layer}") # print(f"Einfügepunkt: {text.dxf.insert}") + # print(f"Breite: {text.dxf.width}") # # print("Farbe:", text.dxf.color) # Achtung: 256 = "ByLayer" # # print("Höhe (height):", text.dxf.height) # # print("Rotation (degrees):", text.dxf.rotation) @@ -104,38 +127,54 @@ def get_input_positions(doc): # # print("Handle:", text.dxf.handle) # print("---") - - -def get_output_positions(doc): - """hole alle Positionen der Ausgänge - """ - pass + ret = dict() + # Alle Texte auf Layer "xy" + for text in msp.query('TEXT[layer=="Busverteiler-Kennzeichnung"]'): + print(f"Text auf Layer 'Busverteiler-Kennzeichnung': {text.dxf.text}") + match = re.search(r"UC\w+", text.dxf.text) + if match: + nameUC = match.group(0) + ret[nameUC] = text.dxf.insert + return ret # helper function -def print_entity(e): +def print_line(e): print("LINE on layer: %s\n" % e.dxf.layer) - print("points: %s\n" % repr(e.dxf.points())) - #print("y point: %s\n" % e.dxf.y) + print("points: %s\n" % repr(e.dxf)) -def get_rack_positions(doc): +def print_polyline(e): + print("POLYLINE on layer: %s\n" % e.dxf.layer) + #print("points: %s\n" % repr(e.dxf)) + #print("y point: %s\n" % e.dxf.y) + for x, y, start_width, end_width, bulge in e.get_points(): # Gibt Tuple (x, y, start_width, end_width, bulge) + print(f" Punkt: ({x}, {y}), Startbreite: ({start_width}, Endbreite: {end_width})") + if e.is_closed: + print("Diese Polyline ist geschlossen.") + +def get_rack_positions(msp): """hole alle Positionen aller Kabelpritschen """ + + ret = list() + for e in msp.query('LWPOLYLINE[layer=="PRITSCHE_200-60"]'): + #print_polyline(e) + poly = list() + for x, y, start_width, end_width, bulge in e.get_points(): # Gibt Tuple (x, y, start_width, end_width, bulge) + point = (x,y) + poly.append(point) + ret.append(poly) # iterate over all entities in modelspace - msp = doc.modelspace() # for e in msp: + # if e.dxftype() == "LINE": + # print_line(e) # if e.dxftype() == "LWPOLYLINE": - # print_entity(e) + # print_polyline(e) + return ret - # # entity query for all LINE entities in modelspace - # for e in msp.query("LWPOLYLINE"): - # print_entity(e) - +def to_json(d, pretty: bool = True) -> str: + return json.dumps(d, indent=2 if pretty else None, default=str) - - - - def get_dxf_file(filepath): """hole das dxf file """ @@ -152,9 +191,10 @@ def get_dxf_file(filepath): if __name__ == '__main__': parser = argparse.ArgumentParser(description='fetches the x/y positions from a dxf file', prog='getpositions') parser.add_argument('-f', '--filename', action='store', required=True, default="ST_6300_Steuerungstestlayout1_neueBloecke.dwg", help='which file should be fetched', metavar='myfile.dwg') - parser.add_argument('-i', '--input', action='store_true', help='fetch all position of inputs') - parser.add_argument('-o', '--output', action='store_true', help='fetch all positions of outputs') + parser.add_argument('-s', '--sensors', action='store_true', help='fetch all position of sensors, motors, actors') + parser.add_argument('-d', '--dists', action='store_true', help='fetch all positions of all subdistributors') parser.add_argument('-r', '--rack', action='store_true', help='fetch all positions of all cable racks') + parser.add_argument('-c', '--console', action='store_true', help='print results to output') args = parser.parse_args() @@ -162,15 +202,25 @@ if __name__ == '__main__': work_dir = os.environ.get('PROJECT_WORK') filename = args.filename - doc = get_dxf_file(os.path.join(work_dir, filename)) + doc = get_dxf_file(os.path.join(work_dir, filename)) # type: ignore + msp = doc.modelspace() + + res_pos = dict() + res_dist = dict() + res_rac = dict() + if args.sensors or args.dists or args.rack: + if args.sensors: + res_pos = get_input_positions(msp) + if args.console: + print(to_json(res_pos)) + if args.dists: + res_dist = get_subdistributor_positions(msp) + if args.console: + print(to_json(res_dist)) + if args.rack: + res_rac = get_rack_positions(msp) + if args.console: + print(to_json(res_rac)) - res = dict() - if args.input: - res = get_input_positions(doc) - if args.output: - get_output_positions(doc) - if args.rack: - get_rack_positions(doc) - else: parser.print_help() diff --git a/lib/model.py b/lib/model.py new file mode 100644 index 0000000..a162b7b --- /dev/null +++ b/lib/model.py @@ -0,0 +1,66 @@ +from dataclasses import dataclass, asdict, fields +from dacite import from_dict +from typing import List +import json + + +@dataclass +class Sensor: + id: str + x: float + y: float + +@dataclass +class Aktor: + id: str + x: float + y: float + +@dataclass +class Pritsche: + id: str + x: float + y: float + +@dataclass +class Anlage: + name: str + sensoren: List[Sensor] + aktoren: List[Aktor] + kabelpritschen: List[Pritsche] + + def to_json(self, pretty: bool = True) -> str: + d = self.to_dict() + return json.dumps(d, indent=2 if pretty else None, default=str) + + def to_dict(self) -> dict: + """Konvertiert das Objekt in ein dict – datetime wird als ISO-String dargestellt.""" + result = asdict(self) + # hier um irgendwas ergänzen + # result["startdatum"] = self.startdatum.isoformat() + return result + + +if __name__ == '__main__': + json_string = '''{ + "name": "H&M", + "sensoren": [ + {"id": "AP4321", "x": 14, "y": 50}, + {"id": "AP4322", "x": 22, "y": 100} + ], + "aktoren": [ + {"id": "AP4321", "x": 14, "y": 50}, + {"id": "AP4322", "x": 22, "y": 100} + ], + "kabelpritschen": [ + {"id": "p1", "x": 1, "y": 0}, + {"id": "p2", "x": 22, "y": 10} + ] + } + ''' + data = json.loads(json_string) + anlage = from_dict( + data_class=Anlage, + data=data + ) + print(anlage) \ No newline at end of file diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 0000000..be0c9c9 --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,5 @@ +{ + "typeCheckingMode": "basic", + "reportMissingTypeStubs": true, + "useLibraryCodeForTypes": true +}