"""DCCT graphics module."""
import numpy as np
from qtpy.QtCore import Qt
from qtpy.QtGui import QColor
from qtpy.QtWidgets import QWidget, QLabel, QPushButton, QGridLayout, \
QHBoxLayout, QGroupBox, QComboBox, QSizePolicy as QSzPly, \
QVBoxLayout, QCheckBox, QMenu
import qtawesome as qta
from siriuspy.namesys import SiriusPVName
from siriuspy.diagbeam.dcct.csdev import Const as _DCCTc
from siriuspy.search import LLTimeSearch as _LLTimeSearch
from siriushla.widgets import SiriusConnectionSignal as SignalChannel, \
SiriusTimePlot, QSpinBoxPlus, SiriusWaveformPlot, SiriusLabel
[docs]
class DCCTMonitor(QWidget):
"""DCCT data monitoring."""
def __init__(self, parent=None, prefix='', device=''):
"""Initialize object."""
super().__init__(parent)
self.prefix = prefix
self.device = SiriusPVName(device)
self.use_raw = self.device.sec == 'BO'
self.dcct_prefix = self.device.substitute(prefix=prefix)
self.acqmode_channel = SignalChannel(
self.dcct_prefix.substitute(propty='MeasMode-Sel'))
self.acqmode_channel.new_value_signal[int].connect(self.updateParams)
self.normalnrsamp_channel = SignalChannel(
self.dcct_prefix.substitute(propty='SampleCnt-RB'))
self.normalnrsamp_channel.new_value_signal[int].connect(
self.updateParams)
self.fastnrsamp_channel = SignalChannel(
self.dcct_prefix.substitute(propty='FastSampleCnt-RB'))
self.fastnrsamp_channel.new_value_signal[int].connect(
self.updateParams)
self._acq_mode = None
self._acq_normalnrsamp = None
self._acq_fastnrsamp = None
self._downsampling = 1
self._smooth_method = 'Average'
self._smooth_nracq = 1
self._smooth_buffer = list()
self._setupUi()
def _setupUi(self):
self.currhist = self._setupTimeHistory()
self.currhist.setVisible(not self.use_raw)
self.rawreads = self._setupRawReadings()
self.rawreads.setVisible(self.use_raw)
self.pb_vis = QPushButton(self)
self.pb_vis.setStyleSheet(
'QPushButton{min-width: 0.8em; max-width: 0.8em;}')
self.menu_vis = QMenu()
self.switch_act = self.menu_vis.addAction(
'Switch to '+('Time History' if self.use_raw else 'Raw Readings'))
self.switch_act.triggered.connect(self._handle_data_visualization)
self.pb_vis.setMenu(self.menu_vis)
self.setStyleSheet("""
.QLabel{max-height:1.5em;}
PyDMWaveformPlot, PyDMTimePlot{min-width:30em; min-height:20em;}
""")
lay = QGridLayout(self)
lay.setContentsMargins(0, 0, 0, 0)
lay.setHorizontalSpacing(0)
lay.addWidget(self.pb_vis, 0, 0, alignment=Qt.AlignTop | Qt.AlignLeft)
lay.addWidget(self.currhist, 0, 1)
lay.addWidget(self.rawreads, 0, 1)
def _setupTimeHistory(self):
self.label_currhist = QLabel(
'<h3>Current History</h3>', self, alignment=Qt.AlignCenter)
self.timegraph = SiriusTimePlot(self)
self.timegraph.timeSpan = 600
channel = self.dcct_prefix.substitute(propty='Current-Mon')
self.timegraph.addYChannel(
y_channel=channel, name='Current History', color='blue',
lineWidth=1, lineStyle=Qt.SolidLine)
self.timegraph.autoRangeX = True
self.timegraph.autoRangeY = True
self.timegraph.backgroundColor = QColor(255, 255, 255)
self.timegraph.showLegend = False
self.timegraph.showXGrid = True
self.timegraph.showYGrid = True
self.timegraph.setLabel('left', text='Current [mA]')
self.timecurve = self.timegraph.curveAtIndex(0)
wid = QWidget()
lay = QGridLayout(wid)
lay.setAlignment(Qt.AlignTop)
lay.setRowStretch(0, 1)
lay.setRowStretch(1, 9)
lay.addWidget(self.label_currhist, 0, 0)
lay.addWidget(self.timegraph, 1, 0)
return wid
def _setupRawReadings(self):
self.label_waveread = QLabel(
'<h3>Current Raw Readings</h3>', self, alignment=Qt.AlignCenter)
self.wavegraph = SiriusWaveformPlot(self)
channel = 'FAKE:Readings'
self.rawreadings_channel = SignalChannel(
self.dcct_prefix.substitute(propty='RawReadings-Mon'))
self.rawreadings_channel.new_value_signal[np.ndarray].connect(
self._updateRawBuffer)
self.wavegraph.addChannel(
y_channel=channel, name='Current Raw Readings', color='blue',
lineWidth=2, lineStyle=Qt.SolidLine)
self.wavegraph.setLabel('left', text='Current [mA]')
self.wavegraph.setLabel('bottom', text='Index')
self.wavegraph.autoRangeX = True
self.wavegraph.autoRangeY = True
self.wavegraph.backgroundColor = QColor(255, 255, 255)
self.wavegraph.showLegend = False
self.wavegraph.showXGrid = True
self.wavegraph.showYGrid = True
self.wavecurve = self.wavegraph.curveAtIndex(0)
wid = QWidget()
lay = QGridLayout(wid)
lay.setAlignment(Qt.AlignTop)
lay.setRowStretch(0, 1)
lay.setRowStretch(1, 9)
lay.addWidget(self.label_waveread, 0, 0)
lay.addWidget(self.wavegraph, 1, 0)
# Smoothing
evgname = SiriusPVName(_LLTimeSearch.get_evg_name())
self._evnt_dly = SignalChannel(evgname.substitute(
prefix=self.prefix, propty='LinacDelay-RB'))
self._evnt_dly.new_value_signal[float].connect(self.updateRawXAxis)
self._trig_dly = SignalChannel(
self.dcct_prefix.substitute(dis='TI', propty='Delay-RB'))
self._trig_dly.new_value_signal[float].connect(self.updateRawXAxis)
self._smpl_cnt = SignalChannel(
self.dcct_prefix.substitute(propty='FastSampleCnt-RB'))
self._smpl_cnt.new_value_signal[float].connect(self.updateRawXAxis)
self._meas_per = SignalChannel(
self.dcct_prefix.substitute(propty='FastMeasPeriod-RB'))
self._meas_per.new_value_signal[float].connect(self.updateRawXAxis)
self.cb_timeaxis = QCheckBox('Use time axis', self)
self.cb_timeaxis.setChecked(True)
self.cb_timeaxis.stateChanged.connect(self.updateRawXAxis)
self.cb_timeaxis.setLayoutDirection(Qt.RightToLeft)
lay.addWidget(self.cb_timeaxis, 2, 0, alignment=Qt.AlignLeft)
lay.setRowStretch(2, 1)
l_smoothmethod = QLabel('Method: ', self)
self.cb_smoothmethod = QComboBox(self)
self.cb_smoothmethod.addItems(['Average', 'Median'])
self.cb_smoothmethod.currentTextChanged.connect(
self.setRawSmoothMethod)
l_smoothnracq = QLabel('Nr.Acqs.: ', self)
self.sb_smoothnracq = QSpinBoxPlus(self)
self.sb_smoothnracq.setValue(1)
self.sb_smoothnracq.valueChanged.connect(
self.setRawSmoothNrAcq)
l_smoothbuff = QLabel('Buffer Size: ', self)
l_smoothbuff.setSizePolicy(QSzPly.Minimum, QSzPly.Preferred)
self.label_buffsize = QLabel('', self)
self.label_buffsize.setStyleSheet(
'min-width:3em; max-width:3em;')
self.pb_resetbuff = QPushButton(
qta.icon('mdi.delete-empty'), '', self)
self.pb_resetbuff.setToolTip('Reset buffer')
self.pb_resetbuff.setObjectName('resetbuff')
self.pb_resetbuff.setStyleSheet(
"#resetbuff{min-width:25px; max-width:25px; icon-size:20px;}")
self.pb_resetbuff.clicked.connect(self.resetRawBuffer)
hlay_buff = QHBoxLayout()
hlay_buff.addWidget(self.label_buffsize)
hlay_buff.addWidget(self.pb_resetbuff)
l_down = QLabel('Downsampling: ', self)
self.sb_down = QSpinBoxPlus(self)
self.sb_down.setValue(1)
self.sb_down.valueChanged.connect(self.setRawDownsampling)
gbox_smooth = QGroupBox('Smoothing of Readings')
glay_smooth = QGridLayout(gbox_smooth)
glay_smooth.addWidget(l_smoothmethod, 0, 0)
glay_smooth.addWidget(self.cb_smoothmethod, 0, 1)
glay_smooth.addWidget(l_smoothnracq, 1, 0)
glay_smooth.addWidget(self.sb_smoothnracq, 1, 1)
glay_smooth.addWidget(QLabel(''), 0, 2)
glay_smooth.addWidget(l_smoothbuff, 0, 3)
glay_smooth.addLayout(hlay_buff, 0, 4, 1, 2)
glay_smooth.addWidget(l_down, 1, 3)
glay_smooth.addWidget(self.sb_down, 1, 4, 1, 2)
glay_smooth.setColumnStretch(0, 10)
glay_smooth.setColumnStretch(1, 10)
glay_smooth.setColumnStretch(2, 2)
glay_smooth.setColumnStretch(3, 10)
glay_smooth.setColumnStretch(4, 5)
glay_smooth.setColumnStretch(5, 5)
lay.addWidget(gbox_smooth, 3, 0)
lay.setRowStretch(3, 3)
gbox_smooth.setStyleSheet("""
.QLabel{
qproperty-alignment: 'AlignVCenter | AlignRight';}
QPushButton{
min-width:3em; max-width:3em;}""")
return wid
def _updateRawBuffer(self, raw):
if raw is None:
return
samp = self._acq_fastnrsamp \
if self._acq_mode == _DCCTc.MeasModeSel.Fast \
else self._acq_normalnrsamp
if not samp:
return
samp -= samp % self._downsampling
if samp < 1:
return
data = raw[:samp]
self._smooth_buffer.append(data)
if len(self._smooth_buffer) > self._smooth_nracq:
self._smooth_buffer.pop(0)
self._updateRawCurve()
def _updateRawCurve(self):
buff = np.array(self._smooth_buffer, dtype=float)
self.label_buffsize.setText(str(self.smoothBufferSize))
if not len(buff):
return
if len(buff) > 1:
if self._smooth_method == 'Average':
fdata = np.mean(buff, axis=0)
elif self._smooth_method == 'Median':
fdata = np.median(buff, axis=0)
else:
fdata = buff[0]
down = self._downsampling
if down > 1:
fdata = np.mean(fdata.reshape(-1, down), axis=1)
self.wavecurve.receiveYWaveform(fdata)
self.wavecurve.redrawCurve()
[docs]
def setRawDownsampling(self, new_value):
"""Update number of samples to use in downsampling."""
self._downsampling = new_value
self.resetRawBuffer()
[docs]
def setRawSmoothMethod(self, new_method):
"""Update method to perform raw readings smoothing."""
self._smooth_method = new_method
self._updateRawCurve()
[docs]
def setRawSmoothNrAcq(self, new_value):
"""Update number of samples to use in smoothing."""
self._smooth_nracq = new_value
exc = len(self._smooth_buffer) - self._smooth_nracq
if exc > 0:
for i in range(exc):
self._smooth_buffer.pop(0)
@property
def smoothBufferSize(self):
"""Smoothing buffer length."""
return len(self._smooth_buffer)
[docs]
def resetRawBuffer(self):
"""Reset smoothing buffer."""
self._smooth_buffer = list()
self._updateRawCurve()
[docs]
def updateParams(self, new_value):
"""Handle control visualization according to mode."""
address = self.sender().address
if 'Mode' in address:
self._acq_mode = new_value
elif 'Fast' in address:
self._acq_fastnrsamp = new_value
else:
self._acq_normalnrsamp = new_value
self.resetRawBuffer()
[docs]
def updateRawXAxis(self):
"""Update X axis of waveform graph."""
smpl = self._smpl_cnt.getvalue()
if self.cb_timeaxis.checkState():
evnt = self._evnt_dly.getvalue()
trig = self._trig_dly.getvalue()
peri = self._meas_per.getvalue()
if any([val is None for val in [evnt, trig, smpl, peri]]):
return
init = (evnt+trig)/1e3
endt = init + peri*1e3
xdata = np.linspace(init, endt, smpl)
xlabel = 'Time [ms]'
else:
xdata = np.arange(0, smpl)
xlabel = 'Index'
self.wavegraph.setLabel('bottom', text=xlabel)
self.wavecurve.receiveXWaveform(xdata)
def _handle_data_visualization(self):
show_raw = 'Raw' in self.sender().text()
self.sender().setText('Switch to ' + (
'Time History' if show_raw else 'Raw Readings'))
self.currhist.setVisible(not show_raw)
self.rawreads.setVisible(show_raw)
[docs]
class EffMonitor(QWidget):
"""Efficiency Graph."""
def __init__(self, parent=None, prefix='', section=''):
"""Initialize object."""
super().__init__(parent)
self.prefix = prefix
self.section = section
if self.section == 'BO':
self._label = 'Booster Ramp'
self._pvname = SiriusPVName(
'BO-Glob:AP-CurrInfo:RampEff-Mon').substitute(
prefix=self.prefix)
else:
self._label = 'Injection'
self._pvname = SiriusPVName(
'SI-Glob:AP-CurrInfo:InjEff-Mon').substitute(
prefix=self.prefix)
self._wavEff = list()
self._inj_idx = 0
self._eje_idx = -1
self._init_indices = False
self._setupUi()
def _setupUi(self):
label = QLabel('<h3>'+self._label+' Efficiency</h3>', self,
alignment=Qt.AlignCenter)
self.graph = SiriusTimePlot(self)
self.graph.timeSpan = 1000 # [s]
self.graph.addYChannel(
y_channel=self._pvname, name='Efficiency',
color='blue', lineWidth=2, lineStyle=Qt.SolidLine,
symbol='o', symbolSize=2)
self.graph.setAutoRangeX(True)
self.graph.setAutoRangeY(True)
self.graph.backgroundColor = QColor(255, 255, 255)
self.graph.showLegend = False
self.graph.showXGrid = True
self.graph.showYGrid = True
self.graph.maxRedrawRate = 2
self.graph.setLabel('left', text='Efficiency [%]')
leftAxis = self.graph.getAxis('left')
leftAxis.setStyle(autoExpandTextSpace=False, tickTextWidth=25)
self.curve = self.graph.curveAtIndex(0)
l_eff = QLabel('<h4>Efficiency:</h4>', self)
self.label_eff = SiriusLabel(self, self._pvname)
self.label_eff.showUnits = True
hbox_eff = QHBoxLayout()
hbox_eff.addStretch()
hbox_eff.addWidget(l_eff)
hbox_eff.addWidget(self.label_eff)
hbox_eff.addStretch()
if self.section == 'BO':
l_injcurr = QLabel('Injected:', self)
self.label_injcurr = SiriusLabel(self, SiriusPVName(
'BO-Glob:AP-CurrInfo:Current150MeV-Mon').substitute(
prefix=self.prefix))
self.label_injcurr.showUnits = True
l_ejecurr = QLabel('Ejected:', self)
self.label_ejecurr = SiriusLabel(self, SiriusPVName(
'BO-Glob:AP-CurrInfo:Current3GeV-Mon').substitute(
prefix=self.prefix))
self.label_ejecurr.showUnits = True
hbox_eff.addWidget(l_injcurr)
hbox_eff.addWidget(self.label_injcurr)
hbox_eff.addStretch()
hbox_eff.addWidget(l_ejecurr)
hbox_eff.addWidget(self.label_ejecurr)
hbox_eff.addStretch()
lay = QVBoxLayout()
lay.addWidget(label)
lay.addWidget(self.graph)
lay.addLayout(hbox_eff)
self.setLayout(lay)
self.setStyleSheet("""
SiriusLabel{
qproperty-alignment:'AlignCenter';
min-width:5em; max-width:5em;}
PyDMTimePlot{
min-width:30em; min-height:20em;}
""")