Neue Suche nach Doppelrahmen über eigene Pufferklasse
This commit is contained in:
+197
-108
@@ -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.")
|
||||||
|
|||||||
Reference in New Issue
Block a user