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

302 行
12 KiB
Python
原始文件 Blame 文件历史

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# -*- coding: utf-8 -*-
u"""端板 v11 = v10 的几何 + **试着把 A/C 两组螺母孔做出来**。
v10 把「肉」给够了(4 根角柱),但孔一直留给 GUI,因为 `FeatureCut4` 在本机
连续 7 次返回 None。⇒ 本脚本按 NOTES 里**唯一成功过的那个配方**再试一次:
✅ 成功的那次:托盘柱顶孔 —— 草图建在**实体的面**上(而且是 z 常量面),沿面法线切。
⛔ 失败的那些:草图建在**基准面**上(前视/右视)去切。
C 组(沿 z,打进角柱顶面 z=72)与那次**构型完全相同**,成功概率最高。
A 组(沿 y,打进角柱外侧面)是同一招换个面,一并试。
⚠ 两条必须守住的纪律:
1. **不动 v10**。v10 已在总装里验过(干涉 360、贴合 10 组),v11 没通过就原样丢掉。
2. **判据是圆柱面半径直方图,而且要双向看**:
· 新增:R2.1 应该 +6(4 个 A + 2 个 C);
· **没丢**:R1.7 仍是 4(风扇螺孔)、R30 仍是 1(风口)。
NOTES 记过一次面上草图的异常拉伸**把已有 8 个孔全吞掉**,只看"新增"是抓不到的。
局部坐标(西端板;装配 x = 本地 x − 38.4,z 与装配同值):
· 角柱 本地 x 2.4~12.4(中心 **7.4** = 装配 −31)、通高 z 0~72
南柱 y −1.655~6.345(中心 **2.345**)/北柱 y 96.345~104.345(中心 **100.345**)
· C 组 2 孔:柱顶面 z=72,(x,y) = (7.4, 2.345) / (7.4, 100.345),Ø4.2 深 6
· A 组 4 孔:柱外侧面 y=−1.655 与 y=104.345,(x,z) = (7.4, 10) / (7.4, 60),Ø4.2 深 6
"""
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_v11.SLDPRT'
EMPTY = wc.VARIANT(pythoncom.VT_DISPATCH, None)
PLATE_T = 2.4
Y0, Y1 = 51.345 - 53.0, 51.345 + 53.0
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
FOOT_LEN = 36.0
FOOT_T = 2.4
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)]
POST_CX = PLATE_T + POST_LEN / 2.0 # 7.4
INSERT_D, INSERT_DEPTH = 4.2, 6.0
C_HOLES = [(POST_CX, (POST_Y[0][0] + POST_Y[0][1]) / 2.0),
(POST_CX, (POST_Y[1][0] + POST_Y[1][1]) / 2.0)] # (x, y) @ z=72
A_Z = (10.0, 60.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
def RZ(z):
return M(-z)
app = wc.GetActiveObject('SldWorks.Application')
for d in list(app.GetDocuments or []):
try:
t = prop(d, 'GetTitle') or ''
if 'ENDPLATE_V11' 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 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 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 pick_face(axis, sign, coord, tag):
u"""遍历选面:法线沿 ±axis、且该面整体落在 coord 上;取面积最大的那个。
⛔ 绝不按坐标 SelectByID2 撞面 —— 实测会静默选中邻面(NOTES §34)。"""
ai = {'x': 0, 'y': 1, 'z': 2}[axis]
best, barea, bbx = None, -1.0, None
for f in faces():
fc = D(f)
s = prop(fc, 'GetSurface')
if not prop(s, 'IsPlane'):
continue
# ⛔ PlaneParams 是**曲面**法线,不是面的朝外法线。本机实测(14 个平面逐一核过):
# FaceInSurfaceSense == True ⇒ 朝外法线 = −PlaneParams
# FaceInSurfaceSense == False ⇒ 朝外法线 = +PlaneParams
# v11 第一次跑就栽在这:三个面一个都没选到(FeatureCut4 根本没被调用)。
n = list(prop(s, 'PlaneParams'))[ai]
if prop(fc, 'FaceInSurfaceSense'):
n = -n
if sign * n < 0.99:
continue
fb = [round(v * 1000, 3) for v in prop(fc, 'GetBox')]
if abs(fb[ai] - coord) > 0.02 or abs(fb[ai + 3] - coord) > 0.02:
continue
a = prop(fc, 'GetArea') * 1e6
if a > barea:
best, barea, bbx = fc, a, fb
if best is None:
P(u' ⛔ %s:没找到法线 %s%s、位于 %.3f 的平面' % (tag, '+' if sign > 0 else '-', axis, coord))
return None
part.ClearSelection2(True)
best.Select4(False, EMPTY)
n = part.SelectionManager.GetSelectedObjectCount2(-1)
P(u' %s:选中面 面积 %.1f mm² box x %.2f~%.2f y %.2f~%.2f z %.2f~%.2f 选中数=%d'
% (tag, barea, bbx[0], bbx[3], bbx[1], bbx[4], bbx[2], bbx[5], n))
return best if n == 1 else None
def cut(depth, tag):
if pick(sk_name(), 'SKETCH') != 1:
P(u' ⛔ %s 草图没选中' % tag)
return None
try:
c = 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)
except Exception as ex:
P(u' ⛔ %s FeatureCut4 异常 %s' % (tag, str(ex)[:90]))
return None
P(u' %s FeatureCut4 → %r' % (tag, prop(c, 'Name') if c else c))
return c
# ══ 1~3:与 v10 逐字相同的几何 ═══════════════════════════════════════════
P(u'\n[1] 板本体 + Ø60 风口 + 4 风扇螺孔')
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
sm.InsertSketch(True)
extrude(PLATE_T)
P(u' box %s 直方图 %s' % (bbox(), cyl_hist()))
P(u'\n[2] L 脚 长 %.0f' % FOOT_LEN)
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)
P(u'\n[3] 角柱 ×2')
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
sm.InsertSketch(True)
extrude(PLATE_T + POST_LEN)
base_box = bbox()
base_hist = cyl_hist()
P(u' box %s 直方图 %s' % (base_box, base_hist))
if base_hist.get(1.7, 0) != 4 or base_hist.get(30.0, 0) != 1:
P(u'⛔ 基础几何就不对,停手'); sys.exit(2)
# ══ 4:C 组 —— 柱顶面(z 常量面)沿 z 切,与托盘柱顶孔同款配方 ═══════════
P(u'\n[4] C 组 2 × Ø%.1f 深 %.1f(沿 z,柱顶面 z=%.1f)' % (INSERT_D, INSERT_DEPTH, Z1))
f = pick_face('z', +1, Z1, u'柱顶面')
if f is not None:
sm.InsertSketch(True)
sm.AddToDB = True
# ⚠ z 常量面上草图的 uv 恰好 = 模型 xy(托盘那次的成功是因为这个,不是"面草图接受模型坐标")
for hx, hy in C_HOLES:
sm.CreateCircleByRadius(M(hx), M(hy), M(Z1), M(INSERT_D / 2))
sm.AddToDB = False
_g = prop(D(prop(sm, 'ActiveSketch')), 'GetSketchSegments')
P(u' 草图 segment = %d(期望 2)' % (len(_g) if _g else 0))
sm.InsertSketch(True)
cut(INSERT_DEPTH, u'C 组')
P(u' 直方图 %s' % cyl_hist())
# ══ 5:A 组 —— 柱外侧面(y 常量面)沿 y 切 ═══════════════════════════════
# ⛔ y 端面草图的映射:u → 模型 x、v → 模型 **−z**,第三参恒 0(NOTES §35 实测)。
# 直接把模型 z 塞进 v 会把孔甩到 z 的镜像位置。
P(u'\n[5] A 组 4 × Ø%.1f 深 %.1f(沿 y,柱外侧面 y=%.3f / %.3f)'
% (INSERT_D, INSERT_DEPTH, Y0, Y1))
for ynom, sign, tag in ((Y0, -1, u'南侧面'), (Y1, +1, u'北侧面')):
f = pick_face('y', sign, ynom, tag)
if f is None:
continue
sm.InsertSketch(True)
sm.AddToDB = True
for az in A_Z:
sm.CreateCircleByRadius(M(POST_CX), M(-az), 0.0, M(INSERT_D / 2))
sm.AddToDB = False
_g = prop(D(prop(sm, 'ActiveSketch')), 'GetSketchSegments')
P(u' 草图 segment = %d(期望 2)' % (len(_g) if _g else 0))
sm.InsertSketch(True)
cut(INSERT_DEPTH, tag)
P(u' 直方图 %s' % cyl_hist())
# ══ 6:验收 —— 双向判据 ═════════════════════════════════════════════════
P(u'\n[6] 验收')
h = cyl_hist()
b = bbox()
n_new = h.get(round(INSERT_D / 2, 2), 0)
n_fan = h.get(1.7, 0)
n_vent = h.get(30.0, 0)
box_ok = b and all(abs(b[i] - base_box[i]) < 0.02 for i in range(6))
P(u' 包围盒 %s %s' % (b, u'✅ 未变' if box_ok else u'⛔ 变了,说明切歪了'))
P(u' Ø%.1f 螺母孔 = %d 个(期望 6 = 4 A + 2 C)%s'
% (INSERT_D, n_new, u'✅' if n_new == 6 else (u'⚠ 少了 %d 个,剩下的留 GUI' % (6 - n_new))))
P(u' Ø3.4 风扇孔 = %d(必须仍是 4)%s' % (n_fan, u'✅' if n_fan == 4 else u'⛔ 被吃掉了'))
P(u' Ø60 风口 = %d(必须仍是 1)%s' % (n_vent, u'✅' if n_vent == 1 else u'⛔ 被吃掉了'))
if n_fan != 4 or n_vent != 1 or not box_ok:
P(u'\n⛔⛔ 已有特征被破坏 —— **不存盘**,v10 保持可用。')
sys.exit(3)
if n_new == 0:
P(u'\n⛔ 一个孔都没切出来 ——**不存盘**,孔仍留给 GUI。'
u'(先看上面:是选面失败还是 FeatureCut4 返回 None,别混为一谈)')
sys.exit(4)
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))
P(u'⚠ 零件保持打开')