refactor: create_numbers ohne Kopie von error_collector
Fehler/Warnungen als zwei einfache Listen (neue_meldungen, meldungen_als_dict) statt der aus kabellaengen kopierten ErrorCollector-Klasse; lib/error_collector.py entfernt. Format der Fehlerdatei unveraendert, an sechs Testzeichnungen gegen das Original verglichen (identische Nummern und Fehlerdateien). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8abb6b3c65
commit
4100cbfb97
4 files changed
+97
-184
No files matched your search
@@ -228,7 +228,7 @@ Abhaengigkeiten: `ezdxf`, Standard-Library. Benoetigen Umgebungsvariablen (`DXFM
|
|||||||
<<<<<<< Updated upstream
|
<<<<<<< Updated upstream
|
||||||
=======
|
=======
|
||||||
| `hw_symbole.py` | HW-Planung: setzt die E-Geraete-Symbole aus dem Plan `<dwg>_hw_symbole.json` (HW_SYMBOLE, HwPlanung.lsp) in die `_HW.dxf` (`hw_materialfluss.hw_dokument_oeffnen`: vorhandene Datei ergaenzen statt ueberschreiben). Entfernt vorher nur die erzeugten Symbole der Praefixe des Plans (XDATA `SSG_HW_SYM`), baut die Blockdefinitionen bei jedem Lauf neu aus `cfg/hw_symbole.json` (`block_aufbauen`: Rahmen, ATTDEFs, Textstil, Layer), **Lage** per `platzieren`: Marke `REALE_POSITION` mit x genau auf dem Zielpunkt, Kasten daneben, Symbole eines Bauteils luekenlos uebereinander (Reihenfolge `benoetigt`), Gruppe um das Ziel zentriert; Ueberlappung mit schon gesetzten Rahmen (2D-Bounding-Boxen, `platzierung` in hw_symbole.json) wird NUR in y ausgewichen, x bleibt auf dem Ziel; Plan immer vollstaendig, eingefuegt nur `praefixe`. IO = Vorlage, Platzhalter-ID in VERW/BEZEICHNUNG ersetzt (`attribut_werte`). Test `tests/test_hw_symbole.py` (inkl. Abdeckung io_regeln -> hw_symbole und Kette Symbole -> Renamer-Polylinie -> create_numbers). Aufruf: `python hw_symbole.py <plan.json> <out_HW.dxf> --symbole cfg/hw_symbole.json` |
|
| `hw_symbole.py` | HW-Planung: setzt die E-Geraete-Symbole aus dem Plan `<dwg>_hw_symbole.json` (HW_SYMBOLE, HwPlanung.lsp) in die `_HW.dxf` (`hw_materialfluss.hw_dokument_oeffnen`: vorhandene Datei ergaenzen statt ueberschreiben). Entfernt vorher nur die erzeugten Symbole der Praefixe des Plans (XDATA `SSG_HW_SYM`), baut die Blockdefinitionen bei jedem Lauf neu aus `cfg/hw_symbole.json` (`block_aufbauen`: Rahmen, ATTDEFs, Textstil, Layer), **Lage** per `platzieren`: Marke `REALE_POSITION` mit x genau auf dem Zielpunkt, Kasten daneben, Symbole eines Bauteils luekenlos uebereinander (Reihenfolge `benoetigt`), Gruppe um das Ziel zentriert; Ueberlappung mit schon gesetzten Rahmen (2D-Bounding-Boxen, `platzierung` in hw_symbole.json) wird NUR in y ausgewichen, x bleibt auf dem Ziel; Plan immer vollstaendig, eingefuegt nur `praefixe`. IO = Vorlage, Platzhalter-ID in VERW/BEZEICHNUNG ersetzt (`attribut_werte`). Test `tests/test_hw_symbole.py` (inkl. Abdeckung io_regeln -> hw_symbole und Kette Symbole -> Renamer-Polylinie -> create_numbers). Aufruf: `python hw_symbole.py <plan.json> <out_HW.dxf> --symbole cfg/hw_symbole.json` |
|
||||||
| `create_numbers.py` | Nummerierung der Symbol-Vorlagen (`@`) entlang von Renamer-Bereichen (Rechteck mit Richtung oder `POLYLINE_PATH`), Layer/Parameter aus `cfg/enumerate.cfg`; Fehler/Warnungen ueber `error_collector.py`. 2026-11-03 aus kabellaengen umgezogen (dort entfernt), die benoetigten Helfer aus kabellaengen/lib/utils.py stehen direkt im Modul, `--all-examples` entfallen. Nummer wird OKTAL gebildet (`oct(counter)`, so gewollt - nicht auf dezimal "korrigieren"). Genutzt von HW_NUMMERIEREN. Test `tests/test_create_numbers.py`. Aufruf: `python create_numbers.py --filename <dxf> [--config ...] [--info ...] [--errorfile ...] [--dryrun\|--overwrite]` |
|
| `create_numbers.py` | Nummerierung der Symbol-Vorlagen (`@`) entlang von Renamer-Bereichen (Rechteck mit Richtung oder `POLYLINE_PATH`), Layer/Parameter aus `cfg/enumerate.cfg`; Fehler/Warnungen als zwei einfache Listen `(art, text)` (`neue_meldungen`/`meldungen_als_dict`, Format der Fehlerdatei unveraendert) - KEINE Kopie von kabellaengens `error_collector.py`/`utils.py`; die wenigen Helfer daraus stehen einmalig im Modul. 2026-11-03 aus kabellaengen umgezogen (dort entfernt), die benoetigten Helfer aus kabellaengen/lib/utils.py stehen direkt im Modul, `--all-examples` entfallen. Nummer wird OKTAL gebildet (`oct(counter)`, so gewollt - nicht auf dezimal "korrigieren"). Genutzt von HW_NUMMERIEREN. Test `tests/test_create_numbers.py`. Aufruf: `python create_numbers.py --filename <dxf> [--config ...] [--info ...] [--errorfile ...] [--dryrun\|--overwrite]` |
|
||||||
| `lager_yaml_export.py` | Lagerplanung: `<dwg>_lager_raw.json` (LAGER_EXPORT) -> `<dwg>_drawing.yaml` (Overlay im Schema der RFQ-Hand-YAML, nur Zeichnungsfelder, plus `_provenance`/`_conflicts`/`_warnings`) und optional `--nachweis` HTML. `--hand <yaml>` gleicht ab (Hand gewinnt; Konflikte bei > 1 % bzw. 0,005). Aggregiert nur, Regeln leben in LISP. Braucht `pyyaml`. Test `tests/test_lager_yaml.py`. Aufruf: `python lager_yaml_export.py <raw.json> <out.yaml> [--hand ...] [--nachweis ...]` |
|
| `lager_yaml_export.py` | Lagerplanung: `<dwg>_lager_raw.json` (LAGER_EXPORT) -> `<dwg>_drawing.yaml` (Overlay im Schema der RFQ-Hand-YAML, nur Zeichnungsfelder, plus `_provenance`/`_conflicts`/`_warnings`) und optional `--nachweis` HTML. `--hand <yaml>` gleicht ab (Hand gewinnt; Konflikte bei > 1 % bzw. 0,005). Aggregiert nur, Regeln leben in LISP. Braucht `pyyaml`. Test `tests/test_lager_yaml.py`. Aufruf: `python lager_yaml_export.py <raw.json> <out.yaml> [--hand ...] [--nachweis ...]` |
|
||||||
>>>>>>> Stashed changes
|
>>>>>>> Stashed changes
|
||||||
| `dxf_scan_components.py` | Erkennt ILS-/Omniflo-Komponenten in einer **fremden** Projektzeichnung (ohne unsere Wrapper-Bloecke) und schreibt sie im JSON-Schema aus `doc/json-input-lisp.md` (Nachfolger des geloeschten `tests/testdata/object_data.md`) — Grundlage fuer Nachbau-Testfaelle wie `tests/testdata/mubea.json`. Liest binaere DXF im GB-Bereich streamend per mmap (ezdxf-DOM waere zu gross), loest INSERTs inkl. OCS/Extrusionsrichtung rekursiv in Weltkoordinaten auf, paart AN8/SP8 zu Kreiseln und Antrieb/Umlenkung zu Foerderern. Gefaellestrecken, die als 3D-LINE auf einem Staustrecken-Layer gezeichnet sind (`GF_LINIE_LAYER`, z.B. `6-SP`), werden in einem zweiten Lesedurchgang zu Modus-1-Eintraegen: Start/Ende werden auf die Ketten der anschliessenden Kreisel verlaengert (gezeichnet ist nur die Staustrecke; `deltaL` reicht von AS- bis ES-Ursprung), AS-Seite aus dem Ausschleuselement am oberen Ende; dieses und Separatoren auf der Achse entfallen als Einzel-Insert (die GF baut beide selbst). Sitzt der AS am UNTEREN Ende, ist es ein Steigfoerderer und wird nur gemeldet (`gf_eintraege_aus_linien`, Test `tests/test_dxf_scan_gf.py`). `--ohne-hoehen` setzt alle Z auf 0, `--kreisel-hoehe X Y Z` gibt einem Kreisel seine Hoehe - die daran haengenden GF bekommen daraus `start_mm.z`/`hoehe_ende_mm`, den Winkel rechnet der Treiber (`dematic_arvato:gf-geometrie`: atan ueber die HORIZONTALE Laenge, ES-Laenge UND -Abfall bei der Einlaufneigung gemessen und iteriert, Staustrecke daraus - die flache ES-Laenge liess steile GF ~144 mm vor der Kette enden - NICHT `gf-winkel-berechnen`, dessen asin bei steilen GF wie im Showroom ~50 statt ~37 Grad liefert; `dematic_arvato:gf-endhoehe-pruefen` meldet `warnung` bei > 5 mm Abweichung von HOEHE_BIS). Flache 2D-Plaene: `--flach` (waagrechte Staustrecke mit AS an genau einem Ende = GF, zerstueckelte Linien werden vereint, ES am unteren Ende wird verbraucht, AS/ES-Seite ohne Namensangabe aus der Lage zur Fahrtrichtung), `--kreisel-achse X1 Y1 X2 Y2` (Kreisel ohne AN8/SP8), Einzelteile auf einer Kreisel-Kette erben dessen Anwender-Hoehe (`sensor_hoehen_von_kreiseln`). Kreisel-Paarung mit Tausch-Nachbesserung auf minimale Abstandssumme (zwei Kreisel auf einer Achse). Genutzt fuer `tests/testdata/showroom_ils.json` (`TEST_SHOWROOM_ILS`). Aufruf: `python dxf_scan_components.py <dxf> <data_dir> <output.json> [--fenster X0 Y0 X1 Y1]` — ohne `--fenster` liefert eine Anlagenzeichnung leicht ueber 1300 Eintraege, zuviel fuer einen Testlauf |
|
| `dxf_scan_components.py` | Erkennt ILS-/Omniflo-Komponenten in einer **fremden** Projektzeichnung (ohne unsere Wrapper-Bloecke) und schreibt sie im JSON-Schema aus `doc/json-input-lisp.md` (Nachfolger des geloeschten `tests/testdata/object_data.md`) — Grundlage fuer Nachbau-Testfaelle wie `tests/testdata/mubea.json`. Liest binaere DXF im GB-Bereich streamend per mmap (ezdxf-DOM waere zu gross), loest INSERTs inkl. OCS/Extrusionsrichtung rekursiv in Weltkoordinaten auf, paart AN8/SP8 zu Kreiseln und Antrieb/Umlenkung zu Foerderern. Gefaellestrecken, die als 3D-LINE auf einem Staustrecken-Layer gezeichnet sind (`GF_LINIE_LAYER`, z.B. `6-SP`), werden in einem zweiten Lesedurchgang zu Modus-1-Eintraegen: Start/Ende werden auf die Ketten der anschliessenden Kreisel verlaengert (gezeichnet ist nur die Staustrecke; `deltaL` reicht von AS- bis ES-Ursprung), AS-Seite aus dem Ausschleuselement am oberen Ende; dieses und Separatoren auf der Achse entfallen als Einzel-Insert (die GF baut beide selbst). Sitzt der AS am UNTEREN Ende, ist es ein Steigfoerderer und wird nur gemeldet (`gf_eintraege_aus_linien`, Test `tests/test_dxf_scan_gf.py`). `--ohne-hoehen` setzt alle Z auf 0, `--kreisel-hoehe X Y Z` gibt einem Kreisel seine Hoehe - die daran haengenden GF bekommen daraus `start_mm.z`/`hoehe_ende_mm`, den Winkel rechnet der Treiber (`dematic_arvato:gf-geometrie`: atan ueber die HORIZONTALE Laenge, ES-Laenge UND -Abfall bei der Einlaufneigung gemessen und iteriert, Staustrecke daraus - die flache ES-Laenge liess steile GF ~144 mm vor der Kette enden - NICHT `gf-winkel-berechnen`, dessen asin bei steilen GF wie im Showroom ~50 statt ~37 Grad liefert; `dematic_arvato:gf-endhoehe-pruefen` meldet `warnung` bei > 5 mm Abweichung von HOEHE_BIS). Flache 2D-Plaene: `--flach` (waagrechte Staustrecke mit AS an genau einem Ende = GF, zerstueckelte Linien werden vereint, ES am unteren Ende wird verbraucht, AS/ES-Seite ohne Namensangabe aus der Lage zur Fahrtrichtung), `--kreisel-achse X1 Y1 X2 Y2` (Kreisel ohne AN8/SP8), Einzelteile auf einer Kreisel-Kette erben dessen Anwender-Hoehe (`sensor_hoehen_von_kreiseln`). Kreisel-Paarung mit Tausch-Nachbesserung auf minimale Abstandssumme (zwei Kreisel auf einer Achse). Genutzt fuer `tests/testdata/showroom_ils.json` (`TEST_SHOWROOM_ILS`). Aufruf: `python dxf_scan_components.py <dxf> <data_dir> <output.json> [--fenster X0 Y0 X1 Y1]` — ohne `--fenster` liefert eine Anlagenzeichnung leicht ueber 1300 Eintraege, zuviel fuer einen Testlauf |
|
||||||
|
|||||||
+8
-3
@@ -185,9 +185,14 @@ einer Gruppe bekommt `...010`.
|
|||||||
|
|
||||||
## Herkunft
|
## Herkunft
|
||||||
|
|
||||||
`create_numbers.py`, `error_collector.py`, `cfg/enumerate.cfg` und der Test
|
`create_numbers.py`, `cfg/enumerate.cfg` und der Test
|
||||||
`tests/test_create_numbers.py` sind am 2026-11-03 aus dem Kabeltool-Repo
|
`tests/test_create_numbers.py` sind am 2026-11-03 aus dem Kabeltool-Repo
|
||||||
`kabellaengen` hierher umgezogen (dort entfernt). Die Hilfsfunktionen aus
|
`kabellaengen` hierher umgezogen (dort entfernt). Es gibt nur diesen einen
|
||||||
`kabellaengen/lib/utils.py` stehen direkt in `create_numbers.py`. Die Testszenen
|
Codestand: die benoetigten Helfer aus `kabellaengen/lib/utils.py` stehen direkt
|
||||||
|
in `create_numbers.py`, die Fehlersammlung `error_collector.py` ist durch zwei
|
||||||
|
einfache Listen ersetzt (gleiches Format der Fehlerdatei; gegen das Original an
|
||||||
|
sechs Testzeichnungen verglichen: identische Nummern und Fehlerdateien).
|
||||||
|
Bewusst doppelt bleibt nur die Konfiguration `cfg/io_regeln.json` (gekuerzt) -
|
||||||
|
beide Repos werden von verschiedenen Abteilungen gepflegt. Die Testszenen
|
||||||
`Nummerierung1/2`, `BGMG`, `POT` (Erzeugung per `kabellaengen/lib/create_example.py`)
|
`Nummerierung1/2`, `BGMG`, `POT` (Erzeugung per `kabellaengen/lib/create_example.py`)
|
||||||
bleiben in kabellaengen; der Modus `--all-examples` ist darum hier entfallen.
|
bleiben in kabellaengen; der Modus `--all-examples` ist darum hier entfallen.
|
||||||
+88
-59
@@ -1,4 +1,5 @@
|
|||||||
import argparse
|
import argparse
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -6,7 +7,35 @@ from pathlib import Path
|
|||||||
import ezdxf
|
import ezdxf
|
||||||
from ezdxf.lldxf.const import DXFStructureError
|
from ezdxf.lldxf.const import DXFStructureError
|
||||||
|
|
||||||
from error_collector import ErrorCollector, write_json_file
|
|
||||||
|
# ============================================================================
|
||||||
|
# Meldungen: zwei einfache Listen von (art, text) - Fehler und Warnungen.
|
||||||
|
# Ersetzt die Fehlersammler-Klasse ErrorCollector aus kabellaengen (lib/error_collector.py),
|
||||||
|
# damit keine Kopie davon in diesem Repo liegt.
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
def neue_meldungen():
|
||||||
|
"""Leere Meldungssammlung: {"errors": [(art, text), ...], "warnings": [...]}."""
|
||||||
|
return {"errors": [], "warnings": []}
|
||||||
|
|
||||||
|
|
||||||
|
def meldungen_als_dict(meldungen):
|
||||||
|
"""Fuer die Fehlerdatei nach Art gruppiert, Format wie bisher:
|
||||||
|
{"errors": {art: [text, ...]}, "warnings": {...}} - leere Teile entfallen."""
|
||||||
|
erg = {}
|
||||||
|
for teil in ("errors", "warnings"):
|
||||||
|
gruppen = {}
|
||||||
|
for art, text in meldungen[teil]:
|
||||||
|
gruppen.setdefault(art, []).append(text)
|
||||||
|
if gruppen:
|
||||||
|
erg[teil] = gruppen
|
||||||
|
return erg
|
||||||
|
|
||||||
|
|
||||||
|
def write_json_file(data, out_dir, filename):
|
||||||
|
"""data als JSON (UTF-8, eingerueckt) nach out_dir/filename schreiben."""
|
||||||
|
with open(Path(out_dir) / filename, 'w', encoding='utf-8') as fh:
|
||||||
|
json.dump(data, fh, indent=2, ensure_ascii=False, default=str)
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -37,7 +66,7 @@ def get_dxf_file(filepath: Path):
|
|||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
|
||||||
def extract_block_attributes(doc, insert, error_collector=None) -> dict:
|
def extract_block_attributes(doc, insert, meldungen=None) -> dict:
|
||||||
"""
|
"""
|
||||||
Alle Attribute eines INSERT als {tag: text}. Unterstuetzt die zweistufige
|
Alle Attribute eines INSERT als {tag: text}. Unterstuetzt die zweistufige
|
||||||
Blockstruktur der Renamer-Bloecke (aeusserer Block mit LWPOLYLINE und einem
|
Blockstruktur der Renamer-Bloecke (aeusserer Block mit LWPOLYLINE und einem
|
||||||
@@ -64,9 +93,9 @@ def extract_block_attributes(doc, insert, error_collector=None) -> dict:
|
|||||||
attributes[attrib.dxf.tag] = attrib.dxf.text
|
attributes[attrib.dxf.tag] = attrib.dxf.text
|
||||||
break
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if error_collector:
|
if meldungen:
|
||||||
error_msg = f"Fehler beim Extrahieren der Attribute aus Block '{insert.dxf.name}': {e}"
|
error_msg = f"Fehler beim Extrahieren der Attribute aus Block '{insert.dxf.name}': {e}"
|
||||||
error_collector.add_warnings({"attribute_extraction_error": error_msg})
|
meldungen["warnings"].append(("attribute_extraction_error", error_msg))
|
||||||
|
|
||||||
return attributes
|
return attributes
|
||||||
|
|
||||||
@@ -377,7 +406,7 @@ def point_near_polyline(point, polyline_points, max_distance=500.0):
|
|||||||
return min_dist_sq <= (max_distance * max_distance)
|
return min_dist_sq <= (max_distance * max_distance)
|
||||||
|
|
||||||
|
|
||||||
def find_symbols_in_boundary(doc, msp, boundary, target_layers, attributes, error_collector=None, polyline_max_distance=500.0):
|
def find_symbols_in_boundary(doc, msp, boundary, target_layers, attributes, meldungen=None, polyline_max_distance=500.0):
|
||||||
"""
|
"""
|
||||||
Findet alle Symbole (INSERT-Blöcke) innerhalb des angegebenen Bereichs auf den Ziel-Layern.
|
Findet alle Symbole (INSERT-Blöcke) innerhalb des angegebenen Bereichs auf den Ziel-Layern.
|
||||||
|
|
||||||
@@ -411,7 +440,7 @@ def find_symbols_in_boundary(doc, msp, boundary, target_layers, attributes, erro
|
|||||||
|
|
||||||
# Prüfe ob der Block Attribute hat (direkt oder in zweistufiger Struktur)
|
# Prüfe ob der Block Attribute hat (direkt oder in zweistufiger Struktur)
|
||||||
# Verwende extract_block_attributes um beides zu unterstützen
|
# Verwende extract_block_attributes um beides zu unterstützen
|
||||||
symbol_attribs = extract_block_attributes(doc, entity, error_collector)
|
symbol_attribs = extract_block_attributes(doc, entity, meldungen)
|
||||||
|
|
||||||
if not symbol_attribs:
|
if not symbol_attribs:
|
||||||
continue
|
continue
|
||||||
@@ -758,7 +787,7 @@ def validate_direction_with_geometry(direction, is_rectangle):
|
|||||||
return True, f"Unbekannte DIRECTION '{direction}' - Verarbeitung wird fortgesetzt"
|
return True, f"Unbekannte DIRECTION '{direction}' - Verarbeitung wird fortgesetzt"
|
||||||
|
|
||||||
|
|
||||||
def collect_and_group_renamer_blocks(doc, msp, renamer_layers, error_collector):
|
def collect_and_group_renamer_blocks(doc, msp, renamer_layers, meldungen):
|
||||||
"""
|
"""
|
||||||
Erster Pass: Sammelt und gruppiert alle Renamer-Blöcke nach Konfiguration.
|
Erster Pass: Sammelt und gruppiert alle Renamer-Blöcke nach Konfiguration.
|
||||||
|
|
||||||
@@ -773,7 +802,7 @@ def collect_and_group_renamer_blocks(doc, msp, renamer_layers, error_collector):
|
|||||||
# Finde alle INSERT-Blöcke auf diesem Layer
|
# Finde alle INSERT-Blöcke auf diesem Layer
|
||||||
for insert in msp.query(f'INSERT[layer=="{layer}"]'):
|
for insert in msp.query(f'INSERT[layer=="{layer}"]'):
|
||||||
# Extrahiere Attribute (unterstützt zweistufige Blockstruktur)
|
# Extrahiere Attribute (unterstützt zweistufige Blockstruktur)
|
||||||
attributes = extract_block_attributes(doc, insert, error_collector)
|
attributes = extract_block_attributes(doc, insert, meldungen)
|
||||||
|
|
||||||
if not attributes:
|
if not attributes:
|
||||||
print(f" Block ohne Attribute gefunden an Position {insert.dxf.insert}")
|
print(f" Block ohne Attribute gefunden an Position {insert.dxf.insert}")
|
||||||
@@ -796,7 +825,7 @@ def collect_and_group_renamer_blocks(doc, msp, renamer_layers, error_collector):
|
|||||||
if boundary is None:
|
if boundary is None:
|
||||||
error_msg = f"Keine Polylinie/Rechteck im Renamer-Block '{block_name}' an Position {insert.dxf.insert} gefunden"
|
error_msg = f"Keine Polylinie/Rechteck im Renamer-Block '{block_name}' an Position {insert.dxf.insert} gefunden"
|
||||||
print(f" FEHLER: {error_msg}")
|
print(f" FEHLER: {error_msg}")
|
||||||
error_collector.add_errors({"missing_boundary": error_msg})
|
meldungen["errors"].append(("missing_boundary", error_msg))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print(f" Boundary gefunden mit {len(boundary)} Punkten (Rechteck: {is_rectangle})")
|
print(f" Boundary gefunden mit {len(boundary)} Punkten (Rechteck: {is_rectangle})")
|
||||||
@@ -809,12 +838,12 @@ def collect_and_group_renamer_blocks(doc, msp, renamer_layers, error_collector):
|
|||||||
if not is_valid:
|
if not is_valid:
|
||||||
error_msg = f"Renamer-Block '{block_name}' an Position {insert.dxf.insert}: {validation_error}"
|
error_msg = f"Renamer-Block '{block_name}' an Position {insert.dxf.insert}: {validation_error}"
|
||||||
print(f" FEHLER: {error_msg}")
|
print(f" FEHLER: {error_msg}")
|
||||||
error_collector.add_errors({"direction_geometry_mismatch": error_msg})
|
meldungen["errors"].append(("direction_geometry_mismatch", error_msg))
|
||||||
continue
|
continue
|
||||||
elif validation_error:
|
elif validation_error:
|
||||||
# Warnung bei unbekannter DIRECTION, aber fortfahren
|
# Warnung bei unbekannter DIRECTION, aber fortfahren
|
||||||
print(f" WARNUNG: {validation_error}")
|
print(f" WARNUNG: {validation_error}")
|
||||||
error_collector.add_warnings({"unknown_direction": validation_error})
|
meldungen["warnings"].append(("unknown_direction", validation_error))
|
||||||
|
|
||||||
# Erstelle Konfigurationsschlüssel und gruppiere
|
# Erstelle Konfigurationsschlüssel und gruppiere
|
||||||
config_key = get_renamer_config_key(attributes)
|
config_key = get_renamer_config_key(attributes)
|
||||||
@@ -825,7 +854,7 @@ def collect_and_group_renamer_blocks(doc, msp, renamer_layers, error_collector):
|
|||||||
return renamer_groups
|
return renamer_groups
|
||||||
|
|
||||||
|
|
||||||
def find_template_symbols_in_geometry(doc, msp, group_blocks, error_collector):
|
def find_template_symbols_in_geometry(doc, msp, group_blocks, meldungen):
|
||||||
"""
|
"""
|
||||||
Findet alle Symbole mit '@' die geometrisch in den Renamer-Bereichen liegen,
|
Findet alle Symbole mit '@' die geometrisch in den Renamer-Bereichen liegen,
|
||||||
unabhängig vom Layer.
|
unabhängig vom Layer.
|
||||||
@@ -834,7 +863,7 @@ def find_template_symbols_in_geometry(doc, msp, group_blocks, error_collector):
|
|||||||
doc: DXF-Dokument
|
doc: DXF-Dokument
|
||||||
msp: Modelspace
|
msp: Modelspace
|
||||||
group_blocks: Liste von (insert, boundary, attributes, is_rectangle) Tupeln
|
group_blocks: Liste von (insert, boundary, attributes, is_rectangle) Tupeln
|
||||||
error_collector: ErrorCollector für Fehlerbehandlung
|
meldungen: Meldungen (neue_meldungen) fuer Fehler/Warnungen
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Liste von Dictionaries mit 'layer', 'position', 'io' für jedes gefundene Symbol
|
Liste von Dictionaries mit 'layer', 'position', 'io' für jedes gefundene Symbol
|
||||||
@@ -842,7 +871,7 @@ def find_template_symbols_in_geometry(doc, msp, group_blocks, error_collector):
|
|||||||
symbols_in_geometry = []
|
symbols_in_geometry = []
|
||||||
for insert, boundary, attributes, is_rectangle in group_blocks:
|
for insert, boundary, attributes, is_rectangle in group_blocks:
|
||||||
for entity in msp.query('INSERT'):
|
for entity in msp.query('INSERT'):
|
||||||
symbol_attribs = extract_block_attributes(doc, entity, error_collector)
|
symbol_attribs = extract_block_attributes(doc, entity, meldungen)
|
||||||
if not symbol_attribs:
|
if not symbol_attribs:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -862,7 +891,7 @@ def find_template_symbols_in_geometry(doc, msp, group_blocks, error_collector):
|
|||||||
|
|
||||||
|
|
||||||
def create_no_symbols_found_error_message(block_name, direction, layer_names, group_blocks,
|
def create_no_symbols_found_error_message(block_name, direction, layer_names, group_blocks,
|
||||||
doc, msp, error_collector):
|
doc, msp, meldungen):
|
||||||
"""
|
"""
|
||||||
Erstellt eine detaillierte Fehlermeldung wenn keine Symbole in einem Renamer-Bereich gefunden wurden.
|
Erstellt eine detaillierte Fehlermeldung wenn keine Symbole in einem Renamer-Bereich gefunden wurden.
|
||||||
|
|
||||||
@@ -873,7 +902,7 @@ def create_no_symbols_found_error_message(block_name, direction, layer_names, gr
|
|||||||
group_blocks: Liste von (insert, boundary, attributes, is_rectangle) Tupeln
|
group_blocks: Liste von (insert, boundary, attributes, is_rectangle) Tupeln
|
||||||
doc: DXF-Dokument
|
doc: DXF-Dokument
|
||||||
msp: Modelspace
|
msp: Modelspace
|
||||||
error_collector: ErrorCollector für Fehlerbehandlung
|
meldungen: Meldungen (neue_meldungen) fuer Fehler/Warnungen
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Fehlermeldungs-String
|
Fehlermeldungs-String
|
||||||
@@ -882,7 +911,7 @@ def create_no_symbols_found_error_message(block_name, direction, layer_names, gr
|
|||||||
for insert, _, _, _ in group_blocks]
|
for insert, _, _, _ in group_blocks]
|
||||||
|
|
||||||
# Prüfe ob es Symbole mit @ gibt, die geometrisch im Bereich liegen (egal auf welchem Layer)
|
# Prüfe ob es Symbole mit @ gibt, die geometrisch im Bereich liegen (egal auf welchem Layer)
|
||||||
symbols_in_geometry = find_template_symbols_in_geometry(doc, msp, group_blocks, error_collector)
|
symbols_in_geometry = find_template_symbols_in_geometry(doc, msp, group_blocks, meldungen)
|
||||||
|
|
||||||
# Erstelle Fehlermeldung basierend auf gefundenen Symbolen
|
# Erstelle Fehlermeldung basierend auf gefundenen Symbolen
|
||||||
if symbols_in_geometry and not layer_names:
|
if symbols_in_geometry and not layer_names:
|
||||||
@@ -917,7 +946,7 @@ def create_no_symbols_found_error_message(block_name, direction, layer_names, gr
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def collect_symbols_from_group(doc, msp, renamer_layers, group_blocks, block_name, error_collector, polyline_max_distance=500.0):
|
def collect_symbols_from_group(doc, msp, renamer_layers, group_blocks, block_name, meldungen, polyline_max_distance=500.0):
|
||||||
"""
|
"""
|
||||||
Sammelt alle Symbole aus allen Boundaries einer Renamer-Gruppe.
|
Sammelt alle Symbole aus allen Boundaries einer Renamer-Gruppe.
|
||||||
Prüft auf Duplikate (Symbole in mehreren Bereichen).
|
Prüft auf Duplikate (Symbole in mehreren Bereichen).
|
||||||
@@ -928,7 +957,7 @@ def collect_symbols_from_group(doc, msp, renamer_layers, group_blocks, block_nam
|
|||||||
renamer_layers: Layer auf denen nach Renamer-Blöcken gesucht wird
|
renamer_layers: Layer auf denen nach Renamer-Blöcken gesucht wird
|
||||||
group_blocks: Liste von (insert, boundary, attributes, is_rectangle) Tupeln
|
group_blocks: Liste von (insert, boundary, attributes, is_rectangle) Tupeln
|
||||||
block_name: Name des Renamer-Blocks (für Fehlermeldungen)
|
block_name: Name des Renamer-Blocks (für Fehlermeldungen)
|
||||||
error_collector: ErrorCollector für Fehlerbehandlung
|
meldungen: Meldungen (neue_meldungen) fuer Fehler/Warnungen
|
||||||
polyline_max_distance: Maximale Distanz zur Polylinie (aus Config)
|
polyline_max_distance: Maximale Distanz zur Polylinie (aus Config)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -939,7 +968,7 @@ def collect_symbols_from_group(doc, msp, renamer_layers, group_blocks, block_nam
|
|||||||
|
|
||||||
for insert, boundary, attributes, is_rectangle in group_blocks:
|
for insert, boundary, attributes, is_rectangle in group_blocks:
|
||||||
# Finde Symbole innerhalb dieses Bereichs
|
# Finde Symbole innerhalb dieses Bereichs
|
||||||
symbols = find_symbols_in_boundary(doc, msp, boundary, renamer_layers, attributes, error_collector, polyline_max_distance)
|
symbols = find_symbols_in_boundary(doc, msp, boundary, renamer_layers, attributes, meldungen, polyline_max_distance)
|
||||||
|
|
||||||
# Füge Symbole hinzu, aber nur wenn das Entity noch nicht gesehen wurde
|
# Füge Symbole hinzu, aber nur wenn das Entity noch nicht gesehen wurde
|
||||||
for symbol in symbols:
|
for symbol in symbols:
|
||||||
@@ -962,12 +991,12 @@ def collect_symbols_from_group(doc, msp, renamer_layers, group_blocks, block_nam
|
|||||||
f"{', '.join([f'({p[0]:.1f}, {p[1]:.1f})' for p in existing_positions])}"
|
f"{', '.join([f'({p[0]:.1f}, {p[1]:.1f})' for p in existing_positions])}"
|
||||||
)
|
)
|
||||||
print(f" FEHLER: {error_msg}")
|
print(f" FEHLER: {error_msg}")
|
||||||
error_collector.add_errors({"symbol_in_multiple_boundaries": error_msg})
|
meldungen["errors"].append(("symbol_in_multiple_boundaries", error_msg))
|
||||||
|
|
||||||
return all_symbols
|
return all_symbols
|
||||||
|
|
||||||
|
|
||||||
def process_renamer_groups(doc, msp, renamer_layers, renamer_groups, error_collector, polyline_max_distance=500.0):
|
def process_renamer_groups(doc, msp, renamer_layers, renamer_groups, meldungen, polyline_max_distance=500.0):
|
||||||
"""
|
"""
|
||||||
Zweiter Pass: Verarbeitet jede Renamer-Block-Gruppe.
|
Zweiter Pass: Verarbeitet jede Renamer-Block-Gruppe.
|
||||||
Sammelt Symbole, sortiert sie und nummeriert sie.
|
Sammelt Symbole, sortiert sie und nummeriert sie.
|
||||||
@@ -992,7 +1021,7 @@ def process_renamer_groups(doc, msp, renamer_layers, renamer_groups, error_colle
|
|||||||
print(f"\n Verarbeite Gruppe: {block_name} ({len(group_blocks)} Renamer-Blöcke)")
|
print(f"\n Verarbeite Gruppe: {block_name} ({len(group_blocks)} Renamer-Blöcke)")
|
||||||
|
|
||||||
# Sammle Symbole aus ALLEN Boundaries dieser Gruppe
|
# Sammle Symbole aus ALLEN Boundaries dieser Gruppe
|
||||||
all_symbols = collect_symbols_from_group(doc, msp, renamer_layers, group_blocks, block_name, error_collector, polyline_max_distance)
|
all_symbols = collect_symbols_from_group(doc, msp, renamer_layers, group_blocks, block_name, meldungen, polyline_max_distance)
|
||||||
|
|
||||||
print(f" Gesamt {len(all_symbols)} eindeutige Template-Symbole aus {len(group_blocks)} Blöcken")
|
print(f" Gesamt {len(all_symbols)} eindeutige Template-Symbole aus {len(group_blocks)} Blöcken")
|
||||||
|
|
||||||
@@ -1000,10 +1029,10 @@ def process_renamer_groups(doc, msp, renamer_layers, renamer_groups, error_colle
|
|||||||
# Fehlermeldung: Keine Symbole gefunden
|
# Fehlermeldung: Keine Symbole gefunden
|
||||||
layer_names = get_layer_names_from_attributes(reference_attributes)
|
layer_names = get_layer_names_from_attributes(reference_attributes)
|
||||||
error_msg = create_no_symbols_found_error_message(
|
error_msg = create_no_symbols_found_error_message(
|
||||||
block_name, direction, layer_names, group_blocks, doc, msp, error_collector
|
block_name, direction, layer_names, group_blocks, doc, msp, meldungen
|
||||||
)
|
)
|
||||||
print(f" FEHLER: {error_msg}")
|
print(f" FEHLER: {error_msg}")
|
||||||
error_collector.add_errors({"no_symbols_found": error_msg})
|
meldungen["errors"].append(("no_symbols_found", error_msg))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Für POLYLINE_PATH: Hole die Polylinie vom ersten Block
|
# Für POLYLINE_PATH: Hole die Polylinie vom ersten Block
|
||||||
@@ -1029,21 +1058,21 @@ def process_renamer_groups(doc, msp, renamer_layers, renamer_groups, error_colle
|
|||||||
return all_renamed
|
return all_renamed
|
||||||
|
|
||||||
|
|
||||||
def find_symbols_with_at_in_io(doc, msp, error_collector):
|
def find_symbols_with_at_in_io(doc, msp, meldungen):
|
||||||
"""
|
"""
|
||||||
Findet alle Symbole mit "@" im IO-Attribut.
|
Findet alle Symbole mit "@" im IO-Attribut.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
doc: DXF-Dokument
|
doc: DXF-Dokument
|
||||||
msp: Modelspace
|
msp: Modelspace
|
||||||
error_collector: ErrorCollector für Fehlerbehandlung
|
meldungen: Meldungen (neue_meldungen) fuer Fehler/Warnungen
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Liste von Dictionaries mit Symbol-Informationen
|
Liste von Dictionaries mit Symbol-Informationen
|
||||||
"""
|
"""
|
||||||
symbols_with_at = []
|
symbols_with_at = []
|
||||||
for entity in msp.query('INSERT'):
|
for entity in msp.query('INSERT'):
|
||||||
symbol_attribs = extract_block_attributes(doc, entity, error_collector)
|
symbol_attribs = extract_block_attributes(doc, entity, meldungen)
|
||||||
if not symbol_attribs:
|
if not symbol_attribs:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -1138,7 +1167,7 @@ def create_wrong_layer_warning(symbol, wrong_layer_info):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def check_symbols_in_boundaries(doc, msp, renamer_groups, error_collector, polyline_max_distance=500.0):
|
def check_symbols_in_boundaries(doc, msp, renamer_groups, meldungen, polyline_max_distance=500.0):
|
||||||
"""
|
"""
|
||||||
Dritter Pass: Prüft ob alle Symbole mit "@" im IO in einem Renamer-Bereich liegen.
|
Dritter Pass: Prüft ob alle Symbole mit "@" im IO in einem Renamer-Bereich liegen.
|
||||||
|
|
||||||
@@ -1156,7 +1185,7 @@ def check_symbols_in_boundaries(doc, msp, renamer_groups, error_collector, polyl
|
|||||||
all_boundaries.append((boundary, attributes, insert.dxf.insert))
|
all_boundaries.append((boundary, attributes, insert.dxf.insert))
|
||||||
|
|
||||||
# Finde alle Symbole mit "@" im IO-Attribut
|
# Finde alle Symbole mit "@" im IO-Attribut
|
||||||
symbols_with_at = find_symbols_with_at_in_io(doc, msp, error_collector)
|
symbols_with_at = find_symbols_with_at_in_io(doc, msp, meldungen)
|
||||||
|
|
||||||
# Prüfe für jedes Symbol, ob es in mindestens einem Renamer-Bereich liegt
|
# Prüfe für jedes Symbol, ob es in mindestens einem Renamer-Bereich liegt
|
||||||
symbols_not_in_any_boundary = []
|
symbols_not_in_any_boundary = []
|
||||||
@@ -1180,7 +1209,7 @@ def check_symbols_in_boundaries(doc, msp, renamer_groups, error_collector, polyl
|
|||||||
# Warnung: Symbol liegt geometrisch im Bereich, aber Layer stimmt nicht
|
# Warnung: Symbol liegt geometrisch im Bereich, aber Layer stimmt nicht
|
||||||
warning_msg = create_wrong_layer_warning(symbol, wrong_layer_info)
|
warning_msg = create_wrong_layer_warning(symbol, wrong_layer_info)
|
||||||
print(f" WARNUNG: {warning_msg}")
|
print(f" WARNUNG: {warning_msg}")
|
||||||
error_collector.add_warnings({"symbol_wrong_layer": warning_msg})
|
meldungen["warnings"].append(("symbol_wrong_layer", warning_msg))
|
||||||
# NICHT als Fehler hinzufügen - Warnung reicht aus
|
# NICHT als Fehler hinzufügen - Warnung reicht aus
|
||||||
else:
|
else:
|
||||||
# Fehler: Symbol liegt weder geometrisch noch vom Layer her in einem Renamer-Bereich
|
# Fehler: Symbol liegt weder geometrisch noch vom Layer her in einem Renamer-Bereich
|
||||||
@@ -1195,7 +1224,7 @@ def check_symbols_in_boundaries(doc, msp, renamer_groups, error_collector, polyl
|
|||||||
f"enthält '@' im IO-Attribut, liegt aber in keinem Renamer-Bereich."
|
f"enthält '@' im IO-Attribut, liegt aber in keinem Renamer-Bereich."
|
||||||
)
|
)
|
||||||
print(f" FEHLER: {error_msg}")
|
print(f" FEHLER: {error_msg}")
|
||||||
error_collector.add_errors({"symbol_with_at_not_in_boundary": error_msg})
|
meldungen["errors"].append(("symbol_with_at_not_in_boundary", error_msg))
|
||||||
else:
|
else:
|
||||||
print(f" Alle {len(symbols_with_at)} Symbole mit '@' im IO liegen in einem Renamer-Bereich.")
|
print(f" Alle {len(symbols_with_at)} Symbole mit '@' im IO liegen in einem Renamer-Bereich.")
|
||||||
|
|
||||||
@@ -1231,14 +1260,14 @@ def rectangles_overlap(boundary1, boundary2):
|
|||||||
return not no_overlap
|
return not no_overlap
|
||||||
|
|
||||||
|
|
||||||
def check_overlapping_renamer_blocks(renamer_groups, error_collector):
|
def check_overlapping_renamer_blocks(renamer_groups, meldungen):
|
||||||
"""
|
"""
|
||||||
Prüft ob sich Renamer-Rechtecke überlappen.
|
Prüft ob sich Renamer-Rechtecke überlappen.
|
||||||
Polyline-Pfade (DIRECTION=POLYLINE_PATH) werden nicht geprüft.
|
Polyline-Pfade (DIRECTION=POLYLINE_PATH) werden nicht geprüft.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
renamer_groups: Dictionary mit gruppierten Renamer-Blöcken
|
renamer_groups: Dictionary mit gruppierten Renamer-Blöcken
|
||||||
error_collector: ErrorCollector für Fehlerbehandlung
|
meldungen: Meldungen (neue_meldungen) fuer Fehler/Warnungen
|
||||||
"""
|
"""
|
||||||
print("\n" + "="*60)
|
print("\n" + "="*60)
|
||||||
print("Prüfe auf überlappende Renamer-Bereiche")
|
print("Prüfe auf überlappende Renamer-Bereiche")
|
||||||
@@ -1287,12 +1316,12 @@ def check_overlapping_renamer_blocks(renamer_groups, error_collector):
|
|||||||
f"Symbole könnten mehrfach nummeriert werden."
|
f"Symbole könnten mehrfach nummeriert werden."
|
||||||
)
|
)
|
||||||
print(f" {error_msg}")
|
print(f" {error_msg}")
|
||||||
error_collector.add_errors({"overlapping_renamer_blocks": error_msg})
|
meldungen["errors"].append(("overlapping_renamer_blocks", error_msg))
|
||||||
else:
|
else:
|
||||||
print(f"\nOK: Keine Überlappungen zwischen {len(rectangle_renamers)} Renamer-Rechtecken gefunden")
|
print(f"\nOK: Keine Überlappungen zwischen {len(rectangle_renamers)} Renamer-Rechtecken gefunden")
|
||||||
|
|
||||||
|
|
||||||
def process_renamer_blocks(doc, msp, renamer_layers, error_collector, polyline_max_distance=500.0):
|
def process_renamer_blocks(doc, msp, renamer_layers, meldungen, polyline_max_distance=500.0):
|
||||||
"""
|
"""
|
||||||
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
|
||||||
@@ -1307,21 +1336,21 @@ def process_renamer_blocks(doc, msp, renamer_layers, error_collector, polyline_m
|
|||||||
- renamer_groups: Dictionary der gruppierten Renamer-Blöcke
|
- 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, meldungen)
|
||||||
|
|
||||||
# Prüfe auf überlappende Renamer-Bereiche
|
# Prüfe auf überlappende Renamer-Bereiche
|
||||||
check_overlapping_renamer_blocks(renamer_groups, error_collector)
|
check_overlapping_renamer_blocks(renamer_groups, meldungen)
|
||||||
|
|
||||||
# Zweiter Pass: Verarbeite jede Gruppe
|
# Zweiter Pass: Verarbeite jede Gruppe
|
||||||
all_renamed = process_renamer_groups(doc, msp, renamer_layers, renamer_groups, error_collector, polyline_max_distance)
|
all_renamed = process_renamer_groups(doc, msp, renamer_layers, renamer_groups, meldungen, polyline_max_distance)
|
||||||
|
|
||||||
# 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, polyline_max_distance)
|
check_symbols_in_boundaries(doc, msp, renamer_groups, meldungen, polyline_max_distance)
|
||||||
|
|
||||||
return all_renamed, renamer_groups
|
return all_renamed, renamer_groups
|
||||||
|
|
||||||
|
|
||||||
def collect_all_symbols_from_groups(doc, msp, renamer_groups, renamer_layers, error_collector, polyline_max_distance=500.0):
|
def collect_all_symbols_from_groups(doc, msp, renamer_groups, renamer_layers, meldungen, polyline_max_distance=500.0):
|
||||||
"""
|
"""
|
||||||
Sammelt alle Symbole aus allen Renamer-Gruppen für Visualisierungszwecke.
|
Sammelt alle Symbole aus allen Renamer-Gruppen für Visualisierungszwecke.
|
||||||
|
|
||||||
@@ -1330,7 +1359,7 @@ def collect_all_symbols_from_groups(doc, msp, renamer_groups, renamer_layers, er
|
|||||||
msp: Modelspace
|
msp: Modelspace
|
||||||
renamer_groups: Dictionary mit gruppierten Renamer-Blöcken
|
renamer_groups: Dictionary mit gruppierten Renamer-Blöcken
|
||||||
renamer_layers: Liste der Layer mit Renamer-Blöcken
|
renamer_layers: Liste der Layer mit Renamer-Blöcken
|
||||||
error_collector: ErrorCollector für Fehlerbehandlung
|
meldungen: Meldungen (neue_meldungen) fuer Fehler/Warnungen
|
||||||
polyline_max_distance: Maximale Distanz zur Polylinie (aus Config)
|
polyline_max_distance: Maximale Distanz zur Polylinie (aus Config)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -1347,7 +1376,7 @@ def collect_all_symbols_from_groups(doc, msp, renamer_groups, renamer_layers, er
|
|||||||
|
|
||||||
for insert, boundary, attributes, is_rectangle in group_blocks:
|
for insert, boundary, attributes, is_rectangle in group_blocks:
|
||||||
# Finde Symbole innerhalb dieses Bereichs
|
# Finde Symbole innerhalb dieses Bereichs
|
||||||
symbols = find_symbols_in_boundary(doc, msp, boundary, renamer_layers, attributes, error_collector, polyline_max_distance)
|
symbols = find_symbols_in_boundary(doc, msp, boundary, renamer_layers, attributes, meldungen, polyline_max_distance)
|
||||||
|
|
||||||
# Füge Symbole hinzu, aber nur wenn das Entity noch nicht gesehen wurde
|
# Füge Symbole hinzu, aber nur wenn das Entity noch nicht gesehen wurde
|
||||||
for symbol in symbols:
|
for symbol in symbols:
|
||||||
@@ -1371,12 +1400,12 @@ def process_single_file(dxf_path, work_dir, renamer_layers, polyline_max_distanc
|
|||||||
args: Argparse-Argumente
|
args: Argparse-Argumente
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tuple (success, error_collector):
|
Tuple (success, meldungen):
|
||||||
- success: True wenn erfolgreich verarbeitet
|
- success: True wenn erfolgreich verarbeitet
|
||||||
- error_collector: ErrorCollector mit gesammelten Fehlern/Warnungen
|
- meldungen: Meldungen (neue_meldungen) mit gesammelten Fehlern/Warnungen
|
||||||
"""
|
"""
|
||||||
# Initialisiere ErrorCollector für diese Datei
|
# Meldungen fuer diese Datei
|
||||||
error_collector = ErrorCollector()
|
meldungen = neue_meldungen()
|
||||||
|
|
||||||
print(f"\n{'='*80}")
|
print(f"\n{'='*80}")
|
||||||
print(f"Verarbeite Datei: {dxf_path.name}")
|
print(f"Verarbeite Datei: {dxf_path.name}")
|
||||||
@@ -1397,7 +1426,7 @@ def process_single_file(dxf_path, work_dir, renamer_layers, polyline_max_distanc
|
|||||||
print("Starte Verarbeitung der Renamer-Blöcke")
|
print("Starte Verarbeitung der Renamer-Blöcke")
|
||||||
print("="*60 + "\n")
|
print("="*60 + "\n")
|
||||||
|
|
||||||
renamed_symbols, renamer_groups = process_renamer_blocks(doc, msp, renamer_layers, error_collector, polyline_max_distance)
|
renamed_symbols, renamer_groups = process_renamer_blocks(doc, msp, renamer_layers, meldungen, polyline_max_distance)
|
||||||
|
|
||||||
print("\n" + "="*60)
|
print("\n" + "="*60)
|
||||||
print(f"Verarbeitung abgeschlossen: {len(renamed_symbols)} Symbole nummeriert")
|
print(f"Verarbeitung abgeschlossen: {len(renamed_symbols)} Symbole nummeriert")
|
||||||
@@ -1411,7 +1440,7 @@ def process_single_file(dxf_path, work_dir, renamer_layers, polyline_max_distanc
|
|||||||
|
|
||||||
# Zeichne Symbol-Rahmen wenn gewünscht
|
# Zeichne Symbol-Rahmen wenn gewünscht
|
||||||
if args.show_symbol_frames:
|
if args.show_symbol_frames:
|
||||||
all_symbols = collect_all_symbols_from_groups(doc, msp, renamer_groups, renamer_layers, error_collector, polyline_max_distance)
|
all_symbols = collect_all_symbols_from_groups(doc, msp, renamer_groups, renamer_layers, meldungen, polyline_max_distance)
|
||||||
if all_symbols:
|
if all_symbols:
|
||||||
draw_symbol_frames(doc, msp, all_symbols)
|
draw_symbol_frames(doc, msp, all_symbols)
|
||||||
else:
|
else:
|
||||||
@@ -1467,9 +1496,9 @@ def process_single_file(dxf_path, work_dir, renamer_layers, polyline_max_distanc
|
|||||||
print(f"Ergebnisse in JSON gespeichert: {output_dir / output_filename}")
|
print(f"Ergebnisse in JSON gespeichert: {output_dir / output_filename}")
|
||||||
|
|
||||||
# Schreibe Fehler-Datei nur wenn Fehler oder Warnungen vorhanden sind
|
# Schreibe Fehler-Datei nur wenn Fehler oder Warnungen vorhanden sind
|
||||||
if error_collector.has_errors_or_warnings():
|
if (meldungen["errors"] or meldungen["warnings"]):
|
||||||
if args.errorfile:
|
if args.errorfile:
|
||||||
error_data = error_collector.get_all_issues()
|
error_data = meldungen_als_dict(meldungen)
|
||||||
error_path = Path(args.errorfile)
|
error_path = Path(args.errorfile)
|
||||||
|
|
||||||
# Wenn nur ein Dateiname ohne Pfad angegeben wurde, speichere im work-Ordner
|
# Wenn nur ein Dateiname ohne Pfad angegeben wurde, speichere im work-Ordner
|
||||||
@@ -1486,19 +1515,19 @@ def process_single_file(dxf_path, work_dir, renamer_layers, polyline_max_distanc
|
|||||||
else:
|
else:
|
||||||
# Keine explizite Fehlerdatei angegeben, generiere eine basierend auf Input-Datei
|
# Keine explizite Fehlerdatei angegeben, generiere eine basierend auf Input-Datei
|
||||||
error_filename = f"{dxf_path.stem}_errors.json"
|
error_filename = f"{dxf_path.stem}_errors.json"
|
||||||
error_data = error_collector.get_all_issues()
|
error_data = meldungen_als_dict(meldungen)
|
||||||
write_json_file(error_data, dxf_path.parent, error_filename)
|
write_json_file(error_data, dxf_path.parent, error_filename)
|
||||||
print(f"Fehler/Warnungen gespeichert: {dxf_path.parent / error_filename}")
|
print(f"Fehler/Warnungen gespeichert: {dxf_path.parent / error_filename}")
|
||||||
|
|
||||||
# Status-Meldung
|
# Status-Meldung
|
||||||
if error_collector.has_errors():
|
if meldungen["errors"]:
|
||||||
print("\n!!! Es sind Fehler aufgetreten !!!")
|
print("\n!!! Es sind Fehler aufgetreten !!!")
|
||||||
return False, error_collector
|
return False, meldungen
|
||||||
elif error_collector.has_errors_or_warnings():
|
elif (meldungen["errors"] or meldungen["warnings"]):
|
||||||
print("\n(Warnungen vorhanden, aber keine kritischen Fehler)")
|
print("\n(Warnungen vorhanden, aber keine kritischen Fehler)")
|
||||||
return True, error_collector
|
return True, meldungen
|
||||||
else:
|
else:
|
||||||
return True, error_collector
|
return True, meldungen
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None):
|
def main(argv=None):
|
||||||
@@ -1552,9 +1581,9 @@ def main(argv=None):
|
|||||||
print(f"Datei nicht gefunden: {dxf_path}")
|
print(f"Datei nicht gefunden: {dxf_path}")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
_success, error_collector = process_single_file(dxf_path, dxf_path.parent, renamer_layers,
|
_success, meldungen = process_single_file(dxf_path, dxf_path.parent, renamer_layers,
|
||||||
polyline_max_distance, args)
|
polyline_max_distance, args)
|
||||||
return 1 if error_collector.has_errors() else 0
|
return 1 if meldungen["errors"] else 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -1,121 +0,0 @@
|
|||||||
"""
|
|
||||||
ErrorCollector - Gemeinsame Klasse zum Sammeln von Fehlern und Warnungen
|
|
||||||
|
|
||||||
Diese Klasse wird von getpositions.py und drawdxf.py verwendet, um Fehler
|
|
||||||
und Warnungen während der Verarbeitung zu sammeln und zu verwalten.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
|
|
||||||
class ErrorCollector:
|
|
||||||
"""Sammelt alle Fehler und Warnungen während der Verarbeitung."""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.errors = {}
|
|
||||||
self.warnings = {}
|
|
||||||
|
|
||||||
def add_errors(self, error_dict):
|
|
||||||
"""Fügt Fehler aus einem Dictionary hinzu."""
|
|
||||||
for key, value in error_dict.items():
|
|
||||||
if key in self.errors:
|
|
||||||
# Key existiert bereits - Verhalten hängt vom Typ ab
|
|
||||||
existing = self.errors[key]
|
|
||||||
|
|
||||||
if isinstance(existing, dict) and isinstance(value, dict):
|
|
||||||
# Beide sind Dictionaries → Merge/Update
|
|
||||||
existing.update(value)
|
|
||||||
elif isinstance(existing, list):
|
|
||||||
# Existierender Wert ist Liste
|
|
||||||
if isinstance(value, list):
|
|
||||||
existing.extend(value)
|
|
||||||
else:
|
|
||||||
existing.append(value)
|
|
||||||
else:
|
|
||||||
# Existierender Wert ist weder dict noch list → Mache Liste daraus
|
|
||||||
if isinstance(value, list):
|
|
||||||
self.errors[key] = [existing] + value
|
|
||||||
else:
|
|
||||||
self.errors[key] = [existing, value]
|
|
||||||
else:
|
|
||||||
# Neuer Key - behalte Typ des Wertes
|
|
||||||
if isinstance(value, (dict, list)):
|
|
||||||
# Dictionary oder Liste → direkt speichern
|
|
||||||
self.errors[key] = value
|
|
||||||
else:
|
|
||||||
# Primitiver Wert (String, etc.) → als Liste speichern
|
|
||||||
self.errors[key] = [value]
|
|
||||||
|
|
||||||
def add_warnings(self, warning_dict):
|
|
||||||
"""Fügt Warnungen aus einem Dictionary hinzu."""
|
|
||||||
for key, value in warning_dict.items():
|
|
||||||
if key in self.warnings:
|
|
||||||
# Key existiert bereits - Verhalten hängt vom Typ ab
|
|
||||||
existing = self.warnings[key]
|
|
||||||
|
|
||||||
if isinstance(existing, dict) and isinstance(value, dict):
|
|
||||||
# Beide sind Dictionaries → Merge/Update
|
|
||||||
existing.update(value)
|
|
||||||
elif isinstance(existing, list):
|
|
||||||
# Existierender Wert ist Liste
|
|
||||||
if isinstance(value, list):
|
|
||||||
existing.extend(value)
|
|
||||||
else:
|
|
||||||
existing.append(value)
|
|
||||||
else:
|
|
||||||
# Existierender Wert ist weder dict noch list → Mache Liste daraus
|
|
||||||
if isinstance(value, list):
|
|
||||||
self.warnings[key] = [existing] + value
|
|
||||||
else:
|
|
||||||
self.warnings[key] = [existing, value]
|
|
||||||
else:
|
|
||||||
# Neuer Key - behalte Typ des Wertes
|
|
||||||
if isinstance(value, (dict, list)):
|
|
||||||
# Dictionary oder Liste → direkt speichern
|
|
||||||
self.warnings[key] = value
|
|
||||||
else:
|
|
||||||
# Primitiver Wert (String, etc.) → als Liste speichern
|
|
||||||
self.warnings[key] = [value]
|
|
||||||
|
|
||||||
def has_errors_or_warnings(self):
|
|
||||||
"""Prüft ob Fehler oder Warnungen vorhanden sind."""
|
|
||||||
return bool(self.errors) or bool(self.warnings)
|
|
||||||
|
|
||||||
def has_errors(self):
|
|
||||||
"""Prüft ob Fehler vorhanden sind."""
|
|
||||||
return bool(self.errors)
|
|
||||||
|
|
||||||
def get_all_issues(self):
|
|
||||||
"""Gibt alle Fehler und Warnungen als Dictionary zurück."""
|
|
||||||
result = {}
|
|
||||||
if self.errors:
|
|
||||||
result["errors"] = self.errors
|
|
||||||
if self.warnings:
|
|
||||||
result["warnings"] = self.warnings
|
|
||||||
return result
|
|
||||||
|
|
||||||
def write_errorfile(self, work_dir: Path, filename: str):
|
|
||||||
"""Schreibt Fehlerdatei nur wenn Fehler oder Warnungen vorhanden sind."""
|
|
||||||
if self.has_errors():
|
|
||||||
print("Fehler gefunden. Schreibe Error-Datei.")
|
|
||||||
all_issues = self.get_all_issues()
|
|
||||||
write_json_file(all_issues, work_dir, filename)
|
|
||||||
else:
|
|
||||||
print("Keine Fehler gefunden. Keine Error-Datei geschrieben.")
|
|
||||||
|
|
||||||
|
|
||||||
def write_json_file(data: dict, out_dir: Path, filename: str) -> None:
|
|
||||||
"""
|
|
||||||
Schreibt Daten in eine JSON-Datei.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
data: Dictionary mit zu schreibenden Daten
|
|
||||||
out_dir: Ausgabeverzeichnis
|
|
||||||
filename: Dateiname für die JSON-Datei
|
|
||||||
"""
|
|
||||||
print("writing data to a json file ...")
|
|
||||||
outfile = out_dir / filename if isinstance(out_dir, Path) else Path(out_dir) / filename
|
|
||||||
with open(outfile, 'w', encoding='utf-8') as fh:
|
|
||||||
json.dump(data, fh, indent=2, ensure_ascii=False, default=str)
|
|
||||||
print("done")
|
|
||||||
Reference in new issue
Block a user