from pathlib import Path
import xml.etree.ElementTree as E,json,hashlib,csv
R=Path(__file__).resolve().parent;B=R.parent;D=R/'recovered_legacy_schematic';sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
assert sha(D/'led_driver.kicad_sch')=='5b9696696a7b29fe83eb48b83939e42ca9b68c1f5be5448670232df582bc4664'
r=E.parse(D/'netlist.xml').getroot();pins={(c.get('ref'),c.get('pin')):n.get('name') for n in r.findall('./nets/net') for c in n}
comps={c.get('ref'):c for c in r.findall('./components/comp')}
e=json.loads((D/'erc.json').read_text());assert not [x for sh in e['sheets'] for x in sh.get('violations',[])]
states={'/GND':0,'PD':1,'FLT':2,'PU':3,'/VDD_3V3':4}
banks=[]
for i,ref in enumerate(['U1','U2','U3']):
 assert comps[ref].findtext('value')=='PCA9956BTW'
 ad=[pins[ref,str(pin)] for pin in [4,3,2]]
 addr=1+25*states[ad[0]]+5*states[ad[1]]+states[ad[2]]
 assert addr==[0x15,0x19,0x65][i]
 outputs=[];unused=[]
 for n in r.findall('./nets/net'):
  for c in n:
   if c.get('ref')==ref and c.get('pinfunction','').startswith('LED'):
    ch=int(c.get('pinfunction').split('_')[0][3:])
    if n.get('name').startswith('/LED_K'):
     logical=int(n.get('name')[6:]);assert logical==24*i+ch;outputs.append(ch)
    else:assert n.get('name').startswith('unconnected-');unused.append(ch)
 count=24 if i<2 else 13
 assert sorted(outputs)==list(range(count)) and sorted(unused)==list(range(count,24))
 assert pins[ref,'36']=='/I2C_SCL' and pins[ref,'37']=='/I2C_SDA'
 assert pins[ref,'5']=='/LED_OE_N' and pins[ref,'35']=='/LED_RESET_N'
 assert pins[ref,'1']==pins['R'+str(i+1),'1']
 assert pins['R'+str(i+1),'2']=='/GND'
 banks.append({'ref':ref,'AD2_AD1_AD0_nets':ad,'address_7bit':addr,'address_hex':hex(addr),'populated_mask':hex((1<<count)-1),'logical_channels':[24*i,24*i+count-1],'unused_channels':unused,'REXT_candidate':'1.80k1% PROVISIONAL','physical_straps_verified':False,'load_and_current_qualified':False})
assert sum(b['logical_channels'][1]-b['logical_channels'][0]+1 for b in banks)==61
sr=E.parse(R/'comparison_only/safety_A39_netlist.xml').getroot();sp={(c.get('ref'),c.get('pin')):n.get('name') for n in sr.findall('./nets/net') for c in n}
rows=[]
for pin in range(1,7):
 p=pins['J1',str(pin)];s=sp['J2',str(pin)]
 assert p==s or (pin==1 and p=='/VDD_3V3' and s=='/CTRL_3V3')
 rows.append([pin,p,s,'DIRECT_WIRE_WITH_3V3_RAIL_ALIAS' if pin==1 else'DIRECT_WIRE','NOT_ASSEMBLED'])
with (R/'driver_to_safety_6pin_map.csv').open('w',newline='') as f:
 w=csv.writer(f);w.writerow(['pin','legacy_driver_J1','A39_safety_J2','required_connection','physical_status']);w.writerows(rows)
assert pins['J1','7']=='/VLED_GUARDED' and pins['J1','8']=='/GND'
footprints={ref:c.findtext('footprint') for ref,c in comps.items() if not ref.startswith('#')}
assert not any(footprints.values())
checks={'status':'A47_LEGACY_PCA_SCHEMATIC_LOCAL_RECOVERED_ERC_AND_ADDRESS_AUDIT_PASS_FABRICATION_HOLD','recovered_source':'work/pcba-v128-current/三款PCBA工程包-v1.28-2026-10-08/硬件/led_driver/led_driver.kicad_sch','source_sha256':sha(D/'led_driver.kicad_sch'),'new_netlist_sha256':sha(D/'netlist.xml'),'ERC':0,'native_source_modified':False,'manufacturer_address_combinations_checked':125,'banks':banks,'driver_safety_connector_pairs_checked':6,'logical_channels':61,'footprints_assigned':False,'PCB_present':False,'STM32_I2C_AF_and_alias_guard_closed':False,'firmware_transport_bound':False,'latest_firmware':'A46 unchanged','powered_release':False,'physical_verified_requirements':0,'reference_requirements':20}
(R/'checks.json').write_text(json.dumps(checks,ensure_ascii=False,indent=2)+'\n')
(R/'candidate_bank_configuration.json').write_text(json.dumps({'origin':'A47 recovered legacy source only','banks':banks,'runtime_use_approved':False,'unused_policy':'U3 channels13..23 LEDOUT=OFF PWM=0 IREF=0','output_profile_and_bank_schedule':'UNQUALIFIED','bank_aggregation':'A46 is perchip. All3bank freshness must be scheduled and measured; cannot assume sequential1ms polling meets20ms oldest-sample limit'},ensure_ascii=False,indent=2)+'\n')
print(json.dumps(checks,ensure_ascii=False,indent=2))
