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
John Lauer
Add native thermal-pour expansion experiment, finite-difference comparison and recording scripts
33c4ac5
1mo ago
"""Record additive native pour expansion and net highlighting; preserve prior board."""
import sys,json,time,shutil,xml.etree.ElementTree as ET
from pathlib import Path
P=Path(__file__).resolve().parent;sys.path.insert(0,str(P.parent/'thermal'));from crr import call,execute
D='bq25792-thermal-expansion-live';T=P/'recording';events=[];records=[]
bounds=json.loads((P/'expansion-03/bounds.json').read_text())
def native(body):
compile(body,'native-expansion','exec');return execute(D,body)
def command(cmd):return native("app.executeTextCommand("+repr('Electron.run '+cmd)+")\napp.executeTextCommand('Electron.ImmediateRepaint /full')\nadsk.doEvents()\nresult={'success':True}")
def mark(n):events.append({'name':n,'time':time.time()});(T/'events.json').write_text(json.dumps(events,indent=2));print(n,flush=True)
def banner(title,detail):
payload={'title':'Astra: '+title,'detail':detail,'percent':50,'tone':'working','routing':{'connection':'Native Fusion / four net-specific thermal regions','plan':'Expand connected copper; preserve ground and compact switch nodes','checks':'No new DRC errors in preflight; final live DRC follows','events':'POLYGON / CHANGE / RATSNEST / SHOW'}}
native("p=app.userInterface.palettes.itemById('AdomActivityPalette')\nif p:\n p.isVisible=True\n p.sendInfoToHTML('update',"+repr(json.dumps(payload))+")\nresult={'palette':bool(p)}")
s=native("result=handle(app,{'expectDocument':target.name},'routing_state')");assert (s['segmentCount'],s['viaCount'],s['pourCount'])==(576,89,8)
w=call('desktop_list_windows',{})['windows'];main=next(x for x in w if x.get('image')=='Fusion360.exe' and x.get('owner')==0)
call('desktop_bring_to_front',{'hwnd':main['hwnd'],'reason':'Record the requested Fusion heatsink expansion and net-by-net video on CRR'})
try:
r=call('desktop_record_window_start',{'hwnd':main['hwnd'],'fps':30,'audio':False,'maxDurationMs':480000,'reason':'Show native copper pours expanding and highlight each actual thermal net'});assert r.get('success'),r;records.append(r);(T/'start.json').write_text(json.dumps({'calledAt':time.time(),**r},indent=2))
command('DISPLAY NONE; DISPLAY 16 17 18 20; WINDOW (-1mm -1mm) (52mm 28mm);');mark('before');banner('Video ten baseline','Existing 576 segments, 89 vias and eight pours');time.sleep(4)
for i,(net,b) in enumerate(bounds.items()):
command('SHOW '+net);mark('before-'+net);time.sleep(3)
x,y,z,w=b;cx=z if net!='VBAT' else x;cy=(y+w)/2
cmd=f"LAYER 16; POLYGON '{net}' 0.1mm ({x}mm {y}mm) +0 ({z}mm {y}mm) +0 ({z}mm {w}mm) +0 ({x}mm {w}mm) +0 ({x}mm {y}mm); CHANGE ISOLATE 0.1mm ({cx}mm {cy}mm); CHANGE THERMALS OFF ({cx}mm {cy}mm); CHANGE ORPHANS OFF ({cx}mm {cy}mm); CHANGE RANK 1 ({cx}mm {cy}mm); RATSNEST;"
mark('expand-'+net);banner('Expanding '+net,'Precomputed boundaries; native fill determines connected copper');command(cmd)
s=native("result=handle(app,{'expectDocument':target.name},'routing_state')");assert (s['segmentCount'],s['viaCount'],s['pourCount'],s['airwireCount'])==(576,89,9+i,0)
(T/('state-'+net+'.json')).write_text(json.dumps(s,indent=2));command('SHOW '+net);mark('after-'+net);time.sleep(6)
command('SHOW PGND');mark('ground');banner('Preserve ground cooling','Ground regions remain for IC1 and U2; inner planes retained');time.sleep(7)
command('DISPLAY NONE; DISPLAY 1 17 18 20 21; WINDOW (-1mm -1mm) (52mm 28mm);');mark('top');time.sleep(4)
command('DISPLAY NONE; DISPLAY 16 17 18 20; WINDOW (-1mm -1mm) (52mm 28mm);');mark('validation')
r=native("s=handle(app,{'expectDocument':target.name},'routing_state')\nresult=handle(app,{'expectDocument':target.name,'boardId':s['boardId'],'expectedRevision':s['revision']},'routing_drc')");(T/'final-drc.json').write_text(json.dumps(r,indent=2));assert r['drc']['airwireCount']==0 and r['drc']['warningCount']==0 and [e['signature'] for e in r['drc']['errors']]==['5,1,f10227fd41e0ab5d'],r['drc']
host='C:/Users/john/AppData/Local/Temp/adom-thermal-expansion/bq25792-expanded-thermal'
native("root,b,m=snapshot(app,{'expectDocument':target.name})\nET.ElementTree(root).write("+repr(host+'.brd')+",encoding='utf-8',xml_declaration=True)\napp.executeTextCommand("+repr('Document.CopyToDesktop '+host+'.fbrd')+")\nresult={'saved':True}")
r=call('pull_file',{'filePaths':[host+'.brd',host+'.fbrd'],'saveTo':str(T)});(T/'exports.json').write_text(json.dumps(r,indent=2))
for ext in ['brd','fbrd']:
f=T/('bq25792-expanded-thermal.'+ext)
if not f.exists():shutil.copy2(Path('/mnt/c')/str(f).lstrip('/'),f)
command('SHOW VAC1 VBUS VAC2 VBAT');mark('final');banner('Expanded and checked','0 airwires / 0 warnings / 0 new errors; one inherited error');time.sleep(7)
finally:
for r in records:
stop=call('desktop_record_window_stop',{'recordingId':r['recordingId']});(T/'stop.json').write_text(json.dumps(stop,indent=2));assert stop.get('success'),stop
pull=call('pull_file',{'filePaths':[stop['path']],'saveTo':str(T)});(T/'pull.json').write_text(json.dumps(pull,indent=2))
f=T/Path(stop['path']).name
if not f.exists():shutil.copy2(Path('/mnt/c')/str(f).lstrip('/'),f)
print('Recorded, exported, native DRC verified',flush=True)