From b72c01768e629dc6af6f40f95d97e6c657ca6f6d Mon Sep 17 00:00:00 2001 From: lertlmaier Date: Thu, 5 Jun 2025 16:14:27 +0200 Subject: [PATCH] =?UTF-8?q?Zeichnen=20von=20Sensoren=20und=20Subdists=20al?= =?UTF-8?q?s=20Textbl=C3=B6cke=20hinzugef=C3=BCgt.=20=C3=9Cbereinanderstaf?= =?UTF-8?q?felung=20von=20mehreren=20Sensoren.=20Nur=20einmaliges=20Zeichn?= =?UTF-8?q?en=20von=20UVs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/drawdxf.py | 285 ++++++++++++++++++++++++++----------------------- 1 file changed, 149 insertions(+), 136 deletions(-) diff --git a/lib/drawdxf.py b/lib/drawdxf.py index 08931a4..ce24152 100644 --- a/lib/drawdxf.py +++ b/lib/drawdxf.py @@ -60,24 +60,24 @@ def new_dxf(plines, out_path): print("creating new .dxf ..") doc = ezdxf.new('R2018', setup=True) - create_cables(plines, doc) + draw_cables(plines, doc) + draw_sensors(plines, doc) + draw_subdists(plines, doc) doc.saveas(out_path) print("done") - def modify_original_dxf(plines, originaldxf): """ adds new layer to original .dxf-file that contains cables """ print("adding cables into original .dxf ..") doc = ezdxf.readfile(originaldxf) - create_cables(plines, doc) - + draw_cables(plines, doc) + doc.saveas(out_path) print("done") - def copy_layers_into_new(originaldxf, outpath, plines): """ creates a new dxf file with a racks, sensors, subdists from original file including cable paths """ @@ -86,28 +86,159 @@ def copy_layers_into_new(originaldxf, outpath, plines): quelle = ezdxf.readfile(originaldxf) ziel = ezdxf.new('R2018', setup=True) - create_cables(plines, ziel) + draw_cables(plines, ziel) + draw_sensors(plines, ziel) copy_layers_into_dxf_by_filter(quelle, ziel) ziel.saveas(out_path) print("done") - - -def create_cables(plines, doc): + +def draw_cables(plines, doc): msp = doc.modelspace() - # # Beispiel für Text und Polyline - # add_text(msp) - # Layer mit Zeitstempel erstellen (z. B. "Layer_2025-05-07_14-30") - timestamp = datetime.now().strftime("Kabel_%Y-%m-%d_%H-%M") - if timestamp not in doc.layers: - doc.layers.add(name=timestamp, color=7) + timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M") + cable_layer = f"cables_{timestamp}" - dxfattribs={"layer": timestamp} + # Kabel-Layer anlegen + if cable_layer not in doc.layers: + doc.layers.add(name=cable_layer, color=7) + + dxfattribs_cable={"layer": cable_layer} + + # Kabel zeichnen for pl in plines.kabel: - add_polyline(msp, pl, dxfattribs) + # Polyline für Kabel zeichnen + add_polyline(msp, pl, dxfattribs_cable) + +def find_close_key(pos2sensors, x, y, tolerance=10): # !!! Toleranz in Config !!! + ''' Funktion überprüft ob Sensoren nahezu identisch an der gleichen Stelle liegen und legt sie in diesem fall aufeinander + Wird benötigt, um zusammengehörige Sensoren gestaffelt auf dxf zu zeichen + ''' + for (px, py) in pos2sensors: + if abs(px - x) <= tolerance and abs(py - y) <= tolerance: + return (px, py) + return None +def draw_sensors(plines, doc): + msp = doc.modelspace() + timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M") + sensor_layer = f"sensors_{timestamp}" + # Sensor-Layer erzeugen + if sensor_layer not in doc.layers: + doc.layers.add(name=sensor_layer, color=5) + + dxfattribs_sensors={"layer": sensor_layer, "height": 100} + + # Sensoren nach Endpunkten gruppieren -> mehrfacheinträge gestaffelt zeichnen + pos2sensors = defaultdict(list) + for pl in plines.kabel: + pt2 = pl.coords[-1] #Endpunkt des Kabels = Sensor Position + pos_key = find_close_key(pos2sensors, pt2.x, pt2.y) + if pos_key: + pos2sensors[pos_key].append(pl) + else: + pos2sensors[(pt2.x, pt2.y)].append(pl) + + # Sensor Blöcke zeichnen + for (x,y), pls in pos2sensors.items(): + for i, pl in enumerate(pls): + sensor_name = pl.id.split('-')[-1] + pt1 = pl.coords[-2] + pt2 = pl.coords[-1] + + dx = pt2.x - pt1.x + dy = pt2.y - pt1.y + text = msp.add_text(sensor_name, dxfattribs=dxfattribs_sensors) + + # Offsets für Beschriftungen + offsetx = 0 + offsety = 0 + + # Kabel Horizontal + if abs(dx) > abs(dy): + n = len(pls) + center_offset = i - (n - 1) / 2 # Falls mehrere Sensoren -> Verschiebung so, dass Kabel immer in Mitte ankommt + offsety = -80 + center_offset * 110 # Wert -80 über Try-and-Error zur Mitte der Beschriftung angepasst + if dx > 0: + halign = 0 # LEFT + offsetx = 50 # Wert 50 durch Try-and-Error sodass etwas Abstand zu Kabelpritsche + else: + halign = 2 # RIGHT + offsetx = -50 # Wert 50 durch Try-and-Error sodass etwas Abstand zu Kabelpritsche + valign = 1 # BOTTOM + + # Kabel vertikal + else: + if dy > 0: + valign = 0 # BASELINE + offsety = 50 + i * 110 # nach oben anfügen + else: + valign = 3 # TOP + offsety = -50 - i * 110 # nach unten anfügen + halign = 1 # CENTER + + # Alignments setzen + text.dxf.halign = halign + text.dxf.valign = valign + text.set_placement((pt2.x + offsetx, pt2.y + offsety)) + +def draw_subdists(plines, doc): + msp = doc.modelspace() + timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M") + subdist_layer = f"subdists_{timestamp}" + + # Sensor-Layer erzeugen + if subdist_layer not in doc.layers: + doc.layers.add(name=subdist_layer, color=3) + + dxfattribs_subdists={"layer": subdist_layer, "height": 100} + + subdist_positions = set() + + for pl in plines.kabel: + pt1 = pl.coords[0] # Startposition = UV-Position + pos = (pt1.x, pt1.y) + + if pos in subdist_positions: + continue + subdist_positions.add(pos) + + subdist_name = pl.id.split('_')[0] + + pt2 = pl.coords[1] + dx = pt2.x - pt1.x + dy = pt2.y - pt1.y + + # Offsets für Beschriftungen + offsetx = 0 + offsety = 0 + + if abs(dx) > abs(dy): # Horizontal + offsety = -80 # Wert -80 über Try-and-Error zur Mitte der Beschriftung angepasst + if dx > 0: + halign = 0 # LEFT + offsetx = 50 # Wert 50 durch Try-and-Error sodass etwas Abstand zu Kabelpritsche + else: + halign = 2 # RIGHT + offsetx = -50 # Wert 50 durch Try-and-Error sodass etwas Abstand zu Kabelpritsche + valign = 1 # BOTTOM + else: # Vertikal + if dy < 0: + valign = 0 # BASELINE + offsety = 50 + else: + valign = 3 # TOP + offsety = -50 + halign = 1 # CENTER + + # Text platzieren + text = msp.add_text(subdist_name, dxfattribs=dxfattribs_subdists) + text.dxf.halign = halign + text.dxf.valign = valign + text.set_placement((pt1.x + offsetx, pt1.y + offsety)) + + def model_from_json(json_file): with open(json_file, encoding='utf-8') as fh: data = json.load(fh) @@ -125,7 +256,6 @@ def export_excel(json_file, out_path): write_excel_from_json(plines, out_path) print("done") - def write_excel_from_json(plines:Polylines, outpath:str): wb = Workbook() length_summary = defaultdict(int) @@ -191,7 +321,6 @@ def write_excel_from_json(plines:Polylines, outpath:str): wb.save(outpath) - def check_file_in_work(work_dir, filename): fexists = True if not os.path.exists(filename): @@ -202,94 +331,6 @@ def check_file_in_work(work_dir, filename): mypath = filename return (mypath, fexists) -##### - -def collect_text_entities(msp): - texts = [] - for entity in msp.query("TEXT MTEXT"): - try: - text = entity.dxf.text - pos = tuple(round(c, 4) for c in entity.dxf.insert) - layer = entity.dxf.layer - texts.append((text, pos, layer)) - except Exception as e: - print(f"Fehler beim Lesen eines Textobjekts: {e}") - return texts - -def compare_texts(source_texts, target_texts): - missing_in_target = [t for t in source_texts if t not in target_texts] - extra_in_target = [t for t in target_texts if t not in source_texts] - - if not missing_in_target and not extra_in_target: - print("✅ Alle TEXT/MTEXT-Objekte wurden korrekt kopiert.") - else: - if missing_in_target: - print("\n❌ Fehlende Texte im Ziel:") - for text in missing_in_target: - print(f" - {text}") - if extra_in_target: - print("\n⚠️ Zusätzliche Texte im Ziel:") - for text in extra_in_target: - print(f" - {text}") - -def collect_block_references(doc): - msp = doc.modelspace() - blocks = [] - for insert in msp.query("INSERT"): - try: - name = insert.dxf.name - pos = tuple(round(c, 4) for c in insert.dxf.insert) - layer = insert.dxf.layer - blocks.append((name, pos, layer)) - except Exception as e: - print(f"Fehler beim Lesen eines INSERT-Objekts: {e}") - return blocks - -def serialize_entity(e): - """Wandelt eine Entity in ein vergleichbares Tupel um""" - dxfattribs = {} - for key in e.dxf.__dir__(): - try: - value = getattr(e.dxf, key) - if callable(value) or key.startswith("_"): - continue - if isinstance(value, (list, tuple)): - value = tuple(round(v, 4) for v in value) - elif isinstance(value, float): - value = round(value, 4) - dxfattribs[key] = value - except Exception: - continue - return (e.dxftype(), dxfattribs) - -def compare_blocks(source_doc, target_doc, used_block_names): - for name in used_block_names: - if name not in source_doc.blocks or name not in target_doc.blocks: - print(f"❌ Block '{name}' fehlt in einer der Dateien.") - continue - - source_block = source_doc.blocks.get(name) - target_block = target_doc.blocks.get(name) - - source_entities = [serialize_entity(e) for e in source_block] - target_entities = [serialize_entity(e) for e in target_block] - - missing_in_target = [e for e in source_entities if e not in target_entities] - extra_in_target = [e for e in target_entities if e not in source_entities] - - if not missing_in_target and not extra_in_target: - print(f"✅ Block '{name}' ist identisch.") - else: - print(f"\n🔍 Block '{name}' hat Unterschiede:") - if missing_in_target: - print(f" ❌ Fehlt im Ziel:") - for e in missing_in_target: - print(f" - {e}") - if extra_in_target: - print(f" ⚠️ Zusätzlich im Ziel:") - for e in extra_in_target: - print(f" - {e}") - def copy_layers_into_dxf_by_filter(dxf_source: ezdxf.document.Drawing, dxf_target:ezdxf.document.Drawing): msp_source = dxf_source.modelspace() @@ -326,26 +367,6 @@ def copy_layers_into_dxf_by_filter(dxf_source: ezdxf.document.Drawing, dxf_targe if style.dxf.name not in dxf_target.styles: dxf_target.styles.new(name=style.dxf.name) - ''' - # 2. alle Blockdefinitionen kopieren die benötigt werden - used_blocks = {ins.dxf.name for ins in msp_source.query("INSERT")} - for name in used_blocks: - # skip vorhandene - if name in dxf_target.blocks: - continue - src_block = dxf_source.blocks.get(name) - # neues BLOCK-Objekt anlegen mit Base-Point - new_block = dxf_target.blocks.new(name=name, base_point=src_block.base_point) - # **ALLE** Entities im Block kopieren - for ent in src_block: - new_block.add_entity(ent.copy()) - - - # 3. Jetzt die INSERTs in den Modelspace kopieren - for ins in msp_source.query("INSERT"): - new_ins = ins.copy() - msp_target.add_entity(new_ins) - ''' # 4. Filter-Layernamen bestimmen @@ -365,15 +386,7 @@ def copy_layers_into_dxf_by_filter(dxf_source: ezdxf.document.Drawing, dxf_targe # Alle Entities auf diesem Layer kopieren entities = msp_source.query(f"*[layer=='{layername}']") for entity in entities: - msp_target.add_entity(entity.copy()) - - # texts_source = collect_text_entities(msp_source) - # texts_target = collect_text_entities(msp_target) - - # compare_texts(texts_source, texts_target) - - # used_block_names = set(insert.dxf.name for insert in dxf_source.modelspace().query("INSERT")) - # compare_blocks(dxf_source, dxf_target, used_block_names) + msp_target.add_entity(entity.copy()) if __name__ == '__main__':