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"
]
},
{
"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",
"type": "debugpy",
@@ -116,17 +132,17 @@
"easy.dxf"
]
},
{
"name": "draw cable dxf from easy.json to new layer",
{
"name": "draw cables from ST-Fortna_todraw",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"args": [
"--json",
"easy.json",
"--dxf",
"easy.dxf"
"--filename",
"ST-Fortna_ASCII_todraw.json",
"-n",
"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
subdist_positions.add(pos)
subdist_name = pl.id.split('_')[0]
subdist_name = pl.id.split('-')[0]
pt2 = pl.coords[1]
dx = pt2.x - pt1.x
@@ -216,7 +216,7 @@ def draw_subdists(plines, doc):
if abs(dx) > abs(dy): # Horizontal
offsety = -80 # Wert -80 über Try-and-Error zur Mitte der Beschriftung angepasst
if dx > 0:
if dx < 0:
halign = 0 # LEFT
offsetx = 50 # Wert 50 durch Try-and-Error sodass etwas Abstand zu Kabelpritsche
else:
+193 -40
View File
@@ -8,14 +8,17 @@ import json
import re
from shapely import Point
from itertools import combinations
from ezdxf.addons import iterdxf
import re
import time
"""
Dieses Programm:
- liest die dxf Datei und holt sich von den Layern der dxf Datei die Positionen
+ der Motoren, Sensoren und Aktoren
+ der Unterverteiler
+ der Polylinien der Kabelpritschen
+ der Motoren, Sensoren und Aktoren
+ der Unterverteiler
+ der Polylinien der Kabelpritschen
- 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
id = ""
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:
attr_tag = attrib.dxf.tag
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"
else:
allIds[id] = ld
return allIds
def create_mappings(positions:dict) -> dict:
@@ -128,20 +187,6 @@ def create_mappings(positions:dict) -> dict:
def get_subdistributor_positions(msp, dist2sensors):
"""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()
# Alle Texte auf Layer "xy"
all_distributors = dist2sensors.keys()
@@ -161,6 +206,28 @@ def get_subdistributor_positions(msp, dist2sensors):
# print("---")
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):
"""hole alle Positionen aller Tunnel Ein und Ausgänge
"""
@@ -171,11 +238,6 @@ def get_tunnel_positions(msp):
for (layer,v) in all_layers:
selectstr = f'MTEXT[layer=="{layer}"]'
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
pattern = r"(TUNNEL\d+)-(\d+)"
match = re.search(pattern, txt)
@@ -192,6 +254,40 @@ def get_tunnel_positions(msp):
allTunnels['length'] = tunnel_length
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
def print_line(e):
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)]
ret[rack_key].append(p)
rack_counter +=1
# iterate over all entities in modelspace
# for e in msp:
# if e.dxftype() == "LINE":
# print_line(e)
# if e.dxftype() == "LWPOLYLINE":
# print_polyline(e)
return ret
def get_rack_positions_iter(dxf_path):
"""Hole alle Positionen aller Kabelpritschen (Racks) mithilfe von iterdxf."""
ret = dict()
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
def scan(dxf_source:ezdxf.document.Drawing):
@@ -282,6 +400,10 @@ def check_existance(res_mappings, res_dist, res_pos):
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__':
@@ -293,7 +415,6 @@ if __name__ == '__main__':
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')
args = parser.parse_args()
out_dir = os.environ.get('PROJECT_DATA')
@@ -303,19 +424,22 @@ if __name__ == '__main__':
filename = args.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_dist = dict()
res_rac = dict()
res_mappings = dict()
if args.sensors or args.dists or args.rack:
config = configparser.ConfigParser(allow_no_value=True, delimiters=("="))
@@ -324,37 +448,66 @@ if __name__ == '__main__':
config.read(os.path.join(config_dir, "allgemein.cfg"))
output_results = dict()
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
if args.console:
print(to_json(res_pos))
# Mapping zu Sensoren auslesen
(res_mappings, warnings) = create_mappings(res_pos)
output_results['mappings'] = res_mappings
if args.console:
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
if args.console:
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
if args.console:
print(to_json(res_tunnel))
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
if args.console:
print(to_json(res_rac))
if args.write:
basename = os.path.splitext(args.write)[0]
res_not_found = check_existance(res_mappings, res_dist, res_pos)
output_results["not_found"] = res_not_found
write_results(to_json(output_results), work_dir, f"{basename}.json")
zeitende = time.time()
print(zeitende-zeitanfang)
else:
parser.print_help()