# -*- 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