Files
DebugTool/source/test_parameter.py
2025-01-20 11:13:57 +08:00

365 lines
22 KiB
Python

import time
import pytest
import logging
from typing import Tuple, Generator, List, Optional, Union
from device.DeviceSerial import DeviceSerial
from device.LaminaAdapter import LaminaAdapter
from device.tools.ByteConv import display_hex
TestCase_Controller = {
# 测试用例定义
# 0 - 正常写入数据序列; [(写入数据, 读取数据)]
# 1 - 范围限制测试; (最小值, 最大值)
# 2 - 相关大小限制测试; ((0-等于, 1-小于, 2-大于), 相关值地址, 死区范围)
0x60: {0: [(0, '0x0000'), (1, '0x0001'),
(2, '0x0001'), (0xFF, '0x0001'), (0xFFFF, '0x0001')]}, # 整机运行使能
0x61: {0: [(60.0, 60.0)], 1: (40, 100), 2: (1, 0x62, 0.5)}, # 最小启动允许输入电压
0x62: {0: [(440.0, 440.0)], 1: (350, 560), 2: (2, 0x61, 0.5)}, # 最大启动允许输入电压
0x63: {0: [(58, 58.0)], 1: (38, 98), 2: (1, 0x64, 0.5)}, # 最小停机输入电压
0x64: {0: [(442, 442.0)], 1: (352, 562), 2: (2, 0x63, 0.5)}, # 最大停机输入电压
0x65: {0: [(41, 41.0)], 1: (30, 60), 2: (1, 0x66, 0.5)}, # 最小启动允许输出电压
0x66: {0: [(57, 57.0)], 1: (50, 80), 2: (2, 0x65, 0.5)}, # 最大启动允许输出电压
0x67: {0: [(40, 40.0)], 1: (28, 58), 2: (1, 0x68, 0.5)}, # 最小停止允许输出电压
0x68: {0: [(58, 58.0)], 1: (52, 82), 2: (2, 0x67, 0.5)}, # 最大停止允许输出电压
0x69: {0: [(0.1, 0.1)], 1: (0, 5), 2: (1, 0x6A, 1.0)}, # 最小MPPT电流限值
0x6A: {0: [(22, 22.0)], 1: (5, 30), 2: (2, 0x69, 1.0)}, # 最大MPPT电流限值
0x6C: {0: [(6000, 6000)], 1: (3000, 7999.999),2: (0, 0x6E, 0.0)}, # 最大功率限值(由于转换误差, 无法写入8000W)
0x6E: {0: [(6000, 6000)], 1: (3000, 7999.999), }, # 最大功率限值存储值(由于转换误差, 无法写入8000W)
0x70: {0: [(500, 500)], 1: (400, 800), }, # Boost输入过压保护值
0x71: {0: [(460, 460)], 1: (300, 600), }, # Boost输出过压保护值
0x72: {0: [(60, 60)], 1: (40, 80), 2: (2, 0x73, 0.5)}, # LLC输出过压保护值
0x73: {0: [(40, 40)], 1: (20, 50), 2: (1, 0x72, 0.5)}, # LLC输出欠压保护值
0x74: {0: [(20, 20)], 1: (6, 30), }, # Boost电感过流保护值
0x75: {0: [(20, 20)], 1: (10, 30), }, # LLC输出电流均值保护值
0x76: {0: [(20, 20)], 1: (10, 30), }, # LLC输出电流峰值保护值
0x78: {0: [(6200, 6200)], 1: (4500, 9000), }, # 过载保护值
0x7A: {0: [(105, 105)], 1: (30, 150), 2: (2, 0x7B, 1.0)}, # 过温故障值
0x7B: {0: [(95, 95)], 1: (20, 150), 2: (1, 0x7A, 1.0)}, # 过温告警值
0x7C: {0: [(85, 85)], 1: (0, 120), 2: (1, 0x7A, 1.0)}, # 过温恢复值
0x7D: {0: [(10, 10)], 1: (0, 60), }, # 输出继电器故障判断差值
0x7E: {0: [(55, 55)], 1: (35, 60), }, # LLC输出电压给定值
0x7F: {0: [(420, 420)], 1: (320, 460), }, # Boost输出电压给定值
0x80: {0: [(56, 56)], 1: (10, 80), }, # 三点法中间阈值
0x81: {0: [(57, 57)], 1: (10, 80), }, # 浮充电压
0x82: {0: [(56, 56)], 1: (10, 80), }, # 恒压充电电压
0x83: {0: [(380, 380)], 1: (0, 450), }, # llc软起开始电压
0x84: {0: [(395, 395)], 1: (0, 450), 2: (1, 0x85, 0.5)}, # boost开始运行电压
0x85: {0: [(410, 410)], 1: (0, 450), 2: (2, 0x84, 0.5)}, # boost停止运行电压
0x86: {0: [(15000, 15000)], 1: (0, 30000), }, # 绝缘检测正阻抗限值
0x88: {0: [(15000, 15000)], 1: (0, 30000), }, # 绝缘检测负阻抗限值
0x8A: {0: [(123, 123), (137, 137)], 1: (50, 200), 2: (1, 0x8B, 0.2)}, # 抖动频率下限(精度误差严重, 难以正常测试)
0x8B: {0: [(137, 137), (123, 123)], 1: (50, 200), 2: (2, 0x8A, -0.2)}, # 抖动频率上限(精度误差严重, 难以正常测试)
0x170: {0: [(b'TTE0102DX20241001120001', 'TTE0102DX20241001120001\x00\x00\x00\x00\x00\x00\x00\x00\x00')]}, # 设备序列号
0x180: {0: [([0x24, 0x10, 0x01, 0x12, 0x00, 0x01], '$\x10\x01\x12\x00\x01' + 26 * '\x00')]}, # 设备MES码
0x190: {0: [(b'241001120001', '241001120001' + 20 * '\000')]}, # 出厂日期批次
}
TestCase_Adapter = {
# 测试用例定义
# 0 - 正常写入数据序列; [(写入数据, 读取数据)]
# 1 - 范围限制测试; (最小值, 最大值)
# 2 - 相关大小限制测试; ((0-等于, 1-小于, 2-大于), 相关值地址, 死区范围)
0x60: {0: [(0, '0x0000'), (1, '0x0001'),
(2, '0x0001'), (0xFF, '0x0001'), (0xFFFF, '0x0001')]}, # 整机运行使能
0x61: {0: [(60.0, 60.0)], 1: (40, 100), 2: (1, 0x62, 0.5)}, # 最小启动允许输入电压
0x62: {0: [(440.0, 440.0)], 1: (350, 560), 2: (2, 0x61, 0.5)}, # 最大启动允许输入电压
0x63: {0: [(58, 58.0)], 1: (38, 98), 2: (1, 0x64, 0.5)}, # 最小停机输入电压
0x64: {0: [(442, 442.0)], 1: (352, 562), 2: (2, 0x63, 0.5)}, # 最大停机输入电压
0x65: {0: [(41, 41.0)], 1: (30, 60), 2: (1, 0x66, 0.5)}, # 最小启动允许输出电压
0x66: {0: [(57, 57.0)], 1: (50, 80), 2: (2, 0x65, 0.5)}, # 最大启动允许输出电压
0x67: {0: [(40, 40.0)], 1: (28, 58), 2: (1, 0x68, 0.5)}, # 最小停止允许输出电压
0x68: {0: [(58, 58.0)], 1: (52, 82), 2: (2, 0x67, 0.5)}, # 最大停止允许输出电压
0x69: {0: [(0.1, 0.1)], 1: (0, 5), 2: (1, 0x6A, 1.0)}, # 最小MPPT电流限值
0x6A: {0: [(22, 22.0)], 1: (5, 30), 2: (2, 0x69, 1.0)}, # 最大MPPT电流限值
0x6C: {0: [(6000, 6000)], 1: (3000, 7999.999),2: (0, 0x6E, 0.0)}, # 最大功率限值(由于转换误差, 无法写入8000W)
0x6E: {0: [(6000, 6000)], 1: (3000, 7999.999), }, # 最大功率限值存储值(由于转换误差, 无法写入8000W)
0x70: {0: [(500, 500)], 1: (400, 800), }, # Boost输入过压保护值
0x71: {0: [(460, 460)], 1: (300, 600), }, # Boost输出过压保护值
0x72: {0: [(60, 60)], 1: (40, 80), 2: (2, 0x73, 0.5)}, # LLC输出过压保护值
0x73: {0: [(40, 40)], 1: (20, 50), 2: (1, 0x72, 0.5)}, # LLC输出欠压保护值
0x74: {0: [(20, 20)], 1: (6, 30), }, # Boost电感过流保护值
0x75: {0: [(20, 20)], 1: (10, 30), }, # LLC输出电流均值保护值
0x76: {0: [(20, 20)], 1: (10, 30), }, # LLC输出电流峰值保护值
0x78: {0: [(6200, 6200)], 1: (4500, 9000), }, # 过载保护值
0x7A: {0: [(105, 105)], 1: (30, 150), 2: (2, 0x7B, 1.0)}, # 过温故障值
0x7B: {0: [(95, 95)], 1: (20, 150), 2: (1, 0x7A, 1.0)}, # 过温告警值
0x7C: {0: [(85, 85)], 1: (0, 120), 2: (1, 0x7A, 1.0)}, # 过温恢复值
0x7D: {0: [(10, 10)], 1: (0, 60), }, # 输出继电器故障判断差值
0x7E: {0: [(55, 55)], 1: (35, 60), }, # LLC输出电压给定值
0x7F: {0: [(420, 420)], 1: (320, 460), }, # Boost输出电压给定值
0x80: {0: [(56, 56)], 1: (10, 80), }, # 三点法中间阈值
0x81: {0: [(57, 57)], 1: (10, 80), }, # 浮充电压
0x82: {0: [(56, 56)], 1: (10, 80), }, # 恒压充电电压
0x83: {0: [(380, 380)], 1: (0, 450), }, # llc软起开始电压
0x84: {0: [(395, 395)], 1: (0, 450), 2: (1, 0x85, 0.5)}, # boost开始运行电压
0x85: {0: [(410, 410)], 1: (0, 450), 2: (2, 0x84, 0.5)}, # boost停止运行电压
0x86: {0: [(15000, 15000)], 1: (0, 30000), }, # 绝缘检测正阻抗限值
0x88: {0: [(15000, 15000)], 1: (0, 30000), }, # 绝缘检测负阻抗限值
0x8A: {0: [(123, 123), (137, 137)], 1: (50, 200), 2: (1, 0x8B, 0.2)}, # 抖动频率下限(精度误差严重, 难以正常测试)
0x8B: {0: [(137, 137), (123, 123)], 1: (50, 200), 2: (2, 0x8A, -0.2)}, # 抖动频率上限(精度误差严重, 难以正常测试)
0x170: {0: [(b'TTE0102DX20241001120001', 'TTE0102DX20241001120001\x00\x00\x00\x00\x00\x00\x00\x00\x00')]}, # 设备序列号
0x180: {0: [([0x24, 0x10, 0x01, 0x12, 0x00, 0x01], '$\x10\x01\x12\x00\x01' + 26 * '\x00')]}, # 设备MES码
0x190: {0: [(b'241001120001', '241001120001' + 20 * '\000')]}, # 出厂日期批次
}
def frame_write(device:DeviceSerial, address, length, value):
""" 整合参数写入接口 """
return device.frame_write(address, length, value)
def frame_read(device:DeviceSerial, address, length=None):
""" 整合参数读取接口 """
if length is None:
info = device.block['data']['data_define'][address]
length = 2 if info[1] in [3, 6] else 1
length = info[2] if info[1] in [4, 5, 7] else length
return device.output['Regs'][address][1] if device.frame_read(address, length) else None
def value_case_generator(device:DeviceSerial, address:int, itemCase:dict) -> Generator[object, object, bool]:
""" 依据地址与参数配置生成测试用例 """
addr_relate = None
list_case_normal = []
match itemCase.keys():
case 0: # 常规读写用例测试
for data_write, data_read in itemCase[0]:
logging.info(f"Param Case:\taddr={display_hex(address)}, \tvalue={data_write}")
yield data_write, data_read, True
case 2: # 存在大小约束相关项
assert device.block['data']['data_define'][address][1] in [2, 3]
type_relate, addr_relate, dead_zone = itemCase[2]
val_relate = frame_read(device, addr_relate)
yield val_relate, val_relate, False
pass
case 1: # 写入范围用例测试
val_min, val_max = itemCase[1]
pass
if 1 in itemCase.keys():
""" 写入范围用例测试 """
testzone = 0.7 if info_param[1] in [2, 3] and len(info_param) < 3 else 7 / info_param[2]
accuracy = 0.20001 if info_param[1] in [2, 3] and len(info_param) < 3 else 2.0001 / info_param[2]
val_min, val_max = itemCase[1]
if 2 in itemCase.keys():
""" 存在大小约束相关项 """
list_case_relate = [] # 对约束相关项的修改
list_case_late = [] # 存在约束相关项影响后的测试用例
mode_relate, addr_relate, deadzone = itemCase[2]
val_relate = frame_read(device, addr_relate, ParamCase[addr_relate])
if mode_relate == 1:
if (val_relate - deadzone) < val_max:
""" 约束项已限制写入范围 """
list_case_relate.append((val_max + 2 * abs(deadzone), val_max + 2 * abs(deadzone), True))
list_case_late.append((val_max + testzone, val_max + testzone, False))
list_case_late.append((val_max, val_max, True))
list_case_late.append((val_max - testzone, val_max - testzone, True))
val_max = val_relate - deadzone
else:
""" 约束项未限制写入范围 """
val_relate = val_max
list_case_relate.append((val_relate, val_relate, True))
list_case_late.append((val_relate - deadzone + testzone, val_relate - deadzone + testzone, False))
list_case_late.append((val_relate - deadzone, val_relate - deadzone, True))
list_case_late.append((val_relate - deadzone - testzone, val_relate - deadzone - testzone, True))
elif mode_relate == 2:
if (val_relate + deadzone) > val_min:
""" 约束项已限制写入范围 """
list_case_relate.append((val_min - 2 * abs(deadzone), val_min - 2 * abs(deadzone), True))
list_case_late.append((val_min - testzone, val_min - testzone, False))
list_case_late.append((val_min, val_min, True))
list_case_late.append((val_min + testzone, val_min + testzone, True))
val_min = val_relate + deadzone
else:
""" 约束项未限制写入范围 """
val_relate = val_min
list_case_relate.append((val_relate, val_relate, True))
list_case_late.append((val_relate + deadzone - testzone, val_relate + deadzone - testzone, False))
list_case_late.append((val_relate + deadzone, val_relate + deadzone, True))
list_case_late.append((val_relate + deadzone + testzone, val_relate + deadzone + testzone, True))
list_case_normal.append((val_min - testzone, val_min - testzone, False))
list_case_normal.append((val_min, val_min, True))
list_case_normal.append((val_min + testzone, val_min + testzone, True))
list_case_normal.append((val_max + testzone, val_max + testzone, False))
list_case_normal.append((val_max, val_max, True))
list_case_normal.append((val_max - testzone, val_max - testzone, True))
print(f"Param Case:\taddr={display_hex(addr_param)}")
pass
def test_communication(device: DeviceSerial, time_out=2):
""" 通信成功率测试 """
time_start = time.time()
param_saved = device.flag_print, device.retry, device.time_out
device.flag_print = False
device.retry = 1
try:
while True:
device.frame_read(0x0C, 0x20)
print(f"Time Stamp: {time.ctime()}")
print(f"Success Frame: {device.log['read']}")
print(f"Failed Frame: {device.log['send'] - device.log['read']}")
print(f"Max Series Failed Frame: {device.log['keep-fail']}")
time.sleep(time_out)
finally:
time_end = time.time()
print("Test Result: ")
print(f"Time Start: {time.strftime(r'%Y-%m-%d %H:%M:%S', time.localtime(time_start))}, \tTime End: {time.strftime(r'%Y-%m-%d %H:%M:%S', time.localtime(time_end))}")
print(f"Time Elapsed: {time_end - time_start}")
print(f"Success Rate: {device.log['read'] / device.log['send'] * 100}%")
device.flag_print, device.retry, device.time_out = param_saved
@pytest.fixture
def device():
mode_config = {
"Debug": {'com_name': 'COM3',
'frame_print': None,
'time_out': 0.5, 'retry': 3},
}
device = LaminaAdapter(**mode_config['Debug'])
yield device
device.close_connection()
@pytest.mark.parametrize("address, case_value", *TestCase_Adapter.items())
def test_parameters(device:DeviceSerial, address, itemCase):
""" 参数读写测试 """
addr_relate = None
itemCase = ParamCase[addr_param]
list_case_normal = []
if 0 in itemCase.keys():
""" 常规读写用例测试 """
for data_write, data_read in itemCase[0]:
list_case_normal.append((data_write, data_read, True))
if 1 in itemCase.keys():
""" 写入范围用例测试 """
testzone = 0.7 if info_param[1] in [2, 3] and len(info_param) < 3 else 7 / info_param[2]
accuracy = 0.20001 if info_param[1] in [2, 3] and len(info_param) < 3 else 2.0001 / info_param[2]
val_min, val_max = itemCase[1]
if 2 in itemCase.keys():
""" 存在大小约束相关项 """
list_case_relate = [] # 对约束相关项的修改
list_case_late = [] # 存在约束相关项影响后的测试用例
mode_relate, addr_relate, deadzone = itemCase[2]
val_relate = frame_read(device, addr_relate, ParamCase[addr_relate])
if mode_relate == 1:
if (val_relate - deadzone) < val_max:
""" 约束项已限制写入范围 """
list_case_relate.append((val_max + 2 * abs(deadzone), val_max + 2 * abs(deadzone), True))
list_case_late.append((val_max + testzone, val_max + testzone, False))
list_case_late.append((val_max, val_max, True))
list_case_late.append((val_max - testzone, val_max - testzone, True))
val_max = val_relate - deadzone
else:
""" 约束项未限制写入范围 """
val_relate = val_max
list_case_relate.append((val_relate, val_relate, True))
list_case_late.append((val_relate - deadzone + testzone, val_relate - deadzone + testzone, False))
list_case_late.append((val_relate - deadzone, val_relate - deadzone, True))
list_case_late.append((val_relate - deadzone - testzone, val_relate - deadzone - testzone, True))
elif mode_relate == 2:
if (val_relate + deadzone) > val_min:
""" 约束项已限制写入范围 """
list_case_relate.append((val_min - 2 * abs(deadzone), val_min - 2 * abs(deadzone), True))
list_case_late.append((val_min - testzone, val_min - testzone, False))
list_case_late.append((val_min, val_min, True))
list_case_late.append((val_min + testzone, val_min + testzone, True))
val_min = val_relate + deadzone
else:
""" 约束项未限制写入范围 """
val_relate = val_min
list_case_relate.append((val_relate, val_relate, True))
list_case_late.append((val_relate + deadzone - testzone, val_relate + deadzone - testzone, False))
list_case_late.append((val_relate + deadzone, val_relate + deadzone, True))
list_case_late.append((val_relate + deadzone + testzone, val_relate + deadzone + testzone, True))
list_case_normal.append((val_min - testzone, val_min - testzone, False))
list_case_normal.append((val_min, val_min, True))
list_case_normal.append((val_min + testzone, val_min + testzone, True))
list_case_normal.append((val_max + testzone, val_max + testzone, False))
list_case_normal.append((val_max, val_max, True))
list_case_normal.append((val_max - testzone, val_max - testzone, True))
print(f"Param Case:\taddr={display_hex(addr_param)}")
last_value = frame_read(device, addr_param, info_param)
for case_test in list_case_normal:
print(f"\tnormal case={case_test}")
result = frame_write(device, addr_param, info_param, case_test[0])
assert result == case_test[2]
current_value = frame_read(device, addr_param, info_param)
if current_value is None:
raise Exception("Param Read Fail")
elif result:
if type(current_value) is float:
if abs(current_value - case_test[1]) > accuracy:
raise Exception("Param Check Fail")
else:
if current_value != case_test[1]:
raise Exception("Param Check Fail")
elif (not result) and current_value != last_value:
raise Exception("Param Check Fail")
last_value = current_value
if list_case_normal and list_case_normal[0][1] != last_value:
""" 为参数写入首个测试用例数据(一般为参数默认值), 避免影响后续参数测试 """
case_test = list_case_normal[0]
result = frame_write(device, addr_param, info_param, case_test[0])
assert result == case_test[2]
last_value = frame_read(device, addr_param, info_param)
if addr_relate:
""" 存在关联测试项 """
for case_relate in list_case_relate:
print(f"\trelate state: addr={display_hex(addr_relate)}, value={case_relate[0]}")
result = frame_write(device, addr_relate, info_param, case_relate[0])
if result == case_relate[2]:
for case_test in list_case_late:
print(f"\t\tstate case={case_test}")
result = frame_write(device, addr_param, info_param, case_test[0])
assert result == case_test[2]
current_value = frame_read(device, addr_param, info_param)
if current_value is None:
raise Exception("Param Read Fail")
elif result:
if type(current_value) is float:
if abs(current_value - case_test[1]) > accuracy:
raise Exception("Param Check Fail")
else:
if current_value != case_test[1]:
raise Exception("Param Check Fail")
elif (not result) and current_value != last_value:
raise Exception("Param Check Fail")
last_value = current_value
if list_case_normal and list_case_normal[0][1] != last_value:
""" 为参数写入首个测试用例数据(一般为参数默认值), 避免影响后续参数测试 """
case_test = list_case_normal[0]
result = frame_write(device, addr_param, info_param, case_test[0])
assert result == case_test[2]
def main():
mode_config = {
"Log": {'com_name': None,
# 'addr_645': [0x01, 0x00, 0x00, 0x00, 0x00, 0x40],
},
"Debug": {'com_name': 'COM3',
'addr_645': [0x01, 0x02, 0x03, 0x04, 0x05, 0x06],
'frame_print': True,
'time_out': 0.5, 'retry': 3},
}
dev_lamina = LaminaAdapter(**mode_config['Debug'])
test_parameters(dev_lamina, dev_lamina.block['data']['data_define'], TestCase)
if __name__== "__main__":
pytest.main()