from pathlib import Path import json, math, traceback import FreeCAD as App import Part base=Path(r'C:\projects\wheel') out=base/'models'/'70_m4_drill_plain' try: doc=App.openDocument(str(out/'70_M4_drill_plain.FCStd')) doc.recompute() final=doc.getObject('Cut70') assert final and final.Shape.isValid() and len(final.Shape.Solids)==1 assert all(not str(obj.Name).startswith('Countersink') for obj in doc.Objects) assert abs(doc.getObject('Parameters').get('B4').Value-3.3)<1e-8 step=Part.read(str(out/'70_M4_drill_plain.step')) assert step.isValid() and len(step.Solids)==1 bores=[] for f in step.Faces: s=f.Surface if isinstance(s,Part.Cylinder) and abs(s.Radius-1.65)<1e-6: c=s.Center bores.append({'x':round(c.x,6),'y':round(c.y,6),'z_span':round(f.BoundBox.ZLength,6),'radius':round(s.Radius,6)}) assert len(bores)==24, f'expected 24, got {len(bores)}' assert all(abs(x['z_span']-8)<1e-6 for x in bores), bores expected=[] for pcd, ang in [(42,45),(48,0),(50.8,30),(70,30)]: for i in range(6): a=math.radians(ang+60*i) expected.append((pcd/2*math.cos(a),pcd/2*math.sin(a))) for x,y in expected: assert min(math.hypot(b['x']-x,b['y']-y) for b in bores)<1e-5, (x,y) src=Part.read(str(base/'models'/'70转接板.step')) outline=doc.getObject('OuterChamfer').Shape sections=[] for z in (0.2,1,2,4,6,7,7.8): sw=max(src.slice(App.Vector(0,0,1),z),key=lambda w:w.Length) ow=max(outline.slice(App.Vector(0,0,1),z),key=lambda w:w.Length) sf,of=Part.Face(sw),Part.Face(ow) a=sf.cut(of).Area; b=of.cut(sf).Area sections.append({'z':z,'source_minus_new_area':a,'new_minus_source_area':b}) assert a<1e-4 and b<1e-4, (z,a,b) refs=[obj for obj in doc.Objects if obj.Label=='Original STEP - reference (hidden)'] assert len(refs)==1 and not refs[0].ViewObject.Visibility sheet=doc.getObject('Parameters') oldcenter=doc.getObject('Hole42_0').Placement.Base sheet.set('B6','46 deg');doc.recompute() newcenter=doc.getObject('Hole42_0').Placement.Base movement=(newcenter-oldcenter).Length assert movement>0.36 and final.Shape.isValid() # No save: the production file remains at the original 45-degree phase. result={'fcstd_valid':True,'step_valid':True,'fcstd_solids':1,'step_solids':len(step.Solids),'step_volume':step.Volume,'bore_count':len(bores),'bore_diameter_mm':3.3,'bore_full_depth_mm':8,'expected_locations_matched':True,'counterbores_removed':True,'source_outline_sections':sections,'source_reference_hidden':True,'parameter_angle_test_movement_mm':movement,'parameter_angle_test_saved':False} (out/'validation.json').write_text(json.dumps(result,indent=2),encoding='utf-8') (out/'validation_status.txt').write_text('OK',encoding='utf-8') except: (out/'validation_status.txt').write_text(traceback.format_exc(),encoding='utf-8') for n in list(App.listDocuments()): App.closeDocument(n) Gui.getMainWindow().close()