Files
ecmc_plugin_motion/tools/ecmcArrayStat.py

542 lines
16 KiB
Python

#!/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()