199 行
8.6 KiB
Python
199 行
8.6 KiB
Python
# -*- coding: utf-8 -*-
|
||
u"""把已确认可用的端板 `EndPlate_v2` 插进总装(两块:西端 + 东端)。
|
||
|
||
端板现状(圆柱面直方图实测 `{1.7: 4, 30.0: 1}`,都是真的):
|
||
· 本体 x 0~2.4、y −4~102、z −10~72
|
||
· Ø60 风口中心 (y,z) = (51.345, 37.85)
|
||
· 4× Ø3.4 风扇螺孔(50×50)
|
||
· L 脚 沿 +x 伸到 x=30.4、占 z −10~−7.6
|
||
⚠ 脚锁孔 2× Ø3.4 **尚未成形**(`FeatureCut4` 在本机不可靠,见 NOTES),留待 GUI 补。
|
||
|
||
摆位:
|
||
西端板:本体外表面贴到 x=−27.4 ⇒ 整体平移 x −27.4,其余不动(单位阵旋转)。
|
||
东端板:绕 z 轴转 180°,使板面朝向相反、L 脚伸向 −x;
|
||
转 180° 后本地 (x,y) → (−x, −y),再平移到本体占 x 123~125.4、y 与西端一致。
|
||
列主序旋转 = [−1,0,0, 0,−1,0, 0,0,1];
|
||
平移由「目标 box 最小值 − 旋转后 box 最小值」现算(不写死,NOTES 的固定动作)。
|
||
"""
|
||
import io
|
||
import os
|
||
import sys
|
||
|
||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', write_through=True)
|
||
import pythoncom
|
||
import win32com.client as wc
|
||
|
||
OUT = r'C:\Users\35138\.claude\jobs\11836093\tmp'
|
||
# v5:y 对称版(−1.655~104.345,以风口中心 51.345 居中),
|
||
# 修掉东端板绕 z 翻转后偏 4.69、撞穿北板 616 mm³ 的问题。
|
||
PLATE = u'D:\\DCDC\\新建文件夹\\5blades\\EndPlate_v9.SLDPRT'
|
||
ASM = 'PhoneRail_5blades'
|
||
EMPTY = wc.VARIANT(pythoncom.VT_DISPATCH, None)
|
||
|
||
ROT_W = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0] # 西端:不转
|
||
# ⛔⛔ 绕 **y 轴** 180°([−1,0,0, 0,1,0, 0,0,−1])是错的:它**把 z 也翻了** ——
|
||
# L 脚本来在底部 z 0~2.4,翻完再按"z 最小端对齐 −10"平移 dz=+62,
|
||
# 脚就跑到 **z 59.6~62**,正好扎进风扇(z 6.37~69.33)的腰部 ⇒ 实测 483.9 mm³。
|
||
# (西端不撞纯粹因为它没旋转,脚老实待在风扇底面 6.37 以下。)
|
||
# ✅ 用**绕 z 轴 180°**:z 不翻、脚留在底部 z 0~2.4,从风扇正下方掠过;
|
||
# x 翻使脚朝 −x 伸向机内。附带好处:锁孔本地 y 21.345/81.345 翻转后互换成
|
||
# 81.345/21.345,**与托盘现有孔位完全重合,托盘不用改**。
|
||
ROT_E = [-1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0] # 东端:绕 z 180°
|
||
# 绕 z 翻转后 y 也镜像了:风口中心从 +FAN_CY 跑到 −FAN_CY,
|
||
# 要平移 2×FAN_CY 才能对回风扇中心(风扇/刀片堆叠中心 y=51.345)。
|
||
FAN_CY = 51.345
|
||
WEST_X, EAST_X = -27.4, 123.0 # 两块板本体外表面的 x(东端板本体占 123~125.4)
|
||
PLATE_T = 2.4
|
||
|
||
|
||
def prop(o, n):
|
||
v = getattr(o, n)
|
||
return v() if type(v).__name__ in ('method', 'function', 'builtin_function_or_method') else v
|
||
|
||
|
||
def P(*a):
|
||
print(*a)
|
||
|
||
|
||
def M(v):
|
||
return v / 1000.0
|
||
|
||
|
||
app = wc.GetActiveObject('SldWorks.Application')
|
||
if not os.path.exists(PLATE):
|
||
P(u'⛔ 端板文件不存在 %s' % PLATE); sys.exit(2)
|
||
|
||
doc = None
|
||
for d in app.GetDocuments:
|
||
if ASM in (prop(d, 'GetTitle') or ''):
|
||
doc = d
|
||
break
|
||
if doc is None:
|
||
P(u'⛔ 没找到总装'); sys.exit(3)
|
||
e0 = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0)
|
||
app.ActivateDoc3(prop(doc, 'GetTitle'), False, 0, e0)
|
||
doc = app.ActiveDoc
|
||
P(u'总装 %s 顶层件 %d' % (prop(doc, 'GetTitle'), len(doc.GetComponents(True))))
|
||
|
||
# 幂等:先删掉已有的端板
|
||
for c in list(doc.GetComponents(True)):
|
||
nm = prop(c, 'Name2')
|
||
if 'ENDPLATE' in nm.upper():
|
||
doc.ClearSelection2(True)
|
||
doc.Extension.SelectByID2(nm, 'COMPONENT', 0, 0, 0, False, 0, EMPTY, 0)
|
||
doc.Extension.DeleteSelection2(0)
|
||
P(u' 删掉旧端板 %s' % nm)
|
||
doc.EditRebuild3
|
||
|
||
did = app._oleobj_.GetIDsOfNames('GetMathUtility')
|
||
mu = wc.Dispatch(app._oleobj_.Invoke(did, 0, pythoncom.DISPATCH_METHOD, 1))
|
||
d2 = mu._oleobj_.GetIDsOfNames('CreateTransform')
|
||
|
||
|
||
def mk(a16):
|
||
v = wc.VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_R8, list(a16))
|
||
xf = wc.Dispatch(mu._oleobj_.Invoke(d2, 0, pythoncom.DISPATCH_METHOD, 1, v))
|
||
if max(abs(a - b) for a, b in zip(list(xf.ArrayData), a16)) > 1e-9:
|
||
raise RuntimeError(u'CreateTransform 回读不符')
|
||
return xf
|
||
|
||
|
||
def setx(c, rot9, t3):
|
||
xf = mk(list(rot9) + [t3[0] / 1000.0, t3[1] / 1000.0, t3[2] / 1000.0, 1.0, 0.0, 0.0, 0.0])
|
||
try:
|
||
c.SetTransformAndSolve2(xf)
|
||
except Exception:
|
||
c.Transform2 = xf
|
||
doc.EditRebuild3
|
||
return [round(v * 1000.0, 3) for v in c.GetBox(False, False)]
|
||
|
||
|
||
P(u'\n插入两块端板:')
|
||
for tag, rot, want_x0 in ((u'西端', ROT_W, WEST_X), (u'东端', ROT_E, EAST_X)):
|
||
c = doc.AddComponent4(PLATE, '', 0.0, 0.0, 0.0)
|
||
if c is None:
|
||
P(u' ⛔ %s AddComponent4 返回 None(零件没开着?)' % tag); sys.exit(4)
|
||
b0 = setx(c, rot, (0.0, 0.0, 0.0)) # 先只转不移,回读
|
||
P(u' %s 只旋转 box %s' % (tag, b0))
|
||
# ⛔ 不能一律拿 box 的**最小端**去对齐:西端板未旋转,本体(板厚那 2.4)在 box 最小端;
|
||
# 而东端板绕 y 转 180° 之后,**本体跑到了 box 的最大端**、L 脚朝最小端伸。
|
||
# 第一版统一用最小端对齐 ⇒ 东端板 box 落在 x 123.00~153.40,脚朝 +x 伸出机外。
|
||
# ✅ 按「本体外表面」这个语义对齐:西端板对齐 box 最小端,东端板对齐 box 最大端。
|
||
if tag == u'西端':
|
||
# 未旋转:本体在 box 最小端
|
||
dx = want_x0 - b0[0]
|
||
# ⛔ 原来写 `dy = −4.0 − b0[1]`(把 box 硬对到 y=−4):零件改成 y 对称之后,
|
||
# 这条旧逻辑把西端板拽偏了 2.345,与按风口对齐的东端板**不一致**
|
||
# (实测西 −4.00~102.00 / 东 −1.66~104.34),东端仍撞北板 589 mm³。
|
||
# ✅ 零件已以风口中心 51.345 对称 ⇒ **dy = 0** 时风口天然落位,两块板 y 完全一致。
|
||
dy = 0.0
|
||
dz = -10.0 - b0[2]
|
||
else:
|
||
# 绕 z 180°:本体跑到 box 最大端 ⇒ 按最大端对齐(123+2.4=125.4)
|
||
dx = (want_x0 + PLATE_T) - b0[3]
|
||
# ⛔ y 不能再按 box 最小端对齐:绕 z 翻转把风口中心镜像到了 −FAN_CY,
|
||
# 必须平移 2×FAN_CY 才能对回风扇/刀片堆叠中心(y=51.345)。
|
||
# 附带结果:锁孔 y 21.345/81.345 互换位置,与托盘孔位仍然重合。
|
||
dy = 2.0 * FAN_CY
|
||
# ⛔ z **不要**再补偿:绕 z 不翻 z,脚仍在底部 z 0~2.4(风扇底面 6.37 以下)。
|
||
dz = 0.0
|
||
t = (dx, dy, dz)
|
||
b = setx(c, rot, t)
|
||
P(u' %s 摆位后 box x %.2f~%.2f y %.2f~%.2f z %.2f~%.2f'
|
||
% (tag, b[0], b[3], b[1], b[4], b[2], b[5]))
|
||
|
||
# 截图
|
||
for nm2, vid in ((u'full_iso', 7), (u'full_front', 1), (u'full_top', 5)):
|
||
try:
|
||
doc.ShowNamedView2('', vid)
|
||
doc.ViewZoomtofit2
|
||
doc.SaveBMP(os.path.join(OUT, '%s.bmp' % nm2), 1600, 1100)
|
||
except Exception as ex:
|
||
P(u'截图 %s 失败 %s' % (nm2, str(ex)[:40]))
|
||
P(u'\n截图完成')
|
||
|
||
e3 = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0)
|
||
w3 = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0)
|
||
doc.Extension.SaveAs3(prop(doc, 'GetPathName'), 0, 1, EMPTY, EMPTY, e3, w3)
|
||
P(u'存盘 err=%s' % e3.value)
|
||
|
||
P(u'\n干涉(fast):')
|
||
try:
|
||
idm = doc.InterferenceDetectionManager
|
||
idm.TreatCoincidenceAsInterference = False
|
||
idm.IncludeMultibodyPartInterferences = False
|
||
idm.TreatSubAssembliesAsComponents = True
|
||
idm.IgnoreHiddenBodies = True
|
||
idm.UseTransform = False
|
||
try:
|
||
n = idm.GetInterferenceCount
|
||
except AttributeError:
|
||
dd = idm._oleobj_.GetIDsOfNames('GetInterferenceCount')
|
||
n = idm._oleobj_.Invoke(dd, 0, pythoncom.DISPATCH_PROPERTYGET, 1)
|
||
P(u' 干涉 %s 处(基线 360)' % n)
|
||
hits = []
|
||
if n:
|
||
try:
|
||
items = idm.GetInterferences
|
||
except AttributeError:
|
||
dd = idm._oleobj_.GetIDsOfNames('GetInterferences')
|
||
items = idm._oleobj_.Invoke(dd, 0, pythoncom.DISPATCH_PROPERTYGET, 1)
|
||
for it in items:
|
||
o = it if hasattr(it, 'Volume') else wc.Dispatch(it)
|
||
nms = [wc.Dispatch(x).Name2.split('/')[0] for x in o.Components]
|
||
if any('ENDPLATE' in y.upper() for y in nms):
|
||
hits.append((o.Volume * 1e9, nms))
|
||
if hits:
|
||
P(u' ⚠ 端板相关 %d 处:' % len(hits))
|
||
for vv, nms in sorted(hits, reverse=True)[:6]:
|
||
P(u' %.3f mm³ %s ↔ %s' % (vv, nms[0][:24], nms[1][:24]))
|
||
else:
|
||
P(u' ✅ 端板与风扇/刀片/底板/托盘零干涉')
|
||
idm.Done()
|
||
except Exception as ex:
|
||
P(u' 干涉失败 %s' % str(ex)[:80])
|
||
try:
|
||
doc.InterferenceDetectionManager.Done()
|
||
except Exception:
|
||
pass
|