Ejemplo n.º 1
0
 def reset(self):
     '''Return glancing angle stage to spinner 1'''
     yield from self.alloff_plan()
     yield from mv(self.garot, self.home)
     report(
         'Returned to spinner 1 at %d degrees and turned off all spinners' %
         self.home,
         level='bold')
Ejemplo n.º 2
0
 def close_plan(self):
     user_ns['RE'].msg_hook = None
     count = 0
     while self.state.get() != self.closeval:
         count += 1
         print(u'\u231b', end=' ', flush=True)
         yield from mv(self.cls, 1)
         if count >= self.maxcount:
             print('tried %d times and failed to close %s %s' % (count, self.name, ':('))
             return(yield from null())
         time.sleep(1.5)
     report('Closed {}'.format(self.name))
     user_ns['RE'].msg_hook = BMM_msg_hook
Ejemplo n.º 3
0
 def open_plan(self):
     user_ns['RE'].msg_hook = None
     count = 0
     while self.state.get() != self.openval:
         count += 1
         print(u'\u231b', end=' ', flush=True)
         yield from mv(self.opn, 1)
         if count >= self.maxcount:
             print('tried %d times and failed to open %s %s' % (count, self.name, ':('))  # u'\u2639'  unicode frown
             return(yield from null())
         time.sleep(1.5)
     report('Opened {}'.format(self.name))
     user_ns['RE'].msg_hook = BMM_msg_hook
def xrd_webcam(filename=None, **kwargs):
    XRDURL = 'http://xf06bm-cam6/axis-cgi/jpg/image.cgi'
    CAM_PROXIES = {
        "http": None,
        "https": None,
    }
    if filename is None:
        filename = os.environ['HOME'] + '/XRD_camera_' + now() + '.jpg'
    r = requests.get(XRDURL, proxies=CAM_PROXIES)
    Image.open(BytesIO(r.content)).save(filename, 'JPEG')
    if 'annotation' in kwargs:
        annotate_image(filename, kwargs['annotation'])
    report('XRD webcam image written to %s' % filename)
Ejemplo n.º 5
0
 def set_slot(self, n):
     '''Move to a numbered slot on a sample wheel.'''
     if type(n) is not int:
         print(
             error_msg(
                 'Slots numbers must be integers between 1 and 24 (argument was %s)'
                 % type(n)))
         return (yield from null())
     if n < 1 or n > 24:
         print(error_msg('Slots are numbered from 1 to 24'))
         return (yield from null())
     angle = self.angle_from_current(n)
     yield from mvr(self, angle)
     report('Arrived at sample wheel slot %d' % n, level='bold')
Ejemplo n.º 6
0
 def close(self):
     user_ns['RE'].msg_hook = None
     if self.state.get() != self.closeval:
         count = 0
         while self.state.get() != self.closeval:
             count += 1
             print(u'\u231b', end=' ', flush=True)
             self.cls.put(1)
             if count >= self.maxcount:
                 print('tried %d times and failed to close %s %s' % (count, self.name, ':('))
                 return
             time.sleep(1.5)
         report(' Closed {}'.format(self.name))
     else:
         print('{} is closed'.format(self.name))
     user_ns['RE'].msg_hook = BMM_msg_hook
Ejemplo n.º 7
0
 def select_plan(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 (yield from null())
         el = self.slots[el - 1]
     if el is None:
         self.myprint(error_msg('\nThat ROI is not configured\n'))
         return (yield from null())
     if Z_number(el) is None:
         self.myprint(error_msg('\n%s is not an element\n' % el))
         return (yield from null())
     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()))
         yield from null()
Ejemplo n.º 8
0
 def open(self):
     RE.msg_hook = None
     if self.state.get() != self.openval:
         count = 0
         while self.state.get() != self.openval:
             count += 1
             print(u'\u231b', end=' ', flush=True)
             self.opn.put(1)
             if count >= self.maxcount:
                 print('tried %d times and failed to open %s %s' %
                       (count, self.name, ':('))
                 return
             time.sleep(1.5)
         report(' Opened {}'.format(self.name))
     else:
         print('{} is open'.format(self.name))
     RE.msg_hook = BMM_msg_hook
Ejemplo n.º 9
0
    def end_experiment(self, force=False):
        '''
        Copy data from the experiment that just finished to the NAS, then
        unset the logger and the DATA variable at the end of an experiment.
        '''
        #global DATA

        if self.echem and not os.path.ismount('/mnt/nfs/ws3'):
            print(
                error_msg('''
**************************************************************************
   This is an electrochemistry experiment and /mnt/nfs/ws3 is not mounted
   Electrochemistry data is not being backed up!
**************************************************************************
            '''))

        if not force:
            if not self.user_is_defined:
                print(error_msg('There is not a current experiment!'))
                return (None)

            ######################################################################
            #copy the electrochemistry data, if this was that sort of experiment #
            ######################################################################
            if self.echem and os.path.ismount('/mnt/nfs/ws3'):
                try:
                    self.fetch_echem()
                except:
                    print(
                        error_msg(
                            'Unable to copy electrochemistry data from ws3'))
                    if not os.path.ismount('/mnt/nfs/ws3'):
                        print(
                            error_msg(
                                '\t/mnt/nfs/ws3 seems not to be mounted...'))

            #######################################################################################
            # create folder and sub-folders on NAS server for this user & experimental start date #
            #######################################################################################
            destination = os.path.join(user_ns['nas_mount_point'], 'xf06bm',
                                       'user', self.name, self.date)
            if not os.path.isdir(destination):
                os.makedirs(destination)
            for d in ('dossier', 'prj', 'snapshots'):
                if not os.path.isdir(os.path.join(destination, d)):
                    os.makedirs(os.path.join(destination, d))
            try:
                copy_tree(self.DATA, destination)
                report('NAS data store: "%s"' % destination, 'bold')
            except Exception as E:
                print(E)
                print(error_msg('Unable to write data to NAS server'))

        #####################################################################
        # remove the json serialization of the start_experiment() arguments #
        #####################################################################

        if os.path.isfile(os.path.join(os.environ['HOME'], 'Data',
                                       '.BMMuser')):
            os.remove(os.path.join(os.environ['HOME'], 'Data', '.BMMuser'))
        jsonfile = os.path.join(os.environ['HOME'], 'Data', '.user.json')
        if os.path.isfile(jsonfile):
            os.chmod(jsonfile, 0o644)
            os.remove(jsonfile)

        ###############################################################
        # unset self attributes, DATA, and experiment specific logger #
        ###############################################################
        BMM_unset_user_log()
        DATA = os.path.join(os.environ['HOME'], 'Data', 'bucket') + '/'
        self.DATA = os.path.join(os.environ['HOME'], 'Data', 'bucket') + '/'
        self.folder = self.DATA
        user_ns["wmb"].folder = self.folder
        self.date = ''
        self.gup = 0
        self.saf = 0
        self.name = None
        self.staff = False
        self.user_is_defined = False
        self.echem = False
        self.echem_remote = None

        return None
Ejemplo n.º 10
0
 def fetch_echem(self):
     dest = os.path.join(self.folder, 'electrochemistry')
     copy_tree(self.echem_remote, dest)
     report(
         'Copied electrochemistry data from: "%s" to "%s"' %
         (self.echem_remote, dest), 'bold')
Ejemplo n.º 11
0
def change_edge(el,
                focus=False,
                edge='K',
                energy=None,
                slits=True,
                target=300.,
                xrd=False,
                bender=True):
    '''Change edge energy by:
    1. Moving the DCM above the edge energy
    2. Moving the photon delivery system to the correct mode
    3. Running a rocking curve scan
    4. Running a slits_height scan

    Parameters
    ----------
    el : str
        one- or two-letter symbol
    focus : bool, optional
        T=focused or F=unfocused beam [False, unfocused]
    edge : str, optional
        edge symbol ['K']
    energy : float, optional
        e0 value [None, determined from el/edge]
    slits : bool, optional
        perform slit_height() scan [False]
    target : float, optional
        energy where rocking curve is measured [300]
    xrd : boolean, optional
        force photon delivery system to XRD [False]

    Examples
    --------
    Normal use, unfocused beam:
       
    >>> RE(change_edge('Fe'))

    Normal use, focused beam:
       
    >>> RE(change_edge('Fe', focus=True))

    L2 or L1 edge:
       
    >>> RE(change_edge('Re', edge='L2'))

    Measure rocking curve at edge energy:
      
    >>> RE(change_edge('Fe', target=0))

    XRD, new energy:
       
    >>> RE(change_edge('Fe', xrd=True, energy=8600))
        
    note that you must specify an element, but it doesn't matter which
    one the energy will be moved to the specified energy xrd=True
    implies focus=True and target=0

    '''
    BMMuser, RE, dcm, dm3_bct = user_ns['BMMuser'], user_ns['RE'], user_ns[
        'dcm'], user_ns['dm3_bct']
    dcm_pitch, dcm_bragg = user_ns['dcm_pitch'], user_ns['dcm_bragg']
    rkvs = user_ns['rkvs']
    try:
        xs = user_ns['xs']
    except:
        pass
    #BMMuser.prompt = True
    el = el.capitalize()

    ######################################################################
    # this is a tool for verifying a macro.  this replaces an xafsmod scan  #
    # with a sleep, allowing the user to easily map out motor motions in #
    # a macro                                                            #
    if BMMuser.macro_dryrun:
        print(
            info_msg(
                '\nBMMuser.macro_dryrun is True.  Sleeping for %.1f seconds rather than changing to the %s edge.\n'
                % (BMMuser.macro_sleep, el)))
        countdown(BMMuser.macro_sleep)
        return (yield from null())
    ######################################################################

    if pds_motors_ready() is False:
        print(
            error_msg(
                '\nOne or more motors are showing amplifier faults.\nToggle the correct kill switch, then re-enable the faulted motor.'
            ))
        return (yield from null())

    (ok, text) = BMM_clear_to_start()
    if ok is False:
        print(
            error_msg('\n' + text) +
            bold_msg('Quitting change_edge() macro....\n'))
        return (yield from null())

    if energy is None:
        energy = edge_energy(el, edge)

    if energy is None:
        print(
            error_msg('\nEither %s or %s is not a valid symbol\n' %
                      (el, edge)))
        return (yield from null())
    if energy > 23500:
        edge = 'L3'
        energy = edge_energy(el, 'L3')

    if energy < 4000:
        print(warning_msg('The %s edge energy is below 4950 eV' % el))
        print(warning_msg('You have to change energy by hand.'))
        return (yield from null())

    if energy > 23500:
        print(
            warning_msg(
                'The %s edge energy is outside the range of this beamline!' %
                el))
        return (yield from null())

    BMMuser.edge = edge
    BMMuser.element = el
    BMMuser.edge_energy = energy
    rkvs.set('BMM:pds:edge', edge)
    rkvs.set('BMM:pds:element', el)
    rkvs.set('BMM:pds:edge_energy', energy)

    if energy > 8000:
        mode = 'A' if focus else 'D'
    elif energy < 6000:
        #mode = 'B' if focus else 'F'   ## mode B currently is inaccessible :(
        mode = 'C' if focus else 'F'
    else:
        mode = 'C' if focus else 'E'
    if xrd:
        mode = 'XRD'
        focus = True
        target = 0.0
    current_mode = get_mode()
    if mode in ('D', 'E', 'F') and current_mode in ('D', 'E', 'F'):
        with_m2 = False
    elif mode in ('A', 'B',
                  'C') and current_mode in ('A', 'B',
                                            'C'):  # no need to move M2
        with_m2 = False
    else:
        with_m2 = True
    if all_connected(with_m2) is False:
        print(warning_msg('Ophyd connection failure' % el))
        return (yield from null())

    ################################
    # confirm configuration change #
    ################################
    print(bold_msg('\nEnergy change:'))
    print('   %s: %s %s' %
          (list_msg('edge'), el.capitalize(), edge.capitalize()))
    print('   %s: %.1f' % (list_msg('edge energy'), energy))
    print('   %s: %.1f' % (list_msg('target energy'), energy + target))
    print('   %s: %s' % (list_msg('focus'), str(focus)))
    print('   %s: %s' % (list_msg('photon delivery mode'), mode))
    print('   %s: %s' % (list_msg('optimizing slits height'), str(slits)))
    if BMMuser.prompt:
        action = input("\nBegin energy change? [Y/n then Enter] ")
        if action.lower() == 'q' or action.lower() == 'n':
            return (yield from null())
        if mode == 'C' and energy < 6000:
            print(
                warning_msg(
                    '\nMoving to mode C for focused beam and an edge energy below 6 keV.'
                ))
            action = input(
                "You will not get optimal harmonic rejection.  Continue anyway?  [Y/n then Enter] "
            )
            if action.lower() == 'q' or action.lower() == 'n':
                return (yield from null())

    start = time.time()
    if mode == 'XRD':
        report('Configuring beamline for XRD', level='bold', slack=True)
    else:
        report(
            f'Configuring beamline for {el.capitalize()} {edge.capitalize()} edge',
            level='bold',
            slack=True)
    yield from dcm.kill_plan()

    ################################################
    # change to the correct photon delivery mode   #
    #      + move mono to correct energy           #
    #      + move reference holder to correct slot #
    ################################################
    # if not calibrating and mode != current_mode:
    #     print('Moving to photon delivery mode %s...' % mode)
    yield from mv(dcm_bragg.acceleration, BMMuser.acc_slow)
    yield from change_mode(mode=mode,
                           prompt=False,
                           edge=energy + target,
                           reference=el,
                           bender=bender)
    yield from mv(dcm_bragg.acceleration, BMMuser.acc_fast)
    if arrived_in_mode(mode=mode) is False:
        print(error_msg(f'\nFailed to arrive in Mode {mode}'))
        print(
            'Fixing this is often as simple as re-running the change_mode() command.'
        )
        print('Or try dm3_bct.kill() the re-run the change_mode() command.')
        print('If that doesn\'t work, call for help')
        return (yield from null())

    yield from user_ns['kill_mirror_jacks']()
    yield from sleep(1)
    if BMMuser.motor_fault is not None:
        print(
            error_msg('\nSome motors are reporting amplifier faults: %s' %
                      BMMuser.motor_fault))
        print(
            'Clear the faults and try running the same change_edge() command again.'
        )
        print('Troubleshooting: ' + url_msg(
            'https://nsls-ii-bmm.github.io/BeamlineManual/trouble.html#amplifier-fault'
        ))
        BMMuser.motor_fault = None
        return (yield from null())
    BMMuser.motor_fault = None

    ############################
    # run a rocking curve scan #
    ############################
    print('Optimizing rocking curve...')
    yield from abs_set(dcm_pitch.kill_cmd, 1, wait=True)
    yield from mv(dcm_pitch, approximate_pitch(energy + target))
    yield from sleep(1)
    yield from abs_set(dcm_pitch.kill_cmd, 1, wait=True)
    yield from rocking_curve()
    close_last_plot()

    ##########################
    # run a slit height scan #
    ##########################
    if slits:
        print('Optimizing slits height...')
        yield from slit_height(move=True)
        close_last_plot()
        ## redo rocking curve?

    ##################################
    # set reference and roi channels #
    ##################################
    if not xrd:
        ## reference channel
        rois = user_ns['rois']
        print('Moving reference foil...')
        yield from rois.select_plan(el)
        ## Xspress3
        BMMuser.verify_roi(xs, el, edge)
        ## feedback
        show_edges()

    if mode == 'XRD':
        report('Finished configuring for XRD', level='bold', slack=True)
    else:
        report(
            f'Finished configuring for {el.capitalize()} {edge.capitalize()} edge',
            level='bold',
            slack=True)
    if slits is False:
        print(
            '  * You may need to verify the slit position:  RE(slit_height())')
    self.to_json(os.path.join(self.DATA, '.BMMuser'))
    yield from dcm.kill_plan()
    end = time.time()
    print('\n\nThat took %.1f min' % ((end - start) / 60))
    return ()
Ejemplo n.º 12
0
 def main_plan():
     dcm = user_ns['dcm']
     report(f'Calibrating the {dcm._crystal} monochrmoator', 'bold')
     yield from calibrate_low_end(mono=dcm._crystal, focus=focus)
     yield from calibrate_high_end(mono=dcm._crystal, focus=focus)
     yield from resting_state_plan()
Ejemplo n.º 13
0
def analog_camera(filename=None,
                  sample=None,
                  folder=os.environ['HOME'],
                  device='/dev/video0',
                  camera=0,
                  skip=30,
                  frames=5,
                  brightness=30,
                  x=320,
                  y=240,
                  linecolor='white',
                  nocrosshair=True,
                  quiet=False,
                  reset=False,
                  usbid='534d:0021',
                  title='NIST BMM (NSLS-II 06BM)',
                  timestamp='%Y-%m-%d %H:%M:%S'):
    """A function for interacting with fswebcam in a way that meets the
    needs of 06BM.

    Parameters
    ----------
    folder : str
        location to drop jpg image [$HOME]
    device : str
        char device of camera [/dev/video0] should be set to something in /dev/v4l/by-id/
    camera : int
        camera number[0]
    skip : int
        number of frames to skip waiting for camera to wake up  [30]
    frames : int
        number of frames to accumulate in image [5]
    brightness : int
        brightness setting of camera as a percentage [30]
    x : int
        middle of image, in pixels,  X-location of cross hair [320]
    y : int
        middle of image, in pixels,  Y-location of cross hair [240]
    linecolor : str
        color of cross hair lines [white]
    nocrosshair : bool
        flag to suppress cross hair [True]
    quiet : bool
        flag to suppress screen messages [False]
    usbid : str
        vendor and product ID of camera of AV to USB device at 06BM [534d:0021]
    title : str
        title string for fswebcam banner [NIST BMM (NSLS-II 06BM)]
    filename : str
        output file name                   

    """

    USBDEVFS_RESET = 21780

    if reset is True:
        if not quiet: print("resetting video device")
        try:
            lsusb_out = Popen("lsusb | grep -i %s" % usbid,
                              shell=True,
                              bufsize=64,
                              stdin=PIPE,
                              stdout=PIPE,
                              close_fds=True).stdout.read().strip().split()
            bus = lsusb_out[1].decode('UTF-8')
            device = lsusb_out[3][:-1].decode('UTF-8')
            print("/dev/bus/usb/%s/%s" % (bus, device))
            f = open("/dev/bus/usb/%s/%s" % (bus, device), 'w', os.O_WRONLY)
            fcntl.ioctl(f, USBDEVFS_RESET, 0)
            sleep(1)
        except Exception as msg:
            print("failed to reset device:", msg)

    quiet = ''
    if quiet: quiet = '-q '
    if filename is None:
        filename = folder + '/analog_camera_' + now() + '.jpg'

    if sample is not None and sample != '':
        title = title + ' - ' + sample
    if user_ns['BMMuser'].host == 'xf06bm-ws1':
        command = [
            'fswebcam', quiet, '-i', f'{camera}', '-d', device, '-r',
            f'{x}x{y}', '--title', title, '--timestamp', timestamp, '-S',
            f'{skip}', '-F', f'{frames}', '--set', f'brightness={brightness}%',
            filename
        ]
    else:
        command = [
            'ssh', 'xf06bm@xf06bm-ws1',
            f"fswebcam {quiet}-i {camera} -d {device} -r {x}x{y} --title '{title}' --timestamp '{timestamp}' -S {skip} -F {frames} --set brightness={brightness}% '{filename}'"
        ]
    run(command)

    #command = f"fswebcam {quiet}-i {camera} -d {device} -r {x}x{y} --title '{title}' --timestamp '{timestamp}' -S {skip} -F {frames} --set brightness={brightness}% '{filename}'"
    #system(command)
    #command = f"fswebcam {quiet}-i {camera} -d {device} -r {x}x{y} --title '{title}' --timestamp '{timestamp}' -S {skip} -F {frames} --set brightness={brightness}% '{filename}'"
    #system(f'ssh xf06bm@xf06bm-ws1 "{command}"')

    report('Analog camera image written to %s' % filename)
Ejemplo n.º 14
0
 def on(self):
     report('Turning {} on'.format(self.name))
     self.state.put(1)
Ejemplo n.º 15
0
 def off(self):
     report('Turning {} off'.format(self.name))
     self.state.put(0)
Ejemplo n.º 16
0
 def reset(self):
     '''Return a sample wheel to slot 1'''
     yield from mv(self, self.slotone)
     report('Returned to sample wheel slot 1 at %d degrees' % self.slotone,
            level='bold')
Ejemplo n.º 17
0
def analog_camera(filename=None,
                  sample=None,
                  folder=os.environ['HOME'],
                  device='/dev/video0',
                  camera=0,
                  skip=30,
                  frames=5,
                  brightness=20,
                  x=320,
                  y=240,
                  linecolor='white',
                  nocrosshair=True,
                  quiet=False,
                  reset=False,
                  usbid='534d:0021',
                  title='NIST BMM (NSLS-II 06BM)',
                  timestamp='%Y-%m-%d %H:%M:%S'):
    """A function for interacting with fswebcam in a way that meets the
    needs of 06BM.

    Parameters:
        folder:      location to drop jpg image         [$HOME]
        device:      char device of camera              [/dev/video0]
        camera:      camera number                      [0]
        skip:        number of frames to skip waiting for camera to wake up [30]
        frames:      number of frames to accumulate in image [5]
        brightness:  brightness setting of camera as a percentage [20]
        x:           X-location of cross hair           [320] (middle of image)
        y:           Y-location of cross hair           [240] (middle of image)
        linecolor:   color of cross hair lines          [white]
        nocrosshair: flag to suppress cross hair        [True]
        quiet:       flag to suppress screen messages   [False]
        usbid:       vendor and product ID of camera    [534d:0021] (AV to USB device at 06BM)
        title:       title string for fswebcam banner   [NIST BMM (NSLS-II 06BM)]
        filename:    output file name                   [ISO 8601 timestamp in folder]

    """

    USBDEVFS_RESET = 21780

    if reset is True:
        if not quiet: print("resetting video device")
        try:
            lsusb_out = Popen("lsusb | grep -i %s" % usbid,
                              shell=True,
                              bufsize=64,
                              stdin=PIPE,
                              stdout=PIPE,
                              close_fds=True).stdout.read().strip().split()
            bus = lsusb_out[1].decode('UTF-8')
            device = lsusb_out[3][:-1].decode('UTF-8')
            print("/dev/bus/usb/%s/%s" % (bus, device))
            f = open("/dev/bus/usb/%s/%s" % (bus, device), 'w', os.O_WRONLY)
            fcntl.ioctl(f, USBDEVFS_RESET, 0)
            sleep(1)
        except Exception as msg:
            print("failed to reset device:", msg)

    quiet = ''
    if quiet: quiet = '-q '
    if filename is None:
        filename = folder + '/analog_camera_' + now() + '.jpg'

    if sample is not None:
        title = title + ' - ' + sample
    command = "fswebcam %s-i %s -d %s -r 640x480 --title \"%s\" --timestamp \"%s\" -S %d -F %d --set brightness=%s%% \"%s\"" %\
              (quiet, camera, device, title, timestamp, skip, frames, brightness, filename)
    system(command)

    report('Analog camera image written to %s' % filename)
Ejemplo n.º 18
0
    def main_plan(detector, slow, startslow, stopslow, nslow, fast, startfast,
                  stopfast, nfast, pluck, force, dwell, md):
        (ok, text) = BMM_clear_to_start()
        if force is False and ok is False:
            print(error_msg(text))
            BMMuser.final_log_entry = False
            yield from null()
            return

        user_ns['RE'].msg_hook = None

        ## sanity checks on slow axis
        if type(slow) is str: slow = slow.lower()
        if slow not in motor_nicknames.keys() and 'EpicsMotor' not in str(
                type(slow)) and 'PseudoSingle' not in str(type(slow)):
            print(
                error_msg('\n*** %s is not an areascan motor (%s)\n' %
                          (slow, str.join(', ', motor_nicknames.keys()))))
            BMMuser.final_log_entry = False
            yield from null()
            return
        if slow in motor_nicknames.keys():
            slow = motor_nicknames[slow]

        ## sanity checks on fast axis
        if type(fast) is str: fast = fast.lower()
        if fast not in motor_nicknames.keys() and 'EpicsMotor' not in str(
                type(fast)) and 'PseudoSingle' not in str(type(fast)):
            print(
                error_msg('\n*** %s is not an areascan motor (%s)\n' %
                          (fast, str.join(', ', motor_nicknames.keys()))))
            BMMuser.final_log_entry = False
            yield from null()
            return
        if fast in motor_nicknames.keys():
            fast = motor_nicknames[fast]

        detector = detector.capitalize()
        yield from mv(_locked_dwell_time, dwell)
        dets = [
            quadem1,
        ]

        if with_xspress3 and detector == 'If':
            detector = 'Xs'

        if detector == 'If':
            dets.append(vor)
            detector = 'ROI1'
        if detector.lower() == 'xs':
            dets.append(xs)
            detector = BMMuser.xs1
            yield from mv(xs.total_points, nslow * nfast)

        if 'PseudoSingle' in str(type(slow)):
            valueslow = slow.readback.get()
        else:
            valueslow = slow.user_readback.get()
        line1 = 'slow motor: %s, %.3f, %.3f, %d -- starting at %.3f\n' % \
            (slow.name, startslow, stopslow, nslow, valueslow)

        if 'PseudoSingle' in str(type(fast)):
            valuefast = fast.readback.get()
        else:
            valuefast = fast.user_readback.get()
        line2 = 'fast motor: %s, %.3f, %.3f, %d -- starting at %.3f\n' % \
            (fast.name, startfast, stopfast, nfast, valuefast)

        npoints = nfast * nslow
        estimate = int(npoints * (dwell + 0.7))

        # extent = (
        #     valuefast + startfast,
        #     valueslow + startslow,
        #     valuefast + stopfast,
        #     valueslow + stopslow,
        # )
        # extent = (
        #     0,
        #     nfast-1,
        #     0,
        #     nslow-1
        # )
        # print(extent)
        # return(yield from null())

        # areaplot = LiveScatter(fast.name, slow.name, detector,
        #                        xlim=(startfast, stopfast), ylim=(startslow, stopslow))

        close_all_plots()

        areaplot = LiveGrid(
            (nslow, nfast),
            detector,  #aspect='equal', #aspect=float(nslow/nfast), extent=extent,
            xlabel='fast motor: %s' % fast.name,
            ylabel='slow motor: %s' % slow.name)
        #BMMuser.ax     = areaplot.ax
        #BMMuser.fig    = areaplot.ax.figure
        BMMuser.motor = fast
        BMMuser.motor2 = slow
        #BMMuser.fig.canvas.mpl_connect('close_event', handle_close)

        thismd = dict()
        thismd['XDI'] = dict()
        thismd['XDI']['Facility'] = dict()
        thismd['XDI']['Facility']['GUP'] = BMMuser.gup
        thismd['XDI']['Facility']['SAF'] = BMMuser.saf
        thismd['slow_motor'] = slow.name
        thismd['fast_motor'] = fast.name

        report(
            f'Starting areascan at x,y = {fast.position:.3f}, {slow.position:.3f}',
            level='bold',
            slack=True)

        ## engage suspenders right before starting scan sequence
        if force is False: BMM_suspenders()

        @subs_decorator(areaplot)
        #@subs_decorator(src.callback)
        def make_areascan(dets,
                          slow,
                          startslow,
                          stopslow,
                          nslow,
                          fast,
                          startfast,
                          stopfast,
                          nfast,
                          snake=False):
            BMMuser.final_log_entry = False
            uid = yield from grid_scan(
                dets,
                slow,
                startslow,
                stopslow,
                nslow,
                fast,
                startfast,
                stopfast,
                nfast,
                snake,
                md={
                    'plan_name':
                    f'grid_scan measurement {slow.name} {fast.name} {detector}'
                })
            BMMuser.final_log_entry = True
            return uid

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

        BMM_log_info('begin areascan observing: %s\n%s%s' %
                     (detector, line1, line2))
        uid = yield from make_areascan(dets,
                                       slow,
                                       valueslow + startslow,
                                       valueslow + stopslow,
                                       nslow,
                                       fast,
                                       valuefast + startfast,
                                       valuefast + stopfast,
                                       nfast,
                                       snake=False)

        if pluck is True:

            close_all_plots()
            thismap = user_ns['db'].v2[uid]
            x = numpy.array(thismap.primary.read()[fast.name])
            y = numpy.array(thismap.primary.read()[slow.name])
            z=numpy.array(thismap.primary.read()[BMMuser.xs1]) +\
                numpy.array(thismap.primary.read()[BMMuser.xs2]) +\
                numpy.array(thismap.primary.read()[BMMuser.xs3]) +\
                numpy.array(thismap.primary.read()[BMMuser.xs4])
            z = z.reshape(nfast, nslow)

            # grabbing the first nfast elements of x and every
            # nslow-th element of y is more reliable than
            # numpy.unique due to float &/or motor precision issues

            #plt.title(f'Energy = {energies["below"]}')
            plt.xlabel(f'fast axis ({fast.name}) position (mm)')
            plt.ylabel(f'slow axis ({slow.name}) position (mm)')
            plt.gca().invert_yaxis()  # plot an xafs_x/xafs_y plot upright
            plt.contourf(x[:nfast], y[::nslow], z, cmap=plt.cm.viridis)
            plt.colorbar()
            plt.show()
            fname = os.path.join(BMMuser.folder, 'map-' + now() + '.png')
            plt.savefig(fname)
            try:
                img_to_slack(fname)
            except:
                post_to_slack('failed to post image: {fname}')
                pass

            BMMuser.x = None
            figs = list(map(plt.figure, plt.get_fignums()))
            canvas = figs[0].canvas
            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())
            print(
                'Single click the left mouse button on the plot to pluck a point...'
            )
            cid = canvas.mpl_connect(
                'button_press_event',
                interpret_click)  # see 65-derivedplot.py and
            while BMMuser.x is None:  #  https://matplotlib.org/users/event_handling.html
                yield from sleep(0.5)

            # print('Converting plot coordinates to real coordinates...')
            # begin = valuefast + startfast
            # stepsize = (stopfast - startfast) / (nfast - 1)
            # pointfast = begin + stepsize * BMMuser.x
            # #print(BMMuser.x, pointfast)

            # begin = valueslow + startslow
            # stepsize = (stopslow - startslow) / (nslow - 1)
            # pointslow = begin + stepsize * BMMuser.y
            # #print(BMMuser.y, pointslow)

            # print('That translates to x=%.3f, y=%.3f' % (pointfast, pointslow))
            yield from mv(fast, BMMuser.x, slow, BMMuser.y)
            report(
                f'Moved to position x,y = {fast.position:.3f}, {slow.position:.3f}',
                level='bold',
                slack=True)
Ejemplo n.º 19
0
 def off_plan(self):
     report('Turning {} off'.format(self.name))
     yield from abs_set(self.state, 0, wait=True)
Ejemplo n.º 20
0
    def auto_align(self, pitch=2, drop=None):
        '''Align a sample on a spinner automatically.  This performs 5 scans.
        The first four iterate twice between linear and pitch
        against the signal in It.  This find the flat position.

        Then the sample is pitched to the requested angle and a fifth
        scan is done to optimize the linear motor position against the
        fluorescence signal.

        The linear scans against It look like a step-down function.
        The center of this step is found as the centroid of a fitted
        error function.

        The xafs_pitch scan should be peaked.  Move to the max of the
        signal.

        The linear scan against fluorescence ideally looks like a
        flat-topped peak.  Move to the center of mass.

        At the end, a three-panel figure is drawn showing the last
        three scans.  This is posted to Slack.  It also finds its way
        into the dossier as a record of the quality of the alignment.

        Arguments
        =========
        pitch : int
          The angle at which to make the glancing angle measurements.
        drop : int or None
          If not None, then this many points will be dropped from the
          end of linear scan against transmission when fitting the error
          function. This is an attempt to deal gracefully with leakage 
          through the adhesive at very high energy.

        '''
        BMMuser = user_ns['BMMuser']
        if BMMuser.macro_dryrun:
            report(f'Auto-aligning glancing angle stage, spinner {self.current()}', level='bold', slack=False)
            print(info_msg(f'\nBMMuser.macro_dryrun is True.  Sleeping for %.1f seconds at spinner %d.\n' %
                           (BMMuser.macro_sleep, self.current())))
            countdown(BMMuser.macro_sleep)
            return(yield from null())

        report(f'Auto-aligning glancing angle stage, spinner {self.current()}', level='bold', slack=True)
            
        BMM_suspenders()

        ## first pass in transmission
        yield from self.align_linear(drop=drop)
        yield from self.align_pitch()

        ## for realsies X or Y in transmission
        yield from self.align_linear(drop=drop)
        self.y_uid = user_ns['db'].v2[-1].metadata['start']['uid'] 

        ## for realsies Y in pitch
        yield from self.align_pitch()
        self.pitch_uid = user_ns['db'].v2[-1].metadata['start']['uid'] 

        ## record the flat position
        if self.orientation == 'parallel':
            motor = user_ns['xafs_y']
        else:
            motor = user_ns['xafs_x']
        self.flat = [motor.position, user_ns['xafs_pitch'].position]

        ## move to measurement angle and align
        yield from mvr(user_ns['xafs_pitch'], pitch)
        yield from linescan(motor, 'xs', -2.3, 2.3, 51, pluck=False)
        self.f_uid = user_ns['db'].v2[-1].metadata['start']['uid'] 
        tf = user_ns['db'][-1].table()
        yy = tf[motor.name]
        signal = (tf[BMMuser.xs1] + tf[BMMuser.xs2] + tf[BMMuser.xs3] + tf[BMMuser.xs4]) / tf['I0']
        if BMMuser.element == 'Zr':
            centroid = yy[signal.idxmax()]
        else:
            com = int(center_of_mass(signal)[0])+1
            centroid = yy[com]
        yield from mv(motor, centroid)
        
        ## make a pretty picture, post it to slack
        self.alignment_plot(self.y_uid, self.pitch_uid, self.f_uid)
        self.alignment_filename = os.path.join(BMMuser.folder, 'snapshots', f'spinner{self.current()}-alignment-{now()}.png')
        plt.savefig(self.alignment_filename)
        try:
            img_to_slack(self.alignment_filename)
        except:
            post_to_slack('failed to post image: {self.alignment_filename}')
            pass
        BMM_clear_suspenders()