0%

模拟手动操作exe软件

使用背景

通过上位机给设备压测升级,需要升级1000次及以上,因此,通过手动点击串口按钮并选择升级固件再确认升级不太现实。为了解决这个问题,笔者通过pywinauto中的application来自动化识别exe程序界面控件属性,并且模拟人工手动点击。

调用库

1
2
3
4
5
from pywinauto.application import Application
import datetime
import time
from loguru import logger
import serial

保存日志

1
logger.add("串口升级固件10000次_runtime_{time}.log", encoding='utf-8')

构建类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Update_Upper_Computer():
def __init__(self, time_sleep, update_count, COM, software_path):
self.time_sleep = time_sleep # 两次通过上位机升级固件的时间间隔,单位秒
self.update_count = update_count # 升级总次数
self.com = COM # 串口号
self.software_path = software_path # 上位机软件路径
# 主机固件
self.update_H3PI_version1 = r"D:\Date\固件\PP_v1.0.1.bin" # 【修改】固件路径
self.update_H3PI_version2 = r"D:\Date\固件\APP_v1.0.2.bin" # 【修改】固件路径
self.check_H3PI_version1 = "v1.0.1" # 【修改】根据固件版本修改
self.check_H3PI_version2 = "v1.0.2" # 【修改】根据固件版本修改

self.update_success_count = 0
self.update_fail_count = 0
self.update_all_count = 0

当前时间函数

1
2
3
def now_time(self):
time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') # 返回当前时间字符串
return str(time)

读取串口函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def ser_read(self, ser):
'''串口读取'''
print('正在读取串口内容!')
data = ''
while True:
data = ser.readline() # 获取串口内容
logger.info(data)
print('***********')
time1 = time.time() # 获取当前时间戳
logger.info(time1)
# time_array = time.localtime(time1) # 时间戳转时间数组
# time_str = time.strftime("%Y-%m-%d %H:%M:%S",time_array) # 时间数组转时间字符串
t = time.ctime() # ‘Sat Jun 06 16:26:11 1998’
print(t, ':')
print(data)
with open('D:/111.txt', 'a') as f: # 打开文件用于追加。如果文件已经存在,则指针放在文件结尾。 【修改文件路径】
f.writelines(t)
f.writelines(':\n')
try:
f.writelines(data.decode(encoding='utf-8'))
except Exception as e:
print(e) # 打印程序异常报错

升级函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
def update_H3PI(self):  # 升级主机固件
for i in range(self.update_count):
# 串口连接设备
ser = serial.Serial(self.com, 115200, 8, 'N', 1) # 该行代码不仅设置了串口的参数,还直接实现了串口的打开和连接

# 优化指令验证
# command = "1005h\n" # 打开日志开关,以便顺利进入升级
# ser.write(command.encode('utf-8'))

# 发送指令
command2 = "1005B\n" # 命令要加\n,否则识别为普通字符串
ser.write(command2.encode('utf-8')) # 命令要编码/410000001*

# 接收串口数据
content = ser.read(1000) # 读取1000个字节的数据
print(str(content))

content1 = content.decode('utf-8', 'ignore')
logger.info(content1)

ser.close() # 关闭串口

print(content1)
time.sleep(5)

update_firmware_version_path = " "
# 校验固件版本
if self.check_H3PI_version1 in content1:
print("当前固件版本为" + self.check_H3PI_version1 + ",即将升级固件版本为" + self.check_H3PI_version2)
logger.info(
"当前固件版本为" + self.check_H3PI_version1 + ",即将升级固件版本为" + self.check_H3PI_version2)
update_firmware_version_path = self.update_H3PI_version2
update_firmware_version_check = self.check_H3PI_version2
elif self.check_H3PI_version2 in content1:
print("当前固件版本为" + self.check_H3PI_version2 + ",即将升级固件版本为" + self.check_H3PI_version1)
logger.info(
"当前固件版本为" + self.check_H3PI_version2 + ",即将升级固件版本为" + self.check_H3PI_version1)
update_firmware_version_path = self.update_H3PI_version1
update_firmware_version_check = self.check_H3PI_version1

# 启动或连接到程序
app = Application(backend="uia").start(self.software_path)

# 连接到主窗口
dlg = app["H3更新工具"] # 用正则模糊匹配窗口标题

# 打开串口
combo = dlg.child_window(auto_id="port_comboBox1")
time.sleep(5)
combo.wait('visible', timeout=5) # 等待控件可见
combo.select(self.com)
open_button = dlg.child_window(auto_id="port_open_button1")
open_button.click()

# 选择加密芯片升级
encrypt_tab = dlg.child_window(title="加密芯片升级")
encrypt_tab.select()
file_path_box = dlg.child_window(auto_id="encrypt_chip_textBox")
file_path_box.set_text(update_firmware_version_path)

# 点击更新
update_button = dlg.child_window(auto_id="encrypt_chip_update_button")
update_button.click()

# 等待固件升级成功
time.sleep(self.time_sleep)

# 关闭应用
app.kill()
self.update_all_count = self.update_all_count + 1
time.sleep(5)

# 校验固件升级是否成功
# 串口检验当前设备H3PRO-I固件版本
ser = serial.Serial(self.com, 115200, 8, 'N', 1)

# 发送指令
command2 = "1005B\n" # 命令要加\n,否则识别为普通字符串
ser.write(command2.encode('utf-8')) # 命令也同样要编码/410000001*
# 接收串口数据
content = ser.read(1500)
content1 = content.decode('utf-8', 'ignore')
logger.info(content1)
# 关闭串口
ser.close()
# 校验固件版本
if update_firmware_version_check in content1:
self.update_success_count = self.update_success_count + 1
print("当前时间:" + self.now_time())
logger.info("当前时间:" + self.now_time())
print("当前升级状态统计:\n升级总次数:" + str(self.update_all_count) + "\n升级成功次数:" + str(
self.update_success_count) + "\n升级失败次数:" + str(self.update_fail_count))
logger.info("当前升级状态统计:\n升级总次数:" + str(self.update_all_count) + "\n升级成功次数:" + str(
self.update_success_count) + "\n升级失败次数:" + str(self.update_fail_count))
else:
print("当前时间:" + self.now_time())
logger.info("当前时间:" + self.now_time())
self.update_fail_count = self.update_fail_count + 1
print("当前升级状态统计:\n升级总次数:" + str(self.update_all_count) + "\n升级成功次数:" + str(
self.update_success_count) + "\n升级失败次数:" + str(self.update_fail_count))
logger.info("当前升级状态统计:\n升级总次数:" + str(self.update_all_count) + "\n升级成功次数:" + str(
self.update_success_count) + "\n升级失败次数:" + str(self.update_fail_count))
time.sleep(5)

主函数

1
2
3
4
5
6
7
if __name__ == "__main__":
a = Update_Upper_Computer(180, 10000, "COM37",
r"D:\Program\固件更新工具\升级配置工具 v1.0.0.5 2025.01.09\升级配置工具 v1.0.0.5 2025.01.09.exe")
# 上述参数的含义分别:['两次升级的时间间隔','升级总次数','串口号','上位机软件路径'

print(a.com)
a.update_H3PI()

调试函数

举一反三,如果读者想要模拟控制别的exe运行文件,确不知道相应控件的属性,那么可以通过以下函数来获取。如果需要选择的界面在另一个tabcontrol中,可以先去选中另一个tabcontrol再打印窗口控件信息。

1
2
3
4
5
6
7
8
9
10
11
from pywinauto.application import Application

# 启动或连接到程序
app = Application(backend="uia").start(
r"D:\Program\固件更新工具\升级配置工具 v1.0.0.5 2025.01.09\升级配置工具 v1.0.0.5 2025.01.09.exe")

# 连接到主窗口
dlg = app.window(title_re=".*H3.*") # 用正则模糊匹配窗口标题

# 显示界面所有的控件信息
dlg.print_control_identifiers()

本文使用升级工具

下载固件升级工具(仅供学习交流使用!)