R2:底板 / 刀片布局布线脚本、快照、截图与规划文档更新(含外壳、打印件、布线归档)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -0,0 +1,245 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
u"""**底盖 v4**(2026-09-13 外观美化 A + E)。
|
||||
|
||||
与 v3 的差别:
|
||||
A 四边**内缩 0.5**:外形 x −24.3~115.4、y −3.555~106.245(顶盖外沿 −24.8~115.9 / −4.055~106.745),
|
||||
接缝处形成一圈 0.5 阴影缝,藏住两件的打印公差;四角圆角用 R2.5(与顶盖 R3 同心)——圆角由 add_fillets 后做。
|
||||
E **加厚 2.4 → 3.6**(向下长,底盖顶面仍在 z=−10,机内尺寸不变;整机高 +1.2):
|
||||
· 四角 M3 过孔下方挖**沉头 Ø6.5 深 2.0**,螺丝头不外露,头下还剩 1.6(2.4 厚时只剩 0.4,会压裂)
|
||||
· 4 个**脚垫窝 Ø10 深 1.0**(贴 Ø10 硅胶脚垫),放在螺丝内侧、不盖住螺丝 ⇒ 拆盖不用撕脚垫
|
||||
PCB 立柱、柱顶螺母孔、四角过孔位置与 v3 相同。
|
||||
|
||||
本地坐标:板 z −3.6~0、立柱 0~10;装配 dz = −10 ⇒ 板 z −13.6~−10。
|
||||
面上切除沿用 NOTES §37:进草图后读 ModelToSketchTransform 验平面,再按变换精确画。
|
||||
"""
|
||||
import io
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', write_through=True)
|
||||
import pythoncom
|
||||
import win32com.client as wc
|
||||
|
||||
DST = u'D:\\DCDC\\新建文件夹\\5blades\\BottomPlate_v4.SLDPRT'
|
||||
EMPTY = wc.VARIANT(pythoncom.VT_DISPATCH, None)
|
||||
|
||||
INSET = 0.5
|
||||
X0, X1, Y0, Y1 = -24.8 + INSET, 115.9 - INSET, -4.055 + INSET, 106.745 - INSET
|
||||
T = 3.6
|
||||
CORNER_HOLES = [(-11.0, 3.345), (-11.0, 99.345), (108.5, 3.345), (108.5, 99.345)]
|
||||
R_THRU = 1.7
|
||||
CB_R, CB_D = 3.25, 2.0 # 沉头 Ø6.5 深 2
|
||||
PCB_POSTS = [(4.0, 4.0), (94.0, 4.0), (4.0, 94.0), (94.0, 94.0)]
|
||||
POST_R, POST_H, R_INS = 4.0, 10.0, 2.1
|
||||
FOOT_R, FOOT_D = 5.0, 1.0 # 脚垫窝 Ø10 深 1
|
||||
_XC, _YC = (X0 + X1) / 2.0, 51.345
|
||||
FEET = [(_XC - 45.0, _YC - 43.0), (_XC + 45.0, _YC - 43.0), (_XC - 45.0, _YC + 43.0), (_XC + 45.0, _YC + 43.0)]
|
||||
|
||||
|
||||
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')
|
||||
for d in list(app.GetDocuments or []):
|
||||
try:
|
||||
t = prop(d, 'GetTitle') or ''
|
||||
if 'BOTTOMPLATE_V4' in t.upper():
|
||||
app.CloseDoc(t)
|
||||
except Exception:
|
||||
pass
|
||||
app.NewDocument(app.GetUserPreferenceStringValue(8), 0, 0.0, 0.0)
|
||||
part = app.ActiveDoc
|
||||
P(u'新建零件 %s' % prop(part, 'GetTitle'))
|
||||
sm, fm = part.SketchManager, part.FeatureManager
|
||||
|
||||
|
||||
def last_sketch():
|
||||
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 boss(depth, rev):
|
||||
if pick(last_sketch(), '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 faces():
|
||||
b = bodies()
|
||||
return prop(D(b[0]), 'GetFaces') if b else []
|
||||
|
||||
|
||||
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 cyls():
|
||||
out = []
|
||||
for f in faces():
|
||||
fc = D(f)
|
||||
s = prop(fc, 'GetSurface')
|
||||
if prop(s, 'IsCylinder'):
|
||||
fb = [v * 1000 for v in prop(fc, 'GetBox')]
|
||||
out.append((round(list(prop(s, 'CylinderParams'))[6] * 1000, 2), fb))
|
||||
return out
|
||||
|
||||
|
||||
def planes():
|
||||
out = []
|
||||
for f in faces():
|
||||
fc = D(f)
|
||||
s = prop(fc, 'GetSurface')
|
||||
if not prop(s, 'IsPlane'):
|
||||
continue
|
||||
n = list(prop(s, 'PlaneParams'))[:3]
|
||||
if prop(fc, 'FaceInSurfaceSense'):
|
||||
n = [-v for v in n]
|
||||
ax = max(range(3), key=lambda i: abs(n[i]))
|
||||
out.append((ax, 1 if n[ax] > 0 else -1, [v * 1000 for v in prop(fc, 'GetBox')], prop(fc, 'GetArea') * 1e6, fc))
|
||||
return out
|
||||
|
||||
|
||||
def open_face_sketch(ax, sign, coord, tag):
|
||||
for attempt in range(1, 4):
|
||||
best, ba = None, -1.0
|
||||
for a_, s_, fb, area, fc in planes():
|
||||
if a_ == ax and s_ == sign and abs(fb[ax] - coord) < 0.02 and abs(fb[ax + 3] - coord) < 0.02 and area > ba:
|
||||
best, ba = fc, area
|
||||
if best is None:
|
||||
P(u' ⛔ %s 选不到面' % tag); sys.exit(4)
|
||||
part.ClearSelection2(True)
|
||||
best.Select4(False, EMPTY)
|
||||
sm.InsertSketch(True)
|
||||
sk = prop(sm, 'ActiveSketch')
|
||||
if sk:
|
||||
arr = list(prop(D(sk), 'ModelToSketchTransform').ArrayData)
|
||||
Mx = [[arr[3 * c + r] for c in range(3)] for r in range(3)]
|
||||
X = (Mx, arr[9:12])
|
||||
nax = max(range(3), key=lambda i: abs(Mx[2][i]))
|
||||
probe = [0.0, 0.0, 0.0]
|
||||
probe[ax] = coord / 1000.0
|
||||
sz = (sum(Mx[2][i] * probe[i] for i in range(3)) + arr[11]) * 1000
|
||||
ok = nax == ax and abs(sz) < 0.01
|
||||
P(u' %s 进草图 第 %d 次:法线轴 %s、面上点草图 z=%.4f %s'
|
||||
% (tag, attempt, 'xyz'[nax], sz, u'✅' if ok else u'✗ 退出重进'))
|
||||
if ok:
|
||||
return X
|
||||
sm.InsertSketch(True)
|
||||
P(u' ⛔ %s 进不到目标面' % tag); sys.exit(4)
|
||||
|
||||
|
||||
def to_sketch(X, p):
|
||||
Mx, t = X
|
||||
q = [v / 1000.0 for v in p]
|
||||
return [sum(Mx[r][c] * q[c] for c in range(3)) + t[r] for r in range(3)]
|
||||
|
||||
|
||||
def cut(depth):
|
||||
if pick(last_sketch(), 'SKETCH') != 1:
|
||||
return None
|
||||
return fm.FeatureCut4(True, False, False, 0, 0, M(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'\n[1] 平板 %.1f × %.1f × %.1f(四边内缩 %.1f)+ 四角 Ø%.1f 过孔' % (X1 - X0, Y1 - Y0, T, INSET, 2 * R_THRU))
|
||||
pick(u'前视基准面', 'PLANE')
|
||||
sm.InsertSketch(True)
|
||||
sm.AddToDB = True
|
||||
sm.CreateCornerRectangle(M(X0), M(Y0), 0.0, M(X1), M(Y1), 0.0)
|
||||
for hx, hy in CORNER_HOLES:
|
||||
sm.CreateCircleByRadius(M(hx), M(hy), 0.0, M(R_THRU))
|
||||
sm.AddToDB = False
|
||||
sm.InsertSketch(True)
|
||||
boss(T, True)
|
||||
P(u' box %s' % bbox())
|
||||
|
||||
P(u'\n[2] PCB 立柱 Ø%.0f × %.0f + 柱顶 Ø%.1f 螺母孔' % (2 * POST_R, POST_H, 2 * R_INS))
|
||||
pick(u'前视基准面', 'PLANE')
|
||||
sm.InsertSketch(True)
|
||||
sm.AddToDB = True
|
||||
for hx, hy in PCB_POSTS:
|
||||
sm.CreateCircleByRadius(M(hx), M(hy), 0.0, M(POST_R))
|
||||
sm.CreateCircleByRadius(M(hx), M(hy), 0.0, M(R_INS))
|
||||
sm.AddToDB = False
|
||||
sm.InsertSketch(True)
|
||||
boss(POST_H, False)
|
||||
b = bbox()
|
||||
P(u' box %s(期望 x %.1f~%.1f y %.3f~%.3f z %.1f~%.1f)' % (b, X0, X1, Y0, Y1, -T, POST_H))
|
||||
if b is None or any(abs(b[i] - w) > 0.02 for i, w in enumerate([X0, Y0, -T, X1, Y1, POST_H])):
|
||||
P(u'⛔ 停手'); sys.exit(2)
|
||||
|
||||
for tag, pts, r, dep in ((u'沉头', CORNER_HOLES, CB_R, CB_D), (u'脚垫窝', FEET, FOOT_R, FOOT_D)):
|
||||
P(u'\n[3] 底面 %s ×4:Ø%.1f 深 %.1f' % (tag, 2 * r, dep))
|
||||
X = open_face_sketch(2, -1, -T, tag)
|
||||
sm.AddToDB = True
|
||||
for hx, hy in pts:
|
||||
q = to_sketch(X, (hx, hy, -T))
|
||||
sm.CreateCircleByRadius(q[0], q[1], 0.0, M(r))
|
||||
sm.AddToDB = False
|
||||
sm.InsertSketch(True)
|
||||
c = cut(dep)
|
||||
got = [fb for rr, fb in cyls() if abs(rr - r) < 0.01]
|
||||
okp = len(got) == 4 and all(abs(fb[2] + T) < 0.05 and abs(fb[5] - (-T + dep)) < 0.05 for fb in got) and \
|
||||
all(any(abs((fb[0] + fb[3]) / 2 - hx) < 0.05 and abs((fb[1] + fb[4]) / 2 - hy) < 0.05 for fb in got) for hx, hy in pts)
|
||||
P(u' 切除 → %r;R%.2f 圆柱面 %d 个,z %.1f~%.1f、圆心在位 %s'
|
||||
% (prop(c, 'Name') if c else c, r, len(got), -T, -T + dep, u'✅' if okp else u'⛔'))
|
||||
if not okp:
|
||||
P(u'⛔ 不存盘'); sys.exit(3)
|
||||
|
||||
h = {}
|
||||
for rr, fb in cyls():
|
||||
h[rr] = h.get(rr, 0) + 1
|
||||
thru = [fb for rr, fb in cyls() if abs(rr - R_THRU) < 0.01]
|
||||
ins = [fb for rr, fb in cyls() if abs(rr - R_INS) < 0.01]
|
||||
P(u'\n[4] 验收 直方图 %s(期望 {1.7: 4, 2.1: 4, 3.25: 4, 4.0: 4, 5.0: 4})' % dict(sorted(h.items())))
|
||||
ok_thru = len(thru) == 4 and all(abs(fb[2] - (-T + CB_D)) < 0.05 and abs(fb[5]) < 0.05 for fb in thru)
|
||||
ok_ins = len(ins) == 4 and all(abs(fb[2]) < 0.05 and abs(fb[5] - POST_H) < 0.05 for fb in ins)
|
||||
P(u' 过孔 z %.1f~0(沉头以上 1.6 实体)%s;柱顶螺母孔 z 0~10 盲孔 %s'
|
||||
% (-T + CB_D, u'✅' if ok_thru else u'⛔', u'✅' if ok_ins else u'⛔'))
|
||||
want = {1.7: 4, 2.1: 4, 3.25: 4, 4.0: 4, 5.0: 4}
|
||||
if not (ok_thru and ok_ins) or any(h.get(k, 0) != v for k, v in want.items()):
|
||||
P(u'⛔ 不存盘'); sys.exit(3)
|
||||
|
||||
e1 = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0)
|
||||
w1 = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0)
|
||||
part.Extension.SaveAs3(DST, 0, 1, EMPTY, EMPTY, e1, w1)
|
||||
P(u'\n另存 err=%s 存在=%s %.0f KB'
|
||||
% (e1.value, os.path.exists(DST), os.path.getsize(DST) / 1024.0 if os.path.exists(DST) else 0))
|
||||
在新工单中引用
屏蔽一个用户