# -*- coding: utf-8 -*- u"""从两份 STEP 全自动重建「底板 + 刀片」总装,供 place_and_probe.py 接手。 用法: python rebuild_assembly.py # 全自动:起 SolidWorks → 导两份 STEP → 建总装 → 插两块板 为什么要有这个脚本:SolidWorks 的运行对象表注册一旦被撤销(见 NOTES §22), 客户端侧无解,只能重启。重启后 `%TEMP%\\swx<旧PID>\\` 里的临时件全没了, 原装配体的底板零部件会丢引用 ⇒ 与其修,不如按 NOTES §15 那条**已验证的链路**重建。 ━━ 关键约定 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ · 被插的必须是**临时目录里的顶层 SLDASM**(`GetDocuments` 遍历着找), 不能另存(§11 那两个是废件),也不能猜文件名(真名是 `xxx**.step**.SLDASM`)。 · 用 `AddComponent4`(5 参),不是 `AddComponent5`(8 参恒返回 None,§15)。 · ⛔ `AddComponent4` 插进来的件会被**重心归零**(§14)⇒ 板坐标公式全失效。 对策:插完**强制把底板零部件的变换设成单位阵**,回读包围盒确认变回 `0.013~97.987`(= 原点回到板左下角),这样 place_and_probe 的公式可原样用。 · ⛔ `CreateTransform` 只能走 dispid + `VARIANT(VT_ARRAY|VT_R8)`;传 tuple 会**静默返回单位阵**。 """ import io import os import sys import time sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') import pythoncom import win32com.client as wc SRC = r'D:\DCDC\新建文件夹' BASE_STEP = os.path.join(SRC, '3D_BASE-PCB_2026-09-12.step') BLADE_STEP = os.path.join(SRC, 'PhoneRail_blade.step') IDENT = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0] def mk_xform(app, arr16): did = app._oleobj_.GetIDsOfNames('GetMathUtility') mu = wc.Dispatch(app._oleobj_.Invoke(did, 0, pythoncom.DISPATCH_METHOD, 1)) d2 = mu._oleobj_.GetIDsOfNames('CreateTransform') v = wc.VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_R8, list(arr16)) xf = wc.Dispatch(mu._oleobj_.Invoke(d2, 0, pythoncom.DISPATCH_METHOD, 1, v)) if max(abs(a - b) for a, b in zip(list(xf.ArrayData), arr16)) > 1e-9: raise RuntimeError(u'CreateTransform 回读不符(多半被静默降成单位阵)') return xf def launch(tries=20): u"""Dispatch 在没有实例时会**起一个新的**——这里正是我们要的。""" for k in range(tries): try: app = wc.Dispatch('SldWorks.Application') app.Visible = True _ = app.RevisionNumber return app except Exception as e: if k == tries - 1: raise print(u' 启动重试 %d/%d (%s)' % (k + 1, tries, str(e)[:60])) time.sleep(5) def import_step(app, path, tag): t0 = time.time() data = app.GetImportFileData(path) err = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0) doc = app.LoadFile4(path, 'r', data, err) print(u' %s 导入完成 err=%s 用时 %.1fs 文档数 %s' % (tag, err.value, time.time() - t0, app.GetDocumentCount)) return doc def find_top_sldasm(app, keyword): u"""在打开的文档里找临时目录下的顶层 SLDASM。⛔ 别用 glob 猜名字。""" best = None for d in app.GetDocuments: try: p, t = d.GetPathName, d.GetType except Exception: continue if not p or t != 2 or not p.upper().endswith('.SLDASM'): continue if keyword.lower() in os.path.basename(p).lower(): best = p return best def main(): for f in (BASE_STEP, BLADE_STEP): if not os.path.exists(f): print(u'⛔ 源文件不存在: %s' % f); return print(u'=== 启动 SolidWorks ===') app = launch() print(u' Revision %s 初始文档数 %s' % (app.RevisionNumber, app.GetDocumentCount)) print(u'\n=== 导入 STEP(底板 54 MB ≈ 20s,刀片 31 MB ≈ 11s)===') import_step(app, BASE_STEP, u'底板') import_step(app, BLADE_STEP, u'刀片') base_asm = find_top_sldasm(app, '3D_BASE-PCB') blade_asm = find_top_sldasm(app, 'PhoneRail_blade') print(u'\n 底板顶层: %s' % base_asm) print(u' 刀片顶层: %s' % blade_asm) if not base_asm or not blade_asm: print(u'⛔ 没找齐顶层 SLDASM,停手'); return print(u'\n=== 新建总装 ===') tmpl = None for pref in (9, 10, 8): # swDefaultTemplateAssembly 通常是 9 try: v = app.GetUserPreferenceStringValue(pref) if v and v.lower().endswith('.asmdot'): tmpl = v; print(u' 模板(pref=%d): %s' % (pref, v)); break except Exception: pass if not tmpl: print(u'⛔ 取不到装配体模板路径,停手'); return asm = app.NewDocument(tmpl, 0, 0.0, 0.0) asm = app.ActiveDoc print(u' 新建: %s type=%s' % (asm.GetTitle, asm.GetType)) print(u'\n=== 插入两块板(AddComponent4,5 参)===') for tag, p in ((u'底板', base_asm), (u'刀片', blade_asm)): c = asm.AddComponent4(p, '', 0.0, 0.0, 0.0) print(u' %s → %s' % (tag, c.Name2 if c else u'⛔ None(换 AddComponent4 也不行才是真问题)')) asm.EditRebuild3 comps = asm.GetComponents(True) print(u'\n 顶层零部件 %d 个' % len(comps)) base = [c for c in comps if 'blade' not in c.Name2.lower()][0] print(u'\n=== 把底板变换归为单位阵(撤销 AddComponent4 的重心归零,§14)===') print(u' 归零前 box %s' % [round(v * 1000, 3) for v in base.GetBox(False, False)]) try: base.SetTransformAndSolve2(mk_xform(app, IDENT)) except Exception: base.Transform2 = mk_xform(app, IDENT) asm.EditRebuild3 b = [round(v * 1000, 3) for v in base.GetBox(False, False)] print(u' 归零后 box %s' % b) ok = abs(b[0] - 0.013) < 0.2 and abs(b[3] - 97.987) < 0.2 print(u' %s' % (u'✅ 原点回到板左下角,place_and_probe 的公式可直接用' if ok else u'⛔ 与预期 0.013~97.987 不符,公式不能直接用,先查')) for c in comps: print(u' %-36s box %s' % (c.Name2[:36], [round(v * 1000, 2) for v in c.GetBox(False, False)])) print(u'\n完成。接着跑:python place_and_probe.py sweep 2') if __name__ == '__main__': main()