add(系统诊断: 设备检测和系统检测、dals中数据库路径)

This commit is contained in:
2026-01-13 18:31:54 +08:00
parent 29c459e0f2
commit 360cb13b73
5 changed files with 193 additions and 67 deletions

View File

@ -16,7 +16,7 @@ class BaseDal:
def __init__(self) -> None:
"""初始化数据访问层,创建数据库连接"""
# 假设数据库文件在db目录下
self.db_dao = SQLiteHandler.get_instance("../db/three.db", max_readers=50, busy_timeout=4000)
self.db_dao = SQLiteHandler.get_instance("db/three.db", max_readers=50, busy_timeout=4000)
class ArtifactDal(BaseDal):
def __init__(self):

View File

@ -18,5 +18,7 @@ ip = 192.168.250.77
[PLC]
ip = 192.168.250.233
[本地]
# 系统诊断检测的服务
[OPC]
ip = 127.0.0.1
port = 4840

View File

@ -8,7 +8,7 @@ from view.widgets.message_popup_widget import MessagePopupWidget
from service.msg_recorder import MessageRecorder
from service.msg_query_thread import MsgQueryThread
from service.device_monitor_thread import DeviceMonitorThread
from service.monitor_thread import MonitorThread
"""
控制主界面底部的所有按钮, 包括系统诊断、系统中心等的行为。
@ -22,8 +22,10 @@ class BottomControlController:
# 系统诊断弹窗
self.system_diagnostics_dialog = SystemDiagnosticsDialog(self.main_window)
self.current_diagnostics_row = 0 # 系统诊断弹窗的行号从0开始
self.current_diagnostics_col = 0 # 系统诊断弹窗的列号从0开始
self.diagnostics_device_row = 0 # 系统诊断弹窗的设备检测行号从0开始
self.diagnostics_device_col = 0 # 系统诊断弹窗的设备检测列号从0开始
self.diagnostics_service_row = 0 # 系统诊断弹窗的服务检测行号从0开始
self.diagnostics_service_col = 2 # 系统诊断弹窗的服务检测行号从2开始
# 系统中心弹窗
self.system_center_dialog = SystemCenterDialog(self.main_window)
@ -42,14 +44,14 @@ class BottomControlController:
self.msg_query_thread.start() # 启动线程
# =======================================================================
# ===================== 设备检测(系统诊断相关) ====================================
self.device_monitor = DeviceMonitorThread()
# ===================== 检测线程(系统诊断相关) ====================================
self.device_monitor = MonitorThread()
self.device_monitor.start()
# =======================================================================
# 绑定主界面底部按钮的信号(如:系统诊断按钮等,点击触发弹窗)
self._bind_buttons()
# 绑定弹窗中按钮的信号
# 绑定弹窗的信号
self._bind_dialog_signals()
@Slot(int, list)
@ -83,20 +85,23 @@ class BottomControlController:
# 底部预警消息列表按钮 → 触发预警消息列表显示/隐藏
self.bottom_control_widget.warning_list_btn.clicked.connect(self.toggle_system_warning_msg_list)
# 设备检测结果显示(底部的系统诊断按钮处显示)
# 底部系统诊断按钮 → 设备检测结果显示(底部的系统诊断按钮处显示)
self.device_monitor.state_result.connect( self.bottom_control_widget.set_system_status, Qt.QueuedConnection)
def _bind_dialog_signals(self):
"""绑定弹窗按钮的信号"""
"""绑定弹窗的信号"""
# 系统中心弹窗的按钮信号
self.system_center_dialog.sys_setting_clicked.connect(self.handle_sys_setting)
self.system_center_dialog.data_center_clicked.connect(self.handle_data_center)
self.system_center_dialog.user_center_clicked.connect(self.handle_user_center)
# 系统诊断弹窗的信号
self.device_monitor.check_finished.connect(self.reset_diagnostics_row_col) # 重置系统诊断的行号和列号
self.device_monitor.connect_success.connect(self._handle_diagnostics_connect_success) # 设备正常
self.device_monitor.connect_failed.connect(self._handle_diagnostics_connect_failed) # 设备异常
self.device_monitor.check_finished.connect(self._reset_diagnostics_device) # 重置系统诊断弹窗中的设备检测
self.device_monitor.connect_success.connect(self._handle_diagnostics_device_connect_success) # 设备正常
self.device_monitor.connect_failed.connect(self._handle_diagnostics_device_connect_failed) # 设备异常
self.device_monitor.check_finished.connect(self._reset_diagnostics_service) # 重置系统诊断弹窗中的服务检测
self.device_monitor.service_normal.connect(self._handle_diagnostics_service_normal) # 服务正常
self.device_monitor.service_error.connect(self._handle_diagnostics_service_error) # 服务异常
# ------------------- 系统中心弹窗逻辑-------------------
def toggle_system_center_dialog(self):
@ -176,39 +181,93 @@ class BottomControlController:
# 设置弹窗位置
self.system_diagnostics_dialog.move(dialog_x, dialog_y)
def get_diagnostics_row_col(self):
"""获取系统诊断弹窗的 行号 和 列号, 都从0开始"""
diagnostics_row = self.current_diagnostics_row
diagnostics_col = self.current_diagnostics_col
self.current_diagnostics_col += 1
if self.current_diagnostics_col == self.system_diagnostics_dialog.max_col:
self.current_diagnostics_row += 1
self.current_diagnostics_col = 0
if self.current_diagnostics_row == self.system_diagnostics_dialog.max_row:
self.current_diagnostics_row = 0
return diagnostics_row, diagnostics_col
def _get_diagnostics_device_row_col(self):
"""获取系统诊断弹窗中 设备检测的 行号 和 列号, 都从0开始"""
device_row = self.diagnostics_device_row
device_col = self.diagnostics_device_col
self.diagnostics_device_col += 1
if self.diagnostics_device_col == self.system_diagnostics_dialog.max_col // 2: # 左边两列是 设备检测
self.diagnostics_device_row += 1
self.diagnostics_device_col = 0
if self.diagnostics_device_row == self.system_diagnostics_dialog.max_row:
self.diagnostics_device_row = 0
return device_row, device_col
def reset_diagnostics_row_col(self):
"""重置系统诊断弹窗的 行号 和 列号 为0"""
self.current_diagnostics_row = 0
self.current_diagnostics_col = 0
def _reset_diagnostics_device(self):
"""重置系统诊断弹窗中 设备检测"""
# 0、隐藏多余的设备检测框
# 1、设备检测的行号 和 列号(diagnostics_device_row、 self.diagnostics_device_col) 重置为0
while True:
if self.diagnostics_device_row == 0 and self.diagnostics_device_col == 0:
break
row, col = self._get_diagnostics_device_row_col()
self.system_diagnostics_dialog.set_selected_hide(row, col)
def _handle_diagnostics_connect_success(self, device_name:str, delay:int):
def _handle_diagnostics_device_connect_success(self, device_name:str, delay:int):
"""处理系统诊断弹窗: 设备连接成功 (设备检测正常)"""
row, col = self.get_diagnostics_row_col()
row, col = self._get_diagnostics_device_row_col()
self.system_diagnostics_dialog.set_selected_device(row, col, device_name)
self.system_diagnostics_dialog.set_ms_value(row, col, delay)
if delay <= self.device_monitor.warning_delay:
self.system_diagnostics_dialog.set_circle_status(row, col, "normal")
else:
self.system_diagnostics_dialog.set_circle_status(row, col, "warning")
# 显示检测组件
self.system_diagnostics_dialog.set_selected_show(row, col)
def _handle_diagnostics_connect_failed(self, device_name:str):
def _handle_diagnostics_device_connect_failed(self, device_name:str):
"""处理系统诊断弹窗: 设备检测异常"""
row, col = self.get_diagnostics_row_col()
row, col = self._get_diagnostics_device_row_col()
self.system_diagnostics_dialog.set_selected_device(row, col, device_name)
self.system_diagnostics_dialog.set_ms_value(row, col, -1)
self.system_diagnostics_dialog.set_circle_status(row, col, "error")
# 显示检测组件
self.system_diagnostics_dialog.set_selected_show(row, col)
def _get_diagnostics_service_row_col(self):
"""获取系统诊断弹窗中 服务检测的 行号 和 列号"""
service_row = self.diagnostics_service_row
service_col = self.diagnostics_service_col
self.diagnostics_service_col += 1
if self.diagnostics_service_col == self.system_diagnostics_dialog.max_col: # 右边两列是 服务检测
self.diagnostics_service_row += 1
self.diagnostics_service_col = 2
if self.diagnostics_service_row == self.system_diagnostics_dialog.max_row:
self.diagnostics_service_row = 0
return service_row, service_col
def _handle_diagnostics_service_normal(self, service_name:str, delay:int):
"""处理系统诊断弹窗: 服务检测正常"""
row, col = self._get_diagnostics_service_row_col()
self.system_diagnostics_dialog.set_selected_service(row, col, service_name)
self.system_diagnostics_dialog.set_ms_value(row, col, delay)
if delay <= self.device_monitor.warning_delay:
self.system_diagnostics_dialog.set_circle_status(row, col, "normal")
else:
self.system_diagnostics_dialog.set_circle_status(row, col, "warning")
# 显示检测组件
self.system_diagnostics_dialog.set_selected_show(row, col)
def _handle_diagnostics_service_error(self, service_name:str):
"""处理系统诊断弹窗: 服务检测异常"""
row, col = self._get_diagnostics_service_row_col()
self.system_diagnostics_dialog.set_selected_service(row, col, service_name)
self.system_diagnostics_dialog.set_ms_value(row, col, -1)
self.system_diagnostics_dialog.set_circle_status(row, col, "error")
# 显示检测组件
self.system_diagnostics_dialog.set_selected_show(row, col)
def _reset_diagnostics_service(self):
"""重置系统诊断弹窗中 服务检测"""
# 0、隐藏多余的服务检测框
# 1、设备检测的行号 self.diagnostics_service_row 重置为0 self.diagnostics_service_col 重置为2
while True:
if self.diagnostics_service_row == 0 and self.diagnostics_service_col == 2:
break
row, col = self._get_diagnostics_service_row_col()
self.system_diagnostics_dialog.set_selected_hide(row, col)
# ================== 系统状态消息列表: 显示系统消息 ===================
def toggle_system_status_msg_list(self):

View File

@ -2,10 +2,13 @@ import configparser
from typing import Dict, Union
from icmplib import ping, ICMPLibError
from PySide6.QtCore import QThread, Signal
import socket
class DeviceMonitorThread(QThread):
class MonitorThread(QThread):
connect_success = Signal(str, int) # 成功信号:设备名称(str) + 延迟毫秒数(int)
connect_failed = Signal(str) # 失败信号:设备名称(str)
service_normal = Signal(str, int) # 服务正常信号: 服务名称(str) + 延迟毫秒数(int)
service_error = Signal(str) # 服务异常信号:服务名称(str)
state_result = Signal(str, int) # 全局状态(str: normal/warning/error) + 异常设备数量(int)
check_finished = Signal() # 本轮检测结束
@ -13,12 +16,12 @@ class DeviceMonitorThread(QThread):
super().__init__(parent)
# 初始化你的原有配置参数,完全不变
self.config_path = config_path
self.ping_timeout_ms = 2000
self.ping_timeout_ms = 1000
self.ping_count = 2
self.check_interval = 10 # 默认检测间隔10秒
self.warning_delay = 30 # 默认的警告的网络延迟(单位: ms)
self.is_stop = False # 线程停止标志位
self.force_check = False # 立即检测标志
self.force_check = False # 立即检测标志
def _ping_device(self, ip: str, timeout_ms: int = 2000) -> Union[int, None]:
"""设备连接状态检测"""
@ -42,7 +45,8 @@ class DeviceMonitorThread(QThread):
def _read_device_config(self) -> Dict[str, str]:
"""读取ini配置文件"""
device_dict = {}
device_dict = {} # 存储设备名和设备中的ip
port_dict = {} # 存储服务名和服务的端口号
config = configparser.ConfigParser()
try:
config.read(self.config_path, encoding="utf-8")
@ -50,15 +54,37 @@ class DeviceMonitorThread(QThread):
if "ip" in config[section]:
device_ip = config[section]["ip"].strip()
device_dict[section] = device_ip
if "port" in config[section]:
service_port = config[section]["port"].strip()
port_dict[section] = int(service_port)
except ValueError:
print(f"配置文件[{self.config_path}]: [{section}] 下的端口值[{service_port}]格式错误")
except FileNotFoundError:
print(f"配置文件[{self.config_path}]不存在,请检查路径!")
except Exception as e:
print(f"读取配置文件失败: {str(e)}")
return device_dict
return device_dict, port_dict
def check_service_status(self, ip: str, port: int, timeout: float = 1.0) -> bool:
"""
检测指定IP的指定端口的服务是否正常
:param ip: 目标IP地址
:param port: 目标端口号
:param timeout: 超时时间
:return: True=服务正常, False=服务异常
"""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(timeout)
result = sock.connect_ex((ip, port)) # 0=连接成功,其他=失败
sock.close()
return result == 0
except Exception as e:
return False
def run(self) -> None:
"""网络设备检测"""
print("设备检测线程已启动")
print("设备/服务检测线程已启动")
self.is_stop = False # 重置停止标志位
while True:
# 线程退出
@ -66,26 +92,39 @@ class DeviceMonitorThread(QThread):
break
# 批量检测所有设备
device_dict = self._read_device_config()
check_result = {} # 所有设备的检测结果
device_dict, port_dict = self._read_device_config()
check_result = {} # 所有的检测结果
if not device_dict:
print("设备检测线程: 未读取到任何设备配置!")
print("设备/服务检测线程: 未读取到任何配置或配置异常")
else:
for device_name, device_ip in device_dict.items():
if self.is_stop:
break
delay_ms = self._ping_device(device_ip, self.ping_timeout_ms)
if delay_ms is not None:
self.connect_success.emit(device_name, delay_ms) # 发送成功信号
else:
self.connect_failed.emit(device_name) # 发送失败信号
if device_name not in port_dict: # 只是设备而不是服务
if delay_ms is not None:
self.connect_success.emit(device_name, delay_ms) # 发送成功信号
else:
self.connect_failed.emit(device_name) # 发送失败信号
else: # 服务
service_name = device_name # 此时,设备名就是服务名
if delay_ms is None:
self.service_error.emit(service_name)
else:
status = self.check_service_status(device_ip, port_dict[device_name])
if status: # 服务正常
self.service_normal.emit(service_name, delay_ms)
else: # 服务异常
self.service_error.emit(service_name)
delay_ms = None # 标志服务异常
check_result[device_name] = delay_ms
# 本轮检测完成
self.check_finished.emit()
# 设备状态统计
self._calc_device_state(check_result)
# 状态统计
self._calc_state(check_result)
# 等待指定间隔后,继续下一次检测
@ -101,10 +140,10 @@ class DeviceMonitorThread(QThread):
break
self.msleep(sleep_slice_ms)
# ============ 设备状态统计 ============
def _calc_device_state(self, check_result:dict):
offline_count = 0 # 离线设备
delay_warn_count = 0# 延迟超30ms的设备数
# ============ 状态统计 ============
def _calc_state(self, check_result:dict):
offline_count = 0 # 离线(异常)
delay_warn_count = 0# 延迟超30ms的设备/服务
# 遍历结果统计
for delay in check_result.values():
if delay is None:
@ -113,13 +152,13 @@ class DeviceMonitorThread(QThread):
delay_warn_count += 1
# 按优先级判定全局状态
if offline_count > 0:
# 设备异常 → error + 离线(异常)数量
# 设备/服务异常 → error + 离线(异常)数量
self.state_result.emit("error", offline_count)
elif delay_warn_count > 0:
# 全部在线但有延迟超标 → warning + 延迟超标数量
# 全部正常但有延迟超标 → warning + 延迟超标数量
self.state_result.emit("warning", delay_warn_count)
else:
# 全部在线且延迟都<30ms → normal + 0
# 全部正常且延迟都<30ms → normal + 0
self.state_result.emit("normal", 0)
# ============ 修改检测间隔 ============
@ -140,9 +179,9 @@ class DeviceMonitorThread(QThread):
def stop_thread(self):
self.is_stop = True
self.wait()
print("设备检测线程已退出")
print("设备/服务检测线程已退出")
# ============ 立即检测============
def force_immediate_check(self):
"""马上执行新一轮设备检测"""
"""马上执行新一轮检测"""
self.force_check = True

View File

@ -173,13 +173,13 @@ class CustomDropdown(QWidget):
for i in range(self.list_widget.count()):
self.list_widget.item(i).setFont(font)
# 获取当前选中的设备名
def get_selected_device(self):
# 获取当前选中的检测框的名称
def get_selected_name(self):
return self.result_label.text()
# 设置选中的设备名
def set_selected_device(self, device_name:str):
self.result_label.setText(device_name)
# 设置当前选中的检测框的名称
def set_selected_name(self, name:str):
self.result_label.setText(name)
class SystemDiagnosticsDialog(QDialog):
def __init__(self, parent=None):
@ -204,8 +204,9 @@ class SystemDiagnosticsDialog(QDialog):
self.setFixedSize(self.bg_pixmap.size())
# 网格布局8行4列小框
grid_layout = QGridLayout(self)
grid_layout.setContentsMargins(24, 28, 20, 24)
self.grid_layout = QGridLayout(self)
self.grid_layout.setContentsMargins(28, 28, 20, 24)
self.grid_layout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
# 图片路径(替换为实际路径)
box_image_path = ImagePaths.SYSTEM_DIAGNOSTICS_BOX
@ -225,6 +226,7 @@ class SystemDiagnosticsDialog(QDialog):
for col in range(self.max_col):
box_container = QWidget()
box_container.setObjectName(f"box_{row}_{col}")
box_container.setFixedSize(156, 50)
box_container.setStyleSheet(
f"""
background-image: url("{box_image_path}");
@ -247,7 +249,7 @@ class SystemDiagnosticsDialog(QDialog):
# ========== 自定义下拉框(支持获取设备名) ==========
led_dropdown = CustomDropdown(
options=["LED1", "LED2", "LED3"], arrow_img_path=dropdown_arrow_path
options=["----", "LED1", "LED2", "LED3"], arrow_img_path=dropdown_arrow_path
)
# ========== 秒数输入框(获取毫秒值) ==========
@ -290,7 +292,10 @@ class SystemDiagnosticsDialog(QDialog):
# box_layout.addItem(spacer3)
box_layout.addWidget(ms_container)
grid_layout.addWidget(box_container, row, col)
self.grid_layout.addWidget(box_container, row, col)
# 初始状态为隐藏
box_container.hide()
def paintEvent(self, event):
"""重写绘制事件,手动在透明背景上绘制图片"""
@ -318,7 +323,7 @@ class SystemDiagnosticsDialog(QDialog):
"""获取指定行列的选中设备名, 行号和列号都从0开始"""
box = self.findChild(QWidget, f"box_{row}_{col}")
if box and hasattr(box, "dropdown"):
return box.dropdown.get_selected_device()
return box.dropdown.get_selected_name()
return None
# ========== 对外接口:设置选中的设备名 ==========
@ -326,7 +331,14 @@ class SystemDiagnosticsDialog(QDialog):
"""设置指定行列的选中设备名, 行号和列号都从0开始"""
box = self.findChild(QWidget, f"box_{row}_{col}")
if box and hasattr(box, "dropdown"):
return box.dropdown.set_selected_device(device_name)
box.dropdown.set_selected_name(device_name)
# ========== 对外接口:设置选中的服务名 ==========
def set_selected_service(self, row, col, service_name:str):
"""设置指定行列的选中服务名, 行号和列号都从0开始"""
box = self.findChild(QWidget, f"box_{row}_{col}")
if box and hasattr(box, "dropdown"):
box.dropdown.set_selected_name(service_name)
# ========== 对外接口:获取毫秒值 ==========
def get_ms_value(self, row, col):
@ -351,6 +363,20 @@ class SystemDiagnosticsDialog(QDialog):
if box and hasattr(box, "ms_edit"):
box.ms_edit.setText(f"{ms}ms")
# ========== 对外接口:设置显示检测组件 ==========
def set_selected_show(self, row, col):
"""设置指定行列的检测组件显示"""
box = self.findChild(QWidget, f"box_{row}_{col}")
if box and box.isHidden():
box.show()
self.grid_layout.update()
# ========== 对外接口:设置隐藏检测组件 ==========
def set_selected_hide(self, row, col):
"""设置指定行列的检测组件隐藏"""
box = self.findChild(QWidget, f"box_{row}_{col}")
if box and box.isVisible():
box.hide()
if __name__ == "__main__":
app = QApplication(sys.argv)