diff --git a/cfg/shapes.cfg b/cfg/shapes.cfg deleted file mode 100644 index 430157a..0000000 --- a/cfg/shapes.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[simple_types] -shape_names = ILS 2.0 Kreisel \ No newline at end of file diff --git a/lib/placeblocks.py b/lib/placeblocks.py deleted file mode 100644 index 707f397..0000000 --- a/lib/placeblocks.py +++ /dev/null @@ -1,180 +0,0 @@ -""" -placeblocks.py -Erzeugt DXF-Elemente aus einer RuleDesigner-CSV. -Einfache Formen (z.B. "ILS 2.0 Kreisel") werden direkt konstruiert, -komplexe per Blockreferenz aus einer DXF-Bibliothek eingefügt. -""" - -import os -import sys -import csv -import json -import re -import argparse -import configparser -import ezdxf -from pathlib import Path - -# --------------------------------------------------------- Konstante Parameter -ATTR_TAG = "TeileId" # Attributtag im Block -RADIUS = 400 # Radius der Kreiselkreise (mm) - -# --------------------------------------------------------- Hilfsfunktionen -def check_environment_var(env_str: str) -> Path: - """Liefert Path aus Umgebungsvariable oder beendet mit Fehlermeldung.""" - out_path = os.environ.get(env_str) - if out_path: - return Path(out_path) - print(f"Umgebungsvariable {env_str} ist nicht gesetzt oder leer.") - sys.exit(1) - -def extract_coords(planquadrat: str) -> tuple[float, float]: - """Extrahiert X/Y Koordinaten aus PlanquadratString.""" - m = re.search(r"X:(\d+[\.,]?\d*)\s+Y:(\d+[\.,]?\d*)", planquadrat) - if not m: - raise ValueError(f"Koordinaten nicht gefunden in: '{planquadrat}'") - x, y = m.groups() - return float(x.replace(",", ".")), float(y.replace(",", ".")) - -def parse_merkmale(merkmale_str: str) -> dict: - """Parst Merkmale-JSON-String in dict; bei Fehler → leeres Dict.""" - try: - return json.loads(merkmale_str) - except json.JSONDecodeError: - return {} - -def import_block(block_name: str, from_doc, to_doc) -> None: - """Importiert Blockdefinition block_name von from_doc nach to_doc.""" - if block_name in to_doc.blocks: - return - if block_name not in from_doc.blocks: - raise ValueError(f"Block '{block_name}' nicht in Bibliothek gefunden.") - src = from_doc.blocks[block_name] - tgt = to_doc.blocks.new(name=block_name, dxfattribs=src.dxf.attribs()) - for ent in src: - tgt.add_entity(ent.copy()) - -def build_simple_shape(msp, teileart: str, x: float, y: float, - teile_id: str, merkmale: dict): - """Erzeugt einfache Geometrien direkt im Modelspace.""" - if teileart == "ILS 2.0 Kreisel": - abstand_m = merkmale.get( - "Abstand (Kreiselachse A - Kreiselachse) in Meter", "20" - ).replace(",", ".") - try: - abstand = float(abstand_m) * 1000 # Meter → mm - except ValueError: - abstand = 20000 # Fallback 20 m - - cx1 = x - abstand / 2 - cx2 = x + abstand / 2 - - # Zwei Kreise + tangentiale Verbindungs­linien - msp.add_circle((cx1, y), RADIUS) - msp.add_circle((cx2, y), RADIUS) - msp.add_line((cx1, y - RADIUS), (cx2, y - RADIUS)) - msp.add_line((cx1, y + RADIUS), (cx2, y + RADIUS)) - - else: - raise NotImplementedError(f"Einfache Form '{teileart}' nicht implementiert.") - -def load_simple_types(cfg_path: Path) -> set[str]: - """Lädt die Liste der einfachen TeileArtNamen aus .cfg.""" - cfg = configparser.ConfigParser() - cfg.read(cfg_path) - names = cfg.get("simple_types", "shape_names", fallback="") - return {n.strip() for n in names.split(",") if n.strip()} - -# --------------------------------------------------------- Hauptfunktion -def main(csv_path: Path, lib_path: Path, cfg_path: Path, - output_path: Path, verbose=False): - - simple_types = load_simple_types(cfg_path) - - # Bibliothek nur laden, wenn Datei existiert - lib_doc = None - if lib_path.exists(): - try: - lib_doc = ezdxf.readfile(lib_path) - if verbose: - print(f"[INFO] Bibliothek geladen: {lib_path}") - except Exception as e: - sys.exit(f"Fehler beim Lesen der Bibliothek '{lib_path}': {e}") - else: - print(f"[INFO] Keine Bibliothek gefunden unter {lib_path}. " - "Komplexe Formen werden übersprungen.") - - # Neue Ziel­zeichnung (DXF R2018) - doc = ezdxf.new(dxfversion="R2018") - msp = doc.modelspace() - - # CSV einlesen - with csv_path.open(newline="", encoding="utf-8") as fh: - reader = csv.DictReader(fh, delimiter=';') - for row in reader: - teileart = row["TeileArt"].strip() - teileid = row["TeileId"].strip() - planquadrat = row["Planquadrat"] - merkmale = parse_merkmale(row.get("Merkmale", "")) - - try: - x, y = extract_coords(planquadrat) - except Exception as e: - print(f"[WARN] {teileid}: {e}") - continue - - # Einfache Form - if teileart in simple_types: - try: - build_simple_shape(msp, teileart, x, y, teileid, merkmale) - if verbose: - print(f"[INFO] Simple '{teileart}' → {teileid} " - f"({x:.1f}, {y:.1f})") - except Exception as e: - print(f"[ERROR] Simple '{teileart}' ({teileid}): {e}") - continue - - # Komplexe Form (Block) - if not lib_doc: - print(f"[WARN] '{teileart}' benötigt Bibliothek, wird übersprungen.") - continue - try: - import_block(teileart, lib_doc, doc) - bref = msp.add_blockref(teileart, (x, y)) - bref.add_auto_attribs({ATTR_TAG: teileid}) - if verbose: - print(f"[INFO] Block '{teileart}' → {teileid} " - f"({x:.1f}, {y:.1f})") - except Exception as e: - print(f"[ERROR] Block '{teileart}' ({teileid}): {e}") - - # DXF speichern - doc.saveas(output_path) - print(f"[DONE] DXF gespeichert unter: {output_path}") - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="Plaziert Anlagenkomponenten aus RuleDesigner CSV.") - parser.add_argument("-f", "--file", required=True, help="CSV-Datei (Name oder Pfad)", metavar="input.csv") - parser.add_argument("-c", "--config", help="CFG mit einfachen Formen", metavar="shapes.cfg") - parser.add_argument("-l", "--lib", help="DXF-Bibliothek mit Blöcken", metavar="bibliothek.dxf") - parser.add_argument("-o", "--output", help="Ziel-DXF (Standard: PROJECT_WORK/anlage.dxf)", metavar="anlage.dxf") - parser.add_argument("-v", "--verbose", action="store_true", help="mehr Ausgaben anzeigen") - args = parser.parse_args() - - # Verzeichnisse aus Umgebungs­variablen - data_dir = check_environment_var("PROJECT_DATA") - work_dir = check_environment_var("PROJECT_WORK") - config_dir = check_environment_var("PROJECT_CFG") - - # CSV‑Pfad: nur Dateiname → im WORK‑Ordner suchen - if os.sep not in args.file and "/" not in args.file: - csv_path = work_dir / args.file - else: - csv_path = Path(args.file) - - cfg_path = Path(args.config) if args.config else config_dir / "shapes.cfg" - lib_path = Path(args.lib) if args.lib else data_dir / "bibliothek.dxf" - output_path = Path(args.output) if args.output else (work_dir / f"{csv_path.stem}.dxf") - - main(csv_path, lib_path, cfg_path, output_path, verbose=args.verbose)