Files

90 lines
5.0 KiB
Python

import json,os,threading,time
from pathlib import Path
import numpy as np
import pyautogui as pg
import win32gui,win32con
from pywinauto import Desktop
from pywinauto.controls.win32_controls import ComboBoxWrapper
R=Path(__file__).resolve().parent;P=R.parents[1]
DLL=P/'tmp/can1-test-20261002/pcan-basic/x64'
os.environ['PATH']=str(DLL)+os.pathsep+os.environ['PATH'];dh=os.add_dll_directory(str(DLL))
import can
W=4262948
w=Desktop(backend='win32').window(handle=W);u=Desktop(backend='uia').window(handle=W);w.set_focus()
if not u.child_window(title='停止',control_type='Hyperlink').exists():
w.child_window(title='用户MCU输入').click();time.sleep(.3)
for c in w.descendants():
if 'COMBOBOX' in c.class_name() and c.is_visible():
combo=ComboBoxWrapper(c.element_info);items=combo.item_texts()
if '9600' in items:combo.select('9600')
if 'COM4' in items:combo.select('COM4')
u.child_window(title='开始',control_type='Hyperlink').invoke();time.sleep(6)
canvas_control=next(c for c in w.children() if c.rectangle().width()==720 and c.rectangle().height()==408)
canvas=canvas_control.rectangle()
def touch(x,y):
point=(y<<16)|x
win32gui.SendMessage(canvas_control.handle,win32con.WM_LBUTTONDOWN,win32con.MK_LBUTTON,point)
time.sleep(.15)
win32gui.SendMessage(canvas_control.handle,win32con.WM_LBUTTONUP,0,point)
ROIS=[(185,275,445,313),(185,328,445,364),(472,349,705,394)]
checks=[];tx=[];errors=[];stop=threading.Event();payload=None
def check(name,colors,delay=1.2):
time.sleep(delay)
shot=pg.screenshot(region=(canvas.left,canvas.top,720,408));shot.save(str(R/f'can-{name}.png'))
pixels=np.asarray(shot);counts=[]
for (x1,y1,x2,y2),color in zip(ROIS,colors):
a=pixels[y1:y2,x1:x2,:3].astype(np.int16);r,g,b=a[:,:,0],a[:,:,1],a[:,:,2]
c=[int(((r>75)&(r<180)&(abs(r-g)<8)&(abs(r-b)<8)).sum()),int(((g>180)&(r<80)&(b<80)).sum()),int(((r>180)&(g<80)&(b<80)).sum())]
assert c[color]>20 and (color==1 or c[1]==0) and (color==2 or c[2]==0),(name,colors,c)
counts.append(c)
assert bus.status()==0 and not errors,(name,errors)
checks.append({'name':name,'colors':colors,'pixel_counts':counts,'passed':True,'screenshot':f'can-{name}.png'})
print(name,'passed',flush=True)
with can.Bus(interface='pcan',channel='PCAN_USBBUS1',bitrate=500000) as bus:
assert bus.reset()
def sender():
try:
while not stop.is_set():
msg=payload
if msg:
bus.send(msg,timeout=.2);tx.append({'id':hex(msg.arbitration_id),'data':msg.data.hex(' ')})
stop.wait(.1)
except Exception as exc:errors.append(repr(exc))
t=threading.Thread(target=sender);t.start()
def feedback(task,locked,normal,flags=7):
global payload
payload=can.Message(arbitration_id=0x183,is_extended_id=False,data=bytes([task,locked,normal,flags,0,0,0,1]))
try:
check('initial-unknown',[0,0,0])
for task in range(7):
feedback(task,1,1);check(f'task-{task}',[2 if task==0 else 1,1,1])
feedback(2,0,0);check('unlocked-ebs-fault',[1,2,2])
feedback(2,1,1,5);check('lock-invalid',[1,0,1])
feedback(2,1,1,3);check('ebs-invalid',[1,1,0])
feedback(2,1,1,6);check('task-invalid',[0,1,1])
feedback(2,1,1);check('before-EBS-request',[1,1,1])
while bus.recv(0):pass
# Actual mouse press/release invokes the real task and confirm events.
touch(75,292);time.sleep(.3)
pg.screenshot(region=(canvas.left,canvas.top,720,408)).save(str(R/'can-confirm-EBS-request.png'))
print('CONFIRM entered; locate confirm control from screenshot',flush=True)
# CONFIRM buttons are placed in the lower half of the page.
touch(180,323)
request=None;end=time.monotonic()+1
while time.monotonic()<end:
m=bus.recv(.1)
if m and m.arbitration_id==0x300:
request={'id':'0x300','data':m.data.hex(' ')};break
assert request and bytes.fromhex(request['data'])[1]==5,request
check('after-EBS-request-task-stays2',[1,1,1])
feedback(6,0,0);check('VCU-changes-task6',[1,2,2])
payload=can.Message(arbitration_id=0x6a0,is_extended_id=False,data=bytes.fromhex('A1 01 00 00 00 00 00 00'))
check('VCU-timeout-other-traffic',[0,0,0],1.4)
feedback(3,1,1);check('feedback-recovery-task3',[1,1,1])
payload=None;check('final-timeout',[0,0,0],1.4)
finally:
stop.set();t.join()
counters=[{'id':c.element_info.automation_id,'text':c.window_text()} for c in u.descendants(control_type='Text') if c.element_info.automation_id.startswith('datalabel')]
(R/'can-display-results.json').write_text(json.dumps({'scope':'Physical CAN1 PCAN -> STM32 -> USART2 COM4 -> official HMI simulator, not physical panel','checks':checks,'tx':tx,'errors':errors,'counters':counters,'request':request,'passed':True},ensure_ascii=False,indent=2),encoding='utf-8')
print(len(checks),'physical CAN/UART/display checks passed',counters,flush=True)