|
|
#!/usr/bin/env python
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
"""
|
|
|
HART设备信息显示界面模块
|
|
|
实现设备唯一标识等信息的显示
|
|
|
"""
|
|
|
|
|
|
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
|
|
QPushButton, QGroupBox, QFormLayout, QTextEdit,
|
|
|
QTableWidget, QTableWidgetItem, QHeaderView, QMessageBox,
|
|
|
QProgressBar, QSplitter)
|
|
|
from PyQt5.QtCore import Qt, QThread, pyqtSignal
|
|
|
from PyQt5.QtGui import QFont, QTextCursor
|
|
|
|
|
|
class ReadDeviceInfoThread(QThread):
|
|
|
"""
|
|
|
读取设备信息的线程类
|
|
|
避免在UI线程中进行耗时操作
|
|
|
"""
|
|
|
# 自定义信号
|
|
|
infoReceived = pyqtSignal(list) # 信息接收信号
|
|
|
errorOccurred = pyqtSignal(str) # 错误信号
|
|
|
progressUpdated = pyqtSignal(int, str) # 进度更新信号(百分比, 描述)
|
|
|
|
|
|
def __init__(self, hartComm):
|
|
|
"""
|
|
|
初始化线程
|
|
|
|
|
|
Args:
|
|
|
hartComm: HART通信对象
|
|
|
"""
|
|
|
super().__init__()
|
|
|
self.hartComm = hartComm
|
|
|
self.isRunning = True
|
|
|
|
|
|
def stop(self):
|
|
|
"""
|
|
|
停止线程执行
|
|
|
"""
|
|
|
self.isRunning = False
|
|
|
|
|
|
def run(self):
|
|
|
"""
|
|
|
线程运行函数
|
|
|
读取设备信息和变量数据
|
|
|
"""
|
|
|
try:
|
|
|
if not self.hartComm:
|
|
|
raise Exception("HART通信对象未初始化")
|
|
|
|
|
|
deviceInfo = []
|
|
|
|
|
|
# 更新进度
|
|
|
self.progressUpdated.emit(10, "正在读取设备唯一标识...")
|
|
|
if not self.isRunning: return
|
|
|
|
|
|
# 读取设备唯一标识
|
|
|
try:
|
|
|
result = self.hartComm.readUniqueId()
|
|
|
if isinstance(result, list):
|
|
|
deviceInfo.extend(result)
|
|
|
elif not isinstance(result, dict) or 'error' not in result:
|
|
|
deviceInfo.append(result)
|
|
|
except Exception as e:
|
|
|
self.errorOccurred.emit(f"读取设备唯一标识失败: {e}")
|
|
|
return
|
|
|
|
|
|
# 变量信息读取已移至 HartVariableInfoWidget
|
|
|
self.progressUpdated.emit(80, "正在读取设备唯一标识...")
|
|
|
|
|
|
# 完成读取
|
|
|
self.progressUpdated.emit(100, "读取完成")
|
|
|
|
|
|
if self.isRunning:
|
|
|
self.infoReceived.emit(deviceInfo)
|
|
|
except Exception as e:
|
|
|
self.errorOccurred.emit(str(e))
|
|
|
|
|
|
class HartDeviceInfoWidget(QWidget):
|
|
|
"""
|
|
|
HART设备信息显示界面模块
|
|
|
实现设备唯一标识等信息的显示
|
|
|
"""
|
|
|
|
|
|
def __init__(self, parent=None):
|
|
|
"""
|
|
|
初始化设备信息界面
|
|
|
"""
|
|
|
super().__init__(parent)
|
|
|
|
|
|
# 初始化变量
|
|
|
self.hartComm = None
|
|
|
self.deviceInfo = None
|
|
|
self.readThread = None
|
|
|
|
|
|
# 初始化界面
|
|
|
self.initUI()
|
|
|
|
|
|
def initUI(self):
|
|
|
"""
|
|
|
初始化用户界面
|
|
|
"""
|
|
|
# 创建主布局
|
|
|
mainLayout = QVBoxLayout()
|
|
|
mainLayout.setContentsMargins(20, 20, 20, 20)
|
|
|
mainLayout.setSpacing(20)
|
|
|
|
|
|
# 创建分割器
|
|
|
splitter = QSplitter(Qt.Vertical)
|
|
|
|
|
|
# 创建设备信息组
|
|
|
deviceInfoGroup = QGroupBox("设备基本信息")
|
|
|
deviceInfoGroup.setFont(QFont("Microsoft YaHei", 10, QFont.Bold))
|
|
|
deviceInfoLayout = QFormLayout()
|
|
|
deviceInfoLayout.setLabelAlignment(Qt.AlignRight)
|
|
|
deviceInfoLayout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
|
|
|
|
|
|
# 设备信息字段
|
|
|
self.manufacturerIdLabel = QLabel("--")
|
|
|
deviceInfoLayout.addRow("制造商ID:", self.manufacturerIdLabel)
|
|
|
|
|
|
self.deviceTypeLabel = QLabel("--")
|
|
|
deviceInfoLayout.addRow("设备类型:", self.deviceTypeLabel)
|
|
|
|
|
|
self.deviceIdLabel = QLabel("--")
|
|
|
deviceInfoLayout.addRow("设备ID:", self.deviceIdLabel)
|
|
|
|
|
|
self.preambleCountLabel = QLabel("--")
|
|
|
deviceInfoLayout.addRow("前导符数量:", self.preambleCountLabel)
|
|
|
|
|
|
self.universalRevLabel = QLabel("--")
|
|
|
deviceInfoLayout.addRow("通用命令版本:", self.universalRevLabel)
|
|
|
|
|
|
self.specificRevLabel = QLabel("--")
|
|
|
deviceInfoLayout.addRow("特定命令版本:", self.specificRevLabel)
|
|
|
|
|
|
self.softwareRevLabel = QLabel("--")
|
|
|
deviceInfoLayout.addRow("软件版本:", self.softwareRevLabel)
|
|
|
|
|
|
self.hardwareRevLabel = QLabel("--")
|
|
|
deviceInfoLayout.addRow("硬件版本:", self.hardwareRevLabel)
|
|
|
|
|
|
self.deviceStatusLabel = QLabel("--")
|
|
|
deviceInfoLayout.addRow("设备状态:", self.deviceStatusLabel)
|
|
|
|
|
|
self.commandNameLabel = QLabel("--")
|
|
|
deviceInfoLayout.addRow("命令名称:", self.commandNameLabel)
|
|
|
|
|
|
self.responseCodeLabel = QLabel("--")
|
|
|
deviceInfoLayout.addRow("响应代码:", self.responseCodeLabel)
|
|
|
|
|
|
self.flagsLabel = QLabel("--")
|
|
|
deviceInfoLayout.addRow("标志:", self.flagsLabel)
|
|
|
|
|
|
# 设置布局
|
|
|
deviceInfoGroup.setLayout(deviceInfoLayout)
|
|
|
|
|
|
# 变量信息组已移至 HartVariableInfoWidget
|
|
|
|
|
|
# 添加到分割器
|
|
|
splitter.addWidget(deviceInfoGroup)
|
|
|
|
|
|
|
|
|
mainLayout.addWidget(splitter)
|
|
|
|
|
|
# 创建原始数据组
|
|
|
rawDataGroup = QGroupBox("原始数据")
|
|
|
rawDataGroup.setFont(QFont("Microsoft YaHei", 10, QFont.Bold))
|
|
|
rawDataLayout = QVBoxLayout()
|
|
|
|
|
|
# 原始数据文本框
|
|
|
self.rawDataText = QTextEdit()
|
|
|
self.rawDataText.setReadOnly(True)
|
|
|
self.rawDataText.setFont(QFont("Consolas", 9))
|
|
|
self.rawDataText.setLineWrapMode(QTextEdit.NoWrap)
|
|
|
self.rawDataText.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
|
|
self.rawDataText.setMinimumHeight(120)
|
|
|
rawDataLayout.addWidget(self.rawDataText)
|
|
|
|
|
|
# 设置布局
|
|
|
rawDataGroup.setLayout(rawDataLayout)
|
|
|
splitter.addWidget(rawDataGroup)
|
|
|
splitter.setSizes([300, 200])
|
|
|
splitter.setStretchFactor(0, 1)
|
|
|
splitter.setStretchFactor(1, 1)
|
|
|
|
|
|
# 创建进度条
|
|
|
progressLayout = QHBoxLayout()
|
|
|
self.progressBar = QProgressBar()
|
|
|
self.progressBar.setRange(0, 100)
|
|
|
self.progressBar.setValue(0)
|
|
|
self.progressBar.setVisible(False)
|
|
|
self.progressLabel = QLabel("准备就绪")
|
|
|
progressLayout.addWidget(self.progressBar)
|
|
|
progressLayout.addWidget(self.progressLabel)
|
|
|
mainLayout.addLayout(progressLayout)
|
|
|
|
|
|
# 创建按钮布局
|
|
|
buttonLayout = QHBoxLayout()
|
|
|
buttonLayout.setSpacing(10)
|
|
|
|
|
|
# 读取按钮
|
|
|
self.readButton = QPushButton("读取设备信息")
|
|
|
self.readButton.clicked.connect(self.readDeviceInfo)
|
|
|
self.readButton.setEnabled(False) # 初始禁用
|
|
|
buttonLayout.addWidget(self.readButton)
|
|
|
|
|
|
# 取消按钮
|
|
|
self.cancelButton = QPushButton("取消")
|
|
|
self.cancelButton.setEnabled(False)
|
|
|
self.cancelButton.clicked.connect(self.cancelOperation)
|
|
|
buttonLayout.addWidget(self.cancelButton)
|
|
|
|
|
|
# 清除按钮
|
|
|
self.clearButton = QPushButton("清除显示")
|
|
|
self.clearButton.clicked.connect(self.clearDisplay)
|
|
|
buttonLayout.addWidget(self.clearButton)
|
|
|
|
|
|
mainLayout.addLayout(buttonLayout)
|
|
|
|
|
|
# 设置主布局
|
|
|
self.setLayout(mainLayout)
|
|
|
|
|
|
def setHartComm(self, hartComm):
|
|
|
"""
|
|
|
设置HART通信对象
|
|
|
|
|
|
Args:
|
|
|
hartComm: HART通信对象
|
|
|
"""
|
|
|
self.hartComm = hartComm
|
|
|
self.readButton.setEnabled(hartComm is not None)
|
|
|
|
|
|
def readDeviceInfo(self):
|
|
|
"""
|
|
|
读取设备信息
|
|
|
"""
|
|
|
if not self.hartComm:
|
|
|
QMessageBox.warning(self, "警告", "未连接到HART设备!")
|
|
|
return
|
|
|
|
|
|
# 清除显示
|
|
|
self.clearDisplay()
|
|
|
|
|
|
# 禁用读取按钮,启用取消按钮
|
|
|
self.readButton.setEnabled(False)
|
|
|
self.readButton.setText("正在读取...")
|
|
|
self.cancelButton.setEnabled(True)
|
|
|
|
|
|
# 显示进度条
|
|
|
self.progressBar.setValue(0)
|
|
|
self.progressBar.setVisible(True)
|
|
|
self.progressLabel.setText("准备读取设备信息...")
|
|
|
|
|
|
# 创建并启动读取线程
|
|
|
self.readThread = ReadDeviceInfoThread(self.hartComm)
|
|
|
self.readThread.infoReceived.connect(self.updateDeviceInfo)
|
|
|
self.readThread.errorOccurred.connect(self.handleError)
|
|
|
self.readThread.progressUpdated.connect(self.updateProgress)
|
|
|
self.readThread.finished.connect(self.onReadFinished)
|
|
|
self.readThread.start()
|
|
|
|
|
|
def updateDeviceInfo(self, deviceInfo):
|
|
|
"""
|
|
|
更新设备信息显示
|
|
|
|
|
|
Args:
|
|
|
deviceInfo: 设备信息列表
|
|
|
"""
|
|
|
if not deviceInfo:
|
|
|
QMessageBox.warning(self, "警告", "未收到任何设备信息!")
|
|
|
return
|
|
|
|
|
|
self.rawDataText.clear()
|
|
|
|
|
|
for item in deviceInfo:
|
|
|
msg = item[0] if isinstance(item, list) and len(item) > 0 else item
|
|
|
|
|
|
if not isinstance(msg, dict):
|
|
|
self.rawDataText.append(f"接收到非字典格式的消息: {type(msg)}\n")
|
|
|
print(f"接收到非字典格式的消息: {type(msg)}, 内容: {msg}")
|
|
|
continue
|
|
|
|
|
|
try:
|
|
|
command_name = msg.get('command_name', '未知命令')
|
|
|
|
|
|
self.setLabelIfValueExists(self.manufacturerIdLabel, msg, 'manufacturer_id')
|
|
|
self.setLabelIfValueExists(self.deviceTypeLabel, msg, 'manufacturer_device_type')
|
|
|
self.setLabelIfValueExists(self.deviceIdLabel, msg, 'device_id')
|
|
|
self.setLabelIfValueExists(self.preambleCountLabel, msg, 'number_response_preamble_characters')
|
|
|
self.setLabelIfValueExists(self.universalRevLabel, msg, 'universal_command_revision_level')
|
|
|
self.setLabelIfValueExists(self.specificRevLabel, msg, 'transmitter_specific_command_revision_level')
|
|
|
self.setLabelIfValueExists(self.softwareRevLabel, msg, 'software_revision_level')
|
|
|
self.setLabelIfValueExists(self.hardwareRevLabel, msg, 'hardware_revision_level')
|
|
|
if command_name != '未知命令':
|
|
|
self.commandNameLabel.setText(command_name)
|
|
|
self.setLabelIfValueExists(self.responseCodeLabel, msg, 'response_code')
|
|
|
|
|
|
if 'device_status' in msg:
|
|
|
status_text = self.formatDeviceStatus(msg['device_status'])
|
|
|
self.flagsLabel.setText(status_text)
|
|
|
|
|
|
self.rawDataText.append(f"--- 接收到的报文: {command_name} ---")
|
|
|
|
|
|
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)
|
|
|
except Exception as e:
|
|
|
error_text = f"处理报文数据时出错: {str(e)}\n"
|
|
|
self.rawDataText.append(error_text)
|
|
|
print(error_text)
|
|
|
continue
|
|
|
|
|
|
def formatDeviceStatus(self, status_byte):
|
|
|
"""
|
|
|
格式化设备状态字节
|
|
|
|
|
|
Args:
|
|
|
status_byte: 状态字节
|
|
|
|
|
|
Returns:
|
|
|
str: 格式化的状态描述
|
|
|
"""
|
|
|
status_text = []
|
|
|
|
|
|
# 解析状态位
|
|
|
if status_byte & 0x80:
|
|
|
status_text.append("设备故障")
|
|
|
if status_byte & 0x40:
|
|
|
status_text.append("配置已更改")
|
|
|
if status_byte & 0x20:
|
|
|
status_text.append("冷启动")
|
|
|
if status_byte & 0x10:
|
|
|
status_text.append("更多状态可用")
|
|
|
if status_byte & 0x08:
|
|
|
status_text.append("回路电流固定")
|
|
|
if status_byte & 0x04:
|
|
|
status_text.append("回路电流饱和")
|
|
|
if status_byte & 0x02:
|
|
|
status_text.append("非主变量超出范围")
|
|
|
if status_byte & 0x01:
|
|
|
status_text.append("主变量超出范围")
|
|
|
|
|
|
if not status_text:
|
|
|
return "正常"
|
|
|
return ", ".join(status_text)
|
|
|
|
|
|
|
|
|
|
|
|
def setLabelIfValueExists(self, label, data_dict, key, formatter=None):
|
|
|
"""
|
|
|
仅当 data_dict 中存在键且值有效时才更新对应标签
|
|
|
"""
|
|
|
value = data_dict.get(key)
|
|
|
if value is not None and value != "":
|
|
|
label.setText(formatter(value) if formatter else str(value))
|
|
|
|
|
|
def handleError(self, errorMsg):
|
|
|
"""
|
|
|
处理错误
|
|
|
|
|
|
Args:
|
|
|
errorMsg: 错误信息
|
|
|
"""
|
|
|
QMessageBox.critical(self, "错误", f"读取设备信息时发生错误:{errorMsg}")
|
|
|
|
|
|
def updateProgress(self, value, message):
|
|
|
"""
|
|
|
更新进度条
|
|
|
|
|
|
Args:
|
|
|
value: 进度值(0-100)
|
|
|
message: 进度消息
|
|
|
"""
|
|
|
self.progressBar.setValue(value)
|
|
|
self.progressLabel.setText(message)
|
|
|
|
|
|
def cancelOperation(self):
|
|
|
"""
|
|
|
取消当前操作
|
|
|
"""
|
|
|
if self.readThread and self.readThread.isRunning():
|
|
|
# 停止线程
|
|
|
self.readThread.stop()
|
|
|
self.readThread.wait()
|
|
|
|
|
|
# 更新界面
|
|
|
self.progressLabel.setText("操作已取消")
|
|
|
QMessageBox.information(self, "操作取消", "读取设备信息操作已取消")
|
|
|
|
|
|
# 恢复按钮状态
|
|
|
self.onReadFinished()
|
|
|
|
|
|
def onReadFinished(self):
|
|
|
"""
|
|
|
读取完成时的处理
|
|
|
"""
|
|
|
# 恢复按钮状态
|
|
|
self.readButton.setEnabled(self.hartComm is not None)
|
|
|
self.readButton.setText("读取设备信息")
|
|
|
self.cancelButton.setEnabled(False)
|
|
|
|
|
|
# 隐藏进度条
|
|
|
self.progressBar.setVisible(False)
|
|
|
|
|
|
def clearDisplay(self):
|
|
|
"""
|
|
|
清除显示内容
|
|
|
"""
|
|
|
# 清除所有标签
|
|
|
self.manufacturerIdLabel.setText("--")
|
|
|
self.deviceTypeLabel.setText("--")
|
|
|
self.deviceIdLabel.setText("--")
|
|
|
self.preambleCountLabel.setText("--")
|
|
|
self.universalRevLabel.setText("--")
|
|
|
self.specificRevLabel.setText("--")
|
|
|
self.softwareRevLabel.setText("--")
|
|
|
self.hardwareRevLabel.setText("--")
|
|
|
self.deviceStatusLabel.setText("--")
|
|
|
self.commandNameLabel.setText("--")
|
|
|
self.responseCodeLabel.setText("--")
|
|
|
self.flagsLabel.setText("--")
|
|
|
|
|
|
# 变量信息已移至新窗口
|
|
|
|
|
|
# 清除原始数据
|
|
|
self.rawDataText.clear()
|
|
|
|
|
|
# 重置进度条
|
|
|
self.progressBar.setValue(0)
|
|
|
self.progressBar.setVisible(False)
|
|
|
self.progressLabel.setText("准备就绪") |