diff --git a/lib/create_numbers.py b/lib/create_numbers.py index 2790c62..72ef845 100644 --- a/lib/create_numbers.py +++ b/lib/create_numbers.py @@ -1,4 +1,5 @@ import argparse +import json from pathlib import Path @@ -52,6 +53,33 @@ Alle Sensoren, Motoren, Aktoren etc. innerhalb der zugehörigen Polylinie nehmen """ +def read_test_scenes_from_json(json_path: Path) -> list: + """ + Liest die create_tests.json und gibt alle konfigurierten Szenen-Namen zurück. + + Args: + json_path: Pfad zur create_tests.json + + Returns: + Liste der Szenen-Namen (z.B. ['Nummerierung1', 'Nummerierung2', 'BGMG', ...]) + """ + if not json_path.exists(): + return [] + + try: + with open(json_path, 'r', encoding='utf-8') as f: + data = json.load(f) + + # Hole alle Keys aus test_scenes + if 'test_scenes' in data: + return list(data['test_scenes'].keys()) + else: + return [] + except Exception as e: + print(f"Fehler beim Lesen der JSON-Datei {json_path}: {e}") + return [] + + 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. @@ -1268,79 +1296,28 @@ def collect_all_symbols_from_groups(doc, msp, renamer_groups, renamer_layers, er return all_symbols -if __name__ == '__main__': - # Lese Umgebungsvariablen vor argparse, um Config-Info in Hilfe anzuzeigen - out_dir = check_environment_var('PROJECT_DATA') - work_dir = check_environment_var('PROJECT_WORK') - config_dir = check_environment_var("PROJECT_CFG") +def process_single_file(dxf_path, work_dir, renamer_layers, polyline_max_distance, args): + """ + Verarbeitet eine einzelne DXF-Datei. - # Versuche Config zu lesen für --help Ausgabe - config_path = Path(config_dir) / "enumerate.cfg" - available_layers = [] - if config_path.exists(): - try: - available_layers = read_config_layers(config_path) - except Exception: - pass # Ignoriere Fehler beim Lesen für --help + Args: + dxf_path: Pfad zur DXF-Datei + work_dir: Work-Verzeichnis + renamer_layers: Liste der konfigurierten Renamer-Layer + polyline_max_distance: Maximale Distanz zur Polylinie + args: Argparse-Argumente - # Erstelle Epilog mit verfügbaren Layern - epilog_text = "\nKonfigurierte Renamer-Layer:\n" - if available_layers: - epilog_text += " " + ", ".join(available_layers) + "\n" - epilog_text += f"\n(aus Konfigurationsdatei: {config_path})" - else: - epilog_text += " (keine Layer konfiguriert oder Config-Datei nicht gefunden)\n" - epilog_text += f" Standard-Pfad: {config_path}" - - parser = argparse.ArgumentParser( - description='Nummeriert Symbole in DXF-Dateien basierend auf Renamer-Blöcken', - prog='create_numbers', - epilog=epilog_text, - formatter_class=argparse.RawDescriptionHelpFormatter - ) - 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('-i', '--info', 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') - parser.add_argument('--overwrite', action='store_true', help='Überschreibt die Originaldatei anstatt eine neue Datei mit Suffix _numbered zu erstellen') - parser.add_argument('--show_symbol_frames', action='store_true', help='Zeichne Rahmen um jedes Symbol, um die Grenze des Symbols zu visualisieren') - - args = parser.parse_args() - - # Validierung: --overwrite und --dryrun können nicht zusammen verwendet werden - if args.overwrite and args.dryrun: - parser.error("--overwrite und --dryrun können nicht zusammen verwendet werden") - - # Initialisiere ErrorCollector + Returns: + Tuple (success, error_collector): + - success: True wenn erfolgreich verarbeitet + - error_collector: ErrorCollector mit gesammelten Fehlern/Warnungen + """ + # Initialisiere ErrorCollector für diese Datei error_collector = ErrorCollector() - filename = Path(args.filename) - if not filename.suffix == ".dxf": - print("Nur für .dxf Dateien verfügbar") - exit(1) - - (dxf_path, dexists) = check_file_in_work(work_dir, filename) - if dexists == False: - print(f"Datei nicht gefunden: {filename}") - parser.print_help() - exit(1) - - # Config-Datei existiert bereits (wurde für --help geprüft) - 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 Parameter aus Config - config_parameters = read_config_parameters(config_path) - polyline_max_distance = config_parameters.get('polyline_max_distance', 500.0) - print(f"Parameter: polyline_max_distance = {polyline_max_distance}") + print(f"\n{'='*80}") + print(f"Verarbeite Datei: {dxf_path.name}") + print(f"{'='*80}\n") # Lese DXF-Datei if dxf_is_binary(dxf_path): @@ -1450,12 +1427,200 @@ if __name__ == '__main__': write_json_file(error_data, dxf_path.parent, error_filename) print(f"Fehler/Warnungen gespeichert: {dxf_path.parent / error_filename}") - # Exit-Code basierend auf Fehlern + # Status-Meldung if error_collector.has_errors(): print("\n!!! Es sind Fehler aufgetreten !!!") - exit(1) + return False, error_collector elif error_collector.has_errors_or_warnings(): print("\n(Warnungen vorhanden, aber keine kritischen Fehler)") - exit(0) + return True, error_collector else: - exit(0) + return True, error_collector + + +if __name__ == '__main__': + # Lese Umgebungsvariablen vor argparse, um Config-Info in Hilfe anzuzeigen + out_dir = check_environment_var('PROJECT_DATA') + work_dir = check_environment_var('PROJECT_WORK') + config_dir = check_environment_var("PROJECT_CFG") + + # Versuche Config zu lesen für --help Ausgabe + config_path = Path(config_dir) / "enumerate.cfg" + available_layers = [] + if config_path.exists(): + try: + available_layers = read_config_layers(config_path) + except Exception: + pass # Ignoriere Fehler beim Lesen für --help + + # Erstelle Epilog mit verfügbaren Layern + epilog_text = "\nKonfigurierte Renamer-Layer:\n" + if available_layers: + epilog_text += " " + ", ".join(available_layers) + "\n" + epilog_text += f"\n(aus Konfigurationsdatei: {config_path})" + else: + epilog_text += " (keine Layer konfiguriert oder Config-Datei nicht gefunden)\n" + epilog_text += f" Standard-Pfad: {config_path}" + + parser = argparse.ArgumentParser( + description='Nummeriert Symbole in DXF-Dateien basierend auf Renamer-Blöcken', + prog='create_numbers', + epilog=epilog_text, + formatter_class=argparse.RawDescriptionHelpFormatter + ) + parser.add_argument('-f', '--filename', action='store', required=False, 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('-i', '--info', 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') + parser.add_argument('--overwrite', action='store_true', help='Überschreibt die Originaldatei anstatt eine neue Datei mit Suffix _numbered zu erstellen') + parser.add_argument('--show_symbol_frames', action='store_true', help='Zeichne Rahmen um jedes Symbol, um die Grenze des Symbols zu visualisieren') + parser.add_argument('--all-examples', action='store_true', help='Verarbeitet alle in create_tests.json konfigurierten DXF-Dateien aus dem work-Ordner') + + args = parser.parse_args() + + # Validierung: --filename oder --all-examples muss angegeben werden + if not args.filename and not args.all_examples: + parser.error("Entweder --filename oder --all-examples muss angegeben werden") + + # Validierung: --filename und --all-examples können nicht zusammen verwendet werden + if args.filename and args.all_examples: + parser.error("--filename und --all-examples können nicht zusammen verwendet werden") + + # Validierung: --overwrite und --dryrun können nicht zusammen verwendet werden + if args.overwrite and args.dryrun: + parser.error("--overwrite und --dryrun können nicht zusammen verwendet werden") + + # Validierung: Bei --all-examples können --errorfile und --info nicht verwendet werden + if args.all_examples and (args.errorfile or args.info): + parser.error("Bei --all-examples können --errorfile und --info nicht verwendet werden (Dateien werden automatisch generiert)") + + # Config-Datei existiert bereits (wurde für --help geprüft) + 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 Parameter aus Config + config_parameters = read_config_parameters(config_path) + polyline_max_distance = config_parameters.get('polyline_max_distance', 500.0) + print(f"Parameter: polyline_max_distance = {polyline_max_distance}") + + # ==================================================================================== + # Verarbeite entweder eine einzelne Datei oder alle Beispiel-Dateien + # ==================================================================================== + + if args.all_examples: + # Verarbeite alle in create_tests.json konfigurierten Dateien + print("\n" + "="*80) + print("MODUS: Verarbeite alle Beispiel-Dateien aus create_tests.json") + print("="*80) + + # Lese Test-Szenen aus create_tests.json + create_tests_path = Path(config_dir) / "create_tests.json" + test_scenes = read_test_scenes_from_json(create_tests_path) + + if not test_scenes: + print(f"\nFEHLER: Keine Test-Szenen in {create_tests_path} gefunden") + exit(1) + + print(f"\nGefundene Test-Szenen: {', '.join(test_scenes)}") + print(f"Anzahl: {len(test_scenes)}\n") + + # Prüfe welche DXF-Dateien im work-Ordner existieren + existing_files = [] + missing_files = [] + + for scene_name in test_scenes: + dxf_filename = f"{scene_name}.dxf" + dxf_path = Path(work_dir) / dxf_filename + + if dxf_path.exists(): + existing_files.append((scene_name, dxf_path)) + else: + missing_files.append(dxf_filename) + + # Melde fehlende Dateien + if missing_files: + print("WARNUNG: Folgende DXF-Dateien wurden im work-Ordner nicht gefunden:") + for filename in missing_files: + print(f" - {filename}") + print() + + if not existing_files: + print("FEHLER: Keine DXF-Dateien im work-Ordner gefunden") + exit(1) + + print(f"Verarbeite {len(existing_files)} Datei(en):\n") + + # Verarbeite alle gefundenen Dateien + results = [] + for scene_name, dxf_path in existing_files: + success, error_collector = process_single_file(dxf_path, work_dir, renamer_layers, polyline_max_distance, args) + results.append({ + 'scene_name': scene_name, + 'filename': dxf_path.name, + 'success': success, + 'has_errors': error_collector.has_errors(), + 'has_warnings': error_collector.has_errors_or_warnings() and not error_collector.has_errors() + }) + + # Zusammenfassung + print("\n" + "="*80) + print("ZUSAMMENFASSUNG") + print("="*80) + + successful = sum(1 for r in results if r['success']) + with_errors = sum(1 for r in results if r['has_errors']) + with_warnings = sum(1 for r in results if r['has_warnings']) + + print(f"\nVerarbeitete Dateien: {len(results)}") + print(f"Erfolgreich: {successful}") + print(f"Mit Fehlern: {with_errors}") + print(f"Mit Warnungen (ohne Fehler): {with_warnings}") + + if with_errors > 0: + print("\nDateien mit Fehlern:") + for r in results: + if r['has_errors']: + print(f" - {r['filename']}") + + if with_warnings > 0: + print("\nDateien mit Warnungen:") + for r in results: + if r['has_warnings']: + print(f" - {r['filename']}") + + # Exit-Code: Fehler wenn mindestens eine Datei Fehler hat + if with_errors > 0: + print("\n!!! Mindestens eine Datei hat Fehler !!!") + exit(1) + else: + print("\nAlle Dateien erfolgreich verarbeitet!") + exit(0) + + else: + # Verarbeite einzelne Datei + filename = Path(args.filename) + if not filename.suffix == ".dxf": + print("Nur für .dxf Dateien verfügbar") + exit(1) + + (dxf_path, dexists) = check_file_in_work(work_dir, filename) + if dexists == False: + print(f"Datei nicht gefunden: {filename}") + parser.print_help() + exit(1) + + success, error_collector = process_single_file(dxf_path, work_dir, renamer_layers, polyline_max_distance, args) + + # Exit-Code basierend auf Fehlern + if error_collector.has_errors(): + exit(1) + else: + exit(0)