Exemple #1
0
    def end_scan(self):
        """Performs the operations needed at the very end of a scan.

        This does the following:
        - Calls ``save_configuration()``.
        - Put the camera back in "FreeRun" mode and acquiring so the user sees live images.

        - Sets the speed of the rotation stage back to the maximum value.

        - Calls ``move_sample_in()``.

        - Calls the base class method.

        - Closes shutter.
        """

        if self.epics_pvs['ReturnRotation'].get(as_string=True) == 'Yes':
            while True:
                ang1 = self.epics_pvs['RotationRBV'].value
                time.sleep(1)
                ang2 = self.epics_pvs['RotationRBV'].value
                print(ang1, ang2)
                if np.abs(ang1 - ang2) < 1e-4:
                    break
            if np.abs(self.epics_pvs['RotationRBV'].value) > 720:
                log.warning('home stage')
                self.epics_pvs['RotationHomF'].put(1, wait=True)
        self.epics_pvs['LensSelect'].clear_callbacks()
        time.sleep(2)
        # Call the base class method
        super().end_scan()
        # Close shutter
        self.close_shutter()
    def open_frontend_shutter(self):
        """Opens the shutters to collect flat fields or projections.

        This does the following:

        - Checks if we are in testing mode. If we are, do nothing else opens the 2-BM-A front-end shutter.

        """
        if self.epics_pvs['Testing'].get():
            log.warning('In testing mode, so not opening shutters.')
        else:
            # Open 2-BM-A front-end shutter
            if not self.epics_pvs['OpenShutter'] is None:
                pv = self.epics_pvs['OpenShutter']
                value = self.epics_pvs['OpenShutterValue'].get(as_string=True)
                status = self.epics_pvs['ShutterStatus'].get(as_string=True)
                log.info('shutter status: %s', status)
                log.info('open shutter: %s, value: %s', pv, value)
                self.epics_pvs['OpenShutter'].put(value, wait=True)
                self.wait_frontend_shutter_open()
                # self.wait_pv(self.epics_pvs['ShutterStatus'], 1)
                status = self.epics_pvs['ShutterStatus'].get(as_string=True)
                log.info('shutter status: %s', status)

                #VN: fix later, fast shutter status check is needed
                time.sleep(2)
    def close_shutter(self):
        """Closes the shutter to collect dark fields and at the end of a scan
        This does the following:

        - Checks if we are in testing mode.  If we are, do nothing

        - Closes the 7-BM-B fast shutter.

        - Closes the beamline shutter.
       """
        if self.epics_pvs['Testing'].get():
            log.warning('In testing mode, so not closing shutters.')
            return
        # Close 7-BM-B fast shutter; don't wait for it
        if not self.epics_pvs['CloseFastShutter'] is None:
            pv = self.epics_pvs['CloseFastShutter']
            value = self.epics_pvs['CloseFastShutterValue'].get(as_string=True)
            log.info('close fast shutter: %s, value: %s', pv, value)
            self.epics_pvs['CloseFastShutter'].put(value, wait=False)
        # Call the base class method
        if not self.epics_pvs['CloseShutter'] is None:
            pv = self.epics_pvs['CloseShutter']
            value = self.epics_pvs['CloseShutterValue'].get(as_string=True)
            status = self.epics_pvs['ShutterStatus'].get(as_string=True)
            log.info('shutter status: %s', status)
            log.info('close shutter: %s, value: %s', pv, value)
            self.epics_pvs['CloseShutter'].put(value, wait=True)
            self.wait_pv(self.epics_pvs['ShutterStatus'], 1)
            status = self.epics_pvs['ShutterStatus'].get(as_string=True)
            log.info('shutter status: %s', status)
    def open_shutter(self):
        """Opens the shutter to collect flat fields or projections.

        This does the following:

        - Checks if we are in testing mode.  If we are, do nothing.

        - Opens the front end shutter, waiting for it to indicate it is open.
            This is copied from the 2-BM implementation 9/2020

        - Opens the 7-BM-B fast shutter.
        """
        if self.epics_pvs['Testing'].get():
            log.warning('In testing mode, so not opening shutters.')
            return
        # Open the front end shutter
        if not self.epics_pvs['OpenShutter'] is None:
            pv = self.epics_pvs['OpenShutter']
            value = self.epics_pvs['OpenShutterValue'].get(as_string=True)
            status = self.epics_pvs['ShutterStatus'].get(as_string=True)
            log.info('shutter status: %s', status)
            log.info('open shutter: %s, value: %s', pv, value)
            self.epics_pvs['OpenShutter'].put(value, wait=True)
            self.wait_pv(self.epics_pvs['ShutterStatus'], 0)
            status = self.epics_pvs['ShutterStatus'].get(as_string=True)
            log.info('shutter status: %s', status)
        # Open 7-BM-B fast shutter
        if not self.epics_pvs['OpenFastShutter'] is None:
            pv = self.epics_pvs['OpenFastShutter']
            value = self.epics_pvs['OpenFastShutterValue'].get(as_string=True)
            log.info('open fast shutter: %s, value: %s', pv, value)
            self.epics_pvs['OpenFastShutter'].put(value, wait=True)
Exemple #5
0
    def add_theta(self):
        """Add theta at the end of a scan.
        Taken from tomoscan_2BM.py function.  This gives the correct theta for scans with missing frames
        """
        log.info('add theta')

        if self.theta is None:
            log.warning('no theta to add')
            return

        full_file_name = self.epics_pvs['FPFullFileName'].get(as_string=True)
        if os.path.exists(full_file_name):
            try:
                with h5py.File(full_file_name, "a") as f:
                    unique_ids = f['/defaults/NDArrayUniqueId']
                    hdf_location = f['/defaults/HDF5FrameLocation']
                    total_dark_fields = self.num_dark_fields * (
                        (self.dark_field_mode in ('Start', 'Both')) +
                        (self.dark_field_mode in ('End', 'Both')))
                    total_flat_fields = self.num_flat_fields * (
                        (self.flat_field_mode in ('Start', 'Both')) +
                        (self.flat_field_mode in ('End', 'Both')))

                    proj_ids = unique_ids[hdf_location[:] == b'/exchange/data']
                    flat_ids = unique_ids[hdf_location[:] ==
                                          b'/exchange/data_white']
                    dark_ids = unique_ids[hdf_location[:] ==
                                          b'/exchange/data_dark']

                    # create theta dataset in hdf5 file
                    if len(proj_ids) > 0:
                        theta_ds = f.create_dataset('/exchange/theta',
                                                    (len(proj_ids), ))
                        theta_ds[:] = self.theta[proj_ids - proj_ids[0]]

                    # warnings that data is missing
                    if len(proj_ids) != len(self.theta):
                        log.warning(
                            f'There are {len(self.theta) - len(proj_ids)} missing data frames'
                        )
                        missed_ids = [
                            ele for ele in range(len(self.theta))
                            if ele not in proj_ids - proj_ids[0]
                        ]
                        missed_theta = self.theta[missed_ids]
                        log.warning(f'Missed theta: {list(missed_theta)}')
                    if len(flat_ids) != total_flat_fields:
                        log.warning(
                            f'There are {total_flat_fields - len(flat_ids)} missing flat field frames'
                        )
                    if (len(dark_ids) != total_dark_fields):
                        log.warning(
                            f'There are {total_dark_fields - len(dark_ids)} missing dark field frames'
                        )
            except:
                log.error('Add theta: Failed accessing: %s', full_file_name)
                traceback.print_exc(file=sys.stdout)
        else:
            log.error('Failed adding theta. %s file does not exist',
                      full_file_name)
Exemple #6
0
    def begin_scan(self):
        """Performs the operations needed at the very start of a scan.

        This does the following:

        - Calls the base class method.

        - Opens the front-end shutter.
        
        - Sets the PSO controller.

        - Creates theta array using list from PSO. 

        """
        log.info('begin scan')
        # Call the base class method
        super().begin_scan()
        # Opens the front-end shutter
        self.open_frontend_shutter()
         
        # Confirm angle step is an integer number of encoder pulses
        # Pass the user selected values to the PSO
        self.epics_pvs['PSOstartPos'].put(self.rotation_start, wait=True)
        self.wait_pv(self.epics_pvs['PSOstartPos'], self.rotation_start)
        self.epics_pvs['PSOendPos'].put(self.rotation_stop, wait=True)
        self.wait_pv(self.epics_pvs['PSOendPos'], self.rotation_stop)
        # Compute and set the motor speed
        time_per_angle = self.compute_frame_time()+7.2/1000
        motor_speed = self.rotation_step / time_per_angle
        self.epics_pvs['PSOslewSpeed'].put(motor_speed)
        self.wait_pv(self.epics_pvs['PSOslewSpeed'], motor_speed)

        self.epics_pvs['PSOscanDelta'].put(self.rotation_step, wait=True)
        self.wait_pv(self.epics_pvs['PSOscanDelta'], self.rotation_step)

        # Get the number of projections and angle steps calculated by the PSO
        calc_rotation_step = self.epics_pvs['PSOscanDelta'].value
        calc_num_proj = int(self.epics_pvs['PSOcalcProjections'].value)
        # If this is different from the user selected values adjust them
        if calc_rotation_step != self.rotation_step:
            # This should happen most of the time since rotation_step is rounded down to the closest integer
            # number of encoder pulses
            log.warning('PSO changed rotation step from %s to %s', self.rotation_step, calc_rotation_step)
            self.rotation_step = calc_rotation_step
        if calc_num_proj != self.num_angles:
            # This happens rarely an it is a +/-1 change in the number of projections to make sure that
            # after the rotation_step round down we don't pass the user set rotation_stop
            log.warning('PSO changed number of projections from %s to %s', self.num_angles, calc_num_proj)
            self.num_angles = calc_num_proj

        self.epics_pvs['PSOscanControl'].put('Standard')
        self.wait_pv(self.epics_pvs['PSOscanControl'], 0)
        time.sleep(1)

        # Create theta array
        self.theta = []
        self.theta = self.epics_pvs['ThetaArray'].get(count=int(self.num_angles))       

        self.begin_stream()     
Exemple #7
0
def yes_or_no(question):
    answer = str(input(question + " (Y/N): ")).lower().strip()
    while not(answer == "y" or answer == "yes" or answer == "n" or answer == "no"):
        log.warning("Input yes or no")
        answer = str(input(question + "(Y/N): ")).lower().strip()
    if answer[0] == "y":
        return True
    else:
        return False
Exemple #8
0
    def end_scan(self):
        """Performs the operations needed at the very end of a scan.

        This does the following:

        - Calls ``save_configuration()``.

        - Put the camera back in "FreeRun" mode and acquiring so the user sees live images.

        - Sets the speed of the rotation stage back to the maximum value.

        - Calls ``move_sample_in()``.

        - Calls the base class method.

        - Closes shutter.  

        - Add theta to the raw data file. 

        - Copy raw data to data analysis computer.      
        """

        if self.return_rotation == 'Yes':
            # Reset rotation position by mod 360 , the actual return
            # to start position is handled by super().end_scan()
            log.info('wait until the stage is stopped')
            time.sleep(self.epics_pvs['RotationAccelTime'].get() * 1.2)
            ang = self.epics_pvs['RotationRBV'].get()
            current_angle = np.sign(ang) * (np.abs(ang) % 360)
            self.epics_pvs['RotationSet'].put('Set', wait=True)
            self.epics_pvs['Rotation'].put(current_angle, wait=True)
            self.epics_pvs['RotationSet'].put('Use', wait=True)
        # Call the base class method
        super().end_scan()
        # Close shutter
        self.close_shutter()

        # Stop the file plugin
        self.epics_pvs['FPCapture'].put('Done')
        self.wait_pv(self.epics_pvs['FPCaptureRBV'], 0)
        # Add theta in the hdf file
        self.add_theta()

        # Copy raw data to data analysis computer
        if self.epics_pvs['CopyToAnalysisDir'].get():
            log.info(
                'Automatic data trasfer to data analysis computer is enabled.')
            full_file_name = self.epics_pvs['FPFullFileName'].get(
                as_string=True)
            remote_analysis_dir = self.epics_pvs['RemoteAnalysisDir'].get(
                as_string=True)
            dm.scp(full_file_name, remote_analysis_dir)
        else:
            log.warning(
                'Automatic data trasfer to data analysis computer is disabled.'
            )
Exemple #9
0
 def auto_copy_data(self):
     '''Copies data from detector computer to analysis computer.
     '''
     # Copy raw data to data analysis computer    
     if self.epics_pvs['CopyToAnalysisDir'].get():
         log.info('Automatic data trasfer to data analysis computer is enabled.')
         full_file_name = self.epics_pvs['FPFullFileName'].get(as_string=True)
         remote_analysis_dir = self.epics_pvs['RemoteAnalysisDir'].get(as_string=True)
         dm.scp(full_file_name, remote_analysis_dir)
     else:
         log.warning('Automatic data trasfer to data analysis computer is disabled.')
Exemple #10
0
    def set_scan_exposure_time(self, exposure_time=None):
        """Sets the camera exposure time during the scan.

        The exposure_time is written to the camera's ``AcquireTime`` PV.

        Parameters
        ----------
        exposure_time : float, optional
            The exposure time to use. If None then the value of the ``ExposureTime`` PV is used.
        """
        if exposure_time is None:
            exposure_time = self.epics_pvs['ExposureTime'].value
            log.warning('Setting exposure time: %f s', exposure_time)
        self.epics_pvs['CamAcquireTime'].put(exposure_time, wait=True, timeout = 10.)
def check_remote_directory(remote_server, remote_dir):
    try:
        rcmd = 'ls ' + remote_dir
        # rcmd is the command used to check if the remote directory exists
        subprocess.check_call(['ssh', '-t', remote_server, rcmd], stderr=open(os.devnull, 'wb'), stdout=open(os.devnull, 'wb'))
        log.warning('      *** remote directory %s exists' % (remote_dir))
        return 0

    except subprocess.CalledProcessError as e: 
        log.warning('      *** remote directory %s does not exist' % (remote_dir))
        if e.returncode == 2:
            return e.returncode
        else:
            log.error('  *** Unknown error code returned: %d' % (e.returncode))
            return -1
Exemple #12
0
    def close_shutter(self):
        """Closes the shutters to collect dark fields.
        This does the following:

        - Closes the 2-BM-A fast shutter.
        """

        # Close 2-BM-A fast shutter
        if not self.epics_pvs['CloseFastShutter'] is None:
            pv = self.epics_pvs['CloseFastShutter']
            value = self.epics_pvs['CloseFastShutterValue'].get(as_string=True)
            log.info('close fast shutter: %s, value: %s', pv, value)
            self.epics_pvs['CloseFastShutter'].put(value, wait=True)
            log.warning('close fast shutter sleep 2 sec')
            time.sleep(2)
Exemple #13
0
    def end_scan(self):
        """Performs the operations needed at the very end of a scan.

        This does the following:

        - Reset rotation position by mod 360.

        - Calls the base class method.

        - Stop the file plugin.

        - Closes shutter.  

        - Add theta to the raw data file. 

        - Copy raw data to data analysis computer.      
        """

        if self.return_rotation == 'Yes':
            # Reset rotation position by mod 360 , the actual return
            # to start position is handled by super().end_scan()
            current_angle = self.epics_pvs['Rotation'].get() % 360
            self.epics_pvs['RotationSet'].put('Set', wait=True)
            self.epics_pvs['Rotation'].put(current_angle, wait=True)
            self.epics_pvs['RotationSet'].put('Use', wait=True)
        # Call the base class method
        super().end_scan()
        # Close shutter
        self.close_shutter()
        # Stop the file plugin
        self.epics_pvs['FPCapture'].put('Done')
        self.wait_pv(self.epics_pvs['FPCaptureRBV'], 0)
        # Add theta in the hdf file
        self.add_theta()

        # Copy raw data to data analysis computer
        if self.epics_pvs['CopyToAnalysisDir'].get():
            log.info(
                'Automatic data trasfer to data analysis computer is enabled.')
            full_file_name = self.epics_pvs['FPFullFileName'].get(
                as_string=True)
            remote_analysis_dir = self.epics_pvs['RemoteAnalysisDir'].get(
                as_string=True)
            dm.scp(full_file_name, remote_analysis_dir)
        else:
            log.warning(
                'Automatic data trasfer to data analysis computer is disabled.'
            )
Exemple #14
0
    def copy_flat_dark_to_hdf(self):
        """Copies the flat and dark field data to the HDF5 file with the
        projection data.  This allows the file to be a fully valid
        DXchange file. Once the file is created it will be copied to the remote 
        data analysis computer (if CopyToAnalysisDir is set to yes)
        """
        log.info('save dark and flat to projection hdf file')
        fname = self.epics_pvs['FPFullFileName'].get(as_string=True)
        basename = os.path.basename(fname)
        dirname = os.path.dirname(fname)
        darkfield_name = os.path.join(dirname, 'dark_fields_' + basename)
        flatfield_name = os.path.join(dirname, 'flat_fields_' + basename)

        log.info('save dark fields')
        cmd = 'cp ' + os.path.join(dirname,
                                   'dark_fields.h5') + ' ' + darkfield_name
        os.system(cmd)
        log.info('save flat fields')
        cmd = 'cp ' + os.path.join(dirname,
                                   'flat_fields.h5') + ' ' + flatfield_name
        os.system(cmd)

        with h5py.File(fname, 'r+') as proj_hdf:
            if 'data_white' in proj_hdf['/exchange'].keys():
                del (proj_hdf['/exchange/data_white'])
            with h5py.File(flatfield_name, 'r') as flat_hdf:
                proj_hdf['/exchange'].create_dataset(
                    'data_white', data=flat_hdf['/exchange/data_white'][...])
            if 'data_dark' in proj_hdf['/exchange'].keys():
                del (proj_hdf['/exchange/data_dark'])
            with h5py.File(darkfield_name, 'r') as dark_hdf:
                proj_hdf['/exchange'].create_dataset(
                    'data_dark', data=dark_hdf['/exchange/data_dark'][...])
        log.info('done saving dark and flat to projection hdf file')
        # Copy raw data to data analysis computer
        if self.epics_pvs['CopyToAnalysisDir'].get():
            log.info(
                'Automatic data trasfer to data analysis computer is enabled.')
            full_file_name = self.epics_pvs['FPFullFileName'].get(
                as_string=True)
            remote_analysis_dir = self.epics_pvs['RemoteAnalysisDir'].get(
                as_string=True)
            dm.scp(full_file_name, remote_analysis_dir)
        else:
            log.warning(
                'Automatic data trasfer to data analysis computer is disabled.'
            )
Exemple #15
0
def show_config(args):
    """Log all values set in the args namespace.

    Arguments are grouped according to their section and logged alphabetically
    using the DEBUG log level thus --verbose is required.
    """
    args = args.__dict__

    log.warning('tomoscan status start')
    for section, name in zip(SECTIONS, NICE_NAMES):
        entries = sorted((k for k in args.keys() if k.replace('_', '-') in SECTIONS[section]))
        if entries:
            for entry in entries:
                value = args[entry] if args[entry] != None else "-"
                log.info("  {:<16} {}".format(entry, value))

    log.warning('tomoscan status end')
Exemple #16
0
    def close_shutter(self):
        """Closes the shutters to collect dark fields.
        This does the following:

        - Closes the 2-BM fast shutter.
        """

        # Close 2-BM fast shutter
        if not self.epics_pvs['CloseFastShutter'] is None:
            pv = self.epics_pvs['CloseFastShutter']
            value = self.epics_pvs['CloseFastShutterValue'].get(as_string=True)
            log.info('close fast shutter: %s, value: %s', pv, value)
            self.epics_pvs['CloseFastShutter'].put(value, wait=True)
            log.warning(
                "Wait 2s  - Temporarily while there is no fast shutter at 2bmb "
            )
            time.sleep(2)
Exemple #17
0
    def set_flat_exposure_time(self, exposure_time=None):
        """Sets the camera exposure time for flat fields.

        The exposure_time is written to the camera's ``AcquireTime`` PV.

        Parameters
        ----------
        exposure_time : float, optional
            The exposure time to use. If None then the value of the ``FlatExposureTime`` PV is used.
        """

        if self.epics_pvs['DifferentFlatExposure'].get(as_string=True) == 'Same':
            self.set_exposure_time(exposure_time)
            return
        if exposure_time is None:
            exposure_time = self.epics_pvs['FlatExposureTime'].value
            log.warning('Setting flat field exposure time: %f s', exposure_time)
        self.epics_pvs['CamAcquireTime'].put(exposure_time, wait=True, timeout = 10.)
Exemple #18
0
    def open_shutter(self):
        """Opens the shutters to collect flat fields or projections.

        This does the following:

        - Opens the 2-BM-A fast shutter.
        """

        # Open 2-BM-A fast shutter
        if not self.epics_pvs['OpenFastShutter'] is None:
            pv = self.epics_pvs['OpenFastShutter']
            value = self.epics_pvs['OpenFastShutterValue'].get(as_string=True)
            log.info('open fast shutter: %s, value: %s', pv, value)
            self.epics_pvs['OpenFastShutter'].put(value, wait=True)
            log.warning(
                "Wait 2s  - Temporarily while there is no fast shutter at 2bmb "
            )
            time.sleep(2)
Exemple #19
0
    def close_frontend_shutter(self):
        """Closes the shutters to collect dark fields.
        This does the following:

        - Closes the 2-BM-A front-end shutter.

        """
        if self.epics_pvs['Testing'].get():
            log.warning('In testing mode, so not opening shutters.')
        else:
            # Close 2-BM-A front-end shutter
            if not self.epics_pvs['CloseShutter'] is None:
                pv = self.epics_pvs['CloseShutter']
                value = self.epics_pvs['CloseShutterValue'].get(as_string=True)
                status = self.epics_pvs['ShutterStatus'].get(as_string=True)
                log.info('shutter status: %s', status)
                log.info('close shutter: %s, value: %s', pv, value)
                self.epics_pvs['CloseShutter'].put(value, wait=True)
                self.wait_pv(self.epics_pvs['ShutterStatus'], 0)
                status = self.epics_pvs['ShutterStatus'].get(as_string=True)
                log.info('shutter status: %s', status)
    def compute_positions_PSO(self):
        '''Computes several parameters describing the fly scan motion.
        Computes the spacing between points, ensuring it is an integer number
        of encoder counts.
        Uses this spacing to recalculate the end of the scan, if necessary.
        Computes the taxi distance at the beginning and end of scan to allow
        the stage to accelerate to speed.
        Assign the fly scan angular position to theta[]
        '''
        overall_sense, user_direction = self._compute_senses()

        # Compute the actual delta to keep each interval an integer number of encoder counts
        encoder_multiply = float(
            self.epics_pvs['PSOCountsPerRotation'].get()) / 360.
        raw_delta_encoder_counts = self.rotation_step * encoder_multiply
        delta_encoder_counts = round(raw_delta_encoder_counts)
        if abs(raw_delta_encoder_counts - delta_encoder_counts) > 1e-4:
            log.warning(
                '  *** *** *** Requested scan would have used a non-integer number of encoder counts.'
            )
            log.warning(
                '  *** *** *** Calculated # of encoder counts per step = {0:9.4f}'
                .format(raw_delta_encoder_counts))
            log.warning('  *** *** *** Instead, using {0:d}'.format(
                delta_encoder_counts))
        self.epics_pvs['PSOEncoderCountsPerStep'].put(delta_encoder_counts)
        # Change the rotation step Python variable and PV
        self.rotation_step = delta_encoder_counts / encoder_multiply
        self.epics_pvs['RotationStep'].put(self.rotation_step)

        # Compute the time for each frame
        time_per_angle = self.compute_frame_time()
        self.motor_speed = np.abs(self.rotation_step) / time_per_angle
        # Get the distance needed for acceleration = 1/2 a t^2 = 1/2 * v * t
        motor_accl_time = float(self.epics_pvs['RotationAccelTime'].get()
                                )  # Acceleration time in s
        accel_dist = motor_accl_time / 2.0 * float(self.motor_speed)
        # Make taxi distance an integer number of measurement deltas >= accel distance
        # Add 1/2 of a delta to ensure that we are really up to speed.
        if self.rotation_step > 0:
            taxi_dist = math.ceil(accel_dist / self.rotation_step +
                                  0.5) * self.rotation_step
        else:
            taxi_dist = math.floor(accel_dist / self.rotation_step -
                                   0.5) * self.rotation_step
        self.epics_pvs['PSOStartTaxi'].put(self.rotation_start -
                                           taxi_dist * user_direction)

        #Where will the last point actually be?
        self.rotation_stop = (
            self.rotation_start +
            (self.num_angles - 1) * self.rotation_step * user_direction)
        self.epics_pvs['PSOEndTaxi'].put(self.rotation_stop +
                                         taxi_dist * user_direction)

        # Assign the fly scan angular position to theta[]
        self.theta = self.rotation_start + np.arange(
            self.num_angles) * self.rotation_step * user_direction
        self.pva_stream_theta['value'] = self.theta
        self.pva_stream_theta['sizex'] = self.num_angles
Exemple #21
0
    def add_theta(self):
        """Add theta at the end of a scan.
        """
        log.info('add theta')

        full_file_name = self.epics_pvs['FPFullFileName'].get(as_string=True)
        if os.path.exists(full_file_name):
            try:
                f = h5py.File(full_file_name, "a")
                with f:
                    try:
                        if self.theta is not None:
                            # theta_ds = f.create_dataset('/exchange/theta', (len(self.theta),))
                            # theta_ds[:] = self.theta[:]

                            unique_ids = f['/defaults/NDArrayUniqueId']
                            shift_start = int(self.num_dark_fields > 0 and (self.dark_field_mode in ('Start', 'Both')))+ \
                                          int(self.num_flat_fields > 0 and (self.flat_field_mode in ('Start', 'Both')))

                            # find beginnings of sorted subarrays
                            # for [1,2,1,3,1,2,3,4,1,2] returns 0,2,4,8
                            ids_list = [
                                0, *np.where(unique_ids[1:] -
                                             unique_ids[:-1] < 0)[0] + 1
                            ]
                            # extract projection ids
                            if (len(ids_list[shift_start:]) == 1):
                                proj_ids = unique_ids[ids_list[shift_start]:]
                            else:
                                proj_ids = unique_ids[ids_list[shift_start]:
                                                      ids_list[shift_start +
                                                               1]]
                            # subtract first id
                            proj_ids -= proj_ids[0]
                            # create theta dataset in hdf5 file
                            theta_ds = f.create_dataset(
                                '/exchange/theta', (len(proj_ids), ))
                            theta_ds[:] = self.theta[proj_ids]

                            if (len(proj_ids) != len(self.theta)):
                                log.warning('There are %d missing frames',
                                            len(self.theta) - len(proj_ids))
                                missed_ids = [
                                    ele for ele in range(len(self.theta))
                                    if ele not in proj_ids
                                ]
                                missed_theta = self.theta[missed_ids]
                                log.warning(f'Missed ids: {list(missed_ids)}')
                                log.warning(
                                    f'Missed theta: {list(missed_theta)}')

                    except:
                        log.error('Add theta: Failed accessing: %s',
                                  full_file_name)
                        traceback.print_exc(file=sys.stdout)
            except OSError:
                log.error('Add theta aborted: %s not closed', full_file_name)
        else:
            log.error('Failed adding theta. %s file does not exist',
                      full_file_name)
Exemple #22
0
    def open_shutter(self):
        """Opens the shutter to collect flat fields or projections.

        This does the following:

        - Checks if we are in testing mode.  If we are, do nothing.

        - Calls the base class method.

        - Opens the 2-BM-A fast shutter.
        """
        if self.epics_pvs['Testing'].get():
            log.warning('In testing mode, so not opening shutters.')
            return
        # Call the base class method
        super().open_shutter()
        # Open 7-BM-B fast shutter
        if not self.epics_pvs['OpenFastShutter'] is None:
            pv = self.epics_pvs['OpenFastShutter']
            value = self.epics_pvs['OpenFastShutterValue'].get(as_string=True)
            log.info('open fast shutter: %s, value: %s', pv, value)
            self.epics_pvs['OpenFastShutter'].put(value, wait=True)
Exemple #23
0
    def begin_scan(self):
        """Performs the operations needed at the very start of a scan.

        This does the following:

        - Set data directory.

         - Set the TomoScan xml files

        - Calls the base class method.

        - Opens the front-end shutter.
        
        - Sets the PSO controller.

        - Creates theta array using list from PSO. 

        """
        log.info('begin scan')

        # Set data directory
        file_path = self.epics_pvs['DetectorTopDir'].get(
            as_string=True) + self.epics_pvs['ExperimentYearMonth'].get(
                as_string=True
            ) + os.path.sep + self.epics_pvs['UserLastName'].get(
                as_string=True) + os.path.sep
        self.epics_pvs['FilePath'].put(file_path, wait=True)

        if self.epics_pvs['ReturnRotation'].get(as_string=True) == 'Yes':
            if np.abs(self.epics_pvs['RotationRBV'].get()) > 720:
                log.warning('home stage')
                self.epics_pvs['RotationHomF'].put(1, wait=True)

        self.lens_cur = self.epics_pvs['LensSelect'].get()
        # Call the base class method
        super().begin_scan()
        # Opens the front-end shutter
        self.open_frontend_shutter()
        self.epics_pvs['LensSelect'].add_callback(self.pv_callback_stream_2bm)
Exemple #24
0
    def close_shutter(self):
        """Closes the shutter to collect dark fields.
        This does the following:

        - Checks if we are in testing mode.  If we are, do nothing

        - Calls the base class method.

        - Closes the 7-BM-B fast shutter.

       """
        if self.epics_pvs['Testing'].get():
            log.warning('In testing mode, so not closing shutters.')
            return
        # Call the base class method
        super().close_shutter()
        # Close 7-BM-B fast shutter
        if not self.epics_pvs['CloseFastShutter'] is None:
            pv = self.epics_pvs['CloseFastShutter']
            value = self.epics_pvs['CloseFastShutterValue'].get(as_string=True)
            log.info('close fast shutter: %s, value: %s', pv, value)
            self.epics_pvs['CloseFastShutter'].put(value, wait=True)
Exemple #25
0
    def wait_frontend_shutter_open(self, timeout=-1):
        """Waits for the front end shutter to open, or for ``abort_scan()`` to be called.

        While waiting this method periodically tries to open the shutter..

        Parameters
        ----------
        timeout : float
            The maximum number of seconds to wait before raising a ShutterTimeoutError exception.

        Raises
        ------
        ScanAbortError
            If ``abort_scan()`` is called
        ShutterTimeoutError
            If the open shutter has not completed within timeout value.
        """

        start_time = time.time()
        pv = self.epics_pvs['OpenShutter']
        value = self.epics_pvs['OpenShutterValue'].get(as_string=True)
        log.info('open shutter: %s, value: %s', pv, value)
        elapsed_time = 0
        while True:
            if self.epics_pvs['ShutterStatus'].get() == int(value):
                log.warning("Shutter is open in %f s", elapsed_time)
                return
            if not self.scan_is_running:
                exit()
            value = self.epics_pvs['OpenShutterValue'].get()
            time.sleep(1.0)
            current_time = time.time()
            elapsed_time = current_time - start_time
            log.warning("Waiting on shutter to open: %f s", elapsed_time)
            self.epics_pvs['OpenShutter'].put(value, wait=True)
            if timeout > 0:
                if elapsed_time >= timeout:
                    exit()
Exemple #26
0
    def end_scan(self):
        """Performs the operations needed at the very end of a scan.

        This does the following:

        - Calls ``save_configuration()``.

        - Put the camera back in "FreeRun" mode and acquiring so the user sees live images.

        - Sets the speed of the rotation stage back to the maximum value.

        - Calls ``move_sample_in()``.

        - Calls the base class method.

        - Closes shutter.  

        - Add theta to the raw data file. 

        - Copy raw data to data analysis computer.      
        """

        if self.return_rotation == 'Yes':
            # Reset rotation position by mod 360 , the actual return
            # to start position is handled by super().end_scan()
            # allow stage to stop
            log.info('wait until the stage is stopped')
            time.sleep(self.epics_pvs['RotationAccelTime'].get() * 1.2)
            ang = self.epics_pvs['RotationRBV'].get()
            current_angle = np.sign(ang) * (np.abs(ang) % 360)
            self.epics_pvs['RotationSet'].put('Set', wait=True)
            self.epics_pvs['Rotation'].put(current_angle, wait=True)
            self.epics_pvs['RotationSet'].put('Use', wait=True)
        # Close shutter
        self.close_shutter()

        # Stop the file plugin
        self.epics_pvs['FPCapture'].put('Done')
        self.wait_pv(self.epics_pvs['FPCaptureRBV'], 0)
        # Add theta in the hdf file
        self.add_theta()

        log.info('Adding a frame from the IP camera')
        ret, frame = cv2.VideoCapture(
            'http://*****:*****@164.54.113.162/cgi-bin/mjpeg?stream=1'
        ).read()  # we should hide the password

        #station A
        # NetBooter = NetBooter_Control(mode='telnet',id=self.access_dic['pdu_username'],password=self.access_dic['pdu_password'],ip=self.access_dic['pdu_ip_address'])
        # NetBooter.power_on(1)
        # log.info('wait 10 sec while the web camera has focused')
        # time.sleep(10)
        # ret, frame = cv2.VideoCapture('http://*****:*****@164.54.113.137/cgi-bin/mjpeg?stream=1').read()# we should hide the password
        #ret, frame = cv2.VideoCapture('http://' + self.access_dic['webcam_username'] +':' + self.access_dic['webcam_password'] + '@' + self.access_dic['webcam_ip_address'] + '/cgi-bin/mjpeg?stream=1').read()
        # NetBooter.power_off(1)

        if ret == True:
            full_file_name = self.epics_pvs['FPFullFileName'].get(
                as_string=True)
            with h5py.File(full_file_name, 'r+') as fid:
                fid.create_dataset('exchange/web_camera_frame', data=frame)
            log.info('The frame was added')
        else:
            log.warning('The frame was not added')

        # Copy raw data to data analysis computer
        if self.epics_pvs['CopyToAnalysisDir'].get():
            log.info(
                'Automatic data trasfer to data analysis computer is enabled.')
            full_file_name = self.epics_pvs['FPFullFileName'].get(
                as_string=True)
            remote_analysis_dir = self.epics_pvs['RemoteAnalysisDir'].get(
                as_string=True)
            dm.scp(full_file_name, remote_analysis_dir)
        else:
            log.warning(
                'Automatic data trasfer to data analysis computer is disabled.'
            )

        # Call the base class method
        super().end_scan()
Exemple #27
0
    def begin_scan(self):
        """Performs the operations needed at the very start of a scan.

        This does the following:

        - Calls the base class method.
        
        - Set data directory.
        
        - Opens the front-end shutter.

        - Sets the PSO controller.

        - Creates theta array using list from PSO. 

        - Turns on data capture.
        """
        log.info('begin scan')

        # Set data directory
        file_path = self.epics_pvs['DetectorTopDir'].get(as_string=True) + self.epics_pvs['ExperimentYearMonth'].get(as_string=True) + os.path.sep + self.epics_pvs['UserLastName'].get(as_string=True) + os.path.sep
        self.epics_pvs['FilePath'].put(file_path, wait=True)

        # Call the base class method
        super().begin_scan()
        # Opens the front-end shutter
        self.open_frontend_shutter()
        
        # Confirm angle step is an integer number of encoder pulses
        # Pass the user selected values to the PSO
        self.epics_pvs['PSOstartPos'].put(self.rotation_start, wait=True)
        self.wait_pv(self.epics_pvs['PSOstartPos'], self.rotation_start)
        self.epics_pvs['PSOendPos'].put(self.rotation_stop+self.rotation_step, wait=True)
        self.wait_pv(self.epics_pvs['PSOendPos'], self.rotation_stop+self.rotation_step)
        # Compute and set the motor speed
        time_per_angle = self.compute_frame_time()#+7.2/1000   ##no overlap mode -> time_per_angle=exposure+readout
        motor_speed = self.rotation_step / time_per_angle
        self.epics_pvs['PSOslewSpeed'].put(motor_speed)
        self.wait_pv(self.epics_pvs['PSOslewSpeed'], motor_speed)

        self.epics_pvs['PSOscanDelta'].put(self.rotation_step, wait=True)
        self.wait_pv(self.epics_pvs['PSOscanDelta'], self.rotation_step)

        # Get the number of projections and angle steps calculated by the PSO
        calc_rotation_step = self.epics_pvs['PSOscanDelta'].value
        calc_num_proj = int(self.epics_pvs['PSOcalcProjections'].value)
        # If this is different from the user selected values adjust them
        if calc_rotation_step != self.rotation_step:
            # This should happen most of the time since rotation_step is rounded down to the closest integer
            # number of encoder pulses
            log.warning('PSO changed rotation step from %s to %s', self.rotation_step, calc_rotation_step)
            self.rotation_step = calc_rotation_step
        if calc_num_proj != self.num_angles:
            # This happens rarely an it is a +/-1 change in the number of projections to make sure that
            # after the rotation_step round down we don't pass the user set rotation_stop
            log.warning('PSO changed number of projections from %s to %s', self.num_angles, calc_num_proj)
            self.num_angles = calc_num_proj

        self.epics_pvs['PSOscanControl'].put('Standard')
        self.wait_pv(self.epics_pvs['PSOscanControl'], 0)
        time.sleep(1)

        # # Create theta array
        self.theta = []
        self.theta = self.epics_pvs['ThetaArray'].get(count=int(self.num_angles))
        print(self.theta,self.num_angles)
        # Compute total number of frames to capture
        self.total_images = self.num_angles
        if self.dark_field_mode != 'None':
            self.total_images += self.num_dark_fields
        if self.dark_field_mode == 'Both':
            self.total_images += self.num_dark_fields
        if self.flat_field_mode != 'None':
            self.total_images += self.num_flat_fields
        if self.flat_field_mode == 'Both':
            self.total_images += self.num_flat_fields
        # Set the total number of frames to capture and start capture on file plugin
        self.epics_pvs['FPNumCapture'].put(self.total_images, wait=True)
        self.epics_pvs['FPCapture'].put('Capture')
Exemple #28
0
    def stream_sync(self):
        """Synchronize new angular step and exposure with rotation speed. Brodcast new array of angles for streaming reconstruction

        - cleanup PSO
        - save last unique ID of projection
        - program pso (base part)
        - change the JOG speed wrt exposure and angular step
        - initiate DATAACQ in aerotech for saving encoder values
        - ARM PSO
        - wait exposure time to acquire 1 projection
        - read encoder value for the projection unique_id + 1
        - convert encoder value to the angle, form array of angles
        - broadcast array of angles for streaming
        """
        log.info('stream sync')

        if self.epics_pvs['StreamMessage'].get(as_string=True)=='Done' and self.epics_pvs['StreamScanType'].get(as_string=True)!='backforth' and \
            (self.exposure_time!=self.epics_pvs['ExposureTime'].value or self.rotation_step!=self.epics_pvs['RotationStep'].value):

            self.exposure_time = self.epics_pvs['ExposureTime'].value
            self.rotation_step = self.epics_pvs['RotationStep'].value
            #compute new counts per step
            # Compute the actual delta to keep each interval an integer number of encoder counts
            encoder_multiply = float(
                self.epics_pvs['PSOCountsPerRotation'].get()) / 360.
            raw_delta_encoder_counts = self.rotation_step * encoder_multiply
            delta_encoder_counts = round(raw_delta_encoder_counts)
            if abs(raw_delta_encoder_counts - delta_encoder_counts) > 1e-4:
                log.warning(
                    '  *** *** *** Requested scan would have used a non-integer number of encoder counts.'
                )
                log.warning(
                    '  *** *** *** Calculated # of encoder counts per step = {0:9.4f}'
                    .format(raw_delta_encoder_counts))
                log.warning('  *** *** *** Instead, using {0:d}'.format(
                    delta_encoder_counts))
            self.epics_pvs['PSOEncoderCountsPerStep'].put(delta_encoder_counts)

            self.cleanup_PSO()
            # wait until last projection is acquired
            time.sleep(self.exposure_time + 0.1)

            # save last unique id of the projection
            projid = self.epics_pvs['CamNumImagesCounter'].get()

            pso_command = self.epics_pvs['PSOCommand.BOUT']
            pso_model = self.epics_pvs['PSOControllerModel'].get(
                as_string=True)
            pso_axis = self.epics_pvs['PSOAxisName'].get(as_string=True)
            pso_input = int(
                self.epics_pvs['PSOEncoderInput'].get(as_string=True))
            overall_sense, user_direction = self._compute_senses()

            # Make sure the PSO control is off
            pso_command.put('PSOCONTROL %s RESET' % pso_axis,
                            wait=True,
                            timeout=10.0)
            # Set the output to occur from the I/O terminal on the controller
            if (pso_model == 'Ensemble'):
                pso_command.put('PSOOUTPUT %s CONTROL 1' % pso_axis,
                                wait=True,
                                timeout=10.0)
            elif (pso_model == 'A3200'):
                pso_command.put('PSOOUTPUT %s CONTROL 0 1' % pso_axis,
                                wait=True,
                                timeout=10.0)
            # Set the pulse width.  The total width and active width are the same, since this is a single pulse.
            pulse_width = self.epics_pvs['PSOPulseWidth'].get()
            pso_command.put('PSOPULSE %s TIME %f,%f' %
                            (pso_axis, pulse_width, pulse_width),
                            wait=True,
                            timeout=10.0)
            # Set the pulses to only occur in a specific window
            pso_command.put('PSOOUTPUT %s PULSE WINDOW MASK' % pso_axis,
                            wait=True,
                            timeout=10.0)
            # Set which encoder we will use.  3 = the MXH (encoder multiplier) input, which is what we generally want
            pso_command.put('PSOTRACK %s INPUT %d' % (pso_axis, pso_input),
                            wait=True,
                            timeout=10.0)
            # Set the distance between pulses. Do this in encoder counts.
            pso_command.put(
                'PSODISTANCE %s FIXED %d' %
                (pso_axis,
                 int(np.abs(self.epics_pvs['PSOEncoderCountsPerStep'].get()))),
                wait=True,
                timeout=10.0)
            # Which encoder is being used to calculate whether we are in the window.  1 for single axis
            pso_command.put('PSOWINDOW %s 1 INPUT %d' % (pso_axis, pso_input),
                            wait=True,
                            timeout=10.0)

            # Calculate window function parameters.  Must be in encoder counts, and is
            # referenced from the stage location where we arm the PSO.  We are at that point now.
            # We want pulses to start at start - delta/2, end at end + delta/2.
            range_start = -round(
                np.abs(self.epics_pvs['PSOEncoderCountsPerStep'].get()) /
                2) * overall_sense
            range_length = np.abs(self.epics_pvs['PSOEncoderCountsPerStep'].
                                  get()) * self.num_angles
            # The start of the PSO window must be < end.  Handle this.
            if overall_sense > 0:
                window_start = range_start
                window_end = window_start + range_length
            else:
                window_end = range_start
                window_start = window_end - range_length
            pso_command.put('PSOWINDOW %s 1 RANGE %d,%d' %
                            (pso_axis, window_start - 5, window_end + 5),
                            wait=True,
                            timeout=10.0)

            self.epics_pvs['CamAcquireTime'].put(self.exposure_time,
                                                 wait=True,
                                                 timeout=10.0)
            time_per_angle = self.compute_frame_time()
            # compute acceleration time
            accelJog_time = np.abs(
                (self.motor_speed - self.rotation_step /
                 time_per_angle)) / self.control_pvs['RotationAccelJog'].value
            # recompute velocity
            self.motor_speed = self.rotation_step / time_per_angle
            self.control_pvs['RotationSpeedJog'].put(self.motor_speed,
                                                     wait=True)
            pso_command.put('PROGRAM RUN 1, "dataacqoff.bcx"',
                            wait=True,
                            timeout=10.0)
            pso_command.put('PROGRAM RUN 1, "dataacqon.bcx"',
                            wait=True,
                            timeout=10.0)
            # wait acceleration
            log.warning(f'wait {accelJog_time+1} for acceleration')
            time.sleep(accelJog_time + 1)
            # Arm the PSO
            log.warning(
                f'ARM PSO and wait until the first projection is acquired')
            pso_command.put('PSOCONTROL %s ARM' % pso_axis,
                            wait=True,
                            timeout=10.0)
            # wait while 1 projection is acquired
            time.sleep(self.exposure_time + 0.4)
            pso_command.put('PROGRAM RUN 1, "dataacqread.bcx"',
                            wait=True,
                            timeout=10.0)
            # need some delay here
            time.sleep(0.1)
            pso_command.put('IGLOBAL(0)', wait=True, timeout=10.0)
            time.sleep(0.1)
            reply = self.epics_pvs['PSOCommand.BINP'].get(as_string=True)
            time.sleep(0.1)
            if reply:
                encoder_angle = int(reply[1:])

                self.rotation_start = self.control_pvs[
                    'RotationOFF'].value + encoder_angle * self.epics_pvs[
                        'RotationEResolution'].value
                self.epics_pvs['FirstProjid'].put(projid + 1, wait=True)
                self.compute_positions_PSO()
                # Assign the fly scan angular position to theta[]
                self.theta = self.rotation_start + np.arange(
                    self.num_angles) * self.rotation_step
                self.pva_stream_theta['value'] = self.theta.astype('float32')
                self.pva_stream_theta['sizex'] = len(self.theta)
                log.info(
                    f'Angle {self.theta[0]} corresponds to unique ID {projid+1}'
                )
            else:
                log.error('PSO didnt return encoder value')
        else:
            log.info('stream sync ignore')
        self.epics_pvs['StreamSync'].put('Done', wait=True)