Files

91 lines
5.4 KiB
Python

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)