# omniflo.py # Omniflo Kern-Modul: JSON-Daten, DXF-Insert, Aluprofil, TEF-Dummies # Ersetzt den Kern von OmniModulInsert.lsp # ============================================ import os import math from datetime import datetime import PyRx as Rx import PyGe as Ge import PyDb as Db import PyEd as Ed from elemente.utils import ( get_data_path, load_json_data, get_block_path, ensure_block_from_dxf, ) # ============================================================ # JSON-Daten Cache # ============================================================ _boegen_data = None _weichen_data = None def load_boegen(): """omniflo_boegen.json laden und cachen.""" global _boegen_data if _boegen_data is not None: return _boegen_data _boegen_data = load_json_data("json/omniflo_boegen.json") print(f"\n[OMNI] {len(_boegen_data)} Boegen geladen.") return _boegen_data def load_weichen(): """omniflo_weichen.json laden und cachen.""" global _weichen_data if _weichen_data is not None: return _weichen_data _weichen_data = load_json_data("json/omniflo_weichen.json") print(f"\n[OMNI] {len(_weichen_data)} Weichen geladen.") return _weichen_data def filter_by(data, key, value): """Eintraege nach key/value filtern.""" return [e for e in data if e.get(key) == value] def get_by_sivasnr(data, sivasnr): """Eintrag anhand Sivasnr suchen.""" s = str(sivasnr) for e in data: if str(e.get("Sivasnr", "")) == s: return e return None def sivasnr_to_str(val): """SivasNr-Wert als String zurueckgeben.""" if val is None: return "" if isinstance(val, float): return str(int(val)) return str(val) # ============================================================ # DXF-Block einfuegen # ============================================================ def insert_dxf_block(sivasnr_str): """DXF aus data/omniflo/ als Block einfuegen. User waehlt Einfuegepunkt und Drehwinkel. Rueckgabe: Einfuegepunkt oder None bei Abbruch. """ dp = get_data_path() if not dp: return None dxf_path = f"{dp}/omniflo/{sivasnr_str}.dxf" ed = Ed.Editor() res_pt = ed.getPoint("\nEinfuegepunkt waehlen: ") if res_pt[0] != Ed.PromptStatus.eOk: print("\n[OMNI] Abgebrochen.") return None pt = res_pt[1] res_ang = ed.getAngle("\nDrehwinkel <0>: ", pt) angle_rad = res_ang[1] if res_ang[0] == Ed.PromptStatus.eOk else 0.0 db = Db.HostApplicationServices().workingDatabase() # DXF als Block-Definition laden if not ensure_block_from_dxf(db, dxf_path, sivasnr_str): return None # Block einfuegen bt = Db.BlockTable(db.blockTableId(), Db.OpenMode.kForRead) block_id = bt.getAt(sivasnr_str) model = Db.BlockTableRecord(db.modelSpaceId(), Db.OpenMode.kForWrite) bref = Db.BlockReference(pt, block_id) bref.setRotation(angle_rad) model.appendAcDbEntity(bref) print(f"\n[OMNI] {sivasnr_str} eingefuegt.") return pt def insert_from_entry(eintrag): """Eintrag aus Dialog-Ergebnis einfuegen (DXF anhand Sivasnr).""" if not eintrag: return None sivasnr_str = sivasnr_to_str(eintrag.get("Sivasnr")) profil = eintrag.get("ProfilTyp", "?") print(f"\n[OMNI] Einfuegen: {profil} ({sivasnr_str})") return insert_dxf_block(sivasnr_str) # ============================================================ # Aluprofil-Fahrstrecke einfuegen # ============================================================ def insert_aluprofil(blockname, laengemax): """Aluprofil-Fahrstrecke einfuegen (AP60/AP110/APG110). Ablauf: 1. Quell-Block (.dwg) laden und Attribute lesen 2. Nutzer waehlt Einfuegepunkt -> Text wird platziert 3. Nutzer waehlt Endpunkt -> Laengenvalidierung 4. Compound-Block aus Linie + ATTDEFs wird erzeugt 5. Text wird gedreht und oberhalb der Linie positioniert """ ed = Ed.Editor() db = Db.HostApplicationServices().workingDatabase() block_path = get_block_path() text_height = 100.0 text_gap = 20.0 # Quell-Block laden und Attribute lesen dwg_path = block_path + blockname + ".dwg" if not os.path.isfile(dwg_path): print(f"\n[OMNI] FEHLER: {dwg_path} nicht gefunden!") return src_block_name = f"_SRC_{blockname}" bt = Db.BlockTable(db.blockTableId(), Db.OpenMode.kForRead) if not bt.has(src_block_name): aux_db = Db.Database(False, True) aux_db.readDwg(dwg_path) db.insert(aux_db, src_block_name) # Attribute aus Quell-Block lesen src_attribs = {} bt = Db.BlockTable(db.blockTableId(), Db.OpenMode.kForRead) if bt.has(src_block_name): btr = Db.BlockTableRecord(bt.getAt(src_block_name), Db.OpenMode.kForRead) for ent_id in btr: ent = Db.Entity(ent_id, Db.OpenMode.kForRead) if ent.isKindOf(Db.AttributeDefinition.desc()): attdef = Db.AttributeDefinition(ent_id, Db.OpenMode.kForRead) src_attribs[attdef.tag()] = attdef.textString() # LAENGEMAX aus Quell-Attribut ueberschreiben falls vorhanden if "LAENGEMAX" in src_attribs and src_attribs["LAENGEMAX"]: try: laengemax = float(src_attribs["LAENGEMAX"]) except ValueError: pass # 1. Einfuegepunkt res_pt = ed.getPoint("\nEinfuegepunkt waehlen: ") if res_pt[0] != Ed.PromptStatus.eOk: print("\n[OMNI] Abgebrochen.") return pt = res_pt[1] z_val = pt.z if pt.z != 0 else 0.0 # Text-Entity am Einfuegepunkt erzeugen model = Db.BlockTableRecord(db.modelSpaceId(), Db.OpenMode.kForWrite) text_ent = Db.Text() text_ent.setPosition(Ge.Point3d(pt.x, pt.y, z_val)) text_ent.setHeight(text_height) text_ent.setTextString(blockname) text_ent.setRotation(0.0) model.appendAcDbEntity(text_ent) # 2. Endpunkt-Schleife mit Laengenvalidierung while True: res_end = ed.getPoint("\nEndpunkt der Fahrstrecke waehlen: ", pt) if res_end[0] != Ed.PromptStatus.eOk: text_ent.upgradeOpen() text_ent.erase() print("\n[OMNI] Abgebrochen.") return pt2 = res_end[1] dx = pt2.x - pt.x dy = pt2.y - pt.y laenge = math.sqrt(dx * dx + dy * dy) if laenge > laengemax: print(f"\n[OMNI] Fahrstreckenlaenge {laenge:.0f} mm ueberschreitet " f"Maximum von {laengemax:.0f} mm. Bitte neu definieren!") continue angle = math.atan2(dy, dx) laenge_str = f"{laenge:.0f}" # 3. Compound-Block erzeugen: Linie + Attribute timestamp = datetime.now().strftime("%Y%m%d%H%M%S") bname = f"{blockname}_{timestamp}" bt = Db.BlockTable(db.blockTableId(), Db.OpenMode.kForRead) if bt.has(bname): bname = f"{bname}_2" bt.upgradeOpen() new_btr = Db.BlockTableRecord() new_btr.setName(bname) bt.add(new_btr) # Linie von (0,0) bis (laenge, 0) line = Db.Line(Ge.Point3d(0, 0, 0), Ge.Point3d(laenge, 0, 0)) new_btr.appendAcDbEntity(line) # Attribute aus Quell-Block als ATTDEFs y_pos = 0.0 attdef_height = 50.0 for tag, default in src_attribs.items(): attdef = Db.AttributeDefinition() attdef.setPosition(Ge.Point3d(0, y_pos, 0)) attdef.setHeight(attdef_height) attdef.setTextString(default) attdef.setPrompt(tag) attdef.setTag(tag) attdef.setInvisible(True) new_btr.appendAcDbEntity(attdef) y_pos -= attdef_height * 2.0 # Block einfuegen block_id = bt.getAt(bname) bref = Db.BlockReference(pt, block_id) bref.setRotation(angle) model.appendAcDbEntity(bref) # Attribute setzen: Quell-Werte + LAENGE/A aktualisieren attrib_values = dict(src_attribs) attrib_values["LAENGE"] = laenge_str attrib_values["A"] = laenge_str btr_def = Db.BlockTableRecord(block_id, Db.OpenMode.kForRead) for ent_id in btr_def: ent = Db.Entity(ent_id, Db.OpenMode.kForRead) if ent.isKindOf(Db.AttributeDefinition.desc()): ad = Db.AttributeDefinition(ent_id, Db.OpenMode.kForRead) attref = Db.AttributeReference() attref.setPropertiesFrom(ad) attref.setTag(ad.tag()) attref.setInvisible(ad.isInvisible()) attref.setPosition(ad.position()) attref.setHeight(ad.height()) val = attrib_values.get(ad.tag(), ad.textString()) attref.setTextString(val) bref.appendAttribute(attref) # 4. Text aktualisieren: oberhalb der Linie positionieren perp_x = -math.sin(angle) * text_gap perp_y = math.cos(angle) * text_gap text_pt = Ge.Point3d(pt.x + perp_x, pt.y + perp_y, z_val) text_ent.upgradeOpen() text_ent.setPosition(text_pt) text_ent.setRotation(angle) text_ent.setTextString(f"{blockname} L={laenge_str}") print(f"\n[OMNI] {bname} eingefuegt. Laenge={laenge_str} mm") break # ============================================================ # Aluprofil-Commands # ============================================================ @Rx.command("OMNI_AP60") def cmd_omni_ap60(): insert_aluprofil("AP60", 7000.0) @Rx.command("OMNI_AP110") def cmd_omni_ap110(): insert_aluprofil("AP110", 6000.0) @Rx.command("OMNI_APG110") def cmd_omni_apg110(): insert_aluprofil("APG110", 6000.0) # ============================================================ # TEF / KettenFoerderer Dummy-Commands # ============================================================ @Rx.command("OMNI_TEF_UmlenkR") def cmd_tef_umlenkr(): print("\n[DUMMY] Umlenkspannst. rechts fuer TEF links") @Rx.command("OMNI_TEF_UmlenkL") def cmd_tef_umlenkl(): print("\n[DUMMY] Umlenkspannst. links fuer TEF rechts") @Rx.command("OMNI_TEF_AntriebL") def cmd_tef_antriebl(): print("\n[DUMMY] Antriebst. links fuer TEF links") @Rx.command("OMNI_TEF_AntriebR") def cmd_tef_antriebr(): print("\n[DUMMY] Antriebst. rechts fuer TEF rechts") @Rx.command("OMNI_TEF_Gerade") def cmd_tef_gerade(): print("\n[DUMMY] TEF Gerade") @Rx.command("OMNI_KettenFoerderer") def cmd_kettenfoerderer(): print("\n[DUMMY] KettenFoerderer") # ============================================================ # Info-Commands # ============================================================ @Rx.command("OMNI_LOAD") def cmd_omni_load(): """Daten neu laden (Cache leeren).""" global _boegen_data, _weichen_data _boegen_data = None _weichen_data = None load_boegen() load_weichen() @Rx.command("OMNI_INFO_BOGEN") def cmd_omni_info_bogen(): """Bogen-Info anhand SivasId anzeigen.""" ed = Ed.Editor() res = ed.getString("\nSivasId des Bogens eingeben: ") if res[0] != Ed.PromptStatus.eOk or not res[1]: print("\nAbgebrochen.") return sid = res[1] try: sid_val = int(sid) except ValueError: sid_val = sid eintrag = get_by_sivasnr(load_boegen(), sid_val) if eintrag: print(f"\n ProfilTyp: {eintrag.get('ProfilTyp', '?')}") print(f" Radius: {eintrag.get('Radius', '?')}") print(f" KurvenWinkel: {eintrag.get('KurvenWinkel', '?')}") print(f" Breite: {eintrag.get('Breite', '?')}") print(f" Laenge: {eintrag.get('Länge', '?')}") print(f" Antriebsart: {eintrag.get('Antriebsart', '?')}") else: print(f"\nKein Bogen mit SivasId '{sid}' gefunden.") @Rx.command("OMNI_INFO_WEICHE") def cmd_omni_info_weiche(): """Weichen-Info anhand SivasId anzeigen.""" ed = Ed.Editor() res = ed.getString("\nSivasId der Weiche eingeben: ") if res[0] != Ed.PromptStatus.eOk or not res[1]: print("\nAbgebrochen.") return sid = res[1] try: sid_val = int(sid) except ValueError: sid_val = sid eintrag = get_by_sivasnr(load_weichen(), sid_val) if eintrag: print(f"\n ProfilTyp: {eintrag.get('ProfilTyp', '?')}") print(f" WeichenTyp: {eintrag.get('WeichenTyp', '?')}") print(f" KurvenWinkel: {eintrag.get('KurvenWinkel', '?')}") print(f" Schaltungstyp: {eintrag.get('Schaltungstyp', '?')}") print(f" Breite: {eintrag.get('Breite', '?')}") print(f" Laenge: {eintrag.get('Länge', '?')}") print(f" KurvenRichtung: {eintrag.get('KurvenRichtung', '?')}") wkl = eintrag.get("WeichenkörperLänge") if wkl: print(f" WK-Laenge: {wkl}") else: print(f"\nKeine Weiche mit SivasId '{sid}' gefunden.")