secop-gui based on secop.client.Client

instead of secop.client.baseclient.Client

Change-Id: I869a3a9ecba40382908b4741ef055a0c5afe018f
Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/22471
Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
2020-02-17 12:34:13 +01:00
parent 685f22330a
commit 8cf4c2d8eb
5 changed files with 160 additions and 106 deletions

View File

@ -24,13 +24,14 @@
import sys
from secop.client.baseclient import Client as SECNode
import secop.client
from secop.gui.modulectrl import ModuleCtrl
from secop.gui.nodectrl import NodeCtrl
from secop.gui.paramview import ParameterView
from secop.gui.qt import QInputDialog, QMainWindow, QMessageBox, \
QObject, QTreeWidgetItem, pyqtSignal, pyqtSlot
from secop.gui.util import loadUi
QObject, QTreeWidgetItem, pyqtSignal, pyqtSlot, QBrush, QColor
from secop.gui.util import loadUi, Value
from secop.lib import formatExtendedTraceback
ITEM_TYPE_NODE = QTreeWidgetItem.UserType + 1
ITEM_TYPE_GROUP = QTreeWidgetItem.UserType + 2
@ -38,34 +39,78 @@ ITEM_TYPE_MODULE = QTreeWidgetItem.UserType + 3
ITEM_TYPE_PARAMETER = QTreeWidgetItem.UserType + 4
class QSECNode(SECNode, QObject):
class QSECNode(QObject):
newData = pyqtSignal(str, str, object) # module, parameter, data
stateChange = pyqtSignal(str, bool, str) # node name, online, connection state
unhandledMsg = pyqtSignal(str) # message
logEntry = pyqtSignal(str)
def __init__(self, opts, autoconnect=False, parent=None):
SECNode.__init__(self, opts, autoconnect)
def __init__(self, uri, parent=None):
QObject.__init__(self, parent)
self.conn = conn = secop.client.SecopClient(uri)
conn.validate_data = True
self.log = conn.log
self.contactPoint = conn.uri
conn.connect()
self.equipmentId = conn.properties['equipment_id']
self.nodename = '%s (%s)' % (self.equipmentId, conn.uri)
self.modules = conn.modules
self.properties = self.conn.properties
self.protocolVersion = conn.secop_version
conn.register(None, self) # generic callback
self.startup(True)
self._subscribeCallbacks()
# provide methods from old baseclient for making other gui code work
def _subscribeCallbacks(self):
for module in self.modules:
self._subscribeModuleCallback(module)
def getParameters(self, module):
return self.modules[module]['parameters']
def _subscribeModuleCallback(self, module):
for parameter in self.getParameters(module):
self._subscribeParameterCallback(module, parameter)
def getCommands(self, module):
return self.modules[module]['commands']
def _subscribeParameterCallback(self, module, parameter):
self.register_callback(module, parameter, self._newDataReceived)
def getModuleProperties(self, module):
return self.modules[module]['properties']
def _newDataReceived(self, module, parameter, data):
self.newData.emit(module, parameter, data)
def getProperties(self, module, parameter):
props = self.modules[module]['parameters'][parameter]
if 'unit' in props['datainfo']:
props['unit'] = props['datainfo']['unit']
return self.modules[module]['parameters'][parameter]
def setParameter(self, module, parameter, value):
self.conn.setParameter(module, parameter, value)
def getParameter(self, module, parameter):
return self.conn.getParameter(module, parameter, True)
def execCommand(self, module, command, arg):
return self.conn.execCommand(module, command, arg)
def queryCache(self, module):
return {k: Value(*self.conn.cache[(module, k)])
for k in self.modules[module]['parameters']}
def syncCommunicate(self, action, ident='', data=None):
reply = self.conn.request(action, ident, data)
return secop.client.encode_msg_frame(*reply).decode('utf-8')
def decode_message(self, msg):
# decode_msg needs bytes as input
return secop.client.decode_msg(msg.encode('utf-8'))
def _getDescribingParameterData(self, module, parameter):
return self.modules[module]['parameters'][parameter]
def updateEvent(self, module, parameter, value, timestamp, readerror):
self.newData.emit(module, parameter, Value(value, timestamp, readerror))
def nodeStateChange(self, online, state):
self.stateChange.emit(self.nodename, online, state)
def unhandledMessage(self, *msg):
self.unhandledMsg.emit('%s %s %r' % msg)
class MainWindow(QMainWindow):
askReopenSignal = pyqtSignal(str, str)
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
@ -83,7 +128,6 @@ class MainWindow(QMainWindow):
self._paramCtrls = {}
self._topItems = {}
self._currentWidget = self.splitter.widget(1).layout().takeAt(0)
self.askReopenSignal.connect(self.askReopen)
# add localhost (if available) and SEC nodes given as arguments
args = sys.argv[1:]
@ -95,7 +139,8 @@ class MainWindow(QMainWindow):
try:
self._addNode(host)
except Exception as e:
print(e)
print(formatExtendedTraceback())
print('error in addNode: %r' % e)
@pyqtSlot()
def on_actionAdd_SEC_node_triggered(self):
@ -132,32 +177,24 @@ class MainWindow(QMainWindow):
def _removeSubTree(self, toplevel_item):
self.treeWidget.invisibleRootItem().removeChild(toplevel_item)
def _nodeDisconnected_callback(self, nodename, host):
def _set_node_state(self, nodename, online, state):
node = self._nodes[nodename]
self._removeSubTree(self._topItems[node])
del self._topItems[node]
node.quit()
self.askReopenSignal.emit(nodename, host)
def askReopen(self, nodename, host):
result = QMessageBox.question(self.parent(), 'connection closed',
'connection to %s closed, reopen?' % nodename)
if result == QMessageBox.Yes:
self._addNode(host)
if online:
self._topItems[node].setBackground(0, QBrush(QColor('white')))
else:
self._topItems[node].setBackground(0, QBrush(QColor('orange')))
# TODO: make connection state be a separate row
node.contactPoint = '%s (%s)' % (node.conn.uri, state)
if nodename in self._nodeCtrls:
self._nodeCtrls[nodename].contactPointLabel.setText(node.contactPoint)
def _addNode(self, host):
# create client
port = 10767
if ':' in host:
host, port = host.split(':', 1)
port = int(port)
node = QSECNode({'host': host, 'port': port}, parent=self)
host = '%s:%d' % (host, port)
node = QSECNode(host, parent=self)
nodename = node.nodename
nodename = '%s (%s)' % (node.equipmentId, host)
self._nodes[nodename] = node
node.register_shutdown_callback(self._nodeDisconnected_callback, nodename, host)
# fill tree
nodeItem = QTreeWidgetItem(None, [nodename], ITEM_TYPE_NODE)
@ -171,11 +208,13 @@ class MainWindow(QMainWindow):
self.treeWidget.addTopLevelItem(nodeItem)
self._topItems[node] = nodeItem
node.stateChange.connect(self._set_node_state)
def _displayNode(self, node):
ctrl = self._nodeCtrls.get(node, None)
if ctrl is None:
ctrl = self._nodeCtrls[node] = NodeCtrl(self._nodes[node])
self._nodes[node].unhandledMsg.connect(ctrl._addLogEntry)
self._replaceCtrlWidget(ctrl)