# -*- coding: utf-8 -*- u"""量 PCIE 插槽**顶面开口**的真实长宽 —— 判断刀片舌头 36.0 插不插得进。 为什么不能用碰撞法:插槽模型把簧片触点建成了**刚性**的,两排触点自由间隙只有 1.22 mm,而 STEP 里刀片板厚 1.67 ⇒ 干涉检查在任何位置都报"插不进", 与舌头宽度无关。所以必须**直接量开口**。 两条互相独立的口径(两个都给,不一致就说明哪条的假设不成立): A. 顶面那个平面的**内环**(inner loop)包围盒 = 开口轮廓 B. 落在卡槽通道里(|y − 通道中心| 小)的所有面的 x 并集 = 通道长度 ⛔ 全程不碰 IInterferenceDetectionMgr(NOTES §24)。 """ import io, re, subprocess, sys, 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 prop(o, name): u"""0 参成员:多数是属性,但 GetFaces / GetLoops 这类是方法(NOTES §25)。 ⛔ 不能用 callable() 判断(COM 对象一律 callable,§2),只认 python 方法类型。""" v = getattr(o, name) if type(v).__name__ in ('method', 'function', 'builtin_function_or_method'): return v() return v def D(x): return x if hasattr(x, '_oleobj_') else wc.Dispatch(x) 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)) app = None try: app = wc.GetActiveObject('SldWorks.Application') print(u'✅ 复用现有实例(文档数 %s)' % app.GetDocumentCount) except Exception as e: print(u'✗ 连不上 (%s) → 自愈' % str(e)[:50]) if sw_pids(): subprocess.run(['taskkill', '/F', '/IM', 'SLDWORKS.exe'], capture_output=True) time.sleep(4) for k in range(20): try: app = wc.Dispatch('SldWorks.Application'); app.Visible = True _ = app.RevisionNumber; break except Exception: if k == 19: raise time.sleep(5) if app.GetDocumentCount == 0: print(u'导入底板 STEP…') data = app.GetImportFileData(BASE_STEP) err = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0) t0 = time.time(); app.LoadFile4(BASE_STEP, 'r', data, err) print(u' err=%s %.1fs' % (err.value, time.time() - t0)) part = None for d in app.GetDocuments: try: if d.GetType == 1 and 'PCIE' in d.GetTitle.upper() and '64P11L' in d.GetTitle.upper(): part = d; break except Exception: pass if part is None: print(u'⛔ 没找到插槽零件'); sys.exit(1) print(u'零件: %s' % part.GetTitle) # 取面数最多的那个实体 = 塑料壳 shell, nmax = None, -1 for bd in part.GetBodies2(0, True): b = D(bd) try: n = len(prop(b, 'GetFaces')) except Exception: continue if n > nmax: shell, nmax = b, n print(u'塑料壳实体:%d 个面' % nmax) faces = prop(shell, 'GetFaces') # ── 找顶面(法线 +Z、z 最高的平面)─────────────────────────────────── top, topz = None, -1e9 info = [] for f in faces: fc = D(f) try: s = prop(fc, 'GetSurface') if not prop(s, 'IsPlane'): continue nz = list(prop(s, 'PlaneParams'))[2] fb = [v * 1000 for v in prop(fc, 'GetBox')] info.append((fc, nz, fb)) if nz > 0.99 and fb[5] > topz: top, topz = fc, fb[5] except Exception: continue print(u'顶面 z = %.3f' % topz) # ── 口径 A:顶面的内环 ─────────────────────────────────────────────── print(u'\n=== 口径 A:顶面的环(内环 = 开口)===') try: loops = prop(top, 'GetLoops') print(u' 共 %d 个环' % len(loops)) for li, lp in enumerate(loops): L = D(lp) try: outer = prop(L, 'IsOuter') except Exception: outer = '?' xs, ys = [], [] try: for e in prop(L, 'GetEdges'): E = D(e) try: cp = list(prop(E, 'GetCurveParams2')) pts = [(cp[0], cp[1], cp[2]), (cp[3], cp[4], cp[5])] except Exception: pts = [] for p in pts: xs.append(p[0] * 1000); ys.append(p[1] * 1000) except Exception as ex: print(u' 环%d 取边失败 %s' % (li, str(ex)[:50])); continue if xs: print(u' 环%-2d 外环=%-5s x %8.3f~%8.3f (%6.3f) y %7.3f~%7.3f (%5.3f)' % (li, outer, min(xs), max(xs), max(xs) - min(xs), min(ys), max(ys), max(ys) - min(ys))) except Exception as e: print(u' ⛔ 取环失败: %s' % str(e)[:90]) # ── 口径 B:卡槽通道里所有面的 x 并集 ──────────────────────────────── print(u'\n=== 口径 B:卡槽通道内各面的 x 并集 ===') YC = 0.74 # 通道中心线(由两排触点 -0.56 / +2.04 推出) for halfw in (0.7, 0.9, 1.2): xs = [] for fc, nz, fb in info: ymid = (fb[1] + fb[4]) / 2.0 if abs(ymid - YC) < halfw and fb[5] > 1.5 and fb[2] < topz + 0.01: xs.append(fb[0]); xs.append(fb[3]) if xs: print(u' |y−%.2f|<%.1f ⇒ x %8.3f ~ %8.3f,通道长 **%.2f mm**' % (YC, halfw, min(xs), max(xs), max(xs) - min(xs))) print(u'\n对照:刀片舌头 36.00 mm(TONGUE_X0/X1 = 27.0/63.0,板局部)') print(u' 塑料体全长 41.00 mm,顶面平面跨度 39.00 mm')