Esempio n. 1
0
class SampleChanger(Device):
    """ A device that moves samples in and out from the sample holder."""

    sample = Selection([None])

    def __init__(self):
        super(SampleChanger, self).__init__()

    def _set_sample(self):
        raise AccessorNotImplementedError

    def _get_sample(self):
        raise AccessorNotImplementedError
Esempio n. 2
0
class LensChanger(BaseLensChanger):
    '''Lens changer class implementation'''
    objective = Selection(['objective_10x', 'objective_5x'], help='objective',
                          check=check(source='standby', target='standby'))

    def __init__(self):
        super(LensChanger, self).__init__()
        self._objective = 'objective_10x'

    async def _set_objective(self, objective):
        self._objective = objective

    async def _get_objective(self):
        return self._objective
Esempio n. 3
0
class AttenuatorBox(base.AttenuatorBox):
    '''Attenuator class implementation'''
    attenuator = Selection([None, 'Al_1mm'],
                           check=check(source='standby', target='standby'))

    def __init__(self):
        super(AttenuatorBox, self).__init__()
        self._filter = None

    async def _set_attenuator(self, attenuator):
        self._filter = attenuator

    async def _get_attenuator(self):
        return self._filter
Esempio n. 4
0
class SampleChanger(base.SampleChanger):

    sample = Selection([None, 1, 2])

    def __init__(self):
        super(SampleChanger, self).__init__()
        self._sample = None

    async def _set_sample(self, sample):
        """Insert the sample in the holder."""
        self._sample = sample

    async def _get_sample(self):
        return self._sample
Esempio n. 5
0
class SelectionDevice(Device):
    """A dummy device with a selection."""

    selection = Selection(list(range(3)))

    def __init__(self):
        super(SelectionDevice, self).__init__()
        self._selection = 0

    async def _get_selection(self):
        return self._selection

    async def _set_selection(self, selection):
        self._selection = selection
Esempio n. 6
0
class Experiment(Parameterizable):
    """
    Experiment base class. An experiment can be run multiple times with the output data and log
    stored on disk. You can prepare every run by :meth:`.prepare` and finsh the run by
    :meth:`.finish`. These methods do nothing by default. They can be useful e.g. if you need to
    reinitialize some experiment parts or want to attach some logging output.

    .. py:attribute:: acquisitions
        :noindex:

        A list of acquisitions this experiment is composed of

    .. py:attribute:: walker

       A :class:`concert.storage.Walker` descends to a data set specific for every run if given

    .. py:attribute:: separate_scans

        If True, *walker* does not descend to data sets based on specific runs

    .. py:attribute:: name_fmt

        Since experiment can be run multiple times each iteration will have a separate entry
        on the disk. The entry consists of a name and a number of the current iteration, so the
        parameter is a formattable string.

    """

    iteration = Parameter()
    separate_scans = Parameter()
    name_fmt = Parameter()
    state = State(default='standby')
    log_level = Selection(['critical', 'error', 'warning', 'info', 'debug'])

    def __init__(self,
                 acquisitions,
                 walker=None,
                 separate_scans=True,
                 name_fmt='scan_{:>04}'):
        self._acquisitions = []
        for acquisition in acquisitions:
            self.add(acquisition)
        self.walker = walker
        self._separate_scans = separate_scans
        self._name_fmt = name_fmt
        self._iteration = 1
        self.log = LOG
        Parameterizable.__init__(self)

        if separate_scans and walker:
            # The data is not supposed to be overwritten, so find an iteration which
            # hasn't been used yet
            while self.walker.exists(self._name_fmt.format(self._iteration)):
                self._iteration += 1

    async def _get_iteration(self):
        return self._iteration

    async def _set_iteration(self, iteration):
        self._iteration = iteration

    async def _get_separate_scans(self):
        return self._separate_scans

    async def _set_separate_scans(self, separate_scans):
        self._separate_scans = separate_scans

    async def _get_name_fmt(self):
        return self._name_fmt

    async def _set_name_fmt(self, fmt):
        self._name_fmt = fmt

    async def _get_log_level(self):
        return logging.getLevelName(self.log.getEffectiveLevel()).lower()

    async def _set_log_level(self, level):
        self.log.setLevel(level.upper())

    async def prepare(self):
        """Gets executed before every experiment run."""
        pass

    async def finish(self):
        """Gets executed after every experiment run."""
        pass

    @property
    def acquisitions(self):
        """Acquisitions is a read-only attribute which has to be manipulated by explicit methods
        provided by this class.
        """
        return tuple(self._acquisitions)

    def add(self, acquisition):
        """
        Add *acquisition* to the acquisition list and make it accessible as
        an attribute::

            frames = Acquisition(...)
            experiment.add(frames)
            # This is possible
            experiment.frames
        """
        self._acquisitions.append(acquisition)
        setattr(self, acquisition.name, acquisition)

    def remove(self, acquisition):
        """Remove *acquisition* from experiment."""
        self._acquisitions.remove(acquisition)
        delattr(self, acquisition.name)

    def swap(self, first, second):
        """
        Swap acquisition *first* with *second*. If there are more occurences
        of either of them then the ones which are found first in the acquisitions
        list are swapped.
        """
        if first not in self._acquisitions or second not in self._acquisitions:
            raise ValueError(
                "Both acquisitions must be part of the experiment")

        first_index = self._acquisitions.index(first)
        second_index = self._acquisitions.index(second)
        self._acquisitions[first_index] = second
        self._acquisitions[second_index] = first

    def get_acquisition(self, name):
        """
        Get acquisition by its *name*. In case there are more like it, the first
        one is returned.
        """
        for acq in self._acquisitions:
            if acq.name == name:
                return acq
        raise ExperimentError(
            "Acquisition with name `{}' not found".format(name))

    async def acquire(self):
        """
        Acquire data by running the acquisitions. This is the method which implements
        the data acquisition and should be overriden if more functionality is required,
        unlike :meth:`~.Experiment.run`.
        """
        for acq in wrap_iterable(self._acquisitions):
            if await self.get_state() != 'running':
                break
            await acq()

    @background
    @check(source=['standby', 'error'], target='standby')
    @transition(immediate='running', target='standby')
    async def run(self):
        start_time = time.time()
        handler = None
        iteration = await self.get_iteration()
        separate_scans = await self.get_separate_scans()

        try:
            if self.walker:
                if separate_scans:
                    self.walker.descend(
                        (await self.get_name_fmt()).format(iteration))
                if os.path.exists(self.walker.current):
                    # We might have a dummy walker which doesn't create the directory
                    handler = logging.FileHandler(
                        os.path.join(self.walker.current, 'experiment.log'))
                    formatter = logging.Formatter(
                        '%(asctime)s - %(name)s - %(levelname)s '
                        '- %(message)s')
                    handler.setFormatter(formatter)
                    self.log.addHandler(handler)
            self.log.info(await self.info_table)
            LOG.debug('Experiment iteration %d start', iteration)
            await self.prepare()
            await self.acquire()
        except asyncio.CancelledError:
            # This is normal, no special state needed -> standby
            LOG.warn('Experiment cancelled')
        except Exception as e:
            # Something bad happened and we can't know what, so set the state to error
            LOG.warn(f"Error `{e}' while running experiment")
            raise StateError('error', msg=str(e))
        except KeyboardInterrupt:
            LOG.warn('Experiment cancelled by keyboard interrupt')
            self._state_value = 'standby'
            raise
        finally:
            try:
                await self.finish()
            except Exception as e:
                LOG.warn(f"Error `{e}' while finalizing experiment")
                raise StateError('error', msg=str(e))
            finally:
                if separate_scans and self.walker:
                    self.walker.ascend()
                LOG.debug('Experiment iteration %d duration: %.2f s',
                          iteration,
                          time.time() - start_time)
                if handler:
                    handler.close()
                    self.log.removeHandler(handler)
                await self.set_iteration(iteration + 1)