400 lines
13 KiB
Python
400 lines
13 KiB
Python
# 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.")
|