feat: add VCU status feedback and map DEBUG safety inputs
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import struct
|
||||
import subprocess
|
||||
import time
|
||||
import pyautogui
|
||||
from pywinauto import Desktop
|
||||
|
||||
ROOT=Path(__file__).resolve().parent
|
||||
PROJECT=ROOT.parents[1]
|
||||
DLL=PROJECT/'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'
|
||||
NM=r'D:\STM32CubeCLT_1.19.0\GNU-tools-for-STM32\bin\arm-none-eabi-nm.exe'
|
||||
nm=subprocess.check_output([NM,'-n',str(PROJECT/'firmware/build/Release/AMI_Controller.elf')]).decode().replace('\r','')
|
||||
symbols={n:int(a,16) for a,k,n in re.findall(r'^(\w+)\s+(\w)\s+(\w+)$',nm,re.M)}
|
||||
names=['sdc_gpio_state','sdc_gpio_changed','sdc_gpio_timestamp_ms','sdc_gpio_received',
|
||||
'display_state','can_port_last_rx_ms','can_rx_error_count','uart_rx_error_count']
|
||||
base=min(symbols[n] for n in names)&~3
|
||||
length=((symbols['display_state']+56-base+3)//4)*4
|
||||
def memory(name):
|
||||
result=subprocess.run([CLI,'-c','port=SWD','mode=HOTPLUG','freq=1000','-r32',hex(base),str(length)],capture_output=True,timeout=15)
|
||||
(ROOT/f'gpio_{name}_ram.log').write_bytes(result.stdout+result.stderr)
|
||||
assert result.returncode==0
|
||||
mem={}
|
||||
for address,words in re.findall(r'0x([0-9A-Fa-f]+)\s*:\s*((?:[0-9A-Fa-f]{8}\s*)+)',result.stdout.decode(errors='replace')):
|
||||
addr=int(address,16)
|
||||
for word in words.split():
|
||||
for i,b in enumerate(struct.pack('<I',int(word,16))):mem[addr+i]=b
|
||||
addr+=4
|
||||
def get(n,fmt):return struct.unpack(fmt,bytes(mem[symbols[n]+i] for i in range(struct.calcsize(fmt))))
|
||||
return {'state':get('sdc_gpio_state','<H')[0],'changed':get('sdc_gpio_changed','<H')[0],
|
||||
'timestamp':get('sdc_gpio_timestamp_ms','<I')[0],'received':bool(get('sdc_gpio_received','<B')[0]),
|
||||
'debug_state':list(bytes(mem[symbols['display_state']+33+i] for i in range(16))),
|
||||
'port_rx_ms':list(get('can_port_last_rx_ms','<2I')),
|
||||
'can_errors':get('can_rx_error_count','<I')[0],'uart_errors':get('uart_rx_error_count','<I')[0]}
|
||||
rows=[]
|
||||
window=Desktop(backend='win32').window(handle=789888)
|
||||
window.set_focus()
|
||||
canvas=next(c.rectangle() for c in window.children() if c.rectangle().width()==720 and c.rectangle().height()==408)
|
||||
with can.Bus(interface='pcan',channel='PCAN_USBBUS1',bitrate=500000) as bus:
|
||||
assert bus.reset()
|
||||
def check(name,state,changed,timestamp,expected_state=None,expected_changed=None,extended=False,truncate=False):
|
||||
data=struct.pack('<HHI',state,changed,timestamp)
|
||||
if truncate:data=data[:7]
|
||||
bus.send(can.Message(arbitration_id=0x6a6,data=data,is_extended_id=extended),timeout=.2)
|
||||
time.sleep(.5)
|
||||
values=memory(name)
|
||||
if expected_state is None:expected_state=state&0x7fff
|
||||
if expected_changed is None:expected_changed=changed&0x7fff
|
||||
assert values['state']==expected_state and values['changed']==expected_changed,(name,values)
|
||||
if not (truncate or extended): assert values['timestamp']==timestamp,(name,values,timestamp)
|
||||
assert values['received'] and values['can_errors']==values['uart_errors']==0,(name,values)
|
||||
assert values['debug_state']==[0]*16,(name,values)
|
||||
assert values['port_rx_ms'][0]>0,(name,values)
|
||||
record={'name':name,'id':'0x6A6','data':data.hex(' '),'extended':extended,'snapshot':values,'pcan_status':hex(bus.status())}
|
||||
shot=ROOT/f'mcu_{name}.png'
|
||||
pyautogui.screenshot(region=(canvas.left,canvas.top,canvas.width(),canvas.height())).save(str(shot))
|
||||
record['screenshot']=shot.name
|
||||
rows.append(record)
|
||||
(ROOT/'gpio-can-results.json').write_text(json.dumps({'checks':rows},indent=2),encoding='utf-8')
|
||||
print(json.dumps({'name':name,'state':hex(values['state']),'changed':hex(values['changed']),'debug_mapped':False,'pcan_status':hex(bus.status())}),flush=True)
|
||||
return values
|
||||
check('zero',0,0x7fff,1000)
|
||||
for bit in range(15):
|
||||
check(f'PD{bit}_high',1<<bit,1<<bit,2000+bit*2)
|
||||
check(f'PD{bit}_low',0,1<<bit,2001+bit*2)
|
||||
check('all_high',0x7fff,0x7fff,0xffffffff)
|
||||
baseline=check('unused_bit15',0x8001,0x8002,5000)
|
||||
assert baseline['timestamp']==5000
|
||||
bad=check('truncated',0xffff,0xffff,6000,1,2,truncate=True)
|
||||
assert bad['timestamp']==5000
|
||||
bad=check('extended',0xffff,0xffff,7000,1,2,extended=True)
|
||||
assert bad['timestamp']==5000
|
||||
check('final_zero',0,0x7fff,8000)
|
||||
time.sleep(6)
|
||||
ui=Desktop(backend='uia').window(handle=789888)
|
||||
counts=[{'id':c.element_info.automation_id,'text':c.window_text()} for c in ui.descendants(control_type='Text') if c.element_info.automation_id.startswith('datalabel')]
|
||||
(ROOT/'mcu-input-ui-counters.json').write_text(json.dumps(counts,indent=2),encoding='utf-8')
|
||||
result={'result':'Raw GPIO CAN reception passed; named HMI mapping absent',
|
||||
'HMI_input_mode':'User MCU input, COM4 9600 8N1; no keyboard commands executed in this run',
|
||||
'HMI_rx_counters':counts,
|
||||
'bitrate':500000,'physical_port':'CAN1','source':'CAN-box simulation of SDC 0x6A6 format on CAN1 500K',
|
||||
'checked_inputs':15,'checks':rows,'firmware_mapping_present':False,'pcan_status':hex(bus.status())}
|
||||
(ROOT/'gpio-can-results.json').write_text(json.dumps(result,indent=2),encoding='utf-8')
|
||||
print(json.dumps({'result':'passed','checks':len(rows),'inputs':15,'mapping_present':False}),flush=True)
|
||||
Reference in New Issue
Block a user