Schalter --show_symbol_frames dazugemacht.

This commit is contained in:
2026-01-29 16:45:48 +01:00
parent c4ef6e28f4
commit ae82736960
3 changed files with 442 additions and 34 deletions
+89 -29
View File
@@ -14,9 +14,11 @@ import json
from pathlib import Path
import ezdxf
from symbol_frames import draw_symbol_frames
# Umgebungsvariablen
PROJECT = os.getenv('PROJECT', Path(__file__).parent.parent.absolute())
PROJECT_TEST = os.getenv('PROJECT_TEST', os.path.join(PROJECT, 'testdata'))
PROJECT_WORK = os.getenv('PROJECT_WORK', os.path.join(PROJECT, 'work'))
PROJECT_CFG = os.getenv('PROJECT_CFG', os.path.join(PROJECT, 'cfg'))
@@ -284,13 +286,38 @@ class TestDataGenerator:
def save(self, output_dir=None):
"""Speichert das DXF-Dokument"""
if output_dir is None:
output_dir = PROJECT_TEST
output_dir = PROJECT_WORK
output_path = os.path.join(output_dir, self.filename)
self.doc.saveas(output_path)
print(f"Test-DXF erstellt: {output_path}")
return output_path
def draw_frames_for_generated_symbols(self):
"""
Zeichnet Rahmen um alle generierten Symbole zur Visualisierung.
Konvertiert das interne Format in das von draw_symbol_frames erwartete Format.
"""
if not self.generated_symbols:
print("Keine generierten Symbole zum Zeichnen von Rahmen gefunden")
return 0
# Konvertiere das interne Format in das erwartete Format
symbols_for_frames = []
for sym in self.generated_symbols:
# generated_symbols enthält 'x', 'y' (Einfügepunkt = linke untere Ecke bei left_bottom_corner)
# draw_symbol_frames erwartet Position = linke untere Ecke des Symbols
# Wir können die gespeicherten Koordinaten direkt verwenden
symbols_for_frames.append({
'position': (sym['x'], sym['y']), # Position = linke untere Ecke
'attributes': {'IO': sym['io']}, # IO-Wert für Breitenberechnung
'layer': sym.get('layer', 'ILS_MOTOR')
})
# Zeichne die Rahmen
return draw_symbol_frames(self.doc, self.msp, symbols_for_frames)
def load_block_from_file(self, source_file, block_name):
"""
Lädt einen Block aus einer anderen DXF-Datei
@@ -608,15 +635,15 @@ class TestDataGenerator:
return offset_map.get(insert_point, (0, 0)) # Default: left_top_corner
def _generate_ma_group(self, group, ma_defaults, width_per_char, fixed_height, spacing_factor, y_offsets_list, insert_point='left_top_corner'):
def _generate_ma_group(self, group, ma_defaults, symbol_width, symbol_height, spacing_factor, y_offsets_list, insert_point='left_top_corner'):
"""
Generiert eine Gruppe von MA-Symbolen
Args:
group: Dict mit Gruppen-Config (name, count, layout_type, base_x, base_y, spacing)
ma_defaults: Dict mit MA-Default-Attributen
width_per_char: Breite pro Zeichen
fixed_height: Fixe Symbol-Höhe
symbol_width: Feste Breite des Symbols
symbol_height: Feste Höhe des Symbols
spacing_factor: Spacing-Faktor zwischen Symbolen
y_offsets_list: Liste von Y-Offsets für horizontal_offset Layout
insert_point: Einfügepunkt des Blocks (z.B. "left_top_corner", "center")
@@ -630,9 +657,6 @@ class TestDataGenerator:
# Extrahiere IO-Pattern aus Name (z.B. "MA-1@@_top" -> "MA-1@@")
io_pattern = name.split('_')[0]
# Berechne Symbol-Ausdehnung
symbol_width = len(io_pattern) * width_per_char
# Berechne Spacing
if layout_type in ['horizontal', 'horizontal_offset']:
actual_spacing = symbol_width * spacing_factor
@@ -644,7 +668,7 @@ class TestDataGenerator:
layer = ma_defaults.get('layer', 'ILS_MOTOR')
# Berechne Offset basierend auf Einfügepunkt
offset_x, offset_y = self._calculate_insert_offset(insert_point, symbol_width, fixed_height)
offset_x, offset_y = self._calculate_insert_offset(insert_point, symbol_width, symbol_height)
# Generiere Symbole basierend auf Layout-Typ
for i in range(count):
@@ -669,7 +693,7 @@ class TestDataGenerator:
# (da der io-Block bei (0,0) startet und nach oben geht)
blockref = self.msp.add_blockref(
'io',
insert=(left_top_x, left_top_y - fixed_height),
insert=(left_top_x, left_top_y - symbol_height),
dxfattribs={'layer': layer}
)
@@ -681,10 +705,12 @@ class TestDataGenerator:
blockref.add_auto_attribs(attrib_values)
# Speichere Symbol-Position für spätere Validierung
# Speichere immer die linke obere Ecke
# Speichere den tatsächlichen Einfügepunkt (linke untere Ecke bei left_bottom_corner)
actual_insert_x = left_top_x
actual_insert_y = left_top_y - symbol_height
self.generated_symbols.append({
'x': left_top_x,
'y': left_top_y,
'x': actual_insert_x,
'y': actual_insert_y,
'io': io_pattern,
'group_name': name,
'layer': layer
@@ -701,10 +727,10 @@ class TestDataGenerator:
ma_defaults = self.config.get('ma_defaults', {})
general = self.config.get('general', {})
# Hole Dimensions aus Config
# Hole Dimensions aus Config (feste Werte)
dimensions = ma_defaults.get('dimensions', {})
width_per_char = dimensions.get('width_per_char', 201.49)
fixed_height = dimensions.get('fixed_height', 380.94)
symbol_width = dimensions.get('width', 1210)
symbol_height = dimensions.get('height', 381)
# Hole Layout-Einstellungen
layout = general.get('layout', {})
@@ -717,7 +743,7 @@ class TestDataGenerator:
# Generiere MA-Gruppen aus Config
for group in scene.get('ma_groups', []):
self._generate_ma_group(group, ma_defaults, width_per_char, fixed_height, spacing_factor, y_offsets_list, insert_point)
self._generate_ma_group(group, ma_defaults, symbol_width, symbol_height, spacing_factor, y_offsets_list, insert_point)
# Generiere Renamer-Rahmen aus Config
for frame_config in scene.get('renaming_frames', []):
@@ -916,10 +942,15 @@ class TestDataGenerator:
print("Prüfe ob alle generierten Symbole in Renamer-Frames liegen")
print("="*60)
# Hole Symbol-Dimensionen aus Config
# Hole Symbol-Dimensionen aus Config (feste Werte)
dimensions = self.config.get('ma_defaults', {}).get('dimensions', {})
width_per_char = dimensions.get('width_per_char', 201.49)
fixed_height = dimensions.get('fixed_height', 380.94)
ma_symbol_width = dimensions.get('width', 1210)
ma_symbol_height = dimensions.get('height', 381)
# Hole Multi-Frame Dimensionen
multi_dimensions = self.config.get('general', {}).get('multi_frame', {}).get('dimensions', {})
multi_symbol_width = multi_dimensions.get('width', 1410)
multi_symbol_height = multi_dimensions.get('height', 381)
# Sammle alle Frame-Boundaries
frame_boundaries = []
@@ -969,15 +1000,20 @@ class TestDataGenerator:
# Prüfe jedes generierte Symbol
symbols_outside = []
for symbol in self.generated_symbols:
# Berechne Symbol-Dimensionen
# Berechne Symbol-Dimensionen basierend auf IO-Text Länge
io_text = symbol['io']
symbol_width = len(io_text) * width_per_char
symbol_height = fixed_height
char_count = len(io_text)
if char_count >= 7:
symbol_width = multi_symbol_width # Multi-Frame (BG/MB/POT)
symbol_height = multi_symbol_height
else:
symbol_width = ma_symbol_width # MA-Frame
symbol_height = ma_symbol_height
# Berechne Mittelpunkt des Symbols
# symbol['x'], symbol['y'] ist die linke obere Ecke
# symbol['x'], symbol['y'] ist die linke untere Ecke
symbol_center_x = symbol['x'] + symbol_width / 2
symbol_center_y = symbol['y'] - symbol_height / 2
symbol_center_y = symbol['y'] + symbol_height / 2
symbol_point = (symbol_center_x, symbol_center_y)
# Radius für Kreis-Test
@@ -1041,7 +1077,8 @@ class TestDataGenerator:
symbol_with_type['symbol_radius'] = symbol_radius
symbol_with_type['shift_x'] = best_shift[0]
symbol_with_type['shift_y'] = best_shift[1]
symbol_with_type['closest_frame'] = closest_frame.get('name', 'UNKNOWN') if closest_frame else 'UNKNOWN'
symbol_with_type['closest_frame_name'] = closest_frame.get('name', 'UNKNOWN') if closest_frame else 'UNKNOWN'
symbol_with_type['closest_frame_dict'] = closest_frame # Speichere das gesamte Dictionary
symbols_outside.append(symbol_with_type)
# Gib Warnungen aus und zeichne Fehlerkreise
@@ -1059,18 +1096,31 @@ class TestDataGenerator:
else:
shift_x = symbol.get('shift_x', 0)
shift_y = symbol.get('shift_y', 0)
closest_frame = symbol.get('closest_frame', 'UNKNOWN')
closest_frame_name = symbol.get('closest_frame_name', 'UNKNOWN')
if shift_x != 0 or shift_y != 0:
print(f"{base_msg}: "
f"Symbol liegt nicht innerhalb des Renamer-Rahmens '{closest_frame}'. "
f"Symbol liegt nicht innerhalb des Renamer-Rahmens '{closest_frame_name}'. "
f"Verschieben Sie um dx={shift_x:.1f}, dy={shift_y:.1f}")
else:
print(f"{base_msg}: "
f"Symbol liegt nicht innerhalb des Renamer-Rahmens")
# Zeichne Fehlerkreis um das Symbol
self._draw_error_circle(symbol['x'], symbol['y'], symbol['symbol_radius'])
# Berechne Symbol-Zentrum (symbol['x'], symbol['y'] = linke untere Ecke)
io_text = symbol['io']
char_count = len(io_text)
if char_count >= 7:
sym_width = multi_symbol_width
sym_height = multi_symbol_height
else:
sym_width = ma_symbol_width
sym_height = ma_symbol_height
symbol_center_x = symbol['x'] + sym_width / 2
symbol_center_y = symbol['y'] + sym_height / 2
self._draw_error_circle(symbol_center_x, symbol_center_y, symbol['symbol_radius'])
else:
print(f"\nOK: Alle {len(self.generated_symbols)} generierten Symbole erfüllen die Frame-Kriterien")
@@ -1186,6 +1236,12 @@ def main():
help=f'JSON-Konfigurationsdatei (Standard: {default_config})'
)
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()
# Bestimme Filename: entweder angegeben oder aus scene ableiten
@@ -1202,6 +1258,10 @@ def main():
generator = TestDataGenerator(filename, args.scene, config_file=args.config)
if generator.generate():
# Zeichne Symbol-Rahmen wenn gewünscht
if args.show_symbol_frames:
generator.draw_frames_for_generated_symbols()
generator.save(args.output_dir)
return 0
else: