895 lines
31 KiB
Python
895 lines
31 KiB
Python
#*************************************************************************
|
|
# Copyright (c) 2020 European Spallation Source ERIC
|
|
# ecmc is distributed subject to a Software License Agreement found
|
|
# in file LICENSE that is included with this distribution.
|
|
#
|
|
# ecmcMtnMainGui.py
|
|
#
|
|
# Created on: October 6, 2020
|
|
# Author: Anders Sandström
|
|
#
|
|
# Plots two waveforms (x vs y) updates for each callback on the y-pv
|
|
#
|
|
#*************************************************************************
|
|
|
|
import sys
|
|
import epics
|
|
from PySide2 import QtCore, QtGui, QtWidgets
|
|
from PySide2.QtWidgets import *
|
|
from PySide2.QtGui import *
|
|
from PySide2.QtCore import *
|
|
|
|
#from PyQt5.QtWidgets import *
|
|
#from PyQt5 import QtWidgets
|
|
#from PyQt5.QtCore import *
|
|
#from PyQt5.QtGui import *
|
|
import numpy as np
|
|
from ecmcOneMotorGUI import *
|
|
import pyqtgraph as pg
|
|
from ecmcPvDataItem import *
|
|
from ecmcParseAxisStatusWord import *
|
|
|
|
import os
|
|
|
|
# Allow buffering of 10s data, need to add setting for this
|
|
xMaxTime = 10
|
|
caqtdmAxisExpertPanelName = "ecmcAxisExpert_v1.ui"
|
|
caqtdmExe ="/usr/local/bin/caqtdm"
|
|
|
|
# List of pv names
|
|
pvlist = [ 'BuffSze',
|
|
'ElmCnt',
|
|
'PosAct-Arr',
|
|
'PosSet-Arr',
|
|
'PosErr-Arr',
|
|
'Time-Arr',
|
|
# 'Ena-Arr',
|
|
# 'EnaAct-Arr',
|
|
# 'Bsy-Arr',
|
|
# 'Exe-Arr',
|
|
# 'TrjSrc-Arr',
|
|
# 'EncSrc-Arr',
|
|
# 'AtTrg-Arr',
|
|
'ErrId-Arr',
|
|
'Stat-Arr',
|
|
'Mde-RB',
|
|
'Cmd-RB',
|
|
'Stat',
|
|
'AxCmd-RB',
|
|
'SmpHz-RB',
|
|
'TrgCmd-RB',
|
|
'EnaCmd-RB' ]
|
|
|
|
pvAnalog = ['PosAct-Arr',
|
|
'PosSet-Arr',
|
|
'PosErr-Arr',
|
|
'ErrId-Arr',
|
|
'Stat-Arr']
|
|
|
|
pvAnaPLotsDefaultEnabled = ['PosAct-Arr',
|
|
'PosSet-Arr']
|
|
|
|
pvBinPLotsDefaultEnabled = ['enable',
|
|
'enabled',
|
|
'busy',
|
|
'attarget']
|
|
pvBinBlock = ['instartup',
|
|
'inrealtime',
|
|
'axisType',
|
|
'seqstate',
|
|
'lastilock']
|
|
|
|
# MCU info PVs
|
|
pvAxisPrefixNamePart1 ='MCU-Cfg-AX'
|
|
pvAxisPrefixNamePart2 ='-Pfx'
|
|
|
|
pvAxisNamePart1 ='MCU-Cfg-AX'
|
|
pvAxisNamePart2 ='-Nam'
|
|
|
|
pvFistAxisIndexName = 'MCU-Cfg-AX-FrstObjId'
|
|
pvNextAxisIndexNamePart1 = 'MCU-Cfg-AX'
|
|
pvNextAxisIndexNamePart2 = '-NxtObjId'
|
|
|
|
pvmiddlestring=':Plg-Mtn'
|
|
|
|
class ecmcMtnMainGui(QtWidgets.QDialog):
|
|
def __init__(self,prefix="IOC_TEST:",mtnPluginId=0):
|
|
super(ecmcMtnMainGui, self).__init__()
|
|
self.pvItems = {}
|
|
self.parseAxisStatWd = ecmcParseAxisStatusWord()
|
|
self.axisStatWdNames = self.parseAxisStatWd.getNames()
|
|
self.plottedLineAnalog = {}
|
|
self.plottedLineBinary = {}
|
|
self.pvAnalogDefaultCbState = {}
|
|
self.pvBinaryDefaultCbState = {}
|
|
self.dataStatWd = None
|
|
self.bufferSize = 0
|
|
for pv in pvlist:
|
|
self.pvItems[pv] = None
|
|
|
|
for pv in pvAnalog:
|
|
self.plottedLineAnalog[pv] = None
|
|
self.pvAnalogDefaultCbState[pv] = False
|
|
|
|
for pv in self.axisStatWdNames:
|
|
self.plottedLineBinary[pv] = None
|
|
self.pvBinaryDefaultCbState[pv] = False
|
|
|
|
for pv in pvBinPLotsDefaultEnabled:
|
|
self.pvBinaryDefaultCbState[pv] = True
|
|
|
|
for pv in pvAnaPLotsDefaultEnabled:
|
|
self.pvAnalogDefaultCbState[pv] = True
|
|
|
|
#Set some default plot colours
|
|
self.plotColor = {}
|
|
|
|
# Analog
|
|
self.plotColor['PosAct-Arr'] = 'g'
|
|
self.plotColor['PosSet-Arr'] = 'b'
|
|
self.plotColor['PosErr-Arr'] = 'r'
|
|
self.plotColor['ErrId-Arr'] = 'k'
|
|
self.plotColor['Stat-Arr'] = 'm'
|
|
|
|
self.checkboxColor={}
|
|
self.checkboxColor['PosAct-Arr'] ='green'
|
|
self.checkboxColor['PosSet-Arr'] ='blue'
|
|
self.checkboxColor['PosErr-Arr'] ='red'
|
|
self.checkboxColor['ErrId-Arr'] ='black'
|
|
self.checkboxColor['Stat-Arr'] ='magenta'
|
|
|
|
# Binary
|
|
self.plotColor['Ena-Arr'] = 'b'
|
|
self.plotColor['EnaAct-Arr'] = 'c'
|
|
self.plotColor['Bsy-Arr'] = 'r'
|
|
self.plotColor['Exe-Arr'] = 'm'
|
|
self.plotColor['TrjSrc-Arr'] = 'y'
|
|
self.plotColor['EncSrc-Arr'] = 'k'
|
|
self.plotColor['AtTrg-Arr'] = 'g'
|
|
|
|
self.checkboxColor['Ena-Arr'] = 'blue'
|
|
self.checkboxColor['EnaAct-Arr'] = 'cyan'
|
|
self.checkboxColor['Bsy-Arr'] = 'red'
|
|
self.checkboxColor['Exe-Arr'] = 'magenta'
|
|
self.checkboxColor['TrjSrc-Arr'] = 'yellow'
|
|
self.checkboxColor['EncSrc-Arr'] = 'black'
|
|
self.checkboxColor['AtTrg-Arr'] = 'green'
|
|
|
|
self.offline = True
|
|
self.pvPrefixStr = prefix
|
|
self.pvPrefixOrigStr = prefix # save for restore after open datafile
|
|
self.mtnPluginId = mtnPluginId
|
|
self.mtnPluginOrigId = mtnPluginId
|
|
self.allowSave = False
|
|
self.path = '.'
|
|
self.unitAnalogY = "[]"
|
|
self.unitBinaryY = "[]"
|
|
self.labelBinaryY = "Binary"
|
|
self.labelAnalogY = "Analog"
|
|
self.title = ""
|
|
|
|
self.sampleRate = 1000
|
|
self.sampleRateValid = False
|
|
self.MtnYDataValid = False
|
|
self.MtnXDataValid = False
|
|
|
|
if prefix is None or mtnPluginId is None:
|
|
self.offline = True
|
|
self.pause = True
|
|
|
|
self.startupDone=False
|
|
self.pause = 0
|
|
|
|
self.createWidgets()
|
|
self.resize(1800,850)
|
|
|
|
self.connectToEcmc()
|
|
self.bufferSize = int(self.sampleRate*xMaxTime)
|
|
self.initPVs(self.bufferSize)
|
|
|
|
# read sample rate to bea able to deduce buffer size
|
|
self.setStatusOfWidgets()
|
|
return
|
|
|
|
def connectToEcmc(self):
|
|
|
|
# Check connection and read sample rate
|
|
pvSampleRate = epics.PV(self.pvPrefixStr + pvmiddlestring + str(int(self.mtnPluginId))+ '-SmpHz-RB')
|
|
print(self.pvPrefixStr + pvmiddlestring + str(int(self.mtnPluginId))+ '-SmpHz-RB')
|
|
connected = pvSampleRate.wait_for_connection(timeout = 3)
|
|
if connected:
|
|
print('Connected to ecmc')
|
|
self.offline = False
|
|
self.pause = False
|
|
self.sampleRate = pvSampleRate.get()
|
|
if self.sampleRate is None:
|
|
print("Read sample rate failed. fallback to 1000Hz")
|
|
self.sampleRate = 1000
|
|
else:
|
|
print('Not Connected')
|
|
self.offline = True
|
|
self.pause = True
|
|
self.sampleRate = 1000
|
|
|
|
self.sampleRateValid = True
|
|
|
|
# calc x Array
|
|
step=1/self.sampleRate
|
|
self.x = np.arange(-xMaxTime,0,step)
|
|
|
|
# Read available axes
|
|
self.readAxisList()
|
|
|
|
|
|
def initPVs(self,bufferSize):
|
|
for pvname in pvlist:
|
|
if pvname in pvAnalog:
|
|
self.pvItems[pvname] = ecmcPvDataItem(prefix + pvmiddlestring,pvname,self.mtnPluginId,bufferSize)
|
|
else:
|
|
self.pvItems[pvname] = ecmcPvDataItem(prefix + pvmiddlestring,pvname,self.mtnPluginId,1)
|
|
|
|
self.pvItems[pvname].setAllowDataCollection(True)
|
|
|
|
# register special callbacks
|
|
self.pvItems['PosAct-Arr'].regExtSigCallback(self.sig_cb_PosAct_Arr)
|
|
self.pvItems['Time-Arr'].regExtSigCallback(self.sig_cb_Time_Arr)
|
|
self.pvItems['Mde-RB'].regExtSigCallback(self.sig_cb_Mde_RB)
|
|
self.pvItems['AxCmd-RB'].regExtSigCallback(self.sig_cb_AxCmd_RB)
|
|
self.pvItems['EnaCmd-RB'].regExtSigCallback(self.sig_cb_EnaCmd_RB)
|
|
self.pvItems['Stat-Arr'].regExtSigCallback(self.sig_cb_Stat_Arr)
|
|
|
|
# Example calls to ecmcPvDataItem:
|
|
#self.testPV.regExtSigCallback(self.testSigCallback)
|
|
#self.testPV.regExtPvMonCallback(self.testPVMonCallback)
|
|
#self.testPV.setAllowDataCollection(True)
|
|
|
|
def createWidgets(self):
|
|
self.graphicsLayoutWidget = pg.GraphicsLayoutWidget()
|
|
self.graphicsLayoutWidget.setBackground('w')
|
|
self.plotItemAnalog = self.graphicsLayoutWidget.addPlot(row=0,col=0)
|
|
self.plotItemBinary = self.graphicsLayoutWidget.addPlot(row=1,col=0)
|
|
self.plotItemBinary.setFixedHeight(150)
|
|
self.plotItemBinary.setMouseEnabled(y=False)
|
|
self.plotItemBinary.setLabel('bottom', 'Time [s]')
|
|
|
|
self.pauseBtn = QPushButton(text = 'pause')
|
|
self.pauseBtn.setFixedSize(100, 50)
|
|
self.pauseBtn.clicked.connect(self.pauseBtnAction)
|
|
self.pauseBtn.setStyleSheet("background-color: green")
|
|
#self.pauseBtn.setEnabled(False)
|
|
#self.pauseBtn.setVisible(False)
|
|
|
|
self.openBtn = QPushButton(text = 'open data')
|
|
self.openBtn.setFixedSize(100, 50)
|
|
self.openBtn.clicked.connect(self.openBtnAction)
|
|
self.openBtn.setEnabled(False)
|
|
self.openBtn.setVisible(False)
|
|
|
|
self.saveBtn = QPushButton(text = 'save data')
|
|
self.saveBtn.setFixedSize(100, 50)
|
|
self.saveBtn.clicked.connect(self.saveBtnAction)
|
|
self.saveBtn.setEnabled(False)
|
|
self.saveBtn.setVisible(False)
|
|
|
|
self.enableBtn = QPushButton(text = 'enable Mtn')
|
|
self.enableBtn.setFixedSize(100, 50)
|
|
self.enableBtn.clicked.connect(self.enableBtnAction)
|
|
self.triggBtn = QPushButton(text = 'trigg Mtn')
|
|
self.triggBtn.setFixedSize(100, 50)
|
|
self.triggBtn.setEnabled(False)
|
|
self.triggBtn.setVisible(False)
|
|
|
|
self.triggBtn.clicked.connect(self.triggBtnAction)
|
|
self.zoomBtn = QPushButton(text = 'auto zoom')
|
|
self.zoomBtn.setFixedSize(100, 50)
|
|
self.zoomBtn.clicked.connect(self.zoomBtnAction)
|
|
self.zoomBtn.setEnabled(False)
|
|
self.zoomBtn.setVisible(False)
|
|
|
|
self.modeCombo = QComboBox()
|
|
self.modeCombo.setFixedSize(100, 50)
|
|
self.modeCombo.currentIndexChanged.connect(self.newModeIndexChanged)
|
|
self.modeCombo.addItem("CONT")
|
|
self.modeCombo.addItem("TRIGG")
|
|
self.modeCombo.setEnabled(False)
|
|
self.modeCombo.setVisible(False)
|
|
|
|
self.progressBar = QProgressBar()
|
|
self.progressBar.reset()
|
|
self.progressBar.setMinimum(0)
|
|
self.progressBar.setMaximum(100) #100%
|
|
self.progressBar.setValue(0)
|
|
self.progressBar.setFixedHeight(20)
|
|
self.progressBar.setEnabled(False)
|
|
self.progressBar.setVisible(False)
|
|
|
|
# Fix layout
|
|
self.setGeometry(300, 300, 1200, 900)
|
|
|
|
frameMainLeft = QFrame(self)
|
|
layoutVertMain = QVBoxLayout()
|
|
|
|
# Bottom button section
|
|
layoutControl = QHBoxLayout()
|
|
layoutControl.addWidget(self.pauseBtn)
|
|
layoutControl.addWidget(self.enableBtn)
|
|
layoutControl.addWidget(self.triggBtn)
|
|
layoutControl.addWidget(self.modeCombo)
|
|
layoutControl.addWidget(self.zoomBtn)
|
|
layoutControl.addWidget(self.saveBtn)
|
|
layoutControl.addWidget(self.openBtn)
|
|
|
|
frameControl = QFrame(self)
|
|
frameControl.setFixedHeight(70)
|
|
frameControl.setLayout(layoutControl)
|
|
|
|
# Centre section
|
|
framePlots = QFrame(self)
|
|
layoutHorPlots = QHBoxLayout()
|
|
framePlots.setLayout(layoutHorPlots)
|
|
|
|
# Centre left
|
|
layoutHorPlots.addWidget(self.graphicsLayoutWidget)
|
|
|
|
# Centre right
|
|
framePlotsSelection = QFrame(self)
|
|
layoutVertPlotsSelection = QVBoxLayout()
|
|
framePlotsSelection.setFixedWidth(150)
|
|
|
|
# Centre right upper (analog selection)
|
|
framePlotsSelectionUpper = QFrame(self)
|
|
layoutVertPlotsSelection.setSpacing(0)
|
|
layoutVertPlotsSelectionUpper = QVBoxLayout()
|
|
anaSelectLabel = QLabel('Analog:')
|
|
layoutVertPlotsSelectionUpper.addWidget(anaSelectLabel)
|
|
|
|
self.checkBoxListAnalog={}
|
|
for pv in pvAnalog:
|
|
self.checkBoxListAnalog[pv] = QCheckBox(pv)
|
|
self.checkBoxListAnalog[pv].setChecked(self.pvAnalogDefaultCbState[pv])
|
|
self.checkBoxListAnalog[pv].setStyleSheet("color: " + self.checkboxColor[pv])
|
|
layoutVertPlotsSelectionUpper.addWidget(self.checkBoxListAnalog[pv])
|
|
self.checkBoxListAnalog[pv].toggled.connect(self.checkBoxStateChangedAnalog)
|
|
|
|
layoutVertPlotsSelectionUpper.addSpacing(0)
|
|
|
|
framePlotsSelectionUpper.setLayout(layoutVertPlotsSelectionUpper)
|
|
layoutVertPlotsSelection.addWidget(framePlotsSelectionUpper)
|
|
|
|
# Centre right lower (binary selection)
|
|
framePlotsSelectionLower = QFrame(self)
|
|
layoutVertPlotsSelectionLower = QVBoxLayout()
|
|
binSelectLabel=QLabel('Binary:')
|
|
layoutVertPlotsSelectionLower.addWidget(binSelectLabel)
|
|
self.checkBoxListBinary={}
|
|
|
|
i = 0
|
|
for pv in self.axisStatWdNames:
|
|
if pv in pvBinBlock:
|
|
i += 1
|
|
continue
|
|
|
|
self.checkBoxListBinary[pv] = QCheckBox(pv)
|
|
self.checkBoxListBinary[pv].setChecked(self.pvBinaryDefaultCbState[pv])
|
|
color = pg.intColor(i)
|
|
p = QPalette(color)
|
|
p.setBrush(QPalette.WindowText,color)
|
|
self.checkBoxListBinary[pv].setPalette(p)
|
|
layoutVertPlotsSelectionLower.addWidget(self.checkBoxListBinary[pv])
|
|
self.checkBoxListBinary[pv].toggled.connect(self.checkBoxStateChangedBinary)
|
|
i += 1
|
|
|
|
framePlotsSelectionLower.setLayout(layoutVertPlotsSelectionLower)
|
|
|
|
layoutVertPlotsSelection.addSpacing(200)
|
|
|
|
layoutVertPlotsSelection.addWidget(framePlotsSelectionLower)
|
|
|
|
framePlotsSelection.setLayout(layoutVertPlotsSelection)
|
|
layoutHorPlots.addWidget(framePlotsSelection)
|
|
|
|
layoutVertMain.addWidget(framePlots)
|
|
layoutVertMain.addWidget(frameControl)
|
|
layoutVertMain.addWidget(self.progressBar)
|
|
|
|
frameMainLeft.setLayout(layoutVertMain)
|
|
|
|
frameMotion = QFrame(self)
|
|
layoutMotionGrid = QGridLayout()
|
|
frameMotion.setLayout(layoutMotionGrid)
|
|
|
|
self.btnMotorRecord = QPushButton(text = 'Motor Record')
|
|
self.btnMotorRecord.clicked.connect(self.openMotorRecordPanel)
|
|
self.btnMotorRecord.setFixedSize(150, 50)
|
|
layoutMotionGrid.addWidget(self.btnMotorRecord,0,0)
|
|
|
|
self.btnCaQTDmAxisExpert = QPushButton(text = 'caqtdm ecmc Axis')
|
|
self.btnCaQTDmAxisExpert.clicked.connect(self.openCaQTDmAxisExpert)
|
|
self.btnCaQTDmAxisExpert.setFixedSize(150, 50)
|
|
layoutMotionGrid.addWidget(self.btnCaQTDmAxisExpert,1,0)
|
|
|
|
label = QLabel('Axis id:')
|
|
self.cmbBxSelectAxis = QComboBox()
|
|
self.cmbBxSelectAxis.currentIndexChanged.connect(self.changeAxisIndex)
|
|
|
|
layoutMotionGrid.addWidget(label,2,0)
|
|
layoutMotionGrid.addWidget(self.cmbBxSelectAxis,2,1)
|
|
|
|
|
|
layoutVertMain.addWidget(frameMotion)
|
|
layoutMain = QHBoxLayout()
|
|
layoutMain.addWidget(frameMainLeft)
|
|
layoutMain.addWidget(frameMotion)
|
|
|
|
self.setLayout(layoutMain)
|
|
|
|
def setStatusOfWidgets(self):
|
|
self.saveBtn.setEnabled(self.allowSave)
|
|
if self.offline:
|
|
self.enableBtn.setStyleSheet("background-color: grey")
|
|
self.enableBtn.setEnabled(False)
|
|
self.pauseBtn.setStyleSheet("background-color: grey")
|
|
self.pauseBtn.setEnabled(False)
|
|
self.modeCombo.setEnabled(False)
|
|
self.triggBtn.setEnabled(False)
|
|
self.setWindowTitle("ecmc Mtn Main plot: Offline")
|
|
else:
|
|
self.modeCombo.setEnabled(True)
|
|
# Check actual value of pvs
|
|
enable = self.pvItems['EnaCmd-RB'].pvGet()
|
|
if enable is None:
|
|
print("pvs['EnaCmd-RB'].get() failed")
|
|
return
|
|
if(enable>0):
|
|
self.enableBtn.setStyleSheet("background-color: green")
|
|
else:
|
|
self.enableBtn.setStyleSheet("background-color: red")
|
|
|
|
# Mode
|
|
self.mode = self.pvItems['Mde-RB'].pvGet()
|
|
if self.mode is None:
|
|
print("pvs['Mde-RB'].get() failed")
|
|
self.modeStr = "NO_MODE"
|
|
|
|
self.triggBtn.setEnabled(False) # Only enable if mode = TRIGG = 2
|
|
if self.mode == 1:
|
|
self.modeStr = "CONT"
|
|
self.modeCombo.setCurrentIndex(self.pvItems['Mde-RB'].getData()-1) # Index start at zero
|
|
|
|
if self.mode == 2:
|
|
self.modeStr = "TRIGG"
|
|
self.triggBtn.setEnabled(True)
|
|
self.modeCombo.setCurrentIndex(self.pvItems['Mde-RB'].getData()-1) # Index start at zero
|
|
|
|
|
|
self.setWindowTitle("ecmc Mtn Main plot: prefix=" + self.pvPrefixStr + " , mtnId=" + str(self.mtnPluginId) +
|
|
", rate=" + str(self.sampleRate))
|
|
|
|
QCoreApplication.processEvents()
|
|
|
|
def sig_cb_PosAct_Arr(self,value):
|
|
if self.pause:
|
|
return
|
|
if(np.size(value)) > 0:
|
|
self.MtnYDataValid = True
|
|
|
|
def sig_cb_Time_Arr(self,value):
|
|
if self.pause:
|
|
return
|
|
if(np.size(value)) > 0:
|
|
self.MtnXDataValid = True
|
|
self.plotAll()
|
|
return
|
|
|
|
def sig_cb_Mde_RB(self,value):
|
|
if value < 1 or value> 2:
|
|
self.modeStr = "NO_MODE"
|
|
print('callbackFuncMode: Error Invalid mode.')
|
|
return
|
|
|
|
self.modeCombo.setCurrentIndex(value-1) # Index starta t zero
|
|
|
|
if value == 1:
|
|
self.modeStr = "CONT"
|
|
self.triggBtn.setEnabled(False) # Only enable if mode = TRIGG = 2
|
|
|
|
if value == 2:
|
|
self.modeStr = "TRIGG"
|
|
self.triggBtn.setEnabled(True)
|
|
return
|
|
|
|
def sig_cb_AxCmd_RB(self,value):
|
|
if value is None:
|
|
return
|
|
print('Axis Id Value: ' + str(value))
|
|
|
|
id = self.cmbBxSelectAxis.findText(str(int(value)))
|
|
if id >= 0:
|
|
self.cmbBxSelectAxis.setCurrentIndex(id)
|
|
|
|
axisPrefixPvName = self.pvPrefixStr + ":" + pvAxisPrefixNamePart1 + str(int(value)) + pvAxisPrefixNamePart2
|
|
prefixPV = epics.PV(axisPrefixPvName)
|
|
axisPrefix = prefixPV.get()
|
|
if axisPrefix is not None:
|
|
self.axisPrefix = axisPrefix.rstrip(':')
|
|
|
|
axisNamePvName = self.pvPrefixStr + ":" + pvAxisNamePart1 + str(int(value)) + pvAxisNamePart2
|
|
namePV = epics.PV(axisNamePvName)
|
|
axisName = namePV.get()
|
|
if axisName is not None:
|
|
self.axisName = axisName
|
|
|
|
def sig_cb_EnaCmd_RB(self,value):
|
|
self.pvItems['EnaCmd-RB'].pvPut(value)
|
|
if value:
|
|
self.enableBtn.setStyleSheet("background-color: green")
|
|
else:
|
|
self.enableBtn.setStyleSheet("background-color: red")
|
|
return
|
|
|
|
def sig_cb_Stat_Arr(self,value):
|
|
if self.pause:
|
|
return
|
|
|
|
data = self.parseAxisStatWd.convert(value)
|
|
self.addStatWdData(data)
|
|
|
|
def addStatWdData(self, values):
|
|
|
|
# Check if first assignment
|
|
if self.dataStatWd is None:
|
|
self.dataStatWd = values
|
|
return
|
|
|
|
self.dataStatWd = np.append(self.dataStatWd,values,axis=1)
|
|
|
|
# check if delete in beginning is needed
|
|
currcount = self.dataStatWd.shape[1]
|
|
|
|
# remove if needed
|
|
if currcount > self.bufferSize:
|
|
self.dataStatWd=self.dataStatWd[:,currcount-self.bufferSize:]
|
|
|
|
self.dataStatWdlength = len(self.dataStatWd)
|
|
|
|
# State chenge for Analog checkboxes
|
|
def checkBoxStateChangedAnalog(self, int):
|
|
# refresh plots
|
|
self.plotAnalog()
|
|
|
|
# State chenge for Binary checkboxes
|
|
def checkBoxStateChangedBinary(self, int):
|
|
# refresh plots
|
|
self.plotBinary()
|
|
|
|
def readAxisList(self):
|
|
axIdPV = epics.PV(self.pvPrefixStr + ":" + pvFistAxisIndexName)
|
|
axId = axIdPV.get()
|
|
if axId is None:
|
|
print('ERROR: First Axis Index PV not found.')
|
|
return
|
|
|
|
self.cmbBxSelectAxis.clear()
|
|
self.cmbBxSelectAxis.addItem(str(int(axId)))
|
|
|
|
while axId >= 0:
|
|
# Get next axis id
|
|
pvName = self.pvPrefixStr + ":" + pvNextAxisIndexNamePart1 + str(int(axId)) + pvNextAxisIndexNamePart2
|
|
axIdPV = epics.PV(pvName)
|
|
axId = axIdPV.get()
|
|
|
|
if axId > 0:
|
|
self.cmbBxSelectAxis.addItem(str(int(ax)))
|
|
|
|
def changeAxisIndex(self,xxx):
|
|
if self.cmbBxSelectAxis.currentData() is not None:
|
|
self.pvItems['AxCmd-RB'].pvPut(self.cmbBxSelectAxis.currentData(), use_complete=True)
|
|
|
|
def openMotorRecordPanel(self,xxx):
|
|
self.dialog = MotorPanel(self,self.axisPrefix + ':' ,self.axisName)
|
|
self.dialog.resize(500, 900)
|
|
self.dialog.show()
|
|
|
|
def openCaQTDmAxisExpert(self,xxx):
|
|
# caqtdm -macro "IOC=$I,SYS=$S,Axis=$A" ecmcAxisExpert_v1.ui
|
|
caqtdmString = caqtdmExe + " -macro "
|
|
caqtdmMacros = "SYS=" + self.axisPrefix
|
|
caqtdmMacros += ",IOC=" + self.axisPrefix
|
|
caqtdmMacros += ",Axis=" + self.axisName
|
|
|
|
caqtdmString+= "\"" + caqtdmMacros + "\"" + " " + caqtdmAxisExpertPanelName
|
|
print(caqtdmString)
|
|
#subprocess.call(caqtdmString)
|
|
os.system(caqtdmString)
|
|
|
|
###### Widget callbacks
|
|
def pauseBtnAction(self):
|
|
self.pause = not self.pause
|
|
if self.pause:
|
|
self.pauseBtn.setStyleSheet("background-color: red")
|
|
else:
|
|
self.pvPrefixStr = self.pvPrefixOrigStr # Restore if dataset was opened
|
|
self.mtnPluginId = self.mtnPluginOrigId # Restore if dataset was opened
|
|
self.pauseBtn.setStyleSheet("background-color: green")
|
|
|
|
return
|
|
|
|
def enableBtnAction(self):
|
|
|
|
currValue = self.pvItems['EnaCmd-RB'].getData()
|
|
|
|
self.pvItems['EnaCmd-RB'].pvPut(not currValue)
|
|
if self.pvItems['EnaCmd-RB'].getData():
|
|
self.enableBtn.setStyleSheet("background-color: green")
|
|
else:
|
|
self.enableBtn.setStyleSheet("background-color: red")
|
|
return
|
|
|
|
def triggBtnAction(self):
|
|
self.pvItems['TrgCmd-RB'].pvPut(True)
|
|
return
|
|
|
|
def zoomBtnAction(self):
|
|
if self.pvItems['Time-Arr'].getData() is None:
|
|
return
|
|
|
|
if self.pvItems['PosAct-Arr'].getData() is None:
|
|
return
|
|
|
|
self.plotAnalog(True)
|
|
self.plotBinary(True)
|
|
return
|
|
|
|
def newModeIndexChanged(self,index):
|
|
if self.pvItems is None:
|
|
return
|
|
|
|
if self.pvItems['Mde-RB'] is None:
|
|
return
|
|
|
|
if index==0 or index==1:
|
|
if not self.offline and self.pvItems['Mde-RB'] is not None:
|
|
self.pvItems['Mde-RB'].pvPut(index+1)
|
|
return
|
|
|
|
def openBtnAction(self):
|
|
#if not self.offline:
|
|
# self.pause = 1 # pause while open if online
|
|
# self.pauseBtn.setStyleSheet("background-color: red")
|
|
# QCoreApplication.processEvents()
|
|
#
|
|
#fname = QFileDialog.getOpenFileName(self, 'Open file', self.path, "Data files (*.npz)")
|
|
#if fname is None:
|
|
# return
|
|
#if np.size(fname) != 2:
|
|
# return
|
|
#if len(fname[0])<=0:
|
|
# return
|
|
#self.path = os.path.dirname(os.path.abspath(fname[0]))
|
|
#
|
|
#npzfile = np.load(fname[0])
|
|
#
|
|
## verify scope plugin
|
|
#if npzfile['plugin'] != "Mtn":
|
|
# print ("Invalid data type (wrong plugin type)")
|
|
# return
|
|
#
|
|
## File valid
|
|
#self.data['Time-Arr'] = npzfile['rawdataX']
|
|
#self.rawdataY = npzfile['rawdataY']
|
|
#self.dataX = npzfile['spectX']
|
|
#self.spectY = npzfile['spectY']
|
|
#self.sampleRate = npzfile['sampleRate']
|
|
#self.NMtn = npzfile['NMtn']
|
|
#self.data['Mde-RB'] = npzfile['mode']
|
|
#self.pvPrefixStr = str(npzfile['pvPrefixStr'])
|
|
#self.mtnPluginId = npzfile['mtnPluginId']
|
|
#if 'unitRawY' in npzfile:
|
|
# self.unitAnalogY = str(npzfile['unitRawY'])
|
|
#if 'unitSpectY' in npzfile:
|
|
# self.unitSpectY = str(npzfile['unitSpectY'])
|
|
#if 'labelRawY' in npzfile:
|
|
# self.labelAnalogY = str(npzfile['labelRawY'])
|
|
#if 'labelSpectY' in npzfile:
|
|
# self.labelSpectY = str(npzfile['labelSpectY'])
|
|
#if 'title' in npzfile:
|
|
# self.title = str(npzfile['title'])
|
|
#
|
|
#self.buildPvNames()
|
|
#
|
|
## trigg draw
|
|
#self.MtnYDataValid = True
|
|
#self.MtnXDataValid = True
|
|
#self.RawYDataValid = True
|
|
#self.RawXDataValid = True
|
|
#self.sampleRateValid = True
|
|
|
|
|
|
#self.comSignalMode.data_signal.emit(self.data['Mde-RB'])
|
|
#self.comSignalX.data_signal.emit(self.dataX)
|
|
#self.comSignalSpectY.data_signal.emit(self.spectY)
|
|
#self.comSignalRawData.data_signal.emit(self.rawdataY)
|
|
|
|
#self.setStatusOfWidgets()
|
|
#
|
|
#self.startupDone=True
|
|
#self.zoomBtnAction()
|
|
return
|
|
|
|
def saveBtnAction(self):
|
|
#fname = QFileDialog.getSaveFileName(self, 'Save file', self.path, "Data files (*.npz)")
|
|
#if fname is None:
|
|
# return
|
|
#if np.size(fname) != 2:
|
|
# return
|
|
#if len(fname[0])<=0:
|
|
# return
|
|
## Save all relevant data
|
|
#np.savez(fname[0],
|
|
# plugin = "Mtn",
|
|
# rawdataX = self.data['Time-Arr'],
|
|
# rawdataY = self.rawdataY,
|
|
# spectX = self.dataX,
|
|
# spectY = self.spectY,
|
|
# sampleRate = self.sampleRate,
|
|
# NMtn = self.NMtn,
|
|
# mode = self.data['Mde-RB'],
|
|
# pvPrefixStr = self.pvPrefixStr,
|
|
# mtnPluginId = self.mtnPluginId,
|
|
# unitRawY = self.unitAnalogY,
|
|
# unitSpectY = self.unitSpectY,
|
|
# labelRawY = self.labelAnalogY,
|
|
# labelSpectY = self.labelSpectY,
|
|
# title = self.title
|
|
# )
|
|
#
|
|
#self.path = os.path.dirname(os.path.abspath(fname[0]))
|
|
|
|
return
|
|
|
|
def plotAll(self):
|
|
if self.MtnYDataValid and self.MtnXDataValid:
|
|
self.plotAnalog()
|
|
self.plotBinary()
|
|
self.MtnYDataValid = False
|
|
self.RawYDataValid = False
|
|
|
|
def convertStatusWordToBits(self,statwd):
|
|
print('test')
|
|
|
|
|
|
def plotAnalog(self, autozoom=False):
|
|
|
|
if self.pvItems['Time-Arr'].getData() is None:
|
|
print('Error: No data')
|
|
return
|
|
|
|
if self.pvItems['PosAct-Arr'].getData() is None:
|
|
print('Error: No data')
|
|
return
|
|
|
|
# plot data
|
|
minimum_x = 0
|
|
for pv in pvAnalog:
|
|
if self.pvItems[pv] is not None:
|
|
|
|
y = self.pvItems[pv].getData()
|
|
if y is None:
|
|
print('Y is None')
|
|
continue
|
|
if self.x is None:
|
|
print('X is None')
|
|
continue
|
|
|
|
x_len=len(self.x)
|
|
y_len=len(y)
|
|
|
|
if self.checkBoxListAnalog[pv].isChecked():
|
|
if self.plottedLineAnalog[pv] is None:
|
|
plotpen=pg.mkPen(self.plotColor[pv],width=2)
|
|
self.plottedLineAnalog[pv] = self.plotItemAnalog.plot(self.x[x_len-y_len:],y,pen=plotpen)
|
|
self.plotItemAnalog.showGrid(x=True,y=True)
|
|
else:
|
|
self.plottedLineAnalog[pv].setData(self.x[x_len-y_len:],y)
|
|
minimum_x_temp=-y_len/self.sampleRate
|
|
if minimum_x_temp < minimum_x:
|
|
minimum_x = minimum_x_temp
|
|
else:
|
|
if self.plottedLineAnalog[pv] is not None:
|
|
self.plotItemAnalog.removeItem(self.plottedLineAnalog[pv])
|
|
self.plottedLineAnalog[pv] = None
|
|
|
|
self.allowSave = True
|
|
self.saveBtn.setEnabled(True)
|
|
|
|
def plotBinary(self, autozoom=False):
|
|
|
|
|
|
if self.dataStatWd is None:
|
|
print('Error: No data')
|
|
return
|
|
if self.x is None:
|
|
print('X is None')
|
|
return
|
|
|
|
# plot data
|
|
minimum_x = 0
|
|
x_len=len(self.x)
|
|
|
|
i = 0
|
|
|
|
for pv in self.axisStatWdNames:
|
|
if pv in pvBinBlock:
|
|
i += 1
|
|
continue
|
|
|
|
if self.checkBoxListBinary[pv].isChecked():
|
|
y = self.dataStatWd[i,:]
|
|
if y is None:
|
|
print('Y is None')
|
|
return
|
|
|
|
y_len=len(y)
|
|
|
|
if self.plottedLineBinary[pv] is None:
|
|
self.plottedLineBinary[pv] = self.plotItemBinary.plot(self.x[x_len-y_len:],self.dataStatWd[i,:],pen=pg.mkPen(i, width=2))
|
|
self.plotItemBinary.showGrid(x=True,y=True)
|
|
self.plotItemBinary.setXLink(self.plotItemAnalog)
|
|
self.plotItemBinary.setYRange(-0.1, 1.1, padding=0)
|
|
else:
|
|
self.plottedLineBinary[pv].setData(self.x[x_len-y_len:],self.dataStatWd[i,:])
|
|
minimum_x_temp=-y_len/self.sampleRate
|
|
if minimum_x_temp < minimum_x:
|
|
minimum_x = minimum_x_temp
|
|
else:
|
|
if self.plottedLineBinary[pv] is not None:
|
|
self.plotItemBinary.removeItem(self.plottedLineBinary[pv])
|
|
self.plottedLineBinary[pv] = None
|
|
i += 1
|
|
|
|
return
|
|
|
|
#if autozoom:
|
|
# ymin = -0.1
|
|
# ymax = 1.1
|
|
# xmin = minimum_x
|
|
# xmax = 0
|
|
# if xmin == xmax:
|
|
# xmin = xmin - 1
|
|
# xmax = xmax + 1
|
|
# range = xmax - xmin
|
|
# xmax += range * 0.02
|
|
# xmin -= range * 0.02
|
|
# self.plotItemBinary.setYRange(ymin, ymax, padding=0)
|
|
# self.plotItemBinary.setXRange(xmin, xmax, padding=0)
|
|
#
|
|
#self.allowSave = True
|
|
#self.saveBtn.setEnabled(True)
|
|
|
|
def printOutHelp():
|
|
print("ecmcMtnMainGui: Plots waveforms of Mtn data (updates on Y data callback). ")
|
|
print("python ecmcMtnMainGui.py <prefix> <mtnId>")
|
|
print("<prefix>: Ioc prefix ('IOC_TEST')")
|
|
print("<mtnId> : Id of mtn plugin ('0')")
|
|
print("example : python ecmcMotionMainGui.py 'IOC_TEST' '0'")
|
|
print("Will connect to Pvs: <prefix>Plg-Mtn<mtnId>-*")
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
prefix = None
|
|
mtnid = None
|
|
if len(sys.argv) == 1:
|
|
prefix = None
|
|
mtnid = None
|
|
elif len(sys.argv) == 3:
|
|
prefix = sys.argv[1]
|
|
mtnid = int(sys.argv[2])
|
|
else:
|
|
printOutHelp()
|
|
sys.exit()
|
|
app = QtWidgets.QApplication(sys.argv)
|
|
window=ecmcMtnMainGui(prefix=prefix,mtnPluginId=mtnid)
|
|
window.show()
|
|
sys.exit(app.exec_())
|
|
|