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