From 8a3a2340b9e1386668a095d7a303d02e21880211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Sandstr=C3=B6m?= Date: Fri, 18 Aug 2023 09:26:45 +0200 Subject: [PATCH] Move all communications to separate class --- tools/ecmcMotionMainPyQtGraph.py | 97 +++++++++++++++++++++++++++++++- tools/ecmcPvDataItem.py | 87 ++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 tools/ecmcPvDataItem.py diff --git a/tools/ecmcMotionMainPyQtGraph.py b/tools/ecmcMotionMainPyQtGraph.py index 5cc5f83..9d961ae 100644 --- a/tools/ecmcMotionMainPyQtGraph.py +++ b/tools/ecmcMotionMainPyQtGraph.py @@ -80,11 +80,89 @@ pvNextAxisIndexNamePart2 = '-NxtObjId' pvmiddlestring='Plg-Mtn' + class comSignal(QObject): data_signal = pyqtSignal(object) +class dataItem(): + def __init__(self,pvPrefix , pvSuffix, pluginId, sampleRateHz, maxTimeSeconds): + self.pvPrefix = pvPrefix + self.pvSuffix = pvSuffix + self.sampleRateHz = sampleRateHz + self.pluginId = pluginId + self.maxTimeSeconds = maxTimeSeconds + self.maxElements = int(self.maxTimeSeconds * self.sampleRateHz) + self.data = {} + self.allowDataCollection = False + + self.pvName = self.pvPrefix + str(self.pluginId) + '-' + self.pvSuffix + if self.pvName is None: + raise RuntimeError("pvname must not be 'None'") + if len(self.pvName) == 0: + raise RuntimeError("pvname must not be ''") + + # Signals + self.pvSignalCallback = comSignal() + self.pvSignalCallback.data_signal.connect(self.sigCallback) + + # pv monitor callback + self.pv = epics.PV(self.pvName) + self.pv.add_callback(self.pvMonCallback) + + # Signal callbacks (update gui) + def sigCallback(self, value): + if value is not None: + if len(value) > 1: # Array + self.addData(value) + else: # Scalar + self.data = value + + # Call custom callback if needed + if self.extSigCallbackFunc is not None: + self.extSigCallbackFunc(value) + + # Pv monitor callbacks + def pvMonCallback(self,pvname=None, value=None, char_value=None,timestamp=None, **kw): + self.pvSignalCallback.data_signal.emit(value) + + # Call custom callback if needed + if self.extPvMonCallbackFunc is not None: + self.extPvMonCallbackFunc(pvname, value, char_value,timestamp, **kw) + + def getData(self): + return self.data + + def addData(self, values): + if not self.allowDataCollection: + return + + # Check if first assignment + if self.data is None: + self.data = values + return + + self.data=np.append(self.data,values) + + # check if delete in beginning is needed + currcount = len(self.data) + + # remove if needed + if currcount > self.maxElements: + self.data=self.data[currcount-self.maxElements:] + + self.datalength = len(self.data) + + def setAllowDataCollection(self,allow): + self.allowDataCollection = allow + + def regExtSigCallback(self, func): + self.extSigCallbackFunc = func + + def regExtPvMonCallback(self, func): + self.extPvMonCallbackFunc = func + class ecmcMtnMainGui(QtWidgets.QDialog): - def __init__(self,prefix=None,mtnPluginId=None): + def __init__(self,prefix="IOC_TEST:",mtnPluginId=0): super(ecmcMtnMainGui, self).__init__() self.pvnames = {} @@ -95,6 +173,11 @@ class ecmcMtnMainGui(QtWidgets.QDialog): self.plottedLineAnalog = {} self.plottedLineBinary = {} + self.testPV = dataItem(prefix + pvmiddlestring,'PosAct-Arr',mtnPluginId,1000,10) + self.testPV.regExtSigCallback(self.testSigCallback) + self.testPV.regExtPvMonCallback(self.testPVMonCallback) + self.testPV.setAllowDataCollection(True) + for pv in pvAnalog: self.plottedLineAnalog[pv] = None @@ -179,6 +262,16 @@ class ecmcMtnMainGui(QtWidgets.QDialog): self.resize(1000,850) return + + def testPVMonCallback(self,pvname=None, value=None, char_value=None,timestamp=None, **kw): + print('PV callback:') + print(pvname) + print(value) + + def testSigCallback(self,value): + print('SIG callback:') + print(value) + def createWidgets(self): self.graphicsLayoutWidget = pg.GraphicsLayoutWidget() self.graphicsLayoutWidget.setBackground('w') @@ -514,7 +607,7 @@ class ecmcMtnMainGui(QtWidgets.QDialog): def sig_cb_PosAct_Arr(self,value): if(np.size(value)) > 0: self.MtnYDataValid = True - self.addData('PosAct-Arr', value) + self.addData('PosAct-Arr', value) def sig_cb_PosSet_Arr(self,value): self.addData('PosSet-Arr', value) diff --git a/tools/ecmcPvDataItem.py b/tools/ecmcPvDataItem.py new file mode 100644 index 0000000..afaf90c --- /dev/null +++ b/tools/ecmcPvDataItem.py @@ -0,0 +1,87 @@ + + +import epics +import numpy as np +import time +import threading + +class comSignal(QObject): + data_signal = pyqtSignal(object) + +class dataItem(): + def __init__(self,pvPrefix , pvSuffix, pluginId, sampleRateHz, maxTimeSeconds): + self.pvPrefix = pvPrefix + self.pvSuffix = pvSuffix + self.sampleRateHz = sampleRateHz + self.pluginId = pluginId + self.maxTimeSeconds = maxTimeSeconds + self.maxElements = int(self.maxTimeSeconds * self.sampleRateHz) + self.data = {} + self.allowDataCollection = False + + self.pvName = self.pvPrefix + str(self.pluginId) + '-' + self.pvSuffix + if self.pvName is None: + raise RuntimeError("pvname must not be 'None'") + if len(self.pvName) == 0: + raise RuntimeError("pvname must not be ''") + + # Signals + self.pvSignalCallback = comSignal() + self.pvSignalCallback.data_signal.connect(self.sigCallback) + + # pv monitor callback + self.pv = epics.PV(self.pvName) + self.pv.add_callback(self.pvMonCallback) + + # Signal callbacks (update gui) + def sigCallback(self, value): + if value is not None: + if len(value) > 1: # Array + self.addData(value) + else: # Scalar + self.data = value + + # Call custom callback if needed + if self.extSigCallbackFunc is not None: + self.extSigCallbackFunc(value) + + # Pv monitor callbacks + def pvMonCallback(self,pvname=None, value=None, char_value=None,timestamp=None, **kw): + self.pvSignalCallback.data_signal.emit(value) + + # Call custom callback if needed + if self.extPvMonCallbackFunc is not None: + self.extPvMonCallbackFunc(pvname, value, char_value,timestamp, **kw) + + def getData(self): + return self.data + + def addData(self, values): + if not self.allowDataCollection: + return + + # Check if first assignment + if self.data is None: + self.data = values + return + + self.data=np.append(self.data,values) + + # check if delete in beginning is needed + currcount = len(self.data) + + # remove if needed + if currcount > self.maxElements: + self.data=self.data[currcount-self.maxElements:] + + self.datalength = len(self.data) + + def setAllowDataCollection(self,allow): + self.allowDataCollection = allow + + def regExtSigCallback(self, func): + self.extSigCallbackFunc = func + + def regExtPvMonCallback(self, func): + self.extPvMonCallbackFunc = func +