from pathlib import Path
import pcbnew,wx,json,xml.etree.ElementTree as E
app=wx.App(False);quiet=wx.LogNull();r=Path(__file__).resolve().parent;stem='vacuum_control_evt_a5_logic_fet';old=pcbnew.LoadBoard(str(r/'baseline'/(stem+'.kicad_pcb')));new=pcbnew.LoadBoard(str(r/'cad'/(stem+'.kicad_pcb')))
def point(p):return[p.x,p.y]
def track(t):
 v=isinstance(t,pcbnew.PCB_VIA)
 return {'start':point(t.GetStart()),'end':point(t.GetEnd()),'width':t.GetWidth(t.GetLayer()) if v else t.GetWidth(),'layer':t.GetLayer(),'net':t.GetNetname(),'via':v,'drill':t.GetDrillValue() if v else None}
orig={t.m_Uuid.AsString():track(t) for t in old.GetTracks()};current={t.m_Uuid.AsString():track(t) for t in new.GetTracks()};assert all(current.get(k)==v for k,v in orig.items())
fs={f.GetReference():f for f in new.GetFootprints()}
for f in old.GetFootprints():
 g=fs[f.GetReference()];assert point(g.GetPosition())==point(f.GetPosition()) and g.GetOrientationDegrees()==f.GetOrientationDegrees()
 pads={p.m_Uuid.AsString():p for p in g.Pads()}
 for p in f.Pads():
  q=pads[p.m_Uuid.AsString()];assert point(q.GetPosition())==point(p.GetPosition()) and q.GetNetname()==p.GetNetname() and point(q.GetSize())==point(p.GetSize()) and point(q.GetDrillSize())==point(p.GetDrillSize()), (f.GetReference(),p.GetNumber(),p.GetNetname(),q.GetNetname(),point(p.GetSize()),point(q.GetSize()))
netlist=E.parse(r/'cad/netlist.xml').getroot();netmap={(p.get('ref'),p.get('pin')):n.get('name') for n in netlist.findall('./nets/net') for p in n.findall('node')}
matched=0
for ref,f in fs.items():
 for p in f.Pads():
  key=(ref,p.GetNumber())
  if key not in netmap:continue
  assert p.GetNetname().replace("{slash}","/")==netmap[key],(key,p.GetNetname(),netmap[key]);matched+=1
for ref,net in [('R18','/PANEL_SCL'),('R19','/PANEL_SDA')]:
 assert fs[ref].GetValue()=='4.7k';assert netmap[(ref,'1')]=='/CTRL_3V3' and netmap[(ref,'2')]==net
 assert fs[ref].GetPath().AsString().endswith(json.loads((r/'changes.json').read_text())['instance'][ref])
assert len(fs)==len(list(old.GetFootprints()))+2
out={'original_copper_objects_preserved':len(orig),'added_copper_objects':len(current)-len(orig),'original_footprint_geometry_and_pad_nets_preserved':True,'matched_electrical_pads':matched,'added_pullups':2,'physical_validation':False}
(r/'saved_board_checks.json').write_text(json.dumps(out,indent=2));print(json.dumps(out))
