258 lines
9.8 KiB
Python
258 lines
9.8 KiB
Python
import time
|
||
import logging
|
||
from threading import Thread, Event
|
||
from PySide6.QtCore import QObject, Signal
|
||
from .modbus_utils import ModbusUtils
|
||
|
||
class RegisterValue:
|
||
"""寄存器值对象,用于存储寄存器的值和状态"""
|
||
def __init__(self, address, value=None):
|
||
self.address = address
|
||
self.value = value
|
||
self.last_value = None
|
||
self.error_count = 0
|
||
self.last_read_time = None
|
||
self.last_change_time = None
|
||
|
||
def update(self, new_value):
|
||
"""更新寄存器值,并返回值是否发生变化"""
|
||
self.last_read_time = time.time()
|
||
if self.value != new_value:
|
||
self.last_value = self.value
|
||
self.value = new_value
|
||
self.last_change_time = self.last_read_time
|
||
return True
|
||
return False
|
||
|
||
def record_error(self):
|
||
"""记录错误次数"""
|
||
self.error_count += 1
|
||
return self.error_count
|
||
|
||
def reset_error(self):
|
||
"""重置错误计数"""
|
||
self.error_count = 0
|
||
|
||
|
||
class RegisterHandler:
|
||
"""寄存器处理器基类"""
|
||
def handle_change(self, value):
|
||
"""处理寄存器值变化的方法,由子类实现具体逻辑"""
|
||
pass
|
||
|
||
|
||
class ModbusMonitor(QObject):
|
||
"""Modbus寄存器监控器"""
|
||
# 定义信号:寄存器地址、新值
|
||
register_changed = Signal(int, int)
|
||
register_error = Signal(int, str)
|
||
monitor_status_changed = Signal(bool, str)
|
||
|
||
def __init__(self, polling_interval=1.0, max_errors=3, retry_interval=5.0):
|
||
"""
|
||
初始化Modbus监控器
|
||
|
||
Args:
|
||
polling_interval: 轮询间隔,单位秒
|
||
max_errors: 最大错误次数,超过此次数将暂停特定寄存器的监控
|
||
retry_interval: 重试间隔,单位秒
|
||
"""
|
||
super().__init__()
|
||
self.polling_interval = polling_interval
|
||
self.max_errors = max_errors
|
||
self.retry_interval = retry_interval
|
||
|
||
# 初始化存储
|
||
self.registers = {} # 存储寄存器值的字典
|
||
self.handlers = {} # 存储寄存器处理器的字典
|
||
self.stop_event = Event() # 用于停止监控线程的事件
|
||
self.monitor_thread = None
|
||
self.modbus = ModbusUtils()
|
||
self.client = None
|
||
self.running = False
|
||
|
||
# 初始化要监控的寄存器列表
|
||
self._initialize_registers()
|
||
|
||
def _initialize_registers(self):
|
||
"""初始化要监控的寄存器列表"""
|
||
# 默认监控的寄存器地址
|
||
register_addresses = [5, 6, 11, 13, 20, 21, 22, 23, 24, 30] # 添加寄存器30用于电力监控
|
||
for address in register_addresses:
|
||
self.registers[address] = RegisterValue(address)
|
||
|
||
def register_handler(self, address, handler):
|
||
"""注册寄存器处理器
|
||
|
||
Args:
|
||
address: 寄存器地址
|
||
handler: RegisterHandler的实例
|
||
"""
|
||
if address not in self.registers:
|
||
self.registers[address] = RegisterValue(address)
|
||
|
||
if address not in self.handlers:
|
||
self.handlers[address] = []
|
||
|
||
self.handlers[address].append(handler)
|
||
logging.info(f"已注册寄存器D{address}的处理器: {handler.__class__.__name__}")
|
||
|
||
def start(self):
|
||
"""启动监控线程"""
|
||
if self.monitor_thread and self.monitor_thread.is_alive():
|
||
logging.warning("监控器已经在运行中")
|
||
return
|
||
|
||
self.stop_event.clear()
|
||
self.monitor_thread = Thread(target=self._monitor_loop, daemon=True)
|
||
self.monitor_thread.start()
|
||
self.running = True
|
||
self.monitor_status_changed.emit(True, "监控器已启动")
|
||
logging.info("Modbus监控器已启动")
|
||
|
||
def stop(self):
|
||
"""停止监控线程"""
|
||
if not self.monitor_thread or not self.monitor_thread.is_alive():
|
||
return
|
||
|
||
self.stop_event.set()
|
||
if self.monitor_thread:
|
||
self.monitor_thread.join(timeout=5.0) # 给线程5秒时间完成
|
||
|
||
if self.client:
|
||
self.modbus.close_client(self.client)
|
||
self.client = None
|
||
|
||
self.running = False
|
||
self.monitor_status_changed.emit(False, "监控器已停止")
|
||
logging.info("Modbus监控器已停止")
|
||
|
||
def _monitor_loop(self):
|
||
"""监控循环,在独立线程中运行"""
|
||
try:
|
||
while not self.stop_event.is_set():
|
||
# 检查连接并根据需要重新连接
|
||
if not self.client:
|
||
self._reconnect()
|
||
|
||
if self.client:
|
||
# 读取并处理所有注册的寄存器
|
||
self._read_registers()
|
||
|
||
# 等待下一次轮询,或者直到收到停止信号
|
||
self.stop_event.wait(self.polling_interval)
|
||
except Exception as e:
|
||
logging.error(f"监控线程发生异常: {str(e)}", exc_info=True)
|
||
self.monitor_status_changed.emit(False, f"监控器异常: {str(e)}")
|
||
finally:
|
||
if self.client:
|
||
self.modbus.close_client(self.client)
|
||
self.client = None
|
||
|
||
def _reconnect(self):
|
||
"""重新连接到Modbus服务器"""
|
||
try:
|
||
if self.client:
|
||
self.modbus.close_client(self.client)
|
||
|
||
self.client = self.modbus.get_client()
|
||
if self.client:
|
||
logging.info("Modbus监控器成功连接到服务器")
|
||
self.monitor_status_changed.emit(True, "已连接到Modbus服务器")
|
||
# 重置所有寄存器的错误计数
|
||
for reg in self.registers.values():
|
||
reg.reset_error()
|
||
else:
|
||
logging.warning("Modbus监控器无法连接到服务器")
|
||
self.monitor_status_changed.emit(False, "无法连接到Modbus服务器")
|
||
# 等待重试间隔
|
||
self.stop_event.wait(self.retry_interval)
|
||
except Exception as e:
|
||
logging.error(f"重新连接Modbus服务器时发生错误: {str(e)}")
|
||
self.monitor_status_changed.emit(False, f"连接错误: {str(e)}")
|
||
# 等待重试间隔
|
||
self.stop_event.wait(self.retry_interval)
|
||
|
||
def _read_registers(self):
|
||
"""读取所有注册的寄存器"""
|
||
for address, reg_value in self.registers.items():
|
||
if self.stop_event.is_set():
|
||
break
|
||
|
||
# 如果错误次数超过阈值且未到重试时间,则跳过此寄存器
|
||
if reg_value.error_count >= self.max_errors:
|
||
current_time = time.time()
|
||
last_read = reg_value.last_read_time or 0
|
||
if current_time - last_read < self.retry_interval:
|
||
continue
|
||
|
||
try:
|
||
# 读取寄存器值
|
||
result = self.modbus.read_holding_register(self.client, address)
|
||
if result is None or len(result) == 0:
|
||
error_count = reg_value.record_error()
|
||
error_msg = f"读取寄存器D{address}失败,这是第{error_count}次失败"
|
||
logging.warning(error_msg)
|
||
self.register_error.emit(address, error_msg)
|
||
|
||
# 如果连续失败次数达到阈值,尝试重连
|
||
if error_count >= self.max_errors:
|
||
logging.error(f"寄存器D{address}连续{error_count}次读取失败,将在{self.retry_interval}秒后重试")
|
||
# 下次将在retry_interval后尝试读取此寄存器
|
||
continue
|
||
|
||
# 成功读取,重置错误计数
|
||
reg_value.reset_error()
|
||
|
||
# 更新值并检查是否发生变化
|
||
if reg_value.update(result[0]):
|
||
logging.info(f"寄存器D{address}值变化: {reg_value.last_value} -> {reg_value.value}")
|
||
# 发出信号
|
||
self.register_changed.emit(address, reg_value.value)
|
||
# 调用注册的处理器
|
||
self._notify_handlers(address, reg_value.value)
|
||
except Exception as e:
|
||
error_count = reg_value.record_error()
|
||
error_msg = f"读取寄存器D{address}时发生异常: {str(e)}"
|
||
logging.error(error_msg)
|
||
self.register_error.emit(address, error_msg)
|
||
|
||
if error_count >= self.max_errors:
|
||
logging.error(f"寄存器D{address}连续{error_count}次读取异常,将在{self.retry_interval}秒后重试")
|
||
|
||
def _notify_handlers(self, address, value):
|
||
"""通知所有注册的处理器"""
|
||
if address in self.handlers:
|
||
for handler in self.handlers[address]:
|
||
try:
|
||
handler.handle_change(value)
|
||
except Exception as e:
|
||
logging.error(f"调用寄存器D{address}的处理器时发生异常: {str(e)}", exc_info=True)
|
||
|
||
def get_register_value(self, address):
|
||
"""获取寄存器的当前值
|
||
|
||
Args:
|
||
address: 寄存器地址
|
||
|
||
Returns:
|
||
当前值,如果未读取过则返回None
|
||
"""
|
||
if address in self.registers:
|
||
return self.registers[address].value
|
||
return None
|
||
|
||
def is_running(self):
|
||
"""返回监控器是否正在运行"""
|
||
return self.running and self.monitor_thread and self.monitor_thread.is_alive()
|
||
|
||
|
||
# 单例模式
|
||
_instance = None
|
||
|
||
def get_instance():
|
||
"""获取ModbusMonitor单例"""
|
||
global _instance
|
||
if _instance is None:
|
||
_instance = ModbusMonitor()
|
||
return _instance |