From 89ea433d451d8fa2ef885c672ca975718fe194cf Mon Sep 17 00:00:00 2001 From: mistangl Date: Fri, 23 Jan 2026 16:39:26 +0100 Subject: [PATCH] Programm versteht auch eine Polylinie als Weg an dem die Nummerierung entlang gehen kann. --- cfg/create_tests.json | 99 +++++++++++++++++++++- lib/create_example.py | 189 +++++++++++++++++++++++++++++++++++------- 2 files changed, 252 insertions(+), 36 deletions(-) diff --git a/cfg/create_tests.json b/cfg/create_tests.json index 600fb2a..c1c66a9 100644 --- a/cfg/create_tests.json +++ b/cfg/create_tests.json @@ -68,6 +68,97 @@ "use_polyline": false } ] + }, + + "Nummerierung2": { + "description": "Test-Szene mit komplexem Layout - 8 MA-1@@ Symbole in verschiedenen Anordnungen", + "ma_groups": [ + { + "name": "MA-1@@_top_left", + "count": 1, + "layout_type": "single", + "base_x": 200, + "base_y": 1400, + "spacing": 0 + }, + { + "name": "MA-1@@_top_right", + "count": 1, + "layout_type": "single", + "base_x": 1200, + "base_y": 1400, + "spacing": 0 + }, + { + "name": "MA-1@@_middle_left", + "count": 1, + "layout_type": "single", + "base_x": 300, + "base_y": 900, + "spacing": 0 + }, + { + "name": "MA-1@@_middle_right", + "count": 1, + "layout_type": "single", + "base_x": 850, + "base_y": 900, + "spacing": 0 + }, + { + "name": "MA-1@@_bottom_center", + "count": 1, + "layout_type": "single", + "base_x": 620, + "base_y": 400, + "spacing": 0 + }, + { + "name": "MA-1@@_right_top", + "count": 1, + "layout_type": "single", + "base_x": 1600, + "base_y": 1200, + "spacing": 0 + }, + { + "name": "MA-1@@_right_middle", + "count": 1, + "layout_type": "single", + "base_x": 1600, + "base_y": 700, + "spacing": 0 + }, + { + "name": "MA-1@@_right_bottom", + "count": 1, + "layout_type": "single", + "base_x": 1520, + "base_y": 400, + "spacing": 0 + } + ], + "renaming_frames": [ + { + "comment": "Polyline-Pfad: Verbindet alle MA-1@@ in kreisförmiger Bewegung", + "type": "polyline_path", + "name": "MA-1@@", + "kennzeichnung": "A01+UH00", + "layer_name": "ILS_MOTOR", + "direction": "POLYLINE_PATH", + "path_points": [ + {"x": 200, "y": 1400}, + {"x": 1200, "y": 1400}, + {"x": 1600, "y": 1200}, + {"x": 1600, "y": 700}, + {"x": 1520, "y": 400}, + {"x": 620, "y": 400}, + {"x": 300, "y": 900}, + {"x": 850, "y": 900}, + {"x": 200, "y": 1400} + ] + } + ] } }, @@ -107,22 +198,22 @@ "_description": "ATTDEF Positionen im inneren Block RENAMER_ATTRIB_MA", "NAME": { "x": 10, - "y": 50, + "y": -10, "height": 21.25 }, "KENNZEICHNUNG": { "x": 10, - "y": 20, + "y": -35, "height": 21.25 }, "LAYER_NAME": { "x": 10, - "y": 0, + "y": -60, "height": 21.25 }, "DIRECTION": { "x": 10, - "y": -80, + "y": -85, "height": 21.25 } } diff --git a/lib/create_example.py b/lib/create_example.py index 5aea6f2..8b7d1b5 100644 --- a/lib/create_example.py +++ b/lib/create_example.py @@ -24,9 +24,10 @@ PROJECT_CFG = os.getenv('PROJECT_CFG', os.path.join(PROJECT, 'cfg')) class TestDataGenerator: """Generiert Test-DXF-Dateien für verschiedene Szenarien""" - def __init__(self, filename, test_type, config_file=None): + def __init__(self, filename, test_type, config_file=None, scene_name=None): self.filename = filename self.test_type = test_type + self.scene_name = scene_name self.doc = None self.msp = None self.config = None @@ -330,13 +331,14 @@ class TestDataGenerator: traceback.print_exc() return None - def create_renamer_attrib_block(self, block_name, attrib_tags): + def create_renamer_attrib_block(self, block_name, attrib_tags, frame_type='MA'): """ Erstellt einen Block mit ATTDEF-Attributen für Renamer-Frames Args: block_name: Name des Attribut-Blocks (z.B. 'RENAMER_ATTRIB_MA') attrib_tags: Liste der Attribut-Tags (z.B. ['NAME', 'KENNZEICHNUNG', ...]) + frame_type: 'MA' oder 'MULTI' für Frame-Typ Returns: Block-Definition @@ -346,9 +348,20 @@ class TestDataGenerator: attrib_block = self.doc.blocks.new(block_name) - # Definiere Standard-Positionen und Werte für Attribute - # Font-Größe auf ein Viertel reduziert (85.0 → 21.25) - attrib_definitions = { + # Hole Positionen aus Config, falls vorhanden + if self.config and 'general' in self.config: + general = self.config['general'] + if frame_type == 'MA' and 'ma_frame' in general: + config_positions = general['ma_frame'].get('attdef_positions', {}) + elif frame_type == 'MULTI' and 'multi_frame' in general: + config_positions = general['multi_frame'].get('attdef_positions', {}) + else: + config_positions = {} + else: + config_positions = {} + + # Definiere Standard-Positionen und Werte für Attribute (Fallback) + default_definitions = { 'NAME': {'insert': (10, 50), 'default': 'MA@@', 'height': 21.25}, 'NAME1': {'insert': (10, 80), 'default': 'BG1@@@', 'height': 21.25}, 'NAME2': {'insert': (10, 60), 'default': 'MB1@@@', 'height': 21.25}, @@ -364,14 +377,25 @@ class TestDataGenerator: # Füge nur die angeforderten Attribute hinzu for tag in attrib_tags: - if tag in attrib_definitions: - attdef = attrib_definitions[tag] + if tag in default_definitions: + default_def = default_definitions[tag] + + # Verwende Config-Werte, falls vorhanden, sonst Defaults + if tag in config_positions and not tag.startswith('_'): + config_pos = config_positions[tag] + x = config_pos.get('x', default_def['insert'][0]) + y = config_pos.get('y', default_def['insert'][1]) + height = config_pos.get('height', default_def['height']) + else: + x, y = default_def['insert'] + height = default_def['height'] + attrib_block.add_attdef( tag=tag, - insert=attdef['insert'], - text=attdef['default'], + insert=(x, y), + text=default_def['default'], dxfattribs={ - 'height': attdef['height'], + 'height': height, 'layer': 'ILS_RENAMER', 'color': 1, 'style': 'textstyle3' @@ -412,8 +436,8 @@ class TestDataGenerator: 'LAYER_NAME1', 'LAYER_NAME2', 'LAYER_NAME3', 'DIRECTION', 'ID'] attrib_block_name = 'RENAMER_ATTRIB_MULTI' - # Erstelle Attribut-Block (wiederverwendbar) - self.create_renamer_attrib_block(attrib_block_name, attrib_tags) + # Erstelle Attribut-Block (wiederverwendbar) mit Config-Unterstützung + self.create_renamer_attrib_block(attrib_block_name, attrib_tags, frame_type=frame_type) # Erstelle äußeren Renamer-Block mit eindeutigem Namen (inkl. Position und Größe) # Jeder Rahmen bekommt einen eindeutigen Namen basierend auf Position @@ -462,13 +486,79 @@ class TestDataGenerator: dxfattribs={'layer': 'ILS_RENAMER'} ) - def generate_nummerierung(self): + def create_polyline_renamer_frame(self, path_points, name, kennzeichnung, layer_name, direction): + """ + Erstellt einen Renamer-Rahmen mit Polyline-Pfad + + Struktur: + - Block (RENAMER_POLYLINE_xxx): + 1. LWPOLYLINE (Pfad durch die Punkte) + 2. Block-Referenz mit Attributen (RENAMER_ATTRIB_MA) + + Args: + path_points: Liste von Dictionaries mit 'x' und 'y' Koordinaten + name: NAME-Attribut + kennzeichnung: KENNZEICHNUNG-Attribut + layer_name: LAYER_NAME-Attribut + direction: DIRECTION-Attribut (z.B. 'POLYLINE_PATH') + """ + # Erstelle Attribut-Block (wiederverwendbar) + attrib_tags = ['NAME', 'KENNZEICHNUNG', 'LAYER_NAME', 'DIRECTION'] + attrib_block_name = 'RENAMER_ATTRIB_MA' + self.create_renamer_attrib_block(attrib_block_name, attrib_tags, frame_type='MA') + + # Erstelle eindeutigen Block-Namen basierend auf erstem Punkt + first_point = path_points[0] if path_points else {'x': 0, 'y': 0} + block_name = f"RENAMER_POLYLINE_{name.replace('@', '').replace('-', '_')}_{int(first_point['x'])}_{int(first_point['y'])}" + + if block_name not in self.doc.blocks: + renamer_block = self.doc.blocks.new(block_name) + + # 1. Füge LWPOLYLINE hinzu (Pfad) + # Konvertiere Punkte von Dict zu Tupeln + points = [(p['x'], p['y']) for p in path_points] + + renamer_block.add_lwpolyline(points, dxfattribs={ + 'layer': 'ILS_RENAMER', + 'color': 1 + }) + + # 2. Füge Block-Referenz mit Attributen hinzu + # Position neben dem ersten Punkt des Pfades (mit kleinem Offset) + first_x = points[0][0] + first_y = points[0][1] + attrib_insert_pos = (first_x + 10, first_y + 30) # Offset vom ersten Punkt + attrib_blockref = renamer_block.add_blockref( + attrib_block_name, + insert=attrib_insert_pos, + dxfattribs={'layer': 'ILS_RENAMER'} + ) + + # Setze Attribut-Werte + attrib_blockref.add_auto_attribs({ + 'NAME': name, + 'KENNZEICHNUNG': kennzeichnung, + 'LAYER_NAME': layer_name, + 'DIRECTION': direction + }) + + # Füge Block-Referenz im Modelspace ein (am Ursprung, da Pfad absolute Koordinaten hat) + blockref = self.msp.add_blockref( + block_name, + insert=(0, 0), + dxfattribs={'layer': 'ILS_RENAMER'} + ) + + def generate_nummerierung(self, scene_name='Nummerierung1'): """ Generiert Testdaten für Nummerierung-Szenario Verwendet entweder die JSON-Config oder Hard-coded Defaults + + Args: + scene_name: Name der Testszene (z.B. 'Nummerierung1', 'Nummerierung2') """ - print("Generiere Nummerierung-Testdaten...") + print(f"Generiere Nummerierung-Testdaten ({scene_name})...") # Erstelle io-Block direkt (ohne Referenzdatei) io_block = self.create_io_block() @@ -478,9 +568,9 @@ class TestDataGenerator: return # Wenn Config geladen ist, verwende sie, ansonsten Hard-coded Defaults - if self.config and 'test_scenes' in self.config and 'Nummerierung1' in self.config['test_scenes']: - print("Verwende JSON-Config für Nummerierung1...") - self._generate_nummerierung_from_config() + if self.config and 'test_scenes' in self.config and scene_name in self.config['test_scenes']: + print(f"Verwende JSON-Config für {scene_name}...") + self._generate_nummerierung_from_config(scene_name) else: print("Verwende Hard-coded Defaults für Nummerierung...") self._generate_nummerierung_hardcoded() @@ -548,9 +638,14 @@ class TestDataGenerator: blockref.add_auto_attribs(attrib_values) - def _generate_nummerierung_from_config(self): - """Generiert Nummerierung-Testdaten aus JSON-Config""" - scene = self.config['test_scenes']['Nummerierung1'] + def _generate_nummerierung_from_config(self, scene_name='Nummerierung1'): + """ + Generiert Nummerierung-Testdaten aus JSON-Config + + Args: + scene_name: Name der Testszene in der Config + """ + scene = self.config['test_scenes'][scene_name] ma_defaults = self.config.get('ma_defaults', {}) general = self.config.get('general', {}) @@ -570,16 +665,29 @@ class TestDataGenerator: # Generiere Renamer-Rahmen aus Config for frame_config in scene.get('renaming_frames', []): - self.create_renamer_frame( - insert_point=(frame_config['x'], frame_config['y']), - width=frame_config['width'], - height=frame_config['height'], - name=frame_config['name'], - kennzeichnung=frame_config['kennzeichnung'], - layer_name=frame_config['layer_name'], - direction=frame_config['direction'], - use_polyline=frame_config.get('use_polyline', False) - ) + frame_type = frame_config.get('type', 'rectangle') + + if frame_type == 'polyline_path': + # Polyline-Pfad Frame + self.create_polyline_renamer_frame( + path_points=frame_config['path_points'], + name=frame_config['name'], + kennzeichnung=frame_config['kennzeichnung'], + layer_name=frame_config['layer_name'], + direction=frame_config['direction'] + ) + else: + # Standard Rectangle Frame + self.create_renamer_frame( + insert_point=(frame_config['x'], frame_config['y']), + width=frame_config['width'], + height=frame_config['height'], + name=frame_config['name'], + kennzeichnung=frame_config['kennzeichnung'], + layer_name=frame_config['layer_name'], + direction=frame_config['direction'], + use_polyline=frame_config.get('use_polyline', False) + ) def _generate_nummerierung_hardcoded(self): """Generiert Nummerierung-Testdaten mit Hard-coded Werten (Fallback)""" @@ -727,7 +835,17 @@ class TestDataGenerator: self.create_document() if self.test_type == 'nummerierung': - self.generate_nummerierung() + # Bestimme Scene-Name: entweder explizit angegeben oder automatisch aus Filename + scene_name = self.scene_name + if not scene_name: + # Versuche Scene-Name aus Filename zu extrahieren (z.B. "Nummerierung2.dxf" -> "Nummerierung2") + import re + match = re.match(r'(Nummerierung\d+)', self.filename, re.IGNORECASE) + if match: + scene_name = match.group(1) + else: + scene_name = 'Nummerierung1' # Default + self.generate_nummerierung(scene_name) elif self.test_type == 'sps': print("SPS-Testdaten noch nicht implementiert") elif self.test_type == 'todraw': @@ -780,6 +898,13 @@ def main(): help='JSON-Konfigurationsdatei (Standard: create_tests.json)' ) + parser.add_argument( + '--scene', + type=str, + default=None, + help='Name der Testszene (z.B. Nummerierung1, Nummerierung2). Falls nicht angegeben, wird aus Filename extrahiert.' + ) + args = parser.parse_args() # Stelle sicher, dass Dateiname .dxf Endung hat @@ -787,7 +912,7 @@ def main(): args.filename += '.dxf' # Generiere Testdaten - generator = TestDataGenerator(args.filename, args.type, config_file=args.config) + generator = TestDataGenerator(args.filename, args.type, config_file=args.config, scene_name=args.scene) if generator.generate(): generator.save(args.output_dir)