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.

439 lines
15 KiB
Python

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/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("准备就绪")