260 行
11 KiB
Python
260 行
11 KiB
Python
# -*- 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 要求目标文档在打开状态)')
|