文件
phonerail/.claude/r2/shell/endplate_v10.py
T

260 行
11 KiB
Python
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# -*- coding: utf-8 -*-
u"""端板 v10 —— 15 mm 分风腔版(2026-09-13 用户拍板「两端各加 11 mm」)。
与 v9 的三处差别,每一处都有实测依据:
1. **L 脚加长 25 → 36**。端板整体外移 11(西端外表面 −27.4 → −38.4),
脚必须仍够到托盘上那两个锁孔(装配 x = −1.9 / 99.9,托盘不动)。
本地孔位随之 25.5 → **36.5**(离脚端 1.9,与 v9 的相对位置一模一样)。
2. **四根角柱**(本件出两根,东端板靠 180° 旋转出另两根)。
这是为了修**两条已坐实的螺丝缺陷**,不是新功能:
⛔ C 组(顶盖→端板)原打在 x=−13.70,可端板在 z=72 那一层**只有 2.4 厚的板本体**
(x −27.4~−25),L 脚在底部 z 0~2.4 ⇒ 4 颗螺丝拧的是空气。
贴合面分析给的「27.4 × 103.7 重叠区」是**包围盒**口径,看不出这一点
—— 与 NOTES 里「包围盒对通孔没有分辨力」同一族的假判据。
⛔ A 组(侧板→端板)要拧进 2.4 的板边,攻不住 M3;v6/v7/v8 三版凸台全部失败
(面上草图只有第一次生效、凸块被板体吞并)。
✅ 角柱把两组螺丝要的「肉」一次给够:柱截面 8(y) × 10(x)、通高 z 0~72。
C 组沿 z 从柱顶打进去、A 组沿 y 从侧板打进柱侧面,两个方向都有 8~10 mm 材料。
做法是**右视基准面草图 + 沿 x 拉伸**,与 L 脚同款 —— 本机唯一可靠的那条路。
角柱摆位(避让实测过的一切):
· 南柱 y −1.655~6.655、北柱 y 96.035~104.345 —— **关于风口中心 51.345 严格对称**,
东端板绕 z 翻转后自动对位(v5 那次不对称吃过亏)。
· 风扇占 y 19.87~82.82 ⇒ 两柱都不碰风扇。
· 柱沿 x 只伸 10(装配 x −36~−26),底板 PCB 从 x=0.01 才开始 ⇒ 不压板。
3. 其余(Ø60 风口、4 × Ø3.4 风扇螺孔、板厚、y 对称范围、z 范围)**与 v9 逐字相同**。
⚠ 仍留给 GUI 的孔(本机 `FeatureCut4` 做不出这两类轴向):
· C 组 4 × Ø4.2 深 6(沿 z,柱顶面 z=72,装配 x=−31/129、y=2.50/100.19)
· A 组 8 × Ø4.2 深 6(沿 y,柱的南/北外侧面,z=10 与 z=60)
——现在**至少有肉可打**了,这是 v10 的全部意义。
"""
import io
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\\EndPlate_v10.SLDPRT'
EMPTY = wc.VARIANT(pythoncom.VT_DISPATCH, None)
PLATE_T = 2.4
Y0, Y1 = 51.345 - 53.0, 51.345 + 53.0 # −1.655 ~ 104.345(以风口中心对称)
Z0, Z1 = -10.0, 72.0
FAN_CY, FAN_CZ = 51.345, 37.85
VENT_D = 60.0
FAN_SCREW = 50.0
SCREW_D = 3.4
# ⛔ 25 → 36:端板外移 11 之后,脚要够到托盘上原有的锁孔(装配 x −1.9 / 99.9)。
FOOT_LEN = 36.0
FOOT_T = 2.4
FOOT_HOLES_LOCAL = [(36.5, 21.345), (36.5, 81.345)] # 本地 x = 装配 x − (−38.4)
# 角柱:截面 8(y) × 通高 z 0~72,沿 x 从板内表面伸 POST_LEN
POST_W = 8.0
POST_LEN = 10.0
POST_Z0, POST_Z1 = 0.0, Z1 # 坐在托盘面板上表面,顶面与板顶齐平
POST_Y = [(Y0, Y0 + POST_W), (Y1 - POST_W, Y1)]
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
def RZ(z):
u"""右视基准面:草图 u → 模型 −z(NOTES §34 实测)"""
return M(-z)
app = wc.GetActiveObject('SldWorks.Application')
# 只关同名的旧 v10(重跑时),不碰 v9 —— 总装还引用着它
for d in list(app.GetDocuments or []):
try:
t = prop(d, 'GetTitle') or ''
if 'ENDPLATE_V10' in t.upper():
app.CloseDoc(t)
P(u'关掉旧文档 %s' % t)
except Exception:
pass
tmpl = app.GetUserPreferenceStringValue(8)
app.NewDocument(tmpl, 0, 0.0, 0.0)
part = app.ActiveDoc
P(u'新建零件 %s' % prop(part, 'GetTitle'))
sm, fm = part.SketchManager, part.FeatureManager
def sk_name():
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 extrude(depth, rev=False):
if pick(sk_name(), '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 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 faces():
b = bodies()
return prop(D(b[0]), 'GetFaces') if b else []
def cyl_hist():
rs = {}
for f in faces():
s = prop(D(f), 'GetSurface')
if prop(s, 'IsCylinder'):
r = round(list(prop(s, 'CylinderParams'))[6] * 1000, 2)
rs[r] = rs.get(r, 0) + 1
return dict(sorted(rs.items()))
def check(tag, want, tol=0.02):
got = bbox()
ok = got and all(abs(got[i] - want[i]) <= tol for i in range(6))
P(u' %s box %s 期望 %s %s' % (tag, got, want, u'✅' if ok else u'⛔'))
return ok
# ── 1) 板本体 + Ø60 风口 + 4 风扇螺孔:同一张草图一次成形 ─────────────────
P(u'\n[1] 板本体 %.0f(z) × %.0f(y) × %.1f,含 Ø%.0f 风口 + 4× Ø%.1f 螺孔'
% (Z1 - Z0, Y1 - Y0, PLATE_T, VENT_D, SCREW_D))
pick(u'右视基准面', 'PLANE')
sm.InsertSketch(True)
sm.AddToDB = True # ⛔ 不开这个,自动捕捉会静默改掉圆心与半径
sm.CreateCornerRectangle(RZ(Z0), M(Y0), 0.0, RZ(Z1), M(Y1), 0.0)
sm.CreateCircleByRadius(RZ(FAN_CZ), M(FAN_CY), 0.0, M(VENT_D / 2))
for dy in (-FAN_SCREW / 2, FAN_SCREW / 2):
for dz in (-FAN_SCREW / 2, FAN_SCREW / 2):
sm.CreateCircleByRadius(RZ(FAN_CZ + dz), M(FAN_CY + dy), 0.0, M(SCREW_D / 2))
sm.AddToDB = False
segs = prop(D(prop(sm, 'ActiveSketch')), 'GetSketchSegments')
P(u' 草图 segment = %d(期望 4 线 + 5 圆 = 9)' % (len(segs) if segs else 0))
for _s in (segs or []): # 回读几何:只数条数查不出吸附
_ss = D(_s)
if prop(_ss, 'GetType') != 1:
continue
_c = D(prop(_ss, 'GetCenterPoint2'))
P(u' 圆 r=%.3f 圆心=(%.3f, %.3f)'
% (prop(_ss, 'GetRadius') * 1000, prop(_c, 'X') * 1000, prop(_c, 'Y') * 1000))
sm.InsertSketch(True)
extrude(PLATE_T)
if not check(u'板本体', [0.0, Y0, Z0, PLATE_T, Y1, Z1]):
P(u'⛔ 停手'); sys.exit(2)
h = cyl_hist()
P(u' 圆柱面直方图 %s' % h)
if h.get(30.0, 0) < 1 or h.get(1.7, 0) < 4:
P(u'⛔ 风口或螺孔没成形(包围盒看不出来),停手'); sys.exit(3)
# ── 2) L 脚(加长到 36)─────────────────────────────────────────────────
P(u'\n[2] L 脚 长 %.0f 厚 %.1f(z %.1f~%.1f,压在托盘面板上表面)'
% (FOOT_LEN, FOOT_T, 0.0, FOOT_T))
pick(u'右视基准面', 'PLANE')
sm.InsertSketch(True)
sm.AddToDB = True
sm.CreateCornerRectangle(RZ(0.0), M(Y0), 0.0, RZ(FOOT_T), M(Y1), 0.0)
sm.AddToDB = False
sm.InsertSketch(True)
extrude(PLATE_T + FOOT_LEN)
if not check(u'加 L 脚后', [0.0, Y0, Z0, PLATE_T + FOOT_LEN, Y1, Z1]):
P(u'⛔ 停手'); sys.exit(4)
# ── 3) 两根角柱(C 组与 A 组螺丝的「肉」)────────────────────────────────
P(u'\n[3] 角柱 ×2:截面 %.0f(y) × 通高 z %.1f~%.1f,沿 x 伸 %.0f'
% (POST_W, POST_Z0, POST_Z1, POST_LEN))
P(u' 南柱 y %.3f~%.3f / 北柱 y %.3f~%.3f(关于 %.3f 对称,风扇占 19.87~82.82 不碰)'
% (POST_Y[0][0], POST_Y[0][1], POST_Y[1][0], POST_Y[1][1], FAN_CY))
nf_before = len(faces())
pick(u'右视基准面', 'PLANE')
sm.InsertSketch(True)
sm.AddToDB = True
for py0, py1 in POST_Y:
sm.CreateCornerRectangle(RZ(POST_Z0), M(py0), 0.0, RZ(POST_Z1), M(py1), 0.0)
sm.AddToDB = False
_g = prop(D(prop(sm, 'ActiveSketch')), 'GetSketchSegments')
P(u' 草图 segment = %d(期望 8)' % (len(_g) if _g else 0))
sm.InsertSketch(True)
extrude(PLATE_T + POST_LEN)
# ⛔ 包围盒在这一步**没有分辨力**:柱只伸 12.4 < 脚的 38.4,box 一点不变。
# ⇒ 判据换成「x = PLATE_T+POST_LEN 处有没有法线 +x 的端面」,逐根数。
if not check(u'加角柱后(box 应与上一步相同)', [0.0, Y0, Z0, PLATE_T + FOOT_LEN, Y1, Z1]):
P(u'⛔ 停手'); sys.exit(5)
x_end = PLATE_T + POST_LEN
hit = []
for f in faces():
fc = D(f)
s = prop(fc, 'GetSurface')
if not prop(s, 'IsPlane'):
continue
if list(prop(s, 'PlaneParams'))[0] < 0.99: # 只要法线 +x
continue
fb = [round(v * 1000, 3) for v in prop(fc, 'GetBox')]
if abs(fb[0] - x_end) < 0.02 and abs(fb[3] - x_end) < 0.02:
hit.append((fb[1], fb[4], fb[2], fb[5], round(prop(fc, 'GetArea') * 1e6, 1)))
P(u' x=%.1f 处法线 +x 的端面 %d 个(期望 2 = 两根柱)' % (x_end, len(hit)))
for y0f, y1f, z0f, z1f, a in sorted(hit):
P(u' y %8.3f~%8.3f z %7.2f~%7.2f 面积 %7.1f mm²' % (y0f, y1f, z0f, z1f, a))
P(u' 面数 %d → %d' % (nf_before, len(faces())))
if len(hit) != 2:
P(u'⛔ 角柱没成形(包围盒查不出来,靠端面计数抓到的),停手'); sys.exit(6)
want_area = POST_W * (POST_Z1 - POST_Z0)
if any(abs(a - want_area) > 1.0 for *_, a in hit):
P(u'⚠ 柱端面面积与期望 %.0f mm² 不符 —— 可能被脚吞并了一段,人工看一眼' % want_area)
P(u'\n最终 box %s' % bbox())
P(u'最终圆柱面 %s' % cyl_hist())
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))
if not os.path.exists(DST):
P(u'⛔ 没落盘,停手(零件留着便于排查)'); sys.exit(7)
P(u'⚠ 零件保持打开(AddComponent4 要求目标文档在打开状态)')