185 行
7.1 KiB
Python
185 行
7.1 KiB
Python
# -*- 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'⛔ 有项没过'))
|