424 行
21 KiB
Python
424 行
21 KiB
Python
# -*- coding: utf-8 -*-
|
||
u"""托盘 v2:重建 + 装进总装。
|
||
|
||
⛔ 为什么不用 heredoc 跑:**bash heredoc 会打碎脚本里的中文路径**。
|
||
上一轮实测报错里路径变成了 `D:\\DCDC\\u65b0建文件夹\x05blades\\...`,
|
||
随后 `SaveAs3` 返回 err=1、文件没落盘。⇒ 含中文路径的脚本一律用 Write 落成 .py 再跑。
|
||
|
||
⛔ 旧的 `Tray_base.SLDPRT` 是**第一版躺错轴**的错版(box `x −4~102 / y 0~2.4 / z −102~4`),
|
||
已弃用;本脚本换名 `Tray_v2.SLDPRT`,并在开头关掉所有旧的 Tray 文档。
|
||
|
||
零件本地坐标(与底板局部坐标同向,装配时整体平移 z −POST_H 即可对上):
|
||
z = 0 托盘顶面(支柱根部)
|
||
z = −2.4 托盘底面
|
||
z = +15.0 支柱顶面(= 装配后 PCB 底面)
|
||
"""
|
||
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'
|
||
# v7:南边加两个 USB-C 缺口。实测托盘面板 z −2.40~0 与 USB-C 插入通道 z −3.25~0.85
|
||
# **重叠 2.40 mm**(两段 x 内各有 5 / 3 个面的板材)⇒ J1 与 J23 现在插不进去。
|
||
# v8:y 外扩到 −4.055~106.745,给南北板落脚(v7 时两板悬空、螺丝无处可打)。
|
||
# ⛔ 换名不覆盖:v7 正开在 SolidWorks 里(`~$Tray_v7` 锁文件),同名 SaveAs3 会 err=1。
|
||
# v9:托盘 y 改成与侧板**内表面**齐平(−1.655~104.345),让侧板从外侧包住托盘端面、
|
||
# 形成真正的面接触(v8 与外表面齐平 ⇒ 只是擦过,贴合面分析里一处接触都没有)。
|
||
# ⛔ 必须换名:v8 正开在 SolidWorks 里(锁文件在),同名 SaveAs3 已实测 err=1 且不写盘。
|
||
# v10:加 B 组螺丝凸台(每侧 3 个 Ø8×6,内嵌 Ø4.2 热熔螺母),
|
||
# 供南北板水平锁进托盘侧端面(贴合带 z −10~0,孔心取中线 z=−5.0)。
|
||
# v11:B 组凸台改用「遍历选端面 + 面上建草图」(v10 画在 y=0 平面,凸台位置全错,
|
||
# 只成了 3 个且悬在板外);B 组孔位同步挪到 x 30/49/68 避开 USB-C 开口。
|
||
DST = u'D:\\DCDC\\新建文件夹\\5blades\\Tray_v11.SLDPRT'
|
||
ASM = 'PhoneRail_5blades'
|
||
EMPTY = wc.VARIANT(pythoncom.VT_DISPATCH, None)
|
||
|
||
BW = BH = 98.0
|
||
# ⛔ 托盘原来 y −4~102(按 PCB + 4 外扩)。端板改 y 对称(−1.655~104.345)、南北板跟着
|
||
# 贴到端板外侧之后,南板落在 y −4.055~−1.66、北板 104.345~106.745 —— **全在托盘之外**,
|
||
# 贴合面分析里 Tray↔SouthPanel / Tray↔NorthPanel **一处接触都没有** ⇒ 两块板悬空、
|
||
# 螺丝无处可打。⚠ 干涉检查当时是 360 基线"零冲突",因为不接触本来就不干涉 ——
|
||
# **干涉为零不等于装配成立**。
|
||
# ✅ 托盘 y 外扩到与南北板外表面齐平:−4.055 ~ 106.745。
|
||
# ⛔ v8 把托盘 y 外扩到 −4.055~106.745(与侧板**外表面**齐平)——**没解决问题**:
|
||
# 贴合面分析里 Tray↔South/North 依旧一处接触都没有。因为侧板内表面在
|
||
# y=−1.655 / 104.345,托盘边缘却跑到了它们**外面**,两者只是侧面擦过、并不面接触。
|
||
# ✅ v9 改成与侧板**内表面**齐平:托盘 y = −1.655 ~ 104.345。
|
||
# 这样侧板从外侧正好包住托盘端面,形成真正的面接触,螺丝可从侧板水平拧进托盘侧棱。
|
||
TRAY_Y0, TRAY_Y1 = -1.655, 104.345
|
||
# ⛔ POST_H 由 15.0 降到 10.0(2026-09-13 用户要求「降低底部立柱高度」)。
|
||
# 15 的来历是 R2 决策 D15 那句「背面最高 12 mm(C2/C3 直立电解),底板下要 ≥15 mm 铜柱」——
|
||
# **这个前提已经失效**:C2/C3 早已挪到顶面(`pcb_base.TOP_EXTRA` = C2/C3/J26/J27/J33),
|
||
# 3D 实测底板背面最低只到 **−6.30**(SRP1265A 电感)。
|
||
# ⇒ 10.0 对背面件余量 **3.70**,同时给底板下的风扇线 / USB 线留走线空间。
|
||
# 改这一个常数即可(装配平移与断言都从它算,不许再写死)。
|
||
MARGIN, FLOOR_T, POST_H, POST_D = 4.0, 2.4, 10.0, 8.0
|
||
INSERT_D, INSERT_DEPTH = 4.2, 6.0
|
||
HOLES = [(4.0, 4.0), (94.0, 4.0), (4.0, 94.0), (94.0, 94.0)]
|
||
|
||
# ── 端板锁孔(2026-09-13 用户选 B:端板独立、螺丝锁到托盘)──────────────
|
||
# 端板在风扇外侧(西 x −27.4~−25 / 东 x 123~125.4),底部带 25 长的 L 脚伸回来,
|
||
# 在 x −4~0(西)与 98~102(东)这两段压在托盘面板上 ⇒ 锁孔打在这两段里。
|
||
# y 取 51.345 ± 30 = 21.345 / 81.345(风扇螺孔投影在 26.345/76.345,错开 5)。
|
||
# ⛔ 第一版孔心放在 x=−2.0 / 100.0:孔半径 2.1 ⇒ 圆占 x −4.1~0.1,**越过面板边界 −4**,
|
||
# 轮廓自相交 ⇒ `FeatureExtrusion3` 返回 None、整个零件建不出来(回读 box 为 None)。
|
||
# ⇒ 内移到 x=1.0 / 97.0:离边 5.0,孔外侧还剩 2.9 壁厚。端板 L 脚相应加长到 28。
|
||
# ⛔⛔ x=1.0 / 97.0 也是错的:实测底板 PCB 占 x **0.01~97.99**,这两排孔正好**落在 PCB 底下**,
|
||
# 螺丝拧不进去。托盘露在 PCB 之外的环只有 **4.01 mm**(西 −4.00~0.01 / 东 97.99~102.00)。
|
||
# ⇒ 锁孔退到环中线 **x = −2.0 / 100.0**。
|
||
# (最早那版也用过 −2.0 并"破边"失败,但那是草图自动捕捉把 r=2.1 吸成 3.1 所致;
|
||
# 现在 `AddToDB=True` 已根治,−2.0 离边 2.0、孔半径 2.1 —— 仍会破边!
|
||
# ⇒ 取环中线偏内一点:**x = −1.9 / 99.9**,离托盘边 2.1 正好等于孔半径,
|
||
# 再往内就压到 PCB。孔壁与托盘边**相切**,所以把孔径改小到 Ø3.4(M3 过孔,
|
||
# 螺母改用底面沉入的 M3 螺母或自攻螺丝),孔半径 1.7 ⇒ 离边 2.3、离 PCB 1.7,两侧都有料。
|
||
END_HOLES = [(-1.9, 21.345), (-1.9, 81.345), (99.9, 21.345), (99.9, 81.345)]
|
||
# ⛔ 由 Ø4.2 热熔螺母孔改成 **Ø3.4 过孔**:环只有 4.01 宽,Ø4.2 的孔(半径 2.1)
|
||
# 放在环中线上会与托盘外边相切/破边。Ø3.4 的孔离边 2.3、离 PCB 1.7,两侧都有料。
|
||
# ⇒ 端板用 M3 螺丝穿过托盘,从**底面**上螺母(或用自攻螺丝)。
|
||
END_INSERT_D = 3.4
|
||
END_INSERT_DEPTH = 2.4 # 打穿 2.4 厚的面板
|
||
# ⛔ y 不再用 ±MARGIN:端板改 y 对称后南北板贴到端板外侧(−4.055 / 106.745),
|
||
# 托盘若仍是 −4~102,两块板整个落在托盘之外 ⇒ 悬空、螺丝无处可打
|
||
# (贴合面分析里 Tray↔South/North 一处接触都没有,而干涉却是"零冲突")。
|
||
# ✅ y 外扩到与南北板外表面齐平。x 方向不动(端板 L 脚已在 x 上锁住托盘)。
|
||
X0, X1, Y0, Y1 = -MARGIN, BW + MARGIN, TRAY_Y0, TRAY_Y1
|
||
|
||
|
||
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)
|
||
|
||
|
||
def M(v):
|
||
return v / 1000.0
|
||
|
||
|
||
app = wc.GetActiveObject('SldWorks.Application')
|
||
P(u'目标目录存在=%s' % os.path.isdir(os.path.dirname(DST)))
|
||
|
||
for d in list(app.GetDocuments):
|
||
try:
|
||
t = prop(d, 'GetTitle') or ''
|
||
if 'TRAY' in t.upper():
|
||
app.CloseDoc(t)
|
||
P(u'关掉旧文档 %s' % t)
|
||
except Exception:
|
||
pass
|
||
|
||
tmpl = app.GetUserPreferenceStringValue(8)
|
||
app.NewDocument(tmpl, 0, 0.0, 0.0)
|
||
part = app.ActiveDoc
|
||
P(u'新建零件 %s' % prop(part, 'GetTitle'))
|
||
sm, fm = part.SketchManager, part.FeatureManager
|
||
|
||
|
||
def sk_name():
|
||
nm, f = None, prop(part, 'FirstFeature')
|
||
while f:
|
||
if prop(f, 'GetTypeName2') == 'ProfileFeature':
|
||
nm = prop(f, 'Name')
|
||
f = prop(f, 'GetNextFeature')
|
||
return nm
|
||
|
||
|
||
def pick(name, typ):
|
||
part.ClearSelection2(True)
|
||
part.Extension.SelectByID2(name, typ, 0, 0, 0, False, 0, EMPTY, 0)
|
||
return part.SelectionManager.GetSelectedObjectCount2(-1)
|
||
|
||
|
||
def extrude(depth, rev=False):
|
||
if pick(sk_name(), 'SKETCH') != 1:
|
||
raise RuntimeError(u'草图没选中')
|
||
return fm.FeatureExtrusion3(True, False, rev, 0, 0, M(depth), 0.0,
|
||
False, False, False, False, 0.0, 0.0,
|
||
False, False, False, False, True, True, True, 0, 0.0, False)
|
||
|
||
|
||
def bodies():
|
||
return part.GetBodies2(0, True) or []
|
||
|
||
|
||
def bbox():
|
||
b = bodies()
|
||
if not b:
|
||
return None
|
||
xs = [[round(v * 1000, 3) for v in prop(D(x), 'GetBodyBox')] for x in b]
|
||
return [min(v[0] for v in xs), min(v[1] for v in xs), min(v[2] for v in xs),
|
||
max(v[3] for v in xs), max(v[4] for v in xs), max(v[5] for v in xs)]
|
||
|
||
|
||
def check(tag, want, tol=0.02):
|
||
got = bbox()
|
||
ok = got and all(abs(got[i] - want[i]) <= tol for i in range(6))
|
||
P(u' %s box %s 期望 %s %s' % (tag, got, want, u'✅' if ok else u'⛔'))
|
||
return ok
|
||
|
||
|
||
P(u'\n[1] 托盘底板 %.0f×%.0f×%.1f + 4 个端板锁孔(同一张草图,一次拉伸成形)'
|
||
% (X1 - X0, Y1 - Y0, FLOOR_T))
|
||
# ⛔ 端板锁孔不再用 FeatureCut4:托盘面板 z=0 那一面的法线是 (0,0,−1)(建模时从 z=0 往 −z 拉),
|
||
# z=0 处没有朝上的自由面,按坐标选面 / 前视基准面草图、rev 两个方向**全部返回 None**
|
||
# (用「Ø4.2 圆柱面计数」这个有分辨力的判据复验过:始终只有 4 个 = 只有柱顶孔)。
|
||
# ✅ 改成把 4 个圆和外框矩形放进**同一张草图**,拉伸时圆自然成为通孔,不需要切除特征。
|
||
pick(u'前视基准面', 'PLANE')
|
||
sm.InsertSketch(True)
|
||
# ⛔⛔ 必须开 AddToDB:否则 SolidWorks 的草图**自动捕捉会静默改掉几何** ——
|
||
# 实测 (1.0, 21.345) r=2.1 被吸成 **(0.0, 21.345) r=3.1**(圆心挪到原点纵轴、半径 +1.0),
|
||
# 于是锁孔要么尺寸不对、要么越界让整个轮廓失效。AddToDB=True 时几何按精确坐标写库、
|
||
# 不做任何推理,四个圆回读全部精确。
|
||
sm.AddToDB = True
|
||
# 外框改成带两个南侧缺口的折线(缺口从南边 Y0 往里挖到 y=0.20,正好让开 USB-C 插入通道)
|
||
# 缺口 x 区间实测:J1 6.53~18.47、J23 78.03~89.97(各含每侧 1.5 装配余量)
|
||
NOTCH_Y = 0.20
|
||
_pts = [(X0, Y0), (6.53, Y0), (6.53, NOTCH_Y), (18.47, NOTCH_Y), (18.47, Y0),
|
||
(78.03, Y0), (78.03, NOTCH_Y), (89.97, NOTCH_Y), (89.97, Y0),
|
||
(X1, Y0), (X1, Y1), (X0, Y1), (X0, Y0)]
|
||
for _i in range(len(_pts) - 1):
|
||
_a, _b = _pts[_i], _pts[_i + 1]
|
||
sm.CreateLine(M(_a[0]), M(_a[1]), 0.0, M(_b[0]), M(_b[1]), 0.0)
|
||
for hx, hy in END_HOLES:
|
||
sm.CreateCircleByRadius(M(hx), M(hy), 0.0, M(END_INSERT_D / 2))
|
||
sm.AddToDB = False
|
||
_sk = prop(sm, 'ActiveSketch')
|
||
_segs = prop(D(_sk), 'GetSketchSegments') if _sk else None
|
||
P(u' 草图 segment = %d(期望 4 线 + 4 圆 = 8)' % (len(_segs) if _segs else 0))
|
||
sm.InsertSketch(True)
|
||
extrude(FLOOR_T, rev=True)
|
||
if not check(u'底板', [X0, Y0, -FLOOR_T, X1, Y1, 0.0]):
|
||
sys.exit(2)
|
||
|
||
P(u'\n[2] 四支柱 Ø%.0f 高 %.0f' % (POST_D, POST_H))
|
||
pick(u'前视基准面', 'PLANE')
|
||
sm.InsertSketch(True)
|
||
for hx, hy in HOLES:
|
||
sm.CreateCircleByRadius(M(hx), M(hy), 0.0, M(POST_D / 2))
|
||
sm.InsertSketch(True)
|
||
extrude(POST_H, rev=False)
|
||
if not check(u'底板+支柱', [X0, Y0, -FLOOR_T, X1, Y1, POST_H]):
|
||
sys.exit(3)
|
||
|
||
P(u'\n[3] 柱顶热熔螺母孔 Ø%.1f 深 %.1f(FeatureCut4 = 27 参)' % (INSERT_D, INSERT_DEPTH))
|
||
part.ClearSelection2(True)
|
||
ok = part.Extension.SelectByID2('', 'FACE', M(HOLES[0][0]), M(HOLES[0][1]), M(POST_H),
|
||
False, 0, EMPTY, 0)
|
||
n = part.SelectionManager.GetSelectedObjectCount2(-1)
|
||
P(u' 按坐标选柱顶面 返回=%s 选中数=%s' % (ok, n))
|
||
if n == 1:
|
||
sm.InsertSketch(True)
|
||
for hx, hy in HOLES:
|
||
sm.CreateCircleByRadius(M(hx), M(hy), M(POST_H), M(INSERT_D / 2))
|
||
sm.InsertSketch(True)
|
||
pick(sk_name(), 'SKETCH')
|
||
try:
|
||
cut = fm.FeatureCut4(True, False, False, 0, 0, M(INSERT_DEPTH), 0.0,
|
||
False, False, False, False, 0.0, 0.0,
|
||
False, False, False, False, False,
|
||
True, True, False, False, False, 0, 0.0, False, False)
|
||
P(u' FeatureCut4 → %r' % (prop(cut, 'Name') if cut else cut))
|
||
except Exception as ex:
|
||
P(u' ⛔ FeatureCut4 失败: %s' % str(ex)[:110])
|
||
check(u'切孔后', [X0, Y0, -FLOOR_T, X1, Y1, POST_H])
|
||
else:
|
||
P(u' ⚠ 选不到柱顶面,跳过孔')
|
||
|
||
# ── [4] 已作废:端板锁孔改在第 1 步的草图里一次成形(见那里的说明)────────
|
||
# 这里原本用 FeatureCut4 切孔,两个方向都恒返回 None,已删除,免得重复打孔或污染日志。
|
||
|
||
# ── [3b] B 组螺丝凸台:托盘两侧端面各 3 个 Ø8×6,供南北板水平锁进来 ──────
|
||
# 2.4 的面板边攻不住 M3 ⇒ 加凸台内嵌 Ø4.2 热熔螺母。孔心在贴合带中线 z=−5.0。
|
||
BOSS_D, BOSS_H = 8.0, 6.0
|
||
# ⛔ B 组孔位与南板同步改成两个 USB-C 开口之间的实心区(原 11/49/87 会撞开口)
|
||
B_X = (30.0, 49.0, 68.0)
|
||
B_Z = -5.0
|
||
P(u'\n[3b] B 组凸台:两侧端面各 3 个 Ø%.0f×%.0f(孔心 z=%.1f)' % (BOSS_D, BOSS_H, B_Z))
|
||
# ⛔ 上一版画在**上视基准面**:草图在 y=0 平面,拉伸从 y=0 起算而不是从端面起算 ⇒
|
||
# 南侧凸台长到 y −6~0(悬在板外)、北侧 3 个整个埋进板体,只成了 3 个(R4.0 从 4→7)。
|
||
# `T0/StartOffset` 本会话已实测静默失效,不能用。
|
||
# ✅ 改走「遍历选端面 → 面上建草图 → 拉伸」——托盘柱顶孔已验证这条路可行,
|
||
# 面上草图接受**模型坐标**(那次传的就是 (x, y, z=柱顶高))。
|
||
for tag, ynom, rev in ((u'南侧', Y0, True), (u'北侧', Y1, False)):
|
||
# 遍历找法线沿 ±y、且位置等于该端面的平面
|
||
tgt, tarea = None, -1.0
|
||
for _f in prop(D(bodies()[0]), 'GetFaces'):
|
||
_fc = D(_f)
|
||
_s = prop(_fc, 'GetSurface')
|
||
if not prop(_s, 'IsPlane'):
|
||
continue
|
||
_n = list(prop(_s, 'PlaneParams'))
|
||
if abs(abs(_n[1]) - 1.0) > 0.01: # 法线必须沿 y
|
||
continue
|
||
_fb = [round(v * 1000, 3) for v in prop(_fc, 'GetBox')]
|
||
if abs(_fb[1] - ynom) > 0.01 or abs(_fb[4] - ynom) > 0.01:
|
||
continue
|
||
_a = prop(_fc, 'GetArea') * 1e6
|
||
if _a > tarea:
|
||
tgt, tarea = _fc, _a
|
||
if tgt is None:
|
||
P(u' ⛔ %s 没找到 y=%.3f 的端面' % (tag, ynom))
|
||
continue
|
||
part.ClearSelection2(True)
|
||
tgt.Select4(False, EMPTY)
|
||
if part.SelectionManager.GetSelectedObjectCount2(-1) != 1:
|
||
P(u' ⛔ %s 端面没选中' % tag)
|
||
continue
|
||
sm.InsertSketch(True)
|
||
sm.AddToDB = True
|
||
# ⛔ 面上草图**不接受模型坐标**(我误以为接受,托盘柱顶孔那次是巧合:
|
||
# 那是 z 常量面,草图 xy 恰好与模型 xy 一致)。
|
||
# y 端面实测映射:草图 u → 模型 x、草图 v → 模型 **−z**、法线 → +y。
|
||
# 传 (bx, ynom, B_Z) 会把 ynom 当成 v ⇒ 凸台被甩到 z=−104,还吞掉已有特征。
|
||
# ✅ 正确写法:第三参恒 0,v 传 −z。
|
||
for bx in B_X:
|
||
sm.CreateCircleByRadius(M(bx), M(-B_Z), 0.0, M(BOSS_D / 2))
|
||
sm.AddToDB = False
|
||
sm.InsertSketch(True)
|
||
if pick(sk_name(), 'SKETCH') != 1:
|
||
P(u' ⛔ %s 草图没选中' % tag)
|
||
continue
|
||
f = fm.FeatureExtrusion3(True, False, rev, 0, 0, M(BOSS_H), 0.0,
|
||
False, False, False, False, 0.0, 0.0,
|
||
False, False, False, False, True, True, True,
|
||
0, 0.0, False)
|
||
P(u' %s 端面(面积 %.0f mm²) 凸台 → %r' % (tag, tarea, prop(f, 'Name') if f else f))
|
||
P(u' 加凸台后 box %s' % bbox())
|
||
P(u' 圆柱面直方图 %s(R4.0 应为 4 支柱 + 6 凸台 = 10)'
|
||
% {r: n for r, n in sorted({}.items())} if False else u'')
|
||
|
||
P(u'\n[4] 复验孔(两种孔径分开数 —— 上一版把两者混成一个 8 的期望值,误报了)')
|
||
# ⛔ 柱顶热熔螺母孔是 **Ø4.2**,端板锁孔是 **Ø3.4**,半径不同不能合并计数。
|
||
# 上一版写「Ø3.4 应有 8 个 = 4 柱顶 + 4 端板」⇒ 柱顶那 4 个根本不是 3.4,
|
||
# 于是永远只数到 4,把已经成形的锁孔误判成"没成形"并提前退出。
|
||
_bd = D(bodies()[0])
|
||
_hist = {}
|
||
for _f in prop(_bd, 'GetFaces'):
|
||
_s = prop(D(_f), 'GetSurface')
|
||
if prop(_s, 'IsCylinder'):
|
||
_r = round(list(prop(_s, 'CylinderParams'))[6] * 1000, 2)
|
||
_hist[_r] = _hist.get(_r, 0) + 1
|
||
P(u' 圆柱面直方图 = %s' % dict(sorted(_hist.items())))
|
||
_n_end = _hist.get(round(END_INSERT_D / 2, 2), 0) # Ø3.4 ⇒ R1.7:端板锁孔
|
||
_n_post = _hist.get(round(INSERT_D / 2, 2), 0) # Ø4.2 ⇒ R2.1:柱顶螺母孔
|
||
P(u' 端板锁孔 Ø%.1f = %d 个(期望 4)%s' % (END_INSERT_D, _n_end, u'✅' if _n_end >= 4 else u'⛔'))
|
||
P(u' 柱顶孔 Ø%.1f = %d 个(期望 4)%s' % (INSERT_D, _n_post, u'✅' if _n_post >= 4 else u'⛔'))
|
||
if _n_end < 4 or _n_post < 4:
|
||
P(u'⛔ 停手:包围盒断言对通孔没有分辨力,必须靠这个计数判'); sys.exit(4)
|
||
|
||
e1 = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0)
|
||
w1 = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0)
|
||
r = part.Extension.SaveAs3(DST, 0, 1, EMPTY, EMPTY, e1, w1)
|
||
# ⚠ NOTES §11 的固定动作:不信返回值,看文件真的在不在
|
||
P(u'\n另存 返回=%s err=%s 存在=%s %.0f KB'
|
||
% (r, e1.value, os.path.exists(DST), os.path.getsize(DST) / 1024.0 if os.path.exists(DST) else 0))
|
||
if not os.path.exists(DST):
|
||
P(u'⛔ 文件没落盘,停手(零件留着便于排查)')
|
||
sys.exit(5)
|
||
# ⛔ 不要在插入前 CloseDoc:本会话三次成功的 AddComponent4(风扇占位体 / OD6025 / STEP 临时件)
|
||
# 共同点都是**被插入的文档当时还开着**;唯一一次关掉零件再插,就恒返回 None
|
||
# (文件明明存在、86 KB)。⇒ 保持零件打开。
|
||
P(u'零件保持打开(AddComponent4 疑似要求目标文档在打开状态)')
|
||
|
||
# ── 4) 插进总装 ───────────────────────────────────────────────────────
|
||
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(6)
|
||
e2 = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0)
|
||
app.ActivateDoc3(prop(doc, 'GetTitle'), False, 0, e2)
|
||
doc = app.ActiveDoc
|
||
for c in list(doc.GetComponents(True)):
|
||
if 'TRAY' in prop(c, 'Name2').upper():
|
||
doc.ClearSelection2(True)
|
||
doc.Extension.SelectByID2(prop(c, 'Name2'), 'COMPONENT', 0, 0, 0, False, 0, EMPTY, 0)
|
||
doc.Extension.DeleteSelection2(0)
|
||
P(u'删掉旧托盘零部件')
|
||
doc.EditRebuild3
|
||
c = doc.AddComponent4(DST, '', 0.0, 0.0, 0.0)
|
||
P(u'AddComponent4 → %s' % (prop(c, 'Name2') if c else u'⛔ None'))
|
||
if c is None:
|
||
sys.exit(7)
|
||
|
||
did = app._oleobj_.GetIDsOfNames('GetMathUtility')
|
||
mu = wc.Dispatch(app._oleobj_.Invoke(did, 0, pythoncom.DISPATCH_METHOD, 1))
|
||
d2 = mu._oleobj_.GetIDsOfNames('CreateTransform')
|
||
xf = wc.Dispatch(mu._oleobj_.Invoke(d2, 0, pythoncom.DISPATCH_METHOD, 1,
|
||
wc.VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_R8,
|
||
[1.0, 0, 0, 0, 1.0, 0, 0, 0, 1.0,
|
||
0.0, 0.0, -M(POST_H), 1.0, 0.0, 0.0, 0.0])))
|
||
try:
|
||
c.SetTransformAndSolve2(xf)
|
||
except Exception:
|
||
c.Transform2 = xf
|
||
doc.EditRebuild3
|
||
b = [round(x * 1000, 3) for x in c.GetBox(False, False)]
|
||
want = [X0, Y0, -(POST_H + FLOOR_T), X1, Y1, 0.0]
|
||
P(u'托盘装配 box %s 期望 %s %s'
|
||
% (b, want, u'✅' if all(abs(b[i] - want[i]) < 0.05 for i in range(6)) else u'⛔'))
|
||
|
||
for nm2, vid in ((u'tray_iso', 7), (u'tray_front', 1), (u'tray_right', 4)):
|
||
try:
|
||
doc.ShowNamedView2('', vid)
|
||
doc.ViewZoomtofit2
|
||
doc.SaveBMP(os.path.join(OUT, '%s.bmp' % nm2), 1600, 1100)
|
||
except Exception:
|
||
pass
|
||
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:
|
||
nn = idm.GetInterferenceCount
|
||
except AttributeError:
|
||
dd = idm._oleobj_.GetIDsOfNames('GetInterferenceCount')
|
||
nn = idm._oleobj_.Invoke(dd, 0, pythoncom.DISPATCH_PROPERTYGET, 1)
|
||
P(u' 干涉 %s 处(基线 360)' % nn)
|
||
hits = []
|
||
if nn:
|
||
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('TRAY' 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)[:5]:
|
||
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
|