Launch angepasst auf ASCII Fortna layout und binary Fortna Layout. Neues Icon erstellt. Kleiner Bugfix in Darw für Positionierung von UV Text. Getpositions mit Support für iterdxf (low Memory)

This commit is contained in:
2025-06-06 15:18:53 +02:00
parent 9ad0c9ca8e
commit 1285742eeb
5 changed files with 217 additions and 48 deletions
+22 -6
View File
@@ -55,6 +55,22 @@
"ST-Fortna_positions.json" "ST-Fortna_positions.json"
] ]
}, },
{
"name": "getpositions with ST-Fortna_ASCII.dxf",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"args": [
"--filename",
"ST-Fortna_ASCII.dxf",
"--sensors",
"--rack",
"--console",
"--write",
"ST-Fortna_ASCII_positions.json"
]
},
{ {
"name": "getpositions with HundM.dxf", "name": "getpositions with HundM.dxf",
"type": "debugpy", "type": "debugpy",
@@ -116,17 +132,17 @@
"easy.dxf" "easy.dxf"
] ]
}, },
{ {
"name": "draw cable dxf from easy.json to new layer", "name": "draw cables from ST-Fortna_todraw",
"type": "debugpy", "type": "debugpy",
"request": "launch", "request": "launch",
"program": "${file}", "program": "${file}",
"console": "integratedTerminal", "console": "integratedTerminal",
"args": [ "args": [
"--json", "--filename",
"easy.json", "ST-Fortna_ASCII_todraw.json",
"--dxf", "-n",
"easy.dxf" "ST-Fortna_ASCII_cables.dxf"
] ]
}, },
{ {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 MiB

After

Width:  |  Height:  |  Size: 663 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

+2 -2
View File
@@ -204,7 +204,7 @@ def draw_subdists(plines, doc):
continue continue
subdist_positions.add(pos) subdist_positions.add(pos)
subdist_name = pl.id.split('_')[0] subdist_name = pl.id.split('-')[0]
pt2 = pl.coords[1] pt2 = pl.coords[1]
dx = pt2.x - pt1.x dx = pt2.x - pt1.x
@@ -216,7 +216,7 @@ def draw_subdists(plines, doc):
if abs(dx) > abs(dy): # Horizontal if abs(dx) > abs(dy): # Horizontal
offsety = -80 # Wert -80 über Try-and-Error zur Mitte der Beschriftung angepasst offsety = -80 # Wert -80 über Try-and-Error zur Mitte der Beschriftung angepasst
if dx > 0: if dx < 0:
halign = 0 # LEFT halign = 0 # LEFT
offsetx = 50 # Wert 50 durch Try-and-Error sodass etwas Abstand zu Kabelpritsche offsetx = 50 # Wert 50 durch Try-and-Error sodass etwas Abstand zu Kabelpritsche
else: else:
+192 -39
View File
@@ -8,14 +8,17 @@ import json
import re import re
from shapely import Point from shapely import Point
from itertools import combinations from itertools import combinations
from ezdxf.addons import iterdxf
import re
import time
""" """
Dieses Programm: Dieses Programm:
- liest die dxf Datei und holt sich von den Layern der dxf Datei die Positionen - liest die dxf Datei und holt sich von den Layern der dxf Datei die Positionen
+ der Motoren, Sensoren und Aktoren + der Motoren, Sensoren und Aktoren
+ der Unterverteiler + der Unterverteiler
+ der Polylinien der Kabelpritschen + der Polylinien der Kabelpritschen
- erzeugt daraus eine .json Datei im Work Ordner - erzeugt daraus eine .json Datei im Work Ordner
""" """
@@ -49,6 +52,61 @@ def get_input_positions(msp: ezdxf.document.Drawing.modelspace):
continue # Überspringe Blöcke ohne Attribute continue # Überspringe Blöcke ohne Attribute
id = "" id = ""
ld = dict() ld = dict()
for attrib in insert.attribs:
attr_tag = attrib.dxf.tag
attr_text = 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[attr_tag] = attr_text
if attr_tag == "IO":
id = attr_text
#print(f"-- coord io {id}--: {attrib.dxf.insert}") # position des Blocks
pos = attrib.dxf.insert #Position aufzeichnen und bei Bedarf später mit REAL_POS überschreiben
ld["pos"] = (round(pos.x, 1), round(pos.y, 1))
if attr_tag == "B":
for spec in SpecialKeys:
if spec in attr_text:
id = attr_text
#print(f"-- coord {attrib.dxf.text} --: {attrib.dxf.insert}")
if attr_tag == "REALE_POSITION" and attr_text == "x":
#print(f"-- coord real --: {attrib.dxf.insert}")
pos = attrib.dxf.insert #Position Ecke unten links von "x"-Marker auslesen
# 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))
# Nur wenn eine ID vorhanden ist, und eine gültige Position existiert
if id and "pos" in ld and isinstance(ld["pos"], tuple) and len(ld["pos"]) == 2:
if id in allIds:
allIds[id] = merge_two_dicts(allIds[id], ld) #Kombiniert alle infos aus dxf und "pos"
else:
allIds[id] = ld
return allIds
def get_input_positions_iter(dxf_path) -> dict:
"""
Iterative Version für große DXF-Dateien mit iterdxf (kein Context Manager!).
"""
SpecialKeys = ["MB", "MA", "BG", "FC"]
allIds = dict()
for insert in iterdxf.modelspace(dxf_path):
if insert.dxftype() != 'INSERT':
continue
attribs = {att.dxf.tag: att.dxf.text for att in insert.attribs or []}
id = ""
ld = dict()
for attrib in insert.attribs: for attrib in insert.attribs:
attr_tag = attrib.dxf.tag attr_tag = attrib.dxf.tag
attr_text = attrib.dxf.text attr_text = attrib.dxf.text
@@ -85,6 +143,7 @@ def get_input_positions(msp: ezdxf.document.Drawing.modelspace):
allIds[id] = merge_two_dicts(allIds[id], ld) #Kombiniert alle infos aus dxf und "pos" allIds[id] = merge_two_dicts(allIds[id], ld) #Kombiniert alle infos aus dxf und "pos"
else: else:
allIds[id] = ld allIds[id] = ld
return allIds return allIds
def create_mappings(positions:dict) -> dict: def create_mappings(positions:dict) -> dict:
@@ -128,20 +187,6 @@ def create_mappings(positions:dict) -> dict:
def get_subdistributor_positions(msp, dist2sensors): def get_subdistributor_positions(msp, dist2sensors):
"""hole alle Positionen der Unterverteiler !!UV-Positionen bereits "Mitte-Mitte"!! """hole alle Positionen der Unterverteiler !!UV-Positionen bereits "Mitte-Mitte"!!
""" """
# # Über alle Texte laufen
# 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)
# # print("Breitenfaktor (width factor):", text.dxf.width)
# # print("Style (Schriftart):", text.dxf.style)
# # print("Handle:", text.dxf.handle)
# print("---")
ret = dict() ret = dict()
# Alle Texte auf Layer "xy" # Alle Texte auf Layer "xy"
all_distributors = dist2sensors.keys() all_distributors = dist2sensors.keys()
@@ -161,6 +206,28 @@ def get_subdistributor_positions(msp, dist2sensors):
# print("---") # print("---")
return ret return ret
def get_subdistributor_positions_iter(dxf_path, dist2sensors):
"""Hole alle Positionen der Unterverteiler aus MTEXT-Objekten mithilfe von iterdxf."""
ret = {}
all_distributors = dist2sensors.keys()
all_layers = config.items('GetPos-Layer_Distributors')
for entity in iterdxf.modelspace(dxf_path):
if entity.dxftype() != "MTEXT":
continue
entity_text = entity.dxf.text
entity_layer = entity.dxf.layer
insert_point = entity.dxf.insert
for (layer_name, _) in all_layers:
if entity_layer != layer_name:
continue
for distname in all_distributors:
if f"-{distname}" in entity_text:
ret[distname] = (round(insert_point[0], 1), round(insert_point[1], 1))
return ret
def get_tunnel_positions(msp): def get_tunnel_positions(msp):
"""hole alle Positionen aller Tunnel Ein und Ausgänge """hole alle Positionen aller Tunnel Ein und Ausgänge
""" """
@@ -171,11 +238,6 @@ def get_tunnel_positions(msp):
for (layer,v) in all_layers: for (layer,v) in all_layers:
selectstr = f'MTEXT[layer=="{layer}"]' selectstr = f'MTEXT[layer=="{layer}"]'
for text in msp.query(selectstr): for text in msp.query(selectstr):
#print(f"Text auf Layer 'Busverteiler-Kennzeichnung': {text.dxf.text}")
# match = re.search("Tunnel-", text.dxf.text)
# if match:
# if tunnelname in allTunnels:
# pos = (round(text.dxf.insert[0],1), round(text.dxf.insert[1],1)) #nur x und y Koordinate in Json schreiben
txt = text.dxf.text txt = text.dxf.text
pattern = r"(TUNNEL\d+)-(\d+)" pattern = r"(TUNNEL\d+)-(\d+)"
match = re.search(pattern, txt) match = re.search(pattern, txt)
@@ -192,6 +254,40 @@ def get_tunnel_positions(msp):
allTunnels['length'] = tunnel_length allTunnels['length'] = tunnel_length
return allTunnels return allTunnels
def get_tunnel_positions_iter(dxf_path):
"""Hole alle Positionen aller Tunnel Ein- und Ausgänge mithilfe von iterdxf."""
allTunnels = dict()
tunnel_length = dict()
all_layers = config.items('GetPos-Layer_Tunnel')
for entity in iterdxf.modelspace(dxf_path):
if entity.dxftype() != "MTEXT":
continue
txt = entity.dxf.text
layer = entity.dxf.layer
insert = entity.dxf.insert
for (layer_name, _) in all_layers:
if layer != layer_name:
continue
pattern = r"(TUNNEL\d+)-(\d+)"
match = re.search(pattern, txt)
if match:
tunnelname = match.group(1)
laenge = match.group(2)
pos = (round(insert[0], 1), round(insert[1], 1))
if tunnelname not in allTunnels:
allTunnels[tunnelname] = []
allTunnels[tunnelname].append(pos)
tunnel_length[tunnelname] = laenge
allTunnels['length'] = tunnel_length
return allTunnels
# helper function # helper function
def print_line(e): def print_line(e):
print("LINE on layer: %s\n" % e.dxf.layer) print("LINE on layer: %s\n" % e.dxf.layer)
@@ -222,12 +318,34 @@ def get_rack_positions(msp):
p = [round(x,1), round(y,1)] p = [round(x,1), round(y,1)]
ret[rack_key].append(p) ret[rack_key].append(p)
rack_counter +=1 rack_counter +=1
# iterate over all entities in modelspace return ret
# for e in msp:
# if e.dxftype() == "LINE": def get_rack_positions_iter(dxf_path):
# print_line(e) """Hole alle Positionen aller Kabelpritschen (Racks) mithilfe von iterdxf."""
# if e.dxftype() == "LWPOLYLINE": ret = dict()
# print_polyline(e) rack_counter = 1 # Zähler für Rack-Nummerierung
all_layers = config.items('GetPos-Layer_Racks')
for entity in iterdxf.modelspace(dxf_path):
if entity.dxftype() != "LWPOLYLINE":
continue
layer = entity.dxf.layer
if not any(layer == cfg_layer for cfg_layer, _ in all_layers):
continue
rack_key = f"Rack_{rack_counter}"
ret[rack_key] = []
# Verwende entity.vertices() statt get_points()
for point in entity.vertices(): # (x, y, start_width, end_width, bulge)
x, y, *_ = point # wir interessieren uns nur für x und y
ret[rack_key].append([round(x, 1), round(y, 1)])
rack_counter += 1
return ret return ret
def scan(dxf_source:ezdxf.document.Drawing): def scan(dxf_source:ezdxf.document.Drawing):
@@ -282,6 +400,10 @@ def check_existance(res_mappings, res_dist, res_pos):
return ret return ret
def dxf_is_binary(dxf_path):
with open(dxf_path, 'rb') as f:
header = f.read(22)
return b'AutoCAD Binary DXF' in header
if __name__ == '__main__': if __name__ == '__main__':
@@ -293,7 +415,6 @@ if __name__ == '__main__':
parser.add_argument('-c', '--console', action='store_true', help='print results to output') parser.add_argument('-c', '--console', action='store_true', help='print results to output')
parser.add_argument('-n', '--scan', action='store_true', help='print all layer of racs, distributes and equiment not empty') parser.add_argument('-n', '--scan', action='store_true', help='print all layer of racs, distributes and equiment not empty')
args = parser.parse_args() args = parser.parse_args()
out_dir = os.environ.get('PROJECT_DATA') out_dir = os.environ.get('PROJECT_DATA')
@@ -303,19 +424,22 @@ if __name__ == '__main__':
filename = args.filename filename = args.filename
(dxf_path, dexists) = check_file_in_work(work_dir, filename) (dxf_path, dexists) = check_file_in_work(work_dir, filename)
zeitanfang = time.time()
if dxf_is_binary(dxf_path): # Wenn dxf eine binary ist, dann komplett parsen und modelspace anlegen
doc = get_dxf_file(dxf_path)
msp = doc.modelspace()
use_iter = False
else:
use_iter = True
doc = get_dxf_file(dxf_path) # type: ignore
msp = doc.modelspace()
if args.scan:
res = scan(doc)
print(to_json(res))
sys.exit()
res_pos = dict() res_pos = dict()
res_dist = dict() res_dist = dict()
res_rac = dict() res_rac = dict()
res_mappings = dict() res_mappings = dict()
if args.sensors or args.dists or args.rack: if args.sensors or args.dists or args.rack:
config = configparser.ConfigParser(allow_no_value=True, delimiters=("=")) config = configparser.ConfigParser(allow_no_value=True, delimiters=("="))
@@ -324,37 +448,66 @@ if __name__ == '__main__':
config.read(os.path.join(config_dir, "allgemein.cfg")) config.read(os.path.join(config_dir, "allgemein.cfg"))
output_results = dict() output_results = dict()
if args.sensors: if args.sensors:
res_pos = get_input_positions(msp) # Sensoren auslesen
if use_iter:
res_pos = get_input_positions_iter(dxf_path)
else:
res_pos = get_input_positions(msp)
output_results['sensors'] = res_pos output_results['sensors'] = res_pos
if args.console: if args.console:
print(to_json(res_pos)) print(to_json(res_pos))
# Mapping zu Sensoren auslesen
(res_mappings, warnings) = create_mappings(res_pos) (res_mappings, warnings) = create_mappings(res_pos)
output_results['mappings'] = res_mappings output_results['mappings'] = res_mappings
if args.console: if args.console:
print(to_json(res_mappings)) print(to_json(res_mappings))
res_dist = get_subdistributor_positions(msp, res_mappings) # Distributoren auslesen
if use_iter:
res_dist = get_subdistributor_positions_iter(dxf_path, res_mappings)
else:
res_dist = get_subdistributor_positions(msp, res_mappings)
output_results['distributors'] = res_dist output_results['distributors'] = res_dist
if args.console: if args.console:
print(to_json(res_dist)) print(to_json(res_dist))
res_tunnel = get_tunnel_positions(msp) # Tunnel auslesen
if use_iter:
res_tunnel = get_tunnel_positions_iter(dxf_path)
else:
res_tunnel = get_tunnel_positions(msp)
output_results['tunnels'] = res_tunnel output_results['tunnels'] = res_tunnel
if args.console: if args.console:
print(to_json(res_tunnel)) print(to_json(res_tunnel))
if args.rack: if args.rack:
res_rac = get_rack_positions(msp) if use_iter:
res_rac = get_rack_positions_iter(dxf_path)
else:
res_rac = get_rack_positions(msp)
output_results['racks'] = res_rac output_results['racks'] = res_rac
if args.console: if args.console:
print(to_json(res_rac)) print(to_json(res_rac))
if args.write: if args.write:
basename = os.path.splitext(args.write)[0] basename = os.path.splitext(args.write)[0]
res_not_found = check_existance(res_mappings, res_dist, res_pos) res_not_found = check_existance(res_mappings, res_dist, res_pos)
output_results["not_found"] = res_not_found output_results["not_found"] = res_not_found
write_results(to_json(output_results), work_dir, f"{basename}.json") write_results(to_json(output_results), work_dir, f"{basename}.json")
zeitende = time.time()
print(zeitende-zeitanfang)
else: else:
parser.print_help() parser.print_help()