import argparse import ezdxf import json import os.path from dataclasses import dataclass, asdict, fields from dacite import from_dict from typing import List from datetime import datetime from openpyxl import Workbook import math from collections import defaultdict import configparser @dataclass class Point: x: float y: float @dataclass class Polyline: id: str coords: List[Point] length: float def to_tuple(self): ret = list() for p in self.coords: ret.append( (p.x,p.y) ) return ret @dataclass class Polylines: kabel: List[Polyline] def add_polyline(msp, points:Polyline, dxf_attribs): pts = points.to_tuple() pline = msp.add_lwpolyline(points=pts, dxfattribs=dxf_attribs) pline.rgb = (255, 128, 0) def new_dxf(plines, out_path): """ creates a new dxf file with a polyline inside which is created by the given json file """ print("creating dxf ..") doc = ezdxf.new('R2018', setup=True) create_cables(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 to original .dxf") doc = ezdxf.readfile(originaldxf) create_cables(plines, doc) doc.saveas(out_path) print("done") def copy_layers_into_new(originaldxf, outpath): """ creates a new dxf file with a racks, sensors, subdists from original file including cable paths """ print("copying layers from original .dxf into new .dxf") quelle = ezdxf.readfile(originaldxf) ziel = ezdxf.readfile(outpath) copy_layers_into_dxf_by_filter(quelle, ziel) create_cables(plines, quelle) ziel.saveas(out_path) print("done") def create_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) dxfattribs={"layer": timestamp} for pl in plines.kabel: add_polyline(msp, pl, dxfattribs) def model_from_json(json_file): with open(json_file, encoding='utf-8') as fh: data = json.load(fh) plines = from_dict( data_class=Polylines, data=data ) return plines def export_excel(json_file, out_path): # Hier für Excel Export print("creating excel file ..") plines = model_from_json(json_file) write_excel_from_json(plines, out_path) print("done") def write_excel_from_json(plines:Polylines, outpath:str): wb = Workbook() length_summary = defaultdict(int) #Worksheet 1 - Kabellängen ws1 = wb.active ws1.title = "Kabellängen" ws1.append(["Kabel-ID", "Länge aus Json (m)", "Gerundete Länge (m)"]) for pl in plines.kabel: length = pl.length /1000 # Umrechnung von mm in m rounded_len = math.ceil(length/10)*10 length_summary[rounded_len] +=1 ws1.append([pl.id, length, int(rounded_len)]) # Worksheet 2 - Zusammengefasste Längen ws2 = wb.create_sheet("Längenübersicht") ws2.append(["Gerundete Länge (m)", "Anzahl Kabel"]) for rlength in sorted(length_summary): ws2.append([int(rlength), length_summary[rlength]]) wb.save(outpath) def check_file_in_work(work_dir, filename): print("workdir", work_dir) print("filename:", filename) fexists = True if not os.path.exists(filename): mypath = os.path.join(work_dir, filename) if not os.path.exists(mypath): fexists = False else: mypath = filename return (mypath, fexists) def copy_layers_into_dxf_by_filter(dxf_source: ezdxf.document.Drawing, dxf_target:ezdxf.document.Drawing): msp_source = dxf_source.modelspace() msp_target = dxf_target.modelspace() subdist_layers = list(config.items('GetPos-Layer_Distributors')) rack_layers = list(config.items('GetPos-Layer_Racks')) layernames = list() layernames.extend(subdist_layers) layernames.extend(rack_layers) for (layername,v) in layernames: if layername not in dxf_source.layers: continue # Falls der Layer noch nicht im Zieldokument existiert, neu anlegen if layername not in dxf_target.layers: quelle_layer = dxf_source.layers.get(layername) dxf_target.layers.add( name=layername, color=quelle_layer.color, linetype=quelle_layer.dxf.linetype, lineweight=quelle_layer.dxf.lineweight ) # Alle Entities auf diesem Layer kopieren for entity in msp_source.query(f"*[layer=='{layername}']"): msp_target.add_entity(entity.copy()) if __name__ == '__main__': parser = argparse.ArgumentParser(description='draws a dxf file with the given cable coordinates', prog='drawdxf') parser.add_argument('-f', '--filename', action='store', required=True, help='this json file contains all cables and its coordinates which should be drawn. Saved with an unique timestamp', metavar='myfile.json') parser.add_argument('-d', '--dxf', action='store', help='this dxf drawing will be copied and the new layer with the cables will be added. Original file must be added with --origin', metavar='myfile.dxf') parser.add_argument('-c', '--copy_layer', action='store', help='copy layers of racks, sensors, distributors into a new .dxf-file. File also contains cable paths. Original file must be added with --origin', metavar='original.dxf') parser.add_argument('-n', '--new', action='store', help='create a new dxf file only with cables in it. Name is basename and a timestamp') parser.add_argument('-x', '--excel', action='store', help='create a xlsx file with cables data', metavar='allCables.xls') parser.add_argument('-o', '--origin', action='store', help='name of original .dxf file used by -d and -a', metavar='original.dxf') args = parser.parse_args() config_dir = os.environ.get("PROJECT_CFG") work_dir = os.fspath(os.environ.get('PROJECT_WORK')) json_file = args.filename (json_path, jexists) = check_file_in_work(work_dir, json_file) if not jexists: print(f"file {json_file} does not exist") parser.print_help() exit() print(json_path) plines = model_from_json(json_path) config = configparser.ConfigParser(allow_no_value=True, delimiters=("=")) config.optionxform = lambda option: option # preserve case for letters config.read(os.path.join(config_dir, "allgemein.cfg")) dxf_file = args.dxf if args.dxf or args.copy_layer: if not args.origin: parser.print_help() exit() else: (origin_path, dexists) = check_file_in_work(work_dir, args.origin) if args.dxf: (dxf_path, dexists) = check_file_in_work(work_dir, dxf_file) if not dexists: print(f"file {dxf_file} does not exist") parser.print_help() exit() out_path = dxf_path res_pos = new_dxf(plines, dxf_path) if args.copy_layer: out_path = os.path.join(work_dir, args.copy_layer) res_pos = new_dxf(plines, out_path) copy_layers_into_new(origin_path, out_path) if args.new: # erzeuge dxf Datei nur mit Kabeln out_path = os.path.join(work_dir, args.new) res_pos = new_dxf(plines, out_path) if args.excel: excel_path = os.path.join(work_dir, args.excel) export_excel(json_path, excel_path)