Source code for siriushla.si_ap_fofb.main

"""High level FOFB main module."""

import time as _time

from qtpy.QtCore import Qt
from qtpy.QtWidgets import QPushButton, QWidget, QGridLayout, QMenu, \
    QLabel, QVBoxLayout, QGroupBox, QMenuBar, QAction, QHBoxLayout, \
    QSizePolicy as QSzPlcy, QDockWidget, QTabWidget
import qtawesome as qta

from pydm.widgets import PyDMPushButton

from siriuspy.namesys import SiriusPVName as _PVName
from siriuspy.fofb.csdev import ETypes as _FOFBEnums

from ..util import connect_window, connect_newprocess
from ..widgets import SiriusLedAlert, SiriusLabel, SiriusSpinbox, CALabel, \
    PyDMLogLabel, SiriusMainWindow, PyDMStateButton, SiriusLedState, \
    SiriusConnectionSignal as _ConnSignal, CAPushButton, SiriusEnumComboBox

from .base import BaseObject, get_fofb_icon
from .custom_widgets import RefOrbWidget, StatusDialog, BPMSwModeWidget, \
    ControllersDetailDialog, PSConfigWidget
from .respmat import RespMatWidget
from .graphics import KickWidget


[docs] class MainWindow(BaseObject, SiriusMainWindow): """FOFB Main Window.""" def __init__(self, parent=None, prefix='', device=''): BaseObject.__init__(self, device, prefix=prefix) SiriusMainWindow.__init__(self, parent) self.setWindowTitle('SI - FOFB') self.setObjectName('SIApp') self.setWindowIcon(get_fofb_icon()) self._enblrule = ( '[{"name": "EnblRule", "property": "Enable", ' + '"expression": "not ch[0]", "channels": [{"channel": "' + self.devpref.substitute(propty='LoopState-Sts') + '", "trigger": true}]}]') self._setupUi() self.setFocusPolicy(Qt.StrongFocus) def _setupUi(self): # layout self.log = self._setupLogWidget() self.kicks_view = KickWidget(self, self.device, self.prefix) self.control = self._setupControlWidget() self.setDocumentMode(False) self.setDockNestingEnabled(True) self.addDockWidget(Qt.LeftDockWidgetArea, self.log) self.setCentralWidget(self.kicks_view) self.addDockWidget(Qt.RightDockWidgetArea, self.control) # menu menubar = QMenuBar(self) self.setMenuBar(menubar) menuopen = QMenu('Open', menubar) actions = ( ('&FOFB Control', 'FOFB Control', '', True, self.control), ('FOFB &Log', 'FOFB Log', '', True, self.log), ) for name, tool, short, check, doc in actions: action = QAction(name, self) action.setToolTip(tool) action.setShortcut(short) action.setCheckable(check) action.setChecked(check) action.setEnabled(True) action.setVisible(True) action.toggled.connect(doc.setVisible) doc.visibilityChanged.connect(action.setChecked) menuopen.addAction(action) menubar.addAction(menuopen.menuAction()) acq2cmd = { 'SYSID': 'sirius-hla-si-bs-fofbsysid.py', 'LAMP': 'sirius-hla-si-ps-fastcorracq.py', } for acq, cmd in acq2cmd.items(): acq_menu = menubar.addMenu(acq+' Acq.') acq_menu.setObjectName('SIApp') for i in range(1, 21): txt = f'SI-{i:02}' dev = f'IA-{i:02}RaBPM:BS-FOFBCtrl' act = QAction(txt, acq_menu) connect_newprocess(act, [cmd, dev]) acq_menu.addAction(act) def _setupStatusWidget(self): # correctors lbl_corr = QLabel( 'Correctors: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) led_corr = SiriusLedAlert( self, self.devpref.substitute(propty='CorrStatus-Mon')) sts_corr = QPushButton('', self) sts_corr.setIcon(qta.icon('fa5s.list-ul')) sts_corr.setToolTip('Open Detailed Status View') sts_corr.setObjectName('sts') sts_corr.setStyleSheet( '#sts{min-width:25px; max-width:25px; icon-size:20px;}') pvname = self.devpref.substitute(propty='CorrStatus-Mon') labels = _FOFBEnums.STS_LBLS_CORR cmds = [None]*len(labels) connect_window( sts_corr, StatusDialog, parent=self, pvname=pvname, labels=labels, cmds=cmds, title='Corrector Status') cnf_corr = PyDMPushButton( self, pressValue=1, init_channel=self.devpref.substitute(propty='CorrConfig-Cmd')) cnf_corr.setToolTip('Send OpMode, AccFreeze, Coefficients and Gains') cnf_corr.setIcon(qta.icon('fa5s.sync')) cnf_corr.setObjectName('conf') cnf_corr.setStyleSheet( '#conf{min-width:25px; max-width:25px; icon-size:20px;}') # controllers lbl_ctrl = QLabel( 'Controllers: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) led_ctrl = SiriusLedAlert( self, self.devpref.substitute(propty='CtrlrStatus-Mon')) sts_ctrl = QPushButton('', self) sts_ctrl.setIcon(qta.icon('fa5s.list-ul')) sts_ctrl.setToolTip('Open Detailed Status View') sts_ctrl.setObjectName('sts') sts_ctrl.setStyleSheet( '#sts{min-width:25px; max-width:25px; icon-size:20px;}') pvname = self.devpref.substitute(propty='CtrlrStatus-Mon') labels = _FOFBEnums.STS_LBLS_FOFBCTRL cmds = [None]*len(labels) cmds[1] = self.devpref.substitute(propty='CtrlrConfBPMId-Cmd') sofbpvname = _PVName('SI-Glob:AP-SOFB').substitute(prefix=self.prefix) cmds[2] = sofbpvname.substitute(propty='SyncBPMs-Cmd') cmds[4] = self.devpref.substitute(propty='CtrlrSyncRefOrb-Cmd') cmds[5] = self.devpref.substitute(propty='CtrlrSyncTFrameLen-Cmd') cmds[6] = self.devpref.substitute(propty='CtrlrConfBPMLogTrg-Cmd') cmds[7] = self.devpref.substitute(propty='CtrlrSyncMaxOrbDist-Cmd') cmds[8] = self.devpref.substitute( propty='CtrlrSyncPacketLossDetec-Cmd') cmds[9] = self.devpref.substitute(propty='CtrlrReset-Cmd') cmds[10] = self.devpref.substitute(propty='CtrlrDsblSYSIDExc-Cmd') dtl_ctrl = QPushButton('Details') dtl_ctrl.setDefault(False) dtl_ctrl.setAutoDefault(False) dtl_ctrl.setIcon(qta.icon('fa5s.ellipsis-h')) dtl_ctrl.setToolTip('Open Controllers Details') dtl_ctrl.setObjectName('sts') dtl_ctrl.setStyleSheet('#sts{icon-size:20px;}') connect_window( dtl_ctrl, ControllersDetailDialog, parent=self, device=self.device, prefix=self.prefix) connect_window( sts_ctrl, StatusDialog, parent=self, pvname=pvname, labels=labels, cmds=cmds, title='FOFB Controller Status', detail_button=dtl_ctrl) cnf_ctrl = PyDMPushButton( self, pressValue=1, init_channel=self.devpref.substitute(propty='CtrlrReset-Cmd')) cnf_ctrl.setToolTip('Send Reset command to controllers') cnf_ctrl.setIcon(qta.icon('fa5s.sync')) cnf_ctrl.setObjectName('conf') cnf_ctrl.setStyleSheet( '#conf{min-width:25px; max-width:25px; icon-size:20px;}') wid = QGroupBox('Status') lay = QGridLayout(wid) lay.setAlignment(Qt.AlignCenter) lay.addWidget(lbl_corr, 0, 0) lay.addWidget(led_corr, 0, 1) lay.addWidget(sts_corr, 0, 2) lay.addWidget(cnf_corr, 0, 3) lay.addWidget(lbl_ctrl, 1, 0) lay.addWidget(led_ctrl, 1, 1) lay.addWidget(sts_ctrl, 1, 2) lay.addWidget(cnf_ctrl, 1, 3) return wid def _setupRefOrbWidget(self): widget = RefOrbWidget(self, self.device, self.prefix) wid = QGroupBox('Ref.Orb.') lay = QVBoxLayout(wid) lay.addWidget(widget) return wid def _setupLoopWidget(self): ld_enbl = QLabel( 'Enable: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) sb_enbl = PyDMStateButton( self, self.devpref.substitute(propty='LoopState-Sel')) lb_enbl = SiriusLedState( self, self.devpref.substitute(propty='LoopState-Sts')) ld_gain_h = QLabel( 'Gain H: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) sb_gain_h = SiriusSpinbox( self, self.devpref.substitute(propty='LoopGainH-SP')) lb_gain_h = SiriusLabel( self, self.devpref.substitute(propty='LoopGainH-RB')) lb_gain_mon_h = SiriusLabel( self, self.devpref.substitute(propty='LoopGainH-Mon')) ld_gain_v = QLabel( 'Gain V: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) sb_gain_v = SiriusSpinbox( self, self.devpref.substitute(propty='LoopGainV-SP')) lb_gain_v = SiriusLabel( self, self.devpref.substitute(propty='LoopGainV-RB')) lb_gain_mon_v = SiriusLabel( self, self.devpref.substitute(propty='LoopGainV-Mon')) wid = QGroupBox('Loop') lay = QGridLayout(wid) lay.addWidget(QLabel('<h4>SP</h4>'), 0, 1, alignment=Qt.AlignCenter) lay.addWidget(QLabel('<h4>RB</h4>'), 0, 2, alignment=Qt.AlignCenter) lay.addWidget(QLabel('<h4>Mon</h4>'), 0, 3, alignment=Qt.AlignCenter) lay.addWidget(ld_enbl, 1, 0) lay.addWidget(sb_enbl, 1, 1) lay.addWidget(lb_enbl, 1, 2) lay.addWidget(ld_gain_h, 2, 0) lay.addWidget(sb_gain_h, 2, 1) lay.addWidget(lb_gain_h, 2, 2) lay.addWidget(lb_gain_mon_h, 2, 3) lay.addWidget(ld_gain_v, 3, 0) lay.addWidget(sb_gain_v, 3, 1) lay.addWidget(lb_gain_v, 3, 2) lay.addWidget(lb_gain_mon_v, 3, 3) return wid def _setupAuxCommWidget(self): ld_orbdist = QLabel( 'Orbit Dist. Detec.: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) sb_orbdist = PyDMStateButton( self, self.devpref.substitute( propty='LoopMaxOrbDistortionEnbl-Sel')) lb_orbdist = SiriusLedState( self, self.devpref.substitute( propty='LoopMaxOrbDistortionEnbl-Sts')) self.ch_currzero = _ConnSignal( self.devpref.substitute(propty='CorrSetCurrZero-Cmd')) self.ch_clearacc = _ConnSignal( self.devpref.substitute(propty='CorrSetAccClear-Cmd')) btn_zero = CAPushButton('Reset Correctors current and Acc', self) btn_zero.setDefault(False) btn_zero.setAutoDefault(False) btn_zero.rules = self._enblrule btn_zero.clicked.connect(self._handle_reset_correctors) wid = QGroupBox('Aux. Commands') lay = QGridLayout(wid) lay.addWidget(btn_zero, 0, 0, 1, 3) lay.addWidget(ld_orbdist, 1, 0) lay.addWidget(sb_orbdist, 1, 1) lay.addWidget(lb_orbdist, 1, 2) lay.setColumnStretch(0, 2) lay.setColumnStretch(1, 1) lay.setColumnStretch(2, 1) return wid def _setupDetailsWidget(self): group2cmd = { 'Correctors': { 'Set all current to zero': 'CorrSetCurrZero-Cmd', 'Clear all Acc': 'CorrSetAccClear-Cmd', 'Set all PwrState to On': 'CorrSetPwrStateOn-Cmd', 'Set all PwrState to Off': 'CorrSetPwrStateOff-Cmd', 'Set all OpMode to Manual': 'CorrSetOpModeManual-Cmd', 'Set all AccFreeze to Enbl': 'CorrSetAccFreezeEnbl-Cmd', 'Set all AccFreeze to Dsbl': 'CorrSetAccFreezeDsbl-Cmd', }, 'Controllers': { 'Sync Net': 'CtrlrSyncNet-Cmd', 'Sync RefOrb': 'CtrlrSyncRefOrb-Cmd', }, 'BPMs': { 'Configure BPM Log.Trigs.': 'CtrlrConfBPMLogTrg-Cmd', }, } wid = QWidget() lay = QVBoxLayout(wid) lay.setContentsMargins(0, 0, 0, 0) for group, commands in group2cmd.items(): gbox = QGroupBox(group) glay = QVBoxLayout(gbox) if 'Corr' in group: glay2 = QGridLayout() glay2.setContentsMargins(0, 0, 0, 0) for row, dev in enumerate(['CH', 'CV']): lbl = QLabel( dev+' Sat. Limit [A]: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) pref = self.devpref spw = SiriusSpinbox( self, pref.substitute(propty=dev+'AccSatMax-SP')) rbw = SiriusLabel( self, pref.substitute(propty=dev+'AccSatMax-RB')) glay2.addWidget(lbl, row, 0) glay2.addWidget(spw, row, 1) glay2.addWidget(rbw, row, 2) lbl = QLabel( 'Curr. Ramp Down Dur. [s]: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) pref = self.devpref spw = SiriusSpinbox( self, pref.substitute(propty='CorrSetCurrZeroDuration-SP')) rbw = SiriusLabel( self, pref.substitute(propty='CorrSetCurrZeroDuration-RB')) glay2.addWidget(lbl, 2, 0) glay2.addWidget(spw, 2, 1) glay2.addWidget(rbw, 2, 2) lbl = QLabel( 'Decimation [count]: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) pref = self.devpref visrule = ( '[{"name": "VisRule", "property": "Visible", ' + '"expression": "ch[0] == 2", "channels": ' + '[{"channel": "' + pref.substitute(propty='FOFBAccDecimation-Sel') + '", "trigger": true}]}]') selw = SiriusEnumComboBox( self, pref.substitute(propty='FOFBAccDecimation-Sel')) stsw = SiriusLabel( self, pref.substitute(propty='FOFBAccDecimation-Sts')) stsw.rules = visrule.replace('==', '!=') lbldiv = CALabel(':') lbldiv.rules = visrule spw = SiriusSpinbox( self, pref.substitute(propty='FOFBAccDecimation-SP')) spw.rules = visrule spw.setVisible(False) lblpro = CALabel('(') lblpro.rules = visrule.replace('==', '!=') rbw = SiriusLabel( self, pref.substitute(propty='FOFBAccDecimation-RB')) rbw.setStyleSheet('min-width: 4em;') rbw.setAlignment(Qt.AlignCenter) lblprc = CALabel(')') lblprc.rules = visrule.replace('==', '!=') hbox = QHBoxLayout() hbox.addWidget(selw) hbox.addWidget(stsw) hbox.addWidget(lbldiv) hbox.addWidget(spw) hbox.addWidget(lblpro) hbox.addWidget(rbw) hbox.addWidget(lblprc) glay2.addWidget(lbl, 3, 0) glay2.addLayout(hbox, 3, 1, 1, 2) ld_psconfig = QLabel( 'PS Config. Matrix: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) wid_psconfig = PSConfigWidget(self, self.device, prefix=self.prefix) glay2.addWidget(ld_psconfig, 4, 0) glay2.addWidget(wid_psconfig, 4, 1, 2, 2) glay.addLayout(glay2) elif 'Control' in group: glay2 = QGridLayout() glay2.setContentsMargins(0, 0, 0, 0) pref = self.devpref lbl = QLabel( 'TimeFrameLen: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) spw = SiriusSpinbox( self, pref.substitute(propty='TimeFrameLen-SP')) rbw = SiriusLabel( self, pref.substitute(propty='TimeFrameLen-RB')) glay2.addWidget(lbl, 0, 0) glay2.addWidget(spw, 0, 1) glay2.addWidget(rbw, 0, 2) lbl = QLabel( 'Consider EnblList in Sync: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) pvn = pref.substitute(propty='CtrlrSyncUseEnblList-Sel') sbt = PyDMStateButton(self, pvn) sbt.rules = self._enblrule led = SiriusLedState(self, pvn.substitute(propty_suffix='Sts')) glay2.addWidget(lbl, 1, 0) glay2.addWidget(sbt, 1, 1) glay2.addWidget(led, 1, 2) lbl = QLabel( 'Orbit Dist. Thres. [um]: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) spw = SiriusSpinbox( self, pref.substitute(propty='LoopMaxOrbDistortion-SP')) rbw = SiriusLabel( self, pref.substitute(propty='LoopMaxOrbDistortion-RB')) glay2.addWidget(lbl, 2, 0) glay2.addWidget(spw, 2, 1) glay2.addWidget(rbw, 2, 2) lbl = QLabel( 'Enable Orbit Dist. Detec.: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) pvn = pref.substitute(propty='LoopMaxOrbDistortionEnbl-Sel') sbt = PyDMStateButton(self, pvn) led = SiriusLedState(self, pvn.substitute(propty_suffix='Sts')) glay2.addWidget(lbl, 3, 0) glay2.addWidget(sbt, 3, 1) glay2.addWidget(led, 3, 2) lbl = QLabel( 'Enable Packet Loss Detec.: ', self, alignment=Qt.AlignRight | Qt.AlignVCenter) pvn = pref.substitute(propty='LoopPacketLossDetecEnbl-Sel') sbt = PyDMStateButton(self, pvn) led = SiriusLedState(self, pvn.substitute(propty_suffix='Sts')) glay2.addWidget(lbl, 4, 0) glay2.addWidget(sbt, 4, 1) glay2.addWidget(led, 4, 2) glay.addLayout(glay2) elif 'BPM' in group: swbpm = BPMSwModeWidget(self, self.device, self.prefix) glay.addWidget(swbpm) for desc, cmd in commands.items(): btn = PyDMPushButton( self, label=desc, pressValue=1, init_channel=self.devpref.substitute(propty=cmd)) btn.setDefault(False) btn.setAutoDefault(False) btn.rules = self._enblrule glay.addWidget(btn) lay.addWidget(gbox) return wid def _setupControlWidget(self): self.controltabs = QTabWidget() self.controltabs.setObjectName('SITab') # tab main self.status = self._setupStatusWidget() self.loop = self._setupLoopWidget() self.auxcmd = self._setupAuxCommWidget() self.reforb = self._setupRefOrbWidget() self.respmat = RespMatWidget(self, self.device, self.prefix) maintab = QWidget() lay = QVBoxLayout(maintab) lay.addWidget(self.status) lay.addWidget(self.loop) lay.addWidget(self.auxcmd) lay.addWidget(self.reforb) lay.addWidget(self.respmat) self.controltabs.addTab(maintab, 'Main') # tab aux self.details = self._setupDetailsWidget() dettab = QWidget() lay = QVBoxLayout(dettab) lay.addWidget(self.details) self.controltabs.addTab(dettab, 'Details') wid = QWidget() lay = QVBoxLayout(wid) lay.addWidget(self.controltabs) # dock widget dockwid = QDockWidget(self) dockwid.setObjectName('SIApp') dockwid.setWindowTitle('FOFB Control') sz_pol = QSzPlcy(QSzPlcy.Preferred, QSzPlcy.Preferred) sz_pol.setVerticalStretch(1) dockwid.setSizePolicy(sz_pol) dockwid.setFloating(False) dockwid.setFeatures(QDockWidget.AllDockWidgetFeatures) dockwid.setAllowedAreas(Qt.AllDockWidgetAreas) dockwid.setWidget(wid) return dockwid def _setupLogWidget(self): loglabel = PyDMLogLabel( self, init_channel=self.devpref.substitute(propty='Log-Mon')) loglabel.setObjectName('log') loglabel.setStyleSheet('#log{min-width: 20em;}') loglabel.setSizePolicy(QSzPlcy.Minimum, QSzPlcy.MinimumExpanding) loglabel.setAlternatingRowColors(True) loglabel.maxCount = 2000 bt_clear = QPushButton('Clear Log', self) bt_clear.clicked.connect(loglabel.clear) wid = QWidget() lay = QVBoxLayout(wid) lay.addWidget(loglabel) lay.addWidget(bt_clear, alignment=Qt.AlignCenter) dockwid = QDockWidget(self) dockwid.setObjectName('SIApp') dockwid.setWindowTitle("FOFB Log") sz_pol = QSzPlcy(QSzPlcy.Preferred, QSzPlcy.Preferred) sz_pol.setVerticalStretch(1) dockwid.setSizePolicy(sz_pol) dockwid.setFloating(False) dockwid.setFeatures(QDockWidget.AllDockWidgetFeatures) dockwid.setAllowedAreas(Qt.AllDockWidgetAreas) dockwid.setWidget(wid) return dockwid def _handle_reset_correctors(self): self.ch_clearacc.send_value_signal[float].emit(1) _time.sleep(0.5) self.ch_currzero.send_value_signal[float].emit(1)