This commit is contained in:
2025-11-17 00:05:40 +08:00
parent f860c5a216
commit e3ecd0550f
55 changed files with 3204 additions and 528 deletions

259
tests/Investor485test.py Normal file
View File

@ -0,0 +1,259 @@
import serial
import time
import struct
class InovanceMD520:
def __init__(self, port='COM4', baudrate=9600, timeout=1):
"""
初始化汇川MD520变频器通信
:param port: 串口名称Windows为COMxLinux为/dev/ttyUSBx
:param baudrate: 波特率默认9600
:param timeout: 超时时间,秒
"""
self.port = port
self.baudrate = baudrate
self.timeout = timeout
self.ser = None
def connect(self):
"""连接串口"""
try:
self.ser = serial.Serial(
port=self.port,
baudrate=self.baudrate,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=self.timeout
)
print(f"成功连接到串口 {self.port}")
return True
except serial.SerialException as e:
print(f"连接串口失败: {e}")
return False
def disconnect(self):
"""断开串口连接"""
if self.ser and self.ser.is_open:
self.ser.close()
print("串口连接已关闭")
def calculate_crc(self, data):
"""
计算Modbus CRC16校验码
:param data: 字节数据
:return: CRC校验码低位在前高位在后
"""
crc = 0xFFFF
for byte in data:
crc ^= byte
for _ in range(8):
if crc & 0x0001:
crc = (crc >> 1) ^ 0xA001
else:
crc = crc >> 1
return struct.pack('<H', crc) # 小端序,低位在前
def query_frequency(self, slave_addr=0x01):
"""
查询变频器运行频率
:param slave_addr: 从站地址默认1
:return: 频率值Hz如查询失败返回None
"""
if not self.ser or not self.ser.is_open:
print("串口未连接")
return None
# 构建查询指令读取运行频率地址1001H
# 01 03 10 01 00 01
cmd_data = bytes([
slave_addr, # 从站地址
0x03, # 功能码:读取保持寄存器
0x10, 0x01, # 寄存器地址1001H运行频率
0x00, 0x01 # 读取1个寄存器
])
# 计算CRC
crc = self.calculate_crc(cmd_data)
# 完整指令
full_cmd = cmd_data + crc
print(f"发送查询指令: {full_cmd.hex().upper()}")
try:
# 清空输入缓冲区
self.ser.reset_input_buffer()
# 发送指令
self.ser.write(full_cmd)
# 等待响应根据文档需要等待3.5个字节时间)
time.sleep(0.01) # 约10ms延迟
# 读取响应
# 正常响应格式:地址(1) + 功能码(1) + 字节数(1) + 数据(2) + CRC(2) = 7字节
response = self.ser.read(7)
if len(response) < 7:
print(f"响应数据长度不足: {len(response)} 字节")
return None
print(f"收到响应: {response.hex().upper()}")
# 验证响应
if response[0] != slave_addr:
print(f"从站地址不匹配: 期望{slave_addr:02X}, 收到{response[0]:02X}")
return None
if response[1] != 0x03:
print(f"功能码错误: 期望03, 收到{response[1]:02X}")
return None
if response[2] != 0x02:
print(f"数据长度错误: 期望02, 收到{response[2]:02X}")
return None
# 验证CRC
received_crc = response[-2:]
calculated_crc = self.calculate_crc(response[:-2])
if received_crc != calculated_crc:
print("CRC校验失败")
return None
# 解析频率数据(高字节在前)
frequency_raw = (response[3] << 8) | response[4]
frequency_hz = frequency_raw / 100.0 # 转换为Hz单位0.01Hz
print(f"原始频率值: {frequency_raw} (0x{frequency_raw:04X})")
print(f"实际频率: {frequency_hz:.2f} Hz")
return frequency_hz
except Exception as e:
print(f"通信错误: {e}")
return None
def read_register(self, slave_addr, register_addr, register_count=1):
"""
通用读取寄存器方法
:param slave_addr: 从站地址
:param register_addr: 寄存器地址16位
:param register_count: 读取的寄存器数量
:return: 读取的数据列表
"""
if not self.ser or not self.ser.is_open:
print("串口未连接")
return None
# 构建读取指令
cmd_data = bytes([
slave_addr, # 从站地址
0x03, # 功能码:读取保持寄存器
(register_addr >> 8) & 0xFF, # 寄存器地址高字节
register_addr & 0xFF, # 寄存器地址低字节
(register_count >> 8) & 0xFF, # 寄存器数量高字节
register_count & 0xFF # 寄存器数量低字节
])
# 计算CRC
crc = self.calculate_crc(cmd_data)
full_cmd = cmd_data + crc
print(f"发送读取指令: {full_cmd.hex().upper()}")
try:
self.ser.reset_input_buffer()
self.ser.write(full_cmd)
time.sleep(0.01)
# 计算预期响应长度
expected_length = 5 + 2 * register_count # 地址1 + 功能码1 + 字节数1 + 数据2*N + CRC2
response = self.ser.read(expected_length)
if len(response) < expected_length:
print(f"响应数据长度不足: {len(response)} 字节,期望 {expected_length} 字节")
return None
print(f"收到响应: {response.hex().upper()}")
# 验证CRC
received_crc = response[-2:]
calculated_crc = self.calculate_crc(response[:-2])
if received_crc != calculated_crc:
print("CRC校验失败")
return None
# 解析数据
data_length = response[2]
data_bytes = response[3:3 + data_length]
results = []
for i in range(0, len(data_bytes), 2):
value = (data_bytes[i] << 8) | data_bytes[i + 1]
results.append(value)
return results
except Exception as e:
print(f"通信错误: {e}")
return None
def main():
# 创建变频器对象
inverter = InovanceMD520(port='COM4', baudrate=9600)
# 连接串口
if not inverter.connect():
return
try:
while True:
print("\n" + "=" * 50)
print("汇川MD520变频器频率查询")
print("=" * 50)
# 查询运行频率
frequency = inverter.query_frequency(slave_addr=0x01)
if frequency is not None:
print(f"✅ 当前运行频率: {frequency:.2f} Hz")
else:
print("❌ 频率查询失败")
# 可选:读取其他监控参数
print("\n--- 其他监控参数 ---")
# 读取母线电压 (地址1002H)
voltage_data = inverter.read_register(0x01, 0x1002)
if voltage_data:
voltage = voltage_data[0] / 10.0 # 单位0.1V
print(f"母线电压: {voltage:.1f} V")
# 读取输出电压 (地址1003H)
output_voltage_data = inverter.read_register(0x01, 0x1003)
if output_voltage_data:
output_voltage = output_voltage_data[0] # 单位1V
print(f"输出电压: {output_voltage} V")
# 读取输出电流 (地址1004H)
current_data = inverter.read_register(0x01, 0x1004)
if current_data:
current = current_data[0] / 100.0 # 单位0.01A
print(f"输出电流: {current:.2f} A")
# 等待5秒后再次查询
print("\n等待5秒后继续查询...")
time.sleep(5)
except KeyboardInterrupt:
print("\n用户中断查询")
finally:
# 断开连接
inverter.disconnect()
if __name__ == "__main__":
main()

View File

@ -20,11 +20,12 @@ def test_relay_controls(relay):
# 测试上料斗滑动门
print("测试上料斗滑动门开启...")
relay.control(relay.DOOR_UPPER, 'open')
relay.control(relay.UPPER_TO_JBL, 'open')
time.sleep(1)
relay.control(relay.UPPER_TO_JBL, 'close')
time.sleep(1)
print("测试上料斗滑动门关闭...")
relay.control(relay.DOOR_UPPER, 'close')
relay.control(relay.UPPER_TO_ZD, 'open')
time.sleep(1)
# 测试上料斗出砼门
@ -137,13 +138,13 @@ def main():
# 初始化控制器
print("初始化控制器...")
relay = RelayController(host='192.168.0.18', port=50000)
inverter = InverterController(relay_controller=relay)
transmitter = TransmitterController(relay_controller=relay)
# inverter = InverterController(relay_controller=relay)
# transmitter = TransmitterController(relay_controller=relay)
# 执行各项测试
test_relay_controls(relay)
test_inverter_controls(inverter)
test_transmitter_reading(transmitter)
# test_transmitter_reading(transmitter)
print("\n所有测试完成!")

View File

@ -13,7 +13,7 @@ class TestRelayController(unittest.TestCase):
def setUp(self):
"""测试前的准备工作"""
self.relay_host = '192.168.0.18'
self.relay_host = '192.168.250.62'
self.relay_port = 50000
self.relay = RelayController(host=self.relay_host, port=self.relay_port)
@ -24,7 +24,8 @@ class TestRelayController(unittest.TestCase):
self.assertIsNotNone(self.relay.modbus_client)
# 检查设备映射
self.assertIn(RelayController.DOOR_UPPER, self.relay.device_bit_map)
self.assertIn(RelayController.UPPER_TO_JBL, self.relay.device_bit_map)
self.assertIn(RelayController.UPPER_TO_ZD, self.relay.device_bit_map)
self.assertIn(RelayController.DOOR_LOWER_1, self.relay.device_bit_map)
self.assertIn(RelayController.DOOR_LOWER_2, self.relay.device_bit_map)
self.assertIn(RelayController.BREAK_ARCH_UPPER, self.relay.device_bit_map)
@ -62,7 +63,7 @@ class TestRelayController(unittest.TestCase):
"""测试控制有效设备和动作"""
with patch.object(self.relay, 'send_command') as mock_send:
# 测试打开上料斗门
self.relay.control(RelayController.DOOR_UPPER, 'open')
self.relay.control(RelayController.UPPER_TO_JBL, 'open')
mock_send.assert_called_once()
def test_control_invalid_device(self):
@ -76,7 +77,7 @@ class TestRelayController(unittest.TestCase):
"""测试控制无效动作"""
with patch.object(self.relay, 'send_command') as mock_send:
# 测试无效动作
self.relay.control(RelayController.DOOR_UPPER, 'invalid_action')
self.relay.control(RelayController.UPPER_TO_JBL, 'invalid_action')
mock_send.assert_not_called()
@patch.object(RelayController, 'send_command')
@ -89,7 +90,8 @@ class TestRelayController(unittest.TestCase):
# 验证返回的状态字典
self.assertIsInstance(status, dict)
self.assertIn(RelayController.DOOR_UPPER, status)
self.assertIn(RelayController.UPPER_TO_JBL, status)
self.assertIn(RelayController.UPPER_TO_ZD, status)
mock_send_command.assert_called_once_with(self.relay.read_status_command)
@patch.object(RelayController, 'send_command')

65
tests/test_vision.py Normal file
View File

@ -0,0 +1,65 @@
import os
import sys
# 添加项目根目录到Python路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
import unittest
from unittest.mock import MagicMock
from vision.camera import CameraController
# from core.vision import Vision
class TestVision(unittest.TestCase):
def setUp(self):
self.jj=2
# self.testclass2 = MagicMock()
# self.testclass = TestClass(self.testclass2)
def test_capture_frame(self):
# 测试capture_frame方法
camera=CameraController()
result = camera.capture_frame()
self.assertIsNone(result, msg="capture_frame方法测试失败")
camera.capture_frame_exec.assert_called_once()
# def test_first(self):
# 测试TestClass的add方法
# mock_testclass2 = MagicMock()
# # mock_testclass2.i = 1
# # mock_testclass2.j = 2
# test_class = TestClass(mock_testclass2)
# result = test_class.add()
# # 验证结果
# self.assertEqual(result, 3, msg="add方法测试失败")
# def test_second(self):
# 测试TestClass2的mock行为
# mock_testclass2 = MagicMock(spec=TestClass2)
# mock_testclass2.sub.return_value = 1
# result = mock_testclass2.sub()
# # 测试返回值
# self.assertEqual(result, 1, msg="sub方法测试失败")
# mock_testclass2.sub.assert_called_once()
class TestClass:
def __init__(self,testclass2):
self.testclass2 = testclass2
pass
def add(self):
return self.testclass2.i + self.testclass2.j
class TestClass2:
def __init__(self):
self.i = 1
self.j = 2
pass
def sub(self):
return self.j - self.i
if __name__ == '__main__':
unittest.main()