master
John Lauer Preserve BQ independent routing, narration and native copper-retention evidence a4b7711 1mo ago
import json,time,sys
from pathlib import Path
from native import execute,P
from call import call
sys.path.insert(0,str(P.parents[1]));from live_panel import LivePanel
DOC='bq25792-ablation-live-02';TAKE=P/'take-02';TAKE.mkdir(exist_ok=False);logs=[];stages=[]
def invoke(v,a):
 start=time.time();r=call(v,a);logs.append({'verb':v,'args':a,'start':start,'end':time.time(),'result':r});(TAKE/'calls.json').write_text(json.dumps(logs,indent=2));assert r.get('success'),r;return r
def native(body):
 start=time.time();r=execute(DOC,body);logs.append({'verb':'fusion_run_modeling_script','start':start,'end':time.time(),'result':r});(TAKE/'calls.json').write_text(json.dumps(logs,indent=2));return r
def command(cmd):return native("app.executeTextCommand("+repr('Electron.run '+cmd)+")\napp.executeTextCommand('Electron.ImmediateRepaint /full')\nresult={'commandCompleted':True}")
w=invoke('desktop_list_windows',{})['windows'];main=next(x for x in w if x.get('image')=='Fusion360.exe' and x.get('owner')==0)
class CaptionLog:
 def update(self,**values):
  with (TAKE/'caption-events.jsonl').open('a') as f:f.write(json.dumps({'time':time.time(),**values})+'\n')
panel=CaptionLog()
s=native("result=handle(app,{'expectDocument':target.name},'routing_state')");assert s['pourCount']==0 and s['segmentCount']==612 and s['viaCount']==60
records=[]
try:
 for name,hwnd in [('board',main['hwnd'])]:
  r=invoke('desktop_record_window_start',{'hwnd':hwnd,'fps':30,'audio':False,'maxDurationMs':360000,'reason':'Record native copper pours, floating islands and ablation-area validation'});records.append((name,r));(TAKE/(name+'-start.json')).write_text(json.dumps(r,indent=2))
 def stage(name):stages.append({'name':name,'time':time.time()});(TAKE/'stages.json').write_text(json.dumps(stages,indent=2))
 stage('trace-only');time.sleep(4)
 for i,l in enumerate([1,2,15,16]):
  stage('pour-'+str(l));panel.update(status='Fill copper layer '+str(l),connection='PGND-connected fill plus floating orphans',plan='Solid pour / 0.1 mm minimum isolation / 0.2 mm edge clearance',checks='Native refill respects board rules and keepouts',event='Create a full-board polygon natively',percent=i*20)
  cmd=f"DISPLAY NONE; DISPLAY {l} 17 18 20; LAYER {l}; POLYGON 'PGND' 0.1mm (0mm 0mm) +0 (50.4mm 0mm) +0 (50.4mm 26.4mm) +0 (0mm 26.4mm) +0 (0mm 0mm); CHANGE ISOLATE 0.1mm (25mm 0mm); CHANGE THERMALS OFF (25mm 0mm); CHANGE ORPHANS ON (25mm 0mm); CHANGE RANK 6 (25mm 0mm); RATSNEST; WINDOW (-1mm -1mm) (52mm 28mm);"
  command(cmd);stage('filled-'+str(l));time.sleep(4)
 stage('orphans-off');panel.update(status='Compare floating islands',connection='Top layer: orphan retention temporarily OFF',plan='Disconnected copper is removed by ordinary pour cleanup',checks='For ablation, removing this copper adds work',event='Compare before and after retaining islands',percent=85);command('DISPLAY NONE; DISPLAY 1 17 18 20; LAYER 1; CHANGE ORPHANS OFF (25mm 0mm); RATSNEST; WINDOW (20mm 7mm) (39mm 18mm);');stage('orphans-off-visible');time.sleep(4)
 stage('orphans-on');panel.update(status='Keep isolated copper',connection='Top layer: orphan retention ON',plan='Retain electrically floating copper where clearances allow',checks='Do not place floating islands inside isolation barriers',event='Restore copper-retention policy',percent=90);command('CHANGE ORPHANS ON (25mm 0mm); RATSNEST;');stage('orphans-on-visible');time.sleep(5)
 stage('validation');panel.update(status='Check the native result',connection='Connectivity and DRC after native refill',plan='0.1 mm is a process limit, not a high-voltage safety rule',checks='Creepage and clearance depend on voltage and environment',event='Native verification',percent=95)
 body="s=handle(app,{'expectDocument':target.name},'routing_state')\nresult=handle(app,{'expectDocument':target.name,'boardId':s['boardId'],'expectedRevision':s['revision']},'routing_drc')"
 result=native(body);(TAKE/'final.json').write_text(json.dumps(result,indent=2));assert result['success'] and result['drc']['airwireCount']==0 and result['drc']['errorCount']==1 and result['drc']['warningCount']==0,result.get('drc')
 r=native("root,b,m=snapshot(app,{'expectDocument':target.name})\nout=Path('C:/Users/john/AppData/Local/Temp/adom-ablation/bq25792-ablation-routed.brd')\nET.ElementTree(root).write(out,encoding='utf-8',xml_declaration=True)\napp.executeTextCommand('Document.CopyToDesktop C:/Users/john/AppData/Local/Temp/adom-ablation/bq25792-ablation-routed.fbrd')\nresult={'path':out.as_posix()}")
 invoke('pull_file',{'filePaths':[r['path'],'C:/Users/john/AppData/Local/Temp/adom-ablation/bq25792-ablation-routed.fbrd'],'saveTo':str(TAKE)})
 from coverage import measure
 coverage=measure(TAKE/'bq25792-ablation-routed.brd');(TAKE/'coverage.json').write_text(json.dumps(coverage,indent=2))
 panel.update(status='Copper retained on all layers',connection='50 nets connected / 612 segments / 60 vias / 4 pours',plan=f"{coverage['combinedCoveragePercent']:.1f}% copper coverage / {coverage['combinedRemovalReductionPercent']:.1f}% less projected removal area",checks='0 airwires / 0 warnings / 0 new DRC errors\n1 unchanged placement error; electrical review remains',event='Measured from native filled export',percent=100)
 command('DISPLAY NONE; DISPLAY 1 17 18 20 21; WINDOW (-1mm -1mm) (52mm 28mm);');stage('final-visible');time.sleep(6)
finally:
 paths=[]
 for name,r in records:
  stop=call('desktop_record_window_stop',{'recordingId':r['recordingId']});(TAKE/(name+'-stop.json')).write_text(json.dumps(stop,indent=2));assert stop.get('success'),stop;paths.append(stop['path'])
 if paths:invoke('pull_file',{'filePaths':paths,'saveTo':str(TAKE)})
print('Recorded, exported, natively validated',flush=True)