# -*- coding: utf-8 -*- u"""总装里 EndPlate_v10 → **v12**(多了 A 组 8 + C 组 4 个 Ø4.2×6 螺母孔),并核「螺丝拧得进去」。 摆位与 place_v10.py 完全相同(西:不转、外表面 −38.4;东:绕 z 180°、外表面 136.4、dy=2×51.345)。 判据: 1. 两块端板装配 box 与 v10 时一致; 2. 干涉仍是 360 基线; 3. ⭐ **同轴核对**:在装配坐标下,把端板上 12 个 R2.1 螺母孔的轴线, 与南板/北板/顶盖上 Ø3.4 过孔的轴线逐一配对 —— 同方向、横向偏差 < 0.05 才算拧得进去。 这是「孔打出来了」和「螺丝能穿过去」之间的那一步,包围盒/干涉/直方图都回答不了。 """ 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' DIR = u'D:\\DCDC\\新建文件夹\\5blades\\' ASM = 'PhoneRail_5blades' EMPTY = wc.VARIANT(pythoncom.VT_DISPATCH, None) IDENT = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0] ROT_Z180 = [-1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0] FAN_CY = 51.345 WEST_OUT, EAST_OUT = -38.4, 136.4 EP_Y0, EP_Y1 = -1.655, 104.345 def prop(o, n): v = getattr(o, n) return v() if type(v).__name__ in ('method', 'function', 'builtin_function_or_method') else v def D(x): return x if hasattr(x, '_oleobj_') else wc.Dispatch(x) def P(*a): print(*a) app = wc.GetActiveObject('SldWorks.Application') doc = None for d in app.GetDocuments or []: if ASM in (prop(d, 'GetTitle') or ''): doc = d break e0 = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0) app.ActivateDoc3(prop(doc, 'GetTitle'), False, 0, e0) doc = app.ActiveDoc 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)) return wc.Dispatch(mu._oleobj_.Invoke(d2, 0, pythoncom.DISPATCH_METHOD, 1, v)) def setx(c, a16): xf = mk(a16) 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'[1] 删 v10') 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 EP = DIR + u'EndPlate_v13.SLDPRT' P(u'\n[2] 插 v12 ×2') allok = True for tag, rot, want in ((u'西', IDENT, [WEST_OUT, EP_Y0, -10.0, 0.0, EP_Y1, 72.0]), (u'东', ROT_Z180, [98.0, EP_Y0, -10.0, EAST_OUT, EP_Y1, 72.0])): c = doc.AddComponent4(EP, '', 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, 1.0, 0.0, 0.0, 0.0]) if tag == u'西': t = (WEST_OUT - b0[0], 0.0, -10.0 - b0[2]) else: t = (EAST_OUT - b0[3], 2.0 * FAN_CY, 0.0) b = setx(c, rot + [t[0] / 1000.0, t[1] / 1000.0, t[2] / 1000.0, 1.0, 0.0, 0.0, 0.0]) ok = all(abs(b[i] - want[i]) < 0.05 for i in range(6)) allok &= ok P(u' %s端板 x %.2f~%.2f y %.2f~%.2f z %.2f~%.2f %s' % (tag, b[0], b[3], b[1], b[4], b[2], b[5], u'✅' if ok else u'⛔ 期望 %s' % want)) # ── [3] 同轴核对 ────────────────────────────────────────────────────────── def world_holes(comp, radius): u"""组件上指定半径的圆柱面 → 装配坐标下的 (轴向单位向量, 轴上一点, 面box)""" xf = list(prop(comp, 'Transform2').ArrayData) R = xf[0:9] T = [xf[9] * 1000, xf[10] * 1000, xf[11] * 1000] out = [] body_list = comp.GetBodies2(0) or [] for bd in body_list: for f in prop(D(bd), 'GetFaces'): s = prop(D(f), 'GetSurface') if not prop(s, 'IsCylinder'): continue cp = list(prop(s, 'CylinderParams')) if abs(cp[6] * 1000 - radius) > 0.01: continue p = [cp[0] * 1000, cp[1] * 1000, cp[2] * 1000] a = cp[3:6] # 列主序:本地 (x,y,z) → 全局 = x*col0 + y*col1 + z*col2 + T gp = [p[0] * R[0] + p[1] * R[3] + p[2] * R[6] + T[0], p[0] * R[1] + p[1] * R[4] + p[2] * R[7] + T[1], p[0] * R[2] + p[1] * R[5] + p[2] * R[8] + T[2]] ga = [a[0] * R[0] + a[1] * R[3] + a[2] * R[6], a[0] * R[1] + a[1] * R[4] + a[2] * R[7], a[0] * R[2] + a[1] * R[5] + a[2] * R[8]] out.append((ga, gp)) return out P(u'\n[3] 同轴核对:端板 R2.1 螺母孔 ↔ 侧板/顶盖 R1.7 过孔') nuts, thru = [], [] for c in doc.GetComponents(True): nm = prop(c, 'Name2').upper() if 'ENDPLATE' in nm: nuts += [(nm, *h) for h in world_holes(c, 2.1)] elif any(k in nm for k in ('SOUTHPANEL', 'NORTHPANEL', 'TOPPANEL')): thru += [(nm, *h) for h in world_holes(c, 1.7)] P(u' 螺母孔 %d 个(期望 12)/ 板上过孔 %d 个(期望 7+7+4=18,其中 B 组 6 个无对象)' % (len(nuts), len(thru))) matched = 0 for nm, ga, gp in nuts: ai = max(range(3), key=lambda i: abs(ga[i])) best = None for tn, ta, tp in thru: if max(range(3), key=lambda i: abs(ta[i])) != ai: continue lat = max(abs(gp[i] - tp[i]) for i in range(3) if i != ai) if best is None or lat < best[0]: best = (lat, tn) ok = best is not None and best[0] < 0.05 matched += ok o = [i for i in range(3) if i != ai] P(u' %-16s 轴%s @ (%s=%.2f, %s=%.2f) ↔ %-16s 偏 %.3f %s' % (nm[:16], 'xyz'[ai], 'xyz'[o[0]], gp[o[0]], 'xyz'[o[1]], gp[o[1]], best[1][:16] if best else '-', best[0] if best else -1, u'✅' if ok else u'⛔')) P(u' 同轴 %d / %d %s' % (matched, len(nuts), u'✅' if matched == 12 else u'⛔')) allok &= (matched == 12) # ── [4] 干涉 ────────────────────────────────────────────────────────────── P(u'\n[4] 干涉(基线 360)') 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) idm.Done() P(u' 干涉 %s %s' % (n, u'✅' if n == 360 else u'⛔')) allok &= (n == 360) 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'\n存盘 err=%s 总判 %s' % (e3.value, u'✅ 全过' if allok else u'⛔ 有项没过'))