Example #1
0
class EmittanceMeasure(QWidget):

    def __init__(self, parent=None, place='LI', prefix=_VACA_PREFIX):
        super().__init__(parent=parent)
        self._place = place or 'LI'
        self._prefix = prefix
        self.setObjectName(self._place[0:2] + 'App')
        self._select_experimental_setup()
        self.nemitx_tm = []
        self.nemity_tm = []
        self.nemitx_parf = []
        self.nemity_parf = []
        self.betax_tm = []
        self.betay_tm = []
        self.betax_parf = []
        self.betay_parf = []
        self.alphax_tm = []
        self.alphay_tm = []
        self.alphax_parf = []
        self.alphay_parf = []
        self.measurement = None
        self.I_meas = None
        self.sigma = None
        self.plane_meas = None

        self._setupUi()

    def meas_emittance(self):
        self._acquire_data()
        self._perform_analysis()

    def _select_experimental_setup(self):
        if self._place.lower().startswith('li'):
            self.plt_image = ProcessImage(
                self, place='LI-Emittance', prefix=self._prefix)
            self.conv2kl = _NormFact.create('LI-01:MA-QF3')
            self.quad_I_sp = PV(_PVName(
                'LI-01:PS-QF3:Current-SP').substitute(prefix=self._prefix))
            self.quad_I_rb = PV(_PVName(
                'LI-01:PS-QF3:Current-Mon').substitute(prefix=self._prefix))
            self.DIST = 2.8775
            self.QUAD_L = 0.112
        if self._place.lower().startswith('tb-qd2a'):
            self.plt_image = ProcessImage(
                self, place='TB-Emittance', prefix=self._prefix)
            self.conv2kl = _NormFact.create('TB-02:MA-QD2A')
            self.quad_I_sp = PV(_PVName(
                'TB-02:PS-QD2A:Current-SP').substitute(prefix=self._prefix))
            self.quad_I_rb = PV(_PVName(
                'TB-02:PS-QD2A:Current-RB').substitute(prefix=self._prefix))
            self.DIST = 6.904
            self.QUAD_L = 0.1
        if self._place.lower().startswith('tb-qf2a'):
            self.plt_image = ProcessImage(
                self, place='TB-Emittance', prefix=self._prefix)
            self.conv2kl = _NormFact.create('TB-02:MA-QF2A')
            self.quad_I_sp = PV(_PVName(
                'TB-02:PS-QF2A:Current-SP').substitute(prefix=self._prefix))
            self.quad_I_rb = PV(_PVName(
                'TB-02:PS-QF2A:Current-RB').substitute(prefix=self._prefix))
            self.DIST = 6.534
            self.QUAD_L = 0.1

    def _acquire_data(self):
        samples = self.spbox_samples.value()
        outlier = self.spbox_outliers.value()
        if samples <= outlier:
            _log.warning(
                'Number of samples must be larger than number o outliers.')
            _log.warning('Acquisition aborted.')
            return
        nsteps = self.spbox_steps.value()
        I_ini = self.spbox_I_ini.value()
        I_end = self.spbox_I_end.value()

        outs = outlier // 2    # outliers below median
        outg = outlier - outs  # outliers above median

        self.line_sigmax.set_xdata([])
        self.line_sigmax.set_ydata([])
        self.line_sigmay.set_xdata([])
        self.line_sigmay.set_ydata([])
        self.line_fit.set_xdata([])
        self.line_fit.set_ydata([])
        self.fig_sigma.figure.canvas.draw()

        pl = 'y' if self.cbbox_plane.currentIndex() else 'x'
        curr_list = np.linspace(I_ini, I_end, nsteps)
        init_curr = self.quad_I_sp.value
        sigma = []
        I_meas = []
        for i, I in enumerate(curr_list):
            _log.info('setting quadrupole to {0:8.3f} A'.format(I))
            if not SIMUL:
                self.quad_I_sp.put(I, wait=True)
            self._measuring.wait(5 if i else 15)
            j = 0
            I_tmp = []
            sig_tmp = []
            while j < samples:
                if self._measuring.is_set():
                    self.pb_stop.setEnabled(False)
                    self.pb_start.setEnabled(True)
                    _log.info('Stopped')
                    return
                _log.info('    sample {0:02d}'.format(j))
                I_now = self.quad_I_rb.value
                cen_x, sigma_x, cen_y, sigma_y = self.plt_image.get_params()
                mu, sig = (cen_x, sigma_x) if pl == 'x' else (cen_y, sigma_y)
                max_size = self.spbox_threshold.value()*1e-3
                if sig > max_size:
                    self._measuring.wait(1)
                    continue
                I_tmp.append(I_now)
                sig_tmp.append(abs(sig))
                self._measuring.wait(0.5)
                j += 1
            ind = np.argsort(sig_tmp)
            I_tmp = np.array(I_tmp)[ind]
            sig_tmp = np.array(sig_tmp)[ind]
            I_meas.extend(I_tmp[outs:-outg])
            sigma.extend(sig_tmp[outs:-outg])
            if pl == 'x':
                self.line_sigmax.set_xdata(I_meas)
                self.line_sigmax.set_ydata(np.array(sigma)*1e3)
            else:
                self.line_sigmay.set_xdata(I_meas)
                self.line_sigmay.set_ydata(np.array(sigma)*1e3)
            self.fig_sigma.figure.axes[0].set_xlim(
                    [min(I_meas)*(1-DT*10), max(I_meas)*(1+DT*10)])
            self.fig_sigma.figure.axes[0].set_ylim(
                    [min(sigma)*(1-DT)*1e3, max(sigma)*(1+DT)*1e3])
            self.fig_sigma.figure.canvas.draw()
        self._measuring.set()
        _log.info('Returning Quad to Initial Current')
        self.quad_I_sp.put(init_curr, wait=True)

        self.pb_stop.setEnabled(False)
        self.pb_start.setEnabled(True)
        _log.info('Finished!')
        self.I_meas = I_meas
        self.sigma = sigma
        self.plane_meas = pl

    def _perform_analysis(self):
        sigma = np.array(self.sigma)
        I_meas = np.array(self.I_meas)
        pl = self.plane_meas
        K1 = self._get_K1_from_I(I_meas)

        # Transfer Matrix
        nem, bet, alp = self._trans_matrix_analysis(K1, sigma, pl=pl)
        getattr(self, 'nemit' + pl + '_tm').append(nem)
        getattr(self, 'beta' + pl + '_tm').append(bet)
        getattr(self, 'alpha' + pl + '_tm').append(alp)

        # Parabola Fitting
        nem, bet, alp = self._thin_lens_approx(K1, sigma, pl=pl)
        getattr(self, 'nemit' + pl + '_parf').append(nem)
        getattr(self, 'beta' + pl + '_parf').append(bet)
        getattr(self, 'alpha' + pl + '_parf').append(alp)

        for pref in ('nemit', 'beta', 'alpha'):
            for var in ('_tm', '_parf'):
                tp = pref + pl + var
                yd = np.array(getattr(self, tp))
                line = getattr(self, 'line_'+tp)
                line.set_xdata(np.arange(yd.shape[0]))
                line.set_ydata(yd)
                lb = getattr(self, 'lb_'+tp)
                lb.setText('{0:.3f}'.format(yd.mean()))
            params = []
            for var in ('x_tm', 'y_tm', 'x_parf', 'y_parf'):
                params.extend(getattr(self, pref + var))
            params = np.array(params)
            axes = getattr(self, 'line_' + pref + 'x_parf').axes
            axes.set_xlim([-0.1, params.shape[0]+0.1])
            axes.set_ylim([params.min()*(1-DT), params.max()*(1+DT)])
            self.fig_res.figure.canvas.draw()

    def _get_K1_from_I(self, I_meas):
        energy = self.spbox_energy.value() * 1e-3  # energy in GeV
        KL = self.conv2kl.conv_current_2_strength(
            I_meas, strengths_dipole=energy)
        return KL/self.QUAD_L

    def _trans_matrix_analysis(self, K1, sigma, pl='x'):
        Rx, Ry = self._get_trans_mat(K1)
        R = Rx if pl == 'x' else Ry
        pseudo_inv = (np.linalg.inv(np.transpose(R) @ R) @ np.transpose(R))
        [s_11, s_12, s_22] = pseudo_inv @ (sigma*sigma)
        # s_11, s_12, s_22 = np.linalg.lstsq(R, sigma * sigma)[0]
        nemit, beta, alpha, gamma = self._twiss(s_11, s_12, s_22)
        return nemit, beta, alpha

    def _thin_lens_approx(self, K1, sigma, pl='x'):
        K1 = K1 if pl == 'x' else -K1
        a, b, c = np.polyfit(K1, sigma*sigma, 2)
        yd = np.sqrt(np.polyval([a, b, c], K1))
        self.line_fit.set_xdata(self.I_meas)
        self.line_fit.set_ydata(yd*1e3)
        self.fig_sigma.figure.canvas.draw()

        d = self.DIST + self.QUAD_L/2
        l = self.QUAD_L
        s_11 = a/(d*l)**2
        s_12 = (-b-2*d*l*s_11)/(2*l*d*d)
        s_22 = (c-s_11-2*d*s_12)/d**2
        nemit, beta, alpha, gamma = self._twiss(s_11, s_12, s_22)
        return nemit, beta, alpha

    def _twiss(self, s_11, s_12, s_22):
        energy = self.spbox_energy.value()  # energy in MeV
        emit = np.sqrt(abs(s_11 * s_22 - s_12 * s_12))
        beta = s_11 / emit
        alpha = -s_12 / emit
        gamma = s_22 / emit
        nemit = emit * energy / E0 * 1e6  # in mm.mrad
        return nemit, beta, alpha, gamma

    def _get_trans_mat(self, K1):
        R = np.zeros((len(K1), 4, 4))
        Rd = gettransmat('drift', L=self.DIST)
        for i, k1 in enumerate(K1):
            Rq = gettransmat('quad', L=self.QUAD_L, K1=k1)
            R[i] = np.dot(Rd, Rq)
        R11 = R[:, 0, 0].reshape(-1, 1)
        R12 = R[:, 0, 1].reshape(-1, 1)
        R33 = R[:, 2, 2].reshape(-1, 1)
        R34 = R[:, 2, 3].reshape(-1, 1)
        Rx = np.column_stack((R11*R11, 2*R11*R12, R12*R12))
        Ry = np.column_stack((R33*R33, 2*R33*R34, R34*R34))
        return Rx, Ry

    def _setupUi(self):
        gl = QGridLayout(self)
        fig = mplt.figure()
        wid = MatplotlibWidget(fig, parent=self)
        wid.setObjectName('fig_result')
        wid.setStyleSheet('#fig_result{min-width: 25em;}')
        self.fig_res = wid

        gs = mgs.GridSpec(3, 1)
        gs.update(left=0.18, right=0.98, top=0.97, bottom=0.08, hspace=0.25)

        axes = wid.figure.add_subplot(gs[0, 0])
        axes.set_xlabel('Index')
        axes.set_ylabel('Normalized Emit. [mm.mrad]')
        axes.grid(True)
        axes.set_aspect('auto')
        self.line_nemitx_tm = axes.plot(
            self.nemitx_tm, '-bo', lw=1, label='Hor. Trans. Mat.')[0]
        self.line_nemitx_parf = axes.plot(
            self.nemitx_parf, '--bd', lw=1, label='Hor. Parab. Fit')[0]
        self.line_nemity_tm = axes.plot(
            self.nemity_tm, '--ro', lw=1, label='Vert. Trans. Mat.')[0]
        self.line_nemity_parf = axes.plot(
            self.nemity_parf, '--rd', lw=1, label='Vert. Parab. Fit')[0]
        axes.legend(loc='best')

        axes = wid.figure.add_subplot(gs[1, 0])
        axes.set_xlabel('Index')
        axes.set_ylabel(r'$\beta$ [m]')
        self.line_betax_tm = axes.plot(
            self.betax_tm, '-bo', lw=1, label='Hor. Trans. Mat.')[0]
        self.line_betax_parf = axes.plot(
            self.betax_parf, '--bd', lw=1, label='Hor. Parab. Fit')[0]
        self.line_betay_tm = axes.plot(
            self.betay_tm, '--ro', lw=1, label='Vert. Trans. Mat.')[0]
        self.line_betay_parf = axes.plot(
            self.betay_parf, '--rd', lw=1, label='Vert. Parab. Fit')[0]

        axes = wid.figure.add_subplot(gs[2, 0])
        axes.set_xlabel('Index')
        axes.set_ylabel(r'$\alpha$')
        self.line_alphax_tm = axes.plot(
            self.alphax_tm, '-bo', lw=1, label='Hor. Trans. Mat.')[0]
        self.line_alphax_parf = axes.plot(
            self.alphax_parf, '--bd', lw=1, label='Hor. Parab. Fit')[0]
        self.line_alphay_tm = axes.plot(
            self.alphay_tm, '--ro', lw=1, label='Vert. Trans. Mat.')[0]
        self.line_alphay_parf = axes.plot(
            self.alphay_parf, '--rd', lw=1, label='Vert. Parab. Fit')[0]

        measlay = QVBoxLayout()

        gb = QGroupBox('QF3 Current [A]', self)
        measlay.addWidget(gb)
        gb.setLayout(QHBoxLayout())
        spnbox = SiriusSpinbox(
            gb, _PVName('LI-01:PS-QF3:Current-SP').substitute(
                prefix=self._prefix))
        lbl = SiriusLabel(
            gb, _PVName('LI-01:PS-QF3:Current-Mon').substitute(
                prefix=self._prefix))
        spnbox.showStepExponent = False
        gb.layout().addWidget(spnbox)
        gb.layout().addWidget(lbl)
        gb.layout().setAlignment(Qt.AlignTop)

        gb = QGroupBox('Data Acquisition Configs.', self)
        fl = QFormLayout(gb)
        measlay.addWidget(gb)
        self.pb_start = QPushButton('Start', gb)
        self.pb_start.clicked.connect(self.pb_start_clicked)
        self.pb_stop = QPushButton('Stop', gb)
        self.pb_stop.clicked.connect(self.pb_stop_clicked)
        self.pb_stop.setEnabled(False)
        hbox_bts = QHBoxLayout()
        hbox_bts.addWidget(self.pb_start)
        hbox_bts.addWidget(self.pb_stop)
        fl.addRow(hbox_bts)
        self.cbbox_plane = QComboBox(gb)
        self.cbbox_plane.addItem('Horizontal')
        self.cbbox_plane.addItem('Vertical')
        fl.addRow(QLabel('Plane', gb), self.cbbox_plane)
        self.spbox_steps = QSpinBoxPlus(gb)
        self.spbox_steps.setValue(11)
        fl.addRow(QLabel('Nr Steps', gb), self.spbox_steps)
        self.spbox_samples = QSpinBoxPlus(gb)
        self.spbox_samples.setMinimum(1)
        self.spbox_samples.setValue(16)
        fl.addRow(QLabel('Nr Samples per step', gb), self.spbox_samples)
        self.spbox_outliers = QSpinBoxPlus(gb)
        self.spbox_outliers.setMinimum(0)
        self.spbox_outliers.setValue(12)
        fl.addRow(QLabel('Nr Outliers', gb), self.spbox_outliers)
        self.spbox_I_ini = QDoubleSpinBoxPlus(gb)
        self.spbox_I_ini.setMinimum(-4)
        self.spbox_I_ini.setMaximum(4)
        self.spbox_I_ini.setValue(-0.7)
        self.spbox_I_ini.setDecimals(3)
        fl.addRow(QLabel('Initial Current [A]', gb), self.spbox_I_ini)
        self.spbox_I_end = QDoubleSpinBoxPlus(gb)
        self.spbox_I_end.setMinimum(-4)
        self.spbox_I_end.setMaximum(4)
        self.spbox_I_end.setValue(0.7)
        self.spbox_I_end.setDecimals(3)
        fl.addRow(QLabel('Final Current [A]', gb), self.spbox_I_end)
        self.spbox_threshold = QDoubleSpinBoxPlus(gb)
        self.spbox_threshold.setMinimum(0)
        self.spbox_threshold.setMaximum(20)
        self.spbox_threshold.setValue(4)
        self.spbox_threshold.setDecimals(2)
        fl.addRow(QLabel('Max. Size Accpbl. [mm]', gb), self.spbox_threshold)

        measlay.setStretch(0, 2)
        measlay.setStretch(1, 8)

        anllay = QVBoxLayout()

        gb = QGroupBox('Analysis Configs.', self)
        vl = QVBoxLayout(gb)
        anllay.addWidget(gb)
        self.spbox_energy = QDoubleSpinBoxPlus(gb)
        self.spbox_energy.setMinimum(0.511)
        self.spbox_energy.setMaximum(200)
        self.spbox_energy.setValue(150)
        self.spbox_energy.setDecimals(2)
        self.pb_analyse_data = QPushButton('Analyse', gb)
        self.pb_analyse_data.clicked.connect(self.pb_analyse_data_clicked)
        hl = QHBoxLayout()
        hl.addWidget(QLabel('Energy [MeV]', gb))
        hl.addWidget(self.spbox_energy)
        hl.addWidget(self.pb_analyse_data)
        vl.addLayout(hl)
        self.pb_save_data = QPushButton('Save Raw', gb)
        self.pb_save_data.clicked.connect(self.pb_save_data_clicked)
        self.pb_load_data = QPushButton('Load Raw', gb)
        self.pb_load_data.clicked.connect(self.pb_load_data_clicked)
        hl = QHBoxLayout()
        hl.addWidget(self.pb_save_data)
        hl.addWidget(self.pb_load_data)
        vl.addLayout(hl)
        self.logdisplay = PyDMLogDisplay(self, level=_log.INFO)
        vl.addWidget(self.logdisplay)

        resultsgb = QGroupBox('Results', self)
        gl2 = QGridLayout(resultsgb)
        gl2.addWidget(QLabel('Trans. Matrix', resultsgb), 0, 1, 1, 2)
        gl2.addWidget(QLabel('Parabola Fit', resultsgb), 0, 3, 1, 2)
        gl2.addWidget(QLabel('Hor.', resultsgb), 1, 1)
        gl2.addWidget(QLabel('Vert.', resultsgb), 1, 2)
        gl2.addWidget(QLabel('Hor.', resultsgb), 1, 3)
        gl2.addWidget(QLabel('Vert.', resultsgb), 1, 4)
        gl2.addWidget(QLabel('Norm. emitt.\n[mm.mrad]', resultsgb), 2, 0)
        gl2.addWidget(QLabel('beta [m]', resultsgb), 3, 0)
        gl2.addWidget(QLabel('alpha', resultsgb), 4, 0)
        for i, pref in enumerate(('nemit', 'beta', 'alpha')):
            for j, tp in enumerate(('x_tm', 'y_tm', 'x_parf', 'y_parf')):
                name = pref + tp
                lb = QLabel('----', resultsgb)
                setattr(self, 'lb_' + name, lb)
                gl2.addWidget(lb, i+2, j+1)

        wid = MatplotlibWidget(parent=self)
        axes = wid.figure.add_subplot(111)
        axes.set_xlabel('Quad. Current [A]')
        axes.set_ylabel('Beam Size [mm]')
        wid.figure.set_tight_layout(True)
        self.line_sigmax = axes.plot([], 'bo', lw=1, label='Horizontal')[0]
        self.line_sigmay = axes.plot([], 'ro', lw=1, label='Vertical')[0]
        self.line_fit = axes.plot([], '-k', lw=1)[0]
        wid.setObjectName('fig_sigma')
        wid.setStyleSheet('#fig_sigma{min-width: 25em;}')
        self.fig_sigma = wid

        gl.addWidget(self.plt_image, 0, 0, 2, 1)
        gl.addItem(measlay, 0, 1)
        gl.addWidget(self.fig_sigma, 1, 1)
        gl.addItem(anllay, 0, 2)
        gl.addWidget(resultsgb, 1, 2)
        gl.addWidget(self.fig_res, 0, 3, 2, 1)

    def pb_save_data_clicked(self):
        if self.I_meas is None or self.sigma is None:
            msg = QMessageBox()
            msg.setIcon(QMessageBox.Warning)
            msg.setText("Could not Save")
            msg.setInformativeText(
                "There are no data saved in Memory. Make a measurement First.")
            msg.setWindowTitle("Warning")
            msg.resize(900, 300)
            msg.exec_()
            return
        fname = QFileDialog.getSaveFileName(
                self, 'Save file', '', 'Text Files (*.txt *.dat)')
        if fname[0]:
            self.save_to_file(fname[0])

    def save_to_file(self, fname):
        header = 'Plane = {0:s}\n'.format(self.plane_meas)
        header += '{0:15s} {1:15s}'.format('Current [A]', 'Beam Size [m]')
        np.savetxt(fname, np.column_stack(
            (self.I_meas, self.sigma)), header=header, fmt='%-15.9f %-15.10f')

    def pb_load_data_clicked(self):
        fname = QFileDialog.getOpenFileName(
            self, 'Open file', '', 'Text Files (*.txt *.dat)')
        if fname[0]:
            self.load_from_file(fname[0])

    def load_from_file(self, fname):
        try:
            self.I_meas, self.sigma = np.loadtxt(
                                        fname, skiprows=2, unpack=True)
        except (ValueError, TypeError):
            msg = QMessageBox()
            msg.setIcon(QMessageBox.Warning)
            msg.setText("Could not Load File")
            msg.setInformativeText(
                "The chosen file does not match the formatting.")
            msg.setWindowTitle("Warning")
            msg.resize(900, 300)
            msg.exec_()
            return
        with open(fname, 'r') as f:
            self.plane_meas = f.readline().split()[-1]

        if self.plane_meas == 'x':
            self.line_sigmax.set_xdata(self.I_meas)
            self.line_sigmax.set_ydata(np.array(self.sigma)*1e3)
        else:
            self.line_sigmay.set_xdata(self.I_meas)
            self.line_sigmay.set_ydata(np.array(self.sigma)*1e3)
        self.fig_sigma.figure.axes[0].set_xlim(
                [min(self.I_meas)*(1-DT*10), max(self.I_meas)*(1+DT*10)])
        self.fig_sigma.figure.axes[0].set_ylim(
                [min(self.sigma)*(1-DT)*1e3, max(self.sigma)*(1+DT)*1e3])
        self.fig_sigma.figure.canvas.draw()

    def pb_analyse_data_clicked(self):
        if self.I_meas is None or self.sigma is None:
            msg = QMessageBox()
            msg.setIcon(QMessageBox.Warning)
            msg.setText("Could Perform Analysis")
            msg.setInformativeText(
                "No data in memory. Please make a measurement or load the data.")
            msg.setWindowTitle("Warning")
            msg.resize(900, 300)
            msg.exec_()
            return
        self._perform_analysis()

    def pb_start_clicked(self):
        """
        Slot documentation goes here.
        """
        _log.info('Starting...')
        if self.measurement is not None and self.measurement.isAlive():
            return
        self.pb_stop.setEnabled(True)
        self.pb_start.setEnabled(False)
        self._measuring = Event()
        self.measurement = Thread(target=self.meas_emittance, daemon=True)
        self.measurement.start()

    def pb_stop_clicked(self):
        """
        Slot documentation goes here.
        """
        _log.info('Stopping...')
        self._measuring.set()
Example #2
0
    def _setupUi(self):
        cw = QWidget(self)
        self.setCentralWidget(cw)
        self.setFocusPolicy(Qt.StrongFocus)

        label = QLabel('<h4>Booster Charge</h4>',
                       self,
                       alignment=Qt.AlignCenter)

        # timeplot
        self.timeplot = SiriusTimePlot(parent=self, background='w')
        timespan = 60 * 60 * 6
        colors = ['blue', 'red', 'green', 'magenta']
        self.timeplot.timeSpan = timespan  # [s]
        self.timeplot.bufferSize = 2 * timespan  # [2 samples/s]
        self.timeplot.autoRangeY = True
        self.timeplot.showXGrid = True
        self.timeplot.showYGrid = True
        self.timeplot.setLabel('left', text='Charge', units='nC')
        self.timeplot.setObjectName('timeplot')
        self.timeplot.setStyleSheet(
            '#timeplot{min-width:28em; min-height: 18em;}')
        t_end = Time.now()
        t_init = t_end - timespan

        self._channels = dict()
        self._curves = dict()
        self._cb_show = dict()
        self._pvs_labels = dict()
        self._cb_offsets = dict()
        glay_aux = QGridLayout()
        glay_aux.setHorizontalSpacing(20)
        glay_aux.setVerticalSpacing(10)
        glay_aux.addWidget(QLabel('Show curves: ', self),
                           0,
                           0,
                           alignment=Qt.AlignCenter)
        glay_aux.addWidget(QLabel('Offset [nC]: ', self),
                           2,
                           0,
                           alignment=Qt.AlignCenter)
        for i, e in enumerate(self._energies):
            pvname = self._ioc_prefix.substitute(propty='Charge' + e + '-Mon')
            self._channels[e] = SiriusConnectionSignal(address=pvname)
            self._channels[e].new_value_signal[float].connect(
                self._update_charges)

            self.timeplot.addYChannel('Charge' + e,
                                      color=colors[i],
                                      lineWidth=2)
            curve = self.timeplot.curveAtIndex(-1)
            self._curves[e] = curve
            self.timeplot.fill_curve_with_archdata(self._curves[e],
                                                   pvname,
                                                   t_init=t_init.get_iso8601(),
                                                   t_end=t_end.get_iso8601())

            cb = QCheckBox(e)
            cb.setChecked(True)
            cb.setStyleSheet('color:' + colors[i] + ';')
            cb.stateChanged.connect(curve.setVisible)
            self._cb_show[e] = cb

            lb = QLabel('', self, alignment=Qt.AlignCenter)
            self._pvs_labels[e] = lb

            sb = QDoubleSpinBoxPlus(self)
            sb.energy = e
            sb.setMinimum(0)
            sb.setMaximum(1e6)
            sb.setDecimals(4)
            sb.setStyleSheet('max-width: 5em;')
            sb.setValue(self._latest_offsets[e])
            sb.editingFinished.connect(self._update_offset)
            self._cb_offsets[e] = sb

            glay_aux.addWidget(cb, 0, i + 1, alignment=Qt.AlignCenter)
            glay_aux.addWidget(lb, 1, i + 1, alignment=Qt.AlignCenter)
            glay_aux.addWidget(sb, 2, i + 1, alignment=Qt.AlignCenter)

        self.pb_offset = QPushButton('Update offset', self)
        self.pb_offset.clicked.connect(self._set_current_values_2_offset)
        self.pb_offset.setToolTip('Set offsets to current charge values.')
        glay_aux.addWidget(self.pb_offset, 1, 0)

        lay = QVBoxLayout(cw)
        lay.addWidget(label)
        lay.addWidget(self.timeplot)
        lay.addLayout(glay_aux)
Example #3
0
class InsertNormalizedConfig(SiriusDialog):
    """Auxiliary window to insert a new normalized config."""

    insertConfig = Signal(float, str, dict)

    def __init__(self, parent, ramp_config):
        """Initialize object."""
        super().__init__(parent)
        self.setObjectName('BOApp')
        self.ramp_config = ramp_config
        self.setWindowTitle('Insert Normalized Configuration')
        self._setupUi()

    def _setupUi(self):
        # selection widgets
        self.rb_interp = QRadioButton('Interpolate')
        self.rb_confsrv = QRadioButton('Take value from Config Server')
        self.rb_create = QRadioButton('Create from template')

        # data widget
        self.config_data = QWidget()
        glay_data = QGridLayout(self.config_data)
        self.le_interp_label = QLineEdit(self)  # interpolate
        self.le_confsrv_name = _ConfigLineEdit(
            parent=self, config_type='bo_normalized')  # from ConfigDB
        self.le_confsrv_name.textChanged.connect(
            self._handleInsertExistingConfig)
        self.le_confsrv_name.setVisible(False)
        self.le_nominal_label = QLineEdit(self)  # from template
        self.le_nominal_label.setVisible(False)
        self.sb_time = QDoubleSpinBoxPlus(self)
        self.sb_time.setMaximum(490)
        self.sb_time.setDecimals(3)
        self.bt_insert = QPushButton('Insert', self)
        self.bt_insert.setAutoDefault(False)
        self.bt_insert.setDefault(False)
        self.bt_insert.clicked.connect(self._emitConfigData)
        self.bt_cancel = QPushButton('Cancel', self)
        self.bt_cancel.setAutoDefault(False)
        self.bt_cancel.setDefault(False)
        self.bt_cancel.clicked.connect(self.close)
        glay_data.addWidget(QLabel('Label: ', self), 0, 0)
        glay_data.addWidget(self.le_interp_label, 0, 1)
        glay_data.addWidget(self.le_confsrv_name, 0, 1)
        glay_data.addWidget(self.le_nominal_label, 0, 1)
        glay_data.addWidget(QLabel('Time [ms]: ', self), 1, 0)
        glay_data.addWidget(self.sb_time, 1, 1)
        glay_data.addWidget(self.bt_cancel, 2, 0)
        glay_data.addWidget(self.bt_insert, 2, 1)

        # connect visibility signals
        self.rb_interp.toggled.connect(self.le_interp_label.setVisible)
        self.rb_interp.setChecked(True)
        self.rb_confsrv.toggled.connect(self.le_confsrv_name.setVisible)
        self.rb_create.toggled.connect(self.le_nominal_label.setVisible)

        # layout
        lay = QVBoxLayout()
        lay.addWidget(QLabel('<h4>Insert a Normalized Configuration</h4>',
                             self),
                      alignment=Qt.AlignCenter)
        lay.addWidget(self.rb_interp)
        lay.addWidget(self.rb_confsrv)
        lay.addWidget(self.rb_create)
        lay.addStretch()
        lay.addWidget(self.config_data)
        self.setLayout(lay)

    @Slot(str)
    def _handleInsertExistingConfig(self, configname):
        try:
            self.norm_config = ramp.BoosterNormalized()
            self.norm_config.name = configname
            self.norm_config.load()
            energy = self.norm_config[ramp.BoosterRamp.PSNAME_DIPOLE_REF]
            time = self.ramp_config.ps_waveform_interp_time(energy)
            self.sb_time.setValue(time)
        except _ConfigDBException as err:
            QMessageBox.critical(self, 'Error', str(err), QMessageBox.Ok)

    def _emitConfigData(self):
        time = self.sb_time.value()
        if self.le_interp_label.isVisible():
            label = self.le_interp_label.text()
            psname2strength = dict()
        elif self.le_confsrv_name.isVisible():
            label = self.le_confsrv_name.text()
            psname2strength = {
                _PVName(k).device_name: v
                for k, v, d in self.norm_config.value['pvs']
            }
        elif self.le_nominal_label.isVisible():
            label = self.le_nominal_label.text()
            psname2strength = \
                self.ramp_config.ps_normalized_config_nominal_values
        self.insertConfig.emit(time, label, psname2strength)
        self.close()
Example #4
0
class BONormEdit(SiriusMainWindow):
    """Widget to perform optics adjust in normalized configurations."""

    normConfigChanged = Signal(float, dict)

    def __init__(self, parent=None, prefix='', ramp_config=None,
                 norm_config=None, time=None, energy=None,
                 magnets=dict(), conn_sofb=None,
                 tunecorr_configname=None, chromcorr_configname=None):
        """Initialize object."""
        super().__init__(parent)
        self.setWindowTitle('Edit Normalized Configuration')
        self.setObjectName('BOApp')
        self.prefix = prefix
        self.ramp_config = ramp_config
        self.norm_config = _dcopy(norm_config)
        self.time = time
        self.energy = energy

        self._aux_magnets = magnets
        self._conn_sofb = conn_sofb
        self._tunecorr = BOTuneCorr(tunecorr_configname)
        self._chromcorr = BOChromCorr(chromcorr_configname)

        self._reference = _dcopy(norm_config)
        self._currChrom = self._estimateChrom(use_ref=True)
        self._deltas = {
            'kicks': dict(),
            'factorH': 0.0,
            'factorV': 0.0,
            'tuneX': 0.0,
            'tuneY': 0.0,
            'chromX': self._currChrom[0],
            'chromY': self._currChrom[1],
        }
        self._setupUi()
        self._setupMenu()
        self.verifySync()

    # ---------- setup/build layout ----------

    def _setupUi(self):
        self.label_description = QLabel(
            '<h2>'+self.norm_config['label']+'</h2>', self)
        self.label_description.setAlignment(Qt.AlignCenter)
        self.label_time = QLabel('<h2>T = '+str(self.time)+'ms</h2>', self)
        self.label_time.setAlignment(Qt.AlignCenter)

        self.strengths = self._setupStrengthWidget()
        self.orbit = self._setupOrbitWidget()
        self.tune = self._setupTuneWidget()
        self.chrom = self._setupChromWidget()

        self.bt_apply = QPushButton(qta.icon('fa5s.angle-right'), '', self)
        self.bt_apply.setToolTip('Apply Changes to Machine')
        self.bt_apply.setStyleSheet('icon-size: 30px 30px;')
        self.bt_apply.clicked.connect(self._updateRampConfig)

        cw = QWidget()
        lay = QGridLayout()
        lay.setVerticalSpacing(10)
        lay.setHorizontalSpacing(10)
        lay.addWidget(self.label_description, 0, 0, 1, 2)
        lay.addWidget(self.label_time, 1, 0, 1, 2)
        lay.addWidget(self.strengths, 2, 0, 4, 1)
        lay.addWidget(self.orbit, 2, 1)
        lay.addWidget(self.tune, 3, 1)
        lay.addWidget(self.chrom, 4, 1)
        lay.addWidget(self.bt_apply, 5, 1)
        lay.setColumnStretch(0, 2)
        lay.setColumnStretch(1, 2)
        lay.setRowStretch(0, 2)
        lay.setRowStretch(1, 2)
        lay.setRowStretch(2, 8)
        lay.setRowStretch(3, 8)
        lay.setRowStretch(4, 8)
        lay.setRowStretch(5, 1)
        cw.setLayout(lay)

        cw.setStyleSheet("""
            QGroupBox::title{
                subcontrol-origin: margin;
                subcontrol-position: top center;
                padding: 0 2px 0 2px;}""")
        cw.setFocusPolicy(Qt.StrongFocus)
        self.setCentralWidget(cw)

    def _setupMenu(self):
        self.menubar = QMenuBar(self)
        self.layout().setMenuBar(self.menubar)
        self.menu = self.menubar.addMenu('Options')
        self.act_saveas = self.menu.addAction('Save as...')
        self.act_saveas.triggered.connect(self._showSaveAsPopup)

        self._undo_stack = QUndoStack(self)
        self.act_undo = self._undo_stack.createUndoAction(self, 'Undo')
        self.act_undo.setShortcut(QKeySequence.Undo)
        self.menu.addAction(self.act_undo)
        self.act_redo = self._undo_stack.createRedoAction(self, 'Redo')
        self.act_redo.setShortcut(QKeySequence.Redo)
        self.menu.addAction(self.act_redo)

    def _setupStrengthWidget(self):
        scrollarea = QScrollArea()
        self.nconfig_data = QWidget()
        flay_configdata = QFormLayout()
        psnames = self._get_PSNames()
        self._map_psnames2wigdets = dict()
        for ps in psnames:
            ps = SiriusPVName(ps)
            if ps in ramp.BoosterRamp.PSNAME_DIPOLES:
                ps_value = QLabel(str(self.norm_config[ps])+' GeV', self)
                flay_configdata.addRow(QLabel(ps + ': ', self), ps_value)
            else:
                ps_value = QDoubleSpinBoxPlus(self.nconfig_data)
                ps_value.setDecimals(6)
                ps_value.setMinimum(-10000)
                ps_value.setMaximum(10000)
                ps_value.setValue(self.norm_config[ps])
                ps_value.valueChanged.connect(self._handleStrenghtsSet)

                if ps.dev in {'QD', 'QF', 'QS'}:
                    unit_txt = '1/m'
                elif ps.dev in {'SD', 'SF'}:
                    unit_txt = '1/m²'
                else:
                    unit_txt = 'urad'
                label_unit = QLabel(unit_txt, self)
                label_unit.setStyleSheet("min-width:2.5em; max-width:2.5em;")
                hb = QHBoxLayout()
                hb.addWidget(ps_value)
                hb.addWidget(label_unit)

                flay_configdata.addRow(QLabel(ps + ': ', self), hb)

            ps_value.setObjectName(ps)
            ps_value.setStyleSheet("min-height:1.29em; max-height:1.29em;")
            self._map_psnames2wigdets[ps] = ps_value

        self.nconfig_data.setObjectName('data')
        self.nconfig_data.setStyleSheet("""
            #data{background-color: transparent;}""")
        self.nconfig_data.setLayout(flay_configdata)
        scrollarea.setWidget(self.nconfig_data)

        self.cb_checklims = QCheckBox('Set limits according to energy', self)
        self.cb_checklims.setChecked(False)
        self.cb_checklims.stateChanged.connect(self._handleStrengtsLimits)

        self.bt_graph = QPushButton(qta.icon('mdi.chart-line'), '', self)
        self.bt_graph.clicked.connect(self._show_kicks_graph)

        gbox = QGroupBox()
        gbox.setObjectName('strengths')
        gbox.setStyleSheet('#strengths{min-width:20em;}')
        glay = QGridLayout()
        glay.addWidget(scrollarea, 0, 0, 1, 2)
        glay.addWidget(self.cb_checklims, 1, 0, alignment=Qt.AlignLeft)
        glay.addWidget(self.bt_graph, 1, 1, alignment=Qt.AlignRight)
        gbox.setLayout(glay)
        return gbox

    def _setupOrbitWidget(self):
        self.bt_get_kicks = QPushButton('Get Kicks from SOFB', self)
        self.bt_get_kicks.clicked.connect(self._handleGetKicksFromSOFB)

        label_correctH = QLabel('Correct H', self,
                                alignment=Qt.AlignRight | Qt.AlignVCenter)
        self.sb_correctH = QDoubleSpinBoxPlus(self)
        self.sb_correctH.setValue(self._deltas['factorH'])
        self.sb_correctH.setDecimals(1)
        self.sb_correctH.setMinimum(-10000)
        self.sb_correctH.setMaximum(10000)
        self.sb_correctH.setSingleStep(0.1)
        self.sb_correctH.setObjectName('factorH')
        self.sb_correctH.editingFinished.connect(self._handleCorrFactorsSet)
        labelH = QLabel('%', self)

        label_correctV = QLabel('Correct V', self,
                                alignment=Qt.AlignRight | Qt.AlignVCenter)
        self.sb_correctV = QDoubleSpinBoxPlus(self)
        self.sb_correctV.setValue(self._deltas['factorV'])
        self.sb_correctV.setDecimals(1)
        self.sb_correctV.setMinimum(-10000)
        self.sb_correctV.setMaximum(10000)
        self.sb_correctV.setSingleStep(0.1)
        self.sb_correctV.setObjectName('factorV')
        self.sb_correctV.editingFinished.connect(self._handleCorrFactorsSet)
        labelV = QLabel('%', self)

        self.bt_update_ref_orbit = QPushButton('Update reference', self)
        self.bt_update_ref_orbit.clicked.connect(
            _part(self._updateReference, 'corrs'))

        gbox = QGroupBox('Orbit', self)
        lay = QGridLayout()
        lay.addWidget(self.bt_get_kicks, 0, 0, 1, 4)
        lay.addWidget(label_correctH, 1, 0)
        lay.addWidget(self.sb_correctH, 1, 2)
        lay.addWidget(labelH, 1, 3)
        lay.addWidget(label_correctV, 2, 0)
        lay.addWidget(self.sb_correctV, 2, 2)
        lay.addWidget(labelV, 2, 3)
        lay.addWidget(self.bt_update_ref_orbit, 3, 2, 1, 2)
        lay.setColumnStretch(0, 16)
        lay.setColumnStretch(1, 1)
        lay.setColumnStretch(2, 14)
        lay.setColumnStretch(3, 2)
        gbox.setLayout(lay)
        return gbox

    def _setupTuneWidget(self):
        for cord in ['X', 'Y']:
            setattr(self, 'label_deltaTune'+cord,
                    QLabel('Δν<sub>'+cord+'</sub>: '))
            lab = getattr(self, 'label_deltaTune'+cord)
            lab.setStyleSheet("min-width:1.55em; max-width:1.55em;")

            setattr(self, 'sb_deltaTune'+cord, QDoubleSpinBoxPlus(self))
            sb = getattr(self, 'sb_deltaTune'+cord)
            sb.setDecimals(6)
            sb.setMinimum(-5)
            sb.setMaximum(5)
            sb.setSingleStep(0.0001)
            sb.setObjectName('tune'+cord)
            sb.editingFinished.connect(self._handleDeltaTuneSet)

        label_KL = QLabel('<h4>ΔKL [1/m]</h4>', self)
        label_KL.setStyleSheet("""min-height:1.55em; max-height:1.55em;
                                  qproperty-alignment: AlignCenter;""")
        self.l_deltaKLQF = QLabel('', self)
        self.l_deltaKLQD = QLabel('', self)

        self.bt_update_ref_deltaKL = QPushButton('Update reference', self)
        self.bt_update_ref_deltaKL.clicked.connect(
            _part(self._updateReference, 'quads'))

        gbox = QGroupBox('Tune', self)
        lay = QGridLayout()
        lay.addWidget(self.label_deltaTuneX, 1, 0)
        lay.addWidget(self.sb_deltaTuneX, 1, 1)
        lay.addWidget(self.label_deltaTuneY, 1, 3)
        lay.addWidget(self.sb_deltaTuneY, 1, 4)
        lay.addWidget(label_KL, 3, 0, 1, 5)
        lay.addWidget(QLabel('QF: '), 4, 0)
        lay.addWidget(self.l_deltaKLQF, 4, 1)
        lay.addWidget(QLabel('QD: '), 4, 3)
        lay.addWidget(self.l_deltaKLQD, 4, 4)
        lay.addWidget(self.bt_update_ref_deltaKL, 6, 3, 1, 2)
        lay.setVerticalSpacing(6)
        lay.setColumnStretch(0, 2)
        lay.setColumnStretch(1, 4)
        lay.setColumnStretch(2, 1)
        lay.setColumnStretch(3, 2)
        lay.setColumnStretch(4, 4)
        lay.setRowStretch(0, 1)
        lay.setRowStretch(1, 2)
        lay.setRowStretch(2, 1)
        lay.setRowStretch(3, 2)
        lay.setRowStretch(4, 2)
        lay.setRowStretch(5, 1)
        lay.setRowStretch(6, 2)
        gbox.setLayout(lay)
        return gbox

    def _setupChromWidget(self):
        for cord in ['X', 'Y']:
            setattr(self, 'label_Chrom'+cord,
                    QLabel('ξ<sub>'+cord+'</sub>: '))
            lab = getattr(self, 'label_Chrom'+cord)
            lab.setStyleSheet("min-width:1.55em; max-width:1.55em;")

            setattr(self, 'sb_Chrom'+cord, QDoubleSpinBoxPlus(self))
            sb = getattr(self, 'sb_Chrom'+cord)
            sb.setDecimals(6)
            sb.setMinimum(-5)
            sb.setMaximum(5)
            sb.setSingleStep(0.0001)
            sb.setObjectName('chrom'+cord)
            sb.setValue(self._deltas['chrom'+cord])
            sb.editingFinished.connect(self._handleChromSet)

        label_SL = QLabel('<h4>ΔSL [1/m<sup>2</sup>]</h4>', self)
        label_SL.setStyleSheet("""min-height:1.55em; max-height:1.55em;
                                  qproperty-alignment: AlignCenter;""")
        self.l_deltaSLSF = QLabel('', self)
        self.l_deltaSLSD = QLabel('', self)

        self.bt_update_ref_deltaSL = QPushButton('Update reference', self)
        self.bt_update_ref_deltaSL.clicked.connect(
            _part(self._updateReference, 'sexts'))

        gbox = QGroupBox('Chromaticity', self)
        lay = QGridLayout()
        lay.addWidget(self.label_ChromX, 1, 0)
        lay.addWidget(self.sb_ChromX, 1, 1)
        lay.addWidget(self.label_ChromY, 1, 3)
        lay.addWidget(self.sb_ChromY, 1, 4)
        lay.addWidget(label_SL, 3, 0, 1, 5)
        lay.addWidget(QLabel('SF: '), 4, 0)
        lay.addWidget(self.l_deltaSLSF, 4, 1)
        lay.addWidget(QLabel('SD: '), 4, 3)
        lay.addWidget(self.l_deltaSLSD, 4, 4)
        lay.addWidget(self.bt_update_ref_deltaSL, 6, 3, 1, 2)
        lay.setVerticalSpacing(6)
        lay.setColumnStretch(0, 2)
        lay.setColumnStretch(1, 4)
        lay.setColumnStretch(2, 1)
        lay.setColumnStretch(3, 2)
        lay.setColumnStretch(4, 4)
        lay.setRowStretch(0, 1)
        lay.setRowStretch(1, 2)
        lay.setRowStretch(2, 1)
        lay.setRowStretch(3, 2)
        lay.setRowStretch(4, 2)
        lay.setRowStretch(5, 1)
        lay.setRowStretch(6, 2)
        gbox.setLayout(lay)
        return gbox

    # ---------- server communication ----------

    def _save(self, name):
        try:
            nconf = ramp.BoosterNormalized()
            nconf.value = self.norm_config
            nconf.save(new_name=name)
        except _ConfigDBException as err:
            QMessageBox.critical(self, 'Error', str(err), QMessageBox.Ok)

    def _showSaveAsPopup(self):
        self._saveAsPopup = _SaveConfigDialog('bo_normalized', self)
        self._saveAsPopup.configname.connect(self._save)
        self._saveAsPopup.open()

    def verifySync(self):
        if self.ramp_config is None:
            return
        if not self.ramp_config.verify_ps_normalized_synchronized(
                self.time, value=self.norm_config):
            self.label_time.setStyleSheet('color: red;')
            self.label_description.setStyleSheet('color: red;')
            self.setToolTip("There are unsaved changes")
        else:
            self.label_time.setStyleSheet('color: black;')
            self.label_description.setStyleSheet('color: black;')
            self.setToolTip("")

    # ---------- strengths ----------

    def _handleStrenghtsSet(self, new_value):
        psname = self.sender().objectName()
        self._stack_command(
            self.sender(), self.norm_config[psname], new_value,
            message='set '+psname+' strength to {}'.format(new_value))
        self.norm_config[psname] = new_value
        self.verifySync()

    def _handleStrengtsLimits(self, state):
        psnames = list(self.norm_config.keys())
        psnames.remove('BO-Fam:PS-B-1')
        psnames.remove('BO-Fam:PS-B-2')
        psnames.remove('label')
        if state:
            for ps in psnames:
                ps_value = self.nconfig_data.findChild(
                    QDoubleSpinBoxPlus, name=ps)
                ma = _MASearch.conv_psname_2_psmaname(ps)
                aux = self._aux_magnets[ma]
                currs = (aux.current_min, aux.current_max)
                lims = aux.conv_current_2_strength(
                    currents=currs, strengths_dipole=self.energy)
                ps_value.setMinimum(min(lims))
                ps_value.setMaximum(max(lims))
        else:
            for ps in psnames:
                ps_value = self.nconfig_data.findChild(
                    QDoubleSpinBoxPlus, name=ps)
                ps_value.setMinimum(-10000)
                ps_value.setMaximum(10000)

    def _updateStrenghtsWidget(self, pstype):
        psnames = self._get_PSNames(pstype)
        wid2change = psnames if psnames else list(self.norm_config.keys())
        for wid in wid2change:
            value = self.norm_config[wid]
            self._map_psnames2wigdets[wid].setValue(value)

    # ---------- orbit ----------

    def _updateCorrKicks(self):
        for psname, dkick in self._deltas['kicks'].items():
            corr_factor = self._deltas['factorV'] if 'CV' in psname \
                else self._deltas['factorH']
            corr_factor /= 100
            self.norm_config[psname] = self._reference[psname] + \
                dkick*corr_factor

    def _handleGetKicksFromSOFB(self):
        if not self._conn_sofb.connected:
            QMessageBox.warning(
                self, 'Not Connected',
                'There are not connected PVs!', QMessageBox.Ok)
            return
        dkicks = self._conn_sofb.get_deltakicks()

        if not dkicks:
            QMessageBox.warning(
                self, 'Could not get kicks',
                'Could not get kicks from SOFB!', QMessageBox.Ok)
            return

        self._deltas['kicks'] = dkicks
        self._updateCorrKicks()
        self._updateStrenghtsWidget('corrs')
        self.verifySync()

    def _handleCorrFactorsSet(self):
        widget = self.sender()
        factor = widget.objectName()
        dim = ' vertical ' if factor == 'factorV' else ' horizantal '
        new_value = widget.value()
        self._stack_command(
            widget, self._deltas[factor], new_value,
            message='set'+dim+'orbit correction factor to {}'.format(
                    new_value))
        self._deltas[factor] = new_value

        self._updateCorrKicks()
        self._updateStrenghtsWidget('corrs')
        self.verifySync()

    def _resetOrbitChanges(self):
        self._deltas['kicks'] = dict()
        self._deltas['factorH'] = 0.0
        self._deltas['factorV'] = 0.0
        self.sb_correctH.setValue(0.0)
        self.sb_correctV.setValue(0.0)

    # ---------- tune ----------

    def _handleDeltaTuneSet(self):
        widget = self.sender()
        tune = widget.objectName()
        dim = ' vertical ' if tune == 'tuneY' else ' horizantal '
        new_value = widget.value()
        self._stack_command(
            widget, self._deltas[tune], new_value,
            message='set'+dim+'delta tune to {}'.format(
                    new_value))
        self._deltas[tune] = new_value

        self._updateDeltaKL()

    def _updateDeltaKL(self):
        self._deltaKL = self._tunecorr.calculate_deltaKL(
            [self._deltas['tuneX'], self._deltas['tuneY']])
        self.l_deltaKLQF.setText('{: 4f}'.format(self._deltaKL[0]))
        self.l_deltaKLQD.setText('{: 4f}'.format(self._deltaKL[1]))

        self.norm_config['BO-Fam:PS-QF'] = \
            self._reference['BO-Fam:PS-QF'] + self._deltaKL[0]
        self.norm_config['BO-Fam:PS-QD'] = \
            self._reference['BO-Fam:PS-QD'] + self._deltaKL[1]

        self._updateStrenghtsWidget('quads')
        self.verifySync()

    def _resetTuneChanges(self):
        self.sb_deltaTuneX.setValue(0)
        self.sb_deltaTuneY.setValue(0)
        self._deltaKL = [0.0, 0.0]
        self.l_deltaKLQF.setText('{: 6f}'.format(self._deltaKL[0]))
        self.l_deltaKLQD.setText('{: 6f}'.format(self._deltaKL[1]))

    # ---------- chromaticity ----------

    def _estimateChrom(self, use_ref=False):
        nom_SL = self._chromcorr.nominal_intstrengths.flatten()
        if use_ref:
            curr_SL = _np.array([self._reference['BO-Fam:PS-SF'],
                                 self._reference['BO-Fam:PS-SD']])
        else:
            curr_SL = _np.array([self.norm_config['BO-Fam:PS-SF'],
                                 self.norm_config['BO-Fam:PS-SD']])
        delta_SL = curr_SL - nom_SL
        return self._chromcorr.calculate_Chrom(delta_SL)

    def _handleChromSet(self):
        widget = self.sender()
        chrom = widget.objectName()
        dim = ' vertical ' if chrom == 'chromY' else ' horizantal '
        new_value = widget.value()
        self._stack_command(
            widget, self._deltas[chrom], new_value,
            message='set'+dim+'chromaticity to {}'.format(
                    new_value))
        self._deltas[chrom] = new_value

        self._updateDeltaSL()

    def _updateDeltaSL(self):
        desired_Chrom = _np.array([self._deltas['chromX'],
                                   self._deltas['chromY']])
        deltas = desired_Chrom - self._currChrom
        self._deltaSL = self._chromcorr.calculate_deltaSL(
            [deltas[0], deltas[1]])
        self.l_deltaSLSF.setText('{: 4f}'.format(self._deltaSL[0]))
        self.l_deltaSLSD.setText('{: 4f}'.format(self._deltaSL[1]))

        self.norm_config['BO-Fam:PS-SF'] = \
            self._reference['BO-Fam:PS-SF'] + self._deltaSL[0]
        self.norm_config['BO-Fam:PS-SD'] = \
            self._reference['BO-Fam:PS-SD'] + self._deltaSL[1]

        self._updateStrenghtsWidget('sexts')
        self.verifySync()

    def _resetChromChanges(self):
        self._currChrom = self._estimateChrom(use_ref=True)
        self.sb_ChromX.setValue(self._currChrom[0])
        self.sb_ChromY.setValue(self._currChrom[1])
        self._deltaSL = [0.0, 0.0]
        self.l_deltaSLSF.setText('{: 6f}'.format(self._deltaSL[0]))
        self.l_deltaSLSD.setText('{: 6f}'.format(self._deltaSL[1]))

    # ---------- update methods ----------

    def _updateReference(self, pstype):
        psnames = self._get_PSNames(pstype)
        for ps in psnames:
            self._reference[ps] = self.norm_config[ps]

        if pstype == 'corrs':
            self._resetOrbitChanges()
        elif pstype == 'quads':
            self._resetTuneChanges()
        elif pstype == 'sexts':
            self._resetChromChanges()
        else:
            self._resetOrbitChanges()
            self._resetTuneChanges()
            self._resetChromChanges()

        self.verifySync()

    def _updateRampConfig(self):
        if self.norm_config is not None:
            self.normConfigChanged.emit(self.time, _dcopy(self.norm_config))

    def updateTime(self, time):
        """Update norm config time."""
        self.time = time
        self.label_time.setText('<h2>T = '+str(time)+'ms</h2>')
        self.energy = self.ramp_config.ps_waveform_interp_energy(time)
        self._handleStrengtsLimits(self.cb_checklims.checkState())
        self.verifySync()

    def updateLabel(self, label):
        """Update norm config label."""
        self.norm_config['label'] = label
        self.label_description.setText('<h2>'+label+'</h2>')
        self.verifySync()

    @Slot(str, str)
    def updateSettings(self, tunecorr_configname, chromcorr_configname):
        self._tunecorr = BOTuneCorr(tunecorr_configname)
        self._chromcorr = BOChromCorr(chromcorr_configname)
        self._updateDeltaKL()
        self._estimateChrom(use_ref=True)
        self._updateDeltaSL()

    # ---------- handle undo redo stack ----------

    def _stack_command(self, widget, old_value, new_value, message):
        global _flag_stack_next_command, _flag_stacking
        if _flag_stack_next_command and (old_value != new_value):
            _flag_stacking = True
            command = _UndoRedoSpinbox(widget, old_value, new_value, message)
            self._undo_stack.push(command)
        else:
            _flag_stack_next_command = True

    # ---------- helper methods ----------

    def _get_PSNames(self, pstype=None):
        psnames = list()
        if pstype == 'corrs':
            psnames = _PSSearch.get_psnames({'sec': 'BO', 'dev': 'C(V|H)'})
        elif pstype == 'quads':
            psnames = ['BO-Fam:PS-QF', 'BO-Fam:PS-QD']
        elif pstype == 'sexts':
            psnames = ['BO-Fam:PS-SF', 'BO-Fam:PS-SD']
        else:
            psnames = _PSSearch.get_psnames({'sec': 'BO', 'sub': 'Fam'})
            psnames.extend(_PSSearch.get_psnames({'sec': 'BO', 'dev': 'QS'}))
            psnames.extend(_PSSearch.get_psnames({'sec': 'BO', 'dev': 'CH'}))
            psnames.extend(_PSSearch.get_psnames({'sec': 'BO', 'dev': 'CV'}))
        return psnames

    def _show_kicks_graph(self):
        strenghts_dict = _dcopy(self.norm_config)
        strenghts_dict.pop('label')
        graph = _ShowCorrectorKicks(self, self.time, strenghts_dict)
        graph.show()
Example #5
0
class EnergyButton(QWidget):
    """Set dipole energy."""

    def __init__(self, section, parent=None):
        """Setups widget interface."""
        super().__init__(parent)
        self._section = section
        self.dips, self.mags = init_section(section.upper())
        self._items_fail = []
        self._items_success = []
        self._setup_ui()
        color = QColor(get_appropriate_color(section.upper()))
        pal = self.palette()
        pal.setColor(QPalette.Background, color)
        self.setAutoFillBackground(True)
        self.setPalette(pal)

    def _setup_ui(self):
        self.setLayout(QVBoxLayout())

        self.energy_value = QDoubleSpinBoxPlus(self)
        self.energy_value.setSingleStep(0.01)
        self.energy_value.setMinimum(self.lower_limit)
        self.energy_value.setMaximum(self.upper_limit)
        self.energy_value.setDecimals(4)

        if self.section == 'tb':
            sp_channel = 'TB-Fam:PS-B:Energy-RB'
        elif self.section == 'bo':
            sp_channel = 'BO-Fam:PS-B-1:Energy-RB'
        elif self.section == 'ts':
            sp_channel = 'TS-Fam:PS-B:Energy-RB'
        elif self.section == 'si':
            sp_channel = 'SI-Fam:PS-B1B2-1:Energy-RB'
        else:
            raise RuntimeError
        sp_channel = _PVName(sp_channel).substitute(prefix=_VACA_PREFIX)
        self.energy_sp = PyDMLabel(self)
        self.energy_sp.channel = sp_channel
        self.energy_sp.showUnits = True

        self._tree = PVNameTree(
            items=self.mags, tree_levels=('dis', 'mag_group'), parent=self)
        self._tree.tree.setHeaderHidden(True)
        self._tree.tree.setColumnCount(1)
        self._tree.check_all()
        self._tree.collapse_all()

        self.set_energy = QPushButton('Set', self)
        self.set_energy.clicked.connect(self._process)

        # forml = Q
        hbl = QHBoxLayout()
        # hbl.addStretch()
        hbl.addWidget(QLabel('<h4>New Energy:</h4> ', self))
        hbl.addWidget(self.energy_value)
        self.layout().addLayout(hbl)
        hbl = QHBoxLayout()
        # hbl.addStretch()
        hbl.addWidget(QLabel('Current Energy: ', self))
        hbl.addWidget(self.energy_sp)
        self.layout().addLayout(hbl)
        self.layout().addWidget(self._tree)
        self.layout().addWidget(self.set_energy)

    @property
    def section(self):
        return self._section

    @property
    def upper_limit(self):
        if self.section == 'tb':
            return 0.155
        elif self.section == 'bo':
            return 0.160
        elif self.section == 'ts':
            return 3.2
        elif self.section == 'si':
            return 3.014
        else:
            raise RuntimeError

    @property
    def lower_limit(self):
        if self.section == 'tb':
            return 0.0
        elif self.section == 'bo':
            return 0.0
        elif self.section == 'ts':
            return 0.0
        elif self.section == 'si':
            return 0.0
        else:
            raise RuntimeError

    def _process(self):
        self._items_success = []
        self._items_fail = []

        # Get selected PVs
        selected_pvs = set(self._tree.checked_items())
        mags = [mag for mag in self.mags if mag in selected_pvs]
        if not mags:
            report = ReportDialog(['Select at least one PS!'], self)
            report.exec_()
            return

        pvs = self.dips + mags

        conn = EpicsConnector(pvs, parent=self)
        get_task = EpicsGetter(pvs, parent=self)
        get_task.itemRead.connect(self._read_success)
        get_task.itemNotRead.connect(self._read_fail)
        dlg = ProgressDialog(
            ['Connecting', 'Reading'], [conn, get_task], parent=self)
        ret = dlg.exec_()
        if ret == dlg.Rejected:
            return
        if self._items_fail:
            failed = ['Failed to read some PVs', ]
            failed += self._items_fail + ['Aborting!', ]
            report = ReportDialog(failed, self)
            report.exec_()
            return

        energy = self.energy_value.value()
        # remove dipole
        pvs, values = zip(*self._items_success[len(self.dips):])
        delays = [0.0, ] * len(pvs)
        self._items_success = []

        energies = [energy, ] * len(self.dips)
        dly_dips = [0.0, ] * len(self.dips)
        conn = EpicsConnector(self.dips, parent=self)
        set_dip = EpicsSetter(self.dips, energies, dly_dips, parent=self)
        sleep_task = EpicsWait([None, ]*10, wait_time=2.0, parent=self)
        check_dip = EpicsChecker(self.dips, energies, dly_dips, parent=self)
        check_dip.itemChecked.connect(self._check_status)

        dlg = ProgressDialog(
            ['Connecting', 'Setting Dipole', 'Waiting Dipole',
             'Checking Dipole'],
            [conn, set_dip, sleep_task, check_dip], parent=self)
        ret = dlg.exec_()
        if ret == dlg.Rejected:
            return
        if self._items_fail:
            failed = ['Failed to set Dipole. Aborting!']
            report = ReportDialog(failed, self)
            report.exec_()
            return

        conn = EpicsConnector(pvs, parent=self)
        set_mags = EpicsSetter(pvs, values, delays, parent=self)
        sleep_task = EpicsWait([None, ]*10, wait_time=2.0, parent=self)
        check_mags = EpicsChecker(pvs, values, delays, parent=self)
        check_mags.itemChecked.connect(self._check_status)

        dlg = ProgressDialog(
            ['Connecting to Magnets', 'Setting Magnets', 'Waiting Magnets',
             'Checking Magnets'],
            [conn, set_mags, sleep_task, check_mags], parent=self)
        ret = dlg.exec_()
        if ret == dlg.Rejected:
            return

        failed = []
        if self._items_fail:
            failed = ['Failed to set magnets:', ] + self._items_fail
        report = ReportDialog(failed, self)
        report.exec_()

    @Slot(str)
    def _read_fail(self, item):
        self._items_fail.append(item)

    @Slot(str, QVariant)
    def _read_success(self, item, value):
        self._items_success.append((item, value))

    @Slot(str, bool)
    def _check_status(self, item, status):
        if status:
            self._items_success.append((item, True))
        else:
            self._items_fail.append(item)