#!/usr/bin/python3.6 import sys import epics import numpy as np from PySide2 import QtCore, QtGui, QtWidgets from PySide2.QtCore import QObject from PySide2.QtCore import Signal as pyqtSignal, Slot as pyqtSlot #from PyQt5 import QtCore,QtWidgets, QtGui #from PyQt5.QtWidgets import * #from PyQt5.QtCore import * #from PyQt5.QtCore import QObject #from PyQt5.QtGui import * import random import ecmcTrend #import ecmcGraphWrapper as graphWrap #import matplotlib as mpl #mpl.use('Qt5Agg') #import matplotlib.pyplot as plt #import matplotlib.lines from datetime import datetime import threading PARSE_ERROR_ELEMENT_COUNT_OUT_OF_RANGE = 1000 ELEMENT_COUNT = 30 TIMESTAMP_INDEX = 30 TREND_DEFAULT_INDEX = 2 ECMC_COMMAND = { 'MOVE_VEL': 1, 'MOVE_REL' :2, 'MOVE_ABS' :3, 'MOVE_HOME' :10, } DATASOURCE = { 'AX_ID': 0, 'POS_SET' :1, 'POS_ACT' :2, 'POS_ERR' :3, 'POS_TARG' :4, 'POS_ERR_TARG' :5, 'POS_RAW' :6, 'CNTRL_OUT' :7, 'VEL_SET' :8, 'VEL_ACT' :9, 'VEL_FF_RAW' :10, 'VEL_RAW' :11, 'CYCLE_COUNTER' :12, 'ERROR' :13, 'COMMAND' :14, 'CMD_DATA' :15, 'SEQ_STATE' :16, 'ILOCK' :17, 'ILOCK_LAST_ACTIVE' :18, 'TRAJ_SOURCE' :19, 'ENC_SOURCE' :20, 'ENABLE' :21, 'ENABLED' :22, 'EXECUTE' :23, 'BUSY' :24, 'AT_TAGEY' :25, 'HOMED' :26, 'LOW_LIM' :27, 'HIGH_LIM' :28, 'HOME_SENSOR' :29 } DESCRIPTION = [ 'axId', 'posSet', 'posAct', 'posErr', 'posTarg', 'posErrTarg', 'posRaw', 'cntrlOut', 'velSet', 'velAct', 'velFFraw', 'velRaw', 'cycleCounter', 'error', 'command', 'cmdData', 'seqState', 'ilock', 'ilockLastActive', 'trajSource', 'encSource', 'enable', 'enabled', 'execute', 'busy', 'atTarget', 'homed', 'lowLim', 'highLim', 'homeSensor', ] STYLES={ 'ArrayStat': ''' QTableView{ background-color: white; foreground-color: black; font: bold; width: 430px; min-width: 430px; max-width: 430px; font-size:10pt; height:750px; min-height:750px; max-height:750px; } ''' } # You need to setup a signal slot mechanism, to # send data to your GUI in a thread-safe way. # Believe me, if you don't do this right, things # go very very wrong.. class comTable(QObject): data_signal = pyqtSignal(str,float) ''' End Class ''' # You need to setup a signal slot mechanism, to # send data to your GUI in a thread-safe way. # Believe me, if you don't do this right, things # go very very wrong.. class comTrend(QObject): data_signal = pyqtSignal(float) ''' End Class ''' class ecmcArrayStat(QtWidgets.QTableView): def __init__(self,parent=None): super(ecmcArrayStat, self).__init__(parent) self.background=None self.startToPlot=False; self.axId=0 self.posSet=0 self.posAct=0 self.posErr=0 self.posTarg=0 self.posErrTarg=0 self.posRaw=0 self.cntrlOut=0 self.velSet=0 self.velAct=0 self.velFFraw=0 self.velRaw=0 self.cycleCounter=0 self.error=0 self.errorCode=0 self.strTime=0 self.command=0 self.cmdData=0 self.seqState=0 self.ilock=0 self.ilockLastActive=0 self.trajSourc=0 self.encSource=0 self.enable=0 self.enabled=0 self.execute=0 self.busy=0 self.atTarget=0 self.homed=0 self.lowLim=0 self.highLim=0 self.homeSensor=0 self.trendValue = 0 self.trendDataIndex = TREND_DEFAULT_INDEX # Pos-Act self.dataList=[] self.dataSourceConvFuncPoint = { 0 :self.defaultStrFunc, 1 :self.defaultStrFunc, 2 :self.defaultStrFunc, 3 :self.defaultStrFunc, 4 :self.defaultStrFunc, 5 :self.defaultStrFunc, 6 :self.defaultStrFunc, 7 :self.defaultStrFunc, 8 :self.defaultStrFunc, 9 :self.defaultStrFunc, 10 :self.defaultStrFunc, 11 :self.defaultStrFunc, 12 :self.defaultStrFunc, 13 :self.errorFunc, 14 :self.commandStrFunc, 15 :self.cmdDataStrFunc, 16 :self.defaultStrFunc, 17 :self.iLockFunc, 18 :self.iLockFunc, 19 :self.sourceFunc, 20 :self.sourceFunc, 21 :self.defaultStrFunc, 22 :self.defaultStrFunc, 23 :self.defaultStrFunc, 24 :self.busyFunc, 25 :self.highOKLowNotOKFunc, 26 :self.highOKLowNotOKFunc, 27 :self.highOKLowNotOKFunc, 28 :self.highOKLowNotOKFunc, 29 :self.defaultStrFunc, } self.axisDiagPvName="" self.stdItemArrayName=[] self.stdItemArrayData=[] self.stdItemArraySelect=[] self.create_GUI() self.comTable = comTable() self.comTable.data_signal.connect(self.updateGUI) self.comTrend = comTrend() self.comTrend.data_signal.connect(self.trend.addData_callbackFunc) return def create_GUI(self): self.table = QtWidgets.QTableView(self) # SELECTING THE VIEW self.model = QtGui.QStandardItemModel(self) # SELECTING THE MODEL - FRAMEWORK THAT HANDLES QUERIES AND EDITS self.table.setModel(self.model) # SETTING THE MODEL self.populate() self.model.setHorizontalHeaderLabels(['Parameter', 'Value', '']) self.btnPlot=QtWidgets.QPushButton('Plot',default=False, autoDefault=False) self.trend=ecmcTrend.ecmcTrend() # Disable put button self.trend.enablePut(False) self.trend.setTitle("ecmc plot") self.show() def populate(self): for i in range(0,ELEMENT_COUNT): row= [] cell=QtGui.QStandardItem(DESCRIPTION[i]) cell.setFlags(QtCore.Qt.ItemIsEditable) cell.setBackground(QtGui.QBrush(QtCore.Qt.white)) cell.setForeground(QtGui.QBrush(QtCore.Qt.black)) self.stdItemArrayName.append(cell) row.append(cell) cell=QtGui.QStandardItem('value'+str(i)) cell.setFlags(QtCore.Qt.ItemIsEditable) cell.setBackground(QtGui.QBrush(QtCore.Qt.white)) cell.setForeground(QtGui.QBrush(QtCore.Qt.black)) self.stdItemArrayData.append(cell) row.append(cell) cell=QtGui.QStandardItem(True) cell.setCheckable(True) cell.setCheckState(QtCore.Qt.Unchecked) row.append(cell) self.stdItemArraySelect.append(cell) self.model.appendRow(row) #Timestamp row= [] cell=QtGui.QStandardItem("Timestamp") cell.setFlags(QtCore.Qt.ItemIsEditable) cell.setBackground(QtGui.QBrush(QtCore.Qt.white)) cell.setForeground(QtGui.QBrush(QtCore.Qt.black)) self.stdItemArrayName.append(cell) row.append(cell) cell=QtGui.QStandardItem('empty') cell.setFlags(QtCore.Qt.ItemIsEditable) cell.setBackground(QtGui.QBrush(QtCore.Qt.white)) cell.setForeground(QtGui.QBrush(QtCore.Qt.black)) self.stdItemArrayData.append(cell) row.append(cell) cell=QtGui.QStandardItem(True) cell.setCheckable(True) cell.setCheckState(QtCore.Qt.Unchecked) row.append(cell) self.stdItemArraySelect.append(cell) self.model.appendRow(row) self.formatTableView() def formatTableView(self): self.table.resizeRowsToContents() #self.table.resizeColumnToContents(0) self.table.setColumnWidth(0,100) self.table.setColumnWidth(1,200) self.table.setColumnWidth(2,20) #self.table.setHorizontalHeaderLabels(['Parameter', 'Value','Select']) def parseAxisStatArray(self,charData): self.dataList=charData.split(',') if len(self.dataList)!=ELEMENT_COUNT: return PARSE_ERROR_ELEMENT_COUNT_OUT_OF_RANGE #Update table view for i in range(0,ELEMENT_COUNT): if self.dataList[i] is not None: if len(self.dataList[i])>0: func=self.dataSourceConvFuncPoint[i] func(self.dataList[i],self.stdItemArrayData[i]) self.covertStringToData(self.dataList) self.update() if self.startToPlot: self.updateDataPlot() def defaultStrFunc(self,strValue,cell): cell.setData(strValue,role=QtCore.Qt.DisplayRole) def sourceFunc(self,strValue,cell): if int(strValue)==0: strToSet="Internal" else: strToSet="PLC" cell.setData(strToSet,role=QtCore.Qt.DisplayRole) def commandStrFunc(self,strValue,cell): switcher = { 1: "Move Vel", 2: "Move Rel", 3: "Move Abs", 10: "Move Home", } strToSet=switcher.get(int(strValue),strValue) if strToSet==strValue: cell.setBackground(QtGui.QBrush(QtCore.Qt.red)) else: cell.setBackground(QtGui.QBrush(QtCore.Qt.white)) cell.setData(strToSet,role=QtCore.Qt.DisplayRole) def highOKLowNotOKFunc(self,strValue,cell): if int(strValue)==1: strToSet="OK" cell.setBackground(QtGui.QBrush(QtCore.Qt.green)) else: strToSet="Not OK" cell.setBackground(QtGui.QBrush(QtCore.Qt.red)) cell.setData(strToSet,role=QtCore.Qt.DisplayRole) def busyFunc(self,strValue,cell): if int(strValue)==0: strToSet="Ready" cell.setBackground(QtGui.QBrush(QtCore.Qt.green)) else: strToSet="Busy" cell.setBackground(QtGui.QBrush(QtCore.Qt.red)) cell.setData(strToSet,role=QtCore.Qt.DisplayRole) def errorFunc(self,strValue,cell): if int(strValue,16)==0: cell.setBackground(QtGui.QBrush(QtCore.Qt.green)) else: cell.setBackground(QtGui.QBrush(QtCore.Qt.red)) cell.setData(strValue,role=QtCore.Qt.DisplayRole) def lowOKHighNotOKShowValFunc(self,strValue,cell): if int(strValue)==0: cell.setBackground(QtGui.QBrush(QtCore.Qt.green)) else: cell.setBackground(QtGui.QBrush(QtCore.Qt.red)) cell.setData(strValue,role=QtCore.Qt.DisplayRole) def iLockFunc(self,strValue,cell): if int(strValue)==0: cell.setBackground(QtGui.QBrush(QtCore.Qt.green)) cell.setData("OK",role=QtCore.Qt.DisplayRole) return switcher = { 1: "Soft Bwd", 2: "Soft Fwd", 3: "Hard Bwd", 4: "Hard Fwd", 5: "No Execute", 6: "Pos. Lag", 7: "Both Lim.", 8: "External", 9: "Transform", 10: "Max Vel.", 11: "Cntrl High Lim.", 12: "Cntrl Inc at Lim.", 13: "Axis Error", 14: "Unexp. Lim.", 15: "Vel. diff", 16: "Hardware", 17: "PLC", 18: "PLC Bwd", 19: "PLC Fwd", } strToSet=switcher.get(int(strValue),strValue) cell.setData(strToSet,role=QtCore.Qt.DisplayRole) cell.setBackground(QtGui.QBrush(QtCore.Qt.red)) def cmdDataStrFunc(self,strValue,cell): if self.command!=ECMC_COMMAND['MOVE_HOME']: self.defaultStrFunc(strValue,cell) return switcher = { 0: "Not defined", 1: "Low Lim", 2: "High Lim", 3: "Low Lim, Home Sens", 4: "High Lim, Home Sens", 5: "Low Lim, Center Home Sens", 6: "High Lim, Center Home Sens", 7: "Bwd, Home sens", 8: "Fwd, Home sens", 9: "Bwd, Center Home Sens", 10: "Fwd, Center Home Sens", 11: "Low Lim, Enc index", 12: "High Lim, Enc index", 15: "Home Direct", 21: "Low Lim Part Abs", 22: "High Lim Part Abs", } strToSet=switcher.get(int(strValue),strValue) if strToSet==strValue: cell.setBackground(QtGui.QBrush(QtCore.Qt.red)) else: cell.setBackground(QtGui.QBrush(QtCore.Qt.white)) cell.setData(strToSet,role=QtCore.Qt.DisplayRole) def covertStringToData(self,dataList): self.axId=int(dataList[0]) self.posSet=float(dataList[1]) self.posAct=float(dataList[2]) self.posErr=float(dataList[3]) self.posTarg=float(dataList[4]) self.posErrTarg=float(dataList[5]) self.posRaw=float(dataList[6]) self.cntrlOut=float(dataList[7]) self.velSet=float(dataList[8]) self.velAct=float(dataList[9]) self.velFFraw=float(dataList[10]) self.velRaw=float(dataList[11]) self.cycleCounter=int(dataList[12]) self.error=int(dataList[13],16) self.command=int(dataList[14]) self.cmdData=int(dataList[15]) self.seqState=int(dataList[16]) self.ilock=int(dataList[17]) self.ilockLastActive=int(dataList[18]) self.trajSource=int(dataList[19]) self.encSource=int(dataList[20]) self.enable=int(dataList[21]) self.enabled=int(dataList[22]) self.execute=int(dataList[23]) self.busy=int(dataList[24]) self.atTarget=int(dataList[25]) self.homed=int(dataList[26]) self.lowLim=int(dataList[27]) self.highLim=int(dataList[28]) self.homeSensor=int(dataList[29]) self.trendValue = float(dataList[self.trendDataIndex]) def onChangeAxisDiagPv(self,pvname=None, value=None, char_value=None,timestamp=None, **kw): self.comTable.data_signal.emit(char_value,timestamp) # All update of GUI here.. def updateGUI(self, char_value, timestamp): self.errorCode=self.parseAxisStatArray(char_value) if self.errorCode: print("Parse failed with error code: " + str(self.errorCode)) self.strTime=datetime.utcfromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S%f') self.stdItemArrayData[TIMESTAMP_INDEX].setData(self.strTime,role=QtCore.Qt.DisplayRole) def connect(self, pvname): if pvname is None: raise RuntimeError("pvname must not be 'None'") if len(pvname)==0: raise RuntimeError("pvname must not be ''") self.axisDiagPvName = pvname self.axisDiagPv = epics.PV(self.axisDiagPvName) self.axisDiagPv.add_callback(self.onChangeAxisDiagPv) self.trend.setTitle(self.axisDiagPvName) def disconnect(self): if self.axisDiagPv is not None: self.axisDiagPv.clear_callbacks() def printInfo(self): print("axId : " + str(self.axId)) print("posSet : " + str(self.posSet)) print("posAct : " + str(self.posAct)) print("posErr : " + str(self.posErr)) print("posTarg : " + str(self.posTarg)) print("posErrTarg : " + str(self.posErrTarg)) print("posRaw : " + str(self.posRaw)) print("cntrlOut : " + str(self.cntrlOut)) print("velSet : " + str(self.velSet)) print("velAct : " + str(self.velAct)) print("velFFraw : " + str(self.velFFraw)) print("velRaw : " + str(self.velRaw)) print("cycleCounter : " + str(self.cycleCounter)) print("error : " + str(self.error)) print("command : " + str(self.command)) print("cmdData : " + str(self.cmdData)) print("seqState : " + str(self.seqState)) print("ilock : " + str(self.ilock)) print("ilockLastActive : " + str(self.ilockLastActive)) print("trajSource : " + str(self.trajSource)) print("encSource : " + str(self.encSource)) print("enable : " + str(self.enable)) print("enabled : " + str(self.enabled)) print("execute : " + str(self.execute)) print("busy : " + str(self.busy)) print("atTarget : " + str(self.atTarget)) print("homed : " + str(self.homed)) print("lowLim : " + str(self.lowLim)) print("highLim : " + str(self.highLim)) print("homeSensor : " + str(self.homeSensor)) return def startPlot(self): self.trendDataIndex, label = self.checkPlotVar() self.trend.setYLabel(label) self.trend.show() self.startToPlot=True; def updateDataPlot(self): if self.startToPlot: self.comTrend.data_signal.emit(self.trendValue) # return first index of selected data (only support one var currently) def checkPlotVar(self): for i in range(0,ELEMENT_COUNT): if self.stdItemArraySelect[i].checkState(): return i, self.stdItemArrayName[i].text() return TREND_DEFAULT_INDEX, self.stdItemArrayName[TREND_DEFAULT_INDEX].text()