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.
DCS/UI/VarManages/ModbusModel.py

507 lines
20 KiB
Python

2 years ago
import typing
import sys
import qtawesome
from PyQt5 import QtGui,QtCore,QtWidgets
from PyQt5.QtCore import QAbstractTableModel, QModelIndex, Qt, QVariant, QSize
from PyQt5.QtWidgets import QItemDelegate, QHBoxLayout, QWidget, QTableView, QMessageBox, QComboBox
from protocol.Celery.MBTCPMaster import app as MBTCPMApp
from protocol.Celery.MBRTUMaster import app as MBRTUMApp
from protocol.Celery.MBRTUSlave import app as MBRTUSApp
from protocol.Celery.MBTCPSlave import app as MBTCPSApp
# from protocol.Celery.MBTCPMaster.MBTCPMTask import setValue as setMTcpValue
# from protocol.Celery.MBRTUMaster.MBRTUMTask import setValue as setMRTUValue
from celery.schedules import crontab, timedelta
from celery.result import AsyncResult, GroupResult
from ..TrendManage.ActualTrendWidget import ActualTrend
from protocol.TCP.TemToMv import temToMv
from protocol.TCP.Analog import getRealAO
from model.ProjectModel.VarManage import *
from utils import Globals
import re
sys.path.append('protocol/Celery/MBTCPMaster')
class QPushButton(QtWidgets.QPushButton):
def __init__(self, *__args: any,clicked = None):
super(QPushButton,self).__init__(*__args)
self.setCursor(Qt.PointingHandCursor)
self.setIconSize(QSize(25, 25))
class VarTableModel(QAbstractTableModel):
''' 变量表模型类'''
def __init__(self, header, data: list, table = None):
'''
header : 表头变量
data : 表格内容
table : 缺省参数
'''
QAbstractTableModel.__init__(self, parent=None)
self.header = header
self.datas = data
self.checkList = ['Unchecked'] * len(self.datas)
self.supportedDragActions()
self.table = table
self.editableList = [] # 表格中可编辑项
def initTable(self):
self.datas = []
2 years ago
self.table.parent.initIcon()
2 years ago
proType = Globals.getValue('currentProType')
if proType in ['1', '2', '3', '0']:
varDatas = ModbusVarManage.getAllVar()
if not varDatas:
# self.layoutChanged.emit()
self.table.proxy.invalidate()
return
for x in varDatas:
x.insert(1, '')
x.insert(2, '')
self.datas.append(x)
self.checkList = ['Unchecked'] * len(self.datas)
# self.layoutChanged.emit()
self.table.proxy.invalidate()
def append_data(self, x):
self.datas.append(x)
self.checkList = ['Unchecked'] * len(self.datas)
self.table.proxy.invalidate()
# self.layoutChanged.emit()
def insert_data(self, x, index):
self.datas.insert(index, x)
self.checkList = ['Unchecked'] * len(self.datas)
self.table.proxy.invalidate()
def remove_row(self, row):
self.datas.pop(row)
self.checkList = ['UnChecked'] * len(self.datas)
self.table.proxy.invalidate()
def rowCount(self, parent: QModelIndex = ...) -> int:
if len(self.datas) > 0:
return len(self.datas)
return 0
def columnCount(self, parent: QModelIndex = ...) -> int:
return len(self.header)
def data(self, QModelIndex, role=None):
# print(Qt.__dict__.items())
if role == Qt.TextAlignmentRole:
return Qt.AlignCenter
if not QModelIndex.isValid():
print("行或者列有问题")
return QVariant()
if role == Qt.BackgroundColorRole:
if QModelIndex.row() % 2 == 0 and self.datas[QModelIndex.row()][3] not in Globals.getValue('forceVars'):
return QtGui.QColor('#EFEFEF')
if self.datas[QModelIndex.row()][3] in Globals.getValue('forceVars'):
return QtGui.QColor('#00FF7F')
if role == Qt.TextColorRole:
return QtGui.QColor('#1A1A1A')
if role == Qt.CheckStateRole:
if QModelIndex.column() == 0:
return Qt.Checked if self.checkList[QModelIndex.row()] == 'Checked' else Qt.Unchecked
else:
return None
if role == Qt.ToolTipRole:
if QModelIndex.column() == 0:
return self.checkList[QModelIndex.row()]
if role == Qt.DisplayRole or role == Qt.EditRole:
if QModelIndex.row() in self.editableList:
return self.datas[QModelIndex.row()][QModelIndex.column()]
if role != Qt.DisplayRole:
return QVariant()
# 获取变量值并插入表格
if QModelIndex.column() == 2:
varName = self.datas[QModelIndex.row()][3]
if varName != '':
# print(uid)
try:
uid = MBTCPMApp.backend.get('ModBus').decode('utf-8')
res = AsyncResult(uid) # 参数为task id
if res.result:
# print(res.result, res.date_done)
result = res.result[varName]
if result or result in [0, '0']:
self.datas[QModelIndex.row()][QModelIndex.column()] = result
except:
pass
return QVariant(self.datas[QModelIndex.row()][QModelIndex.column()])
def headerData(self, section: int, orientation: Qt.Orientation, role: int = ...) -> typing.Any: # 表头
if role != Qt.DisplayRole:
return None
if orientation == Qt.Horizontal:
return self.header[section]
def setData(self, index, value, role):
row = index.row()
col = index.column()
if role == Qt.CheckStateRole and col == 0:
self.checkList[row] = 'Checked' if value == Qt.Checked else 'Unchecked'
return True
if role == Qt.EditRole:
self.datas[row][col] = value
return True
return True
def flags(self, index):
if index.column() == 0:
return Qt.ItemIsEnabled | Qt.ItemIsUserCheckable
if index.row() in self.editableList and index.column() or index.column() == 1:
return Qt.ItemIsEnabled | Qt.ItemIsUserCheckable | Qt.ItemIsEditable
return Qt.ItemIsEnabled
def headerClick(self, isOn):
self.beginResetModel()
if isOn:
self.checkList = []
self.checkList = ['Checked'] * len(self.datas)
else:
self.checkList = []
self.checkList = ['UnChecked'] * len(self.datas)
self.endResetModel()
def dragMoveEvent(self, event):
event.setDropAction(QtCore.Qt.MoveAction)
event.accept()
def moveRow(self, sourceParent: QModelIndex, sourceRow: int, destinationParent: QModelIndex,
destinationChild: int) -> bool:
if self.datas[destinationChild] == self.datas[sourceRow]:
return
self.datas[sourceRow], self.datas[destinationChild] = self.datas[destinationChild], self.datas[sourceRow]
self.table.proxy.invalidate()
2 years ago
def refreshComboBox(self):
for i in range(len(self.datas)):
cbRow = str('cb' + str(i) + '5')
index = self.index(i, 5)
delegate = self.table.itemDelegate(index)
comboBox = getattr(delegate, cbRow)
if self.datas[i][5] in [0, 1]:
comboBox.setCurrentIndex(self.datas[i][5])
elif self.datas[i][5] in [3, 4]:
comboBox.setCurrentIndex(self.datas[i][5] - 1)
else:
comboBox.setCurrentIndex(-1)
2 years ago
class ModBusTCPSlaveModel(VarTableModel):
def __init__(self, header, data: list, table = None):
'''
header : 表头变量
data : 表格内容
table : 缺省参数
'''
VarTableModel.__init__(self, header, data, table = table)
class VarButtonDelegate(QItemDelegate):
"""该类用于向单元格中添加按钮 任务表格"""
def __init__(self, parent=None):
super(VarButtonDelegate, self).__init__(parent)
def paint(self, painter, option, index):
if not self.parent().indexWidget(index):
button1 = QPushButton(
qtawesome.icon('fa.play', color='#1fbb6f'),
"",
self.parent(),
clicked=self.start_action
)
# button1.setIconSize(QSize(15, 15))
# button1.setStyleSheet("border:none;")
button2 = QPushButton(
qtawesome.icon('fa.pencil', color='#4c8cf2'),
"",
self.parent(),
clicked=self.edit_action
)
# button2.setStyleSheet("border:none;")
button3 = QPushButton(
qtawesome.icon('fa.trash', color='#ff6d6d'),
"",
self.parent(),
clicked=self.delete_action
)
# button3.setStyleSheet("border:none;")
button4 = QPushButton(
qtawesome.icon('fa.line-chart', color='#393c4e'),
"",
self.parent(),
clicked=self.trend_action
)
comboBox = QComboBox(self.parent())
comboBox.addItem('int', 0)
comboBox.addItem('ABCD', 1)
comboBox.addItem('CDAB', 2)
comboBox.addItem('BADC', 3)
comboBox.addItem('DCBA', 4)
comboBox.setCurrentText(self.parent().model.datas[index.row()][index.column()])
comboBox.currentIndexChanged.connect(self.indexChange)
button1.clicked.connect(self.start_action)
button2.clicked.connect(self.edit_action)
button3.clicked.connect(self.delete_action)
button4.clicked.connect(self.trend_action)
button2.oldName = False
button2.isEdit = True
# button4.setStyleSheet("border:none;")
button1.index = [index.row(), index.column()]
button2.index = [index.row(), index.column()]
button3.index = [index.row(), index.column()]
button4.index = [index.row(), index.column()]
comboBox.index = [index.row(), index.column()]
data = self.parent().model.datas[index.row()]
for x in data[:-1]:
if x != '':
break
else:
button2.isEdit = False
button2.setIcon(qtawesome.icon('fa.save', color='#1fbb6f'))
self.parent().model.editableList.append(button2.index[0])
h_box_layout = QHBoxLayout()
h_box_layout.addWidget(comboBox)
h_box_layout.addWidget(button1)
h_box_layout.addWidget(button2)
h_box_layout.addWidget(button3)
h_box_layout.addWidget(button4)
h_box_layout.setContentsMargins(2, 0, 0, 2)
h_box_layout.setAlignment(Qt.AlignCenter)
widget = QWidget()
widget.setLayout(h_box_layout)
self.parent().setIndexWidget(
index,
widget
)
# widget.setStyleSheet('''
# QWidget{background : #ffffff;
# margin-bottom:4px;
# margin-top:4px;}''')
def indexChange(self):
sender = self.sender()
index = sender.currentIndex()
text = sender.itemText(index)
name = self.parent().model.datas[sender.index[0]][3]
ModbusVarManage.editOrder(name, text)
self.parent().model.datas[sender.index[0]][sender.index[1]] = text
def start_action(self):
sender = self.sender()
model = self.parent().model
varMes = model.datas[sender.index[0]]
value, name, des, varType, slaveID, address, min, max, order = str(varMes[1]), str(varMes[3]), str(varMes[4]), str(varMes[5]), str(varMes[6]), str(varMes[7]), str(varMes[8]), str(varMes[9]), str(varMes[10])
pattern = re.compile(r'[^0-9\.-]+')
if not value or re.findall(pattern, str(value)):
reply = QMessageBox.question(self.parent(),
'警告',
"请输入强制值或数字",
QMessageBox.Yes)
return
if min and max:
if float(value) < float(min) or float(value) > float(max):
reply = QMessageBox.question(self.parent(),
'警告',
"超出量程范围",
QMessageBox.Yes)
return
elif min and not max:
if float(value) < float(min):
reply = QMessageBox.question(self.parent(),
'警告',
"超出量程范围",
QMessageBox.Yes)
return
elif max and not min:
if float(value) > float(max):
reply = QMessageBox.question(self.parent(),
'警告',
"超出量程范围",
QMessageBox.Yes)
return
# 0 : MODBUSTCP 主站模式
# 1 : MODBUSTCP 从站模式
# 2 : MODBUSRTU 主站模式
# 3 : MODBUSRTU 从站模式
proType = Globals.getValue('currentProType')
forceVars = Globals.getValue('forceVars')
forceVars.add(name)
Globals.setValue('forceVars', forceVars)
# print(Globals.getValue('forceVars'))
if proType == '0':
MBTCPMApp.send_task('protocol.Celery.MBTCPMaster.MBTCPMTask.setValue',args=[name, varType, slaveID, address, value, order])
if proType == '1':
MBTCPSApp.send_task('protocol.Celery.MBTCPSlave.MBTCPSTask.setValue',args=[name, varType, slaveID, address, value])
elif proType == '2':
MBRTUMApp.send_task('protocol.Celery.MBRTUMaster.MBRTUMTask.setValue',args=[name, varType, slaveID, address, value, order])
elif proType == '3':
MBRTUSApp.send_task('protocol.Celery.MBRTUSlave.MBRTUSTask.setValue',args=[name, varType, slaveID, address, value])
def edit_action(self):
sender = self.sender()
model = self.parent().model
cbRow = str('cb' + str(sender.index[0]) + str(5))
index = self.parent().model.index(sender.index[0], 5)
delegate = self.parent().itemDelegate(index)
checkbox = getattr(delegate, cbRow)
if sender.isEdit:
sender.setIcon(qtawesome.icon('fa.save', color='#1fbb6f'))
sender.isEdit = False
sender.oldName = model.datas[sender.index[0]][3]
model.editableList.append(sender.index[0])
checkbox.setEnabled(True)
else:
varMes = model.datas[sender.index[0]]
name, des, varType, slaveID, address, min, max, order = str(varMes[3]), str(varMes[4]), str(varMes[5]), str(varMes[6]), str(varMes[7]), str(varMes[8]), str(varMes[9]), str(varMes[-1])
2 years ago
# print(name, des, varType, slaveID, address, min, max, order)
2 years ago
if not name or not varType:
reply = QMessageBox.question(self.parent(),
'警告',
"有字段为空",
QMessageBox.Yes)
return
2 years ago
2 years ago
2 years ago
if sender.oldName and ModBusVar.getByName(sender.oldName):
if sender.oldName == name or [varMes[3] for varMes in model.datas].count(name) < 2:
ModbusVarManage.editVar(name = sender.oldName, Nname = name, des = des, varType = varType, slaveID = slaveID, address = address, min = min, max = max, order = order)
else:
QMessageBox.information(self.parent(), '提示', '已有同名变量')
return
2 years ago
else:
2 years ago
res = ModbusVarManage.createVar(varName = name, varType = varType, des = des, address = address, slaveID = slaveID, min = min, max = max, order = order)
if res:
QMessageBox.information(self.parent(), '提示', '已有同名变量')
return
2 years ago
sender.setIcon(qtawesome.icon('fa.pencil', color='#4c8cf2'))
rowIndex = sender.index[0]
varMes = ModbusVarManage.getByName(name)
varMes.append('int')
varMes.insert(1, '')
varMes.insert(2, '')
model.insert_data(varMes, rowIndex)
model.remove_row(rowIndex + 1)
2 years ago
sender.isEdit = True
checkbox.setEnabled(False)
model.editableList.remove(sender.index[0])
2 years ago
def delete_action(self):
sender = self.sender()
model = self.parent().model
name = str(model.datas[sender.index[0]][3])
ModbusVarManage.deleteVar(name = name)
model.remove_row(sender.index[0])
def trend_action(self):
model = self.parent().model
2 years ago
if model.table.parent._isPopenOpen:
sender = self.sender()
name = str(model.datas[sender.index[0]][3])
trend = ActualTrend(varName = name)
trend.show()
2 years ago
class ModbusTypeBox(VarButtonDelegate):
def __init__(self, parent=None):
super(ModbusTypeBox, self).__init__(parent)
def paint(self, painter, option, index):
if not self.parent().indexWidget(index):
data = self.parent().model.datas[index.row()]
comBox = str('cb' + str(index.row()) + str(index.column()))
setattr(self, comBox, QComboBox(self.parent()))
comboBox = getattr(self, comBox)
item = ['Coil Status', 'Input Status', 'Input Register', 'Holding Register']
comboBox.addItems(item)
# comboBox.setItemData(0, 'Coil Status')
# comboBox.setItemData(1, 'Input Status')
# comboBox.setItemData(2, '3', role = Qt.UserRole)
# comboBox.setItemData(3, '4', role = Qt.UserRole)
if self.parent().model.datas[index.row()][index.column()] in [0, 1]:
comboBox.setCurrentIndex(self.parent().model.datas[index.row()][index.column()])
elif self.parent().model.datas[index.row()][index.column()] in [3, 4]:
comboBox.setCurrentIndex(self.parent().model.datas[index.row()][index.column()] - 1)
else:
comboBox.setCurrentIndex(-1)
# self.parent().model.comboboxList.append(comboBox)
comboBox.currentIndexChanged.connect(self.indexChange)
comboBox.setObjectName('ModbusTypeBox')
comboBox.setEditable(True)
comboBox.lineEdit().setAlignment(Qt.AlignCenter)
# comboBox.setEnabled(False)
h_box_layout = QHBoxLayout()
h_box_layout.addWidget(comboBox)
h_box_layout.setContentsMargins(0, 0, 0, 0)
# h_box_layout.setAlignment(Qt.AlignCenter)
comboBox.index = [index.row(), index.column()]
row = index.row()
if row % 2 == 0:
comboBox.setStyleSheet("QComboBox { background-color: #EFEFEF; height: 40px; }")
else:
comboBox.setStyleSheet("QComboBox { background-color: #e9e7e3; height: 40px; }")
if str(data[index.column()]):
comboBox.setEnabled(False)
else:
comboBox.setEnabled(True)
widget = QWidget()
widget.setLayout(h_box_layout)
self.parent().setIndexWidget(
index,
widget
)
def indexChange(self):
sender = self.sender()
index = sender.currentIndex()
if index in [2, 3]:
index += 1
text = sender.itemText(index)
self.parent().model.datas[sender.index[0]][sender.index[1]] = index