Source code for siriushla.si_ap_idff.main

"""Main window."""

from time import strftime, localtime

from qtpy.QtCore import Qt
from qtpy.QtWidgets import QLabel, QGridLayout, QSizePolicy as QSzPlcy, \
    QWidget, QGroupBox, QHBoxLayout, QVBoxLayout, QPushButton, QSpacerItem, \
    QRadioButton, QButtonGroup, QStackedWidget
from qtpy.QtGui import QColor

import qtawesome as qta

from pydm.widgets import PyDMPushButton

from siriuspy.envars import VACA_PREFIX as _VACA_PREFIX
from siriuspy.namesys import SiriusPVName as _PVName
from siriuspy.search import IDSearch
from siriuspy.idff.csdev import IDFFConst, ETypes as IDFFEnums

from ..util import connect_window
from ..widgets import SiriusMainWindow, SiriusLabel, SiriusSpinbox, \
    PyDMStateButton, SiriusLedState, PyDMLogLabel, SiriusLedAlert, \
    SiriusConnectionSignal, SiriusWaveformPlot
from ..widgets.dialog import StatusDetailDialog
from ..as_ps_control.control_widget.ControlWidgetFactory import \
    ControlWidgetFactory
from ..as_ps_control import PSDetailWindow
from .custom_widgets import ConfigLineEdit, SectionedWaveformCurveItem
from .util import get_idff_icon


[docs] class IDFFWindow(SiriusMainWindow): """ID FF main window.""" def __init__(self, parent=None, prefix='', idname=''): """Initialize.""" super().__init__(parent) self.prefix = prefix or _VACA_PREFIX self.idname = _PVName(idname) self._is_llidff = self.idname.dev.startswith(("IVU", "VPU")) self._idffname = IDFFConst(idname).idffname self._idffdata = IDSearch.conv_idname_2_idff(self.idname) self.device = _PVName(self._idffname) self.dev_pref = _PVName( f"SI-{self.device.sub}:BS-IDFF-CHCV:" if (self._is_llidff and self.idname.dev.startswith("IVU")) else f"SI-{self.device.sub}:BS-IDFF-CC:" if (self._is_llidff and self.idname.dev.startswith("VPU")) else self.device.substitute(prefix=prefix)) self.setObjectName('IDApp') self.setWindowTitle(self.device) self.setWindowIcon(get_idff_icon()) self._setupUi() self.setFocusPolicy(Qt.StrongFocus) def _setupUi(self): self.title = QLabel( '<h2>' + self.idname + ' Feedforward Settings</h2>', alignment=Qt.AlignCenter) wid = QWidget() lay = QGridLayout(wid) lay.addWidget(self.title, 0, 0, 1, 2) if self._is_llidff: corrs = [] if self.idname.dev.startswith("IVU"): corrs = ["CH1", "CH2", "CV1", "CV2"] elif self.idname.dev.startswith("VPU"): corrs = ["CC1-1", "CC2-1", "CC1-2", "CC2-2"] lay.addWidget(self._llStatusWidget(), 1, 0, 1, 1) lay.addWidget(self._llSettingsWidget(corrs), 2, 0, 3, 1) else: lay.addWidget(self._idStatusWidget(), 1, 0) lay.addWidget(self._corrStatusWidget(), 2, 0) lay.addWidget(self._basicSettingsWidget(), 3, 0) lay.addWidget(self._logWidget(), 4, 0) lay.addWidget(self._corrsMonitorWidget(), 1, 1, 4, 1) self.setCentralWidget(wid) def _llStatusWidget(self): gbox = QGroupBox('ID Status', self) hlay = QHBoxLayout(gbox) lbl_gap = QLabel( 'Gap: ', self) gap_val = SiriusLabel( self, self.dev_pref.substitute(propty='IDPos-Mon')) gap_val.showUnits = True hlay.addWidget(lbl_gap) hlay.addWidget(gap_val) return gbox def _format_timestamp_label(self, value): self.timestamp.setText(strftime( '%Y-%m-%d %H:%M:%S', localtime(value/1000))) def _llSettingsWidget(self, corrs): gbox = QGroupBox('Settings', self) lay = QGridLayout(gbox) ld_loopstate = QLabel('Loop State: ', self) self.sb_loopstate = PyDMStateButton( self, self.dev_pref.substitute(propty='LoopState-Sel')) self.lb_loopstate = SiriusLedState( self, self.dev_pref.substitute(propty='LoopState-Sts')) lbl_table_pointer = QLabel('Table Pointer: ', self) self.table_pointer = SiriusLabel( self, self.dev_pref.substitute(propty='TablePointer-Mon')) lbl_alarm = QLabel('Alarm: ', self) self.alarm_led = SiriusLedState( self, self.dev_pref.substitute(propty='Alarms-Mon')) alarm_details = QPushButton('', self) alarm_details.setIcon(qta.icon('fa5s.list-ul')) alarm_details.setToolTip('Open Detailed Alarms View') alarm_details.setObjectName('sts') alarm_details.setStyleSheet( '#sts{min-width:25px; max-width:25px; icon-size:20px;}') connect_window( alarm_details, StatusDetailDialog, parent=self, pvname=self.dev_pref.substitute(propty='Alarms-Mon'), labels=["Mode/Polar. out of range", "Gap/phase out of range", "Gap/phase high deviation", "Timeout for serial data"], section='ID', title='FeedForward Status') self.clear_alarms = PyDMPushButton( self, "Clear Alarms", pressValue=1, init_channel=self.dev_pref.substitute(propty='ClearFlags-Cmd')) lbl_plc_counter = QLabel('PLC Counter: ', self) self.plc_counter = SiriusLabel( self, self.dev_pref.substitute(propty='PLCCounter-Mon')) lbl_timestamp = QLabel('PLC Timestamp: ', self) self.timestamp = QLabel('0:00:00', self) self.timestamp_mon = SiriusConnectionSignal( self.dev_pref.substitute(propty='PLCTimestamp-Mon')) self.timestamp_mon.new_value_signal[float].connect( self._format_timestamp_label) buttonGroupWid = QWidget() vlay = QVBoxLayout(buttonGroupWid) self.button_group = QButtonGroup(buttonGroupWid) self.button_group.buttonClicked.connect(lambda btn: self.stack.setCurrentIndex(self.button_group.id(btn))) self.stack = QStackedWidget() self.plot_dict = {} for idx, name in enumerate(corrs): channel_btn = QRadioButton(name) vlay.addWidget(channel_btn) self.button_group.addButton(channel_btn, idx) if idx == 0: channel_btn.setChecked(True) graph = SiriusWaveformPlot() graph.setShowLegend(True) graph.autoRangeX = True graph.autoRangeY = True graph.showXGrid = True graph.showYGrid = True graph.showLegend = True graph.setLabel('bottom', text='Index') graph.setLabel('left', text='Current [A]') graph.setBackgroundColor(QColor(255, 255, 255)) self._addNewTableCurve(graph, name, idx) self.stack.addWidget(graph) self.plot_dict[name] = graph lay.addWidget(ld_loopstate, 0, 0) lay.addWidget(self.sb_loopstate, 0, 1) lay.addWidget(self.lb_loopstate, 0, 2) lay.addWidget(lbl_table_pointer, 1, 0) lay.addWidget(self.table_pointer, 1, 1, 1, 2) lay.addWidget(lbl_alarm, 2, 0) lay.addWidget(self.alarm_led, 2, 1) lay.addWidget(alarm_details, 2, 2) lay.addWidget(self.clear_alarms, 3, 0, 1, 3) lay.addWidget(lbl_plc_counter, 4, 0) lay.addWidget(self.plc_counter, 4, 1, 1, 2) lay.addWidget(lbl_timestamp, 5, 0) lay.addWidget(self.timestamp, 5, 1, 1, 2) lay.addWidget(buttonGroupWid, 6, 0) lay.addWidget(self.stack, 6, 1, 1, 2) return gbox def _addNewTableCurve(self, plt, name, section): if "CH" in name: color_sp, color_rb = 'blue', 'darkBlue' elif "CV" in name: color_sp, color_rb = 'red', 'darkRed' else: color_sp, color_rb = 'green', 'darkGreen' curve_sp = SectionedWaveformCurveItem( section=section, y_addr=self.dev_pref.substitute(propty='Table-SP'), name="SP", color=QColor(color_sp), ) plt._needs_redraw = False plt.addCurve(curve_sp, curve_color=QColor(color_sp)) curve_sp.data_changed.connect(plt.set_needs_redraw) curve_rb = SectionedWaveformCurveItem( section=section, y_addr=self.dev_pref.substitute(propty='Table-RB'), name="RB", color=QColor(color_rb), ) plt._needs_redraw = False plt.addCurve(curve_rb, curve_color=QColor(color_rb)) curve_rb.data_changed.connect(plt.set_needs_redraw) return plt def _basicSettingsWidget(self): ld_configname = QLabel( 'Config. Name: ', self, alignment=Qt.AlignRight) self.le_configname = ConfigLineEdit( self, self.dev_pref.substitute(propty='ConfigName-SP')) self.le_configname.setStyleSheet('min-width:10em; max-width:10em;') self.lb_configname = SiriusLabel( self, self.dev_pref.substitute(propty='ConfigName-RB')) ld_loopstate = QLabel( 'Loop State: ', self, alignment=Qt.AlignRight) self.sb_loopstate = PyDMStateButton( self, self.dev_pref.substitute(propty='LoopState-Sel')) self.lb_loopstate = SiriusLedState( self, self.dev_pref.substitute(propty='LoopState-Sts')) ld_loopfreq = QLabel( 'Loop Freq.: ', self, alignment=Qt.AlignRight) self.sb_loopfreq = SiriusSpinbox( self, self.dev_pref.substitute(propty='LoopFreq-SP')) self.lb_loopfreq = SiriusLabel( self, self.dev_pref.substitute(propty='LoopFreq-RB')) chnames = IDSearch.conv_idname_2_idff_chnames(self.idname) cvnames = IDSearch.conv_idname_2_idff_cvnames(self.idname) qsnames = IDSearch.conv_idname_2_idff_qsnames(self.idname) lcnames = IDSearch.conv_idname_2_idff_lcnames(self.idname) qnnames = IDSearch.conv_idname_2_idff_qnnames(self.idname) # ccnames = IDSearch.conv_idname_2_idff_ccnames(self.idname) ld_calccorr = QLabel( 'Calc. values:', self, alignment=Qt.AlignRight) glay_calccorr = QGridLayout() glay_calccorr.addWidget(ld_calccorr, 0, 0) corr_fams = ['CH', 'CV', 'QS', 'LCH', 'QD1', 'QF', 'QD2', 'CC'] for ridx, corr in enumerate(corr_fams): if corr == 'CH' and not chnames: continue if corr == 'CV' and not cvnames: continue if corr == 'QS' and not qsnames: continue if corr == 'LCH' and not lcnames: continue if corr in ('QD1', 'QF', 'QD2') and not qnnames: continue if corr == 'CC': continue # if corr == 'CC' and not ccnames: # continue row = ridx + 1 hheader = QLabel(f'{corr}', alignment=Qt.AlignCenter) hheader.setStyleSheet('.QLabel{font-weight: bold;}') glay_calccorr.addWidget(hheader, row, 0) for cidx in range(2): col = cidx + 1 if ridx == 0: vheader = QLabel(f'{col}', alignment=Qt.AlignCenter) vheader.setStyleSheet('.QLabel{font-weight: bold;}') glay_calccorr.addWidget(vheader, 0, col) propty = f'Corr{corr}_{col}Current-Mon' pvname = self.dev_pref.substitute(propty=propty) lbl = SiriusLabel(self, pvname, keep_unit=True) lbl.showUnits = True glay_calccorr.addWidget(lbl, row, col) gbox = QGroupBox('Settings', self) lay = QGridLayout(gbox) lay.addWidget(ld_loopstate, 0, 0) lay.addWidget(self.sb_loopstate, 0, 1) lay.addWidget(self.lb_loopstate, 0, 2) lay.addItem(QSpacerItem(0, 15, QSzPlcy.Ignored, QSzPlcy.Fixed), 1, 0) lay.addWidget(ld_loopfreq, 2, 0) lay.addWidget(self.sb_loopfreq, 2, 1) lay.addWidget(self.lb_loopfreq, 2, 2, 1, 2) lay.addWidget(ld_configname, 3, 0) lay.addWidget(self.le_configname, 3, 1, 1, 3) lay.addWidget(self.lb_configname, 4, 1, 1, 3) lay.addItem(QSpacerItem(0, 15, QSzPlcy.Ignored, QSzPlcy.Fixed), 6, 0) row = 5 if chnames: ld_controlch = QLabel( 'Control CH: ', self, alignment=Qt.AlignRight) self.sb_controlch = PyDMStateButton( self, self.dev_pref.substitute(propty='ControlCH-Sel')) self.lb_controlch = SiriusLedState( self, self.dev_pref.substitute(propty='ControlCH-Sts')) lay.addWidget(ld_controlch, row, 0) lay.addWidget(self.sb_controlch, row, 1) lay.addWidget(self.lb_controlch, row, 2) row += 1 if cvnames: ld_controlcv = QLabel( 'Control CV: ', self, alignment=Qt.AlignRight) self.sb_controlcv = PyDMStateButton( self, self.dev_pref.substitute(propty='ControlCV-Sel')) self.lb_controlcv = SiriusLedState( self, self.dev_pref.substitute(propty='ControlCV-Sts')) lay.addWidget(ld_controlcv, row, 0) lay.addWidget(self.sb_controlcv, row, 1) lay.addWidget(self.lb_controlcv, row, 2) row += 1 if qsnames: ld_controlqs = QLabel( 'Control QS: ', self, alignment=Qt.AlignRight) self.sb_controlqs = PyDMStateButton( self, self.dev_pref.substitute(propty='ControlQS-Sel')) self.lb_controlqs = SiriusLedState( self, self.dev_pref.substitute(propty='ControlQS-Sts')) lay.addWidget(ld_controlqs, row, 0) lay.addWidget(self.sb_controlqs, row, 1) lay.addWidget(self.lb_controlqs, row, 2) row += 1 if lcnames: ld_controllc = QLabel( 'Control LC: ', self, alignment=Qt.AlignRight) self.sb_controllc = PyDMStateButton( self, self.dev_pref.substitute(propty='ControlLC-Sel')) self.lb_controllc = SiriusLedState( self, self.dev_pref.substitute(propty='ControlLC-Sts')) lay.addWidget(ld_controllc, row, 0) lay.addWidget(self.sb_controllc, row, 1) lay.addWidget(self.lb_controllc, row, 2) row += 1 if qnnames: ld_controlqn = QLabel( 'Control QN: ', self, alignment=Qt.AlignRight) self.sb_controlqn = PyDMStateButton( self, self.dev_pref.substitute(propty='ControlQN-Sel')) self.lb_controlqn = SiriusLedState( self, self.dev_pref.substitute(propty='ControlQN-Sts')) lay.addWidget(ld_controlqn, row, 0) lay.addWidget(self.sb_controlqn, row, 1) lay.addWidget(self.lb_controlqn, row, 2) row += 1 # if ccnames: # ld_controlcc = QLabel( # 'Control CC: ', self, alignment=Qt.AlignRight) # self.sb_controlcc = PyDMStateButton( # self, self.dev_pref.substitute(propty='ControlCC-Sel')) # self.lb_controlcc = SiriusLedState( # self, self.dev_pref.substitute(propty='ControlCC-Sts')) # lay.addWidget(ld_controlcc, row, 0) # lay.addWidget(self.sb_controlcc, row, 1) # lay.addWidget(self.lb_controlcc, row, 2) # row += 1 lay.addLayout(glay_calccorr, row, 0, 1, 3) return gbox def _corrStatusWidget(self): ld_corconf = QLabel( 'Corr. Status: ', self, alignment=Qt.AlignRight) self.led_corr = SiriusLedAlert( self, self.dev_pref.substitute(propty='CorrStatus-Mon')) pb_corsts = QPushButton('', self) pb_corsts.setIcon(qta.icon('fa5s.list-ul')) pb_corsts.setToolTip('Open Detailed Status View') pb_corsts.setObjectName('sts') pb_corsts.setStyleSheet( '#sts{min-width:25px; max-width:25px; icon-size:20px;}') connect_window( pb_corsts, StatusDetailDialog, parent=self, pvname=self.dev_pref.substitute(propty='CorrStatus-Mon'), labels=IDFFEnums.STS_LBLS_CORR, section='ID', title='Corrector Status') self.pb_corconf = PyDMPushButton( self, pressValue=1, init_channel=self.dev_pref.substitute(propty='CorrConfig-Cmd')) self.pb_corconf.setToolTip('Send PwrState and OpMode') self.pb_corconf.setIcon(qta.icon('fa5s.sync')) self.pb_corconf.setObjectName('conf') self.pb_corconf.setStyleSheet( '#conf{min-width:25px; max-width:25px; icon-size:20px;}') gbox = QGroupBox('Corrector Status', self) lay = QGridLayout(gbox) lay.addWidget(ld_corconf, 0, 0) lay.addWidget(self.led_corr, 0, 1) lay.addWidget(pb_corsts, 0, 2) lay.addWidget(self.pb_corconf, 0, 3) return gbox def _idStatusWidget(self): gbox = QGroupBox('ID Status', self) lay = QGridLayout(gbox) if self._idffdata['pparameter']: pparam = _PVName(self._idffdata['pparameter']) ld_pparam = QLabel( pparam.propty_name + ': ', self, alignment=Qt.AlignRight) self._lb_pparam = SiriusLabel(self, pparam, keep_unit=True) self._lb_pparam.showUnits = True lay.addWidget(ld_pparam, 0, 0) lay.addWidget(self._lb_pparam, 0, 1) if self._idffdata['kparameter']: kparam = _PVName(self._idffdata['kparameter']) ld_kparam = QLabel( kparam.propty_name + ': ', self, alignment=Qt.AlignRight) self._lb_kparam = SiriusLabel(self, kparam, keep_unit=True) self._lb_kparam.showUnits = True lay.addWidget(ld_kparam, 1, 0) lay.addWidget(self._lb_kparam, 1, 1) ld_polar = QLabel( 'Polarization: ', self, alignment=Qt.AlignRight) self.lb_polar = SiriusLabel( self, self.dev_pref.substitute(propty='Polarization-Mon')) lay.addItem(QSpacerItem(0, 15, QSzPlcy.Ignored, QSzPlcy.Fixed), 2, 0) lay.addWidget(ld_polar, 3, 0) lay.addWidget(self.lb_polar, 3, 1, 1, 3) return gbox def _logWidget(self): self.log = PyDMLogLabel( self, init_channel=self.dev_pref.substitute(propty='Log-Mon')) self.log.setSizePolicy( QSzPlcy.MinimumExpanding, QSzPlcy.MinimumExpanding) self.log.setAlternatingRowColors(True) self.log.maxCount = 2000 gbox = QGroupBox('Log', self) lay = QVBoxLayout(gbox) lay.addWidget(self.log) return gbox def _corrsMonitorWidget(self): widget = ControlWidgetFactory.factory( self, section='SI', device='corrector-idff', subsection=self.device.sub, orientation=Qt.Vertical) for wid in widget.get_summary_widgets(): detail_bt = wid.get_detail_button() psname = detail_bt.text() if not psname: psname = detail_bt.toolTip() connect_window(detail_bt, PSDetailWindow, self, psname=psname) gbox = QGroupBox('Correctors', self) lay = QVBoxLayout(gbox) lay.setContentsMargins(3, 3, 3, 3) lay.addWidget(widget) return gbox