Files
wheel/scripts/update_assembly_latest_standoff.FCMacro

92 lines
6.5 KiB
Plaintext

from pathlib import Path
import FreeCAD as A, FreeCADGui as G, Part, Import
import math, json, os, time, traceback
V=A.Vector
root=Path(r'C:\projects\wheel')
out=root/'models'/'assembly_latest_standoff'
out.mkdir(parents=True,exist_ok=True)
try:
d=A.openDocument(str(root/'models'/'symmetric_corner_18_concept'/'Assembly_对称角码装配_概念.FCStd'))
d.recompute()
for n in ['EncoderPlateConcept']+['EncoderHex%d'%i for i in range(4)]+['EncoderSpacer%d'%i for i in range(4)]:
o=d.getObject(n)
if o: o.ViewObject.Visibility=False
pdoc=A.openDocument(str(root/'models'/'encoder_standoff_plate_print'/'EncoderPlate.FCStd'))
plate=pdoc.getObject('EncoderPlate').Shape.copy()
plate.translate(V(0,10,11))
po=d.addObject('PartDesign::Feature','FinalEncoderPlate')
po.Shape=plate;po.Label='Encoder printable plate, 10mm standoffs + 1mm shim';po.ViewObject.ShapeColor=(.08,.30,.85)
A.closeDocument(pdoc.Name)
# Move sensor PCB and chip by the same 0.1 mm as the real plate.
for n in ['PCB','Chip']+[o.Name for o in d.Objects if o.Name.startswith('PCBHead')]:
o=d.getObject(n)
if o:
sh=o.Shape.copy();sh.translate(V(0,0,.1));o.Placement=A.Placement();o.Shape=sh
def obj(name,shape,label,color):
o=d.addObject('PartDesign::Feature',name);o.Shape=shape.removeSplitter();o.Label=label;o.ViewObject.ShapeColor=color;o.ViewObject.LineColor=(.12,.12,.12);return o
fast=[]
for i in range(4):
a=math.radians(45+90*i);x=25*math.cos(a);y=10+25*math.sin(a)
rr=7/(2*math.cos(math.pi/6))
p=[V(x+rr*math.cos(math.radians(30+60*k)),y+rr*math.sin(math.radians(30+60*k)),0) for k in range(6)];p.append(p[0])
body=Part.Face(Part.makePolygon(p)).extrude(V(0,0,10))
body=body.cut(Part.makeCylinder(1.6,4,V(x,y,6)))
# Placeholder stud length 5 mm, nominal diameter: actual hardware TBD.
body=body.fuse(Part.makeCylinder(2,5,V(x,y,-5)))
st=obj('BrassStandoff%d'%i,body,'M4 brass stand-off body 10 mm AF7, male 5 mm EXAMPLE',(.83,.65,.23));fast.append(st)
sh=Part.makeCylinder(4.5,1,V(x,y,10)).cut(Part.makeCylinder(2.2,1,V(x,y,10)))
wash=obj('StandoffShim%d'%i,sh,'M4 adjustment washer 1 mm assumed',(.75,.75,.76));fast.append(wash)
screw=Part.makeCylinder(1.95,6.2,V(x,y,8)).fuse(Part.makeCylinder(3.5,2,V(x,y,14.2)))
sc=obj('EncoderScrew%d'%i,screw,'M4 fixing screw placeholder; length not specified',(.65,.65,.68));fast.append(sc)
d.recompute()
objlist=[o for o in d.Objects if hasattr(o,'Shape') and not o.Shape.isNull() and o.ViewObject.Visibility]
# Intersections for panel, standoff, washers and heads against unrelated visible objects.
ignore={'Motor','PCB','Chip','Holder','Magnet'} # checked separately where appropriate; PCB/plate intentionally meet
potential=[]
for a in [po]+fast:
for b in objlist:
if a==b or b.Name in ignore or b in fast or b==po:continue
if not a.Shape.BoundBox.intersect(b.Shape.BoundBox):continue
v=a.Shape.common(b.Shape).Volume
if v>0.001:potential.append([a.Name,b.Name,round(v,5)])
# PCB and chip dimensions and actual modeled gap, if geometry is available.
chip=d.getObject('Chip'); magnet=d.getObject('Magnet')
gap=chip.Shape.distToShape(magnet.Shape)[0] if chip and magnet else None
selected_names=[o.Name for o in objlist]
counts={k:sum(n.startswith(k) for n in selected_names) for k in ('NewMotorCorner','NewMotorBase','NewSwitchBase','NewBeamBase','PurchasedAngle','SwitchAngle','BrassStandoff','StandoffShim')}
assert counts['NewMotorCorner']==4 and counts['NewMotorBase']==2 and counts['NewSwitchBase']==2 and counts['NewBeamBase']==4
assert counts['PurchasedAngle']==4 and counts['SwitchAngle']==2 and counts['BrassStandoff']==4 and counts['StandoffShim']==4
corners=[d.getObject('NewMotorCorner%d%d'%(i,j)).Shape for i in range(2) for j in range(2)]
post_corner_clearance=[min(d.getObject('BrassStandoff%d'%i).Shape.distToShape(c)[0] for c in corners) for i in range(4)]
sensor_contact={}
for name in ('PCB','Chip','Magnet'):
o=d.getObject(name)
if o: sensor_contact[name]={'distance_mm':po.Shape.distToShape(o.Shape)[0],'volume_mm3':po.Shape.common(o.Shape).Volume}
d.Label='最新装配 - M4 铜柱编码器'
d.recompute();d.saveAs(str(out/'Assembly.FCStd'))
Import.export(objlist,str(out/'Assembly.step'))
A.setActiveDocument(d.Name);view=G.activeDocument().activeView();G.activateWorkbench('PartWorkbench')
view.viewAxonometric();view.fitAll();G.updateGui();time.sleep(.5);view.saveImage(str(out/'装配全景.png'),1800,1300,'White')
# Back-side encoder detail excluding parts obstructing the view; restore visibility.
vis={o.Name:o.ViewObject.Visibility for o in d.Objects if hasattr(o,'Shape')}
show={'Motor','Holder','Magnet','PCB','Chip','FinalEncoderPlate','Rail0','Rail1'}
show.update('BrassStandoff%d'%i for i in range(4));show.update('StandoffShim%d'%i for i in range(4));show.update('EncoderScrew%d'%i for i in range(4))
show.update('NewMotorCorner%d%d'%(i,j) for i in range(2) for j in range(2));show.update('RailWasher%d%d'%(i,j) for i in range(2) for j in range(2))
for o in d.Objects:
if hasattr(o,'Shape'):o.ViewObject.Visibility=o.Name in show
view.viewTop();view.fitAll();G.updateGui();time.sleep(.5);view.saveImage(str(out/'编码器背面.png'),1600,1200,'White')
for o in d.Objects:
if hasattr(o,'Shape'):o.ViewObject.Visibility=vis.get(o.Name,False)
d.recompute();d.saveAs(str(out/'Assembly.FCStd'))
A.closeDocument(d.Name)
d=A.openDocument(str(out/'Assembly.FCStd'));d.recompute()
assert d.getObject('FinalEncoderPlate').Shape.isValid()
A.closeDocument(d.Name)
step=Part.read(str(out/'Assembly.step'));assert step.isValid()
report={'counts':counts,'new_component_intersections_mm3':potential,'stand_off_to_motor_corner_clearance_mm':post_corner_clearance,'panel_to_sensor_relations':sensor_contact,'magnet_to_chip_shape_distance_mm':gap,'panel_bottom_from_motor_mm':11,'stand_off_body_mm':10,'shim_mm':1,'stud_length_mm':'5 schematic only','visible_names':selected_names,'limitations':'No real slot, fastener threads, countersink, switch wiring or real sensor dimensions; not physical fit or strength verification.'}
(out/'验证.json').write_text(json.dumps(report,ensure_ascii=False,indent=2),encoding='utf8')
(out/'完成.txt').write_text('ok',encoding='utf8')
except Exception:
(out/'错误.txt').write_text(traceback.format_exc(),encoding='utf8')
os._exit(0)