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