예제 #1
0
    def warmup(self):
        """
        A convenience method for 'priming' the plugin.
        The plugin has to 'see' one acquisition before it is ready to capture.
        This sets the array size, etc.
        NOTE : this comes from:
            https://github.com/NSLS-II/ophyd/blob/master/ophyd/areadetector/plugins.py
        We had to replace "cam" with "settings" here.
        Also modified the stage sigs.
        """
        print(whisper("                warming up the hdf5 plugin..."))
        set_and_wait(self.enable, 1)
        sigs = OrderedDict([(self.parent.settings.array_callbacks, 1),
                            (self.parent.settings.trigger_mode, 'Internal'),
                            # just in case the acquisition time is set very long...
                            (self.parent.settings.acquire_time, 1),
                            # (self.capture, 1),
                            (self.parent.settings.acquire, 1)])

        original_vals = {sig: sig.get() for sig in sigs}

        # Remove the hdf5.capture item here to avoid an error as it should reset back to 0 itself
        # del original_vals[self.capture]

        for sig, val in sigs.items():
            ttime.sleep(0.1)  # abundance of caution
            set_and_wait(sig, val)

        ttime.sleep(2)  # wait for acquisition

        for sig, val in reversed(list(original_vals.items())):
            ttime.sleep(0.1)
            set_and_wait(sig, val)
        print(whisper("                done"))
예제 #2
0
    def cycle(self, mc=None):
        '''Cycle power to the amplifiers on a motor controller, then reenable
        the motors on that controller.

        Identify the motor controller by these strings:
           'dcm', 'slits2', 'm2', 'm3', 'dm3'

        '''
        if self.check(mc) is False:
            return
        print(bold_msg(f'Cycling amplifiers on {mc} motor controller'))
        print(whisper('killing amplifiers'))
        self.kill(mc)
        countdown(5)
        print(whisper('reactivating amplifiers'))
        self.enable(mc)
        print(whisper('enabling motors'))
        if mc == 'm2':
            user_ns['m2'].ena()
        elif mc == 'm3':
            user_ns['m3'].ena()
        elif mc == 'slits2':
            user_ns['slits2'].enable()
        elif mc == 'dm3':
            user_ns['slits3'].enable()
            for axis in ('dm3_bct', 'dm3_bpm', 'dm3_foils', 'dm3_fs'):
                user_ns[axis].enable()
                time.sleep(0.5)
                user_ns[axis].kill()
        elif mc == 'dcm':
            user_ns['dcm'].ena()
            time.sleep(0.5)
            user_ns['dcm'].kill()
예제 #3
0
    def make_merged_triplot(self, uidlist, filename, mode):
        '''Make a pretty, three panel plot of the data from the scan sequence
        just finished.

        '''
        BMMuser = user_ns['BMMuser']
        cnt = 0
        try:
            base = Pandrosus()
            projname = os.path.join(BMMuser.folder, 'prj',
                                    os.path.basename(filename)).replace(
                                        '.png', '.prj')
            proj = create_athena(projname)
            base.fetch(uidlist[0], mode=mode)
            ee = base.group.energy
            mm = base.group.mu
            save = base.group.args['label']
            proj.add_group(base.group)
            base.group.args['label'] = save
            cnt = 1
            if len(uidlist) > 1:
                for uid in uidlist[1:]:
                    this = Pandrosus()
                    try:
                        this.fetch(uid, mode=mode)
                        mu = numpy.interp(ee, this.group.energy, this.group.mu)
                        mm = mm + mu
                        save = this.group.args['label']
                        proj.add_group(this.group)
                        this.group.args['label'] = save
                        cnt += 1
                    except:
                        pass  # presumably this is noisy data for which a valid background was not found
        except:
            pass  # presumably this is noisy data for which a valid background was not found
        if cnt == 0:
            print(whisper(f'Unable to make triplot'))
            try:
                self.simple_plot(uidlist, filename, mode)
            except:
                print(whisper(f'Also unable to make simple plot'))
            return
        mm = mm / cnt
        merge = Pandrosus()
        merge.put(ee, mm, 'merge')
        thisagg = matplotlib.get_backend()
        matplotlib.use('Agg')  # produce a plot without screen display
        merge.triplot()
        plt.savefig(filename)
        print(whisper(f'Wrote triplot to {filename}'))
        matplotlib.use(thisagg)  # return to screen display
        proj.save()
        print(whisper(f'Wrote Athena project to {projname}'))
예제 #4
0
 def do_ChangeXtals(self):
     if user_ns['dcm']._crystal == '111':
         print(
             go_msg('You would like to change from the ') +
             whisper('Si(111)') + go_msg(' to the ') + bold_msg('Si(311)') +
             go_msg(' crystals...\n'))
         print(disconnected_msg('yield from change_xtals("311")'))
     else:
         print(
             go_msg('You would like to change from the ') +
             whisper('Si(311)') + go_msg(' to the ') + bold_msg('Si(111)') +
             go_msg(' crystals...\n'))
         print(disconnected_msg('yield from change_xtals("111")'))
     yield from null()
예제 #5
0
 def align_linear(self, force=False, drop=None):
     '''Fit an error function to the linear scan against It. Plot the
     result. Move to the centroid of the error function.'''
     if self.orientation == 'parallel':
         motor = user_ns['xafs_liny']
     else:
         motor = user_ns['xafs_linx']
     yield from linescan(motor, 'it', -2.3, 2.3, 51, pluck=False)
     close_last_plot()
     table  = user_ns['db'][-1].table()
     yy     = table[motor.name]
     signal = table['It']/table['I0']
     if drop is not None:
         yy = yy[:-drop]
         signal = signal[:-drop]
     if float(signal[2]) > list(signal)[-2] :
         ss     = -(signal - signal[2])
         self.inverted = 'inverted '
     else:
         ss     = signal - signal[2]
         self.inverted    = ''
     mod    = StepModel(form='erf')
     pars   = mod.guess(ss, x=numpy.array(yy))
     out    = mod.fit(ss, pars, x=numpy.array(yy))
     print(whisper(out.fit_report(min_correl=0)))
     target = out.params['center'].value
     yield from mv(motor, target)
     self.y_plot(yy, out)
예제 #6
0
def all_connected(with_m2=False):
    motors = [
        dm3_bct,
        xafs_yu,
        xafs_ydo,
        xafs_ydi,
        m3_yu,
        m3_ydo,
        m3_ydi,
        m3_xu,
        m3_xd,
    ]
    if with_m2 is True:
        motors.extend([m2_yu, m2_ydo, m2_ydi])
    ok = True
    for m in motors:
        if m.connected is False:
            print(disconnected_msg(f'{m.name} is not connected'))
            for walk in m.walk_signals(include_lazy=False):
                if walk.item.connected is False:
                    print(
                        disconnected_msg(
                            f'      {walk.item.name} is a disconnected PV'))
            print(
                whisper(
                    f'try: {m.name} = {m.__class__}("{m.prefix}", name={m.name})'
                ))
            ok = False
    return ok
예제 #7
0
 def select(self, el):
     '''Choose the ROI configured for element el'''
     if type(el) is int:
         if el < 1 or el > 3:
             self.myprint(error_msg('\n%d is not a valid ROI channel\n' % el))
             return
         el = self.slots[el-1]
     if el is None:
         self.myprint(error_msg('\nThat ROI is not configured\n'))
         return
     if Z_number(el) is None:
         self.myprint(error_msg('\n%s is not an element\n' % el))
         return
     selected = False
     vor = user_ns['vor']
     BMMuser = user_ns['BMMuser']
     for i in range(3):
         if element_symbol(el) == self.slots[i]:
             BMMuser.roi_channel = i+1
             if i == 0:      # help out the best effort callback
                 (BMMuser.roi1, BMMuser.roi2, BMMuser.roi3, BMMuser.roi4) = ('ROI1', 'ROI2', 'ROI3', 'ROI4')
                 (BMMuser.dtc1, BMMuser.dtc2, BMMuser.dtc3, BMMuser.dtc4) = ('DTC1', 'DTC2', 'DTC3', 'DTC4')
                 vor.set_hints(1)
             elif i == 1:
                 (BMMuser.roi1, BMMuser.roi2, BMMuser.roi3, BMMuser.roi4) = ('ROI2_1', 'ROI2_2', 'ROI2_3', 'ROI2_4')
                 (BMMuser.dtc1, BMMuser.dtc2, BMMuser.dtc3, BMMuser.dtc4) = ('DTC2_1', 'DTC2_2', 'DTC2_3', 'DTC2_4')
                 vor.set_hints(2)
             elif i == 2:
                 (BMMuser.roi1, BMMuser.roi2, BMMuser.roi3, BMMuser.roi4) = ('ROI3_1', 'ROI3_2', 'ROI3_3', 'ROI3_4')
                 (BMMuser.dtc1, BMMuser.dtc2, BMMuser.dtc3, BMMuser.dtc4) = ('DTC3_1', 'DTC3_2', 'DTC3_3', 'DTC3_4')
                 vor.set_hints(3)
             report('Set ROI channel to %s at channel %d' % (el.capitalize(), i+1))
             selected = True
     if not selected:
         self.myprint(whisper('%s is not in a configured channel, not changing BMMuser.roi_channel' % el.capitalize()))
예제 #8
0
        def doscan():
            line1 = '%s, %s, %.3f, %.3f, %d -- starting at %.3f\n' % \
                    (motor.name, sgnl, start, stop, nsteps, motor.user_readback.get())

            uid = yield from rel_scan(dets, motor, start, stop, nsteps, md={'plan_name' : f'rel_scan linescan {motor.name} I0'})
            t  = user_ns['db'][-1].table()
            if detector.lower() == 'if':
                signal   = numpy.array((t[BMMuser.xs1]+t[BMMuser.xs2]+t[BMMuser.xs3]+t[BMMuser.xs4])/t['I0'])
            elif detector.lower() == 'it':
                signal   = numpy.array(t['It']/t['I0'])
            elif detector.lower() == 'ir':
                signal   = numpy.array(t['Ir']/t['It'])

            signal = signal - signal[0]
            if negate is True:
                signal = -1 * signal
            pos      = numpy.array(t[motor.name])
            mod      = RectangleModel(form='erf')
            pars     = mod.guess(signal, x=pos)
            out      = mod.fit(signal, pars, x=pos)
            print(whisper(out.fit_report(min_correl=0)))
            out.plot()
            if filename is not None:
                plt.savefig(filename)
            #middle   = out.params['center1'].value + (out.params['center2'].value - out.params['center1'].value)/2
            yield from mv(motor, out.params['midpoint'].value)
            print(bold_msg(f'Found center at {motor.name} = {motor.position}'))
            rkvs.set('BMM:lmfit:center1',   out.params['center1'].value)
            rkvs.set('BMM:lmfit:center2',   out.params['center2'].value)
            rkvs.set('BMM:lmfit:sigma1',    out.params['sigma1'].value)
            rkvs.set('BMM:lmfit:sigma2',    out.params['sigma2'].value)
            rkvs.set('BMM:lmfit:amplitude', out.params['amplitude'].value)
            rkvs.set('BMM:lmfit:midpoint',  out.params['midpoint'].value)
 def align_y(self, force=False, drop=None):
     '''Fit an error function to the xafs_y scan against It. Plot the
     result. Move to the centroid of the error function.'''
     xafs_y = user_ns['xafs_y']
     db = user_ns['db']
     yield from linescan(xafs_y, 'it', -1, 1, 31, pluck=False)
     close_last_plot()
     table = db[-1].table()
     yy = table['xafs_y']
     signal = table['It'] / table['I0']
     if drop is not None:
         yy = yy[:-drop]
         signal = signal[:-drop]
     if float(signal[2]) > list(signal)[-2]:
         ss = -(signal - signal[2])
         self.inverted = 'inverted '
     else:
         ss = signal - signal[2]
         self.inverted = ''
     mod = StepModel(form='erf')
     pars = mod.guess(ss, x=numpy.array(yy))
     out = mod.fit(ss, pars, x=numpy.array(yy))
     print(whisper(out.fit_report(min_correl=0)))
     self.y_plot(yy, out)
     target = out.params['center'].value
     yield from mv(xafs_y, target)
예제 #10
0
    def do_ChangeEdge(self):
        print(go_msg('You would like to change to a different edge...\n'))

        el = input(" What element? ")
        el = el.capitalize()
        if el == '':
            print(whisper('doing nothing'))
            return (yield from null())
        if el not in ELEMENTS:
            print(error_msg(f'{el} is not an element'))
            return (yield from null())

        if Z_number(el) < 46:
            default_ed = 'K'
            prompt = go_msg('K') + '/L3/L2/L1'
        else:
            default_ed = 'L3'
            prompt = 'K/' + go_msg('L3') + '/L2/L1'
        ed = input(f' What edge? [{prompt}] ')
        ed = ed.capitalize()
        if ed not in ('K', 'L3', 'L2', 'L1'):
            ed = default_ed

        focus = input(' Focused beam? [y/N] ')
        if focus.lower() == 'y':
            focus = True
        else:
            focus = False

        print(
            disconnected_msg(
                f'yield from change_edge("{el}", focus={focus}, edge="{ed}")'))
        yield from null()
예제 #11
0
def wafer_edge(motor='x'):
    '''Fit an error function to the linear scan against It. Plot the
    result. Move to the centroid of the error function.'''
    if motor == 'x':
        motor = user_ns['xafs_linx']
    else:
        motor = user_ns['xafs_liny']
    yield from linescan(motor, 'it', -2, 2, 41, pluck=False)
    close_last_plot()
    table = user_ns['db'][-1].table()
    yy = table[motor.name]
    signal = table['It'] / table['I0']
    if float(signal[2]) > list(signal)[-2]:
        ss = -(signal - signal[2])
    else:
        ss = signal - signal[2]
    mod = StepModel(form='erf')
    pars = mod.guess(ss, x=numpy.array(yy))
    out = mod.fit(ss, pars, x=numpy.array(yy))
    print(whisper(out.fit_report(min_correl=0)))
    out.plot()
    target = out.params['center'].value
    yield from mv(motor, target)
    yield from resting_state_plan()
    print(
        f'Edge found at X={user_ns["xafs_x"].position} and Y={user_ns["xafs_y"].position}'
    )
예제 #12
0
def calibrate_pitch(mono='111'):
    BMMuser = user_ns['BMMuser']
    # read content from INI file
    datafile = os.path.join(BMMuser.DATA, 'edges%s.ini' % mono)
    print(f'reading {datafile}')
    config.read_file(open(datafile))

    edges = dict()
    for i in config.items('edges'):
        el = i[0]
        vals = [float(j) for j in i[1].split(',')
                ]  # convert CSV string -> list of strings -> list of floats
        edges[el] = vals

    # organize the data from the INI file
    ordered = [y[1] for y in sorted([(edges[x][1], x) for x in edges.keys()])]
    ee = list()
    tt = list()
    for el in ordered:
        ee.append(edges[el][1])
        tt.append(edges[el][3])

    mod = LinearModel()
    pars = mod.guess(tt, x=ee)
    out = mod.fit(tt, pars, x=ee)
    print(whisper(out.fit_report(min_correl=0)))
    out.plot()
예제 #13
0
def show_shutters():

    ena_text = '  Beamline: '
    try:
        if bl_enabled.get() == 1:
            ena_text += 'enabled      '
        else:
            ena_text += error_msg('disabled      ')
    except:
        ena_text += whisper('unavailable   ')

    bmps_text = '  BMPS: '
    try:
        bmps_state = bool(bmps.state.get())
        if bmps_state is True:
            bmps_text += 'open'
        else:
            bmps_text += error_msg('closed')
    except:
        bmps_text += whisper('unavailable   ')

    idps_text = '            IDPS: '
    try:
        idps_state = bool(idps.state.get())
        if idps_state is True:
            idps_text += 'open'
        else:
            idps_text += error_msg('closed')
    except:
        idps_text += whisper('unavailable   ')

    # sha_state = bool(sha.enabled.get()) and bool(sha.state.get())
    # sha_text = '            FOE Shutter: '
    # if sha_state is True:
    #     sha_text += 'open'
    # else:
    #     sha_text += error_msg('closed')

    shb_state = bool(shb.state.get())
    shb_text = '            Photon Shutter: '
    if shb_state is False:
        shb_text += 'open'
    else:
        shb_text += error_msg('closed')

    return (ena_text + bmps_text + idps_text + shb_text)
예제 #14
0
    def write_ini_and_plan(self):
        #################################
        # write out the master INI file #
        #################################
        config = configparser.ConfigParser()
        default = self.measurements[0].copy()
        #          things in the spreadsheet but not in the INI file
        for k in ('default', 'slot', 'measure', 'spin', 'focus', 'method', 'samplep', 'samplex', 'sampley', 'slitwidth', 'detectorx', 'settle', 'power', 'temperature', 'motor1', 'position1', 'motor2', 'position2'):
            default.pop(k, None)
        default['url'] = '...'
        default['doi'] = '...'
        default['cif'] = '...'
        default['experimenters'] = self.ws['E1'].value  # top line of xlsx file
        default = self.ini_sanity(default)
        if default is None:
            print(error_msg(f'Could not interpret {self.source} as a wheel macro.'))
            return
        # print(default)
        config.read_dict({'scan': default})
        with open(self.ini, 'w') as configfile:
            config.write(configfile)
        print(whisper('Wrote default INI file: %s' % self.ini))

        ########################################################
        # write the full macro to a file and %run -i that file #
        ########################################################
        with open(self.tmpl) as f:
            text = f.readlines()
        fullmacro = ''.join(text).format(folder      = self.folder,
                                         base        = self.basename,
                                         content     = self.content,
                                         description = self.description,
                                         instrument  = self.instrument,
                                         cleanup     = self.cleanup,
                                         initialize  = self.initialize)
        o = open(self.macro, 'w')
        o.write(fullmacro)
        o.close()
        ## I think this will never be called by queueserver
        from IPython import get_ipython
        ipython = get_ipython()
        ipython.magic('run -i \'%s\'' % self.macro)
        print(whisper('Wrote and read macro file: %s' % self.macro))
예제 #15
0
 def reset_rois(self, el=None, tab=''):
     BMMuser = user_ns['BMMuser']
     if el is None:
         el = BMMuser.element
     if el in self.slots:
         print(whisper(f'{tab}Resetting rois with {el} as the active ROI'))
         BMMuser.element = el
         self.set_rois()
         self.measure_roi()
     else:
         print(
             error_msg(
                 f'{tab}Cannot reset rois, {el} is not in {self.name}.slots'
             ))
예제 #16
0
def ocd(text, signal):
    '''Indicated open/close/disconnected state with suitable text coloring.
    '''
    outtext = text
    try:
        if signal.connected is False:
            outtext += disconnected_msg('disconnected ')
        elif signal.get() == 1:
            outtext += 'enabled      '
        else:
            outtext += error_msg('disabled      ')
    except:
        outtext += whisper('unavailable   ')
    return outtext
예제 #17
0
 def status(self):
     text = '\n  %s is %s\n\n' % (self.name, self.prefix)
     for signal in list(self.configuration_attrs):
         if signal.upper() not in status_list.keys():
             continue
         suffix = getattr(self, signal).pvname.replace(self.prefix, '')
         string = getattr(self, signal).enum_strs[getattr(self,
                                                          signal).get()]
         if signal != 'asscs':
             if getattr(self, signal).get() != status_list[signal.upper()]:
                 string = error_msg('%-19s' % string)
         text += '  %-26s : %-19s  %s   %s \n' % (
             getattr(self, signal + '_desc').get(), string,
             bold_msg(getattr(self, signal).get()), whisper(suffix))
     boxedtext('%s status signals' % self.name, text, 'green')
예제 #18
0
    def _pressure(self):
        #print(self.pressure.get())
        #print(type(self.pressure.get()))
        if self.connected is False:
            return(disconnected_msg('?????'))
        if self.pressure.get() == 'OFF':
            return(disconnected_msg(-1.1E-15))

        if type(self.pressure.get()) is str and self.pressure.get() == 'LO<E-11':
            return whisper('1.00e-11')
        if float(self.pressure.get()) > 1e-6:
            return error_msg(self.pressure.get())
        if float(self.pressure.get()) > 1e-8:
            return warning_msg(self.pressure.get())
        return(self.pressure.get())
예제 #19
0
    def cycle(self, mc=None):
        '''Cycle power to the amplifiers on a motor controller, then reenable
        the motors on that controller.

        Identify the motor controller by these strings:
           'dcm', 'slits2', 'm2', 'm3', 'dm3'

        '''
        if self.check(mc) is False:
            return
        print(bold_msg(f'Cycling amplifiers on {mc} motor controller'))
        print(whisper('killing amplifiers'))
        self.kill(mc)
        countdown(5)
        print(whisper('reactivating amplifiers'))
        self.enable(mc)
        print(whisper('enabling motors'))
        if mc == 'm2':
            m2.ena()
        elif mc == 'm3':
            m3.ena()
        elif mc == 'slits2':
            slits2.enable()
        elif mc == 'dm3':
            slits3.enable()
            for axis in (dm3_bct, dm3_bpm, dm3_foils, dm3_fs):
                try:
                    axis.enable()
                    time.sleep(0.5)
                    axis.kill()
                except:
                    pass
        elif mc == dcm:
            dcm.ena()
            time.sleep(0.5)
            dcm.kill()
예제 #20
0
def report(text, level=None, slack=False):
    '''Print a string to:
      * the log file
      * the screen
      * the BMM beamtime slack channel

    Report level decorations  on screen:

      * 'error' (red)
      * 'warning' (yellow)
      * 'info' (brown)
      * 'url' (undecorated)
      * 'bold' (bright white)
      * 'verbosebold' (bright cyan)
      * 'list' (cyan)
      * 'disconnected' (purple)
      * 'whisper' (gray)

    not matching a report level will be undecorated
    '''
    BMMuser = user_ns['BMMuser']
    BMM_log_info(text)
    if color:  # test that color is sensible...
        if level == 'error':
            print(error_msg(text))
        elif level == 'warning':
            print(warning_msg(text))
        elif level == 'info':
            print(info_msg(text))
        elif level == 'url':
            print(url_msg(text))
        elif level == 'bold':
            print(bold_msg(text))
        elif level == 'verbosebold':
            print(verbosebold_msg(text))
        elif level == 'disconnected':
            print(disconnected_msg(text))
        elif level == 'list':
            print(list_msg(text))
        elif level == 'whisper':
            print(whisper(text))
        else:
            print(text)
    else:
        print(text)
    if BMMuser.use_slack and slack:
        post_to_slack(text)
예제 #21
0
        def scan_dcmpitch(sgnl):
            line1 = '%s, %s, %.3f, %.3f, %d -- starting at %.3f\n' % \
                    (motor.name, sgnl, start, stop, nsteps, motor.user_readback.get())

            yield from abs_set(user_ns['_locked_dwell_time'], 0.1, wait=True)
            yield from dcm.kill_plan()

            yield from mv(slits3.vsize, 3)
            if sgnl == 'Bicron':
                yield from mv(slitsg.vsize, 5)

            uid = yield from rel_scan(dets, motor, start, stop, nsteps)
            #yield from rel_adaptive_scan(dets, 'I0', motor,
            #                             start=start,
            #                             stop=stop,
            #                             min_step=0.002,
            #                             max_step=0.03,
            #                             target_delta=.15,
            #                             backstep=True)
            t = db[-1].table()
            signal = t[sgnl]
            if choice.lower() == 'com':
                position = com(signal)
                top = t[motor.name][position]
            elif choice.lower() == 'fit':
                pitch = t['dcm_pitch']
                mod = SkewedGaussianModel()
                pars = mod.guess(signal, x=pitch)
                out = mod.fit(signal, pars, x=pitch)
                print(whisper(out.fit_report(min_correl=0)))
                out.plot()
                top = out.params['center'].value
            else:
                position = peak(signal)
                top = t[motor.name][position]

            yield from sleep(3.0)
            yield from abs_set(motor.kill_cmd, 1, wait=True)
            RE.msg_hook = BMM_msg_hook

            BMM_log_info('rocking curve scan: %s\tuid = %s, scan_id = %d' %
                         (line1, uid, user_ns['db'][-1].start['scan_id']))
            yield from mv(motor, top)
            if sgnl == 'Bicron':
                yield from mv(slitsg.vsize, gonio_slit_height)
예제 #22
0
 def status(self):
     text = '\n  %s is %s\n\n' % (self.name, self.prefix)
     for signal in status_list.keys():
         sig = signal.lower()
         try:
             suffix = getattr(self, sig).pvname.replace(self.prefix, '')
             string = getattr(self, sig).enum_strs[getattr(self, sig).get()]
             if signal != 'asscs':
                 if getattr(self, sig).get() != status_list[signal]:
                     string = verbosebold_msg('%-19s' % string)
             #text += '  %-26s : %-19s  %s   %s \n' % (getattr(self, sig+'_desc').get(),
             #                                         string,
             #                                         bold_msg(getattr(self, sig).get()),
             #                                         whisper(suffix))
             text += '  %-19s  %s   %s \n' % (
                 string, bold_msg(getattr(self,
                                          sig).get()), whisper(suffix))
         except:
             pass
     boxedtext('%s status signals' % self.name, text, 'green')
예제 #23
0
    def simple_plot(self, uidlist, filename, mode):
        '''If the triplot cannot be made for some reason, make a fallback,
        much simpler plot so that something is available for Slack and
        the dossier.

        '''
        BMMuser = user_ns['BMMuser']
        this = db.v2[uidlist[0]].primary.read()
        if mode == 'test':
            title, ylab = 'XAS test scan', 'I0 (nA)'
            signal = this['I0']
        elif mode == 'transmission':
            title, ylab = '', 'transmission'
            signal = numpy.log(numpy.abs(this['I0'] / this['It']))
        elif mode == 'reference':
            title, ylab = '', 'reference'
            signal = numpy.log(numpy.abs(this['It'] / this['Ir']))
        elif mode == 'yield':
            title, ylab = '', 'yield'
            signal = this['Iy'] / this['I0']
        elif mode == 'xs1':
            title, ylab = '', 'fluorescence'
            signal = (this[BMMuser.xs8] + 0.001) / (this['I0'] + 1)
        else:
            title, ylab = '', 'fluorescence'
            signal = (this[BMMuser.xs1] + this[BMMuser.xs2] + this[BMMuser.xs3]
                      + this[BMMuser.xs4] + 0.001) / (this['I0'] + 1)

        thisagg = matplotlib.get_backend()
        matplotlib.use('Agg')  # produce a plot without screen display
        plt.cla()
        plt.title(title)
        plt.xlabel('energy (eV)')
        plt.ylabel(ylab)
        plt.plot(this['dcm_energy'], signal)
        plt.savefig(filename)
        matplotlib.use(thisagg)  # return to screen display
        print(whisper(f'Wrote simple plot to {filename}'))
예제 #24
0
def check_for_synaxis():
    '''A disconnected motor (due to IOC or controller not running) will be
    defined as a SynAxis. This does a test for that situation and
    reports about it at startup.  It also sets BMMuser.syns to True so
    things like motor_status() behave non-disastrously.
    '''
    BMMuser.syns = False
    syns = []
    for m in mcs8_motors:
        if 'SynAxis' in f'{m}':
            syns.append(m.name)
    if len(syns) > 0:
        BMMuser.syns = True
        text = 'The following are disconnected & defined as simulated motors:\n\n'
        text += '\n'.join(
            disconnected_msg(x)
            for x in textwrap.wrap(', '.join(syns))) + '\n\n'
        text += 'This allows bsui to operate normally, but do not expect anything\n'
        text += 'involving those motors to work correctly.\n'
        text += whisper(
            '(This likely means that an IOC or a motor controller (or both) are off.)'
        )
        boxedtext('Disconnected motors', text, 'red', width=74)
예제 #25
0
    def new_experiment(self,
                       folder,
                       gup=0,
                       saf=0,
                       name='Betty Cooper',
                       use_pilatus=False,
                       echem=False):
        '''
        Do the work of prepping for a new experiment.  This will:
          * Create a data folder and it's subfolders, if needed, and set the DATA variable
          * Set up the experimental log, creating an experiment.log file, if needed
          * Write templates for scan.ini and macro.py + xlsx templates, if needed
          * Make folders for XRF, HDF5, Pilatus, and electrochemistry
          * Set the GUP and SAF numbers as metadata

        Parameters
        ----------
        folder : str
            data destination
        gup : str
            GUP number
        saf : str
            SAF number
        name : str
            name of PI
        use_pilatus : bool
            true if this experiment uses the Pilatus
        echem : bool
            true if this experiment uses the BioLogic potentiostat
        '''

        step = 1
        ## --*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--
        ## Main folders, point BMMuser and wmb objects at data folder
        data_folder = os.path.join(folder, self.date)
        user_ns['DATA'] = self.DATA = self.folder = data_folder + '/'
        try:
            hdf5folder = os.path.join('/nsls2', 'data', 'bmm', 'assets',
                                      'xspress3', *self.date.split('-'))
            user_ns['xs'].hdf5.read_path_template = hdf5folder
            user_ns['xs'].hdf5.write_path_template = hdf5folder
            user_ns['xs'].hdf5.file_path.put(hdf5folder)
        except:
            # if we are starting up bsui, the xs folder will get set
            # later.  the try block is needed when running
            # start_experiment from a running bsui instance.
            pass
        try:
            user_ns['wmb'].folder = data_folder + '/'
        except:
            pass

        imagefolder = os.path.join(data_folder, 'snapshots')
        prjfolder = os.path.join(data_folder, 'prj')
        htmlfolder = os.path.join(data_folder, 'dossier')
        self.establish_folder(step, 'Lustre folder', folder)
        self.establish_folder(0, 'data folder', data_folder)
        self.establish_folder(0, 'snapshot folder', imagefolder)
        self.establish_folder(0, 'Athena prj folder', prjfolder)
        if self.establish_folder(0, 'dossier folder', htmlfolder) == 'Created':
            # 'sample.tmpl', 'sample_xs.tmpl', 'sample_ga.tmpl'
            for f in ('manifest.tmpl', 'logo.png', 'style.css', 'trac.css'):
                shutil.copyfile(os.path.join(startup_dir, 'dossier', f),
                                os.path.join(htmlfolder, f))
            manifest = open(os.path.join(self.DATA, 'dossier', 'MANIFEST'),
                            'a')
            manifest.close()
            print('    copied html generation files, touched MANIFEST')

        step += 1

        ## --*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--
        ## setup logger
        BMM_user_log(os.path.join(data_folder, 'experiment.log'))
        print('%2d. Set up experimental log file:          %-75s' %
              (step, os.path.join(data_folder, 'experiment.log')))
        step += 1

        ## --*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--
        ## scan.ini template, macro template & wheel/ga spreadsheets
        initmpl = os.path.join(startup_dir, 'tmpl', 'scan.tmpl')
        scanini = os.path.join(data_folder, 'scan.ini')
        if not os.path.isfile(scanini):
            with open(initmpl) as f:
                content = f.readlines()
            o = open(scanini, 'w')
            o.write(''.join(content).format(folder=data_folder, name=name))
            o.close()
            verb, pad = 'Copied', ' '
        else:
            verb, pad = 'Found', '  '
        self.print_verb_message(step, verb, 'INI file', pad, scanini)

        macrotmpl = os.path.join(startup_dir, 'tmpl', 'macro.tmpl')
        macropy = os.path.join(data_folder, 'sample_macro.py')
        commands = '''
        ## sample 1
        yield from slot(1)
        yield from xafs('scan.ini', filename='samp1', sample='first sample')
        close_last_plot()
    
        ## sample 2
        yield from slot(2)
        ## yield from mvr(xafs_x, 0.5)
        yield from xafs('scan.ini', filename='samp2', sample='another sample', comment='my comment')
        close_last_plot()

        ## sample 3
        yield from slot(3)
        yield from xafs('scan.ini', filename='samp3', sample='a different sample', prep='this sample prep', nscans=4)
        close_last_plot()'''
        if not os.path.isfile(macropy):
            with open(macrotmpl) as f:
                content = f.readlines()
            o = open(macropy, 'w')
            o.write(''.join(content).format(folder=data_folder,
                                            base='sample',
                                            content=commands,
                                            description='(example...)',
                                            instrument='',
                                            cleanup='',
                                            initialize=''))
            o.close()
            verb, pad = 'Copied', ' '
        else:
            verb, pad = 'Found', '  '
        self.print_verb_message(0, verb, 'macro template', pad, macropy)

        self.find_or_copy_file(0, 'wheel macro spreadsheet', 'wheel.xlsx')
        self.find_or_copy_file(0, 'glancing angle spreadsheet',
                               'glancing_angle.xlsx')
        self.find_or_copy_file(0, 'double wheel spreadsheet',
                               'doublewheel.xlsx')
        self.find_or_copy_file(0, 'Linkam stage spreadsheet', 'linkam.xlsx')
        self.find_or_copy_file(0, 'motor grid spreadsheet', 'grid.xlsx')
        step += 1

        ## --*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--
        ## XRF & HDF5folders
        xrffolder = os.path.join(data_folder, 'XRF')
        self.establish_folder(step, 'XRF spectra folder', xrffolder)
        #hdf5folder = os.path.join(data_folder, 'raw', 'HDF5')
        #hdf5folder = os.path.join('/nsls2', 'data', 'bmm', 'assets', 'xspress3', *self.date.split('-'))
        #self.establish_folder(0, 'Xspress3 HDF5 folder', hdf5folder)
        #xs.hdf5.file_path.put(hdf5folder)

        ## --*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--
        ## Pilatus folder
        if use_pilatus:
            pilfolder = os.path.join(data_folder, 'raw', 'Pilatus')
            self.establish_folder(0, 'Pilatus folder', pilfolder)

        ## --*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--
        ## echem folder
        if echem:
            self.echem = True
            ecfolder = os.path.join(data_folder, 'raw', 'electrochemistry')
            self.establish_folder(0, 'electrochemistry folder', ecfolder)
            #self.echem_remote = os.path.join('/mnt/nfs/ws3', name, self.date)
            #if not os.path.isdir(self.echem_remote):
            #    os.makedirs(self.echem_remote)
            #    print('   Created remote echem folder:       %-75s' % (self.echem_remote))
            #else:
            #    print('   Found remote echem folder:         %-75s' % (self.echem_remote))

        step += 1

        ## --*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--
        ## GDrive folder
        user_folder = make_gdrive_folder(prefix='    ', update=False)
        for f in ('logo.png', 'style.css', 'trac.css'):
            shutil.copyfile(os.path.join(startup_dir, 'dossier', f),
                            os.path.join(user_folder, 'dossier', f))
        print('%2d. Established Google Drive folder:       %-75s' %
              (step, user_folder))
        print(
            whisper(
                f'    Don\'t foget to share Google Drive folder and Slack channel with {name}'
            ))
        step += 1

        ## --*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--
        ## SAF and GUP
        self.gup = gup
        self.saf = saf
        print(
            f'{step:2d}. Set GUP and SAF numbers as metadata    GUP: {gup}   SAF: {saf}'
        )
        shipping_folder = os.path.join(os.getenv('HOME'), 'SDS', self.cycle,
                                       f'{saf}')
        self.establish_folder(0, 'shipping docs folder', shipping_folder)
        step += 1

        self.user_is_defined = True

        return None
예제 #26
0
    def _write_macro(self):
        '''Write a macro paragraph for each sample described in the
        spreadsheet.  A paragraph consists of lines to change the
        temperature, lines to move sample and detector to the correct
        positions (if needed), a line to change the edge energy (if
        needed), a line to measure the XAFS using the correct set of
        control parameters, and a line to close plot windows after the
        scan.

        '''
        element, edge, focus = (None, None, None)
        settle_time, ramp_time = 0,0
        previous = 25
        
        self.content += self.tab + 'yield from mv(linkam.setpoint, linkam.readback.get())\n\n'
        self.content += self.tab + 'yield from linkam.on_plan()\n'
        self.content += self.tab + 'yield from sleep(15)\n'

        for m in self.measurements:

            if m['default'] is True:
                element     = m['element']
                edge        = m['edge']
                temperature = m['temperature']
                continue
            if m['temperature'] is None:
                continue
            if self.skip_row(m) is True:
                continue

            #######################################
            # default element/edge(/focus) values #
            #######################################
            for k in ('element', 'edge', 'focus'):
                if m[k] is None:
                    m[k] = self.measurements[0][k]
            if m['settle'] == 0:
                m['settle'] = self.measurements[0]['settle']

            ################################
            # change temperature is needed #
            ################################
            if m['temperature'] != temperature:
                self.content += self.tab + f"report('== Moving to temperature {m['temperature']:.1f}C', slack=True)\n"
                self.content += self.tab + f'linkam.settle_time = {m["settle"]:.1f}\n'
                self.content += self.tab + f'yield from mv(linkam, {m["temperature"]:.1f})\n'
                temperature = m['temperature']
                settle_time += m["settle"]
                ramp_time += (temperature - previous) / user_ns['linkam'].RR.get()
                previous = temperature

            ############################
            # sample and slit movement #
            ############################           
            if m['samplex'] is not None:
                self.content += self.tab + f'yield from mv(xafs_x, {m["samplex"]:.3f})\n'
            if m['sampley'] is not None:
                self.content += self.tab + f'yield from mv(xafs_y, {m["sampley"]:.3f})\n'
            if m['detectorx'] is not None:
                self.content += self.tab + f'yield from mv(xafs_det, {m["detectorx"]:.2f})\n'

            
            ##########################
            # change edge, if needed #
            ##########################
            focus = False
            if m['focus'] == 'focused':
                focus = True
            if self.do_first_change is True:
                self.content += self.tab + 'yield from change_edge(\'%s\', edge=\'%s\', focus=%r)\n' % (m['element'], m['edge'], focus)
                self.do_first_change = False
                self.totaltime += 4
                
            elif m['element'] != element or m['edge'] != edge: # focus...
                element = m['element']
                edge    = m['edge']
                self.content += self.tab + 'yield from change_edge(\'%s\', edge=\'%s\', focus=%r)\n' % (m['element'], m['edge'], focus)
                self.totaltime += 4
                
            else:
                if self.verbose:
                    self.content += self.tab + '## staying at %s %s\n' % (m['element'], m['edge'])
                pass

            ######################################
            # measure XAFS, then close all plots #
            ######################################
            command = self.tab + 'yield from xafs(\'%s.ini\'' % self.basename
            for k in m.keys():
                ## skip cells with macro-building parameters that are not INI parameters
                if self.skip_keyword(k):
                    continue
                ## skip element & edge if they are same as default
                elif k in ('element', 'edge'):
                    if m[k] == self.measurements[0][k]:
                        continue
                ## skip cells with only whitespace
                if type(m[k]) is str and len(m[k].strip()) == 0:
                    m[k] = None
                ## if a cell has data, put it in the argument list for xafs()
                if m[k] is not None:
                    if k == 'filename':
                        fname = self.make_filename(m)
                        command += f', filename=\'{fname}\''
                    elif type(m[k]) is int:
                        command += ', %s=%d' % (k, m[k])
                    elif type(m[k]) is float:
                        command += ', %s=%.3f' % (k, m[k])
                    else:
                        command += ', %s=\'%s\'' % (k, m[k])
            command += ')\n'
            self.content += command
            self.content += self.tab + 'close_last_plot()\n\n'
            #self.content += self.tab + 'yield from linkam.off_plan()\n\n'

            ########################################
            # approximate time cost of this sample #
            ########################################
            self.estimate_time(m, element, edge)
            

        print(whisper(f'XAS time:      about {self.totaltime/60:.1f} hours'))
        print(whisper(f'ramp time:     about {ramp_time:.1f} minutes'))
        print(whisper(f'settling time: about {settle_time/60:.1f} minutes'))
        self.totaltime = self.totaltime + (settle_time / 60) + ramp_time
        if self.close_shutters:
            self.content += self.tab + 'if not dryrun:\n'
            self.content += self.tab + '    BMMuser.running_macro = False\n'
            self.content += self.tab + '    BMM_clear_suspenders()\n'
            self.content += self.tab + '    yield from shb.close_plan()\n'
예제 #27
0
 def bailout():
     print(whisper('doing nothing'))
     yield from null()
예제 #28
0
    def main_plan(detector, axis, start, stop, nsteps, pluck, force):
        (ok, text) = BMM_clear_to_start()
        if force is False and ok is False:
            print(error_msg(text))
            yield from null()
            return

        detector, axis = ls_backwards_compatibility(detector, axis)
        # print('detector is: ' + str(detector))
        # print('axis is: ' + str(axis))
        # return(yield from null())

        RE.msg_hook = None
        ## sanitize input and set thismotor to an actual motor
        if type(axis) is str: axis = axis.lower()
        detector = detector.capitalize()

        ## sanity checks on axis
        if axis not in motor_nicknames.keys() and 'EpicsMotor' not in str(type(axis)) \
           and 'PseudoSingle' not in str(type(axis)) and 'WheelMotor' not in str(type(axis)):
            print(
                error_msg('\n*** %s is not a linescan motor (%s)\n' %
                          (axis, str.join(', ', motor_nicknames.keys()))))
            yield from null()
            return

        if 'EpicsMotor' in str(type(axis)):
            thismotor = axis
        elif 'PseudoSingle' in str(type(axis)):
            thismotor = axis
        elif 'WheelMotor' in str(type(axis)):
            thismotor = axis
        else:  # presume it's an xafs_XXXX motor
            thismotor = motor_nicknames[axis]

        current = thismotor.position
        if current + start < thismotor.limits[0]:
            print(
                error_msg(
                    f'These scan parameters will take {thismotor.name} outside it\'s lower limit of {thismotor.limits[0]}'
                ))
            print(whisper(f'(starting position = {thismotor.position})'))
            return (yield from null())
        if current + stop > thismotor.limits[1]:
            print(
                error_msg(
                    f'These scan parameters will take {thismotor.name} outside it\'s upper limit of {thismotor.limits[1]}'
                ))
            print(whisper(f'(starting position = {thismotor.position})'))
            return (yield from null())

        BMMuser.motor = thismotor

        ## sanity checks on detector
        if detector not in ('It', 'If', 'I0', 'Iy', 'Ir', 'Both', 'Bicron',
                            'Ia', 'Ib', 'Dualio', 'Xs', 'Xs1'):
            print(
                error_msg(
                    '\n*** %s is not a linescan measurement (%s)\n' %
                    (detector,
                     'it, if, i0, iy, ir, both, bicron, dualio, xs, xs1')))
            yield from null()
            return

        yield from abs_set(user_ns['_locked_dwell_time'], inttime, wait=True)
        if detector == 'Xs':
            yield from mv(xs.settings.acquire_time, inttime)
            yield from mv(xs.total_points, nsteps)
        dets = [
            user_ns['quadem1'],
        ]
        if user_ns['with_dualem']:
            dualio = user_ns['dualio']
        denominator = ''
        detname = ''

        ## func is an anonymous function, built on the fly, for feeding to DerivedPlot
        if detector == 'It':
            denominator = ' / I0'
            detname = 'transmission'
            func = lambda doc: (doc['data'][thismotor.name], doc['data']['It']
                                / doc['data']['I0'])
        elif detector == 'Ia' and dualio is not None:
            dets.append(dualio)
            detname = 'Ia'
            func = lambda doc: (doc['data'][thismotor.name], doc['data']['Ia'])
        elif detector == 'Ib' and dualio is not None:
            dets.append(dualio)
            detname = 'Ib'
            func = lambda doc: (doc['data'][thismotor.name], doc['data']['Ib'])
        elif detector == 'Ir':
            #denominator = ' / It'
            detname = 'reference'
            #func = lambda doc: (doc['data'][thismotor.name], doc['data']['Ir']/doc['data']['It'])
            func = lambda doc: (doc['data'][thismotor.name], doc['data']['Ir'])
        elif detector == 'I0':
            detname = 'I0'
            func = lambda doc: (doc['data'][thismotor.name], doc['data']['I0'])
        elif detector == 'Bicron':
            dets.append(user_ns['vor'])
            detname = 'Bicron'
            func = lambda doc: (doc['data'][thismotor.name], doc['data'][
                'Bicron'])
        elif detector == 'Iy':
            denominator = ' / I0'
            detname = 'electron yield'
            func = lambda doc: (doc['data'][thismotor.name], doc['data']['Iy']
                                / doc['data']['I0'])
        elif detector == 'If':
            dets.append(user_ns['vor'])
            denominator = ' / I0'
            detname = 'fluorescence'
            func = lambda doc: (doc['data'][thismotor.name], (doc['data'][
                BMMuser.dtc1] + doc['data'][BMMuser.dtc2] + doc['data'][
                    BMMuser.dtc3] + doc['data'][BMMuser.dtc4]) / doc['data'][
                        'I0'])
        elif detector == 'Xs':
            dets.append(user_ns['xs'])
            denominator = ' / I0'
            detname = 'fluorescence'
            func = lambda doc: (doc['data'][thismotor.name],
                                (doc['data'][BMMuser.xs1] + doc['data'][
                                    BMMuser.xs2] + doc['data'][BMMuser.xs3] +
                                 doc['data'][BMMuser.xs4]) / doc['data']['I0'])
            yield from mv(xs.total_points,
                          nsteps)  # Xspress3 demands that this be set up front

        elif detector == 'Xs1':
            dets.append(user_ns['xs'])
            denominator = ' / I0'
            detname = 'fluorescence'
            func = lambda doc: (doc['data'][thismotor.name], doc['data'][
                BMMuser.xs8] / doc['data']['I0'])
            yield from mv(xs1.total_points,
                          nsteps)  # Xspress3 demands that this be set up front

        elif detector == 'Dualio':
            dets.append(dualio)
            funcia = lambda doc: (doc['data'][thismotor.name], doc['data']['Ia'
                                                                           ])
            funcib = lambda doc: (doc['data'][thismotor.name], doc['data']['Ib'
                                                                           ])

        ## need a "Both" for trans + xs !!!!!!!!!!
        elif detector == 'Both':
            dets.append(user_ns['vor'])
            functr = lambda doc: (doc['data'][thismotor.name], doc['data'][
                'It'] / doc['data']['I0'])
            funcfl = lambda doc: (doc['data'][thismotor.name], (doc['data'][
                BMMuser.dtc1] + doc['data'][BMMuser.dtc2] + doc['data'][
                    BMMuser.dtc3] + doc['data'][BMMuser.dtc4]) / doc['data'][
                        'I0'])
        ## and this is the appropriate way to plot this linescan

        #abs_set(_locked_dwell_time, 0.5)
        if detector == 'Both':
            plot = [
                DerivedPlot(funcfl,
                            xlabel=thismotor.name,
                            ylabel='If/I0',
                            title='fluorescence vs. %s' % thismotor.name),
                DerivedPlot(functr,
                            xlabel=thismotor.name,
                            ylabel='It/I0',
                            title='transmission vs. %s' % thismotor.name)
            ]
        elif detector == 'Dualio':
            plot = [
                DerivedPlot(funcia,
                            xlabel=thismotor.name,
                            ylabel='Ia/I0',
                            title='Ia vs. %s' % thismotor.name),
                DerivedPlot(funcib,
                            xlabel=thismotor.name,
                            ylabel='Ib/I0',
                            title='Ib vs. %s' % thismotor.name)
            ]
        else:
            plot = DerivedPlot(func,
                               xlabel=thismotor.name,
                               ylabel=detector + denominator,
                               title='%s vs. %s' % (detname, thismotor.name))
        if 'PseudoSingle' in str(type(axis)):
            value = thismotor.readback.get()
        else:
            value = thismotor.user_readback.get()
        line1 = '%s, %s, %.3f, %.3f, %d -- starting at %.3f\n' % \
                (thismotor.name, detector, start, stop, nsteps, value)
        ##BMM_suspenders()            # engage suspenders

        thismd = dict()
        thismd['XDI'] = dict()
        thismd['XDI']['Facility'] = dict()
        thismd['XDI']['Facility']['GUP'] = BMMuser.gup
        thismd['XDI']['Facility']['SAF'] = BMMuser.saf

        rkvs.set('BMM:scan:type', 'line')
        rkvs.set('BMM:scan:starttime',
                 str(datetime.datetime.timestamp(datetime.datetime.now())))
        rkvs.set('BMM:scan:estimated', 0)

        @subs_decorator(plot)
        #@subs_decorator(src.callback)
        def scan_xafs_motor(dets, motor, start, stop, nsteps):
            uid = yield from rel_scan(dets,
                                      motor,
                                      start,
                                      stop,
                                      nsteps,
                                      md={
                                          **thismd,
                                          **md
                                      })
            return uid

        uid = yield from scan_xafs_motor(dets, thismotor, start, stop, nsteps)
        #global mytable
        #run = src.retrieve()
        #mytable = run.primary.read().to_dataframe()
        BMM_log_info('linescan: %s\tuid = %s, scan_id = %d' %
                     (line1, uid, user_ns['db'][-1].start['scan_id']))
        if pluck is True:
            action = input('\n' + bold_msg(
                'Pluck motor position from the plot? [Y/n then Enter] '))
            if action.lower() == 'n' or action.lower() == 'q':
                return (yield from null())
            yield from move_after_scan(thismotor)
예제 #29
0
 def recenter(self):
     here = self.user_readback.get()
     center = round(here / 15) * 15
     yield from mv(self, center)
     print(whisper('recentered %s to %.1f' % (self.name, center)))
예제 #30
0
 def do_nothing(self):
     print(whisper('doing nothing'))
     yield from null()