import hashlib,json,re,subprocess,threading,time from datetime import datetime,timezone,timedelta from pathlib import Path import serial R=Path('D:/YMR/AMI') O=R/'docs/evidence/2026-10-07/as-status-flash'/datetime.now(timezone(timedelta(hours=8))).strftime('%H%M%S') O.mkdir(parents=True) CLI='D:/STM32CubeCLT_1.19.0/STM32CubeProgrammer/bin/STM32_Programmer_CLI.exe' SN='37FF71064E573436188C1543' binary=R/'output/firmware/AMI_Controller.bin';data=binary.read_bytes() assert len(data)==16672 and hashlib.sha256(data).hexdigest()=='1951c63e779429d08a992d1171fc92173e4399307ff741fdc5d9b7646848855d' report={'date':'2026-10-07','firmware':'v2.7','evidence_dir':str(O),'probe_serial':SN,'binary_bytes':len(data),'binary_sha256':hashlib.sha256(data).hexdigest(),'flash_verified':False,'full_readback_identical':False,'physical_can_test_performed':False,'physical_hmi_test_performed':False} def run(name,args,mode='HOTPLUG'): p=subprocess.run([CLI,'-c','port=SWD',f'sn={SN}','freq=1000',f'mode={mode}',*args],capture_output=True,timeout=45) log=p.stdout+p.stderr;(O/(name+'.log')).write_bytes(log) assert p.returncode==0,(name,log.decode(errors='replace')) assert b'Device ID : 0x467' in log,(name,'unexpected target') return log report['target_probe']=run('target-probe',[]).decode(errors='replace') assert 'Flash size : 128 KBytes' in report['target_probe'] backup=O/'before-flash-128k.bin' run('before-flash-backup',['-u','0x08000000','131072',str(backup)]) assert backup.stat().st_size==131072 report['backup_bytes']=backup.stat().st_size;report['backup_sha256']=hashlib.sha256(backup.read_bytes()).hexdigest() print('Backup complete: 128 KB',flush=True) capture=bytearray();errors=[];stop=threading.Event();port=None;thread=None try: port=serial.Serial(port=None,baudrate=9600,timeout=.05) port.port='COM6';port.dtr=False;port.rts=False;port.open() def reader(): try: while not stop.is_set():capture.extend(port.read(512)) except Exception as exc:errors.append(repr(exc)) thread=threading.Thread(target=reader);thread.start() except Exception as exc:report['serial_open_error']=repr(exc) try: log=run('flash',['reset=HWrst','-w',str(binary),'0x08000000','-v','-rst'],'UR') assert b'Download verified successfully' in log report['flash_verified']=True print('v2.7 programmed and verified',flush=True) readback=O/'flash-readback-128k.bin' run('flash-readback',['-u','0x08000000','131072',str(readback)]) assert readback.stat().st_size==131072 assert readback.read_bytes()[:len(data)]==data,'Flash image differs from published BIN' report['full_readback_identical']=True report['readback_image_sha256']=hashlib.sha256(readback.read_bytes()[:len(data)]).hexdigest() report['readback_128k_sha256']=hashlib.sha256(readback.read_bytes()).hexdigest() log=run('reset',['-rst']);assert b'reset is performed' in log.lower() report['reset_performed']=True time.sleep(8) finally: stop.set() if thread:thread.join(timeout=2) if port:port.close() (O/'uart-com6.bin').write_bytes(capture) commands=[];unknown=[] for frame in bytes(capture).split(b'\xff\xff\xff'): if not frame:continue try: command=frame.decode('ascii') if re.fullmatch(r'(?:[A-Za-z_][A-Za-z_0-9]*\.){0,2}[A-Za-z_][A-Za-z_0-9]*=-?\d+',command):commands.append(command) else:unknown.append(frame.hex(' ')) except UnicodeDecodeError:unknown.append(frame.hex(' ')) report['serial']={'port':'COM6','baudrate':9600,'captured_bytes':len(capture),'complete_commands':commands,'other_frames_hex':unknown,'errors':errors,'boot_unknown_uart_verified':all(c in commands for c in ['current_task=7','lock_state=0','ebs_state=0','as_state=6'])} (O/'flash-and-uart-results.json').write_text(json.dumps(report,ensure_ascii=False,indent=2)+'\n',encoding='utf-8') (R/'tmp/as-flash-evidence-path.txt').write_text(str(O),encoding='utf-8') print(json.dumps(report,ensure_ascii=False,indent=2),flush=True)