35 lines
2.6 KiB
Python
35 lines
2.6 KiB
Python
import os, json, time, subprocess, hashlib, statistics, sys
|
|
from pathlib import Path
|
|
root=Path.cwd(); out=root/'docs/evidence/2026-10-04/heartbeat'
|
|
dll=root/'tmp/can1-test-20261002/pcan-basic/x64'
|
|
os.environ['PATH']=str(dll)+os.pathsep+os.environ['PATH']; handle=os.add_dll_directory(str(dll))
|
|
import can
|
|
cli=r'D:\STM32CubeCLT_1.19.0\STM32CubeProgrammer\bin\STM32_Programmer_CLI.exe'
|
|
firmware=root/'firmware/build/Release/AMI_Controller.bin'
|
|
def run(name,args):
|
|
p=subprocess.run([cli]+args,capture_output=True,timeout=40)
|
|
(out/(name+'.log')).write_bytes(p.stdout+p.stderr)
|
|
print(name, p.returncode, p.stdout.decode(errors='replace')[-750:],flush=True)
|
|
assert p.returncode==0,name
|
|
return p
|
|
with can.Bus(interface='pcan',channel='PCAN_USBBUS1',bitrate=500000) as bus:
|
|
assert bus.reset()
|
|
if '--capture-only' not in sys.argv: run('flash',['-c','port=SWD','mode=UR','freq=1000','-d',str(firmware),'0x08000000','-v','-rst'])
|
|
readback=out/'readback.bin'
|
|
run('readback',['-c','port=SWD','mode=HOTPLUG','freq=1000','-u','0x08000000',str(firmware.stat().st_size),str(readback)])
|
|
assert firmware.read_bytes()==readback.read_bytes()
|
|
while bus.recv(0): pass
|
|
frames=[];end=time.monotonic()+12
|
|
while time.monotonic()<end:
|
|
m=bus.recv(.2)
|
|
if m:frames.append({'timestamp':m.timestamp,'id':m.arbitration_id,'data':m.data.hex(' '),'dlc':m.dlc,'extended':m.is_extended_id,'remote':m.is_remote_frame,'error':m.is_error_frame})
|
|
status=bus.status()
|
|
(out/'capture.json').write_text(json.dumps(frames,indent=2))
|
|
h=[f for f in frames if f['id']==0x301 and not f['error']]
|
|
seq=[int.from_bytes(bytes.fromhex(f['data'])[2:4],'little') for f in h]
|
|
ticks=[int.from_bytes(bytes.fromhex(f['data'])[4:8],'little') for f in h]
|
|
intervals=[(b['timestamp']-a['timestamp'])*1000 for a,b in zip(h,h[1:])]
|
|
checks={'enough_frames':len(h)>=100,'bus_ok':status==0,'format':all(f['dlc']==8 and not f['extended'] and not f['remote'] and bytes.fromhex(f['data'])[:2]==b'\x01\x00' for f in h),'sequence_contiguous':all((b-a)%65536==1 for a,b in zip(seq,seq[1:])),'uptime_100ms':all((b-a)%2**32==100 for a,b in zip(ticks,ticks[1:])),'wire_period_95_to_105ms':bool(intervals) and all(95<=v<=105 for v in intervals)}
|
|
result={'firmware_bytes':firmware.stat().st_size,'sha256':hashlib.sha256(firmware.read_bytes()).hexdigest(),'flash_readback_identical':True,'bus':'CAN1 500 kbit/s PCAN_USBBUS1','duration_s':12,'heartbeat_count':len(h),'bus_status':status,'interval_ms':{'min':min(intervals),'mean':statistics.mean(intervals),'max':max(intervals)} if intervals else None,'checks':checks,'passed':all(checks.values()),'CAN2_wire_verified':False}
|
|
(out/'results.json').write_text(json.dumps(result,indent=2));print(json.dumps(result,indent=2))
|