|
|
#!/usr/bin/env python3
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
"""
|
|
|
控制系统管理界面
|
|
|
提供规则配置和监控功能
|
|
|
"""
|
|
|
|
|
|
from PyQt5.QtCore import Qt, QTimer, pyqtSignal
|
|
|
from PyQt5.QtGui import QFont
|
|
|
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QGroupBox,
|
|
|
QTableWidget, QTableWidgetItem, QPushButton,
|
|
|
QLabel, QLineEdit, QComboBox, QSpinBox,
|
|
|
QCheckBox, QTextEdit, QSplitter, QHeaderView,
|
|
|
QMessageBox, QDialog, QFormLayout, QDialogButtonBox,
|
|
|
QDoubleSpinBox)
|
|
|
|
|
|
from control.ControlManager import getControlManager
|
|
|
from control.RuleEngine import OperatorType, ActionType
|
|
|
from utils import Globals
|
|
|
import os
|
|
|
|
|
|
|
|
|
class RuleConfigDialog(QDialog):
|
|
|
"""规则配置对话框"""
|
|
|
|
|
|
def __init__(self, parent=None, ruleData=None):
|
|
|
super().__init__(parent)
|
|
|
self.ruleData = ruleData
|
|
|
self.isEditMode = ruleData is not None
|
|
|
|
|
|
if self.isEditMode:
|
|
|
self.setWindowTitle("编辑规则")
|
|
|
else:
|
|
|
self.setWindowTitle("新建规则")
|
|
|
|
|
|
self.setMinimumSize(600, 500)
|
|
|
self.setupUI()
|
|
|
|
|
|
# 如果是编辑模式,填充现有数据
|
|
|
if self.isEditMode:
|
|
|
self.loadRuleData()
|
|
|
|
|
|
def setupUI(self):
|
|
|
"""设置界面"""
|
|
|
layout = QVBoxLayout(self)
|
|
|
|
|
|
# 基本信息
|
|
|
basicGroup = QGroupBox("基本信息")
|
|
|
basicLayout = QFormLayout(basicGroup)
|
|
|
|
|
|
self.nameEdit = QLineEdit()
|
|
|
self.nameEdit.setPlaceholderText("输入规则名称")
|
|
|
self.descEdit = QLineEdit()
|
|
|
self.descEdit.setPlaceholderText("输入规则描述(可选)")
|
|
|
|
|
|
# 启用状态
|
|
|
self.enabledCheckBox = QCheckBox("启用规则")
|
|
|
self.enabledCheckBox.setChecked(True)
|
|
|
|
|
|
basicLayout.addRow("规则名称:", self.nameEdit)
|
|
|
basicLayout.addRow("规则描述:", self.descEdit)
|
|
|
basicLayout.addRow("", self.enabledCheckBox)
|
|
|
|
|
|
layout.addWidget(basicGroup)
|
|
|
|
|
|
# 条件设置
|
|
|
conditionGroup = QGroupBox("触发条件")
|
|
|
conditionLayout = QFormLayout(conditionGroup)
|
|
|
|
|
|
self.variableEdit = QLineEdit()
|
|
|
self.variableEdit.setPlaceholderText("输入变量名(如:温度传感器_01)")
|
|
|
|
|
|
self.operatorCombo = QComboBox()
|
|
|
self.operatorCombo.addItems([op.value for op in OperatorType])
|
|
|
|
|
|
self.thresholdSpin = QDoubleSpinBox()
|
|
|
self.thresholdSpin.setRange(-999999, 999999)
|
|
|
self.thresholdSpin.setDecimals(3)
|
|
|
self.thresholdSpin.setValue(0.0)
|
|
|
|
|
|
conditionLayout.addRow("监控变量:", self.variableEdit)
|
|
|
conditionLayout.addRow("比较操作:", self.operatorCombo)
|
|
|
conditionLayout.addRow("阈值:", self.thresholdSpin)
|
|
|
|
|
|
layout.addWidget(conditionGroup)
|
|
|
|
|
|
# 动作设置
|
|
|
actionGroup = QGroupBox("执行动作")
|
|
|
actionLayout = QFormLayout(actionGroup)
|
|
|
|
|
|
self.actionTypeCombo = QComboBox()
|
|
|
self.actionTypeCombo.addItems([act.value for act in ActionType])
|
|
|
self.actionTypeCombo.currentTextChanged.connect(self.onActionTypeChanged)
|
|
|
|
|
|
self.targetEdit = QLineEdit()
|
|
|
self.valueEdit = QLineEdit()
|
|
|
|
|
|
self.delaySpin = QSpinBox()
|
|
|
self.delaySpin.setRange(0, 60000)
|
|
|
self.delaySpin.setSuffix(" ms")
|
|
|
self.delaySpin.setValue(0)
|
|
|
|
|
|
actionLayout.addRow("动作类型:", self.actionTypeCombo)
|
|
|
actionLayout.addRow("目标:", self.targetEdit)
|
|
|
actionLayout.addRow("值:", self.valueEdit)
|
|
|
actionLayout.addRow("延迟:", self.delaySpin)
|
|
|
|
|
|
layout.addWidget(actionGroup)
|
|
|
|
|
|
# 高级设置
|
|
|
advancedGroup = QGroupBox("高级设置")
|
|
|
advancedLayout = QFormLayout(advancedGroup)
|
|
|
|
|
|
self.cooldownSpin = QSpinBox()
|
|
|
self.cooldownSpin.setRange(0, 300000) # 最大5分钟
|
|
|
self.cooldownSpin.setSuffix(" ms")
|
|
|
self.cooldownSpin.setValue(0)
|
|
|
self.cooldownSpin.setToolTip("规则执行后的冷却时间,防止频繁触发")
|
|
|
|
|
|
self.logicCombo = QComboBox()
|
|
|
self.logicCombo.addItems(["and", "or"])
|
|
|
self.logicCombo.setToolTip("多条件时的逻辑关系(当前版本仅支持单条件)")
|
|
|
self.logicCombo.setEnabled(False) # 当前版本禁用
|
|
|
|
|
|
advancedLayout.addRow("冷却期:", self.cooldownSpin)
|
|
|
advancedLayout.addRow("逻辑操作:", self.logicCombo)
|
|
|
|
|
|
layout.addWidget(advancedGroup)
|
|
|
|
|
|
# 按钮
|
|
|
buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
|
|
if self.isEditMode:
|
|
|
buttonBox.button(QDialogButtonBox.Ok).setText("保存")
|
|
|
else:
|
|
|
buttonBox.button(QDialogButtonBox.Ok).setText("创建")
|
|
|
|
|
|
buttonBox.accepted.connect(self.validateAndAccept)
|
|
|
buttonBox.rejected.connect(self.reject)
|
|
|
layout.addWidget(buttonBox)
|
|
|
|
|
|
# 初始化界面状态
|
|
|
self.onActionTypeChanged(self.actionTypeCombo.currentText())
|
|
|
|
|
|
def loadRuleData(self):
|
|
|
"""加载规则数据到界面"""
|
|
|
if not self.ruleData:
|
|
|
return
|
|
|
|
|
|
try:
|
|
|
# 基本信息
|
|
|
self.nameEdit.setText(self.ruleData.get('name', ''))
|
|
|
self.descEdit.setText(self.ruleData.get('description', ''))
|
|
|
self.enabledCheckBox.setChecked(self.ruleData.get('enabled', True))
|
|
|
|
|
|
# 条件信息(取第一个条件)
|
|
|
conditions = self.ruleData.get('conditions', [])
|
|
|
if conditions:
|
|
|
condition = conditions[0]
|
|
|
self.variableEdit.setText(condition.get('variable', ''))
|
|
|
|
|
|
# 设置操作符
|
|
|
operator = condition.get('operator', '==')
|
|
|
index = self.operatorCombo.findText(operator)
|
|
|
if index >= 0:
|
|
|
self.operatorCombo.setCurrentIndex(index)
|
|
|
|
|
|
self.thresholdSpin.setValue(float(condition.get('value', 0)))
|
|
|
|
|
|
# 动作信息(取第一个动作)
|
|
|
actions = self.ruleData.get('actions', [])
|
|
|
if actions:
|
|
|
action = actions[0]
|
|
|
actionType = action.get('actionType', 'set_variable')
|
|
|
|
|
|
# 设置动作类型
|
|
|
index = self.actionTypeCombo.findText(actionType)
|
|
|
if index >= 0:
|
|
|
self.actionTypeCombo.setCurrentIndex(index)
|
|
|
|
|
|
self.targetEdit.setText(action.get('target', ''))
|
|
|
self.valueEdit.setText(str(action.get('value', '')))
|
|
|
self.delaySpin.setValue(int(action.get('delay', 0)))
|
|
|
|
|
|
# 高级设置
|
|
|
self.cooldownSpin.setValue(int(self.ruleData.get('cooldownPeriod', 0)))
|
|
|
|
|
|
logicOp = self.ruleData.get('logicOperator', 'and')
|
|
|
index = self.logicCombo.findText(logicOp)
|
|
|
if index >= 0:
|
|
|
self.logicCombo.setCurrentIndex(index)
|
|
|
|
|
|
except Exception as e:
|
|
|
print(f"加载规则数据失败: {e}")
|
|
|
|
|
|
def validateAndAccept(self):
|
|
|
"""验证输入并接受"""
|
|
|
# 验证必填字段
|
|
|
if not self.nameEdit.text().strip():
|
|
|
QMessageBox.warning(self, "验证错误", "规则名称不能为空")
|
|
|
self.nameEdit.setFocus()
|
|
|
return
|
|
|
|
|
|
if not self.variableEdit.text().strip():
|
|
|
QMessageBox.warning(self, "验证错误", "监控变量不能为空")
|
|
|
self.variableEdit.setFocus()
|
|
|
return
|
|
|
|
|
|
if not self.targetEdit.text().strip():
|
|
|
QMessageBox.warning(self, "验证错误", "动作目标不能为空")
|
|
|
self.targetEdit.setFocus()
|
|
|
return
|
|
|
|
|
|
if not self.valueEdit.text().strip():
|
|
|
QMessageBox.warning(self, "验证错误", "动作值不能为空")
|
|
|
self.valueEdit.setFocus()
|
|
|
return
|
|
|
|
|
|
# 验证通过,接受对话框
|
|
|
self.accept()
|
|
|
|
|
|
def onActionTypeChanged(self, actionType):
|
|
|
"""动作类型变化时更新界面"""
|
|
|
if actionType == "set_variable":
|
|
|
self.targetEdit.setPlaceholderText("目标变量名(如:阀门_01)")
|
|
|
self.valueEdit.setPlaceholderText("设置值(如:true, false, 100)")
|
|
|
elif actionType == "send_alarm":
|
|
|
self.targetEdit.setPlaceholderText("报警源(如:温度传感器_01)")
|
|
|
self.valueEdit.setPlaceholderText("报警消息内容")
|
|
|
elif actionType == "log_message":
|
|
|
self.targetEdit.setPlaceholderText("日志类别(如:system)")
|
|
|
self.valueEdit.setPlaceholderText("日志消息内容")
|
|
|
elif actionType == "execute_script":
|
|
|
self.targetEdit.setPlaceholderText("脚本路径或命令")
|
|
|
self.valueEdit.setPlaceholderText("脚本参数(可选)")
|
|
|
else:
|
|
|
self.targetEdit.setPlaceholderText("目标")
|
|
|
self.valueEdit.setPlaceholderText("值")
|
|
|
|
|
|
def getRuleData(self):
|
|
|
"""获取规则数据"""
|
|
|
# 处理值的类型转换
|
|
|
value = self.valueEdit.text().strip()
|
|
|
actionType = self.actionTypeCombo.currentText()
|
|
|
|
|
|
# 如果是设置变量动作,尝试转换为数值
|
|
|
if actionType == "set_variable":
|
|
|
try:
|
|
|
# 尝试转换为数值
|
|
|
if '.' in value:
|
|
|
value = float(value)
|
|
|
else:
|
|
|
value = int(value)
|
|
|
except ValueError:
|
|
|
# 如果转换失败,检查是否是布尔值
|
|
|
if value.lower() in ['true', 'false']:
|
|
|
value = value.lower() == 'true'
|
|
|
# 否则保持字符串
|
|
|
|
|
|
return {
|
|
|
'name': self.nameEdit.text().strip(),
|
|
|
'description': self.descEdit.text().strip(),
|
|
|
'enabled': self.enabledCheckBox.isChecked(),
|
|
|
'variable': self.variableEdit.text().strip(),
|
|
|
'operator': self.operatorCombo.currentText(),
|
|
|
'threshold': self.thresholdSpin.value(),
|
|
|
'actionType': actionType,
|
|
|
'target': self.targetEdit.text().strip(),
|
|
|
'value': value,
|
|
|
'delay': self.delaySpin.value(),
|
|
|
'cooldownPeriod': self.cooldownSpin.value(),
|
|
|
'logicOperator': self.logicCombo.currentText()
|
|
|
}
|
|
|
|
|
|
|
|
|
class ControlWidget(QWidget):
|
|
|
"""控制系统管理主界面"""
|
|
|
|
|
|
def __init__(self):
|
|
|
super().__init__()
|
|
|
self.controlManager = getControlManager()
|
|
|
|
|
|
# 设置对象名称用于QSS样式
|
|
|
self.setObjectName("controlWidget")
|
|
|
|
|
|
# 加载样式
|
|
|
self.loadStyleSheet()
|
|
|
|
|
|
self.setupUI()
|
|
|
self.setupTimer()
|
|
|
|
|
|
# 初始化控制系统
|
|
|
self.controlManager.initialize()
|
|
|
|
|
|
# 刷新界面
|
|
|
self.refreshRuleTable()
|
|
|
self.refreshStatus()
|
|
|
|
|
|
def loadStyleSheet(self):
|
|
|
"""加载QSS样式文件"""
|
|
|
try:
|
|
|
qss_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))),
|
|
|
"Static", "ControlWidget.qss")
|
|
|
if os.path.exists(qss_path):
|
|
|
with open(qss_path, 'r', encoding='utf-8') as f:
|
|
|
self.setStyleSheet(f.read())
|
|
|
print("控制界面样式加载成功")
|
|
|
else:
|
|
|
print(f"样式文件不存在: {qss_path}")
|
|
|
except Exception as e:
|
|
|
print(f"加载样式文件失败: {e}")
|
|
|
|
|
|
def setupUI(self):
|
|
|
"""设置界面"""
|
|
|
layout = QVBoxLayout(self)
|
|
|
layout.setSpacing(8)
|
|
|
layout.setContentsMargins(8, 8, 8, 8)
|
|
|
|
|
|
# 创建分割器
|
|
|
splitter = QSplitter(Qt.Vertical)
|
|
|
splitter.setHandleWidth(4)
|
|
|
|
|
|
# 上半部分:规则管理
|
|
|
ruleWidget = self.createRuleManageWidget()
|
|
|
splitter.addWidget(ruleWidget)
|
|
|
|
|
|
# 下半部分:状态监控
|
|
|
statusWidget = self.createStatusWidget()
|
|
|
splitter.addWidget(statusWidget)
|
|
|
|
|
|
# 设置分割比例 - 针对1080P优化
|
|
|
splitter.setStretchFactor(0, 4) # 规则管理占更多空间
|
|
|
splitter.setStretchFactor(1, 1) # 状态监控占较少空间
|
|
|
splitter.setSizes([600, 200]) # 设置初始大小
|
|
|
|
|
|
layout.addWidget(splitter)
|
|
|
|
|
|
def createRuleManageWidget(self):
|
|
|
"""创建规则管理组件"""
|
|
|
widget = QGroupBox("规则管理")
|
|
|
layout = QVBoxLayout(widget)
|
|
|
|
|
|
# 工具栏 - 分两行布局以适应1080P
|
|
|
toolWidget = QWidget()
|
|
|
toolMainLayout = QVBoxLayout(toolWidget)
|
|
|
toolMainLayout.setSpacing(4)
|
|
|
toolMainLayout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
|
|
# 第一行:主要操作按钮
|
|
|
toolLayout1 = QHBoxLayout()
|
|
|
toolLayout1.setSpacing(6)
|
|
|
|
|
|
self.addRuleBtn = QPushButton("简单规则")
|
|
|
self.addRuleBtn.setObjectName("addRuleBtn")
|
|
|
self.addRuleBtn.setToolTip("创建单条件规则")
|
|
|
self.addRuleBtn.setFixedSize(80, 32)
|
|
|
self.addRuleBtn.clicked.connect(self.addRule)
|
|
|
|
|
|
self.addMultiRuleBtn = QPushButton("多条件规则")
|
|
|
self.addMultiRuleBtn.setObjectName("addMultiRuleBtn")
|
|
|
self.addMultiRuleBtn.setToolTip("创建多条件AND/OR规则")
|
|
|
self.addMultiRuleBtn.setFixedSize(90, 32)
|
|
|
self.addMultiRuleBtn.clicked.connect(self.addMultiConditionRule)
|
|
|
|
|
|
self.editRuleBtn = QPushButton("编辑")
|
|
|
self.editRuleBtn.setToolTip("编辑选中的规则")
|
|
|
self.editRuleBtn.setFixedSize(60, 32)
|
|
|
self.editRuleBtn.clicked.connect(self.editRule)
|
|
|
|
|
|
self.deleteRuleBtn = QPushButton("删除")
|
|
|
self.deleteRuleBtn.setObjectName("deleteRuleBtn")
|
|
|
self.deleteRuleBtn.setToolTip("删除选中的规则")
|
|
|
self.deleteRuleBtn.setFixedSize(60, 32)
|
|
|
self.deleteRuleBtn.clicked.connect(self.deleteRule)
|
|
|
|
|
|
self.testRuleBtn = QPushButton("测试")
|
|
|
self.testRuleBtn.setObjectName("testRuleBtn")
|
|
|
self.testRuleBtn.setToolTip("测试规则条件和显示变量值")
|
|
|
self.testRuleBtn.setFixedSize(60, 32)
|
|
|
self.testRuleBtn.clicked.connect(self.testRule)
|
|
|
|
|
|
toolLayout1.addWidget(self.addRuleBtn)
|
|
|
toolLayout1.addWidget(self.addMultiRuleBtn)
|
|
|
toolLayout1.addWidget(self.editRuleBtn)
|
|
|
toolLayout1.addWidget(self.deleteRuleBtn)
|
|
|
toolLayout1.addWidget(self.testRuleBtn)
|
|
|
toolLayout1.addStretch()
|
|
|
|
|
|
# 第二行:辅助操作按钮
|
|
|
toolLayout2 = QHBoxLayout()
|
|
|
toolLayout2.setSpacing(6)
|
|
|
|
|
|
self.setVarBtn = QPushButton("设置变量")
|
|
|
self.setVarBtn.setToolTip("手动设置变量值用于测试")
|
|
|
self.setVarBtn.setFixedSize(80, 28)
|
|
|
self.setVarBtn.clicked.connect(self.setVariableValue)
|
|
|
|
|
|
self.copyRuleBtn = QPushButton("复制")
|
|
|
self.copyRuleBtn.setToolTip("复制选中的规则")
|
|
|
self.copyRuleBtn.setFixedSize(60, 28)
|
|
|
self.copyRuleBtn.clicked.connect(self.copyRule)
|
|
|
|
|
|
# 批量操作按钮
|
|
|
self.enableAllBtn = QPushButton("全部启用")
|
|
|
self.enableAllBtn.setFixedSize(80, 28)
|
|
|
self.enableAllBtn.clicked.connect(self.enableAllRules)
|
|
|
|
|
|
self.disableAllBtn = QPushButton("全部禁用")
|
|
|
self.disableAllBtn.setFixedSize(80, 28)
|
|
|
self.disableAllBtn.clicked.connect(self.disableAllRules)
|
|
|
|
|
|
toolLayout2.addWidget(self.setVarBtn)
|
|
|
toolLayout2.addWidget(self.copyRuleBtn)
|
|
|
toolLayout2.addWidget(self.enableAllBtn)
|
|
|
toolLayout2.addWidget(self.disableAllBtn)
|
|
|
toolLayout2.addStretch()
|
|
|
|
|
|
toolMainLayout.addLayout(toolLayout1)
|
|
|
toolMainLayout.addLayout(toolLayout2)
|
|
|
|
|
|
layout.addWidget(toolWidget)
|
|
|
|
|
|
# 搜索框
|
|
|
searchLayout = QHBoxLayout()
|
|
|
searchLabel = QLabel("搜索规则:")
|
|
|
self.searchEdit = QLineEdit()
|
|
|
self.searchEdit.setObjectName("searchEdit")
|
|
|
self.searchEdit.setPlaceholderText("输入规则名称进行搜索...")
|
|
|
self.searchEdit.textChanged.connect(self.searchRules)
|
|
|
|
|
|
searchLayout.addWidget(searchLabel)
|
|
|
searchLayout.addWidget(self.searchEdit)
|
|
|
searchLayout.addStretch()
|
|
|
|
|
|
layout.addLayout(searchLayout)
|
|
|
|
|
|
# 规则表格
|
|
|
self.ruleTable = QTableWidget()
|
|
|
self.ruleTable.setColumnCount(5)
|
|
|
self.ruleTable.setHorizontalHeaderLabels(["规则名称", "描述", "状态", "最后执行", "操作"])
|
|
|
|
|
|
# 设置表格属性
|
|
|
header = self.ruleTable.horizontalHeader()
|
|
|
header.setSectionResizeMode(0, QHeaderView.Interactive) # 规则名称
|
|
|
header.setSectionResizeMode(1, QHeaderView.Stretch) # 描述
|
|
|
header.setSectionResizeMode(2, QHeaderView.Fixed) # 状态
|
|
|
header.setSectionResizeMode(3, QHeaderView.Fixed) # 最后执行
|
|
|
header.setSectionResizeMode(4, QHeaderView.Fixed) # 操作
|
|
|
|
|
|
# 设置固定列宽
|
|
|
header.resizeSection(0, 180) # 规则名称
|
|
|
header.resizeSection(2, 80) # 状态
|
|
|
header.resizeSection(3, 140) # 最后执行
|
|
|
header.resizeSection(4, 80) # 操作
|
|
|
|
|
|
self.ruleTable.setSelectionBehavior(QTableWidget.SelectRows)
|
|
|
self.ruleTable.setAlternatingRowColors(True)
|
|
|
self.ruleTable.setSortingEnabled(True)
|
|
|
self.ruleTable.setRowHeight(0, 32) # 设置行高
|
|
|
|
|
|
layout.addWidget(self.ruleTable)
|
|
|
|
|
|
return widget
|
|
|
|
|
|
def createStatusWidget(self):
|
|
|
"""创建状态监控组件"""
|
|
|
widget = QGroupBox("系统状态")
|
|
|
layout = QHBoxLayout(widget)
|
|
|
layout.setSpacing(8)
|
|
|
layout.setContentsMargins(8, 8, 8, 8)
|
|
|
|
|
|
# 左侧:状态信息 - 使用网格布局更紧凑
|
|
|
statusWidget = QWidget()
|
|
|
statusLayout = QVBoxLayout(statusWidget)
|
|
|
statusLayout.setSpacing(4)
|
|
|
|
|
|
# 状态信息行
|
|
|
statusInfoLayout = QHBoxLayout()
|
|
|
statusInfoLayout.setSpacing(20)
|
|
|
|
|
|
self.statusLabel = QLabel("系统状态: 未知")
|
|
|
self.statusLabel.setObjectName("statusLabel")
|
|
|
self.statusLabel.setStyleSheet("QLabel { font-weight: bold; color: #28A745; }")
|
|
|
|
|
|
self.rulesCountLabel = QLabel("规则: 0")
|
|
|
self.variablesCountLabel = QLabel("变量: 0")
|
|
|
self.lastUpdateLabel = QLabel("更新: --")
|
|
|
|
|
|
statusInfoLayout.addWidget(self.statusLabel)
|
|
|
statusInfoLayout.addWidget(self.rulesCountLabel)
|
|
|
statusInfoLayout.addWidget(self.variablesCountLabel)
|
|
|
statusInfoLayout.addWidget(self.lastUpdateLabel)
|
|
|
statusInfoLayout.addStretch()
|
|
|
|
|
|
statusLayout.addLayout(statusInfoLayout)
|
|
|
|
|
|
# 变量值显示 - 更紧凑
|
|
|
self.variableValuesLabel = QLabel("变量值: --")
|
|
|
self.variableValuesLabel.setWordWrap(True)
|
|
|
self.variableValuesLabel.setMaximumHeight(60)
|
|
|
self.variableValuesLabel.setStyleSheet("""
|
|
|
QLabel {
|
|
|
background-color: #F8F9FA;
|
|
|
border: 1px solid #DEE2E6;
|
|
|
border-radius: 4px;
|
|
|
padding: 6px;
|
|
|
font-size: 11px;
|
|
|
font-family: 'Consolas', 'Monaco', monospace;
|
|
|
}
|
|
|
""")
|
|
|
statusLayout.addWidget(self.variableValuesLabel)
|
|
|
|
|
|
layout.addWidget(statusWidget)
|
|
|
|
|
|
# 右侧:日志显示 - 更紧凑
|
|
|
logWidget = QWidget()
|
|
|
logLayout = QVBoxLayout(logWidget)
|
|
|
logLayout.setSpacing(4)
|
|
|
logLayout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
|
|
logLabel = QLabel("系统日志:")
|
|
|
logLabel.setStyleSheet("QLabel { font-weight: bold; }")
|
|
|
logLayout.addWidget(logLabel)
|
|
|
|
|
|
self.logText = QTextEdit()
|
|
|
self.logText.setMaximumHeight(120)
|
|
|
self.logText.setReadOnly(True)
|
|
|
self.logText.setStyleSheet("""
|
|
|
QTextEdit {
|
|
|
font-size: 10px;
|
|
|
font-family: 'Consolas', 'Monaco', monospace;
|
|
|
background-color: #FAFAFA;
|
|
|
border: 1px solid #E0E0E0;
|
|
|
border-radius: 4px;
|
|
|
padding: 4px;
|
|
|
}
|
|
|
""")
|
|
|
|
|
|
logLayout.addWidget(self.logText)
|
|
|
|
|
|
layout.addWidget(logWidget)
|
|
|
layout.setStretchFactor(statusWidget, 1)
|
|
|
layout.setStretchFactor(logWidget, 2)
|
|
|
|
|
|
return widget
|
|
|
|
|
|
def setupTimer(self):
|
|
|
"""设置定时器"""
|
|
|
self.refreshTimer = QTimer()
|
|
|
self.refreshTimer.timeout.connect(self.refreshStatus)
|
|
|
self.refreshTimer.start(5000) # 5秒刷新一次
|
|
|
|
|
|
def addRule(self):
|
|
|
"""添加规则"""
|
|
|
dialog = RuleConfigDialog(self)
|
|
|
if dialog.exec_() == QDialog.Accepted:
|
|
|
data = dialog.getRuleData()
|
|
|
|
|
|
try:
|
|
|
# 验证输入数据
|
|
|
if not data['name'] or not data['variable']:
|
|
|
QMessageBox.warning(self, "错误", "规则名称和监控变量不能为空")
|
|
|
return
|
|
|
|
|
|
if data['actionType'] == "set_variable":
|
|
|
if not data['target']:
|
|
|
QMessageBox.warning(self, "错误", "目标变量不能为空")
|
|
|
return
|
|
|
|
|
|
ruleId = self.controlManager.createSimpleRule(
|
|
|
data['name'],
|
|
|
data['variable'],
|
|
|
data['operator'],
|
|
|
data['threshold'],
|
|
|
data['target'],
|
|
|
data['value']
|
|
|
)
|
|
|
elif data['actionType'] == "send_alarm":
|
|
|
if not data['value']:
|
|
|
QMessageBox.warning(self, "错误", "报警消息不能为空")
|
|
|
return
|
|
|
|
|
|
ruleId = self.controlManager.createAlarmRule(
|
|
|
data['name'],
|
|
|
data['variable'],
|
|
|
data['operator'],
|
|
|
data['threshold'],
|
|
|
data['value']
|
|
|
)
|
|
|
else:
|
|
|
QMessageBox.warning(self, "错误", f"不支持的动作类型: {data['actionType']}")
|
|
|
return
|
|
|
|
|
|
# 添加变量监控
|
|
|
self.controlManager.addMonitorVariable(data['variable'])
|
|
|
|
|
|
# 保存配置
|
|
|
self.controlManager.saveConfiguration()
|
|
|
|
|
|
self.refreshRuleTable()
|
|
|
self.logMessage(f"已添加规则: {data['name']} (ID: {ruleId})")
|
|
|
|
|
|
except Exception as e:
|
|
|
import traceback
|
|
|
traceback.print_exc()
|
|
|
QMessageBox.warning(self, "错误", f"添加规则失败: {str(e)}")
|
|
|
|
|
|
def addMultiConditionRule(self):
|
|
|
"""添加多条件规则"""
|
|
|
dialog = MultiConditionRuleDialog(self)
|
|
|
if dialog.exec_() == QDialog.Accepted:
|
|
|
try:
|
|
|
ruleData = dialog.getRuleData()
|
|
|
|
|
|
# 使用控制管理器创建多条件规则
|
|
|
ruleId = self.controlManager.createComplexRule(ruleData)
|
|
|
|
|
|
# 保存配置
|
|
|
self.controlManager.saveConfiguration()
|
|
|
|
|
|
self.logMessage(f"成功创建多条件规则: {ruleData['name']} (ID: {ruleId})")
|
|
|
self.refreshRuleTable()
|
|
|
|
|
|
except Exception as e:
|
|
|
import traceback
|
|
|
traceback.print_exc()
|
|
|
QMessageBox.warning(self, "错误", f"创建多条件规则失败: {str(e)}")
|
|
|
self.logMessage(f"创建多条件规则失败: {str(e)}")
|
|
|
|
|
|
def editRule(self):
|
|
|
"""编辑规则"""
|
|
|
currentRow = self.ruleTable.currentRow()
|
|
|
if currentRow >= 0:
|
|
|
try:
|
|
|
rules = self.controlManager.getAllRules()
|
|
|
if currentRow < len(rules):
|
|
|
rule = rules[currentRow]
|
|
|
ruleId = rule['ruleId']
|
|
|
|
|
|
# 创建编辑对话框
|
|
|
dialog = RuleConfigDialog(self, rule)
|
|
|
if dialog.exec_() == QDialog.Accepted:
|
|
|
# 获取编辑后的数据
|
|
|
updatedData = dialog.getRuleData()
|
|
|
|
|
|
# 验证规则名称是否重复(排除当前规则)
|
|
|
existingNames = [r['name'] for r in rules if r['ruleId'] != ruleId]
|
|
|
if updatedData['name'] in existingNames:
|
|
|
QMessageBox.warning(self, "错误", f"规则名称 '{updatedData['name']}' 已存在")
|
|
|
return
|
|
|
|
|
|
# 更新规则
|
|
|
success = self.controlManager.updateRule(ruleId, updatedData)
|
|
|
if success:
|
|
|
# 更新变量监控(如果变量名发生变化)
|
|
|
self.controlManager.addMonitorVariable(updatedData['variable'])
|
|
|
|
|
|
# 保存配置
|
|
|
self.controlManager.saveConfiguration()
|
|
|
|
|
|
self.refreshRuleTable()
|
|
|
self.logMessage(f"已更新规则: {updatedData['name']}")
|
|
|
QMessageBox.information(self, "成功", f"规则 '{updatedData['name']}' 已成功更新")
|
|
|
else:
|
|
|
QMessageBox.warning(self, "错误", "更新规则失败")
|
|
|
else:
|
|
|
QMessageBox.warning(self, "错误", "无法获取规则信息")
|
|
|
except Exception as e:
|
|
|
import traceback
|
|
|
traceback.print_exc()
|
|
|
QMessageBox.warning(self, "错误", f"编辑规则失败: {str(e)}")
|
|
|
|
|
|
def deleteRule(self):
|
|
|
"""删除规则"""
|
|
|
currentRow = self.ruleTable.currentRow()
|
|
|
if currentRow >= 0:
|
|
|
try:
|
|
|
rules = self.controlManager.getAllRules()
|
|
|
if currentRow < len(rules):
|
|
|
rule = rules[currentRow]
|
|
|
ruleName = rule['name']
|
|
|
ruleId = rule['ruleId']
|
|
|
|
|
|
reply = QMessageBox.question(self, "确认", f"确定要删除规则 '{ruleName}' 吗?")
|
|
|
|
|
|
if reply == QMessageBox.Yes:
|
|
|
self.controlManager.deleteRule(ruleId)
|
|
|
|
|
|
# 保存配置
|
|
|
self.controlManager.saveConfiguration()
|
|
|
|
|
|
self.logMessage(f"已删除规则: {ruleName}")
|
|
|
self.refreshRuleTable()
|
|
|
else:
|
|
|
QMessageBox.warning(self, "错误", "无法获取规则信息")
|
|
|
except Exception as e:
|
|
|
QMessageBox.warning(self, "错误", f"删除规则失败: {str(e)}")
|
|
|
|
|
|
def testRule(self):
|
|
|
"""测试规则"""
|
|
|
currentRow = self.ruleTable.currentRow()
|
|
|
if currentRow >= 0:
|
|
|
try:
|
|
|
rules = self.controlManager.getAllRules()
|
|
|
if currentRow < len(rules):
|
|
|
rule = rules[currentRow]
|
|
|
ruleName = rule['name']
|
|
|
ruleId = rule['ruleId']
|
|
|
|
|
|
# 获取规则详细信息
|
|
|
ruleObj = self.controlManager.ruleEngine.getRule(ruleId)
|
|
|
if not ruleObj:
|
|
|
QMessageBox.warning(self, "错误", "无法获取规则对象")
|
|
|
return
|
|
|
|
|
|
# 构建详细信息
|
|
|
detailInfo = f"规则: {ruleName}\n"
|
|
|
detailInfo += f"状态: {'启用' if ruleObj.enabled else '禁用'}\n\n"
|
|
|
|
|
|
# 显示条件信息
|
|
|
if ruleObj.conditions:
|
|
|
detailInfo += "条件检查:\n"
|
|
|
for i, condition in enumerate(ruleObj.conditions):
|
|
|
varName = condition.variable
|
|
|
currentValue = self.controlManager.variableMonitor.getVariableValue(varName)
|
|
|
|
|
|
detailInfo += f" {i+1}. {varName} {condition.operator.value} {condition.value}\n"
|
|
|
detailInfo += f" 当前值: {currentValue}\n"
|
|
|
|
|
|
if currentValue is None:
|
|
|
detailInfo += f" 状态: ❌ 变量值未设置\n"
|
|
|
else:
|
|
|
condResult = condition.evaluate(self.controlManager.variableMonitor)
|
|
|
detailInfo += f" 状态: {'✅ 满足' if condResult else '❌ 不满足'}\n"
|
|
|
|
|
|
# 测试规则条件
|
|
|
result = self.controlManager.testRule(ruleId)
|
|
|
|
|
|
detailInfo += f"\n整体结果: {'✅ 规则会被触发' if result else '❌ 规则不会被触发'}"
|
|
|
|
|
|
if result:
|
|
|
self.logMessage(f"测试规则 '{ruleName}': 条件满足")
|
|
|
QMessageBox.information(self, "测试结果 - 满足条件", detailInfo)
|
|
|
else:
|
|
|
self.logMessage(f"测试规则 '{ruleName}': 条件不满足")
|
|
|
QMessageBox.information(self, "测试结果 - 不满足条件", detailInfo)
|
|
|
else:
|
|
|
QMessageBox.warning(self, "错误", "无法获取规则信息")
|
|
|
except Exception as e:
|
|
|
import traceback
|
|
|
traceback.print_exc()
|
|
|
QMessageBox.warning(self, "错误", f"测试规则失败: {str(e)}")
|
|
|
self.logMessage(f"测试规则失败: {str(e)}")
|
|
|
|
|
|
def setVariableValue(self):
|
|
|
"""设置变量值(用于测试)"""
|
|
|
from PyQt5.QtWidgets import QInputDialog
|
|
|
|
|
|
try:
|
|
|
# 获取所有监控的变量
|
|
|
allVars = self.controlManager.variableMonitor.getAllVariables()
|
|
|
varNames = list(allVars.keys())
|
|
|
|
|
|
if not varNames:
|
|
|
QMessageBox.information(self, "提示", "当前没有监控的变量。\n请先创建规则,系统会自动添加变量监控。")
|
|
|
return
|
|
|
|
|
|
# 选择变量
|
|
|
varName, ok = QInputDialog.getItem(self, "选择变量", "选择要设置值的变量:", varNames, 0, False)
|
|
|
if not ok:
|
|
|
return
|
|
|
|
|
|
# 获取当前值
|
|
|
currentValue = allVars.get(varName, 0)
|
|
|
|
|
|
# 输入新值
|
|
|
newValue, ok = QInputDialog.getDouble(self, "设置变量值",
|
|
|
f"变量: {varName}\n当前值: {currentValue}\n\n请输入新值:",
|
|
|
currentValue, -999999, 999999, 3)
|
|
|
if not ok:
|
|
|
return
|
|
|
|
|
|
# 更新变量值
|
|
|
self.controlManager.variableMonitor.updateVariable(varName, newValue)
|
|
|
|
|
|
# 验证设置是否成功
|
|
|
actualValue = self.controlManager.variableMonitor.getVariableValue(varName)
|
|
|
|
|
|
if actualValue == newValue:
|
|
|
self.logMessage(f"已设置变量 {varName} = {newValue}")
|
|
|
QMessageBox.information(self, "成功", f"变量 '{varName}' 已设置为 {newValue}")
|
|
|
|
|
|
# 刷新状态显示
|
|
|
self.refreshStatus()
|
|
|
else:
|
|
|
QMessageBox.warning(self, "错误", f"设置变量值失败\n期望: {newValue}\n实际: {actualValue}")
|
|
|
|
|
|
except Exception as e:
|
|
|
QMessageBox.warning(self, "错误", f"设置变量值失败: {str(e)}")
|
|
|
self.logMessage(f"设置变量值失败: {str(e)}")
|
|
|
|
|
|
def copyRule(self):
|
|
|
"""复制规则"""
|
|
|
currentRow = self.ruleTable.currentRow()
|
|
|
if currentRow >= 0:
|
|
|
try:
|
|
|
rules = self.controlManager.getAllRules()
|
|
|
if currentRow < len(rules):
|
|
|
rule = rules[currentRow]
|
|
|
|
|
|
# 创建规则副本数据
|
|
|
copyData = rule.copy()
|
|
|
copyData['name'] = f"{rule['name']}_副本"
|
|
|
copyData['ruleId'] = None # 清除ID,让系统生成新的
|
|
|
|
|
|
# 创建编辑对话框,预填充副本数据
|
|
|
dialog = RuleConfigDialog(self, copyData)
|
|
|
dialog.setWindowTitle("复制规则")
|
|
|
|
|
|
if dialog.exec_() == QDialog.Accepted:
|
|
|
# 获取编辑后的数据
|
|
|
newRuleData = dialog.getRuleData()
|
|
|
|
|
|
# 验证规则名称是否重复
|
|
|
existingNames = [r['name'] for r in rules]
|
|
|
if newRuleData['name'] in existingNames:
|
|
|
QMessageBox.warning(self, "错误", f"规则名称 '{newRuleData['name']}' 已存在")
|
|
|
return
|
|
|
|
|
|
# 创建新规则
|
|
|
try:
|
|
|
if newRuleData['actionType'] == "set_variable":
|
|
|
ruleId = self.controlManager.createSimpleRule(
|
|
|
newRuleData['name'],
|
|
|
newRuleData['variable'],
|
|
|
newRuleData['operator'],
|
|
|
newRuleData['threshold'],
|
|
|
newRuleData['target'],
|
|
|
newRuleData['value']
|
|
|
)
|
|
|
elif newRuleData['actionType'] == "send_alarm":
|
|
|
ruleId = self.controlManager.createAlarmRule(
|
|
|
newRuleData['name'],
|
|
|
newRuleData['variable'],
|
|
|
newRuleData['operator'],
|
|
|
newRuleData['threshold'],
|
|
|
newRuleData['value']
|
|
|
)
|
|
|
|
|
|
# 更新规则的其他属性
|
|
|
if ruleId:
|
|
|
newRule = self.controlManager.ruleEngine.getRule(ruleId)
|
|
|
if newRule:
|
|
|
newRule.enabled = newRuleData.get('enabled', True)
|
|
|
newRule.cooldownPeriod = newRuleData.get('cooldownPeriod', 0)
|
|
|
newRule.description = newRuleData.get('description', '')
|
|
|
|
|
|
# 添加变量监控
|
|
|
self.controlManager.addMonitorVariable(newRuleData['variable'])
|
|
|
|
|
|
self.refreshRuleTable()
|
|
|
self.logMessage(f"已复制规则: {newRuleData['name']}")
|
|
|
QMessageBox.information(self, "成功", f"规则 '{newRuleData['name']}' 已成功创建")
|
|
|
|
|
|
except Exception as e:
|
|
|
QMessageBox.warning(self, "错误", f"复制规则失败: {str(e)}")
|
|
|
else:
|
|
|
QMessageBox.warning(self, "错误", "无法获取规则信息")
|
|
|
except Exception as e:
|
|
|
QMessageBox.warning(self, "错误", f"复制规则失败: {str(e)}")
|
|
|
|
|
|
def enableAllRules(self):
|
|
|
"""启用所有规则"""
|
|
|
try:
|
|
|
rules = self.controlManager.getAllRules()
|
|
|
enabledCount = 0
|
|
|
|
|
|
for rule in rules:
|
|
|
if not rule['enabled']:
|
|
|
self.controlManager.enableRule(rule['ruleId'])
|
|
|
enabledCount += 1
|
|
|
|
|
|
if enabledCount > 0:
|
|
|
self.refreshRuleTable()
|
|
|
self.logMessage(f"已启用 {enabledCount} 个规则")
|
|
|
QMessageBox.information(self, "成功", f"已启用 {enabledCount} 个规则")
|
|
|
else:
|
|
|
QMessageBox.information(self, "提示", "所有规则都已经是启用状态")
|
|
|
|
|
|
except Exception as e:
|
|
|
QMessageBox.warning(self, "错误", f"批量启用失败: {str(e)}")
|
|
|
|
|
|
def disableAllRules(self):
|
|
|
"""禁用所有规则"""
|
|
|
try:
|
|
|
rules = self.controlManager.getAllRules()
|
|
|
disabledCount = 0
|
|
|
|
|
|
reply = QMessageBox.question(self, "确认",
|
|
|
f"确定要禁用所有 {len(rules)} 个规则吗?\n这将停止所有自动化控制功能。")
|
|
|
|
|
|
if reply == QMessageBox.Yes:
|
|
|
for rule in rules:
|
|
|
if rule['enabled']:
|
|
|
self.controlManager.disableRule(rule['ruleId'])
|
|
|
disabledCount += 1
|
|
|
|
|
|
if disabledCount > 0:
|
|
|
self.refreshRuleTable()
|
|
|
self.logMessage(f"已禁用 {disabledCount} 个规则")
|
|
|
QMessageBox.information(self, "成功", f"已禁用 {disabledCount} 个规则")
|
|
|
else:
|
|
|
QMessageBox.information(self, "提示", "所有规则都已经是禁用状态")
|
|
|
|
|
|
except Exception as e:
|
|
|
QMessageBox.warning(self, "错误", f"批量禁用失败: {str(e)}")
|
|
|
|
|
|
def reloadControlSystem(self):
|
|
|
"""重新加载控制系统(在工程切换后调用)"""
|
|
|
try:
|
|
|
# 重新初始化控制系统
|
|
|
if not self.controlManager.isInitialized:
|
|
|
self.controlManager.initialize()
|
|
|
else:
|
|
|
# 如果已初始化,重新加载配置(包含自动启动)
|
|
|
self.controlManager._loadConfiguration()
|
|
|
|
|
|
# 刷新界面
|
|
|
self.refreshRuleTable()
|
|
|
self.refreshStatus()
|
|
|
|
|
|
print("控制系统重新加载完成")
|
|
|
except Exception as e:
|
|
|
print(f"重新加载控制系统失败: {e}")
|
|
|
|
|
|
def refreshRuleTable(self):
|
|
|
"""刷新规则表格"""
|
|
|
rules = self.controlManager.getAllRules()
|
|
|
self.ruleTable.setRowCount(len(rules))
|
|
|
|
|
|
for i, rule in enumerate(rules):
|
|
|
# 规则名称
|
|
|
nameItem = QTableWidgetItem(rule['name'])
|
|
|
nameItem.setToolTip(f"规则ID: {rule['ruleId']}")
|
|
|
self.ruleTable.setItem(i, 0, nameItem)
|
|
|
|
|
|
# 描述
|
|
|
descItem = QTableWidgetItem(rule['description'])
|
|
|
descItem.setToolTip(rule['description'])
|
|
|
self.ruleTable.setItem(i, 1, descItem)
|
|
|
|
|
|
# 状态指示器
|
|
|
statusLabel = QLabel()
|
|
|
statusLabel.setObjectName("statusIndicator")
|
|
|
if rule['enabled']:
|
|
|
statusLabel.setText("● 启用")
|
|
|
statusLabel.setProperty("status", "enabled")
|
|
|
statusLabel.setStyleSheet("""
|
|
|
QLabel {
|
|
|
color: #28A745;
|
|
|
font-weight: bold;
|
|
|
background-color: #D4EDDA;
|
|
|
border-radius: 4px;
|
|
|
padding: 4px 8px;
|
|
|
}
|
|
|
""")
|
|
|
else:
|
|
|
statusLabel.setText("● 禁用")
|
|
|
statusLabel.setProperty("status", "disabled")
|
|
|
statusLabel.setStyleSheet("""
|
|
|
QLabel {
|
|
|
color: #DC3545;
|
|
|
font-weight: bold;
|
|
|
background-color: #F8D7DA;
|
|
|
border-radius: 4px;
|
|
|
padding: 4px 8px;
|
|
|
}
|
|
|
""")
|
|
|
statusLabel.setAlignment(Qt.AlignCenter)
|
|
|
self.ruleTable.setCellWidget(i, 2, statusLabel)
|
|
|
|
|
|
# 最后执行时间
|
|
|
ruleObj = self.controlManager.ruleEngine.getRule(rule['ruleId'])
|
|
|
if ruleObj and ruleObj.lastExecutionTime:
|
|
|
lastExec = ruleObj.lastExecutionTime.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
else:
|
|
|
lastExec = "未执行"
|
|
|
|
|
|
lastExecItem = QTableWidgetItem(lastExec)
|
|
|
lastExecItem.setToolTip(f"规则最后一次执行时间: {lastExec}")
|
|
|
self.ruleTable.setItem(i, 3, lastExecItem)
|
|
|
|
|
|
# 操作按钮 - 更紧凑的设计
|
|
|
toggleBtn = QPushButton("禁用" if rule['enabled'] else "启用")
|
|
|
toggleBtn.setFixedSize(60, 26) # 固定按钮大小
|
|
|
|
|
|
if rule['enabled']:
|
|
|
toggleBtn.setStyleSheet("""
|
|
|
QPushButton {
|
|
|
background-color: #FFC107;
|
|
|
color: #333;
|
|
|
font-size: 10px;
|
|
|
font-weight: bold;
|
|
|
border-radius: 3px;
|
|
|
border: none;
|
|
|
}
|
|
|
QPushButton:hover { background-color: #FFD43B; }
|
|
|
""")
|
|
|
else:
|
|
|
toggleBtn.setStyleSheet("""
|
|
|
QPushButton {
|
|
|
background-color: #28A745;
|
|
|
color: white;
|
|
|
font-size: 10px;
|
|
|
font-weight: bold;
|
|
|
border-radius: 3px;
|
|
|
border: none;
|
|
|
}
|
|
|
QPushButton:hover { background-color: #34CE57; }
|
|
|
""")
|
|
|
|
|
|
toggleBtn.clicked.connect(lambda checked, rId=rule['ruleId']: self.toggleRule(rId))
|
|
|
self.ruleTable.setCellWidget(i, 4, toggleBtn)
|
|
|
|
|
|
# 设置行高
|
|
|
self.ruleTable.setRowHeight(i, 36)
|
|
|
|
|
|
def toggleRule(self, ruleId: str):
|
|
|
"""切换规则状态"""
|
|
|
rule = self.controlManager.ruleEngine.getRule(ruleId)
|
|
|
if rule:
|
|
|
if rule.enabled:
|
|
|
self.controlManager.disableRule(ruleId)
|
|
|
self.logMessage(f"已禁用规则: {rule.name}")
|
|
|
else:
|
|
|
self.controlManager.enableRule(ruleId)
|
|
|
self.logMessage(f"已启用规则: {rule.name}")
|
|
|
|
|
|
# 保存配置
|
|
|
self.controlManager.saveConfiguration()
|
|
|
|
|
|
self.refreshRuleTable()
|
|
|
|
|
|
def refreshStatus(self):
|
|
|
"""刷新状态信息"""
|
|
|
status = self.controlManager.getSystemStatus()
|
|
|
|
|
|
self.statusLabel.setText(f"系统状态: {'运行中' if status['initialized'] else '未初始化'}")
|
|
|
self.rulesCountLabel.setText(f"规则数量: {status['totalRules']} (启用: {status['enabledRules']})")
|
|
|
self.variablesCountLabel.setText(f"监控变量: {status['monitoredVariables']}")
|
|
|
self.lastUpdateLabel.setText(f"最后更新: {status['lastUpdate'][:19]}")
|
|
|
|
|
|
# 显示变量值
|
|
|
try:
|
|
|
allVars = self.controlManager.variableMonitor.getAllVariables()
|
|
|
if allVars:
|
|
|
varText = "变量值:\n"
|
|
|
for varName, value in allVars.items():
|
|
|
varText += f" {varName}: {value}\n"
|
|
|
self.variableValuesLabel.setText(varText.strip())
|
|
|
else:
|
|
|
self.variableValuesLabel.setText("变量值: 无监控变量")
|
|
|
except Exception as e:
|
|
|
self.variableValuesLabel.setText(f"变量值: 获取失败 ({e})")
|
|
|
|
|
|
def searchRules(self, searchText: str):
|
|
|
"""搜索规则"""
|
|
|
try:
|
|
|
for row in range(self.ruleTable.rowCount()):
|
|
|
item = self.ruleTable.item(row, 0) # 规则名称列
|
|
|
if item:
|
|
|
# 如果搜索文本为空或者规则名称包含搜索文本,则显示该行
|
|
|
shouldShow = not searchText or searchText.lower() in item.text().lower()
|
|
|
self.ruleTable.setRowHidden(row, not shouldShow)
|
|
|
except Exception as e:
|
|
|
print(f"搜索规则失败: {e}")
|
|
|
|
|
|
def logMessage(self, message: str):
|
|
|
"""记录日志消息"""
|
|
|
from datetime import datetime
|
|
|
timestamp = datetime.now().strftime("%H:%M:%S")
|
|
|
self.logText.append(f"[{timestamp}] {message}")
|
|
|
|
|
|
# 限制日志行数
|
|
|
if self.logText.document().blockCount() > 100:
|
|
|
cursor = self.logText.textCursor()
|
|
|
cursor.movePosition(cursor.Start)
|
|
|
cursor.select(cursor.BlockUnderCursor)
|
|
|
cursor.removeSelectedText()
|
|
|
|
|
|
def closeEvent(self, event):
|
|
|
"""关闭事件"""
|
|
|
self.controlManager.shutdown()
|
|
|
event.accept()
|
|
|
|
|
|
|
|
|
# 全局控制界面实例
|
|
|
_controlWidget = None
|
|
|
|
|
|
|
|
|
def getControlWidget():
|
|
|
"""获取控制界面实例"""
|
|
|
global _controlWidget
|
|
|
if _controlWidget is None:
|
|
|
_controlWidget = ControlWidget()
|
|
|
return _controlWidget
|
|
|
class MultiConditionRuleDialog(QDialog):
|
|
|
"""多条件规则创建对话框"""
|
|
|
|
|
|
def __init__(self, parent=None):
|
|
|
super().__init__(parent)
|
|
|
self.setWindowTitle("创建多条件规则")
|
|
|
self.setModal(True)
|
|
|
self.resize(800, 600)
|
|
|
self.setObjectName("multiConditionDialog")
|
|
|
|
|
|
# 应用父窗口的样式
|
|
|
if parent and hasattr(parent, 'styleSheet') and parent.styleSheet():
|
|
|
self.setStyleSheet(parent.styleSheet())
|
|
|
|
|
|
self.setupUI()
|
|
|
|
|
|
def setupUI(self):
|
|
|
"""设置界面"""
|
|
|
layout = QVBoxLayout(self)
|
|
|
|
|
|
# 基本信息
|
|
|
basicGroup = QGroupBox("基本信息")
|
|
|
basicLayout = QFormLayout(basicGroup)
|
|
|
|
|
|
self.nameEdit = QLineEdit()
|
|
|
self.nameEdit.setPlaceholderText("输入规则名称")
|
|
|
self.descEdit = QLineEdit()
|
|
|
self.descEdit.setPlaceholderText("输入规则描述")
|
|
|
|
|
|
self.enabledCheckBox = QCheckBox("启用规则")
|
|
|
self.enabledCheckBox.setChecked(True)
|
|
|
|
|
|
self.logicCombo = QComboBox()
|
|
|
self.logicCombo.addItems(["and", "or"])
|
|
|
self.logicCombo.setCurrentText("and")
|
|
|
|
|
|
self.cooldownSpin = QSpinBox()
|
|
|
self.cooldownSpin.setRange(0, 60000)
|
|
|
self.cooldownSpin.setSuffix(" ms")
|
|
|
self.cooldownSpin.setValue(0)
|
|
|
|
|
|
basicLayout.addRow("规则名称:", self.nameEdit)
|
|
|
basicLayout.addRow("规则描述:", self.descEdit)
|
|
|
basicLayout.addRow("", self.enabledCheckBox)
|
|
|
basicLayout.addRow("条件逻辑:", self.logicCombo)
|
|
|
basicLayout.addRow("冷却期:", self.cooldownSpin)
|
|
|
|
|
|
layout.addWidget(basicGroup)
|
|
|
|
|
|
# 条件设置
|
|
|
conditionGroup = QGroupBox("触发条件")
|
|
|
conditionLayout = QVBoxLayout(conditionGroup)
|
|
|
|
|
|
# 条件列表
|
|
|
self.conditionTable = QTableWidget()
|
|
|
self.conditionTable.setColumnCount(4)
|
|
|
self.conditionTable.setHorizontalHeaderLabels(["变量名", "操作符", "阈值", "操作"])
|
|
|
self.conditionTable.horizontalHeader().setStretchLastSection(True)
|
|
|
|
|
|
conditionLayout.addWidget(self.conditionTable)
|
|
|
|
|
|
# 条件操作按钮
|
|
|
condBtnLayout = QHBoxLayout()
|
|
|
self.addConditionBtn = QPushButton("添加条件")
|
|
|
self.addConditionBtn.clicked.connect(self.addCondition)
|
|
|
self.removeConditionBtn = QPushButton("删除条件")
|
|
|
self.removeConditionBtn.clicked.connect(self.removeCondition)
|
|
|
|
|
|
condBtnLayout.addWidget(self.addConditionBtn)
|
|
|
condBtnLayout.addWidget(self.removeConditionBtn)
|
|
|
condBtnLayout.addStretch()
|
|
|
|
|
|
conditionLayout.addLayout(condBtnLayout)
|
|
|
layout.addWidget(conditionGroup)
|
|
|
|
|
|
# 动作设置
|
|
|
actionGroup = QGroupBox("执行动作")
|
|
|
actionLayout = QVBoxLayout(actionGroup)
|
|
|
|
|
|
# 动作列表
|
|
|
self.actionTable = QTableWidget()
|
|
|
self.actionTable.setColumnCount(4)
|
|
|
self.actionTable.setHorizontalHeaderLabels(["动作类型", "目标", "值", "操作"])
|
|
|
self.actionTable.horizontalHeader().setStretchLastSection(True)
|
|
|
|
|
|
actionLayout.addWidget(self.actionTable)
|
|
|
|
|
|
# 动作操作按钮
|
|
|
actionBtnLayout = QHBoxLayout()
|
|
|
self.addActionBtn = QPushButton("添加动作")
|
|
|
self.addActionBtn.clicked.connect(self.addAction)
|
|
|
self.removeActionBtn = QPushButton("删除动作")
|
|
|
self.removeActionBtn.clicked.connect(self.removeAction)
|
|
|
|
|
|
actionBtnLayout.addWidget(self.addActionBtn)
|
|
|
actionBtnLayout.addWidget(self.removeActionBtn)
|
|
|
actionBtnLayout.addStretch()
|
|
|
|
|
|
actionLayout.addLayout(actionBtnLayout)
|
|
|
layout.addWidget(actionGroup)
|
|
|
|
|
|
# 对话框按钮
|
|
|
buttonLayout = QHBoxLayout()
|
|
|
self.okBtn = QPushButton("确定")
|
|
|
self.okBtn.clicked.connect(self.accept)
|
|
|
self.cancelBtn = QPushButton("取消")
|
|
|
self.cancelBtn.clicked.connect(self.reject)
|
|
|
|
|
|
buttonLayout.addStretch()
|
|
|
buttonLayout.addWidget(self.okBtn)
|
|
|
buttonLayout.addWidget(self.cancelBtn)
|
|
|
|
|
|
layout.addLayout(buttonLayout)
|
|
|
|
|
|
# 初始化一个条件和一个动作
|
|
|
self.addCondition()
|
|
|
self.addAction()
|
|
|
|
|
|
def addCondition(self):
|
|
|
"""添加条件"""
|
|
|
row = self.conditionTable.rowCount()
|
|
|
self.conditionTable.insertRow(row)
|
|
|
|
|
|
# 变量名输入框
|
|
|
varEdit = QLineEdit()
|
|
|
varEdit.setPlaceholderText("变量名")
|
|
|
self.conditionTable.setCellWidget(row, 0, varEdit)
|
|
|
|
|
|
# 操作符下拉框
|
|
|
opCombo = QComboBox()
|
|
|
opCombo.addItems([op.value for op in OperatorType])
|
|
|
self.conditionTable.setCellWidget(row, 1, opCombo)
|
|
|
|
|
|
# 阈值输入框
|
|
|
valueEdit = QLineEdit()
|
|
|
valueEdit.setPlaceholderText("阈值")
|
|
|
self.conditionTable.setCellWidget(row, 2, valueEdit)
|
|
|
|
|
|
# 删除按钮
|
|
|
delBtn = QPushButton("删除")
|
|
|
delBtn.clicked.connect(lambda: self.removeConditionRow(row))
|
|
|
self.conditionTable.setCellWidget(row, 3, delBtn)
|
|
|
|
|
|
def removeCondition(self):
|
|
|
"""删除选中的条件"""
|
|
|
currentRow = self.conditionTable.currentRow()
|
|
|
if currentRow >= 0:
|
|
|
self.conditionTable.removeRow(currentRow)
|
|
|
|
|
|
def removeConditionRow(self, row):
|
|
|
"""删除指定行的条件"""
|
|
|
self.conditionTable.removeRow(row)
|
|
|
# 重新设置删除按钮的连接
|
|
|
for i in range(self.conditionTable.rowCount()):
|
|
|
delBtn = self.conditionTable.cellWidget(i, 3)
|
|
|
if delBtn:
|
|
|
delBtn.clicked.disconnect()
|
|
|
delBtn.clicked.connect(lambda checked, r=i: self.removeConditionRow(r))
|
|
|
|
|
|
def addAction(self):
|
|
|
"""添加动作"""
|
|
|
row = self.actionTable.rowCount()
|
|
|
self.actionTable.insertRow(row)
|
|
|
|
|
|
# 动作类型下拉框
|
|
|
typeCombo = QComboBox()
|
|
|
typeCombo.addItems([act.value for act in ActionType])
|
|
|
self.actionTable.setCellWidget(row, 0, typeCombo)
|
|
|
|
|
|
# 目标输入框
|
|
|
targetEdit = QLineEdit()
|
|
|
targetEdit.setPlaceholderText("目标")
|
|
|
self.actionTable.setCellWidget(row, 1, targetEdit)
|
|
|
|
|
|
# 值输入框
|
|
|
valueEdit = QLineEdit()
|
|
|
valueEdit.setPlaceholderText("值")
|
|
|
self.actionTable.setCellWidget(row, 2, valueEdit)
|
|
|
|
|
|
# 删除按钮
|
|
|
delBtn = QPushButton("删除")
|
|
|
delBtn.clicked.connect(lambda: self.removeActionRow(row))
|
|
|
self.actionTable.setCellWidget(row, 3, delBtn)
|
|
|
|
|
|
def removeAction(self):
|
|
|
"""删除选中的动作"""
|
|
|
currentRow = self.actionTable.currentRow()
|
|
|
if currentRow >= 0:
|
|
|
self.actionTable.removeRow(currentRow)
|
|
|
|
|
|
def removeActionRow(self, row):
|
|
|
"""删除指定行的动作"""
|
|
|
self.actionTable.removeRow(row)
|
|
|
# 重新设置删除按钮的连接
|
|
|
for i in range(self.actionTable.rowCount()):
|
|
|
delBtn = self.actionTable.cellWidget(i, 3)
|
|
|
if delBtn:
|
|
|
delBtn.clicked.disconnect()
|
|
|
delBtn.clicked.connect(lambda checked, r=i: self.removeActionRow(r))
|
|
|
|
|
|
def getRuleData(self):
|
|
|
"""获取规则数据"""
|
|
|
# 基本信息
|
|
|
ruleData = {
|
|
|
'name': self.nameEdit.text().strip(),
|
|
|
'description': self.descEdit.text().strip(),
|
|
|
'enabled': self.enabledCheckBox.isChecked(),
|
|
|
'logicOperator': self.logicCombo.currentText(),
|
|
|
'cooldownPeriod': self.cooldownSpin.value(),
|
|
|
'conditions': [],
|
|
|
'actions': []
|
|
|
}
|
|
|
|
|
|
# 收集条件
|
|
|
for row in range(self.conditionTable.rowCount()):
|
|
|
varEdit = self.conditionTable.cellWidget(row, 0)
|
|
|
opCombo = self.conditionTable.cellWidget(row, 1)
|
|
|
valueEdit = self.conditionTable.cellWidget(row, 2)
|
|
|
|
|
|
if varEdit and opCombo and valueEdit:
|
|
|
varName = varEdit.text().strip()
|
|
|
operator = opCombo.currentText()
|
|
|
valueText = valueEdit.text().strip()
|
|
|
|
|
|
if varName and valueText:
|
|
|
try:
|
|
|
# 尝试转换为数值
|
|
|
if '.' in valueText:
|
|
|
value = float(valueText)
|
|
|
else:
|
|
|
value = int(valueText)
|
|
|
except ValueError:
|
|
|
value = valueText # 保持字符串
|
|
|
|
|
|
ruleData['conditions'].append({
|
|
|
'variable': varName,
|
|
|
'operator': operator,
|
|
|
'value': value
|
|
|
})
|
|
|
|
|
|
# 收集动作
|
|
|
for row in range(self.actionTable.rowCount()):
|
|
|
typeCombo = self.actionTable.cellWidget(row, 0)
|
|
|
targetEdit = self.actionTable.cellWidget(row, 1)
|
|
|
valueEdit = self.actionTable.cellWidget(row, 2)
|
|
|
|
|
|
if typeCombo and targetEdit and valueEdit:
|
|
|
actionType = typeCombo.currentText()
|
|
|
target = targetEdit.text().strip()
|
|
|
valueText = valueEdit.text().strip()
|
|
|
|
|
|
if target and valueText:
|
|
|
# 根据动作类型处理值
|
|
|
if actionType == "set_variable":
|
|
|
try:
|
|
|
if '.' in valueText:
|
|
|
value = float(valueText)
|
|
|
else:
|
|
|
value = int(valueText)
|
|
|
except ValueError:
|
|
|
value = valueText
|
|
|
else:
|
|
|
value = valueText
|
|
|
|
|
|
ruleData['actions'].append({
|
|
|
'actionType': actionType,
|
|
|
'target': target,
|
|
|
'value': value
|
|
|
})
|
|
|
|
|
|
return ruleData
|
|
|
|
|
|
def validate(self):
|
|
|
"""验证输入"""
|
|
|
if not self.nameEdit.text().strip():
|
|
|
QMessageBox.warning(self, "验证错误", "规则名称不能为空")
|
|
|
return False
|
|
|
|
|
|
if not self.getRuleData()['conditions']:
|
|
|
QMessageBox.warning(self, "验证错误", "至少需要一个条件")
|
|
|
return False
|
|
|
|
|
|
if not self.getRuleData()['actions']:
|
|
|
QMessageBox.warning(self, "验证错误", "至少需要一个动作")
|
|
|
return False
|
|
|
|
|
|
return True
|
|
|
|
|
|
def accept(self):
|
|
|
"""确定按钮处理"""
|
|
|
if self.validate():
|
|
|
super().accept()
|
|
|
|
|
|
|
|
|
def getControlWidget():
|
|
|
"""获取控制界面实例"""
|
|
|
return ControlWidget() |