skill:solidworks-assembly 实测笔记与脚本、easyeda-sch-geometry 补充

Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
zhou yang
2026-10-04 19:24:42 +08:00
共同撰写人 Claude Opus 5.5
父节点 22a255acd7
当前提交 cd01640c40
共修改 8 个文件,包含 2283 行新增和 1 行删除
@@ -0,0 +1,153 @@
# -*- 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()