from pathlib import Path
import xml.etree.ElementTree as E,json,itertools
R=Path(__file__).resolve().parent;r=E.parse(R/'cad/netlist.xml').getroot()
nets={(x.get('ref'),x.get('pin')):n.get('name') for n in r.findall('./nets/net') for x in n.findall('node')}
values={c.get('ref'):c.findtext('value') for c in r.findall('./components/comp')}
assert [nets[('J1',str(i))] for i in range(1,5)]==['/CTRL_3V3','/GND','/KEY_IN','/USER_ADC']
for ref in ['SW1','SW2','SW3']:assert nets[(ref,'1')]==nets[(ref,'3')] and nets[(ref,'2')]==nets[(ref,'4')]
resistors=[]
for ref in ['R1','R2','R3']:
 value=float(values[ref].split()[0].replace('k',''))*1000
 resistors.append((nets[(ref,'1')],nets[(ref,'2')],value))
assert sorted(x[2] for x in resistors)==[10000,10000,22000]
# Independent KCL solve over actual exported netlist, not a hardcoded divider.
def solve(edges):
 known={'/CTRL_3V3':3.3,'/GND':0.0};unknown=sorted(set(x for a,b,r in edges for x in (a,b))-set(known));idx={n:i for i,n in enumerate(unknown)};m=[[0.0]*(len(unknown)+1) for _ in unknown]
 for a,b,res in edges:
  g=1/res
  for u,v in [(a,b),(b,a)]:
   if u in idx:
    i=idx[u];m[i][i]+=g
    if v in idx:m[i][idx[v]]-=g
    else:m[i][-1]+=g*known[v]
 for i in range(len(m)):
  pivot=max(range(i,len(m)),key=lambda j:abs(m[j][i]));m[i],m[pivot]=m[pivot],m[i];assert abs(m[i][i])>1e-12
  scale=m[i][i];m[i]=[v/scale for v in m[i]]
  for j in range(len(m)):
   if j!=i:
    scale=m[j][i];m[j]=[a-scale*b for a,b in zip(m[j],m[i])]
 return {**known,**{n:m[i][-1] for n,i in idx.items()}}
rows=[]
for power,plus,minus in itertools.product([False,True],repeat=3):
 edges=resistors+[('/KEY_IN','/CTRL_3V3',10000)] # actual controller R9, not panel BOM
 for ref,closed in zip(['SW1','SW2','SW3'],[power,plus,minus]):
  if closed:edges.append((nets[(ref,'1')],nets[(ref,'2')],.1)) # hypothetical closed-contact resistance
 v=solve(edges);raw=round(v['/USER_ADC']/3.3*4095)
 low,high=(1530,1800) if plus and minus else (1900,2200) if plus else (2650,2970) if minus else (3850,4095)
 assert low<=raw<=high
 assert v['/KEY_IN']<.01 if power else v['/KEY_IN']>3.29
 rows.append({'power':power,'plus':plus,'minus':minus,'ADC_count_model':raw,'key_voltage_model_V':v['/KEY_IN']})
(R/'network_checks.json').write_text(json.dumps({'source':'actual exported panel netlist KCL','external_power_key_pullup_ohm':10000,'closed_contact_ohm_assumed':.1,'cases':rows,'physical_validation':False},indent=2)+'\n')
print('PASS: actual netlist, all 8 switch combinations, independent KCL, controller pin contract')
