# -*- coding: utf-8 -*- u"""决定性判据:底板上的 PCIE 插槽 3D 模型,是没建腔的实心块,还是真连接器? 结论口径: 实心块 → 实体体积/包围盒 ≈ 1,面数个位~十几 ⇒ 3D **量不出**插入深度 d, 也判不了"舌头是不是比腔长" 真连接器 → 体积比明显 <1,面数几百 ⇒ 干涉结论可信,可以继续追 为什么独立成一个脚本:它**不需要装配体、不需要刀片、更不碰 IInterferenceDetectionMgr**。 重度用 IDM 之后 SolidWorks 会丢掉运行对象表注册(NOTES §22/§24), 所以把这个判据做成"一个进程内从头到尾跑完"的形式,不依赖既有连接。 """ import io import os import re import subprocess import sys import time sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') import pythoncom import win32com.client as wc BASE_STEP = r'D:\DCDC\新建文件夹\3D_BASE-PCB_2026-09-12.step' def sw_pids(): out = subprocess.run(['tasklist', '/FI', 'IMAGENAME eq SLDWORKS.EXE', '/FO', 'CSV', '/NH'], capture_output=True, text=True, encoding='gbk', errors='ignore').stdout return sorted(re.findall(r'"SLDWORKS\.exe","(\d+)"', out, re.I)) def rot_count(): try: rot = pythoncom.GetRunningObjectTable() return sum(1 for _ in rot.EnumRunning()) except Exception as e: return u'枚举失败 %s' % str(e)[:40] print(u'=== 诊断 ===') print(u' SLDWORKS 进程: %s' % sw_pids()) print(u' 运行对象表条数: %s' % rot_count()) app = None try: app = wc.GetActiveObject('SldWorks.Application') print(u' ✅ 现有实例可连,不重启') except Exception as e: print(u' ✗ 连不上 (%s)' % str(e)[:50]) if app is None: if sw_pids(): print(u'\n=== 结束现有实例(已失联,救不回来)===') subprocess.run(['taskkill', '/F', '/IM', 'SLDWORKS.exe'], capture_output=True, text=True) time.sleep(4) print(u' 剩余: %s' % sw_pids()) print(u'\n=== 启动新实例 ===') for k in range(20): try: app = wc.Dispatch('SldWorks.Application') app.Visible = True _ = app.RevisionNumber break except Exception as e: if k == 19: raise print(u' 重试 %d/20 (%s)' % (k + 1, str(e)[:50])) time.sleep(5) print(u' Revision %s 文档数 %s' % (app.RevisionNumber, app.GetDocumentCount)) if app.GetDocumentCount == 0: print(u'\n=== 导入底板 STEP(≈50 s)===') t0 = time.time() data = app.GetImportFileData(BASE_STEP) err = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0) app.LoadFile4(BASE_STEP, 'r', data, err) print(u' err=%s 用时 %.1fs 文档数 %s' % (err.value, time.time() - t0, app.GetDocumentCount)) # ── 直接在打开的文档里按标题找插槽**零件**,不经装配树 ────────────────── print(u'\n=== 找插槽零件 ===') cand = [] for d in app.GetDocuments: try: t, ty = d.GetTitle, d.GetType except Exception: continue if ty == 1 and 'PCIE' in t.upper() and '64P11L' in t.upper(): cand.append(d) print(u' 命中 %d 个零件文档' % len(cand)) if not cand: print(u' ⛔ 没找到;打开的零件标题样本:') for d in list(app.GetDocuments)[:12]: try: if d.GetType == 1: print(u' %s' % d.GetTitle[:80]) except Exception: pass sys.exit(1) part = cand[0] print(u' 用: %s' % part.GetTitle) bb = [v * 1000 for v in part.GetPartBox(True)] bbv = (bb[3] - bb[0]) * (bb[4] - bb[1]) * (bb[5] - bb[2]) print(u' 包围盒 %.2f x %.2f x %.2f = %.1f mm^3' % (bb[3] - bb[0], bb[4] - bb[1], bb[5] - bb[2], bbv)) bodies = None for args in ((0, True), (-1, True), (0, False)): try: bodies = part.GetBodies2(*args) if bodies: print(u' GetBodies2%s → %d 个实体' % (str(args), len(bodies))) break except Exception as e: print(u' GetBodies2%s 失败 %s' % (str(args), str(e)[:60])) if not bodies: print(u' ⛔ 取不到实体'); sys.exit(1) tot, faces = 0.0, 0 for i, bd in enumerate(bodies): o = bd if hasattr(bd, 'GetMassProperties') else wc.Dispatch(bd) try: tot += o.GetMassProperties(1.0)[3] * 1e9 except Exception as e: print(u' 实体%d 体积失败 %s' % (i, str(e)[:60])) try: nf = o.GetFaceCount faces += nf if i < 8: print(u' 实体%-2d 面数 %s' % (i, nf)) except Exception: pass r = tot / bbv if bbv else 0 print(u'\n 实体体积 %.1f mm^3 / 包围盒 %.1f = **%.3f**' % (tot, bbv, r)) print(u' 总面数 **%d**%s' % (faces, u' ⚠ 0 是假值(实体不可能 0 面),忽略它' if faces == 0 else u'')) print() # ⛔⛔ 教训:原来这里写的是 `if r > 0.80 or faces < 30`, # 结果 `GetFaceCount` 返回假值 0 → `faces < 30` 成立 → 一票推翻了 # 体积比 0.492 给出的相反证据,打印出错误的"实心块"结论。 # ⇒ 不要把强弱不同的信号用 `or` 并列;面数只在**读到非 0** 时才参与判断。 faces_ok = faces > 0 if r > 0.80 or (faces_ok and faces < 30): print(u' ⇒ **实心块,没有槽腔**。') print(u' 3D 量不出插入深度 d;"舌头 36.0 比腔长"也**无法由 3D 判定** ——') print(u' 刀片停在塑料体顶面纯粹是因为模型没开口。只能靠卡尺实测。') else: print(u' ⇒ **模型建了腔**,几何冲突是真的,干涉结论可信,可以继续追。')