"""Inject violations into a disposable board to check R2 rules actually fire.""" import json import shutil import pcbnew as pcb from bootstrap_pcb import xy, rounded_rect from optimize_pcb_layout import ROOT, OUT from verify_pcb_layout import run, routing def main(): folder=OUT/'rule-probe' folder.mkdir(exist_ok=True) path=folder/'RuleProbe.kicad_pcb' b=pcb.BOARD(); b.SetCopperLayerCount(4) rounded_rect(b,50,50,220,135,5) def net(name): item=pcb.NETINFO_ITEM(b,name); b.Add(item); return item usb,sw,control=net('USB_DP'),net('Net-(C105-Pad2)'),net('SDA') def track(a,z,layer,n,width=.25): t=pcb.PCB_TRACK(b); t.SetStart(xy(*a));t.SetEnd(xy(*z));t.SetLayer(layer) t.SetWidth(pcb.FromMM(width));t.SetNet(n);b.Add(t) track((60,60),(65,60),pcb.F_Cu,usb,.8) track((70,70),(80,70),pcb.In1_Cu,control) track((90,80),(90,81.5),pcb.F_Cu,control) track((90,81.5),(90,83),pcb.F_Cu,control) track((100,90),(101.5,90),pcb.B_Cu,control) track((101.5,90),(103,90),pcb.B_Cu,control) v=pcb.PCB_VIA(b);v.SetPosition(xy(120,100));v.SetWidth(pcb.FromMM(.6)) v.SetDrill(pcb.FromMM(.3));v.SetLayerPair(pcb.F_Cu,pcb.B_Cu);v.SetNet(sw);b.Add(v) pcb.SaveBoard(str(path),b) for ext in ('.kicad_pro','.kicad_dru'): shutil.copy2(ROOT/'PhoneRail-Board1'/('PhoneRail-Board1'+ext),path.with_suffix(ext)) drc=folder/'drc.json' run(['pcb','drc','--format','json','-o',str(drc),str(path)],drc) data=json.loads(drc.read_text(encoding='utf-8')) descriptions='\n'.join(v['description'] for v in data['violations']) expected=['R2 USB differential target','R2 inner layers reserved','R2 switch copper stays'] checks={s:s in descriptions for s in expected} contract=json.loads((ROOT/'pcb-routing-rules.json').read_text(encoding='utf-8')) audit=routing(b,contract,1) direction=[v for v in audit['errors'] if v['kind']=='direction'] checks['Split transverse tracks rejected on both outer layers']=len(direction)==2 checks['Inner signal track rejected']=any(v['kind']=='inner_signal_track' for v in audit['errors']) checks['Explicit nonstandard USB pair skew checked']=any(v['kind']=='usb_skew' for v in audit['errors']) result={'checks':checks,'all_passed':all(checks.values()),'routing':audit} (folder/'verification.json').write_text(json.dumps(result,indent=2)+'\n',encoding='utf-8') print(checks) assert result['all_passed'],descriptions if __name__=='__main__': main()