103 行
4.8 KiB
Python
103 行
4.8 KiB
Python
"""Verify the five reviewed EDL NC markers against a compiled pre-change baseline."""
|
|
import argparse
|
|
import json
|
|
import xml.etree.ElementTree as ET
|
|
|
|
from check_hardware import Board
|
|
from schematic_maintenance import ROOT
|
|
from unconnected_inventory import inventory, write_inventory
|
|
|
|
OUT = ROOT / 'out/edl-nc-review'
|
|
CLI = 'D:/KiCad/bin/kicad-cli.exe'
|
|
|
|
|
|
def snapshot(path):
|
|
root = ET.parse(path).getroot()
|
|
return ({c.attrib['ref']: ET.tostring(c) for c in root.findall('components/comp')},
|
|
{n.attrib['name']: {(p.attrib['ref'], p.attrib['pin']): dict(p.attrib)
|
|
for p in n.findall('node')} for n in root.findall('nets/net')})
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser(description=__doc__)
|
|
ap.add_argument('--baseline', action='store_true')
|
|
args = ap.parse_args()
|
|
out = OUT / ('before' if args.baseline else 'after')
|
|
if args.baseline and out.exists():
|
|
raise RuntimeError('Refusing to overwrite the baseline')
|
|
out.mkdir(parents=True, exist_ok=True)
|
|
results, rows = {}, []
|
|
for number in (1, 2):
|
|
b = Board(number)
|
|
b.compile(CLI, out)
|
|
if args.baseline:
|
|
continue
|
|
old_parts, old_nets = snapshot(OUT / 'before' / f'Board{number}.xml')
|
|
new_parts, new_nets = snapshot(out / f'Board{number}.xml')
|
|
assert old_parts == new_parts, 'Component metadata changed'
|
|
approved = {(f'U4{ch}', '3') for ch in range(1, 6)} if number == 2 else set()
|
|
changed = set()
|
|
assert old_nets.keys() == new_nets.keys(), 'Net names changed'
|
|
for name, endpoints in old_nets.items():
|
|
assert endpoints.keys() == new_nets[name].keys(), 'Connectivity changed'
|
|
for endpoint, attrs in endpoints.items():
|
|
expected = dict(attrs)
|
|
if endpoint in approved:
|
|
assert len(endpoints) == 1 and name.startswith('unconnected-')
|
|
assert attrs['pintype'] == 'passive'
|
|
expected['pintype'] = 'passive+no_connect'
|
|
actual = new_nets[name][endpoint]
|
|
assert actual == expected, (endpoint, actual, expected)
|
|
if actual != attrs:
|
|
changed.add(endpoint)
|
|
assert changed == approved
|
|
if number == 2:
|
|
for ch in range(1, 6):
|
|
u, j, r = f'U4{ch}', f'J6{ch}', f'R74{ch}'
|
|
assert b.net_members[b.net(u, 1)] == {(u, '1'), (j, '1')}
|
|
assert b.net_members[b.net(u, 4)] == {(u, '4'), (j, '2')}
|
|
assert b.net(u, 2) == 'GND' and b.net(u, 5) == '+3V3_HUB'
|
|
assert b.net(u, 6) == f'EDL_EN{ch}'
|
|
assert b.resistor(r) == 100000
|
|
assert {b.net(r, 1), b.net(r, 2)} == {'GND', b.net(u, 6)}
|
|
old_erc = json.loads((OUT / 'before' / f'Board{number}-erc.json').read_text())
|
|
new_erc = json.loads((out / f'Board{number}-erc.json').read_text())
|
|
for key in ('ignored_checks', 'included_severities'):
|
|
assert old_erc[key] == new_erc[key]
|
|
def violations(report):
|
|
# KiCad regenerates ERC pin UUIDs on each CLI load.
|
|
return sorted(json.dumps([s['path'], v['type'], v['severity'], v['description'],
|
|
[{k: value for k, value in item.items() if k != 'uuid'}
|
|
for item in v['items']]], sort_keys=True)
|
|
for s in report['sheets'] for v in s['violations'])
|
|
expected_erc = json.loads(json.dumps(old_erc))
|
|
removed = 0
|
|
for sheet in expected_erc['sheets']:
|
|
kept = []
|
|
for v in sheet['violations']:
|
|
is_target = (number == 2 and v['type'] == 'pin_not_connected'
|
|
and any(f'Symbol U4{ch} ' in v['items'][0]['description']
|
|
for ch in range(1, 6)))
|
|
if is_target:
|
|
removed += 1
|
|
else:
|
|
kept.append(v)
|
|
sheet['violations'] = kept
|
|
assert removed == len(approved)
|
|
assert violations(expected_erc) == violations(new_erc), 'Unexpected ERC change'
|
|
rows.extend(inventory(b))
|
|
results[number] = {'components_unchanged': len(new_parts),
|
|
'nets_unchanged': len(new_nets), 'reviewed_nc': sorted(approved),
|
|
'erc': b.erc}
|
|
if not args.baseline:
|
|
result = {'status': 'PASS', 'boards': results,
|
|
'inventory': write_inventory(OUT, rows),
|
|
'evidence': 'datasheets/ic/TS5A3157.pdf pages 3 and 16',
|
|
'scope': 'Unused switch port and powered default selection only; not power-off isolation qualification'}
|
|
(OUT / 'verification.json').write_text(json.dumps(result, indent=2) + '\n')
|
|
print(json.dumps(result))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|