0718更新 修复Modbus相关
parent
02f42ab766
commit
165659ea10
@ -1,116 +1,113 @@
|
||||
from PyQt5.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||
QMetaObject, QObject, QPoint, QRect,
|
||||
QSize, QTime, QUrl, Qt, QTimer)
|
||||
from PyQt5.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
||||
QFont, QFontDatabase, QGradient, QIcon,
|
||||
QImage, QKeySequence, QLinearGradient, QPainter,
|
||||
QPalette, QPixmap, QRadialGradient, QTransform)
|
||||
from PyQt5.QtWidgets import (QApplication, QGridLayout, QHBoxLayout, QLabel,
|
||||
QPushButton, QSizePolicy, QSpacerItem, QTextEdit,
|
||||
QWidget)
|
||||
from PyQt5.QtWidgets import QWidget, QLabel, QPushButton, QTextEdit, QHBoxLayout, QVBoxLayout, QGridLayout, QSpacerItem, QSizePolicy
|
||||
from PyQt5.QtCore import Qt, QTimer, QCoreApplication
|
||||
from PyQt5.QtGui import QIcon
|
||||
from utils import Globals
|
||||
|
||||
|
||||
from protocol.Celery.MBTCPMaster import app
|
||||
from Static import static
|
||||
class MessageWidget(QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super(MessageWidget, self).__init__(parent)
|
||||
|
||||
self.setObjectName(u"self")
|
||||
self.setupUI()
|
||||
self.setupConnections()
|
||||
|
||||
def setupUI(self):
|
||||
"""设置UI界面"""
|
||||
self.setObjectName("MessageWidget")
|
||||
self.resize(1037, 648)
|
||||
|
||||
self.decima = 16
|
||||
|
||||
self.recvLabel = QLabel(self)
|
||||
self.setWindowTitle('报文查看')
|
||||
# self.setWindowIcon(QIcon('./Static/file.png'))
|
||||
|
||||
# 创建控件
|
||||
self.recvLabel = QLabel("接收到报文")
|
||||
self.recvLabel.setObjectName("mesLabel")
|
||||
self.recvLabel.setAlignment(Qt.AlignCenter)
|
||||
|
||||
self.sendLabel = QLabel(self)
|
||||
|
||||
self.sendLabel = QLabel("发送的报文")
|
||||
self.sendLabel.setObjectName("mesLabel")
|
||||
self.sendLabel.setAlignment(Qt.AlignCenter)
|
||||
|
||||
self.clearButton = QPushButton(self)
|
||||
|
||||
self.clearButton = QPushButton("清空报文")
|
||||
self.clearButton.setObjectName("mesButton")
|
||||
|
||||
self.reButton = QPushButton(self)
|
||||
self.reButton.setObjectName("mesButton")
|
||||
|
||||
self.decimaButton = QPushButton(self)
|
||||
self.decimaButton.setObjectName("mesButton")
|
||||
|
||||
self.recvEdit = QTextEdit(self)
|
||||
|
||||
self.refreshButton = QPushButton("停止刷新")
|
||||
self.refreshButton.setObjectName("mesButton")
|
||||
|
||||
self.recvEdit = QTextEdit()
|
||||
self.recvEdit.setObjectName("mesEdit")
|
||||
|
||||
self.sendEdit = QTextEdit(self)
|
||||
|
||||
self.sendEdit = QTextEdit()
|
||||
self.sendEdit.setObjectName("mesEdit")
|
||||
|
||||
self.horizontalLayout = QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName("horizontalLayout")
|
||||
self.horizontalLayout.addWidget(self.clearButton)
|
||||
self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
self.horizontalLayout.addItem(self.horizontalSpacer)
|
||||
self.horizontalLayout.addWidget(self.reButton)
|
||||
self.horizontalLayout.addWidget(self.decimaButton)
|
||||
|
||||
self.gridLayout = QGridLayout(self)
|
||||
self.gridLayout.setObjectName("gridLayout")
|
||||
self.gridLayout.addWidget(self.recvLabel, 1, 0, 1, 1)
|
||||
self.gridLayout.addWidget(self.sendLabel, 1, 1, 1, 1)
|
||||
self.gridLayout.addWidget(self.recvEdit, 2, 0, 1, 1)
|
||||
self.gridLayout.addWidget(self.sendEdit, 2, 1, 1, 1)
|
||||
self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 2)
|
||||
|
||||
self.setWindowTitle('报文查看')
|
||||
self.setWindowIcon(QIcon('./:/static/file.png'))
|
||||
self.recvLabel.setText(QCoreApplication.translate("Form", u"\u63a5\u6536\u5230\u62a5\u6587", None))
|
||||
self.sendLabel.setText(QCoreApplication.translate("Form", u"\u53d1\u9001\u7684\u62a5\u6587", None))
|
||||
self.clearButton.setText("清空报文")
|
||||
self.reButton.setText('停止刷新')
|
||||
self.decimaButton.setText('切换进制')
|
||||
|
||||
|
||||
# 布局设置
|
||||
self.setupLayout()
|
||||
|
||||
# 定时器设置
|
||||
self.timer = QTimer(self)
|
||||
# 将定时器超时信号与槽函数showTime()连接
|
||||
self.timer.timeout.connect(self.addText)
|
||||
self.timer.start(1000) # 启动timer
|
||||
|
||||
self.reButton.clicked.connect(self.stopRe)
|
||||
self.decimaButton.clicked.connect(self.changeDecima)
|
||||
self.clearButton.clicked.connect(self.clearText)
|
||||
|
||||
def clearText(self):
|
||||
self.timer.timeout.connect(self.updateMessages)
|
||||
self.timer.start(1000) # 每秒更新一次
|
||||
|
||||
def setupLayout(self):
|
||||
"""设置布局"""
|
||||
# 按钮布局
|
||||
buttonLayout = QHBoxLayout()
|
||||
buttonLayout.addWidget(self.clearButton)
|
||||
buttonSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
|
||||
buttonLayout.addItem(buttonSpacer)
|
||||
buttonLayout.addWidget(self.refreshButton)
|
||||
|
||||
# 主布局
|
||||
mainLayout = QGridLayout(self)
|
||||
mainLayout.addLayout(buttonLayout, 0, 0, 1, 2)
|
||||
mainLayout.addWidget(self.recvLabel, 1, 0, 1, 1)
|
||||
mainLayout.addWidget(self.sendLabel, 1, 1, 1, 1)
|
||||
mainLayout.addWidget(self.recvEdit, 2, 0, 1, 1)
|
||||
mainLayout.addWidget(self.sendEdit, 2, 1, 1, 1)
|
||||
|
||||
def setupConnections(self):
|
||||
"""设置信号连接"""
|
||||
self.clearButton.clicked.connect(self.clearMessages)
|
||||
self.refreshButton.clicked.connect(self.toggleRefresh)
|
||||
|
||||
def updateMessages(self):
|
||||
"""更新报文显示"""
|
||||
try:
|
||||
protocolManage = Globals.getValue('protocolManage')
|
||||
if not protocolManage:
|
||||
return
|
||||
|
||||
# 获取 Modbus 报文
|
||||
messages = protocolManage.getModbusMessages()
|
||||
|
||||
# 清空并更新接收报文
|
||||
self.recvEdit.clear()
|
||||
for msg in messages['receive']:
|
||||
self.recvEdit.append(msg)
|
||||
|
||||
# 清空并更新发送报文
|
||||
self.sendEdit.clear()
|
||||
for msg in messages['send']:
|
||||
self.sendEdit.append(msg)
|
||||
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
def clearMessages(self):
|
||||
"""清空报文"""
|
||||
self.recvEdit.clear()
|
||||
self.sendEdit.clear()
|
||||
|
||||
def changeDecima(self):
|
||||
if self.decima == 16:
|
||||
self.decima = 10
|
||||
elif self.decima == 10:
|
||||
self.decima = 16
|
||||
|
||||
def stopRe(self):
|
||||
|
||||
try:
|
||||
protocolManage = Globals.getValue('protocolManage')
|
||||
if protocolManage:
|
||||
protocolManage.clearModbusMessages()
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
def toggleRefresh(self):
|
||||
"""切换刷新状态"""
|
||||
if self.timer.isActive():
|
||||
self.timer.stop()
|
||||
self.reButton.setText('开始刷新')
|
||||
self.refreshButton.setText('开始刷新')
|
||||
else:
|
||||
self.timer.start()
|
||||
self.reButton.setText('停止刷新')
|
||||
|
||||
def addText(self):
|
||||
try:
|
||||
self.recvEdit.clear()
|
||||
self.sendEdit.clear()
|
||||
if self.decima == 16:
|
||||
for x in app.backend.client.lrange("16R" , 0 , -1):
|
||||
text = str(x)[2:-1]
|
||||
self.recvEdit.append(text)
|
||||
for x in app.backend.client.lrange("16S" , 0 , -1):
|
||||
text = str(x)[2:-1]
|
||||
self.sendEdit.append(text)
|
||||
elif self.decima == 10:
|
||||
for x in app.backend.client.lrange("10R" , 0 , -1):
|
||||
text = str(x)[2:-1]
|
||||
self.recvEdit.append(text)
|
||||
for x in app.backend.client.lrange("10S" , 0 , -1):
|
||||
text = str(x)[2:-1]
|
||||
self.sendEdit.append(text)
|
||||
except:
|
||||
pass
|
||||
self.timer.start(1000)
|
||||
self.refreshButton.setText('停止刷新')
|
@ -0,0 +1,703 @@
|
||||
from utils.DBModels.ProtocolModel import TCPSetting, RTUSetting
|
||||
from .tcpmaster_example import TcpMaster
|
||||
from .rtumaster_example import RTUMaster
|
||||
from .tcpslave_example import TCPSlave
|
||||
from .rtuslave_example import RTUSlave
|
||||
from modbus_tk import hooks
|
||||
import threading
|
||||
import time
|
||||
|
||||
|
||||
class ModbusManager:
|
||||
"""Modbus 通讯管理器,负责管理所有 Modbus TCP/RTU 主站和从站"""
|
||||
|
||||
def __init__(self):
|
||||
# Modbus 通讯实例
|
||||
self.modbusTcpMaster = None
|
||||
self.modbusRtuMaster = None
|
||||
self.modbusTcpSlave = None
|
||||
self.modbusRtuSlave = None
|
||||
|
||||
# 线程管理
|
||||
self.modbusReadThreads = {} # 存储各个Modbus读取线程
|
||||
self.modbusStopEvents = {} # 存储各个Modbus停止事件
|
||||
self.modbusLocks = {} # 存储各个Modbus的锁
|
||||
|
||||
# 变量缓存回调
|
||||
self.variableValueCache = None
|
||||
self.cacheLock = None
|
||||
self.varInfoCache = None
|
||||
|
||||
# 报文记录
|
||||
self.messageHistory = {
|
||||
'send': [], # 发送的报文
|
||||
'receive': [] # 接收的报文
|
||||
}
|
||||
self.maxMessageCount = 100 # 最大保存报文数量
|
||||
self.messageLock = threading.Lock()
|
||||
|
||||
# 设置报文捕获 hooks
|
||||
self._setupHooks()
|
||||
|
||||
def setVariableCache(self, variableValueCache, cacheLock, varInfoCache):
|
||||
"""设置变量缓存引用"""
|
||||
self.variableValueCache = variableValueCache
|
||||
self.cacheLock = cacheLock
|
||||
self.varInfoCache = varInfoCache
|
||||
|
||||
# ==================== 启动/停止方法 ====================
|
||||
|
||||
def startModbusTcpMaster(self):
|
||||
"""启动 Modbus TCP 主站"""
|
||||
try:
|
||||
if self.modbusTcpMaster is not None:
|
||||
return True
|
||||
|
||||
# 从数据库获取TCP设置
|
||||
tcpSettings = self._getTcpSettings('master')
|
||||
if not tcpSettings:
|
||||
return False
|
||||
|
||||
# 创建TCP主站实例
|
||||
# print(tcpSettings['host'], tcpSettings['port'])
|
||||
self.modbusTcpMaster = TcpMaster(
|
||||
host=tcpSettings['host'],
|
||||
port=tcpSettings['port']
|
||||
)
|
||||
|
||||
# 启动读取线程
|
||||
self._startModbusReadThread('TCP_MASTER', tcpSettings['frequency'])
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"启动 Modbus TCP 主站失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def stopModbusTcpMaster(self):
|
||||
"""停止 Modbus TCP 主站"""
|
||||
try:
|
||||
if self.modbusTcpMaster is None:
|
||||
return True
|
||||
|
||||
# 停止读取线程
|
||||
self._stopModbusReadThread('TCP_MASTER')
|
||||
|
||||
# 清理主站实例
|
||||
self.modbusTcpMaster = None
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
def startModbusRtuMaster(self):
|
||||
"""启动 Modbus RTU 主站"""
|
||||
try:
|
||||
if self.modbusRtuMaster is not None:
|
||||
return True
|
||||
|
||||
# 从数据库获取RTU设置
|
||||
rtuSettings = self._getRtuSettings('master')
|
||||
if not rtuSettings:
|
||||
return False
|
||||
|
||||
# 创建RTU主站实例
|
||||
self.modbusRtuMaster = RTUMaster(
|
||||
port=rtuSettings['port'],
|
||||
baudrate=rtuSettings['baudrate'],
|
||||
bytesize=rtuSettings['byteSize'],
|
||||
parity=rtuSettings['parity'][0], # 取第一个字符
|
||||
stopbits=rtuSettings['stopbits']
|
||||
)
|
||||
|
||||
# 启动读取线程
|
||||
self._startModbusReadThread('RTU_MASTER', rtuSettings['frequency'])
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
def stopModbusRtuMaster(self):
|
||||
"""停止 Modbus RTU 主站"""
|
||||
try:
|
||||
if self.modbusRtuMaster is None:
|
||||
return True
|
||||
|
||||
# 停止读取线程
|
||||
self._stopModbusReadThread('RTU_MASTER')
|
||||
|
||||
# 清理主站实例
|
||||
self.modbusRtuMaster = None
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
def startModbusTcpSlave(self):
|
||||
"""启动 Modbus TCP 从站"""
|
||||
try:
|
||||
if self.modbusTcpSlave is not None:
|
||||
return True
|
||||
|
||||
# 从数据库获取TCP设置
|
||||
tcpSettings = self._getTcpSettings('slave')
|
||||
if not tcpSettings:
|
||||
return False
|
||||
|
||||
# 创建TCP从站实例
|
||||
self.modbusTcpSlave = TCPSlave(
|
||||
address=tcpSettings['host'],
|
||||
port=tcpSettings['port']
|
||||
)
|
||||
|
||||
# 添加默认从站ID
|
||||
self.modbusTcpSlave.addSlave(1)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
def stopModbusTcpSlave(self):
|
||||
"""停止 Modbus TCP 从站"""
|
||||
try:
|
||||
if self.modbusTcpSlave is None:
|
||||
return True
|
||||
|
||||
# 停止从站服务器
|
||||
if hasattr(self.modbusTcpSlave, 'server'):
|
||||
self.modbusTcpSlave.server.stop()
|
||||
|
||||
self.modbusTcpSlave = None
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
def startModbusRtuSlave(self):
|
||||
"""启动 Modbus RTU 从站"""
|
||||
try:
|
||||
if self.modbusRtuSlave is not None:
|
||||
return True
|
||||
|
||||
# 从数据库获取RTU设置
|
||||
rtuSettings = self._getRtuSettings('slave')
|
||||
if not rtuSettings:
|
||||
return False
|
||||
|
||||
# 创建RTU从站实例
|
||||
self.modbusRtuSlave = RTUSlave(
|
||||
port=rtuSettings['port'],
|
||||
baudrate=rtuSettings['baudrate'],
|
||||
bytesize=rtuSettings['byteSize'],
|
||||
parity=rtuSettings['parity'][0], # 取第一个字符
|
||||
stopbits=rtuSettings['stopbits']
|
||||
)
|
||||
|
||||
# 启动从站服务器
|
||||
self.modbusRtuSlave.start()
|
||||
|
||||
# 添加默认从站ID
|
||||
self.modbusRtuSlave.addSlave(1)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
def stopModbusRtuSlave(self):
|
||||
"""停止 Modbus RTU 从站"""
|
||||
try:
|
||||
if self.modbusRtuSlave is None:
|
||||
return True
|
||||
|
||||
# 停止从站服务器
|
||||
if hasattr(self.modbusRtuSlave, 'server'):
|
||||
self.modbusRtuSlave.server.stop()
|
||||
|
||||
self.modbusRtuSlave = None
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
def stopAllModbus(self):
|
||||
"""停止所有 Modbus 通讯"""
|
||||
results = []
|
||||
results.append(self.stopModbusTcpMaster())
|
||||
results.append(self.stopModbusRtuMaster())
|
||||
results.append(self.stopModbusTcpSlave())
|
||||
results.append(self.stopModbusRtuSlave())
|
||||
return all(results)
|
||||
|
||||
def getModbusStatus(self):
|
||||
"""获取所有 Modbus 通讯状态"""
|
||||
return {
|
||||
'tcpMaster': self.modbusTcpMaster is not None,
|
||||
'rtuMaster': self.modbusRtuMaster is not None,
|
||||
'tcpSlave': self.modbusTcpSlave is not None,
|
||||
'rtuSlave': self.modbusRtuSlave is not None
|
||||
}
|
||||
|
||||
# ==================== 读写方法 ====================
|
||||
|
||||
def writeModbusTcpMasterValue(self, info, value):
|
||||
"""写入TCP主站变量值"""
|
||||
try:
|
||||
if self.modbusTcpMaster is None:
|
||||
return False
|
||||
|
||||
slaveId = int(info['slaveID'])
|
||||
address = int(info['address'])
|
||||
varType = int(info['varType'])
|
||||
order = info['order']
|
||||
|
||||
return self._writeModbusValue(self.modbusTcpMaster, slaveId, address, varType, value, order)
|
||||
|
||||
except Exception as e:
|
||||
print(f"写入TCP主站变量失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def writeModbusRtuMasterValue(self, info, value):
|
||||
"""写入RTU主站变量值"""
|
||||
try:
|
||||
if self.modbusRtuMaster is None:
|
||||
return False
|
||||
|
||||
slaveId = int(info['slaveID'])
|
||||
address = int(info['address'])
|
||||
varType = int(info['varType'])
|
||||
order = info['order']
|
||||
|
||||
return self._writeModbusValue(self.modbusRtuMaster, slaveId, address, varType, value, order)
|
||||
|
||||
except Exception as e:
|
||||
print(f"写入RTU主站变量失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def writeModbusTcpSlaveValue(self, info, value):
|
||||
"""写入TCP从站变量值"""
|
||||
try:
|
||||
if self.modbusTcpSlave is None:
|
||||
print("Modbus TCP 从站未启动")
|
||||
return False
|
||||
|
||||
slaveId = int(info['slaveID'])
|
||||
address = int(info['address'])
|
||||
varType = int(info['varType'])
|
||||
order = info['order']
|
||||
|
||||
return self._writeModbusSlaveValue(self.modbusTcpSlave, slaveId, address, varType, value, order)
|
||||
|
||||
except Exception as e:
|
||||
print(f"写入TCP从站变量失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def writeModbusRtuSlaveValue(self, info, value):
|
||||
"""写入RTU从站变量值"""
|
||||
try:
|
||||
if self.modbusRtuSlave is None:
|
||||
print("Modbus RTU 从站未启动")
|
||||
return False
|
||||
|
||||
slaveId = int(info['slaveID'])
|
||||
address = int(info['address'])
|
||||
varType = int(info['varType'])
|
||||
order = info['order']
|
||||
|
||||
return self._writeModbusSlaveValue(self.modbusRtuSlave, slaveId, address, varType, value, order)
|
||||
|
||||
except Exception as e:
|
||||
print(f"写入RTU从站变量失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def readModbusTcpMasterValue(self, info):
|
||||
"""读取TCP主站变量值"""
|
||||
try:
|
||||
if self.modbusTcpMaster is None:
|
||||
return None
|
||||
|
||||
slaveId = int(info['slaveID'])
|
||||
address = int(info['address'])
|
||||
varType = int(info['varType'])
|
||||
order = info['order']
|
||||
|
||||
return self._readModbusValue(self.modbusTcpMaster, slaveId, address, varType, order)
|
||||
|
||||
except Exception as e:
|
||||
print(f"读取TCP主站变量失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def readModbusRtuMasterValue(self, info):
|
||||
"""读取RTU主站变量值"""
|
||||
try:
|
||||
if self.modbusRtuMaster is None:
|
||||
return None
|
||||
|
||||
slaveId = int(info['slaveID'])
|
||||
address = int(info['address'])
|
||||
varType = int(info['varType'])
|
||||
order = info['order']
|
||||
|
||||
return self._readModbusValue(self.modbusRtuMaster, slaveId, address, varType, order)
|
||||
|
||||
except Exception as e:
|
||||
print(f"读取RTU主站变量失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def readModbusTcpSlaveValue(self, info):
|
||||
"""读取TCP从站变量值"""
|
||||
try:
|
||||
if self.modbusTcpSlave is None:
|
||||
return None
|
||||
|
||||
slaveId = int(info['slaveID'])
|
||||
address = int(info['address'])
|
||||
varType = int(info['varType'])
|
||||
order = info['order']
|
||||
|
||||
return self._readModbusSlaveValue(self.modbusTcpSlave, slaveId, address, varType, order)
|
||||
|
||||
except Exception as e:
|
||||
print(f"读取TCP从站变量失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def readModbusRtuSlaveValue(self, info):
|
||||
"""读取RTU从站变量值"""
|
||||
try:
|
||||
if self.modbusRtuSlave is None:
|
||||
return None
|
||||
|
||||
slaveId = int(info['slaveID'])
|
||||
address = int(info['address'])
|
||||
varType = int(info['varType'])
|
||||
order = info['order']
|
||||
|
||||
return self._readModbusSlaveValue(self.modbusRtuSlave, slaveId, address, varType, order)
|
||||
|
||||
except Exception as e:
|
||||
print(f"读取RTU从站变量失败: {str(e)}")
|
||||
return None
|
||||
|
||||
# ==================== 私有方法 ====================
|
||||
|
||||
def _getTcpSettings(self, tcpType):
|
||||
"""从数据库获取TCP设置"""
|
||||
try:
|
||||
setting = TCPSetting.select().where(TCPSetting.tcpType == tcpType).first()
|
||||
if setting:
|
||||
return {
|
||||
'host': setting.host,
|
||||
'port': setting.port,
|
||||
'frequency': float(setting.frequency),
|
||||
'offset': setting.offset
|
||||
}
|
||||
else:
|
||||
# 如果没有设置,创建默认设置
|
||||
newSetting = TCPSetting()
|
||||
newSetting.initSetting(tcpType)
|
||||
return {
|
||||
'host': newSetting.host,
|
||||
'port': newSetting.port,
|
||||
'frequency': float(newSetting.frequency),
|
||||
'offset': newSetting.offset
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"获取TCP设置失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def _getRtuSettings(self, tcpType):
|
||||
"""从数据库获取RTU设置"""
|
||||
try:
|
||||
setting = RTUSetting.select().where(RTUSetting.tcpType == tcpType).first()
|
||||
if setting:
|
||||
return {
|
||||
'port': setting.port,
|
||||
'byteSize': setting.byteSize,
|
||||
'baudrate': setting.baudrate,
|
||||
'stopbits': setting.stopbits,
|
||||
'parity': setting.parity,
|
||||
'frequency': float(setting.frequency),
|
||||
'offset': setting.offset
|
||||
}
|
||||
else:
|
||||
# 如果没有设置,创建默认设置
|
||||
newSetting = RTUSetting()
|
||||
newSetting.initSetting(tcpType)
|
||||
return {
|
||||
'port': newSetting.port,
|
||||
'byteSize': newSetting.byteSize,
|
||||
'baudrate': newSetting.baudrate,
|
||||
'stopbits': newSetting.stopbits,
|
||||
'parity': newSetting.parity,
|
||||
'frequency': float(newSetting.frequency),
|
||||
'offset': newSetting.offset
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"获取RTU设置失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def _startModbusReadThread(self, protocolType, frequency):
|
||||
"""启动Modbus读取线程"""
|
||||
if protocolType in self.modbusReadThreads:
|
||||
return
|
||||
|
||||
stopEvent = threading.Event()
|
||||
lock = threading.Lock()
|
||||
|
||||
self.modbusStopEvents[protocolType] = stopEvent
|
||||
self.modbusLocks[protocolType] = lock
|
||||
|
||||
readThread = threading.Thread(
|
||||
target=self._modbusReadWorker,
|
||||
args=(protocolType, frequency, stopEvent, lock),
|
||||
daemon=True
|
||||
)
|
||||
|
||||
self.modbusReadThreads[protocolType] = readThread
|
||||
readThread.start()
|
||||
|
||||
def _stopModbusReadThread(self, protocolType):
|
||||
"""停止Modbus读取线程"""
|
||||
if protocolType in self.modbusStopEvents:
|
||||
self.modbusStopEvents[protocolType].set()
|
||||
|
||||
if protocolType in self.modbusReadThreads:
|
||||
self.modbusReadThreads[protocolType].join(timeout=2)
|
||||
del self.modbusReadThreads[protocolType]
|
||||
|
||||
if protocolType in self.modbusStopEvents:
|
||||
del self.modbusStopEvents[protocolType]
|
||||
|
||||
if protocolType in self.modbusLocks:
|
||||
del self.modbusLocks[protocolType]
|
||||
|
||||
def _modbusReadWorker(self, protocolType, frequency, stopEvent, lock):
|
||||
"""Modbus读取工作线程"""
|
||||
interval = 1.0 / frequency if frequency > 0 else 1.0
|
||||
|
||||
while not stopEvent.is_set():
|
||||
try:
|
||||
with lock:
|
||||
self._readModbusVariables(protocolType)
|
||||
except Exception as e:
|
||||
print(f"Modbus读取线程错误 ({protocolType}): {str(e)}")
|
||||
|
||||
time.sleep(interval)
|
||||
|
||||
def _readModbusVariables(self, protocolType):
|
||||
"""读取指定协议类型的所有Modbus变量"""
|
||||
if protocolType == 'TCP_MASTER':
|
||||
self._readTcpMasterVariables()
|
||||
elif protocolType == 'RTU_MASTER':
|
||||
self._readRtuMasterVariables()
|
||||
|
||||
def _readTcpMasterVariables(self):
|
||||
"""读取TCP主站变量"""
|
||||
if self.modbusTcpMaster is None or self.varInfoCache is None:
|
||||
return
|
||||
|
||||
for varName, varInfo in self.varInfoCache.items():
|
||||
if varInfo['modelType'] == 'ModbusTcpMasterVar':
|
||||
try:
|
||||
info = varInfo['variableData']
|
||||
slaveId = int(info['slaveID'])
|
||||
address = int(info['address'])
|
||||
varType = int(info['varType'])
|
||||
order = info['order']
|
||||
|
||||
value = self._readModbusValue(self.modbusTcpMaster, slaveId, address, varType, order)
|
||||
if value != 'error' and self.variableValueCache is not None and self.cacheLock is not None:
|
||||
with self.cacheLock:
|
||||
self.variableValueCache[varName] = value
|
||||
|
||||
except Exception as e:
|
||||
print(f"读取TCP主站变量失败 {varName}: {str(e)}")
|
||||
|
||||
def _readRtuMasterVariables(self):
|
||||
"""读取RTU主站变量"""
|
||||
if self.modbusRtuMaster is None or self.varInfoCache is None:
|
||||
return
|
||||
|
||||
for varName, varInfo in self.varInfoCache.items():
|
||||
if varInfo['modelType'] == 'ModbusRtuMasterVar':
|
||||
try:
|
||||
info = varInfo['variableData']
|
||||
slaveId = int(info['slaveID'])
|
||||
address = int(info['address'])
|
||||
varType = int(info['varType'])
|
||||
order = info['order']
|
||||
|
||||
value = self._readModbusValue(self.modbusRtuMaster, slaveId, address, varType, order)
|
||||
if value != 'error' and self.variableValueCache is not None and self.cacheLock is not None:
|
||||
with self.cacheLock:
|
||||
self.variableValueCache[varName] = value
|
||||
|
||||
except Exception as e:
|
||||
print(f"读取RTU主站变量失败 {varName}: {str(e)}")
|
||||
|
||||
def _readModbusValue(self, master, slaveId, address, varType, order):
|
||||
"""通用Modbus值读取方法"""
|
||||
try:
|
||||
# varType: 1=线圈, 2=离散输入, 3=输入寄存器, 4=保持寄存器
|
||||
if varType == 0: # 线圈
|
||||
return master.readCoils(slaveId, address, 1)
|
||||
elif varType == 1: # 离散输入
|
||||
return master.readInputCoils(slaveId, address, 1)
|
||||
elif varType == 3: # 输入寄存器
|
||||
return master.readInputRegisters(slaveId, address, 1, order)
|
||||
elif varType == 4: # 保持寄存器
|
||||
return master.readHoldingRegisters(slaveId, address, 1, order)
|
||||
else:
|
||||
return 'error'
|
||||
except Exception as e:
|
||||
print(f"读取Modbus值失败: {str(e)}")
|
||||
return 'error'
|
||||
|
||||
def _writeModbusValue(self, master, slaveId, address, varType, value, order):
|
||||
"""通用Modbus主站值写入方法"""
|
||||
try:
|
||||
# varType: 0=线圈, 1=离散输入, 3=输入寄存器, 4=保持寄存器
|
||||
if varType == 0: # 线圈
|
||||
result = master.writeSingleCoil(slaveId, address, value)
|
||||
return result != 'error'
|
||||
elif varType == 4: # 保持寄存器
|
||||
result = master.writeSingleRegister(slaveId, address, value, order)
|
||||
return result != 'error'
|
||||
else:
|
||||
print(f"不支持写入的变量类型: {varType}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"写入Modbus主站值失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def _writeModbusSlaveValue(self, slave, slaveId, address, varType, value, order):
|
||||
"""通用Modbus从站值写入方法"""
|
||||
try:
|
||||
# 根据变量类型确定存储区名称
|
||||
blockName = self._getModbusBlockName(varType, address)
|
||||
if not blockName:
|
||||
return False
|
||||
|
||||
# 转换地址为存储区内部地
|
||||
|
||||
slave.setValue(slaveId, blockName, address, value, order)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"写入Modbus从站值失败: {str(e)}")
|
||||
return False
|
||||
|
||||
def _readModbusSlaveValue(self, slave, slaveId, address, varType, order):
|
||||
"""通用Modbus从站值读取方法"""
|
||||
try:
|
||||
# 根据变量类型确定存储区名称
|
||||
blockName = self._getModbusBlockName(varType, address)
|
||||
if not blockName:
|
||||
return None
|
||||
|
||||
return slave.readValue(slaveId, blockName, address, order)
|
||||
|
||||
except Exception as e:
|
||||
print(f"读取Modbus从站值失败: {str(e)}")
|
||||
return None
|
||||
|
||||
def _getModbusBlockName(self, varType, address):
|
||||
"""根据变量类型和地址获取Modbus存储区名称"""
|
||||
# varType: 0=线圈, 1=离散输入, 3=输入寄存器, 4=保持寄存器
|
||||
if varType == 0: # 线圈 (0-9999)
|
||||
return '0'
|
||||
elif varType == 1: # 离散输入 (10000-19999)
|
||||
return '1'
|
||||
elif varType == 3: # 输入寄存器 (30000-39999)
|
||||
return '3'
|
||||
elif varType == 4: # 保持寄存器 (40000-49999)
|
||||
return '4'
|
||||
else:
|
||||
print(f"未知的变量类型: {varType}")
|
||||
return None
|
||||
|
||||
|
||||
|
||||
# ==================== 报文记录方法 ====================
|
||||
|
||||
def _setupHooks(self):
|
||||
"""设置 modbus_tk hooks 来捕获报文"""
|
||||
try:
|
||||
def captureRequest(args):
|
||||
"""捕获请求报文数据"""
|
||||
try:
|
||||
if args and len(args) > 0:
|
||||
# args[-1] 通常包含报文数据
|
||||
data = args[-1]
|
||||
if isinstance(data, (list, tuple)):
|
||||
hexData = ' '.join([f'{b:02X}' for b in data])
|
||||
elif isinstance(data, bytes):
|
||||
hexData = ' '.join([f'{b:02X}' for b in data])
|
||||
else:
|
||||
hexData = str(data)
|
||||
self._addMessage('receive', f"接收请求: {hexData}")
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
def captureResponse(args):
|
||||
"""捕获响应报文数据"""
|
||||
try:
|
||||
if args and len(args) > 0:
|
||||
# args[-1] 通常包含报文数据
|
||||
data = args[-1]
|
||||
if isinstance(data, (list, tuple)):
|
||||
hexData = ' '.join([f'{b:02X}' for b in data])
|
||||
elif isinstance(data, bytes):
|
||||
hexData = ' '.join([f'{b:02X}' for b in data])
|
||||
else:
|
||||
hexData = str(data)
|
||||
self._addMessage('send', f"发送响应: {hexData}")
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
# 使用正确的 hook 名称
|
||||
hooks.install_hook('modbus.Server.before_handle_request', captureRequest)
|
||||
hooks.install_hook('modbus.Server.after_handle_request', captureResponse)
|
||||
|
||||
except Exception as e:
|
||||
print(f"设置 hooks 失败: {e}")
|
||||
# 如果 hooks 设置失败,不影响正常通讯功能
|
||||
pass
|
||||
|
||||
def _addMessage(self, messageType, content):
|
||||
"""添加报文记录"""
|
||||
try:
|
||||
with self.messageLock:
|
||||
timestamp = time.strftime('%H:%M:%S')
|
||||
message = f"[{timestamp}] {content}"
|
||||
|
||||
if messageType == 'send':
|
||||
self.messageHistory['send'].append(message)
|
||||
if len(self.messageHistory['send']) > self.maxMessageCount:
|
||||
self.messageHistory['send'].pop(0)
|
||||
elif messageType == 'receive':
|
||||
self.messageHistory['receive'].append(message)
|
||||
if len(self.messageHistory['receive']) > self.maxMessageCount:
|
||||
self.messageHistory['receive'].pop(0)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
def getMessages(self):
|
||||
"""获取报文信息"""
|
||||
try:
|
||||
with self.messageLock:
|
||||
return {
|
||||
'send': self.messageHistory['send'].copy(),
|
||||
'receive': self.messageHistory['receive'].copy()
|
||||
}
|
||||
except Exception as e:
|
||||
return {'send': [], 'receive': []}
|
||||
|
||||
def clearMessages(self):
|
||||
"""清空报文记录"""
|
||||
try:
|
||||
with self.messageLock:
|
||||
self.messageHistory['send'].clear()
|
||||
self.messageHistory['receive'].clear()
|
||||
except Exception as e:
|
||||
pass
|
Loading…
Reference in New Issue