Neue Suche nach Doppelrahmen über eigene Pufferklasse

This commit is contained in:
2025-07-04 22:10:28 +02:00
parent 3aee069460
commit ea807c5365
+197 -108
View File
@@ -8,6 +8,7 @@ import re
from ezdxf.addons import iterdxf from ezdxf.addons import iterdxf
import re import re
from pathlib import Path from pathlib import Path
from shapely.geometry import Point
""" """
@@ -33,41 +34,6 @@ def merge_two_dicts(x, y):
z.update(y) z.update(y)
return z return z
def get_type_of_name(name):
SpecialKeys = ["MB", # Ventil
"MA", # Motor
"BG", # Stausensor
"BP", # Schalter Druckluft
"QM", # Ventile
"BX" # Scanner
]
KabelKey = ["WD", "WF"]
DropKeys = [
"FC", # Motorschutzschalter
"PF", # Leuchtmelder
"DI", # Feedback vom Gerät
"QA", # Hauptschütz
"SF" # Drucktaster
]
prefix = name[:2]
if prefix in SpecialKeys:
typ = "Sensor"
# Suche nach Kabel
elif prefix in KabelKey:
typ = "Kabel"
# suche nach Items die wir nicht weiter verfolgen
elif prefix in DropKeys:
typ = "Schaltschrankelement"
else:
typ = "unknown"
return typ
def get_type_of_name_cfg(name): def get_type_of_name_cfg(name):
prefix = name[:2] prefix = name[:2]
@@ -79,7 +45,7 @@ def get_type_of_name_cfg(name):
else: else:
return "unknown" return "unknown"
def get_attributes_of_insert(insert): def get_attributes_of_insert(dinsert, dpos):
""" """
Diese Funktion schaut nach den aktuell definierten Attributen in den Blöcken Diese Funktion schaut nach den aktuell definierten Attributen in den Blöcken
@@ -95,39 +61,22 @@ def get_attributes_of_insert(insert):
""" """
id = "" id = ""
ld = dict() ld = dinsert
typ = 'unknown' typ = 'unknown'
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 == "NAME": # die neueren Bläcke heissen nicht IO, sondern haben einen Namen # die neueren Bläcke heissen nicht IO, sondern haben einen Namen
typ = get_type_of_name_cfg(attr_text) if "NAME" in dinsert:
id = attr_text typ = get_type_of_name_cfg(dinsert["NAME"])
# pos = attrib.dxf.insert #Position aufzeichnen und bei Bedarf später mit REAL_POS überschreiben id = dinsert["NAME"]
# ld["pos"] = (round(pos.x, 1), round(pos.y, 1))
if attr_tag == "IO":
typ = get_type_of_name_cfg(attr_text)
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": if "IO" in dinsert:
# Suche nach Sensoren attr_text = dinsert["IO"]
typ = get_type_of_name_cfg(attr_text) typ = get_type_of_name_cfg(attr_text)
id = attr_text id = dinsert["IO"]
pos = attrib.dxf.insert #Position aufzeichnen und bei Bedarf später mit REAL_POS überschreiben pos = dpos["IO"]
ld["pos"] = (round(pos.x, 1), round(pos.y, 1))
if attr_tag == "REALE_POSITION" and attr_text == "x": if "REALE_POSITION" in dinsert and dinsert["REALE_POSITION"] == 'x':
#typ = get_type_of_name(attr_text) pos = dpos["REALE_POSITION"]
#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 # Hoehe und Breite von "x" addieren, um Mittelpunkt zu finden
breite_marker = config.getfloat("GetPos-Geom-Sensor", "Breite") breite_marker = config.getfloat("GetPos-Geom-Sensor", "Breite")
@@ -135,54 +84,200 @@ def get_attributes_of_insert(insert):
midx = pos[0] + breite_marker * 0.5 midx = pos[0] + breite_marker * 0.5
midy = pos[1] + hoehe_marker * 0.5 midy = pos[1] + hoehe_marker * 0.5
ld["pos"] = (round(midx, 1), round(midy, 1)) ld["pos"] = (round(midx, 1), round(midy, 1))
else:
ld["pos"] = (pos[0], pos[1])
if "B" in dinsert:
attr_text = dinsert["B"]
typ = get_type_of_name_cfg(attr_text)
id = attr_text
#Position aufzeichnen und bei Bedarf später mit REAL_POS überschreiben
pos = dpos["B"]
ld["pos"] = (pos[0], pos[1])
return (ld, id, typ) return (ld, id, typ)
def check_double_ids(ld, id, allSensors):
if id in allSensors and "NAME" in ld: class CompareBuffer:
return True """ein Puffer um alle als Doppelt zugewiesenen blöcke zwischenzulagern.
if id in allSensors and "B" in ld and "B" in allSensors[id]: Anhand von ähnlicher Position und gleicher ID wird dann entschieden,
return True ob die Daten beider dicts zusammen gehören"""
if id in allSensors and "IO" in ld and "IO" in allSensors[id]: def __init__(self):
return True self._wartepuffer = dict()
return False
def add_block(self, id, buffer):
"""adds one block under the buffer list under this id"""
if id not in self._wartepuffer:
self._wartepuffer[id] = list()
self._wartepuffer[id].append(buffer)
def get_blocks(self, id):
if id in self._wartepuffer:
return self._wartepuffer[id]
else:
return []
def get_block_ids(self)->list:
return list(self._wartepuffer.keys())
def set_block(self, id, buffer):
if id in self._wartepuffer:
del self._wartepuffer[id]
self._wartepuffer[id] = buffer
def dict_compare(self, d1, d2):
str1 = json.dumps(d1)
str2 = json.dumps(d2)
return str1 == str2
def remove_block(self, id, toRemove):
"""removes this block from the list under the given id"""
buffers = self.get_blocks(id)
l = list()
for b in buffers:
if self.dict_compare(b, toRemove):
pass
else:
l.append(b)
self.set_block(id, l)
def positions_are_close(self, dict1:dict, dict2:dict, border:float):
pos1 = None
pos2 = None
if "pos" in dict1:
pos1 = dict1["pos"]
if "pos" in dict2:
pos2 = dict2["pos"]
if not (pos1 and pos2):
return False
p1 = Point(pos1[0], pos1[1])
p2 = Point(pos2[0], pos2[1] )
dist = p1.distance(p2)
if dist < border:
return True
return False
def get_all_sps_blocks(self, id):
"""gives back all blocks with SPS inside to the given id"""
buffers = self.get_blocks(id)
l = list()
for b in buffers:
if "SPS" in b:
l.append(b)
return l
def get_non_sps_blocks(self, id):
"""gives back all blocks without SPS inside to the given id"""
buffers = self.get_blocks(id)
l = list()
for b in buffers:
if "SPS" not in b:
l.append(b)
return l
def extract_input_positions(insert_iterable) -> tuple: def extract_input_positions(insert_iterable) -> tuple:
allSensors = dict() allSensors = dict()
allCables = dict() allCables = dict()
allunknowns = dict()
doubleIds = dict()
allSchaltschrank = dict() allSchaltschrank = dict()
allunknowns = list()
for insert in insert_iterable: WP = CompareBuffer()
if insert.dxftype() != 'INSERT': all_inserts, all_positions = attribs_to_dicts(insert_iterable)
continue
ld, id, typ = get_attributes_of_insert(insert) for insert, pos in zip(all_inserts, all_positions):
ld, id, typ = get_attributes_of_insert(insert, pos)
if typ == "Sensor": if typ == "Sensor":
if id and "pos" in ld and isinstance(ld["pos"], tuple) and len(ld["pos"]) == 2: # wenn NAME enthalten ist, dann ist das ein eindeutiger Bezeichner
if id in allSensors: if "NAME" in ld:
is_double = check_double_ids(ld, id, allSensors) allSensors[id] = ld
if not is_double: else:
allSensors[id] = merge_two_dicts(allSensors[id], ld) #Kombiniert alle infos aus dxf und "pos" # alle anderen Sachen werden dann aus mehreren Rahmen zusammen gesammelt
else: WP.add_block(id, ld)
# gib aus wo sich beide gleichlautenden Elemente auf der Zeichnung befinden
if id not in doubleIds:
doubleIds[id] = []
doubleIds[id].append(allSensors[id]["pos"]) # erste Position
doubleIds[id].append(ld["pos"]) # zweite Position
else:
allSensors[id] = ld
elif typ == "Kabel": elif typ == "Kabel":
allCables[id] = ld allCables[id] = ld
elif typ == "Schaltschrankelement": elif typ == "Schaltschrankelement":
allSchaltschrank[id] = ld allSchaltschrank[id] = ld
else: else:
allunknowns[id] = ld allunknowns.append(ld)
return allSensors, doubleIds # spezialbehandlung der Sensoren, da diese in IO und A,B,C Blöcke geteilt sind
# Die Funktion sucht die übereinanderliegenen Elemente und baut ein Dict daraus
allocate_blocks_together(allSensors, WP)
# die noch übrigen Blöcke melden
missing_attribs, doubleIds = get_errors_double_and_attributes(WP)
return allSensors, doubleIds, missing_attribs
def get_errors_double_and_attributes(WP):
missing_attribs = dict()
doubleIds = dict()
for id in WP.get_block_ids():
blocks = WP.get_blocks(id)
# einzelne Blöcke deren Zuordnung nicht gelungen ist, fehlen Angaben in den Attributen, z.B. SPS, B, u.ä.
if len(blocks) == 1:
given_keys = str(blocks[0].keys())
missing_attribs[id] = f"Nur ein Block und/oder fehlende Attribute: {given_keys}"
WP.remove_block(id, blocks[0])
# alle anderen Angaben sind mindestens zwei oder mehr Blöcke mit derselben Id
else:
for block in blocks:
if id not in doubleIds:
doubleIds[id] = []
doubleIds[id].append(block['pos'])
return missing_attribs, doubleIds
def allocate_blocks_together(allSensors:dict, WP:CompareBuffer):
"""geht alle gemerkten Sensoren durch die gleich heissen.
Falls ein SPS Präfix angegeben wird, wird es zur Id hinzugefügt und als neuer Name gemerkt """
all_sensors_ids = WP.get_block_ids()
for id in all_sensors_ids:
blks_sps = WP.get_all_sps_blocks(id)
blks_other = WP.get_non_sps_blocks(id)
for block_with_sps in blks_sps:
for block_without_sps in blks_other:
# Vergleiche alle Blöcke mit SPS und denen ohne auf die gleiche Position
if WP.positions_are_close(block_with_sps, block_without_sps, 1000):
newId = create_new_id(id, block_with_sps, block_without_sps) # hier das Präfix davor
allSensors[newId] = merge_two_dicts(block_without_sps, block_with_sps) #Kombiniert alle infos aus dxf und "pos"
WP.remove_block(id, block_with_sps)
WP.remove_block(id, block_without_sps)
def create_new_id(id, dict1, dict2):
sps_praefix = None
if "SPS" in dict1:
sps_praefix = dict1["SPS"]
if "SPS" in dict2:
sps_praefix = dict2["SPS"]
if not sps_praefix:
raise Exception
return f"{sps_praefix}:{id}"
def attribs_to_dicts(insert_iterable):
all_inserts = list()
all_positions = list()
for insert in insert_iterable:
if insert.dxftype() != 'INSERT':
continue
itemdata = dict()
positions = dict()
typ = 'unknown'
for attrib in insert.attribs:
if len(insert.attribs) == 0:
continue # Überspringe Blöcke ohne Attribute
attr_tag = attrib.dxf.tag
attr_text = attrib.dxf.text
pos = attrib.dxf.insert
itemdata[attr_tag] = attr_text
positions[attr_tag] = (round(pos.x, 1), round(pos.y, 1), round(pos.z, 1))
if len(itemdata) > 0:
all_inserts.append(itemdata)
all_positions.append(positions)
return all_inserts, all_positions
def get_input_positions(msp) -> tuple: def get_input_positions(msp) -> tuple:
return extract_input_positions(msp.query('INSERT')) return extract_input_positions(msp.query('INSERT'))
@@ -193,7 +288,7 @@ def get_input_positions_iter(dxf_path) -> tuple:
def create_mappings(positions:dict) -> tuple: def create_mappings(positions:dict) -> tuple:
unterverteiler_pfad = "" unterverteiler_pfad = ""
dnamen = dict() dnamen = dict()
# sammle die Sensoren mit ihren zugehörigen Unterverteilern # s
sensor2unterverteiler = dict() sensor2unterverteiler = dict()
warnings = dict() warnings = dict()
for sensorname,v in positions.items(): for sensorname,v in positions.items():
@@ -205,17 +300,11 @@ def create_mappings(positions:dict) -> tuple:
#print(unterverteiler_pfad) #print(unterverteiler_pfad)
# 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"
pattern = r"^=([A-Z]+\d+)([+\-])([A-Z]+\d+)([+\-])([A-Z]+\d+.)$" matches = re.findall(r'[^\-+=]+', unterverteiler_pfad.lstrip('='))
match = re.match(pattern, unterverteiler_pfad) if matches:
if match: anlage = matches[0]
anlage = match.group(1) verteiler = matches[1]
verteiler = match.group(3) karte = matches[2]
karte = match.group(5)
# match.group(1) # AH01
# match.group(2) # +
# match.group(3) # UH02
# match.group(4) # -
# match.group(5) # KF1FDI7
else: else:
warnings[sensorname] = f"Ungültiger Pfad in Kennzeichnung: {unterverteiler_pfad}" warnings[sensorname] = f"Ungültiger Pfad in Kennzeichnung: {unterverteiler_pfad}"
continue continue
@@ -586,9 +675,9 @@ if __name__ == '__main__':
if args.sensors: if args.sensors:
# Sensoren auslesen # Sensoren auslesen
if use_iter: if use_iter:
res_sens, res_double = get_input_positions_iter(dxf_path) res_sens, res_double, missing_attribs = get_input_positions_iter(dxf_path)
else: else:
res_sens, res_double = get_input_positions(msp) res_sens, 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.")