Schalter --show_symbol_frames dazugemacht.
This commit is contained in:
@@ -0,0 +1,295 @@
|
|||||||
|
import ezdxf
|
||||||
|
from ezdxf import colors
|
||||||
|
from ezdxf.enums import TextEntityAlignment
|
||||||
|
import math
|
||||||
|
|
||||||
|
def create_grounding_symbol(filename="erdung.dxf"):
|
||||||
|
"""Erstellt ein Schutzerde-Symbol nach DIN EN 60617"""
|
||||||
|
doc = ezdxf.new('R2010')
|
||||||
|
msp = doc.modelspace()
|
||||||
|
|
||||||
|
# Block erstellen
|
||||||
|
block = doc.blocks.new(name='ERDUNG')
|
||||||
|
|
||||||
|
# Mittelpunkt (relativ zum Block)
|
||||||
|
cx, cy = 0, 0
|
||||||
|
|
||||||
|
# Vertikale Linie (Erdungsleiter)
|
||||||
|
block.add_line((cx, cy + 60), (cx, cy - 20))
|
||||||
|
|
||||||
|
# Drei horizontale Linien (Erdsymbol)
|
||||||
|
# Obere Linie (längste)
|
||||||
|
block.add_line((cx - 40, cy - 20), (cx + 40, cy - 20))
|
||||||
|
# Mittlere Linie
|
||||||
|
block.add_line((cx - 25, cy - 35), (cx + 25, cy - 35))
|
||||||
|
# Untere Linie (kürzeste)
|
||||||
|
block.add_line((cx - 15, cy - 50), (cx + 15, cy - 50))
|
||||||
|
|
||||||
|
# Beschriftung PE
|
||||||
|
block.add_text("PE", dxfattribs={
|
||||||
|
'height': 12,
|
||||||
|
'insert': (cx, cy + 70),
|
||||||
|
'halign': TextEntityAlignment.CENTER
|
||||||
|
})
|
||||||
|
|
||||||
|
# Attribute definieren
|
||||||
|
block.add_attdef(tag='REALE_POSITION', insert=(cx, cy - 70), dxfattribs={
|
||||||
|
'height': 8,
|
||||||
|
'prompt': 'Reale Position:',
|
||||||
|
'default': 'x',
|
||||||
|
'halign': TextEntityAlignment.CENTER,
|
||||||
|
'invisible': 1 # Unsichtbar, nur für Daten
|
||||||
|
})
|
||||||
|
|
||||||
|
block.add_attdef(tag='NAME', insert=(cx, cy - 85), dxfattribs={
|
||||||
|
'height': 10,
|
||||||
|
'prompt': 'Name:',
|
||||||
|
'default': '',
|
||||||
|
'halign': TextEntityAlignment.CENTER
|
||||||
|
})
|
||||||
|
|
||||||
|
block.add_attdef(tag='KENNZEICHNUNG', insert=(cx, cy - 100), dxfattribs={
|
||||||
|
'height': 8,
|
||||||
|
'prompt': 'Kennzeichnung:',
|
||||||
|
'default': '=A01+UH00-X01',
|
||||||
|
'halign': TextEntityAlignment.CENTER,
|
||||||
|
'invisible': 1 # Unsichtbar, nur für Daten
|
||||||
|
})
|
||||||
|
|
||||||
|
# Block-Instanz einfügen
|
||||||
|
msp.add_blockref('ERDUNG', (125, 125))
|
||||||
|
|
||||||
|
doc.saveas(filename)
|
||||||
|
print(f"✓ {filename} erstellt")
|
||||||
|
|
||||||
|
def create_switchboard_symbol(filename="schaltschrank.dxf"):
|
||||||
|
"""Erstellt ein schematisches Schaltschrank-Symbol für Lagepläne"""
|
||||||
|
doc = ezdxf.new('R2010')
|
||||||
|
msp = doc.modelspace()
|
||||||
|
|
||||||
|
# Block erstellen
|
||||||
|
block = doc.blocks.new(name='SCHALTSCHRANK')
|
||||||
|
|
||||||
|
# Mittelpunkt (relativ zum Block)
|
||||||
|
cx, cy = 0, 0
|
||||||
|
|
||||||
|
# Rechteck für Schaltschrank (60x80mm)
|
||||||
|
width, height = 60, 80
|
||||||
|
x1, y1 = cx - width/2, cy - height/2
|
||||||
|
x2, y2 = cx + width/2, cy + height/2
|
||||||
|
|
||||||
|
# Außenrahmen
|
||||||
|
block.add_lwpolyline([
|
||||||
|
(x1, y1), (x2, y1), (x2, y2), (x1, y2), (x1, y1)
|
||||||
|
], dxfattribs={'closed': True})
|
||||||
|
|
||||||
|
# Innere Unterteilung (Fächer)
|
||||||
|
block.add_line((x1, cy - 15), (x2, cy - 15))
|
||||||
|
block.add_line((x1, cy + 15), (x2, cy + 15))
|
||||||
|
|
||||||
|
# Türgriff-Symbol (vertikale Linie links)
|
||||||
|
block.add_line((x1 + 5, cy - 25), (x1 + 5, cy + 25))
|
||||||
|
|
||||||
|
# Beschriftung
|
||||||
|
block.add_text("Schaltschrank", dxfattribs={
|
||||||
|
'height': 10,
|
||||||
|
'insert': (cx, y2 + 15),
|
||||||
|
'halign': TextEntityAlignment.CENTER
|
||||||
|
})
|
||||||
|
|
||||||
|
# Attribute definieren
|
||||||
|
block.add_attdef(tag='REALE_POSITION', insert=(cx, y1 - 15), dxfattribs={
|
||||||
|
'height': 8,
|
||||||
|
'prompt': 'Reale Position:',
|
||||||
|
'default': 'x',
|
||||||
|
'halign': TextEntityAlignment.CENTER,
|
||||||
|
'invisible': 1
|
||||||
|
})
|
||||||
|
|
||||||
|
block.add_attdef(tag='NAME', insert=(cx, y1 - 30), dxfattribs={
|
||||||
|
'height': 10,
|
||||||
|
'prompt': 'Name:',
|
||||||
|
'default': '',
|
||||||
|
'halign': TextEntityAlignment.CENTER
|
||||||
|
})
|
||||||
|
|
||||||
|
block.add_attdef(tag='KENNZEICHNUNG', insert=(cx, y1 - 45), dxfattribs={
|
||||||
|
'height': 8,
|
||||||
|
'prompt': 'Kennzeichnung:',
|
||||||
|
'default': '=A01+UH00-X01',
|
||||||
|
'halign': TextEntityAlignment.CENTER,
|
||||||
|
'invisible': 1
|
||||||
|
})
|
||||||
|
|
||||||
|
# Block-Instanz einfügen
|
||||||
|
msp.add_blockref('SCHALTSCHRANK', (125, 125))
|
||||||
|
|
||||||
|
doc.saveas(filename)
|
||||||
|
print(f"✓ {filename} erstellt")
|
||||||
|
|
||||||
|
def create_subdistribution_symbol(filename="unterverteiler.dxf"):
|
||||||
|
"""Erstellt ein Unterverteiler-Symbol (ähnlich Schaltschrank, kleiner)"""
|
||||||
|
doc = ezdxf.new('R2010')
|
||||||
|
msp = doc.modelspace()
|
||||||
|
|
||||||
|
# Block erstellen
|
||||||
|
block = doc.blocks.new(name='UNTERVERTEILER')
|
||||||
|
|
||||||
|
# Mittelpunkt (relativ zum Block)
|
||||||
|
cx, cy = 0, 0
|
||||||
|
|
||||||
|
# Rechteck für Unterverteiler (45x60mm - kleiner als Schaltschrank)
|
||||||
|
width, height = 45, 60
|
||||||
|
x1, y1 = cx - width/2, cy - height/2
|
||||||
|
x2, y2 = cx + width/2, cy + height/2
|
||||||
|
|
||||||
|
# Außenrahmen
|
||||||
|
block.add_lwpolyline([
|
||||||
|
(x1, y1), (x2, y1), (x2, y2), (x1, y2), (x1, y1)
|
||||||
|
], dxfattribs={'closed': True})
|
||||||
|
|
||||||
|
# Innere Unterteilung (weniger Fächer)
|
||||||
|
block.add_line((x1, cy), (x2, cy))
|
||||||
|
|
||||||
|
# Türgriff-Symbol
|
||||||
|
block.add_line((x1 + 4, cy - 15), (x1 + 4, cy + 15))
|
||||||
|
|
||||||
|
# Beschriftung UV (Unterverteiler)
|
||||||
|
block.add_text("UV", dxfattribs={
|
||||||
|
'height': 12,
|
||||||
|
'insert': (cx, cy),
|
||||||
|
'halign': TextEntityAlignment.CENTER,
|
||||||
|
'valign': TextEntityAlignment.MIDDLE
|
||||||
|
})
|
||||||
|
|
||||||
|
# Zusätzliche Beschriftung
|
||||||
|
block.add_text("Unterverteiler", dxfattribs={
|
||||||
|
'height': 10,
|
||||||
|
'insert': (cx, y2 + 15),
|
||||||
|
'halign': TextEntityAlignment.CENTER
|
||||||
|
})
|
||||||
|
|
||||||
|
# Attribute definieren
|
||||||
|
block.add_attdef(tag='REALE_POSITION', insert=(cx, y1 - 15), dxfattribs={
|
||||||
|
'height': 8,
|
||||||
|
'prompt': 'Reale Position:',
|
||||||
|
'default': 'x',
|
||||||
|
'halign': TextEntityAlignment.CENTER,
|
||||||
|
'invisible': 1
|
||||||
|
})
|
||||||
|
|
||||||
|
block.add_attdef(tag='NAME', insert=(cx, y1 - 30), dxfattribs={
|
||||||
|
'height': 10,
|
||||||
|
'prompt': 'Name:',
|
||||||
|
'default': '',
|
||||||
|
'halign': TextEntityAlignment.CENTER
|
||||||
|
})
|
||||||
|
|
||||||
|
block.add_attdef(tag='KENNZEICHNUNG', insert=(cx, y1 - 45), dxfattribs={
|
||||||
|
'height': 8,
|
||||||
|
'prompt': 'Kennzeichnung:',
|
||||||
|
'default': '=A01+UH00-X01',
|
||||||
|
'halign': TextEntityAlignment.CENTER,
|
||||||
|
'invisible': 1
|
||||||
|
})
|
||||||
|
|
||||||
|
# Block-Instanz einfügen
|
||||||
|
msp.add_blockref('UNTERVERTEILER', (125, 125))
|
||||||
|
|
||||||
|
doc.saveas(filename)
|
||||||
|
print(f"✓ {filename} erstellt")
|
||||||
|
|
||||||
|
def create_motor_symbol(filename="motor.dxf"):
|
||||||
|
"""Erstellt ein allgemeines Motor-Symbol (M im Kreis) nach DIN EN 60617"""
|
||||||
|
doc = ezdxf.new('R2010')
|
||||||
|
msp = doc.modelspace()
|
||||||
|
|
||||||
|
# Block erstellen
|
||||||
|
block = doc.blocks.new(name='MOTOR')
|
||||||
|
|
||||||
|
# Mittelpunkt (relativ zum Block)
|
||||||
|
cx, cy = 0, 0
|
||||||
|
radius = 30
|
||||||
|
|
||||||
|
# Kreis
|
||||||
|
block.add_circle((cx, cy), radius)
|
||||||
|
|
||||||
|
# Buchstabe "M" in der Mitte
|
||||||
|
block.add_text("M", dxfattribs={
|
||||||
|
'height': 25,
|
||||||
|
'insert': (cx, cy),
|
||||||
|
'halign': TextEntityAlignment.CENTER,
|
||||||
|
'valign': TextEntityAlignment.MIDDLE
|
||||||
|
})
|
||||||
|
|
||||||
|
# Anschlussklemmen (drei Linien für Drehstrom - optional)
|
||||||
|
# Oben
|
||||||
|
block.add_line((cx, cy + radius), (cx, cy + radius + 20))
|
||||||
|
# Links unten
|
||||||
|
angle1 = math.radians(210)
|
||||||
|
x1 = cx + radius * math.cos(angle1)
|
||||||
|
y1 = cy + radius * math.sin(angle1)
|
||||||
|
block.add_line((x1, y1), (x1 - 15, y1 - 10))
|
||||||
|
# Rechts unten
|
||||||
|
angle2 = math.radians(330)
|
||||||
|
x2 = cx + radius * math.cos(angle2)
|
||||||
|
y2 = cy + radius * math.sin(angle2)
|
||||||
|
block.add_line((x2, y2), (x2 + 15, y2 - 10))
|
||||||
|
|
||||||
|
# Beschriftung
|
||||||
|
block.add_text("Motor", dxfattribs={
|
||||||
|
'height': 10,
|
||||||
|
'insert': (cx, cy - radius - 25),
|
||||||
|
'halign': TextEntityAlignment.CENTER
|
||||||
|
})
|
||||||
|
|
||||||
|
# Attribute definieren
|
||||||
|
block.add_attdef(tag='REALE_POSITION', insert=(cx, cy - radius - 40), dxfattribs={
|
||||||
|
'height': 8,
|
||||||
|
'prompt': 'Reale Position:',
|
||||||
|
'default': 'x',
|
||||||
|
'halign': TextEntityAlignment.CENTER,
|
||||||
|
'invisible': 1
|
||||||
|
})
|
||||||
|
|
||||||
|
block.add_attdef(tag='NAME', insert=(cx, cy - radius - 55), dxfattribs={
|
||||||
|
'height': 10,
|
||||||
|
'prompt': 'Name:',
|
||||||
|
'default': '',
|
||||||
|
'halign': TextEntityAlignment.CENTER
|
||||||
|
})
|
||||||
|
|
||||||
|
block.add_attdef(tag='KENNZEICHNUNG', insert=(cx, cy - radius - 70), dxfattribs={
|
||||||
|
'height': 8,
|
||||||
|
'prompt': 'Kennzeichnung:',
|
||||||
|
'default': '=A01+UH00-X01',
|
||||||
|
'halign': TextEntityAlignment.CENTER,
|
||||||
|
'invisible': 1
|
||||||
|
})
|
||||||
|
|
||||||
|
# Block-Instanz einfügen
|
||||||
|
msp.add_blockref('MOTOR', (125, 125))
|
||||||
|
|
||||||
|
doc.saveas(filename)
|
||||||
|
print(f"✓ {filename} erstellt")
|
||||||
|
|
||||||
|
# Hauptprogramm
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("Erstelle DXF Elektrosymbole nach DIN EN 60617 mit Attributen...\n")
|
||||||
|
|
||||||
|
create_grounding_symbol()
|
||||||
|
create_switchboard_symbol()
|
||||||
|
create_subdistribution_symbol()
|
||||||
|
create_motor_symbol()
|
||||||
|
|
||||||
|
print("\n✓ Alle Symbole wurden erfolgreich erstellt!")
|
||||||
|
print("\nDateien:")
|
||||||
|
print(" - erdung.dxf")
|
||||||
|
print(" - schaltschrank.dxf")
|
||||||
|
print(" - unterverteiler.dxf")
|
||||||
|
print(" - motor.dxf")
|
||||||
|
print("\nJedes Symbol ist als Block mit folgenden Attributen definiert:")
|
||||||
|
print(" - REALE_POSITION (Standard: 'x', unsichtbar)")
|
||||||
|
print(" - NAME (Standard: leer, sichtbar unter dem Symbol)")
|
||||||
|
print(" - KENNZEICHNUNG (Standard: '=A01+UH00-X01', unsichtbar)")
|
||||||
|
print("\nDie Attribute können in CAD-Programmen bearbeitet werden.")
|
||||||
+89
-29
@@ -14,9 +14,11 @@ import json
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import ezdxf
|
import ezdxf
|
||||||
|
|
||||||
|
from symbol_frames import draw_symbol_frames
|
||||||
|
|
||||||
# Umgebungsvariablen
|
# Umgebungsvariablen
|
||||||
PROJECT = os.getenv('PROJECT', Path(__file__).parent.parent.absolute())
|
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'))
|
PROJECT_CFG = os.getenv('PROJECT_CFG', os.path.join(PROJECT, 'cfg'))
|
||||||
|
|
||||||
|
|
||||||
@@ -284,13 +286,38 @@ class TestDataGenerator:
|
|||||||
def save(self, output_dir=None):
|
def save(self, output_dir=None):
|
||||||
"""Speichert das DXF-Dokument"""
|
"""Speichert das DXF-Dokument"""
|
||||||
if output_dir is None:
|
if output_dir is None:
|
||||||
output_dir = PROJECT_TEST
|
output_dir = PROJECT_WORK
|
||||||
|
|
||||||
output_path = os.path.join(output_dir, self.filename)
|
output_path = os.path.join(output_dir, self.filename)
|
||||||
self.doc.saveas(output_path)
|
self.doc.saveas(output_path)
|
||||||
print(f"Test-DXF erstellt: {output_path}")
|
print(f"Test-DXF erstellt: {output_path}")
|
||||||
return 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):
|
def load_block_from_file(self, source_file, block_name):
|
||||||
"""
|
"""
|
||||||
Lädt einen Block aus einer anderen DXF-Datei
|
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
|
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
|
Generiert eine Gruppe von MA-Symbolen
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
group: Dict mit Gruppen-Config (name, count, layout_type, base_x, base_y, spacing)
|
group: Dict mit Gruppen-Config (name, count, layout_type, base_x, base_y, spacing)
|
||||||
ma_defaults: Dict mit MA-Default-Attributen
|
ma_defaults: Dict mit MA-Default-Attributen
|
||||||
width_per_char: Breite pro Zeichen
|
symbol_width: Feste Breite des Symbols
|
||||||
fixed_height: Fixe Symbol-Höhe
|
symbol_height: Feste Höhe des Symbols
|
||||||
spacing_factor: Spacing-Faktor zwischen Symbolen
|
spacing_factor: Spacing-Faktor zwischen Symbolen
|
||||||
y_offsets_list: Liste von Y-Offsets für horizontal_offset Layout
|
y_offsets_list: Liste von Y-Offsets für horizontal_offset Layout
|
||||||
insert_point: Einfügepunkt des Blocks (z.B. "left_top_corner", "center")
|
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@@")
|
# Extrahiere IO-Pattern aus Name (z.B. "MA-1@@_top" -> "MA-1@@")
|
||||||
io_pattern = name.split('_')[0]
|
io_pattern = name.split('_')[0]
|
||||||
|
|
||||||
# Berechne Symbol-Ausdehnung
|
|
||||||
symbol_width = len(io_pattern) * width_per_char
|
|
||||||
|
|
||||||
# Berechne Spacing
|
# Berechne Spacing
|
||||||
if layout_type in ['horizontal', 'horizontal_offset']:
|
if layout_type in ['horizontal', 'horizontal_offset']:
|
||||||
actual_spacing = symbol_width * spacing_factor
|
actual_spacing = symbol_width * spacing_factor
|
||||||
@@ -644,7 +668,7 @@ class TestDataGenerator:
|
|||||||
layer = ma_defaults.get('layer', 'ILS_MOTOR')
|
layer = ma_defaults.get('layer', 'ILS_MOTOR')
|
||||||
|
|
||||||
# Berechne Offset basierend auf Einfügepunkt
|
# 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
|
# Generiere Symbole basierend auf Layout-Typ
|
||||||
for i in range(count):
|
for i in range(count):
|
||||||
@@ -669,7 +693,7 @@ class TestDataGenerator:
|
|||||||
# (da der io-Block bei (0,0) startet und nach oben geht)
|
# (da der io-Block bei (0,0) startet und nach oben geht)
|
||||||
blockref = self.msp.add_blockref(
|
blockref = self.msp.add_blockref(
|
||||||
'io',
|
'io',
|
||||||
insert=(left_top_x, left_top_y - fixed_height),
|
insert=(left_top_x, left_top_y - symbol_height),
|
||||||
dxfattribs={'layer': layer}
|
dxfattribs={'layer': layer}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -681,10 +705,12 @@ class TestDataGenerator:
|
|||||||
blockref.add_auto_attribs(attrib_values)
|
blockref.add_auto_attribs(attrib_values)
|
||||||
|
|
||||||
# Speichere Symbol-Position für spätere Validierung
|
# 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({
|
self.generated_symbols.append({
|
||||||
'x': left_top_x,
|
'x': actual_insert_x,
|
||||||
'y': left_top_y,
|
'y': actual_insert_y,
|
||||||
'io': io_pattern,
|
'io': io_pattern,
|
||||||
'group_name': name,
|
'group_name': name,
|
||||||
'layer': layer
|
'layer': layer
|
||||||
@@ -701,10 +727,10 @@ class TestDataGenerator:
|
|||||||
ma_defaults = self.config.get('ma_defaults', {})
|
ma_defaults = self.config.get('ma_defaults', {})
|
||||||
general = self.config.get('general', {})
|
general = self.config.get('general', {})
|
||||||
|
|
||||||
# Hole Dimensions aus Config
|
# Hole Dimensions aus Config (feste Werte)
|
||||||
dimensions = ma_defaults.get('dimensions', {})
|
dimensions = ma_defaults.get('dimensions', {})
|
||||||
width_per_char = dimensions.get('width_per_char', 201.49)
|
symbol_width = dimensions.get('width', 1210)
|
||||||
fixed_height = dimensions.get('fixed_height', 380.94)
|
symbol_height = dimensions.get('height', 381)
|
||||||
|
|
||||||
# Hole Layout-Einstellungen
|
# Hole Layout-Einstellungen
|
||||||
layout = general.get('layout', {})
|
layout = general.get('layout', {})
|
||||||
@@ -717,7 +743,7 @@ class TestDataGenerator:
|
|||||||
|
|
||||||
# Generiere MA-Gruppen aus Config
|
# Generiere MA-Gruppen aus Config
|
||||||
for group in scene.get('ma_groups', []):
|
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
|
# Generiere Renamer-Rahmen aus Config
|
||||||
for frame_config in scene.get('renaming_frames', []):
|
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("Prüfe ob alle generierten Symbole in Renamer-Frames liegen")
|
||||||
print("="*60)
|
print("="*60)
|
||||||
|
|
||||||
# Hole Symbol-Dimensionen aus Config
|
# Hole Symbol-Dimensionen aus Config (feste Werte)
|
||||||
dimensions = self.config.get('ma_defaults', {}).get('dimensions', {})
|
dimensions = self.config.get('ma_defaults', {}).get('dimensions', {})
|
||||||
width_per_char = dimensions.get('width_per_char', 201.49)
|
ma_symbol_width = dimensions.get('width', 1210)
|
||||||
fixed_height = dimensions.get('fixed_height', 380.94)
|
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
|
# Sammle alle Frame-Boundaries
|
||||||
frame_boundaries = []
|
frame_boundaries = []
|
||||||
@@ -969,15 +1000,20 @@ class TestDataGenerator:
|
|||||||
# Prüfe jedes generierte Symbol
|
# Prüfe jedes generierte Symbol
|
||||||
symbols_outside = []
|
symbols_outside = []
|
||||||
for symbol in self.generated_symbols:
|
for symbol in self.generated_symbols:
|
||||||
# Berechne Symbol-Dimensionen
|
# Berechne Symbol-Dimensionen basierend auf IO-Text Länge
|
||||||
io_text = symbol['io']
|
io_text = symbol['io']
|
||||||
symbol_width = len(io_text) * width_per_char
|
char_count = len(io_text)
|
||||||
symbol_height = fixed_height
|
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
|
# 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_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)
|
symbol_point = (symbol_center_x, symbol_center_y)
|
||||||
|
|
||||||
# Radius für Kreis-Test
|
# Radius für Kreis-Test
|
||||||
@@ -1041,7 +1077,8 @@ class TestDataGenerator:
|
|||||||
symbol_with_type['symbol_radius'] = symbol_radius
|
symbol_with_type['symbol_radius'] = symbol_radius
|
||||||
symbol_with_type['shift_x'] = best_shift[0]
|
symbol_with_type['shift_x'] = best_shift[0]
|
||||||
symbol_with_type['shift_y'] = best_shift[1]
|
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)
|
symbols_outside.append(symbol_with_type)
|
||||||
|
|
||||||
# Gib Warnungen aus und zeichne Fehlerkreise
|
# Gib Warnungen aus und zeichne Fehlerkreise
|
||||||
@@ -1059,18 +1096,31 @@ class TestDataGenerator:
|
|||||||
else:
|
else:
|
||||||
shift_x = symbol.get('shift_x', 0)
|
shift_x = symbol.get('shift_x', 0)
|
||||||
shift_y = symbol.get('shift_y', 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:
|
if shift_x != 0 or shift_y != 0:
|
||||||
print(f"{base_msg}: "
|
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}")
|
f"Verschieben Sie um dx={shift_x:.1f}, dy={shift_y:.1f}")
|
||||||
else:
|
else:
|
||||||
print(f"{base_msg}: "
|
print(f"{base_msg}: "
|
||||||
f"Symbol liegt nicht innerhalb des Renamer-Rahmens")
|
f"Symbol liegt nicht innerhalb des Renamer-Rahmens")
|
||||||
|
|
||||||
# Zeichne Fehlerkreis um das Symbol
|
# 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:
|
else:
|
||||||
print(f"\nOK: Alle {len(self.generated_symbols)} generierten Symbole erfüllen die Frame-Kriterien")
|
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})'
|
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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# Bestimme Filename: entweder angegeben oder aus scene ableiten
|
# Bestimme Filename: entweder angegeben oder aus scene ableiten
|
||||||
@@ -1202,6 +1258,10 @@ def main():
|
|||||||
generator = TestDataGenerator(filename, args.scene, config_file=args.config)
|
generator = TestDataGenerator(filename, args.scene, config_file=args.config)
|
||||||
|
|
||||||
if generator.generate():
|
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)
|
generator.save(args.output_dir)
|
||||||
return 0
|
return 0
|
||||||
else:
|
else:
|
||||||
|
|||||||
+58
-5
@@ -4,6 +4,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
from error_collector import ErrorCollector, write_json_file
|
from error_collector import ErrorCollector, write_json_file
|
||||||
from utils import check_environment_var, check_file_in_work, dxf_is_binary, get_dxf_file
|
from utils import check_environment_var, check_file_in_work, dxf_is_binary, get_dxf_file
|
||||||
|
from symbol_frames import draw_symbol_frames
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -491,6 +492,7 @@ def collect_and_group_renamer_blocks(doc, msp, renamer_layers, error_collector):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
print(f" Boundary gefunden mit {len(boundary)} Punkten (Rechteck: {is_rectangle})")
|
print(f" Boundary gefunden mit {len(boundary)} Punkten (Rechteck: {is_rectangle})")
|
||||||
|
print(f" Eckpunkte: {', '.join([f'({p[0]:.2f}, {p[1]:.2f})' for p in boundary])}")
|
||||||
|
|
||||||
# Validiere DIRECTION gegen Geometrie
|
# Validiere DIRECTION gegen Geometrie
|
||||||
direction = attributes.get("DIRECTION", "LEFT_RIGHT")
|
direction = attributes.get("DIRECTION", "LEFT_RIGHT")
|
||||||
@@ -679,17 +681,59 @@ def process_renamer_blocks(doc, msp, renamer_layers, error_collector):
|
|||||||
Verarbeitet alle Renamer-Blöcke auf den angegebenen Layern.
|
Verarbeitet alle Renamer-Blöcke auf den angegebenen Layern.
|
||||||
Gruppiert Blöcke mit identischer Konfiguration und nummeriert alle Symbole
|
Gruppiert Blöcke mit identischer Konfiguration und nummeriert alle Symbole
|
||||||
aus allen Blöcken einer Gruppe gemeinsam.
|
aus allen Blöcken einer Gruppe gemeinsam.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tuple (all_renamed, renamer_groups):
|
||||||
|
- all_renamed: Liste der umbenannten Symbole
|
||||||
|
- renamer_groups: Dictionary der gruppierten Renamer-Blöcke
|
||||||
"""
|
"""
|
||||||
# Erster Pass: Sammle und gruppiere alle Renamer-Blöcke
|
# Erster Pass: Sammle und gruppiere alle Renamer-Blöcke
|
||||||
renamer_groups = collect_and_group_renamer_blocks(doc, msp, renamer_layers, error_collector)
|
renamer_groups = collect_and_group_renamer_blocks(doc, msp, renamer_layers, error_collector)
|
||||||
|
|
||||||
# Zweiter Pass: Verarbeite jede Gruppe
|
# Zweiter Pass: Verarbeite jede Gruppe
|
||||||
all_renamed = process_renamer_groups(doc, msp, renamer_layers, renamer_groups, error_collector)
|
all_renamed = process_renamer_groups(doc, msp, renamer_layers, renamer_groups, error_collector)
|
||||||
|
|
||||||
# Dritter Pass: Prüfe ob alle Symbole mit "@" im IO in einem Renamer-Bereich liegen
|
# Dritter Pass: Prüfe ob alle Symbole mit "@" im IO in einem Renamer-Bereich liegen
|
||||||
check_symbols_in_boundaries(doc, msp, renamer_groups, error_collector)
|
check_symbols_in_boundaries(doc, msp, renamer_groups, error_collector)
|
||||||
|
|
||||||
return all_renamed
|
return all_renamed, renamer_groups
|
||||||
|
|
||||||
|
|
||||||
|
def collect_all_symbols_from_groups(doc, msp, renamer_groups, renamer_layers, error_collector):
|
||||||
|
"""
|
||||||
|
Sammelt alle Symbole aus allen Renamer-Gruppen für Visualisierungszwecke.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
doc: DXF-Dokument
|
||||||
|
msp: Modelspace
|
||||||
|
renamer_groups: Dictionary mit gruppierten Renamer-Blöcken
|
||||||
|
renamer_layers: Liste der Layer mit Renamer-Blöcken
|
||||||
|
error_collector: ErrorCollector für Fehlerbehandlung
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Liste aller gefundenen Symbole mit 'entity', 'position', 'attributes', 'layer'
|
||||||
|
"""
|
||||||
|
all_symbols = []
|
||||||
|
seen_entities = set() # Verhindere Duplikate
|
||||||
|
|
||||||
|
for config_key, group_blocks in renamer_groups.items():
|
||||||
|
if not group_blocks:
|
||||||
|
continue
|
||||||
|
|
||||||
|
reference_attributes = group_blocks[0][2]
|
||||||
|
|
||||||
|
for insert, boundary, attributes, is_rectangle in group_blocks:
|
||||||
|
# Finde Symbole innerhalb dieses Bereichs
|
||||||
|
symbols = find_symbols_in_boundary(doc, msp, boundary, renamer_layers, attributes, error_collector)
|
||||||
|
|
||||||
|
# Füge Symbole hinzu, aber nur wenn das Entity noch nicht gesehen wurde
|
||||||
|
for symbol in symbols:
|
||||||
|
entity_id = id(symbol['entity'])
|
||||||
|
if entity_id not in seen_entities:
|
||||||
|
seen_entities.add(entity_id)
|
||||||
|
all_symbols.append(symbol)
|
||||||
|
|
||||||
|
return all_symbols
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
@@ -726,6 +770,7 @@ if __name__ == '__main__':
|
|||||||
parser.add_argument('-e', '--errorfile', action='store', required=False, help='JSON-Datei für Fehler und Warnungen', metavar='errors.json')
|
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('-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')
|
parser.add_argument('-d', '--dryrun', action='store_true', help='Symbole nicht in der DXF-Datei überschreiben, nur Ausgabe auf Konsole')
|
||||||
|
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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -770,7 +815,7 @@ if __name__ == '__main__':
|
|||||||
print("Starte Verarbeitung der Renamer-Blöcke")
|
print("Starte Verarbeitung der Renamer-Blöcke")
|
||||||
print("="*60 + "\n")
|
print("="*60 + "\n")
|
||||||
|
|
||||||
renamed_symbols = process_renamer_blocks(doc, msp, renamer_layers, error_collector)
|
renamed_symbols, renamer_groups = process_renamer_blocks(doc, msp, renamer_layers, error_collector)
|
||||||
|
|
||||||
print("\n" + "="*60)
|
print("\n" + "="*60)
|
||||||
print(f"Verarbeitung abgeschlossen: {len(renamed_symbols)} Symbole nummeriert")
|
print(f"Verarbeitung abgeschlossen: {len(renamed_symbols)} Symbole nummeriert")
|
||||||
@@ -782,6 +827,14 @@ if __name__ == '__main__':
|
|||||||
for item in renamed_symbols:
|
for item in renamed_symbols:
|
||||||
print(f" {item['old_value']} -> {item['new_value']} (Layer: {item['layer']}, Pos: {item['position']})")
|
print(f" {item['old_value']} -> {item['new_value']} (Layer: {item['layer']}, Pos: {item['position']})")
|
||||||
|
|
||||||
|
# Zeichne Symbol-Rahmen wenn gewünscht
|
||||||
|
if args.show_symbol_frames:
|
||||||
|
all_symbols = collect_all_symbols_from_groups(doc, msp, renamer_groups, renamer_layers, error_collector)
|
||||||
|
if all_symbols:
|
||||||
|
draw_symbol_frames(doc, msp, all_symbols)
|
||||||
|
else:
|
||||||
|
print("\nKeine Symbole für Rahmen-Zeichnung gefunden")
|
||||||
|
|
||||||
# Speichere DXF-Datei wenn nicht dry-run
|
# Speichere DXF-Datei wenn nicht dry-run
|
||||||
if not args.dryrun:
|
if not args.dryrun:
|
||||||
output_path = dxf_path.parent / f"{dxf_path.stem}_numbered{dxf_path.suffix}"
|
output_path = dxf_path.parent / f"{dxf_path.stem}_numbered{dxf_path.suffix}"
|
||||||
|
|||||||
Reference in New Issue
Block a user