R2:底板 / 刀片布局布线脚本、快照、截图与规划文档更新(含外壳、打印件、布线归档)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -0,0 +1,198 @@
|
||||
# -*- 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
|
||||
在新工单中引用
屏蔽一个用户