mehrere oft verwendete Routinen in eigene utils.py eingebaut
This commit is contained in:
Vendored
+34
-6
@@ -123,35 +123,35 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getpositions with ST500592_Omniflo.dxf",
|
"name": "getpositions with PR500592_10_5.13_POT_20251216.dxf",
|
||||||
"type": "debugpy",
|
"type": "debugpy",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${file}",
|
"program": "${file}",
|
||||||
"console": "integratedTerminal",
|
"console": "integratedTerminal",
|
||||||
"args": [
|
"args": [
|
||||||
"--filename",
|
"--filename",
|
||||||
"ST500592_Omniflo.dxf",
|
"PR500592_10_5.13_POT_20251216.dxf",
|
||||||
"--sensors",
|
"--sensors",
|
||||||
"--rack",
|
"--rack",
|
||||||
"--console",
|
"--console",
|
||||||
"--write",
|
"--write",
|
||||||
"ST500592_Omniflo_positions.json"
|
"PR500592_10_5.13_POT_20251216_positions.json"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getpositions with ST500592_10_5-11_ILS.dxf",
|
"name": "getpositions with POT_ST1_ST6_12.dxf",
|
||||||
"type": "debugpy",
|
"type": "debugpy",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${file}",
|
"program": "${file}",
|
||||||
"console": "integratedTerminal",
|
"console": "integratedTerminal",
|
||||||
"args": [
|
"args": [
|
||||||
"--filename",
|
"--filename",
|
||||||
"ST500592_10_5-11_ILS.dxf",
|
"POT_ST1_ST6_12.dxf",
|
||||||
"--sensors",
|
"--sensors",
|
||||||
"--rack",
|
"--rack",
|
||||||
"--console",
|
"--console",
|
||||||
"--write",
|
"--write",
|
||||||
"ST500592_10_5-11_ILS_positions.json"
|
"POT_ST1_ST6_12_positions.json"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -289,6 +289,21 @@
|
|||||||
"ST-Fortna_ASCII_cables.dxf"
|
"ST-Fortna_ASCII_cables.dxf"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "draw cables for test.json",
|
||||||
|
"type": "debugpy",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${file}",
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"args": [
|
||||||
|
"--filename",
|
||||||
|
"test_todraw.json",
|
||||||
|
"-n",
|
||||||
|
"test_cables.dxf",
|
||||||
|
"-x",
|
||||||
|
"test_cables.xlsx"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "routing for easy_positions.json",
|
"name": "routing for easy_positions.json",
|
||||||
"type": "debugpy",
|
"type": "debugpy",
|
||||||
@@ -316,6 +331,19 @@
|
|||||||
"-g"
|
"-g"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "routing for test_positionsdraw.json",
|
||||||
|
"type": "debugpy",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${file}",
|
||||||
|
"console": "integratedTerminal",
|
||||||
|
"args": [
|
||||||
|
"--filename",
|
||||||
|
"test_positionsdraw.json",
|
||||||
|
"-w",
|
||||||
|
"test_draw.json",
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "run_tests.py with easy_3tunnels.dxf",
|
"name": "run_tests.py with easy_3tunnels.dxf",
|
||||||
"type": "debugpy",
|
"type": "debugpy",
|
||||||
|
|||||||
+13
-27
@@ -12,12 +12,12 @@ REM echo Dateiname ohne Erweiterung: %FILENAME%
|
|||||||
REM echo Erweiterung: %EXT%
|
REM echo Erweiterung: %EXT%
|
||||||
REM echo Verzeichnis: %DIR%
|
REM echo Verzeichnis: %DIR%
|
||||||
REM
|
REM
|
||||||
REM Namen der Zwischenergebnis Dateien
|
REM Namen der auf dem RENAMER Layer gefundenen Symbole, die überschrieben werden sollen
|
||||||
set JSON_POS=%FILENAME%_positionsconv.json
|
set JSON_SYMBOLS=%FILENAME%_symbols.json
|
||||||
set JSON_TODRAW=%FILENAME%_todraw.json
|
|
||||||
REM Namen der Ergebnisdateien
|
REM Fehlerdatei
|
||||||
set ERROR_DOUBLE=%FILENAME%_errors.json
|
set ERROR_FILE=%FILENAME%_errors.json
|
||||||
set RESULT_TIA=%FILENAME%-*_TIA.xlsx
|
|
||||||
|
|
||||||
if exist "%~dp0_setenv.bat" (
|
if exist "%~dp0_setenv.bat" (
|
||||||
echo Lade lokale Umgebungseinstellungen aus _setenv.bat...
|
echo Lade lokale Umgebungseinstellungen aus _setenv.bat...
|
||||||
@@ -27,31 +27,17 @@ REM Zielverzeichnis
|
|||||||
set TARGET_DIR=%PROJECT_IO_RESULTS%\%FILENAME%
|
set TARGET_DIR=%PROJECT_IO_RESULTS%\%FILENAME%
|
||||||
mkdir "%TARGET_DIR%"
|
mkdir "%TARGET_DIR%"
|
||||||
REM lösche alte Fehlermeldungen
|
REM lösche alte Fehlermeldungen
|
||||||
del "%PROJECT_WORK%\%ERROR_DOUBLE%"
|
del "%PROJECT_WORK%\%ERROR_FILE%"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
echo.
|
echo === Creating enriched dxf file ===
|
||||||
echo === Fetching Positions ===
|
call create_numbers.bat --filename %1 --errorfile %PROJECT_WORK%\%ERROR_FILE% --write %PROJECT_WORK%\%JSON_SYMBOLS%
|
||||||
call getpositions.bat --filename %1 -s -r -w %JSON_POS% -e %ERROR_DOUBLE%
|
if exist "%PROJECT_WORK%\%ERROR_FILE%" (
|
||||||
if exist "%PROJECT_WORK%\%ERROR_DOUBLE%" (
|
@echo -failed- errors found during processing!
|
||||||
@echo == failed: errors, e.g. duplicate IDs in given layout
|
|
||||||
pause
|
|
||||||
move %PROJECT_WORK%\%ERROR_DOUBLE% %TARGET_DIR%
|
|
||||||
move %PROJECT_WORK%\%JSON_TODRAW% %TARGET_DIR%
|
|
||||||
|
|
||||||
goto :eof
|
|
||||||
)
|
|
||||||
if not exist "%PROJECT_WORK%\%JSON_POS%" (
|
|
||||||
@echo == failed: getpositions
|
|
||||||
pause
|
|
||||||
goto :eof
|
|
||||||
)
|
|
||||||
echo === Creating Excel Files for TIA, WSCAD, .. ===
|
|
||||||
call portalexport.bat --filename %JSON_POS% --outname %FILENAME%
|
|
||||||
if not exist "%PROJECT_WORK%\%RESULT_TIA%" (
|
|
||||||
@echo == failed: creating .xlsx files
|
|
||||||
pause
|
pause
|
||||||
|
move %PROJECT_WORK%\%ERROR_FILE% %TARGET_DIR%
|
||||||
|
move %PROJECT_WORK%\%JSON_SYMBOLS% %TARGET_DIR%
|
||||||
goto :eof
|
goto :eof
|
||||||
)
|
)
|
||||||
echo move %PROJECT_WORK%\%FILENAME%-* %TARGET_DIR%
|
echo move %PROJECT_WORK%\%FILENAME%-* %TARGET_DIR%
|
||||||
|
|||||||
+388
-31
@@ -8,10 +8,10 @@ from pathlib import Path
|
|||||||
|
|
||||||
import ezdxf
|
import ezdxf
|
||||||
from ezdxf.addons import iterdxf
|
from ezdxf.addons import iterdxf
|
||||||
from shapely.geometry import Point
|
|
||||||
from ezdxf.lldxf.const import DXFStructureError
|
from ezdxf.lldxf.const import DXFStructureError
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -56,56 +56,413 @@ Alle Sensoren, Motoren, Aktoren etc. innerhalb der zugehörigen Polylinie nehmen
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def check_file_in_work(work_dir: Path, filename: Path) -> tuple[Path, bool]:
|
def read_config_layers(config_path: Path) -> list:
|
||||||
fexists = True
|
"""
|
||||||
if not filename.exists():
|
Liest die enumerate.cfg und gibt die Layer zurück, auf denen nach Renamer-Blöcken gesucht werden soll.
|
||||||
mypath = work_dir.joinpath(filename)
|
Die Konfiguration hat eine einfache Liste unter [Layers]:
|
||||||
ex = mypath.exists()
|
[Layers]
|
||||||
if not mypath.exists():
|
RENAMER
|
||||||
fexists = False
|
OTHER_LAYER
|
||||||
else:
|
"""
|
||||||
mypath = filename
|
layers = []
|
||||||
return mypath, fexists
|
|
||||||
|
|
||||||
def check_environment_var(env_str: str) -> Path:
|
with open(config_path, 'r', encoding='utf-8') as f:
|
||||||
out_path = os.environ.get(env_str)
|
in_layers_section = False
|
||||||
if out_path:
|
for line in f:
|
||||||
return Path(out_path)
|
line = line.strip()
|
||||||
|
|
||||||
|
# Überspringe leere Zeilen und Kommentare
|
||||||
|
if not line or line.startswith('#') or line.startswith(';'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Prüfe ob wir in der [Layers] Sektion sind
|
||||||
|
if line.startswith('['):
|
||||||
|
in_layers_section = line.lower() == '[layers]'
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Wenn in [Layers], füge Layer hinzu
|
||||||
|
if in_layers_section:
|
||||||
|
layers.append(line)
|
||||||
|
|
||||||
|
return layers
|
||||||
|
|
||||||
|
|
||||||
|
def extract_block_attributes(insert) -> dict:
|
||||||
|
"""
|
||||||
|
Extrahiert alle Attribute aus einem INSERT-Block.
|
||||||
|
"""
|
||||||
|
attributes = {}
|
||||||
|
if insert.dxftype() != 'INSERT':
|
||||||
|
return attributes
|
||||||
|
|
||||||
|
if insert.has_attrib:
|
||||||
|
for attrib in insert.attribs:
|
||||||
|
tag = attrib.dxf.tag
|
||||||
|
value = attrib.dxf.text
|
||||||
|
attributes[tag] = value
|
||||||
|
|
||||||
|
return attributes
|
||||||
|
|
||||||
|
|
||||||
|
def get_boundary_geometry(doc, insert):
|
||||||
|
"""
|
||||||
|
Sucht im Block nach einem Rechteck oder einer geschlossenen Polylinie.
|
||||||
|
Gibt die Eckpunkte zurück.
|
||||||
|
"""
|
||||||
|
block_layout = doc.blocks.get(insert.dxf.name)
|
||||||
|
|
||||||
|
# Suche nach LWPOLYLINE, POLYLINE oder geschlossenen Linien
|
||||||
|
for entity in block_layout:
|
||||||
|
if entity.dxftype() == 'LWPOLYLINE':
|
||||||
|
# Get points and transform them
|
||||||
|
points = list(entity.get_points())
|
||||||
|
|
||||||
|
# Prüfe ob geschlossen (entweder Flag gesetzt oder erster == letzter Punkt)
|
||||||
|
is_closed = entity.closed or (entity.dxf.flags & 1)
|
||||||
|
if not is_closed and len(points) > 1:
|
||||||
|
# Prüfe ob erster und letzter Punkt gleich sind
|
||||||
|
first = points[0][:2]
|
||||||
|
last = points[-1][:2]
|
||||||
|
if abs(first[0] - last[0]) < 0.001 and abs(first[1] - last[1]) < 0.001:
|
||||||
|
is_closed = True
|
||||||
|
|
||||||
|
if is_closed:
|
||||||
|
# Transform points relative to insert position
|
||||||
|
insert_point = insert.dxf.insert
|
||||||
|
transformed_points = []
|
||||||
|
for p in points:
|
||||||
|
# Einfache Translation (ohne Rotation/Skalierung für ersten Ansatz)
|
||||||
|
transformed_points.append((
|
||||||
|
insert_point[0] + p[0],
|
||||||
|
insert_point[1] + p[1]
|
||||||
|
))
|
||||||
|
return transformed_points
|
||||||
|
|
||||||
|
elif entity.dxftype() == 'POLYLINE':
|
||||||
|
if entity.is_closed:
|
||||||
|
points = [(v.dxf.location.x, v.dxf.location.y) for v in entity.vertices]
|
||||||
|
insert_point = insert.dxf.insert
|
||||||
|
transformed_points = []
|
||||||
|
for p in points:
|
||||||
|
transformed_points.append((
|
||||||
|
insert_point[0] + p[0],
|
||||||
|
insert_point[1] + p[1]
|
||||||
|
))
|
||||||
|
return transformed_points
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def point_in_polygon(point, polygon):
|
||||||
|
"""
|
||||||
|
Prüft, ob ein Punkt innerhalb eines Polygons liegt (Ray-casting Algorithmus).
|
||||||
|
"""
|
||||||
|
x, y = point
|
||||||
|
n = len(polygon)
|
||||||
|
inside = False
|
||||||
|
|
||||||
|
p1x, p1y = polygon[0]
|
||||||
|
for i in range(1, n + 1):
|
||||||
|
p2x, p2y = polygon[i % n]
|
||||||
|
if y > min(p1y, p2y):
|
||||||
|
if y <= max(p1y, p2y):
|
||||||
|
if x <= max(p1x, p2x):
|
||||||
|
if p1y != p2y:
|
||||||
|
xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
|
||||||
|
if p1x == p2x or x <= xinters:
|
||||||
|
inside = not inside
|
||||||
|
p1x, p1y = p2x, p2y
|
||||||
|
|
||||||
|
return inside
|
||||||
|
|
||||||
|
|
||||||
|
def find_symbols_in_boundary(doc, msp, boundary, target_layers, attributes):
|
||||||
|
"""
|
||||||
|
Findet alle Symbole (INSERT-Blöcke) innerhalb des angegebenen Bereichs auf den Ziel-Layern.
|
||||||
|
"""
|
||||||
|
symbols = []
|
||||||
|
|
||||||
|
# Bestimme welche Layer durchsucht werden sollen
|
||||||
|
search_layers = []
|
||||||
|
for i in range(1, 4): # NAME1, NAME2, NAME3
|
||||||
|
layer_key = f"LAYER_NAME{i}"
|
||||||
|
if layer_key in attributes and attributes[layer_key]:
|
||||||
|
search_layers.append(attributes[layer_key])
|
||||||
|
|
||||||
|
# Falls nur NAME/LAYER_NAME vorhanden
|
||||||
|
if "LAYER_NAME" in attributes and attributes["LAYER_NAME"]:
|
||||||
|
search_layers.append(attributes["LAYER_NAME"])
|
||||||
|
|
||||||
|
# Durchsuche alle INSERT-Blöcke
|
||||||
|
for entity in msp.query('INSERT'):
|
||||||
|
if entity.dxf.layer not in search_layers:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Prüfe ob der Block Attribute hat
|
||||||
|
if not entity.has_attrib:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Hole Position des Symbols
|
||||||
|
pos = entity.dxf.insert
|
||||||
|
point = (pos[0], pos[1])
|
||||||
|
|
||||||
|
# Prüfe ob innerhalb des Bereichs
|
||||||
|
if point_in_polygon(point, boundary):
|
||||||
|
# Extrahiere Attribute des Symbols
|
||||||
|
symbol_attribs = extract_block_attributes(entity)
|
||||||
|
|
||||||
|
# Prüfe ob es ein Template ist (enthält @)
|
||||||
|
has_template = False
|
||||||
|
for value in symbol_attribs.values():
|
||||||
|
if '@' in str(value):
|
||||||
|
has_template = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if has_template:
|
||||||
|
symbols.append({
|
||||||
|
'entity': entity,
|
||||||
|
'position': point,
|
||||||
|
'attributes': symbol_attribs,
|
||||||
|
'layer': entity.dxf.layer
|
||||||
|
})
|
||||||
|
|
||||||
|
return symbols
|
||||||
|
|
||||||
|
|
||||||
|
def sort_symbols_by_direction(symbols, direction):
|
||||||
|
"""
|
||||||
|
Sortiert die Symbole nach der angegebenen Richtung.
|
||||||
|
TOP_BOTTOM: nach Y absteigend, dann X
|
||||||
|
BOTTOM_TOP: nach Y aufsteigend, dann X
|
||||||
|
LEFT_RIGHT: nach X aufsteigend, dann Y
|
||||||
|
RIGHT_LEFT: nach X absteigend, dann Y
|
||||||
|
"""
|
||||||
|
if direction == "TOP_BOTTOM":
|
||||||
|
return sorted(symbols, key=lambda s: (-s['position'][1], s['position'][0]))
|
||||||
|
elif direction == "BOTTOM_TOP":
|
||||||
|
return sorted(symbols, key=lambda s: (s['position'][1], s['position'][0]))
|
||||||
|
elif direction == "LEFT_RIGHT":
|
||||||
|
return sorted(symbols, key=lambda s: (s['position'][0], s['position'][1]))
|
||||||
|
elif direction == "RIGHT_LEFT":
|
||||||
|
return sorted(symbols, key=lambda s: (-s['position'][0], s['position'][1]))
|
||||||
else:
|
else:
|
||||||
print(f"Umgebungsvariable {env_str} ist nicht gesetzt oder leer.")
|
# Fallback: LEFT_RIGHT
|
||||||
exit()
|
return sorted(symbols, key=lambda s: (s['position'][0], s['position'][1]))
|
||||||
|
|
||||||
|
|
||||||
|
def enumerate_symbols(symbols, attributes):
|
||||||
|
"""
|
||||||
|
Nummeriert die Symbole durch und ersetzt die @@ Platzhalter.
|
||||||
|
"""
|
||||||
|
counter = 1
|
||||||
|
renamed = []
|
||||||
|
|
||||||
|
for symbol in symbols:
|
||||||
|
symbol_attribs = symbol['attributes']
|
||||||
|
layer = symbol['layer']
|
||||||
|
|
||||||
|
# Finde das passende NAME-Template für diesen Layer
|
||||||
|
name_template = None
|
||||||
|
for i in range(1, 4):
|
||||||
|
layer_key = f"LAYER_NAME{i}"
|
||||||
|
name_key = f"NAME{i}"
|
||||||
|
if layer_key in attributes and attributes[layer_key] == layer:
|
||||||
|
if name_key in attributes:
|
||||||
|
name_template = attributes[name_key]
|
||||||
|
break
|
||||||
|
|
||||||
|
# Falls nur NAME/LAYER_NAME vorhanden
|
||||||
|
if not name_template and "LAYER_NAME" in attributes and attributes["LAYER_NAME"] == layer:
|
||||||
|
if "NAME" in attributes:
|
||||||
|
name_template = attributes["NAME"]
|
||||||
|
|
||||||
|
if not name_template:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Ersetze @-Zeichen durch Zahlen
|
||||||
|
# Zähle wie viele @ im Template sind
|
||||||
|
at_count = name_template.count('@')
|
||||||
|
number_str = str(counter).zfill(at_count)
|
||||||
|
new_name = name_template.replace('@' * at_count, number_str)
|
||||||
|
|
||||||
|
# Aktualisiere Attribute im Symbol
|
||||||
|
for attrib in symbol['entity'].attribs:
|
||||||
|
old_value = attrib.dxf.text
|
||||||
|
if '@' in old_value:
|
||||||
|
# Ersetze @ durch die Nummer
|
||||||
|
new_value = old_value.replace('@' * at_count, number_str)
|
||||||
|
attrib.dxf.text = new_value
|
||||||
|
|
||||||
|
renamed.append({
|
||||||
|
'position': symbol['position'],
|
||||||
|
'layer': layer,
|
||||||
|
'old_value': old_value,
|
||||||
|
'new_value': new_value
|
||||||
|
})
|
||||||
|
|
||||||
|
counter += 1
|
||||||
|
|
||||||
|
return renamed
|
||||||
|
|
||||||
|
|
||||||
|
def process_renamer_blocks(doc, msp, renamer_layers, error_collector):
|
||||||
|
"""
|
||||||
|
Verarbeitet alle Renamer-Blöcke auf den angegebenen Layern.
|
||||||
|
"""
|
||||||
|
all_renamed = []
|
||||||
|
|
||||||
|
for layer in renamer_layers:
|
||||||
|
print(f"Durchsuche Layer: {layer}")
|
||||||
|
|
||||||
|
# Finde alle INSERT-Blöcke auf diesem Layer
|
||||||
|
for insert in msp.query(f'INSERT[layer=="{layer}"]'):
|
||||||
|
# Extrahiere Attribute
|
||||||
|
attributes = extract_block_attributes(insert)
|
||||||
|
|
||||||
|
if not attributes:
|
||||||
|
print(f" Block ohne Attribute gefunden an Position {insert.dxf.insert}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Prüfe ob es ein Renamer-Block ist
|
||||||
|
has_name = "NAME" in attributes or "NAME1" in attributes
|
||||||
|
has_direction = "DIRECTION" in attributes
|
||||||
|
|
||||||
|
if not (has_name and has_direction):
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(f" Renamer-Block gefunden: {attributes.get('NAME', attributes.get('NAME1', 'UNKNOWN'))}")
|
||||||
|
print(f" Direction: {attributes.get('DIRECTION', 'UNKNOWN')}")
|
||||||
|
print(f" Kennzeichnung: {attributes.get('KENNZEICHNUNG', 'N/A')}")
|
||||||
|
|
||||||
|
# Finde Boundary (Rechteck oder Polylinie)
|
||||||
|
boundary = get_boundary_geometry(doc, insert)
|
||||||
|
if not boundary:
|
||||||
|
error_msg = f"Keine Polylinie/Rechteck im Renamer-Block an Position {insert.dxf.insert} gefunden"
|
||||||
|
print(f" WARNUNG: {error_msg}")
|
||||||
|
error_collector.add_warnings({"missing_boundary": error_msg})
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(f" Boundary gefunden mit {len(boundary)} Punkten")
|
||||||
|
|
||||||
|
# Finde Symbole innerhalb des Bereichs
|
||||||
|
symbols = find_symbols_in_boundary(doc, msp, boundary, renamer_layers, attributes)
|
||||||
|
print(f" {len(symbols)} Template-Symbole gefunden")
|
||||||
|
|
||||||
|
if not symbols:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Sortiere nach Richtung
|
||||||
|
direction = attributes.get("DIRECTION", "LEFT_RIGHT")
|
||||||
|
sorted_symbols = sort_symbols_by_direction(symbols, direction)
|
||||||
|
|
||||||
|
# Nummeriere durch
|
||||||
|
renamed = enumerate_symbols(sorted_symbols, attributes)
|
||||||
|
all_renamed.extend(renamed)
|
||||||
|
|
||||||
|
print(f" {len(renamed)} Symbole nummeriert")
|
||||||
|
|
||||||
|
return all_renamed
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser(description='fetches the x/y positions from a dxf file', prog='getpositions')
|
parser = argparse.ArgumentParser(description='Nummeriert Symbole in DXF-Dateien basierend auf Renamer-Blöcken', prog='create_numbers')
|
||||||
parser.add_argument('-f', '--filename', action='store', required=True, default="ST_6300_Steuerungstestlayout1_neueBloecke.dwg", help='which file should be fetched', metavar='myfile.dxf')
|
parser.add_argument('-f', '--filename', action='store', required=True, help='DXF-Datei die verarbeitet werden soll', metavar='myfile.dxf')
|
||||||
parser.add_argument('-w', '--write', action='store', help='write results into a json file')
|
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('-d', '--dryrun', action='store_true', help='Symbole nicht in der DXF-Datei überschreiben, nur Ausgabe auf Konsole')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
out_dir = check_environment_var('PROJECT_DATA')
|
out_dir = check_environment_var('PROJECT_DATA')
|
||||||
work_dir = check_environment_var('PROJECT_WORK')
|
work_dir = check_environment_var('PROJECT_WORK')
|
||||||
config_dir = check_environment_var("PROJECT_CFG")
|
config_dir = check_environment_var("PROJECT_CFG")
|
||||||
|
|
||||||
|
# Initialisiere ErrorCollector
|
||||||
|
error_collector = ErrorCollector()
|
||||||
|
|
||||||
filename = Path(args.filename)
|
filename = Path(args.filename)
|
||||||
if not filename.suffix == ".dxf":
|
if not filename.suffix == ".dxf":
|
||||||
print("only available for .dxf files")
|
print("Nur für .dxf Dateien verfügbar")
|
||||||
exit()
|
exit(1)
|
||||||
|
|
||||||
(dxf_path, dexists) = check_file_in_work(work_dir, filename)
|
(dxf_path, dexists) = check_file_in_work(work_dir, filename)
|
||||||
if dexists == False:
|
if dexists == False:
|
||||||
print("no such file ")
|
print(f"Datei nicht gefunden: {filename}")
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
exit()
|
exit(1)
|
||||||
|
|
||||||
if dxf_is_binary(dxf_path): # Wenn dxf eine binary ist, dann komplett parsen und modelspace anlegen
|
# Lese Konfiguration
|
||||||
print("Given .dxf-file is binary dxf. Proceeding to read file. Watch RAM-usage.")
|
config_path = Path(config_dir) / "enumerate.cfg"
|
||||||
|
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 DXF-Datei
|
||||||
|
if dxf_is_binary(dxf_path):
|
||||||
|
print("DXF-Datei ist binär. Lese komplette Datei. Achten Sie auf RAM-Nutzung.")
|
||||||
doc = get_dxf_file(dxf_path)
|
doc = get_dxf_file(dxf_path)
|
||||||
msp = doc.modelspace()
|
msp = doc.modelspace()
|
||||||
use_iter = False
|
|
||||||
else:
|
else:
|
||||||
print("Given .dxf-file is ASCII-dxf. Proceeding to use iterative functions. Process may take longer.")
|
print("DXF-Datei ist ASCII.")
|
||||||
use_iter = True
|
doc = get_dxf_file(dxf_path)
|
||||||
|
msp = doc.modelspace()
|
||||||
|
|
||||||
|
# Verarbeite Renamer-Blöcke
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print("Starte Verarbeitung der Renamer-Blöcke")
|
||||||
|
print("="*60 + "\n")
|
||||||
|
|
||||||
|
renamed_symbols = process_renamer_blocks(doc, msp, renamer_layers, error_collector)
|
||||||
|
|
||||||
|
print("\n" + "="*60)
|
||||||
|
print(f"Verarbeitung abgeschlossen: {len(renamed_symbols)} Symbole nummeriert")
|
||||||
|
print("="*60 + "\n")
|
||||||
|
|
||||||
|
# Ausgabe der Ergebnisse
|
||||||
|
if renamed_symbols:
|
||||||
|
print("Nummerierte Symbole:")
|
||||||
|
for item in renamed_symbols:
|
||||||
|
print(f" {item['old_value']} -> {item['new_value']} (Layer: {item['layer']}, Pos: {item['position']})")
|
||||||
|
|
||||||
|
# Speichere DXF-Datei wenn nicht dry-run
|
||||||
|
if not args.dryrun:
|
||||||
|
output_path = dxf_path.parent / f"{dxf_path.stem}_numbered{dxf_path.suffix}"
|
||||||
|
doc.saveas(output_path)
|
||||||
|
print(f"\nNummerierte DXF-Datei gespeichert: {output_path}")
|
||||||
else:
|
else:
|
||||||
parser.print_help()
|
print("\nDry-run Modus: DXF-Datei wurde nicht überschrieben")
|
||||||
|
|
||||||
|
# Schreibe JSON-Ausgabe wenn gewünscht
|
||||||
|
if args.write:
|
||||||
|
output_data = {
|
||||||
|
'renamed_symbols': renamed_symbols,
|
||||||
|
'total_count': len(renamed_symbols)
|
||||||
|
}
|
||||||
|
output_path = Path(args.write)
|
||||||
|
write_json_file(output_data, output_path.parent, output_path.name)
|
||||||
|
print(f"Ergebnisse in JSON gespeichert: {args.write}")
|
||||||
|
|
||||||
|
# Schreibe Fehler-Datei
|
||||||
|
if args.errorfile:
|
||||||
|
error_data = error_collector.get_all_issues()
|
||||||
|
error_path = Path(args.errorfile)
|
||||||
|
write_json_file(error_data, error_path.parent, error_path.name)
|
||||||
|
print(f"Fehler/Warnungen gespeichert: {args.errorfile}")
|
||||||
|
|
||||||
|
# Exit-Code basierend auf Fehlern
|
||||||
|
if error_collector.has_errors():
|
||||||
|
print("\n!!! Es sind Fehler aufgetreten !!!")
|
||||||
|
exit(1)
|
||||||
|
elif error_collector.has_errors_or_warnings():
|
||||||
|
print("\n(Warnungen vorhanden, aber keine kritischen Fehler)")
|
||||||
|
exit(0)
|
||||||
|
else:
|
||||||
|
print("\nKeine Fehler oder Warnungen")
|
||||||
|
exit(0)
|
||||||
|
|||||||
+1
-10
@@ -14,6 +14,7 @@ import updateconfignames as uc
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from error_collector import ErrorCollector, write_json_file
|
from error_collector import ErrorCollector, write_json_file
|
||||||
|
from utils import check_file_in_work
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -794,16 +795,6 @@ def _create_bom_workbook(outpath, processed_data, bezeichner_cfg):
|
|||||||
wb.save(bom_path)
|
wb.save(bom_path)
|
||||||
print(f"BOM exported to Excel-file")
|
print(f"BOM exported to Excel-file")
|
||||||
|
|
||||||
def check_file_in_work(work_dir, filename):
|
|
||||||
fexists = True
|
|
||||||
if not os.path.exists(filename):
|
|
||||||
mypath = os.path.join(work_dir, filename)
|
|
||||||
if not os.path.exists(mypath):
|
|
||||||
fexists = False
|
|
||||||
else:
|
|
||||||
mypath = filename
|
|
||||||
return (mypath, fexists)
|
|
||||||
|
|
||||||
def copy_layers_into_dxf_by_filter(dxf_source: ezdxf.document.Drawing, dxf_target:ezdxf.document.Drawing):
|
def copy_layers_into_dxf_by_filter(dxf_source: ezdxf.document.Drawing, dxf_target:ezdxf.document.Drawing):
|
||||||
"""
|
"""
|
||||||
Kopiert bestimmte Layer (nach Filter) von einer Quell-DXF in eine Ziel-DXF.
|
Kopiert bestimmte Layer (nach Filter) von einer Quell-DXF in eine Ziel-DXF.
|
||||||
|
|||||||
+9
-56
@@ -12,6 +12,15 @@ from shapely.geometry import Point
|
|||||||
from ezdxf.lldxf.const import DXFStructureError
|
from ezdxf.lldxf.const import DXFStructureError
|
||||||
|
|
||||||
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,
|
||||||
|
merge_two_dicts,
|
||||||
|
to_json,
|
||||||
|
write_results,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -24,21 +33,6 @@ Dieses Programm:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def write_results(jsn_results: str, out_dir: Path, filename: str) -> None:
|
|
||||||
"""Write results to a JSON file."""
|
|
||||||
print("writing results to a json file ...")
|
|
||||||
outfile = os.path.join(out_dir, filename)
|
|
||||||
with open(outfile, 'w', encoding='utf-8') as fh:
|
|
||||||
fh.write(jsn_results)
|
|
||||||
print("done")
|
|
||||||
|
|
||||||
|
|
||||||
def merge_two_dicts(x: dict, y: dict) -> dict:
|
|
||||||
z = x.copy()
|
|
||||||
z.update(y)
|
|
||||||
return z
|
|
||||||
|
|
||||||
|
|
||||||
def get_type_of_name_cfg(name: str) -> str:
|
def get_type_of_name_cfg(name: str) -> str:
|
||||||
prefix = name[:2]
|
prefix = name[:2]
|
||||||
|
|
||||||
@@ -733,34 +727,6 @@ def scan(dxf_source) -> dict:
|
|||||||
ret['used_blocks'] = used_block_names
|
ret['used_blocks'] = used_block_names
|
||||||
ret['all_blocks'] = alle_block_defs
|
ret['all_blocks'] = alle_block_defs
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def to_json(d: object, pretty: bool = True) -> str:
|
|
||||||
return json.dumps(d, indent=2 if pretty else None, ensure_ascii=False, default=str) #ensure_ascii false für darstellung von "ue"
|
|
||||||
|
|
||||||
def get_dxf_file(filepath: Path):
|
|
||||||
"""Hole das dxf file."""
|
|
||||||
try:
|
|
||||||
print("reading file ..", end='')
|
|
||||||
doc = ezdxf.filemanagement.readfile(filepath)
|
|
||||||
print("done")
|
|
||||||
except IOError:
|
|
||||||
print("Not a DXF file or a generic I/O error.")
|
|
||||||
sys.exit(1)
|
|
||||||
except DXFStructureError:
|
|
||||||
print("Invalid or corrupted DXF file.")
|
|
||||||
sys.exit(2)
|
|
||||||
return doc
|
|
||||||
|
|
||||||
def check_file_in_work(work_dir: Path, filename: Path) -> tuple[Path, bool]:
|
|
||||||
fexists = True
|
|
||||||
if not filename.exists():
|
|
||||||
mypath = work_dir.joinpath(filename)
|
|
||||||
ex = mypath.exists()
|
|
||||||
if not mypath.exists():
|
|
||||||
fexists = False
|
|
||||||
else:
|
|
||||||
mypath = filename
|
|
||||||
return mypath, fexists
|
|
||||||
|
|
||||||
def check_rack_z_coordinates(res_racks: dict, error_collector, config) -> None:
|
def check_rack_z_coordinates(res_racks: dict, error_collector, config) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -829,11 +795,6 @@ def check_existance(res_mappings: dict, res_dist: dict, res_pos: dict, res_tunne
|
|||||||
ret["overdefined_tunnel"] = list()
|
ret["overdefined_tunnel"] = list()
|
||||||
ret["overdefined_tunnel"].append(tname)
|
ret["overdefined_tunnel"].append(tname)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def dxf_is_binary(dxf_path: Path) -> bool:
|
|
||||||
with open(dxf_path, 'rb') as f:
|
|
||||||
header = f.read(22)
|
|
||||||
return b'AutoCAD Binary DXF' in header
|
|
||||||
|
|
||||||
def validate_configs() -> None:
|
def validate_configs() -> None:
|
||||||
errors = []
|
errors = []
|
||||||
@@ -869,14 +830,6 @@ def validate_configs() -> None:
|
|||||||
else:
|
else:
|
||||||
print("No inconsistencies found. Continuing with routing process.")
|
print("No inconsistencies found. Continuing with routing process.")
|
||||||
|
|
||||||
def check_environment_var(env_str: str) -> Path:
|
|
||||||
out_path = os.environ.get(env_str)
|
|
||||||
if out_path:
|
|
||||||
return Path(out_path)
|
|
||||||
else:
|
|
||||||
print(f"Umgebungsvariable {env_str} ist nicht gesetzt oder leer.")
|
|
||||||
exit()
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser(description='fetches the x/y positions from a dxf file', prog='getpositions')
|
parser = argparse.ArgumentParser(description='fetches the x/y positions from a dxf file', prog='getpositions')
|
||||||
parser.add_argument('-f', '--filename', action='store', required=True, default="ST_6300_Steuerungstestlayout1_neueBloecke.dwg", help='which file should be fetched', metavar='myfile.dxf')
|
parser.add_argument('-f', '--filename', action='store', required=True, default="ST_6300_Steuerungstestlayout1_neueBloecke.dwg", help='which file should be fetched', metavar='myfile.dxf')
|
||||||
|
|||||||
+2
-3
@@ -11,6 +11,8 @@ from shapely.strtree import STRtree
|
|||||||
import math
|
import math
|
||||||
import shapely
|
import shapely
|
||||||
|
|
||||||
|
from utils import to_json
|
||||||
|
|
||||||
# Globale Variable, die in main aufgerufen wird und steuert ob Graphen in unittests gezeichnet werden
|
# Globale Variable, die in main aufgerufen wird und steuert ob Graphen in unittests gezeichnet werden
|
||||||
draw = False
|
draw = False
|
||||||
class PointSorter:
|
class PointSorter:
|
||||||
@@ -33,9 +35,6 @@ class PointSorter:
|
|||||||
|
|
||||||
def get_sorted_by_y(self):
|
def get_sorted_by_y(self):
|
||||||
return sorted(self.points, key = lambda p: p.y)
|
return sorted(self.points, key = lambda p: p.y)
|
||||||
|
|
||||||
def to_json(d, pretty: bool = True) -> str:
|
|
||||||
return json.dumps(d, indent=2 if pretty else None, default=str) #ensure_ascii false für darstellung von "ue"
|
|
||||||
|
|
||||||
class NodeIDs():
|
class NodeIDs():
|
||||||
''' Klasse, die Punkte verwaltet und NodeIDs zu Punkten zuordnet.
|
''' Klasse, die Punkte verwaltet und NodeIDs zu Punkten zuordnet.
|
||||||
|
|||||||
+2
-16
@@ -5,6 +5,8 @@ from ioconverter import ExcelConverter
|
|||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from utils import check_file_in_work, load_json
|
||||||
|
|
||||||
def process_item(sname, sdata):
|
def process_item(sname, sdata):
|
||||||
data = {
|
data = {
|
||||||
"id": sdata.get("IO", ""),
|
"id": sdata.get("IO", ""),
|
||||||
@@ -57,22 +59,6 @@ def prepare_data(rawdata:dict):
|
|||||||
return (dsensors, lsensors)
|
return (dsensors, lsensors)
|
||||||
|
|
||||||
|
|
||||||
# einfache Funktionen
|
|
||||||
def load_json(jsonfilename):
|
|
||||||
with open(jsonfilename, encoding='utf-8') as fh:
|
|
||||||
return json.load(fh)
|
|
||||||
|
|
||||||
def check_file_in_work(work_dir:Path, filename:Path):
|
|
||||||
fexists = True
|
|
||||||
if not filename.exists(): # dann schau im Work Ordner nach
|
|
||||||
mypath = work_dir.joinpath(filename)
|
|
||||||
ex = mypath.exists()
|
|
||||||
if not mypath.exists():
|
|
||||||
fexists = False
|
|
||||||
else:
|
|
||||||
mypath = filename
|
|
||||||
return (mypath, fexists)
|
|
||||||
|
|
||||||
# Helper zum Laden der Error-JSON
|
# Helper zum Laden der Error-JSON
|
||||||
def load_error_json(json_path: Path, work_dir: Path):
|
def load_error_json(json_path: Path, work_dir: Path):
|
||||||
"""
|
"""
|
||||||
|
|||||||
+2
-16
@@ -8,24 +8,10 @@ import os
|
|||||||
import configparser
|
import configparser
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
from utils import load_json, to_json, write_results
|
||||||
|
|
||||||
|
|
||||||
# Funktionen
|
# Funktionen
|
||||||
def load_json(jsonfilename):
|
|
||||||
with open(jsonfilename, encoding='utf-8') as fh:
|
|
||||||
return json.load(fh)
|
|
||||||
|
|
||||||
def write_results(jsnResults, outdir, filename):
|
|
||||||
""" write results to a json file
|
|
||||||
"""
|
|
||||||
print("writing results to a json file ...")
|
|
||||||
outfile = Path(outdir) / filename
|
|
||||||
with open(outfile, 'w', encoding='utf-8') as fh:
|
|
||||||
fh.write(jsnResults)
|
|
||||||
print("done")
|
|
||||||
|
|
||||||
def to_json(d, pretty: bool = True) -> str:
|
|
||||||
return json.dumps(d, indent=2 if pretty else None, ensure_ascii=False, default=str) #ensure_ascii false für darstellung von "ue"
|
|
||||||
|
|
||||||
def create_plant(racks:dict, sensors:dict, distributors:dict, mapping:dict, tunnels: dict, tunlength:dict ) -> dict:
|
def create_plant(racks:dict, sensors:dict, distributors:dict, mapping:dict, tunnels: dict, tunlength:dict ) -> dict:
|
||||||
|
|
||||||
# racks = {'Rack_1-0': [Point(0, 0), Point(0, 10)],
|
# racks = {'Rack_1-0': [Point(0, 0), Point(0, 10)],
|
||||||
|
|||||||
+2
-10
@@ -10,6 +10,8 @@ from fnmatch import fnmatch
|
|||||||
import ezdxf
|
import ezdxf
|
||||||
from openpyxl import Workbook
|
from openpyxl import Workbook
|
||||||
|
|
||||||
|
from utils import check_environment_var
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Dieses Programm:
|
Dieses Programm:
|
||||||
@@ -24,16 +26,6 @@ Dieses Programm:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def check_environment_var(env_str: str) -> Path:
|
|
||||||
"""Prüft ob eine Umgebungsvariable gesetzt ist und gibt den Pfad zurück."""
|
|
||||||
out_path = os.environ.get(env_str)
|
|
||||||
if out_path:
|
|
||||||
return Path(out_path)
|
|
||||||
else:
|
|
||||||
print(f"Umgebungsvariable {env_str} ist nicht gesetzt oder leer.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def load_translation_config(translation_lang: str, translation_dir: Path) -> tuple[dict[str, str], dict[str, str], dict[str, str], dict[str, str]]:
|
def load_translation_config(translation_lang: str, translation_dir: Path) -> tuple[dict[str, str], dict[str, str], dict[str, str], dict[str, str]]:
|
||||||
"""
|
"""
|
||||||
Lädt eine Übersetzungs-Config-Datei (z.B. CS.cfg, EN.cfg, FR.cfg).
|
Lädt eine Übersetzungs-Config-Datei (z.B. CS.cfg, EN.cfg, FR.cfg).
|
||||||
|
|||||||
Reference in New Issue
Block a user