diff --git a/.vscode/launch.json b/.vscode/launch.json index 5f7abe4..dfda9f3 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -123,35 +123,35 @@ ] }, { - "name": "getpositions with ST500592_Omniflo.dxf", + "name": "getpositions with PR500592_10_5.13_POT_20251216.dxf", "type": "debugpy", "request": "launch", "program": "${file}", "console": "integratedTerminal", "args": [ "--filename", - "ST500592_Omniflo.dxf", + "PR500592_10_5.13_POT_20251216.dxf", "--sensors", "--rack", "--console", "--write", - "ST500592_Omniflo_positions.json" + "PR500592_10_5.13_POT_20251216_positions.json" ] }, { - "name": "getpositions with ST500592_10_5-11_ILS.dxf", + "name": "getpositions with POT_ST1_ST6_12.dxf", "type": "debugpy", "request": "launch", "program": "${file}", "console": "integratedTerminal", "args": [ "--filename", - "ST500592_10_5-11_ILS.dxf", + "POT_ST1_ST6_12.dxf", "--sensors", "--rack", "--console", "--write", - "ST500592_10_5-11_ILS_positions.json" + "POT_ST1_ST6_12_positions.json" ] }, { @@ -289,6 +289,21 @@ "ST-Fortna_ASCII_cables.dxf" ] }, + { + "name": "draw cables for test.json", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "args": [ + "--filename", + "test_todraw.json", + "-n", + "test_cables.dxf", + "-x", + "test_cables.xlsx" + ] + }, { "name": "routing for easy_positions.json", "type": "debugpy", @@ -316,6 +331,19 @@ "-g" ] }, + { + "name": "routing for test_positionsdraw.json", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "args": [ + "--filename", + "test_positionsdraw.json", + "-w", + "test_draw.json", + ] + }, { "name": "run_tests.py with easy_3tunnels.dxf", "type": "debugpy", diff --git a/bin/enumerate.bat b/bin/enumerate.bat index b697fa2..b496cfe 100644 --- a/bin/enumerate.bat +++ b/bin/enumerate.bat @@ -12,12 +12,12 @@ REM echo Dateiname ohne Erweiterung: %FILENAME% REM echo Erweiterung: %EXT% REM echo Verzeichnis: %DIR% REM -REM Namen der Zwischenergebnis Dateien -set JSON_POS=%FILENAME%_positionsconv.json -set JSON_TODRAW=%FILENAME%_todraw.json -REM Namen der Ergebnisdateien -set ERROR_DOUBLE=%FILENAME%_errors.json -set RESULT_TIA=%FILENAME%-*_TIA.xlsx +REM Namen der auf dem RENAMER Layer gefundenen Symbole, die überschrieben werden sollen +set JSON_SYMBOLS=%FILENAME%_symbols.json + +REM Fehlerdatei +set ERROR_FILE=%FILENAME%_errors.json + if exist "%~dp0_setenv.bat" ( echo Lade lokale Umgebungseinstellungen aus _setenv.bat... @@ -27,31 +27,17 @@ REM Zielverzeichnis set TARGET_DIR=%PROJECT_IO_RESULTS%\%FILENAME% mkdir "%TARGET_DIR%" REM lösche alte Fehlermeldungen -del "%PROJECT_WORK%\%ERROR_DOUBLE%" +del "%PROJECT_WORK%\%ERROR_FILE%" -echo. -echo === Fetching Positions === -call getpositions.bat --filename %1 -s -r -w %JSON_POS% -e %ERROR_DOUBLE% -if exist "%PROJECT_WORK%\%ERROR_DOUBLE%" ( - @echo == failed: errors, e.g. duplicate IDs in given layout - pause - move %PROJECT_WORK%\%ERROR_DOUBLE% %TARGET_DIR% - move %PROJECT_WORK%\%JSON_TODRAW% %TARGET_DIR% - - goto :eof -) -if not exist "%PROJECT_WORK%\%JSON_POS%" ( - @echo == failed: getpositions - pause - goto :eof -) -echo === Creating Excel Files for TIA, WSCAD, .. === -call portalexport.bat --filename %JSON_POS% --outname %FILENAME% -if not exist "%PROJECT_WORK%\%RESULT_TIA%" ( - @echo == failed: creating .xlsx files +echo === Creating enriched dxf file === +call create_numbers.bat --filename %1 --errorfile %PROJECT_WORK%\%ERROR_FILE% --write %PROJECT_WORK%\%JSON_SYMBOLS% +if exist "%PROJECT_WORK%\%ERROR_FILE%" ( + @echo -failed- errors found during processing! pause + move %PROJECT_WORK%\%ERROR_FILE% %TARGET_DIR% + move %PROJECT_WORK%\%JSON_SYMBOLS% %TARGET_DIR% goto :eof ) echo move %PROJECT_WORK%\%FILENAME%-* %TARGET_DIR% diff --git a/lib/create_numbers.py b/lib/create_numbers.py index 2091d6c..384ecf0 100644 --- a/lib/create_numbers.py +++ b/lib/create_numbers.py @@ -8,10 +8,10 @@ from pathlib import Path import ezdxf from ezdxf.addons import iterdxf -from shapely.geometry import Point from ezdxf.lldxf.const import DXFStructureError from error_collector import ErrorCollector, write_json_file +from utils import check_environment_var, check_file_in_work, dxf_is_binary, get_dxf_file """ @@ -56,56 +56,413 @@ Alle Sensoren, Motoren, Aktoren etc. innerhalb der zugehörigen Polylinie nehmen """ -def check_file_in_work(work_dir: Path, filename: Path) -> tuple[Path, bool]: - fexists = True - if not filename.exists(): - mypath = work_dir.joinpath(filename) - ex = mypath.exists() - if not mypath.exists(): - fexists = False - else: - mypath = filename - return mypath, fexists +def read_config_layers(config_path: Path) -> list: + """ + Liest die enumerate.cfg und gibt die Layer zurück, auf denen nach Renamer-Blöcken gesucht werden soll. + Die Konfiguration hat eine einfache Liste unter [Layers]: + [Layers] + RENAMER + OTHER_LAYER + """ + layers = [] -def check_environment_var(env_str: str) -> Path: - out_path = os.environ.get(env_str) - if out_path: - return Path(out_path) + with open(config_path, 'r', encoding='utf-8') as f: + in_layers_section = False + for line in f: + line = line.strip() + + # Überspringe leere Zeilen und Kommentare + if not line or line.startswith('#') or line.startswith(';'): + continue + + # Prüfe ob wir in der [Layers] Sektion sind + if line.startswith('['): + in_layers_section = line.lower() == '[layers]' + continue + + # Wenn in [Layers], füge Layer hinzu + if in_layers_section: + layers.append(line) + + return layers + + +def extract_block_attributes(insert) -> dict: + """ + Extrahiert alle Attribute aus einem INSERT-Block. + """ + attributes = {} + if insert.dxftype() != 'INSERT': + return attributes + + if insert.has_attrib: + for attrib in insert.attribs: + tag = attrib.dxf.tag + value = attrib.dxf.text + attributes[tag] = value + + return attributes + + +def get_boundary_geometry(doc, insert): + """ + Sucht im Block nach einem Rechteck oder einer geschlossenen Polylinie. + Gibt die Eckpunkte zurück. + """ + block_layout = doc.blocks.get(insert.dxf.name) + + # Suche nach LWPOLYLINE, POLYLINE oder geschlossenen Linien + for entity in block_layout: + if entity.dxftype() == 'LWPOLYLINE': + # Get points and transform them + points = list(entity.get_points()) + + # Prüfe ob geschlossen (entweder Flag gesetzt oder erster == letzter Punkt) + is_closed = entity.closed or (entity.dxf.flags & 1) + if not is_closed and len(points) > 1: + # Prüfe ob erster und letzter Punkt gleich sind + first = points[0][:2] + last = points[-1][:2] + if abs(first[0] - last[0]) < 0.001 and abs(first[1] - last[1]) < 0.001: + is_closed = True + + if is_closed: + # Transform points relative to insert position + insert_point = insert.dxf.insert + transformed_points = [] + for p in points: + # Einfache Translation (ohne Rotation/Skalierung für ersten Ansatz) + transformed_points.append(( + insert_point[0] + p[0], + insert_point[1] + p[1] + )) + return transformed_points + + elif entity.dxftype() == 'POLYLINE': + if entity.is_closed: + points = [(v.dxf.location.x, v.dxf.location.y) for v in entity.vertices] + insert_point = insert.dxf.insert + transformed_points = [] + for p in points: + transformed_points.append(( + insert_point[0] + p[0], + insert_point[1] + p[1] + )) + return transformed_points + + return None + + +def point_in_polygon(point, polygon): + """ + Prüft, ob ein Punkt innerhalb eines Polygons liegt (Ray-casting Algorithmus). + """ + x, y = point + n = len(polygon) + inside = False + + p1x, p1y = polygon[0] + for i in range(1, n + 1): + p2x, p2y = polygon[i % n] + if y > min(p1y, p2y): + if y <= max(p1y, p2y): + if x <= max(p1x, p2x): + if p1y != p2y: + xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x + if p1x == p2x or x <= xinters: + inside = not inside + p1x, p1y = p2x, p2y + + return inside + + +def find_symbols_in_boundary(doc, msp, boundary, target_layers, attributes): + """ + Findet alle Symbole (INSERT-Blöcke) innerhalb des angegebenen Bereichs auf den Ziel-Layern. + """ + symbols = [] + + # Bestimme welche Layer durchsucht werden sollen + search_layers = [] + for i in range(1, 4): # NAME1, NAME2, NAME3 + layer_key = f"LAYER_NAME{i}" + if layer_key in attributes and attributes[layer_key]: + search_layers.append(attributes[layer_key]) + + # Falls nur NAME/LAYER_NAME vorhanden + if "LAYER_NAME" in attributes and attributes["LAYER_NAME"]: + search_layers.append(attributes["LAYER_NAME"]) + + # Durchsuche alle INSERT-Blöcke + for entity in msp.query('INSERT'): + if entity.dxf.layer not in search_layers: + continue + + # Prüfe ob der Block Attribute hat + if not entity.has_attrib: + continue + + # Hole Position des Symbols + pos = entity.dxf.insert + point = (pos[0], pos[1]) + + # Prüfe ob innerhalb des Bereichs + if point_in_polygon(point, boundary): + # Extrahiere Attribute des Symbols + symbol_attribs = extract_block_attributes(entity) + + # Prüfe ob es ein Template ist (enthält @) + has_template = False + for value in symbol_attribs.values(): + if '@' in str(value): + has_template = True + break + + if has_template: + symbols.append({ + 'entity': entity, + 'position': point, + 'attributes': symbol_attribs, + 'layer': entity.dxf.layer + }) + + return symbols + + +def sort_symbols_by_direction(symbols, direction): + """ + Sortiert die Symbole nach der angegebenen Richtung. + TOP_BOTTOM: nach Y absteigend, dann X + BOTTOM_TOP: nach Y aufsteigend, dann X + LEFT_RIGHT: nach X aufsteigend, dann Y + RIGHT_LEFT: nach X absteigend, dann Y + """ + if direction == "TOP_BOTTOM": + return sorted(symbols, key=lambda s: (-s['position'][1], s['position'][0])) + elif direction == "BOTTOM_TOP": + return sorted(symbols, key=lambda s: (s['position'][1], s['position'][0])) + elif direction == "LEFT_RIGHT": + return sorted(symbols, key=lambda s: (s['position'][0], s['position'][1])) + elif direction == "RIGHT_LEFT": + return sorted(symbols, key=lambda s: (-s['position'][0], s['position'][1])) else: - print(f"Umgebungsvariable {env_str} ist nicht gesetzt oder leer.") - exit() + # Fallback: LEFT_RIGHT + return sorted(symbols, key=lambda s: (s['position'][0], s['position'][1])) + + +def enumerate_symbols(symbols, attributes): + """ + Nummeriert die Symbole durch und ersetzt die @@ Platzhalter. + """ + counter = 1 + renamed = [] + + for symbol in symbols: + symbol_attribs = symbol['attributes'] + layer = symbol['layer'] + + # Finde das passende NAME-Template für diesen Layer + name_template = None + for i in range(1, 4): + layer_key = f"LAYER_NAME{i}" + name_key = f"NAME{i}" + if layer_key in attributes and attributes[layer_key] == layer: + if name_key in attributes: + name_template = attributes[name_key] + break + + # Falls nur NAME/LAYER_NAME vorhanden + if not name_template and "LAYER_NAME" in attributes and attributes["LAYER_NAME"] == layer: + if "NAME" in attributes: + name_template = attributes["NAME"] + + if not name_template: + continue + + # Ersetze @-Zeichen durch Zahlen + # Zähle wie viele @ im Template sind + at_count = name_template.count('@') + number_str = str(counter).zfill(at_count) + new_name = name_template.replace('@' * at_count, number_str) + + # Aktualisiere Attribute im Symbol + for attrib in symbol['entity'].attribs: + old_value = attrib.dxf.text + if '@' in old_value: + # Ersetze @ durch die Nummer + new_value = old_value.replace('@' * at_count, number_str) + attrib.dxf.text = new_value + + renamed.append({ + 'position': symbol['position'], + 'layer': layer, + 'old_value': old_value, + 'new_value': new_value + }) + + counter += 1 + + return renamed + + +def process_renamer_blocks(doc, msp, renamer_layers, error_collector): + """ + Verarbeitet alle Renamer-Blöcke auf den angegebenen Layern. + """ + all_renamed = [] + + for layer in renamer_layers: + print(f"Durchsuche Layer: {layer}") + + # Finde alle INSERT-Blöcke auf diesem Layer + for insert in msp.query(f'INSERT[layer=="{layer}"]'): + # Extrahiere Attribute + attributes = extract_block_attributes(insert) + + if not attributes: + print(f" Block ohne Attribute gefunden an Position {insert.dxf.insert}") + continue + + # Prüfe ob es ein Renamer-Block ist + has_name = "NAME" in attributes or "NAME1" in attributes + has_direction = "DIRECTION" in attributes + + if not (has_name and has_direction): + continue + + print(f" Renamer-Block gefunden: {attributes.get('NAME', attributes.get('NAME1', 'UNKNOWN'))}") + print(f" Direction: {attributes.get('DIRECTION', 'UNKNOWN')}") + print(f" Kennzeichnung: {attributes.get('KENNZEICHNUNG', 'N/A')}") + + # Finde Boundary (Rechteck oder Polylinie) + boundary = get_boundary_geometry(doc, insert) + if not boundary: + error_msg = f"Keine Polylinie/Rechteck im Renamer-Block an Position {insert.dxf.insert} gefunden" + print(f" WARNUNG: {error_msg}") + error_collector.add_warnings({"missing_boundary": error_msg}) + continue + + print(f" Boundary gefunden mit {len(boundary)} Punkten") + + # Finde Symbole innerhalb des Bereichs + symbols = find_symbols_in_boundary(doc, msp, boundary, renamer_layers, attributes) + print(f" {len(symbols)} Template-Symbole gefunden") + + if not symbols: + continue + + # Sortiere nach Richtung + direction = attributes.get("DIRECTION", "LEFT_RIGHT") + sorted_symbols = sort_symbols_by_direction(symbols, direction) + + # Nummeriere durch + renamed = enumerate_symbols(sorted_symbols, attributes) + all_renamed.extend(renamed) + + print(f" {len(renamed)} Symbole nummeriert") + + return all_renamed + if __name__ == '__main__': - parser = argparse.ArgumentParser(description='fetches the x/y positions from a dxf file', prog='getpositions') - parser.add_argument('-f', '--filename', action='store', required=True, default="ST_6300_Steuerungstestlayout1_neueBloecke.dwg", help='which file should be fetched', metavar='myfile.dxf') - parser.add_argument('-w', '--write', action='store', help='write results into a json file') + parser = argparse.ArgumentParser(description='Nummeriert Symbole in DXF-Dateien basierend auf Renamer-Blöcken', prog='create_numbers') + parser.add_argument('-f', '--filename', action='store', required=True, help='DXF-Datei die verarbeitet werden soll', metavar='myfile.dxf') + parser.add_argument('-e', '--errorfile', action='store', required=False, help='JSON-Datei für Fehler und Warnungen', metavar='errors.json') + parser.add_argument('-w', '--write', action='store', help='Schreibe Ergebnisse der Nummerierung in eine JSON-Datei') + parser.add_argument('-d', '--dryrun', action='store_true', help='Symbole nicht in der DXF-Datei überschreiben, nur Ausgabe auf Konsole') args = parser.parse_args() out_dir = check_environment_var('PROJECT_DATA') work_dir = check_environment_var('PROJECT_WORK') config_dir = check_environment_var("PROJECT_CFG") - + + # Initialisiere ErrorCollector + error_collector = ErrorCollector() + filename = Path(args.filename) if not filename.suffix == ".dxf": - print("only available for .dxf files") - exit() + print("Nur für .dxf Dateien verfügbar") + exit(1) (dxf_path, dexists) = check_file_in_work(work_dir, filename) if dexists == False: - print("no such file ") + print(f"Datei nicht gefunden: {filename}") parser.print_help() - exit() + exit(1) - if dxf_is_binary(dxf_path): # Wenn dxf eine binary ist, dann komplett parsen und modelspace anlegen - print("Given .dxf-file is binary dxf. Proceeding to read file. Watch RAM-usage.") + # Lese Konfiguration + config_path = Path(config_dir) / "enumerate.cfg" + if not config_path.exists(): + print(f"Konfigurationsdatei nicht gefunden: {config_path}") + exit(1) + + renamer_layers = read_config_layers(config_path) + print(f"Konfigurierte Layer: {renamer_layers}") + + if not renamer_layers: + print("Keine Layer in der Konfiguration gefunden") + exit(1) + + # Lese DXF-Datei + if dxf_is_binary(dxf_path): + print("DXF-Datei ist binär. Lese komplette Datei. Achten Sie auf RAM-Nutzung.") doc = get_dxf_file(dxf_path) msp = doc.modelspace() - use_iter = False else: - print("Given .dxf-file is ASCII-dxf. Proceeding to use iterative functions. Process may take longer.") - use_iter = True + print("DXF-Datei ist ASCII.") + doc = get_dxf_file(dxf_path) + msp = doc.modelspace() + # Verarbeite Renamer-Blöcke + print("\n" + "="*60) + print("Starte Verarbeitung der Renamer-Blöcke") + print("="*60 + "\n") + renamed_symbols = process_renamer_blocks(doc, msp, renamer_layers, error_collector) + + print("\n" + "="*60) + print(f"Verarbeitung abgeschlossen: {len(renamed_symbols)} Symbole nummeriert") + print("="*60 + "\n") + + # Ausgabe der Ergebnisse + if renamed_symbols: + print("Nummerierte Symbole:") + for item in renamed_symbols: + print(f" {item['old_value']} -> {item['new_value']} (Layer: {item['layer']}, Pos: {item['position']})") + + # Speichere DXF-Datei wenn nicht dry-run + if not args.dryrun: + output_path = dxf_path.parent / f"{dxf_path.stem}_numbered{dxf_path.suffix}" + doc.saveas(output_path) + print(f"\nNummerierte DXF-Datei gespeichert: {output_path}") else: - parser.print_help() + print("\nDry-run Modus: DXF-Datei wurde nicht überschrieben") + + # Schreibe JSON-Ausgabe wenn gewünscht + if args.write: + output_data = { + 'renamed_symbols': renamed_symbols, + 'total_count': len(renamed_symbols) + } + output_path = Path(args.write) + write_json_file(output_data, output_path.parent, output_path.name) + print(f"Ergebnisse in JSON gespeichert: {args.write}") + + # Schreibe Fehler-Datei + if args.errorfile: + error_data = error_collector.get_all_issues() + error_path = Path(args.errorfile) + write_json_file(error_data, error_path.parent, error_path.name) + print(f"Fehler/Warnungen gespeichert: {args.errorfile}") + + # Exit-Code basierend auf Fehlern + if error_collector.has_errors(): + print("\n!!! Es sind Fehler aufgetreten !!!") + exit(1) + elif error_collector.has_errors_or_warnings(): + print("\n(Warnungen vorhanden, aber keine kritischen Fehler)") + exit(0) + else: + print("\nKeine Fehler oder Warnungen") + exit(0) diff --git a/lib/drawdxf.py b/lib/drawdxf.py index 14a50d1..8f7ab49 100644 --- a/lib/drawdxf.py +++ b/lib/drawdxf.py @@ -14,6 +14,7 @@ import updateconfignames as uc from pathlib import Path from error_collector import ErrorCollector, write_json_file +from utils import check_file_in_work @dataclass @@ -794,16 +795,6 @@ def _create_bom_workbook(outpath, processed_data, bezeichner_cfg): wb.save(bom_path) print(f"BOM exported to Excel-file") -def check_file_in_work(work_dir, 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): """ Kopiert bestimmte Layer (nach Filter) von einer Quell-DXF in eine Ziel-DXF. diff --git a/lib/getpositions.py b/lib/getpositions.py index 51037e7..1644b8c 100644 --- a/lib/getpositions.py +++ b/lib/getpositions.py @@ -12,6 +12,15 @@ from shapely.geometry import Point from ezdxf.lldxf.const import DXFStructureError from error_collector import ErrorCollector, write_json_file +from utils import ( + check_environment_var, + check_file_in_work, + dxf_is_binary, + get_dxf_file, + merge_two_dicts, + to_json, + write_results, +) """ @@ -24,21 +33,6 @@ Dieses Programm: """ -def write_results(jsn_results: str, out_dir: Path, filename: str) -> None: - """Write results to a JSON file.""" - print("writing results to a json file ...") - outfile = os.path.join(out_dir, filename) - with open(outfile, 'w', encoding='utf-8') as fh: - fh.write(jsn_results) - print("done") - - -def merge_two_dicts(x: dict, y: dict) -> dict: - z = x.copy() - z.update(y) - return z - - def get_type_of_name_cfg(name: str) -> str: prefix = name[:2] @@ -733,34 +727,6 @@ def scan(dxf_source) -> dict: ret['used_blocks'] = used_block_names ret['all_blocks'] = alle_block_defs return ret - -def to_json(d: object, pretty: bool = True) -> str: - return json.dumps(d, indent=2 if pretty else None, ensure_ascii=False, default=str) #ensure_ascii false für darstellung von "ue" - -def get_dxf_file(filepath: Path): - """Hole das dxf file.""" - try: - print("reading file ..", end='') - doc = ezdxf.filemanagement.readfile(filepath) - print("done") - except IOError: - print("Not a DXF file or a generic I/O error.") - sys.exit(1) - except DXFStructureError: - print("Invalid or corrupted DXF file.") - sys.exit(2) - return doc - -def check_file_in_work(work_dir: Path, filename: Path) -> tuple[Path, bool]: - fexists = True - if not filename.exists(): - mypath = work_dir.joinpath(filename) - ex = mypath.exists() - if not mypath.exists(): - fexists = False - else: - mypath = filename - return mypath, fexists def check_rack_z_coordinates(res_racks: dict, error_collector, config) -> None: """ @@ -829,11 +795,6 @@ def check_existance(res_mappings: dict, res_dist: dict, res_pos: dict, res_tunne ret["overdefined_tunnel"] = list() ret["overdefined_tunnel"].append(tname) return ret - -def dxf_is_binary(dxf_path: Path) -> bool: - with open(dxf_path, 'rb') as f: - header = f.read(22) - return b'AutoCAD Binary DXF' in header def validate_configs() -> None: errors = [] @@ -869,14 +830,6 @@ def validate_configs() -> None: else: print("No inconsistencies found. Continuing with routing process.") -def check_environment_var(env_str: str) -> Path: - out_path = os.environ.get(env_str) - if out_path: - return Path(out_path) - else: - print(f"Umgebungsvariable {env_str} ist nicht gesetzt oder leer.") - exit() - if __name__ == '__main__': parser = argparse.ArgumentParser(description='fetches the x/y positions from a dxf file', prog='getpositions') parser.add_argument('-f', '--filename', action='store', required=True, default="ST_6300_Steuerungstestlayout1_neueBloecke.dwg", help='which file should be fetched', metavar='myfile.dxf') diff --git a/lib/plant.py b/lib/plant.py index 3572531..571aace 100644 --- a/lib/plant.py +++ b/lib/plant.py @@ -11,6 +11,8 @@ from shapely.strtree import STRtree import math import shapely +from utils import to_json + # Globale Variable, die in main aufgerufen wird und steuert ob Graphen in unittests gezeichnet werden draw = False class PointSorter: @@ -33,9 +35,6 @@ class PointSorter: def get_sorted_by_y(self): return sorted(self.points, key = lambda p: p.y) - -def to_json(d, pretty: bool = True) -> str: - return json.dumps(d, indent=2 if pretty else None, default=str) #ensure_ascii false für darstellung von "ue" class NodeIDs(): ''' Klasse, die Punkte verwaltet und NodeIDs zu Punkten zuordnet. diff --git a/lib/portalexport.py b/lib/portalexport.py index 3f638a3..ec9baf7 100644 --- a/lib/portalexport.py +++ b/lib/portalexport.py @@ -5,6 +5,8 @@ from ioconverter import ExcelConverter import json from pathlib import Path +from utils import check_file_in_work, load_json + def process_item(sname, sdata): data = { "id": sdata.get("IO", ""), @@ -57,22 +59,6 @@ def prepare_data(rawdata:dict): return (dsensors, lsensors) -# einfache Funktionen -def load_json(jsonfilename): - with open(jsonfilename, encoding='utf-8') as fh: - return json.load(fh) - -def check_file_in_work(work_dir:Path, filename:Path): - fexists = True - if not filename.exists(): # dann schau im Work Ordner nach - mypath = work_dir.joinpath(filename) - ex = mypath.exists() - if not mypath.exists(): - fexists = False - else: - mypath = filename - return (mypath, fexists) - # Helper zum Laden der Error-JSON def load_error_json(json_path: Path, work_dir: Path): """ diff --git a/lib/routing.py b/lib/routing.py index df7917a..7f7f32c 100644 --- a/lib/routing.py +++ b/lib/routing.py @@ -8,24 +8,10 @@ import os import configparser import matplotlib.pyplot as plt +from utils import load_json, to_json, write_results + # Funktionen -def load_json(jsonfilename): - with open(jsonfilename, encoding='utf-8') as fh: - return json.load(fh) - -def write_results(jsnResults, outdir, filename): - """ write results to a json file - """ - print("writing results to a json file ...") - outfile = Path(outdir) / filename - with open(outfile, 'w', encoding='utf-8') as fh: - fh.write(jsnResults) - print("done") - -def to_json(d, pretty: bool = True) -> str: - return json.dumps(d, indent=2 if pretty else None, ensure_ascii=False, default=str) #ensure_ascii false für darstellung von "ue" - def create_plant(racks:dict, sensors:dict, distributors:dict, mapping:dict, tunnels: dict, tunlength:dict ) -> dict: # racks = {'Rack_1-0': [Point(0, 0), Point(0, 10)], diff --git a/lib/translate.py b/lib/translate.py index 798147e..5a8ade4 100644 --- a/lib/translate.py +++ b/lib/translate.py @@ -10,6 +10,8 @@ from fnmatch import fnmatch import ezdxf from openpyxl import Workbook +from utils import check_environment_var + """ Dieses Programm: @@ -24,16 +26,6 @@ Dieses Programm: """ -def check_environment_var(env_str: str) -> Path: - """Prüft ob eine Umgebungsvariable gesetzt ist und gibt den Pfad zurück.""" - out_path = os.environ.get(env_str) - if out_path: - return Path(out_path) - else: - print(f"Umgebungsvariable {env_str} ist nicht gesetzt oder leer.") - sys.exit(1) - - def load_translation_config(translation_lang: str, translation_dir: Path) -> tuple[dict[str, str], dict[str, str], dict[str, str], dict[str, str]]: """ Lädt eine Übersetzungs-Config-Datei (z.B. CS.cfg, EN.cfg, FR.cfg).