#!/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()