873 lines
47 KiB
Python
873 lines
47 KiB
Python
import json
|
||
import os
|
||
import uuid
|
||
import base64
|
||
from PyQt6 import *
|
||
from PyQt6.QtCore import *
|
||
from PyQt6.QtGui import *
|
||
from PyQt6.QtWidgets import *
|
||
from ui.Ui_deviceForm import Ui_deviceForm
|
||
from createDeviceDlg import CreateDeviceDlg
|
||
from show_editor import CodeDialog
|
||
from interfaceSession.interfaceManager import interfaceManager
|
||
from instructionModel.instructionManager import instructionManager
|
||
from deviceModel.deviceManager import deviceManager
|
||
from devmodelModel.devModelManager import devModelManager
|
||
from taskModel.taskActuatorManager import taskActuatorManager
|
||
from devGroupModel.devGroupManager import devGroupManager
|
||
from common import common
|
||
from common import DragDropInfo
|
||
from common import AlignDelegate
|
||
from common import AlignNoDelegate
|
||
from common import QxStandardItem
|
||
class QxProgressStandardItem(QStandardItem):
|
||
def __init__(self, text='', parent=None):
|
||
self.obj = parent
|
||
super().__init__(text)
|
||
def updateProgress(self, instruction_id, value, maxValue):
|
||
obj = self.data(Qt.ItemDataRole.UserRole)
|
||
if obj is not None and isinstance(obj, dict):
|
||
|
||
if str(instruction_id) == str(obj.get('instruction_id')):
|
||
self.obj.instructionModelBlockSignals = True
|
||
self.setData({'value': value, 'maximum': maxValue, 'instruction_id': obj.get('instruction_id'), 'name':obj.get('name')}, Qt.ItemDataRole.UserRole)
|
||
self.obj.instructionModelBlockSignals = False
|
||
class ProgressBarDelegate(QStyledItemDelegate):
|
||
def paint(self, painter, option, index):
|
||
if index.column() == 5: # 仅对第二列添加进度条
|
||
data = index.data(Qt.ItemDataRole.UserRole) # 获取进度条数据
|
||
if data is not None and isinstance(data, dict):
|
||
value = data.get('value', -9999)
|
||
maximum = data.get('maximum', -9999)
|
||
progress_rect = option.rect # 进度条的矩形边界
|
||
progress_width = int((value / maximum) * progress_rect.width()) # 计算进度条的宽度
|
||
progress_rect.adjust(2, 2, -2, -2)
|
||
# 绘制进度条背景
|
||
painter.fillRect(progress_rect, QColor("#F8FAFF"))
|
||
# 绘制实际数值文本
|
||
text = f"{value}/{maximum}"
|
||
text_rect = QRect(option.rect)
|
||
# text_rect.adjust(0, 0, -1, 0) # 调整文本矩形边界
|
||
# 修改字体和颜色
|
||
# font = QFont("Arial", 10, QFont.Weight.Bold) # 创建字体对象
|
||
# painter.setFont(font) # 设置字体
|
||
pen = painter.pen()
|
||
pen.setColor(Qt.GlobalColor.black) # 设置画笔颜色为红色
|
||
painter.setPen(pen)
|
||
# 绘制实际进度
|
||
progress_rect.setWidth(progress_width)
|
||
if value == -1 and maximum == -1:
|
||
return
|
||
if value >=0 and maximum >= 1 :
|
||
painter.fillRect(progress_rect, QColor("#007EFF"))
|
||
painter.drawText(text_rect, Qt.AlignmentFlag.AlignCenter, text)
|
||
else:
|
||
super().paint(painter, option, index)
|
||
class DeviceForm(QWidget):
|
||
|
||
def __init__(self, parent=None):
|
||
super().__init__(parent)
|
||
self.ui = Ui_deviceForm()
|
||
self.ui.setupUi(self)
|
||
self.deviceModel = QStandardItemModel()
|
||
self.propertyModel = QStandardItemModel()
|
||
self.devGroupModel = QStandardItemModel()
|
||
self.interfaceModel = QStandardItemModel()
|
||
self.devInstructionModel = QStandardItemModel()
|
||
self.createDevDlg = CreateDeviceDlg()
|
||
self.codeDialog = CodeDialog("脚本编辑")
|
||
self.interfaceCombModel = QStandardItemModel()
|
||
self.supportedTypes = instructionManager.getSupportedType()
|
||
self.devmodelProperty = {}
|
||
self.interfaceModelSignalsBlock = False
|
||
self.instructionModelBlockSignals = False
|
||
self.interfaceTypeNames = []
|
||
self.selectedDeviceId = None
|
||
self.topWidget = None
|
||
self.selectedDeviceModelId = None
|
||
self.devGroupModelBlockSignals = False
|
||
self.current_groupId = None
|
||
self.interfaceTypes = []
|
||
self.confInterfaces = []
|
||
self.realInterfaces = {}
|
||
self.setTableViewModel()
|
||
self.createDevDlg.editFinished.connect(self.editDeviceFinished)
|
||
self.createDevDlg.createFinished.connect(self.createDeviceFinished)
|
||
self.ui.tableViewDevice.doubleClicked.connect(self.editDevice)
|
||
self.ui.tableViewDevCmd.doubleClicked.connect(self.handleDevCmdDoubleClicked)
|
||
self.ui.tableViewDevCmd.selectionModel().selectionChanged.connect(self.handleDevCmdSelection)
|
||
self.ui.tableViewDevice.selectionModel().selectionChanged.connect(self.handleDeviceSelection)
|
||
self.ui.tableViewDevGroup.selectionModel().selectionChanged.connect(self.handleDevGroupSelection)
|
||
self.ui.tableViewDevGroup.dropEvent = self.devGroupDropEvent
|
||
self.ui.lbDeviceImage.mouseDoubleClickEvent = self.handleImageClick
|
||
self.propertyModel.itemChanged.connect(self.updateProperty)
|
||
self.interfaceModel.itemChanged.connect(self.updateProperty)
|
||
self.devGroupModel.itemChanged.connect(self.updateDevGroup)
|
||
self.ui.pbCreateDev.clicked.connect(self.showCreateDevDlg)
|
||
self.ui.pbDeleteDev.clicked.connect(self.deleteDevice)
|
||
self.ui.pbAddProperty.clicked.connect(self.addProperty)
|
||
self.ui.pbDeleteProperty.clicked.connect(self.deleteProperty)
|
||
self.ui.pbEditDev.clicked.connect(self.editDevice)
|
||
self.ui.pbExportDev.clicked.connect(self.exportDevice)
|
||
self.ui.pbEditScript.clicked.connect(self.editScript)
|
||
self.ui.pbStart.clicked.connect(self.startInstruction)
|
||
self.ui.pbStop.clicked.connect(self.stopInstruction)
|
||
self.ui.pbCreateDevGroup.clicked.connect(self.createDevGroup)
|
||
self.ui.pbDeleteDevGroup.clicked.connect(self.deleteDevGroup)
|
||
self.setSplitterStretchFactor()
|
||
self.batchSetItemDelegate()
|
||
taskActuatorManager.updateInstructProgress.connect(self._onUpdateInstructProgress)
|
||
|
||
def showCreateDevDlg(self):
|
||
if self.current_groupId != None:
|
||
self.createDevDlg.showCreateDlg(self.current_groupId)
|
||
|
||
def batchSetItemDelegate(self):
|
||
self.ui.tableViewDevice.verticalHeader().setDefaultAlignment(Qt.AlignmentFlag.AlignHCenter|Qt.AlignmentFlag.AlignVCenter)
|
||
self.ui.tableViewDevCmd.verticalHeader().setDefaultAlignment(Qt.AlignmentFlag.AlignHCenter|Qt.AlignmentFlag.AlignVCenter)
|
||
self.ui.tableViewDevice.horizontalHeader().setDefaultAlignment(Qt.AlignmentFlag.AlignLeft|Qt.AlignmentFlag.AlignVCenter)
|
||
self.ui.tableViewDevCmd.horizontalHeader().setDefaultAlignment(Qt.AlignmentFlag.AlignLeft|Qt.AlignmentFlag.AlignVCenter)
|
||
self.ui.tableViewInterFace.horizontalHeader().setDefaultAlignment(Qt.AlignmentFlag.AlignLeft|Qt.AlignmentFlag.AlignVCenter)
|
||
self.ui.tableViewProperty.horizontalHeader().setDefaultAlignment(Qt.AlignmentFlag.AlignLeft|Qt.AlignmentFlag.AlignVCenter)
|
||
self.ui.tableViewInterFace.setItemDelegate(AlignDelegate(self.ui.tableViewInterFace))
|
||
self.ui.tableViewProperty.setItemDelegate(AlignDelegate(self.ui.tableViewProperty))
|
||
self.ui.tableViewDevice.setItemDelegate(AlignNoDelegate(self.ui.tableViewDevice,2))
|
||
self.ui.tableViewDevCmd.setItemDelegate(AlignNoDelegate(self.ui.tableViewDevCmd))
|
||
self.ui.tableViewDevGroup.setItemDelegate(AlignDelegate(self.ui.tableViewDevGroup))
|
||
|
||
def devGroupDropEvent(self, event: QDropEvent):
|
||
if event.source() != self:
|
||
mime_data = event.mimeData()
|
||
source_index = mime_data.data('application/x-qabstractitemmodeldatalist')
|
||
target_index = self.ui.tableViewDevGroup.indexAt(event.position().toPoint())
|
||
drag_drop_info = DragDropInfo(source_index, target_index)
|
||
self.handleGroupDrop(drag_drop_info)
|
||
event.accept()
|
||
else:
|
||
event.ignore()
|
||
|
||
def handleGroupDrop(self, drag_drop_info):
|
||
target_row = drag_drop_info.target_index.row()
|
||
if target_row > -1:
|
||
row_id_index = self.devGroupModel.index(target_row, 0, QModelIndex())
|
||
groupId = self.devGroupModel.data(row_id_index, Qt.ItemDataRole.DisplayRole)
|
||
rows = self.ui.tableViewDevice.selectionModel().selectedRows()
|
||
for row in rows:
|
||
row_id_index = self.deviceModel.index(row.row(), 0, QModelIndex())
|
||
deviceId = self.deviceModel.data(row_id_index, Qt.ItemDataRole.DisplayRole)
|
||
param = {
|
||
"group_id": groupId
|
||
}
|
||
result = deviceManager.update(deviceId, param)
|
||
# deviceManager.createCopy(deviceId, groupId)
|
||
self.refreshSelect()
|
||
|
||
# def batchInstallEventFilter(self):
|
||
# self.ui.w1.resizeEvent = self.onResize
|
||
# self.ui.w2.resizeEvent = self.onResize
|
||
# self.ui.w3.resizeEvent = self.onResize
|
||
|
||
# def onResize(self, event):
|
||
# self.setAllColumnWidth()
|
||
|
||
def startInstruction(self):
|
||
if len(self.ui.tableViewDevice.selectionModel().selectedRows())== 1:
|
||
row = self.ui.tableViewDevice.selectionModel().selectedRows()[0].row()
|
||
row_deviceId_index = self.deviceModel.index(row, 0, QModelIndex())
|
||
deviceId = self.deviceModel.data(row_deviceId_index, Qt.ItemDataRole.DisplayRole)
|
||
if len(self.ui.tableViewDevCmd.selectionModel().selectedRows())== 1:
|
||
row = self.ui.tableViewDevCmd.selectionModel().selectedRows()[0].row()
|
||
row_instructionId_index = self.devInstructionModel.index(row, 0, QModelIndex())
|
||
instructionId = self.devInstructionModel.data(row_instructionId_index, Qt.ItemDataRole.DisplayRole)
|
||
count = self.interfaceModel.rowCount()
|
||
interfaceIndex = 0
|
||
for i in range(count):
|
||
row_selected_index = self.interfaceModel.index(i, 4 , QModelIndex())
|
||
selected = self.interfaceModel.data(row_selected_index, Qt.ItemDataRole.DisplayRole)
|
||
if selected == "True":
|
||
interfaceIndex = i
|
||
break
|
||
attr = {
|
||
"deviceId": deviceId,
|
||
"param": "",
|
||
"interfaceIndex": interfaceIndex
|
||
}
|
||
taskActuatorManager.startInstruction(instructionId, attr)
|
||
|
||
def stopInstruction(self):
|
||
if len(self.ui.tableViewDevCmd.selectionModel().selectedRows()) == 1:
|
||
row = self.ui.tableViewDevCmd.selectionModel().selectedRows()[0].row()
|
||
row_index = self.devInstructionModel.index(row, 0, QModelIndex())
|
||
instructionid = self.devInstructionModel.data(row_index, Qt.ItemDataRole.DisplayRole)
|
||
taskActuatorManager.stop(instructionid)
|
||
|
||
def findTopLevelParent(self, widget):
|
||
parent = widget.parent()
|
||
while parent and not isinstance(parent, QMainWindow): # 顶层窗口是 QMainWindow
|
||
widget = parent
|
||
parent = widget.parent()
|
||
return parent
|
||
|
||
def editScript(self):
|
||
indexs = self.ui.tableViewDevice.selectionModel().selectedRows()
|
||
if len(indexs) == 1:
|
||
if len(self.ui.tableViewDevCmd.selectionModel().selectedRows())== 1:
|
||
row = self.ui.tableViewDevCmd.selectionModel().selectedRows()[0].row()
|
||
row_index = self.devInstructionModel.index(row, 0, QModelIndex())
|
||
instructionId = self.devInstructionModel.data(row_index, Qt.ItemDataRole.DisplayRole)
|
||
instruction = instructionManager.getInfo(instructionId)
|
||
if instruction != None:
|
||
dataAttr = instruction.get('attr')
|
||
if dataAttr != None:
|
||
if dataAttr.get('script') != None:
|
||
base64_encoded_str = dataAttr.get('script')
|
||
result = self.codeDialog.showEditDlg(str(base64.b64decode(base64_encoded_str).decode('utf-8')))
|
||
if result == "":
|
||
return
|
||
script = base64.b64encode(result.encode("utf-8")).decode('utf-8')
|
||
dataAttr['script'] = script
|
||
instructionManager.update(instructionId, {"attr": dataAttr})
|
||
script = base64.b64encode(result.encode("utf-8")).decode('utf-8')
|
||
dataAttr['script'] = script
|
||
instructionManager.update(instructionId, {"attr": dataAttr})
|
||
|
||
def exportDevice(self):
|
||
try:
|
||
filename = "data.zip"
|
||
packeagePath = filename
|
||
fname = QFileDialog.getSaveFileName(self, '保存文件', packeagePath, 'JSON files (*.zip)')
|
||
if fname[0]:
|
||
packeagePath = fname[0]
|
||
exportPath = os.path.join(os.path.dirname(__file__),"export")
|
||
#清空exportPath目录
|
||
common.clearDir(exportPath)
|
||
#创建exportPath目录
|
||
common.createDir(exportPath)
|
||
indexs = self.ui.tableViewDevice.selectionModel().selectedRows()
|
||
for index in indexs:
|
||
row = index.row()
|
||
row_index = self.deviceModel.index(row, 0, QModelIndex())
|
||
deviceId = self.deviceModel.data(row_index, Qt.ItemDataRole.DisplayRole)
|
||
deviceInfo = deviceManager.getOriginalInfo(deviceId)
|
||
groupInfo = devGroupManager.getInfo(deviceInfo["group_id"])
|
||
#判断modelInfo 中的Id 长度是否小于10,如果是则改为uuid
|
||
newdeviceId = str(uuid.uuid4())
|
||
if len(str(deviceId)) < 10:
|
||
deviceInfo["id"] = newdeviceId
|
||
if deviceInfo!= None:
|
||
common.writeJson(os.path.join(os.path.dirname(__file__),"export"),"device", deviceInfo)
|
||
if groupInfo!= None:
|
||
common.writeJson(os.path.join(os.path.dirname(__file__),"export"),"device_group", groupInfo)
|
||
common.writeZip(packeagePath, exportPath)
|
||
except Exception as e:
|
||
if self.topWidget == None:
|
||
self.topWidget = self.findTopLevelParent(self)
|
||
if self.topWidget != None:
|
||
self.topWidget.alertDialog(e,"导出文件异常")
|
||
def handleDevCmdDoubleClicked(self, index):
|
||
self.startInstruction()
|
||
|
||
def deleteDevice(self):
|
||
indexs = self.ui.tableViewDevice.selectionModel().selectedRows()
|
||
if len(indexs)== 1:
|
||
row = indexs[0].row()
|
||
row_devName_index = self.deviceModel.index(row, 3, QModelIndex())
|
||
devName = self.deviceModel.data(row_devName_index, Qt.ItemDataRole.DisplayRole)
|
||
if self.topWidget == None:
|
||
self.topWidget = self.findTopLevelParent(self)
|
||
if self.topWidget != None:
|
||
if self.topWidget.removeDialog(devName,"删除设备"):
|
||
row_devModel_index = self.deviceModel.index(row, 0, QModelIndex())
|
||
deviceId = self.deviceModel.data(row_devModel_index, Qt.ItemDataRole.DisplayRole)
|
||
deviceManager.delete(deviceId)
|
||
|
||
if len(indexs)>1:
|
||
if self.topWidget == None:
|
||
self.topWidget = self.findTopLevelParent(self)
|
||
if self.topWidget != None: #必须这样写,不能写else
|
||
if self.topWidget.removeDialog(f"{len(indexs)}个设备"):
|
||
for index in indexs:
|
||
row = index.row()
|
||
row_id_index = self.deviceModel.index(row, 0, QModelIndex())
|
||
instructionId = self.deviceModel.data(row_id_index, Qt.ItemDataRole.DisplayRole)
|
||
deviceManager.delete(instructionId)
|
||
self.refreshSelect()
|
||
|
||
def handleImageClick(self, event):
|
||
if event.button() == Qt.MouseButton.LeftButton:
|
||
if len(self.ui.tableViewDevice.selectionModel().selectedRows())== 1:
|
||
row = self.ui.tableViewDevice.selectionModel().selectedRows()[0].row()
|
||
row_index = self.deviceModel.index(row, 0, QModelIndex())
|
||
deviceId = self.deviceModel.data(row_index, Qt.ItemDataRole.DisplayRole)
|
||
data = deviceManager.getInfo(deviceId)
|
||
description = ""
|
||
extendedProperty = ""
|
||
interfaceIndex = -1
|
||
file_dialog = QFileDialog(self)
|
||
file_dialog.setWindowTitle('选择图片')
|
||
file_dialog.setNameFilter('Supported file formats (*.png *.jpg)') # 设置文件过滤器
|
||
if file_dialog.exec():
|
||
file_path = file_dialog.selectedFiles()[0]
|
||
pixmap = QPixmap(file_path)
|
||
self.ui.lbDeviceImage.setPixmap(pixmap)
|
||
self.ui.lbDeviceImage.setScaledContents(True)
|
||
self.ui.lbDeviceImage.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||
base64_data = common.base64Data(pixmap.toImage())
|
||
|
||
if 'attr' in data:
|
||
attr_json_dict = data['attr']
|
||
if 'description' in attr_json_dict:
|
||
description = attr_json_dict['description']
|
||
if 'extendedProperty' in attr_json_dict:
|
||
extendedProperty = attr_json_dict['extendedProperty']
|
||
if 'interfaceIndex' in attr_json_dict:
|
||
interfaceIndex = attr_json_dict['interfaceIndex']
|
||
attr = {
|
||
"description": description,
|
||
"image": base64_data,
|
||
"extendedProperty" : extendedProperty,
|
||
"interfaceIndex" : interfaceIndex
|
||
}
|
||
param = {
|
||
"attr" : attr
|
||
}
|
||
result = deviceManager.update(deviceId, param)
|
||
|
||
def editDevice(self):
|
||
if len(self.ui.tableViewDevice.selectionModel().selectedRows())== 1:
|
||
row = self.ui.tableViewDevice.selectionModel().selectedRows()[0].row()
|
||
row_dev_index = self.deviceModel.index(row, 0, QModelIndex())
|
||
row_devModel_index = self.deviceModel.index(row, 1, QModelIndex())
|
||
deviceId = self.deviceModel.data(row_dev_index, Qt.ItemDataRole.DisplayRole)
|
||
devModelId = self.deviceModel.data(row_devModel_index, Qt.ItemDataRole.DisplayRole)
|
||
device = deviceManager.getInfo(deviceId)
|
||
if device != None:
|
||
self.createDevDlg.showEditDlg( devModelId, device)
|
||
|
||
def clearChildrenModel(self):
|
||
self.devInstructionModel.clear()
|
||
self.propertyModel.clear()
|
||
self.interfaceModel.clear()
|
||
self.ui.lbDeviceImage.setText("封面")
|
||
|
||
def setTableViewModel(self):
|
||
self.ui.tableViewDevGroup.setModel(self.devGroupModel)
|
||
self.ui.tableViewDevice.setModel(self.deviceModel)
|
||
self.ui.tableViewDevCmd.setModel(self.devInstructionModel)
|
||
self.ui.tableViewProperty.setModel(self.propertyModel)
|
||
self.ui.tableViewInterFace.setModel(self.interfaceModel)
|
||
|
||
# def refreshSelect(self):
|
||
# selectedRows = self.ui.tableViewDevice.selectionModel().selectedRows()
|
||
# self.loadDevice()
|
||
# if len(selectedRows) < 1:
|
||
# self.ui.tableViewDevice.selectRow(0)
|
||
# if len(selectedRows)== 1:
|
||
# row = selectedRows[0].row()
|
||
# self.ui.tableViewDevice.selectRow(row)
|
||
def refreshSelect(self):
|
||
selectedRows = self.ui.tableViewDevGroup.selectionModel().selectedRows()
|
||
self.loadDevGroup()
|
||
if len(selectedRows) < 1:
|
||
self.ui.tableViewDevGroup.selectRow(0)
|
||
if len(selectedRows) == 1:
|
||
row = selectedRows[0].row()
|
||
self.ui.tableViewDevGroup.selectRow(row)
|
||
|
||
# 创建完成
|
||
def createDeviceFinished(self):
|
||
self.loadDevice()
|
||
if self.deviceModel.rowCount() > 0:
|
||
self.ui.tableViewDevice.selectRow(self.deviceModel.rowCount()-1)
|
||
|
||
# 编辑完成
|
||
def editDeviceFinished(self):
|
||
if len(self.ui.tableViewDevice.selectionModel().selectedRows())== 1:
|
||
row = self.ui.tableViewDevice.selectionModel().selectedRows()[0].row()
|
||
self.loadDevice()
|
||
if self.deviceModel.rowCount() > 0:
|
||
self.ui.tableViewDevice.selectRow(row)
|
||
|
||
def updateDevBtnState(self):
|
||
if len(self.ui.tableViewDevice.selectionModel().selectedRows())== 1:
|
||
self.ui.pbEditDev.setEnabled(True)
|
||
else:
|
||
self.ui.pbEditDev.setEnabled(False)
|
||
|
||
def handleDeviceSelection(self):
|
||
selectedRows = self.ui.tableViewDevice.selectionModel().selectedRows()
|
||
if len(selectedRows)== 1:
|
||
row = selectedRows[0].row()
|
||
row_deviceId_index = self.deviceModel.index(row, 0, QModelIndex())
|
||
row_devModelId_index = self.deviceModel.index(row, 1, QModelIndex())
|
||
self.selectedDeviceId = self.deviceModel.data(row_deviceId_index, Qt.ItemDataRole.DisplayRole)
|
||
self.selectedDevModelId = self.deviceModel.data(row_devModelId_index, Qt.ItemDataRole.DisplayRole)
|
||
self.loadInterface()
|
||
self.loadDevModelProperty(row, self.selectedDevModelId)
|
||
self.loadDevInstruction(self.selectedDevModelId)
|
||
self.loadDeviceProperty(self.selectedDeviceId)
|
||
self.updateDevBtnState()
|
||
|
||
def handleDevCmdSelection(self):
|
||
selectedRows = self.ui.tableViewDevCmd.selectionModel().selectedRows()
|
||
if len(selectedRows)== 1:
|
||
row = selectedRows[0].row()
|
||
row_index = self.devInstructionModel.index(row, 6, QModelIndex())
|
||
description = self.devInstructionModel.data(row_index, Qt.ItemDataRole.DisplayRole)
|
||
if description:
|
||
self.ui.textEditDescription.setText(description)
|
||
else:
|
||
self.ui.textEditDescription.clear()
|
||
self.updateDevBtnState()
|
||
|
||
# 加载设备
|
||
def loadDevice(self):
|
||
devices = deviceManager.getGroupDevices(self.current_groupId)
|
||
self.deviceModel.clear()
|
||
self.ui.textEditDescription.clear()
|
||
if len(devices) > 0:
|
||
for device in devices:
|
||
index = self.deviceModel.rowCount()
|
||
self.deviceModel.setItem(index, 0, QxStandardItem(str(device["id"])))
|
||
self.deviceModel.setItem(index, 1, QxStandardItem(str(device["dev_model_id"])))
|
||
self.deviceModel.setItem(index, 2, QxStandardItem(str(index+1)))
|
||
self.deviceModel.setItem(index, 3, QxStandardItem(device["name"], max_length=15))
|
||
devModel = devModelManager.getInfo(device["dev_model_id"])
|
||
if devModel != None:
|
||
self.deviceModel.setItem(index, 4, QxStandardItem(devModel["name"], max_length=8))
|
||
self.ui.tableViewDevice.sortByColumn(3, Qt.SortOrder.AscendingOrder)
|
||
self.batchOperation()
|
||
|
||
# 任务分组选中
|
||
def handleDevGroupSelection(self, index):
|
||
if len(self.ui.tableViewDevGroup.selectionModel().selectedRows()) == 1:
|
||
row = self.ui.tableViewDevGroup.selectionModel().selectedRows()[0].row()
|
||
row_id_index = self.devGroupModel.index(row, 0, QModelIndex())
|
||
group_id = self.devGroupModel.data(row_id_index, Qt.ItemDataRole.DisplayRole)
|
||
self.current_groupId = group_id
|
||
self.loadDevice()
|
||
self.clearChildrenModel()
|
||
|
||
|
||
# 加载队列组
|
||
def loadDevGroup(self):
|
||
self.devGroupModel.clear()
|
||
self.ui.textEditDescription.clear()
|
||
groups = devGroupManager.getInfo()
|
||
self.devGroupModelBlockSignals = True
|
||
if len(groups) > 0:
|
||
for group in groups:
|
||
index = self.devGroupModel.rowCount()
|
||
self.devGroupModel.setItem(index, 0, QStandardItem(str(group["id"])))
|
||
self.devGroupModel.setItem(index, 1, QStandardItem(group["name"]))
|
||
self.devGroupModelBlockSignals = False
|
||
self.batchOperation()
|
||
|
||
# 切换接口
|
||
def changeInterface(self,index):
|
||
if len(self.ui.tableViewInterFace.selectionModel().selectedRows())== 1:
|
||
row = self.ui.tableViewInterFace.selectionModel().selectedRows()[0].row()
|
||
self.interfaceModel.setItem(row, 2 , QxStandardItem(str(self.sender().currentData()["id"])))
|
||
|
||
# 更新属性
|
||
def updateProperty(self):
|
||
if self.interfaceModelSignalsBlock == False:
|
||
self.interfaceModelSignalsBlock = True
|
||
if len(self.ui.tableViewDevice.selectionModel().selectedRows())== 1:
|
||
row = self.ui.tableViewDevice.selectionModel().selectedRows()[0].row()
|
||
row_id_index = self.deviceModel.index(row, 0, QModelIndex())
|
||
deviceId = self.deviceModel.data(row_id_index, Qt.ItemDataRole.DisplayRole)
|
||
interfaceIndex = -1
|
||
devinfo = deviceManager.getInfo(deviceId)
|
||
if devinfo!= None:
|
||
if 'attr' in devinfo:
|
||
attr_json_dict = devinfo['attr']
|
||
if 'interfaceIndex' in attr_json_dict:
|
||
interfaceIndex = attr_json_dict['interfaceIndex']
|
||
interfaceIds = []
|
||
extendedProperty = {}
|
||
for row in range(self.interfaceModel.rowCount()):
|
||
index = self.interfaceModel.index(row, 2)
|
||
interfaceId = self.interfaceModel.data(index)
|
||
interfaceIds.append(interfaceId)
|
||
description = ""
|
||
for row in range(self.propertyModel.rowCount()):
|
||
if row >= len(self.devmodelProperty):
|
||
if self.propertyModel.item(row, 0).text() == "设备描述":
|
||
description = self.propertyModel.item(row, 1).text()
|
||
else:
|
||
if self.propertyModel.item(row, 0).text() != "":
|
||
extendedProperty[self.propertyModel.item(row, 0).text()] = self.propertyModel.item(row, 1).text()
|
||
image = common.base64Data(self.ui.lbDeviceImage.pixmap().toImage())
|
||
attr = {
|
||
"description": description,
|
||
"image": image,
|
||
"extendedProperty" : extendedProperty,
|
||
"interfaceIndex": interfaceIndex
|
||
}
|
||
param = {
|
||
"id": deviceId,
|
||
"interface": interfaceIds,
|
||
"attr": attr
|
||
}
|
||
deviceManager.update(deviceId, param)
|
||
self.interfaceModelSignalsBlock = False
|
||
|
||
# 添加属性
|
||
def addProperty(self):
|
||
if len(self.ui.tableViewDevice.selectionModel().selectedRows())== 1:
|
||
# 列表末位名称为空不允许创建
|
||
if self.propertyModel.rowCount() > 0:
|
||
lastIndex = self.propertyModel.rowCount() - 1
|
||
if self.propertyModel.item(lastIndex, 0).text().strip() == "":
|
||
self.ui.tableViewProperty.edit(self.propertyModel.index(lastIndex, 0))
|
||
return
|
||
index = self.propertyModel.rowCount()
|
||
self.propertyModel.appendRow([QxStandardItem(), QxStandardItem()])
|
||
self.ui.tableViewProperty.setCurrentIndex(self.propertyModel.index(index, 0) )
|
||
self.ui.tableViewProperty.edit(self.propertyModel.index(index, 0))
|
||
|
||
# 删除属性
|
||
def deleteProperty(self):
|
||
if len(self.ui.tableViewProperty.selectionModel().selectedRows())== 1:
|
||
row = self.ui.tableViewProperty.selectionModel().selectedRows()[0].row()
|
||
if row >= len(self.devmodelProperty):
|
||
if not self.propertyModel.item(row, 0).text() == "设备描述":
|
||
self.propertyModel.removeRow(row)
|
||
self.updateProperty()
|
||
#加载接口
|
||
def loadInterface(self):
|
||
self.interfaceTypeNames = []
|
||
self.interfaceTypes = []
|
||
supportedType = interfaceManager.getSupportedType()
|
||
for type in supportedType:
|
||
self.interfaceTypeNames.append(supportedType[type])
|
||
self.interfaceTypes.append(type)
|
||
self.confInterfaces = interfaceManager.getInfo()
|
||
self.realInterfaces = {}
|
||
for i in range (len(self.interfaceTypes)):
|
||
interfaceType_list = [{"name":"未选择", "id":common.noChooseUuid}]
|
||
for interface in self.confInterfaces:
|
||
if interface["type"] == self.interfaceTypes[i]:
|
||
interfaceType_list.append(interface)
|
||
self.realInterfaces[self.interfaceTypeNames[i]] = interfaceType_list
|
||
self.batchOperation()
|
||
|
||
#默认接口选择
|
||
def interfaceModelCheckedChanged(self):
|
||
checked = self.sender().isChecked()
|
||
if self.interfaceModelSignalsBlock:
|
||
return
|
||
if len(self.ui.tableViewDevice.selectionModel().selectedRows())== 1:
|
||
row = self.ui.tableViewDevice.selectionModel().selectedRows()[0].row()
|
||
row_index = self.deviceModel.index(row, 0, QModelIndex())
|
||
deviceId = self.deviceModel.data(row_index, Qt.ItemDataRole.DisplayRole)
|
||
data = deviceManager.getInfo(deviceId)
|
||
index = self.ui.tableViewInterFace.currentIndex()
|
||
if index.isValid():
|
||
self.interfaceModelSignalsBlock = True
|
||
count = self.interfaceModel.rowCount()
|
||
for i in range(count):
|
||
self.interfaceModel.setItem(i, 4 , QxStandardItem("False"))
|
||
self.interfaceModel.setItem(index.row(), 4 , QxStandardItem("True"))
|
||
description = ""
|
||
extendedProperty = ""
|
||
interfaceIndex = index.row()
|
||
if 'attr' in data:
|
||
attr_json_dict = data['attr']
|
||
if 'description' in attr_json_dict:
|
||
description = attr_json_dict['description']
|
||
if 'extendedProperty' in attr_json_dict:
|
||
extendedProperty = attr_json_dict['extendedProperty']
|
||
if 'image' in attr_json_dict:
|
||
image = attr_json_dict['image']
|
||
attr = {
|
||
"description": description,
|
||
"image": image,
|
||
"extendedProperty" : extendedProperty,
|
||
"interfaceIndex" : interfaceIndex
|
||
}
|
||
param = {
|
||
"attr" : attr
|
||
}
|
||
result = deviceManager.update(deviceId, param)
|
||
self.interfaceModelSignalsBlock = False
|
||
|
||
#加载设备属性和接口
|
||
def loadDeviceProperty(self, deviceId):
|
||
if deviceId:
|
||
device = deviceManager.getInfo(deviceId)
|
||
if device != None:
|
||
self.propertyModel.blockSignals(True)
|
||
interfaceIndex = -1
|
||
if "attr" in device:
|
||
attr_json_dict = device["attr"]
|
||
description = ""
|
||
pixmap = QPixmap()
|
||
|
||
if "image" in attr_json_dict and attr_json_dict["image"]:
|
||
pixmap = common.base64ToPixmap(attr_json_dict["image"])
|
||
|
||
#设置封面
|
||
self.ui.lbDeviceImage.setPixmap(pixmap)
|
||
self.ui.lbDeviceImage.setScaledContents(True)
|
||
self.ui.lbDeviceImage.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||
if "description" in attr_json_dict:
|
||
description = attr_json_dict["description"]
|
||
index = self.propertyModel.rowCount()
|
||
self.propertyModel.setItem(index, 0, QxStandardItem("设备描述"))
|
||
self.propertyModel.setItem(index, 1, QxStandardItem(description))
|
||
self.setUnEditableStyle(self.propertyModel.item(index, 0))
|
||
deviceextendedProperty= {}
|
||
if "extendedProperty" in attr_json_dict:
|
||
deviceextendedProperty = attr_json_dict["extendedProperty"]
|
||
#扩展属性添加
|
||
for key in deviceextendedProperty:
|
||
index = self.propertyModel.rowCount()
|
||
self.propertyModel.setItem(index, 0, QxStandardItem(key))
|
||
self.propertyModel.setItem(index, 1, QxStandardItem(deviceextendedProperty[key]))
|
||
if "interfaceIndex" in attr_json_dict:
|
||
interfaceIndex = attr_json_dict["interfaceIndex"]
|
||
self.propertyModel.blockSignals(False)
|
||
if "interface" in device:
|
||
interface_id_list = device["interface"]
|
||
#阻塞interfaceModel信号
|
||
self.interfaceModelSignalsBlock = True
|
||
if len(interface_id_list) == self.interfaceModel.rowCount():
|
||
for index in range(len(interface_id_list)):
|
||
interfaceId = interface_id_list[index]
|
||
radio = QRadioButton()
|
||
radio.setStyleSheet("QRadioButton::indicator {"
|
||
" subcontrol-origin: padding;"
|
||
" subcontrol-position: center;"
|
||
"}")
|
||
self.interfaceModel.setItem(index, 3 , QxStandardItem())
|
||
if index == interfaceIndex:
|
||
radio.setChecked(True)
|
||
self.interfaceModel.setItem(index, 4 , QxStandardItem("True"))
|
||
else:
|
||
radio.setChecked(False)
|
||
self.interfaceModel.setItem(index, 4 , QxStandardItem("False"))
|
||
radio.toggled.connect(self.interfaceModelCheckedChanged)
|
||
cmb = QComboBox()
|
||
type = self.interfaceModel.item(index, 1).text()
|
||
currentIndex = 0
|
||
for i in range (len(self.realInterfaces[type])):
|
||
cmb.addItem(self.realInterfaces[type][i]["name"], self.realInterfaces[type][i])
|
||
if interfaceId == self.realInterfaces[type][i]["id"]:
|
||
currentIndex = i
|
||
cmb.setCurrentIndex(currentIndex)
|
||
self.interfaceModel.setItem(index, 2 , QxStandardItem(self.realInterfaces[type][currentIndex]["id"]))
|
||
cmb.currentIndexChanged.connect(self.changeInterface)
|
||
self.ui.tableViewInterFace.setIndexWidget(self.interfaceModel.index(index, 3), radio)
|
||
self.ui.tableViewInterFace.setIndexWidget(self.interfaceModel.index(index, 2), cmb)
|
||
else:
|
||
for index in range(self.interfaceModel.rowCount()):
|
||
self.interfaceModel.setItem(index, 2 , QxStandardItem())
|
||
radio = QRadioButton()
|
||
radio.setStyleSheet("QRadioButton::indicator {"
|
||
" subcontrol-origin: padding;"
|
||
" subcontrol-position: center;"
|
||
"}")
|
||
self.interfaceModel.setItem(index, 3 , QxStandardItem())
|
||
if index == interfaceIndex:
|
||
radio.setChecked(True)
|
||
self.interfaceModel.setItem(index, 4 , QxStandardItem("True"))
|
||
else:
|
||
radio.setChecked(False)
|
||
self.interfaceModel.setItem(index, 4 , QxStandardItem("False"))
|
||
radio.toggled.connect(self.interfaceModelCheckedChanged)
|
||
cmb = QComboBox()
|
||
type = self.interfaceModel.item(index, 1).text()
|
||
currentIndex = 0
|
||
for i in range (len(self.realInterfaces[type])):
|
||
cmb.addItem(self.realInterfaces[type][i]["name"], self.realInterfaces[type][i])
|
||
cmb.setCurrentIndex(currentIndex)
|
||
self.interfaceModel.setItem(index, 2 , QxStandardItem(self.realInterfaces[type][currentIndex]["id"]))
|
||
cmb.currentIndexChanged.connect(self.changeInterface)
|
||
self.ui.tableViewInterFace.setIndexWidget(self.interfaceModel.index(index, 3), radio)
|
||
self.ui.tableViewInterFace.setIndexWidget(self.interfaceModel.index(index, 2), cmb)
|
||
#解除阻塞
|
||
self.interfaceModelSignalsBlock = False
|
||
self.batchOperation()
|
||
|
||
#加载模型属性
|
||
def loadDevModelProperty(self,index, devModelId):
|
||
if devModelId:
|
||
devModel = devModelManager.getInfo(devModelId)
|
||
if devModel != None:
|
||
#更新扩展属性
|
||
if "attr" in devModel:
|
||
#加载属性参数
|
||
attr_json_dict = devModel["attr"]
|
||
devmodelInterfacetype = {}
|
||
modelnumber = ""
|
||
if "modelnumber" in attr_json_dict:
|
||
modelnumber = attr_json_dict["modelnumber"]
|
||
self.devmodelProperty= {"设备型号":modelnumber}
|
||
|
||
if "interfaceType" in attr_json_dict:
|
||
devmodelInterfacetype = attr_json_dict["interfaceType"]
|
||
if "extendedProperty" in attr_json_dict:
|
||
self.devmodelProperty.update( attr_json_dict["extendedProperty"] )
|
||
#阻塞propertyModel信号
|
||
self.propertyModel.blockSignals(True)
|
||
self.propertyModel.clear()
|
||
#扩展属性添加
|
||
for key in self.devmodelProperty:
|
||
index = self.propertyModel.rowCount()
|
||
self.propertyModel.setItem(index, 0, QxStandardItem(key))
|
||
self.propertyModel.setItem(index, 1, QxStandardItem(self.devmodelProperty[key]))
|
||
self.setUnEditableStyle(self.propertyModel.item(index, 0))
|
||
self.setUnEditableStyle(self.propertyModel.item(index, 1))
|
||
#解除propertyModel阻塞
|
||
self.propertyModel.blockSignals(False)
|
||
|
||
#阻塞interFaceTypeModel信号
|
||
self.interfaceModel.blockSignals(True)
|
||
self.interfaceModel.clear()
|
||
|
||
for interFaceType in devmodelInterfacetype:
|
||
index = self.interfaceModel.rowCount()
|
||
self.interfaceModel.setItem(index, 0, QxStandardItem(interFaceType))
|
||
self.interfaceModel.setItem(index, 1, QxStandardItem(devmodelInterfacetype[interFaceType]))
|
||
#解除interFaceTypeModel阻塞
|
||
self.interfaceModel.blockSignals(False)
|
||
self.batchOperation()
|
||
|
||
# 加载设备指令
|
||
def loadDevInstruction(self,devModelId):
|
||
instructions = instructionManager.getDevmodelInstructions(devModelId)
|
||
self.devInstructionModel.clear()
|
||
for data in instructions:
|
||
index = self.devInstructionModel.rowCount()
|
||
self.devInstructionModel.setItem(index, 0, QxStandardItem(str(data["id"])))
|
||
self.devInstructionModel.setItem(index, 1, QxStandardItem(str(index+1)))
|
||
self.devInstructionModel.setItem(index, 2, QxStandardItem(data["name"]))
|
||
attr = data["attr"]
|
||
|
||
if data["type"] is not None and data["type"] in self.supportedTypes:
|
||
self.devInstructionModel.setItem(index, 3, QxStandardItem(attr[self.supportedTypes[data["type"]]["digestAttr"]]))
|
||
else:
|
||
self.devInstructionModel.setItem(index, 3, QxStandardItem(""))
|
||
self.devInstructionModel.setItem(index, 4, QxStandardItem(attr["remark"]))
|
||
progressItem = QxProgressStandardItem('',self)
|
||
progressItem.setData({'instruction_id': data["id"], 'name': data["name"]}, Qt.ItemDataRole.UserRole)
|
||
self.devInstructionModel.setItem(index, 5, progressItem)
|
||
delegate = ProgressBarDelegate()
|
||
self.ui.tableViewDevCmd.setItemDelegateForColumn(5, delegate)
|
||
if "description" not in attr:
|
||
attr["description"] = ""
|
||
self.devInstructionModel.setItem(index, 6, QStandardItem(attr["description"]))
|
||
self.ui.tableViewDevCmd.sortByColumn(2, Qt.SortOrder.AscendingOrder)
|
||
self.batchOperation()
|
||
|
||
def _onUpdateInstructProgress(self, instructionId, value, maxValue):
|
||
for row in range(self.devInstructionModel.rowCount()):
|
||
item = self.devInstructionModel.item(row, 5)
|
||
if item is not None:
|
||
obj = item.data(Qt.ItemDataRole.UserRole)
|
||
if obj is not None and isinstance(obj, dict):
|
||
if str(instructionId) == str(obj.get('instruction_id')):
|
||
self.instructionModelBlockSignals = True
|
||
item.setData({'value': value, 'maximum': maxValue, 'instruction_id': obj.get('instruction_id'), 'name': obj.get('name')}, Qt.ItemDataRole.UserRole)
|
||
self.instructionModelBlockSignals = False
|
||
return
|
||
|
||
#设置不可编辑样式
|
||
def setUnEditableStyle(self, item):
|
||
item.setEditable(False)
|
||
|
||
def setSplitterStretchFactor(self):
|
||
self.ui.splitter.setStretchFactor(1, 40)
|
||
self.ui.splitter.setStretchFactor(2, 60)
|
||
|
||
# 批量操作
|
||
def batchOperation(self):
|
||
self.setAllHorizontalHeaderLabels()
|
||
self.hideAllColumn()
|
||
self.setAllColumnWidth()
|
||
|
||
# 批量设置列宽
|
||
def setAllColumnWidth(self):
|
||
try:
|
||
if self.deviceModel.rowCount() > 0:
|
||
self.ui.tableViewDevice.horizontalHeader().setSectionResizeMode(3,QHeaderView.ResizeMode.Stretch)
|
||
self.ui.tableViewDevice.horizontalHeader().setSectionResizeMode(4,QHeaderView.ResizeMode.Stretch)
|
||
self.ui.tableViewProperty.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
|
||
self.ui.tableViewInterFace.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
|
||
if self.devInstructionModel.rowCount() > 0:
|
||
self.ui.tableViewDevCmd.horizontalHeader().setSectionResizeMode(2,QHeaderView.ResizeMode.ResizeToContents)
|
||
self.ui.tableViewDevCmd.horizontalHeader().setSectionResizeMode(3,QHeaderView.ResizeMode.Stretch)
|
||
self.ui.tableViewDevCmd.horizontalHeader().setSectionResizeMode(4,QHeaderView.ResizeMode.Stretch)
|
||
self.ui.tableViewDevCmd.horizontalHeader().setSectionResizeMode(5,QHeaderView.ResizeMode.Stretch)
|
||
|
||
if self.devGroupModel.rowCount() > 0:
|
||
self.ui.tableViewDevGroup.horizontalHeader().setSectionResizeMode(1,QHeaderView.ResizeMode.Stretch)
|
||
except Exception as e:
|
||
print(e)
|
||
|
||
def setSplitterStretchFactor(self):
|
||
self.ui.splitter.setStretchFactor(0, 30)
|
||
self.ui.splitter.setStretchFactor(1, 30)
|
||
self.ui.splitter.setStretchFactor(2, 40)
|
||
|
||
# 批量设置标题
|
||
def setAllHorizontalHeaderLabels(self):
|
||
self.devGroupModel.setHorizontalHeaderLabels(["ID","名称"])
|
||
self.deviceModel.setHorizontalHeaderLabels(["ID","模型ID","", "名称","模型"])
|
||
self.devInstructionModel.setHorizontalHeaderLabels(["ID", "", "名称", "摘要", "备注", "进度", "描述"])
|
||
self.propertyModel.setHorizontalHeaderLabels([" 属性", " 值"])
|
||
self.interfaceModel.setHorizontalHeaderLabels([" 名称"," 类型", "接口", "默认"])
|
||
|
||
# 隐藏指定列
|
||
def hideAllColumn(self):
|
||
self.ui.tableViewDevice.setColumnHidden(0, True)
|
||
self.ui.tableViewDevGroup.setColumnHidden(0, True)
|
||
self.ui.tableViewDevice.setColumnHidden(1, True)
|
||
self.ui.tableViewDevice.setColumnHidden(2, True)
|
||
self.ui.tableViewDevCmd.setColumnHidden(0, True)
|
||
self.ui.tableViewDevCmd.setColumnHidden(1, True)
|
||
self.ui.tableViewDevCmd.setColumnHidden(6, True)
|
||
self.ui.tableViewInterFace.setColumnHidden(4, True)
|
||
|
||
def createDevGroup(self):
|
||
index = self.devGroupModel.rowCount()
|
||
self.devGroupModel.appendRow([ QStandardItem(), QStandardItem()])
|
||
self.ui.tableViewDevGroup.setCurrentIndex(self.devGroupModel.index(index, 0) )
|
||
self.ui.tableViewDevGroup.edit(self.devGroupModel.index(index, 1))
|
||
|
||
def deleteDevGroup(self):
|
||
if len(self.ui.tableViewDevGroup.selectionModel().selectedRows())== 1:
|
||
row = self.ui.tableViewDevGroup.selectionModel().selectedRows()[0].row()
|
||
row_id_index = self.devGroupModel.index(row, 0, QModelIndex())
|
||
groupId = self.devGroupModel.data(row_id_index, Qt.ItemDataRole.DisplayRole)
|
||
if groupId == common.defaultGroupId:
|
||
return
|
||
row_groupName_index = self.devGroupModel.index(row, 1, QModelIndex())
|
||
groupName = self.devGroupModel.data(row_groupName_index, Qt.ItemDataRole.DisplayRole)
|
||
if self.topWidget == None:
|
||
self.topWidget = self.findTopLevelParent(self)
|
||
if self.topWidget != None:
|
||
if self.deviceModel.rowCount() > 0:
|
||
self.topWidget.alertDialog("分组下存在设备,不能删除分组", "警告")
|
||
return
|
||
if self.topWidget.removeDialog(groupName,"删除分组"):
|
||
result = devGroupManager.delete(groupId)
|
||
if result == True:
|
||
self.clearChildrenModel()
|
||
self.devGroupModel.removeRow(row)
|
||
|
||
def updateDevGroup(self):
|
||
if len(self.ui.tableViewDevGroup.selectionModel().selectedRows())== 1:
|
||
row = self.ui.tableViewDevGroup.selectionModel().selectedRows()[0].row()
|
||
row_id_index = self.devGroupModel.index(row, 0, QModelIndex())
|
||
groupId = self.devGroupModel.data(row_id_index, Qt.ItemDataRole.DisplayRole)
|
||
row_nane_index = self.devGroupModel.index(row, 1, QModelIndex())
|
||
groupName = self.devGroupModel.data(row_nane_index, Qt.ItemDataRole.DisplayRole)
|
||
if groupId == None and groupName.strip() != "":
|
||
groupId = devGroupManager.create({"name":groupName})
|
||
self.devGroupModel.setData(row_id_index, groupId)
|
||
else:
|
||
if groupName.strip() != "":
|
||
devGroupManager.update(groupId, {"name":groupName}) |