# -*- 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))