app
Fusion - the Fusion 360 Bridge
Public Made by Adomby adom
Drive Autodesk Fusion 360 from the cloud via Adom Bridge: component libraries, IPC package generation, board layout, exports (STEP/Gerbers/BOM/CPL), fast APS cloud search, and parametric modeling.
master
"""Record verified native Fusion edits; keep wall timestamps for action editing."""
import argparse
import json
from pathlib import Path
import sys
import time
sys.path.insert(0,str(Path(__file__).resolve().parents[1]))
from call import call
ROOT=Path(__file__).resolve().parent
p=argparse.ArgumentParser();p.add_argument('--hwnd',type=int,required=True);p.add_argument('--take',type=Path,required=True);a=p.parse_args()
a.take.mkdir(parents=True,exist_ok=False)
records=[]
def invoke(v,args):
start=time.time();r=call(v,{**args,'reason':'Create and record the requested Fusion 24-net crossover demo'})
records.append({'verb':v,'start':start,'end':time.time(),'args':args,'result':r})
(a.take/'calls.json').write_text(json.dumps(records,indent=2)+'\n')
if not r.get('success',r.get('ok',False)):raise RuntimeError(json.dumps(r))
return r
s=invoke('fusion_routing_state',{'expectDocument':'fusion-crossbar-24'})
assert s['bridgeVersion']=='1.10.0-dev.routing.2' and s['segmentCount']==s['viaCount']==0
assert len(s['pads'])==72 and len(s['nets'])==24
(a.take/'before.json').write_text(json.dumps(s,indent=2)+'\n')
def guard():return {'expectDocument':'fusion-crossbar-24','boardId':s['boardId'],'expectedRevision':s['revision']}
plans=[]
for row in json.loads((ROOT/'plan.json').read_text()):
preview=invoke('fusion_route_net',{**guard(),**row,'dryRun':True})
assert len(preview['plan']['items'])==7
plans.append((row,preview['plan']['items']))
invoke('fusion_execute_text_command',{'command':'Electron.ImmediateRepaint /full','expectDocument':'fusion-crossbar-24'})
rec=invoke('desktop_record_window_start',{'hwnd':a.hwnd,'fps':30,'audio':False,'maxDurationMs':600000})
(a.take/'record-start.json').write_text(json.dumps(rec,indent=2)+'\n')
try:
time.sleep(1)
for row,items in plans:
for item in items:
if item['kind']=='segment':
route={'net':item['net'],'width':item['width'],'layer':item['layer'],'points':[item['start'],item['end']]}
else:
xy=item['at'];route={'net':item['net'],'layer':1,'width':.3,'viaDiameter':item['diameter'],'viaDrill':item['drill'],'points':[xy,{'x':xy[0],'y':xy[1],'layer':16}]}
s=invoke('fusion_route_net',{**guard(),**route})
assert s['verified'] and s['mutated']
time.sleep(.2)
s=invoke('fusion_routing_drc',guard())
assert s['drc']['checked'] and not s['drc']['errorCount'] and not s['drc']['warningCount'],s['drc']
print(row['net'],s['segmentCount'],s['viaCount'],len(s['remainingNets']),'nets remain',flush=True)
assert s['segmentCount']==120 and s['viaCount']==48 and not s['remainingNets'] and s['drc']['clean']
(a.take/'final.json').write_text(json.dumps(s,indent=2)+'\n')
time.sleep(2)
finally:
stop=invoke('desktop_record_window_stop',{'recordingId':rec['recordingId']})
(a.take/'record-stop.json').write_text(json.dumps(stop,indent=2)+'\n')
invoke('pull_file',{'filePaths':[stop['path']],'saveTo':str(a.take.resolve())})
print('PASS: 24 nets / 120 segments / 48 vias / native DRC clean',flush=True)