You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

261 lines
11 KiB
Python

1 month ago
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
HART变量信息显示界面模块
实现主变量第二变量回路电流等动态数据的读取与显示
"""
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
QPushButton, QGroupBox, QFormLayout, QTextEdit,
QMessageBox, QProgressBar, QSplitter)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtGui import QFont, QTextCursor
from protocol.HART import common
class ReadVariableThread(QThread):
"""读取单个变量信息的线程类"""
infoReceived = pyqtSignal(dict, str) # 信号发送(响应字典, 命令类型)
errorOccurred = pyqtSignal(str, str)
def __init__(self, hartComm, command_name):
super().__init__()
self.hartComm = hartComm
self.command_name = command_name
self._is_running = True
def stop(self):
self._is_running = False
def run(self):
try:
if not self.hartComm:
raise Exception("HART通信对象未初始化")
func = getattr(self.hartComm, self.command_name, None)
if not func:
raise Exception(f"未找到名为 {self.command_name} 的通信方法")
response = func()
if self._is_running:
msg = response[0] if isinstance(response, list) and response else response
# print(msg)
if msg and isinstance(msg, dict):
if msg.get("status") != "fail":
self.infoReceived.emit(msg, self.command_name)
else:
error_msg = msg.get("error", "未知错误")
self.errorOccurred.emit(f"命令 {self.command_name} 执行失败: {error_msg}", self.command_name)
else:
self.errorOccurred.emit(f"收到无效响应: {response}", self.command_name)
except Exception as e:
self.errorOccurred.emit(str(e), self.command_name)
class HartVariableInfoWidget(QWidget):
"""HART变量信息显示界面"""
def __init__(self, parent=None):
super().__init__(parent)
self.hartComm = None
self.readThreads = {}
self.initUI()
def initUI(self):
mainLayout = QVBoxLayout(self)
topLayout = QHBoxLayout()
# --- Command 1 Group ---
cmd1Group = QGroupBox("主变量 (Command 1)")
cmd1Layout = QFormLayout()
self.pvLabel = QLabel("--")
self.pvUnitLabel = QLabel("--")
cmd1Layout.addRow("主变量值:", self.pvLabel)
cmd1Layout.addRow("单位:", self.pvUnitLabel)
self.readCmd1Button = QPushButton("读取")
self.readCmd1Button.clicked.connect(lambda: self.readVariableInfo('readPrimaryVariable'))
cmd1Layout.addRow(self.readCmd1Button)
cmd1Group.setLayout(cmd1Layout)
# --- Command 2 Group ---
cmd2Group = QGroupBox("回路电流和百分比 (Command 2)")
cmd2Layout = QFormLayout()
self.loopCurrentLabel_cmd2 = QLabel("--")
self.percentRangeLabel = QLabel("--")
cmd2Layout.addRow("回路电流:", self.loopCurrentLabel_cmd2)
cmd2Layout.addRow("量程百分比:", self.percentRangeLabel)
self.readCmd2Button = QPushButton("读取")
self.readCmd2Button.clicked.connect(lambda: self.readVariableInfo('readLoopCurrentAndPercent'))
cmd2Layout.addRow(self.readCmd2Button)
cmd2Group.setLayout(cmd2Layout)
# --- Command 3 Group ---
cmd3Group = QGroupBox("动态变量和回路电流 (Command 3)")
cmd3Layout = QFormLayout()
self.loopCurrentLabel_cmd3 = QLabel("--")
self.pv_cmd3_Label = QLabel("--")
self.pv_cmd3_UnitLabel = QLabel("--")
self.sv_cmd3_Label = QLabel("--")
self.sv_cmd3_UnitLabel = QLabel("--")
self.tv_cmd3_Label = QLabel("--")
self.tv_cmd3_UnitLabel = QLabel("--")
self.qv_cmd3_Label = QLabel("--")
self.qv_cmd3_UnitLabel = QLabel("--")
cmd3Layout.addRow("回路电流:", self.loopCurrentLabel_cmd3)
cmd3Layout.addRow("主变量:", self.pv_cmd3_Label)
cmd3Layout.addRow("主变量单位:", self.pv_cmd3_UnitLabel)
cmd3Layout.addRow("第二变量:", self.sv_cmd3_Label)
cmd3Layout.addRow("第二变量单位:", self.sv_cmd3_UnitLabel)
cmd3Layout.addRow("第三变量:", self.tv_cmd3_Label)
cmd3Layout.addRow("第三变量单位:", self.tv_cmd3_UnitLabel)
cmd3Layout.addRow("第四变量:", self.qv_cmd3_Label)
cmd3Layout.addRow("第四变量单位:", self.qv_cmd3_UnitLabel)
self.readCmd3Button = QPushButton("读取")
self.readCmd3Button.clicked.connect(lambda: self.readVariableInfo('readDynamicVariablesAndLoopCurrent'))
cmd3Layout.addRow(self.readCmd3Button)
cmd3Group.setLayout(cmd3Layout)
topLayout.addWidget(cmd1Group)
topLayout.addWidget(cmd2Group)
topLayout.addWidget(cmd3Group)
mainLayout.addLayout(topLayout)
# --- Raw Data Group ---
rawDataGroup = QGroupBox("原始数据")
rawDataLayout = QVBoxLayout()
self.rawDataText = QTextEdit()
self.rawDataText.setReadOnly(True)
self.rawDataText.setFont(QFont("Consolas", 9))
self.rawDataText.setLineWrapMode(QTextEdit.NoWrap)
rawDataLayout.addWidget(self.rawDataText)
rawDataGroup.setLayout(rawDataLayout)
mainLayout.addWidget(rawDataGroup)
# --- Global Controls ---
controlLayout = QHBoxLayout()
self.clearButton = QPushButton("清除所有显示")
self.clearButton.clicked.connect(self.clearAllDisplays)
controlLayout.addStretch()
controlLayout.addWidget(self.clearButton)
mainLayout.addLayout(controlLayout)
self.setHartComm(None) # Disable buttons initially
def setHartComm(self, hartComm):
self.hartComm = hartComm
is_connected = hartComm is not None
self.readCmd1Button.setEnabled(is_connected)
self.readCmd2Button.setEnabled(is_connected)
self.readCmd3Button.setEnabled(is_connected)
def readVariableInfo(self, command_name):
if not self.hartComm:
QMessageBox.warning(self, "警告", "未连接到HART设备")
return
if command_name in self.readThreads and self.readThreads[command_name].isRunning():
self.readThreads[command_name].stop()
self.readThreads[command_name].wait()
thread = ReadVariableThread(self.hartComm, command_name)
thread.infoReceived.connect(self.updateVariableInfo)
thread.errorOccurred.connect(self.handleError)
thread.finished.connect(lambda: self.onReadFinished(command_name))
self.readThreads[command_name] = thread
button = self.getButtonForCommand(command_name)
if button:
button.setText("读取中...")
button.setEnabled(False)
thread.start()
def updateVariableInfo(self, msg, command_name):
self.appendRawData(msg)
if command_name == 'readPrimaryVariable':
pv = msg.get('primary_variable')
unit_code = msg.get('primary_variable_units')
self.pvLabel.setText(f"{pv:.4f}" if pv is not None else "--")
self.pvUnitLabel.setText(common.get_unit_description(unit_code))
elif command_name == 'readLoopCurrentAndPercent':
current = msg.get('analog_signal')
percent = msg.get('primary_variable')
self.loopCurrentLabel_cmd2.setText(f"{current:.4f} mA" if current is not None else "--")
self.percentRangeLabel.setText(f"{percent:.2f} %" if percent is not None else "--")
elif command_name == 'readDynamicVariablesAndLoopCurrent':
current = msg.get('analog_signal')
self.loopCurrentLabel_cmd3.setText(f"{current:.4f} mA" if current is not None else "--")
pv = msg.get('primary_variable')
pv_unit = msg.get('primary_variable_units')
self.pv_cmd3_Label.setText(f"{pv:.4f}" if pv is not None else "--")
self.pv_cmd3_UnitLabel.setText(common.get_unit_description(pv_unit))
sv = msg.get('secondary_variable')
sv_unit = msg.get('secondary_variable_units')
self.sv_cmd3_Label.setText(f"{sv:.4f}" if sv is not None else "--")
self.sv_cmd3_UnitLabel.setText(common.get_unit_description(sv_unit))
tv = msg.get('tertiary_variable')
tv_unit = msg.get('tertiary_variable_units')
self.tv_cmd3_Label.setText(f"{tv:.4f}" if tv is not None else "--")
self.tv_cmd3_UnitLabel.setText(common.get_unit_description(tv_unit))
qv = msg.get('quaternary_variable')
qv_unit = msg.get('quaternary_variable_units')
self.qv_cmd3_Label.setText(f"{qv:.4f}" if qv is not None else "--")
self.qv_cmd3_UnitLabel.setText(common.get_unit_description(qv_unit))
def appendRawData(self, msg):
# self.rawDataText.append(\"--- 接收到的报文: {msg.get('command_name', 'N/A')} ---\")
for key, value in msg.items():
if isinstance(value, bytes):
value_str = ' '.join(f'{b:02x}' for b in value)
self.rawDataText.append(f"{key}: {value_str} (hex)")
else:
self.rawDataText.append(f"{key}: {value}")
self.rawDataText.append("\\n")
self.rawDataText.moveCursor(QTextCursor.End)
def handleError(self, errorMsg, command_name):
QMessageBox.critical(self, "错误", f"执行 {command_name} 时出错:\\n{errorMsg}")
def onReadFinished(self, command_name):
button = self.getButtonForCommand(command_name)
if button:
button.setText("读取")
button.setEnabled(self.hartComm is not None)
def getButtonForCommand(self, command_name):
if command_name == 'readPrimaryVariable':
return self.readCmd1Button
elif command_name == 'readLoopCurrentAndPercent':
return self.readCmd2Button
elif command_name == 'readDynamicVariablesAndLoopCurrent':
return self.readCmd3Button
return None
def clearAllDisplays(self):
# Cmd 1
self.pvLabel.setText("--")
self.pvUnitLabel.setText("--")
# Cmd 2
self.loopCurrentLabel_cmd2.setText("--")
self.percentRangeLabel.setText("--")
# Cmd 3
self.loopCurrentLabel_cmd3.setText("--")
self.pv_cmd3_Label.setText("--")
self.pv_cmd3_UnitLabel.setText("--")
self.sv_cmd3_Label.setText("--")
self.sv_cmd3_UnitLabel.setText("--")
self.tv_cmd3_Label.setText("--")
self.tv_cmd3_UnitLabel.setText("--")
self.qv_cmd3_Label.setText("--")
self.qv_cmd3_UnitLabel.setText("--")
self.rawDataText.clear()