Programm versteht auch eine Polylinie als Weg an dem die Nummerierung entlang gehen kann.

This commit is contained in:
2026-01-23 16:39:26 +01:00
parent 84f8751eff
commit 89ea433d45
2 changed files with 252 additions and 36 deletions
+95 -4
View File
@@ -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
}
}
+157 -32
View File
@@ -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)