Exemple #1
0
class FooDevice(BaseDevice):

    state = State(default='standby')

    no_write = Parameter()
    foo = Quantity(q.m, check=check(source='*', target='moved'))
    bar = Quantity(q.m)
    test = Quantity(q.m, fset=_test_setter, fget=_test_getter)

    def __init__(self, default):
        super(FooDevice, self).__init__()
        self._value = default
        self._param_value = 0 * q.mm
        self._test_value = 0 * q.mm

    async def _get_foo(self):
        return self._value

    @transition(target='moved')
    async def _set_foo(self, value):
        self._value = value

    async def _get_bar(self):
        return 5 * q.m

    param = Parameter(fget=_get_foo, fset=_set_foo)

    async def _get_param(self):
        return self._param_value

    async def _set_param(self, value):
        self._param_value = value

    param = Parameter(fget=_get_param, fset=_set_param)
Exemple #2
0
 def __init__(self, port_id, name, fget=None, fset=None, doc=None):
     if fget is None:
         new_fget = None
     else:
         new_fget = lambda: fget(port_id)
     if fset is None:
         new_fset = None
     else:
         new_fset = lambda value: fset(port_id, value)
     Parameter.__init__(self, name, fget=new_fget, fset=new_fset, doc=doc)
     self.port_id = port_id
Exemple #3
0
    def __init__(self, param, feedback):
        params = [
            Parameter('minimum', doc="Left bound of the interval"),
            Parameter('maximum', doc="Right bound of the interval"),
            Parameter('intervals', doc="Number of intervals")
        ]

        super(Scanner, self).__init__(params)
        self.intervals = 64
        self.param = param
        self.feedback = feedback
Exemple #4
0
    def __init__(self):
        def setter(value):
            pass

        def getter():
            return 1

        self.params = [Parameter('readonly', fget=getter),
                       Parameter('writeonly', fset=setter)]

        super(MockDevice, self).__init__(self.params)
Exemple #5
0
 def __init__(self, calibration):
     params = [
         Parameter("energy",
                   self._get_energy,
                   self._set_energy,
                   q.eV,
                   doc="Monochromatic energy"),
         Parameter("wavelength",
                   self._get_wavelength,
                   self._set_wavelength,
                   q.nanometer,
                   doc="Monochromatic wavelength")
     ]
     super(Monochromator, self).__init__(params)
     self._calibration = calibration
Exemple #6
0
 def __init__(self, parameters=None):
     super(Device, self).__init__(parameters)
     self.add_parameter(Parameter('state', self._get_state,
                                  owner_only=True))
     self._lock = threading.Lock()
     self._states = set([self.NA])
     self._state = self.NA
Exemple #7
0
    def __init__(self, directory):
        # Let users change the directory
        self.directory = directory
        params = [
            Parameter('trigger-mode',
                      lower=FileCamera.TRIGGER_AUTO,
                      upper=FileCamera.TRIGGER_SOFTWARE)
        ]
        super(FileCamera, self).__init__(params)

        self._frame_rate = None
        self._recording = None
        self._index = -1
        self._start_time = None
        self._stop_time = None
        self._trigger = Event()
        self._trigger_time = None
        self.roi_x = 0
        self.roi_y = 0
        self.roi_width = None
        self.roi_height = None
        self._files = [
            os.path.join(directory, file_name)
            for file_name in sorted(os.listdir(directory))
        ]
Exemple #8
0
class SpiralMixin(Parameterizable):
    """
    Mixin for spiral tomography.
    """
    start_position_vertical = Quantity(q.mm)
    """Initial position of the vertical motor."""

    vertical_shift_per_tomogram = Quantity(q.mm)
    """Vertical shift per tomogram."""

    sample_height = Quantity(q.mm)
    """Height of the sample. *vertical_motor* will be scanned from *start_position_vertical* to
    *sample_height* + *vertical_shift_per_tomogram* to sample the whole specimen.
    """

    num_tomograms = Parameter()
    """Number of tomograms, that are required to cover the whole specimen."""
    def __init__(self, vertical_motor, start_position_vertical, sample_height,
                 vertical_shift_per_tomogram):
        """
        :param vertical_motor: LinearMotor to translate the sample along the tomographic axis.
        :type vertical_motor: concert.devices.motors.base.LinearMotor
        :param start_position_vertical: Start position of *vertical_motor*.
        :type start_position_vertical: q.mm
        :param sample_height: Height of the sample.
        :type sample_height: q.mm
        :param vertical_shift_per_tomogram: Distance *vertical_motor* is translated during one
            *angular_range*.
        :type vertical_shift_per_tomogram: q.mm
        """
        self._start_position_vertical = start_position_vertical
        self._vertical_shift_per_tomogram = vertical_shift_per_tomogram
        self._sample_height = sample_height
        self._vertical_motor = vertical_motor
        Parameterizable.__init__(self)

    async def _get_start_position_vertical(self):
        return self._start_position_vertical

    async def _get_num_tomograms(self):
        shift = await self.get_vertical_shift_per_tomogram()
        height = await self.get_sample_height()

        return abs(height / shift).to_base_units().magnitude + 1

    async def _get_vertical_shift_per_tomogram(self):
        return self._vertical_shift_per_tomogram

    async def _get_sample_height(self):
        return self._sample_height

    async def _set_start_position_vertical(self, position):
        self._start_position_vertical = position

    async def _set_sample_height(self, height):
        self._sample_height = height

    async def _set_vertical_shift_per_tomogram(self, shift):
        self._vertical_shift_per_tomogram = shift
Exemple #9
0
class MockDevice(Device):

    readonly = Parameter()
    writeonly = Parameter()

    def __init__(self):
        super(MockDevice, self).__init__()
        self.aborted = False

    async def _get_readonly(self):
        return 1

    async def _set_writeonly(self, value):
        pass

    async def _emergency_stop(self):
        self.aborted = True
Exemple #10
0
    def __init__(self):
        params = [
            Parameter('current',
                      fget=self._get_current,
                      unit=q.mA,
                      doc="Current of the ring"),
            Parameter('energy',
                      fget=self._get_energy,
                      unit=q.MeV,
                      doc="Energy of the ring"),
            Parameter('lifetime',
                      fget=self._get_lifetime,
                      unit=q.h,
                      doc="Lifetime of the ring")
        ]

        super(StorageRing, self).__init__(params)
Exemple #11
0
    def test_write_only_parameter(self):
        parameter = Parameter('foo', fset=empty_setter)
        self.assertTrue(parameter.is_writable())
        self.assertFalse(parameter.is_readable())

        parameter.set(1).result()

        with self.assertRaises(ReadAccessError) as ctx:
            parameter.get().result()

        self.assertEqual("parameter `foo' cannot be read", str(ctx.exception))
Exemple #12
0
    def __init__(self, parameters=None):
        # We have to create the lock early on because it will be accessed in
        # any add_parameter calls, especially those in the Parameterizable base
        # class
        self._lock = threading.Lock()

        super(Device, self).__init__(parameters)
        self.add_parameter(Parameter('state', self._get_state))
        self._states = set([self.NA])
        self._state = self.NA
Exemple #13
0
 def __init__(self, calibration):
     params = [
         Parameter('flow_rate',
                   fget=self._get_calibrated_flow_rate,
                   fset=self._set_calibrated_flow_rate,
                   unit=q.l / q.s,
                   doc="Pump flow rate.")
     ]
     super(Pump, self).__init__(params)
     self._calibration = calibration
     self._states = self._states.union(set([self.STANDBY, self.PUMPING]))
Exemple #14
0
    def test_hard_limit(self):
        def setter(value):
            pass

        class Limited(object):
            in_limit = False

            def __call__(self):
                return self.in_limit

        in_limit = Limited()

        parameter = Parameter('foo', fset=setter, in_hard_limit=in_limit)
        parameter.set(0).result()
        parameter.set(0.5).result()
        parameter.set(1).result()

        with self.assertRaises(HardLimitError):
            in_limit.in_limit = True
            parameter.set(1.5).result()
Exemple #15
0
class PyplotImageViewer(ImageViewerBase):

    """Dynamic image viewer using matplotlib.

    .. py:attribute:: imshow_kwargs

        matplotlib's imshow keyword arguments

    .. py:attribute:: fast

        Whether to use the fast version without colorbar

    """

    colormap = Parameter(help='Colormap')

    def __init__(self, imshow_kwargs: dict = None, fast: bool = True, limits: str = 'stream',
                 downsampling: int = 1, title: str = "", show_refresh_rate: bool = False,
                 force: bool = False):
        super().__init__(limits=limits, downsampling=downsampling, title=title,
                         show_refresh_rate=show_refresh_rate, force=force)
        self._has_colorbar = not fast
        self._imshow_kwargs = {} if imshow_kwargs is None else imshow_kwargs
        self._make_imshow_defaults()

    def _make_updater(self):
        if self._has_colorbar:
            return _PyplotImageUpdater(
                self._queue, self._imshow_kwargs, limits=self._limits, title=self._title,
                show_refresh_rate=self._show_refresh_rate
            )
        return _SimplePyplotImageUpdater(
            self._queue, self._imshow_kwargs, limits=self._limits, title=self._title,
            show_refresh_rate=self._show_refresh_rate
        )

    def reset(self):
        """Reset the viewer's state."""
        self._queue.put(('reset', None))

    def _make_imshow_defaults(self):
        """Override matplotlib's image showing defafults."""
        if "cmap" not in self._imshow_kwargs:
            self._imshow_kwargs["cmap"] = 'gray'
        self._colormap = self._imshow_kwargs["cmap"]
        if "interpolation" not in self._imshow_kwargs:
            self._imshow_kwargs["interpolation"] = "nearest"

    async def _get_colormap(self):
        return self._colormap

    async def _set_colormap(self, colormap):
        """Set colormp of the shown image to *colormap*."""
        self._queue.put(('colormap', colormap))
Exemple #16
0
        def __init__(self, pos=(0., 0.), imgsize=(640, 480)):
            params = [
                Parameter('exposure-time', unit=q.s),
                Parameter('roi-width'),
                Parameter('roi-height'),
                Parameter('sensor-pixel-width', unit=q.micrometer),
                Parameter('sensor-pixel-height', unit=q.micrometer)
            ]
            super(DummyCamera, self).__init__(params)
            self.exposure_time = 1 * q.ms
            self.sensor_pixel_width = 5 * q.micrometer
            self.sensor_pixel_height = 5 * q.micrometer
            self._sigma = 10
            self._rel_sec = 1.
            self._m_ax = [1, 0]
            self._imgsize = imgsize
            self._noise = imgnoise
            self._frm_mode = 'generate'

            self._coord = [pos[0] * q.meter, pos[1] * q.meter]
            self._coord_at_trigger = [pos[0] * q.meter, pos[1] * q.meter]
Exemple #17
0
    def __init__(self, camera, rotary_stage, prepare_dark_scan,
                 prepare_flat_scan, prepare_proj_scan):

        params = [Parameter('angle', unit=q.deg)]

        self.camera = camera
        self.rotary_stage = rotary_stage
        self.prepare_dark_scan = prepare_dark_scan
        self.prepare_flat_scan = prepare_flat_scan
        self.prepare_proj_scan = prepare_proj_scan
        self.num_projections = 4

        super(StepTomoScanner, self).__init__(params)
Exemple #18
0
        def _create_parameter(node, prop):
            from gi.repository import GObject

            getter, setter = None, None

            if prop.flags & GObject.ParamFlags.READABLE:
                getter = _create_getter(node, prop.name)

            if prop.flags & GObject.ParamFlags.WRITABLE:
                setter = _create_setter(node, prop.name)

            param = Parameter(prop.name, getter, setter, doc=prop.blurb)
            return param
Exemple #19
0
    def test_soft_limit(self):
        parameter = Parameter('foo',
                              fset=empty_setter,
                              unit=q.mm,
                              lower=2 * q.mm,
                              upper=4 * q.mm)

        parameter.set(2.3 * q.mm).wait()
        parameter.set(2 * q.mm).wait()
        parameter.set(4 * q.mm).wait()

        self.assertRaises(SoftLimitError, parameter.set(4.2 * q.mm).wait)
Exemple #20
0
    def __init__(self, background=None):
        params = [
            Parameter('exposure-time', unit=q.s),
            Parameter('roi-width'),
            Parameter('roi-height'),
            Parameter('sensor-pixel-width', unit=q.micrometer),
            Parameter('sensor-pixel-height', unit=q.micrometer)
        ]

        super(Camera, self).__init__(params)

        self.exposure_time = 1 * q.ms
        self.sensor_pixel_width = 5 * q.micrometer
        self.sensor_pixel_height = 5 * q.micrometer

        if background:
            self.roi_width = background.shape[1]
            self.roi_height = background.shape[0]
            self._background = background
        else:
            shape = (640, 480)
            self.roi_width, self.roi_height = shape
            self._background = np.ones(shape)
Exemple #21
0
    def test_read_only_parameter(self):
        def getter():
            return 0

        parameter = Parameter('foo', fget=getter)
        self.assertTrue(parameter.is_readable())
        self.assertFalse(parameter.is_writable())

        self.assertEqual(parameter.get().result(), 0)

        with self.assertRaises(WriteAccessError) as ctx:
            parameter.set(None).result()

        self.assertEqual("parameter `foo' cannot be written",
                         str(ctx.exception))
Exemple #22
0
class DummyDevice(Device):
    """A dummy device."""

    value = Parameter()

    def __init__(self):
        super(DummyDevice, self).__init__()
        self._value = None

    def _get_value(self):
        """Get the real value."""
        return self._value

    def _set_value(self, value):
        """The real value setter."""
        self._value = value

    @async
    def do_nothing(self):
        """Do nothing."""
        pass
Exemple #23
0
    def __init__(self, name):
        from gi.repository import GObject, Uca

        self._manager = Uca.PluginManager()
        self._data = None
        self._array = None

        try:
            self.camera = self._manager.get_camerav(name, [])
        except:
            raise ValueError("`{0}' is not a valid camera".format(name))

        units = {
            Uca.Unit.METER: q.m,
            Uca.Unit.SECOND: q.s,
            Uca.Unit.DEGREE_CELSIUS: q.Celsius,
            Uca.Unit.COUNT: q.count
        }

        parameters = []

        for prop in self.camera.props:
            getter, setter, unit = None, None, None

            uca_unit = self.camera.get_unit(prop.name)

            if uca_unit in units:
                unit = units[uca_unit]

            if prop.flags & GObject.ParamFlags.READABLE:
                getter = _new_getter_wrapper(self.camera, prop.name, unit)

            if prop.flags & GObject.ParamFlags.WRITABLE:
                setter = _new_setter_wrapper(self.camera, prop.name, unit)

            parameter = Parameter(prop.name, getter, setter, unit)
            parameters.append(parameter)

        super(Camera, self).__init__(parameters)
Exemple #24
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)
Exemple #25
0
class Camera(Device):
    """Base class for remotely controllable cameras.

    .. py:attribute:: frame-rate

        Frame rate of acquisition in q.count per time unit.
    """

    trigger_sources = Bunch(['AUTO', 'SOFTWARE', 'EXTERNAL'])
    trigger_types = Bunch(['EDGE', 'LEVEL'])
    state = State(default='standby')
    frame_rate = Quantity(1 / q.second, help="Frame frequency")
    trigger_source = Parameter(help="Trigger source")

    def __init__(self):
        super(Camera, self).__init__()
        self.convert = identity

    @background
    @check(source='standby', target='recording')
    async def start_recording(self):
        """
        start_recording()

        Start recording frames.
        """
        await self._record_real()

    @background
    @check(source='recording', target='standby')
    async def stop_recording(self):
        """
        stop_recording()

        Stop recording frames.
        """
        await self._stop_real()

    @contextlib.asynccontextmanager
    async def recording(self):
        """
        recording()

        A context manager for starting and stopping the camera.

        In general it is used with the ``async with`` keyword like this::

            async with camera.recording():
                frame = await camera.grab()
        """
        await self.start_recording()
        try:
            yield
        finally:
            LOG.log(AIODEBUG, 'stop recording in recording()')
            await self.stop_recording()

    @background
    async def trigger(self):
        """Trigger a frame if possible."""
        await self._trigger_real()

    @background
    async def grab(self):
        """Return a NumPy array with data of the current frame."""
        return self.convert(await self._grab_real())

    async def stream(self):
        """
        stream()

        Grab frames continuously yield them. This is an async generator.
        """
        await self.set_trigger_source(self.trigger_sources.AUTO)
        await self.start_recording()

        while await self.get_state() == 'recording':
            yield await self.grab()

    async def _get_trigger_source(self):
        raise AccessorNotImplementedError

    async def _set_trigger_source(self, source):
        raise AccessorNotImplementedError

    async def _record_real(self):
        raise AccessorNotImplementedError

    async def _stop_real(self):
        raise AccessorNotImplementedError

    async def _trigger_real(self):
        raise AccessorNotImplementedError

    async def _grab_real(self):
        raise AccessorNotImplementedError
Exemple #26
0
    def __init__(self, name, params=None):
        """
        Create a new libuca camera.

        The *name* is passed to the uca plugin manager.

        :raises CameraError: In case camera *name* does not exist.
        """

        super(Camera, self).__init__()

        import gi
        gi.require_version('Uca', '2.0')

        from gi.repository import GObject, GLib, Uca

        self._manager = Uca.PluginManager()

        params = params if params else {}

        try:
            self.uca = self._manager.get_camerah(name, params)
        except GLib.GError as ge:
            raise base.CameraError(str(ge))
        except Exception:
            raise base.CameraError("`{0}' is not a valid camera".format(name))

        units = {
            Uca.Unit.METER: q.m,
            Uca.Unit.SECOND: q.s,
            Uca.Unit.DEGREE_CELSIUS: q.celsius,
            Uca.Unit.COUNT: q.dimensionless,
            Uca.Unit.PIXEL: q.pixel,
        }

        parameters = {}

        for prop in self.uca.props:
            if prop.name in ('trigger-source', 'trigger-type',
                             'frames-per-second'):
                continue

            getter, setter, unit = None, None, None

            uca_unit = self.uca.get_unit(prop.name)

            if uca_unit in units:
                unit = units[uca_unit]

            if prop.flags & GObject.ParamFlags.READABLE:
                getter = _new_getter_wrapper(prop.name, unit)

            if prop.flags & GObject.ParamFlags.WRITABLE:
                setter = _new_setter_wrapper(prop.name, unit)

            name = prop.name.replace('-', '_')

            if uca_unit in units:
                parameters[name] = Quantity(unit,
                                            fget=getter,
                                            fset=setter,
                                            help=prop.blurb)
            else:
                parameters[name] = Parameter(fget=getter,
                                             fset=setter,
                                             help=prop.blurb)

        if parameters:
            self.install_parameters(parameters)

        class _Dummy(object):
            pass

        setattr(self.uca, 'enum_values', _Dummy())

        def get_enum_bunch(enum):
            enum_map = {}

            for key, value in list(enum.__enum_values__.items()):
                name = value.value_nick.upper().replace('-', '_')
                enum_map[name] = key

            return Bunch(enum_map)

        for prop in self.uca.props:
            if hasattr(prop, 'enum_class'):
                setattr(self.uca.enum_values, prop.name.replace('-', '_'),
                        get_enum_bunch(prop.default_value))

        self._uca_get_frame_rate = _new_getter_wrapper('frames-per-second')
        self._uca_set_frame_rate = _new_setter_wrapper('frames-per-second')

        # Invert the uca trigger source dict in order to return concert values
        trigger_dict = self.uca.enum_values.trigger_source.__dict__
        self._uca_to_concert_trigger = {
            v: k
            for k, v in list(trigger_dict.items())
        }
        self._uca_get_trigger = _new_getter_wrapper('trigger-source')
        self._uca_set_trigger = _new_setter_wrapper('trigger-source')

        self._record_shape = None
        self._record_dtype = None
Exemple #27
0
class ViewerBase(Parameterizable):

    """
    A base class for data viewer which sends commands to a backend-specific updater which runs in a
    separate process.
    """

    force = Parameter(help='Make sure every item is displayed')

    def __init__(self, force: bool = False):
        super().__init__()
        self._force = force
        self._queue = _MP_CTX.Queue()
        # This prevents hanging in the case we exit the session after something is put in the queue
        # and before it is consumed.
        self._queue.cancel_join_thread()
        self._paused = False
        # To be set up by an actual implementation which runs the drawing backed in a separate
        # process
        self._proc = None
        # __del__ is not going to help because it's never called from our concert session

    async def _set_force(self, value):
        self._force = value

    async def _get_force(self):
        return self._force

    @background
    async def __call__(self, producer, size=0, force=None):
        """
        Display stream from *producer*. If *size* is specified, stop after displaying *size* items.
        If *force* is True make sure the item is displayed, if False it may be skipped if there is
        something in the queue waiting to be shown, if it is None, the viewer's *force* parameter is
        used.
        """
        i = 0

        async for item in producer:
            if not size or i < size:
                await self.show(item, force=self._force if force is None else force)

            i += 1

    @background
    async def show(self, item, force=False):
        """Push *item* to the queue for display in a separate proces. If *force* is True make sure
        the item is displayed, otherwise it may be skipped if there is something in the queue
        waiting to be shown.
        """
        # If the circumstances allow it, push the item to the queue for display
        # This must happen before instantiation of the updater below because _show may raise
        # exception, in which case we don't want the updater to have started yet.
        if not self._paused and (not self._queue.qsize() or force or not self._proc):
            await run_in_executor(self._show, item)

        # If there is no updater or it has been stopped, instantiate it and start it in a process.
        if not (self._proc and self._proc.is_alive()):
            updater = self._make_updater()
            self._proc = _MP_CTX.Process(target=updater.run, daemon=True)
            self._proc.start()
            # Make sure that all control commands, like changing colormap, have been processed
            while self._queue.qsize():
                await asyncio.sleep(0.01)

    def pause(self):
        """Pause, no images are dispayed but image commands work."""
        self._paused = True

    def resume(self):
        """Resume the viewer."""
        self._paused = False

    def _show(self, item):
        """Implementation of pushing *item* to the display queue."""
        raise NotImplementedError

    def _make_updater(self):
        """Updater factory method."""
        raise NotImplementedError
Exemple #28
0
class DummyDevice(Device):
    """A dummy device."""

    position = Quantity(unit=q.mm)
    sleep_time = Quantity(unit=q.s)
    # Value with a check-decorated setter before it is bound to instance, so still a function
    value = Parameter()
    # value with check wrapping the bound method
    cvalue = Parameter(check=check(source='*', target='standby'))
    # Value set elsewhere
    evalue = Parameter(fset=set_evalue,
                       fget=get_evalue,
                       check=check(source='*', target='standby'))
    slow = Parameter()
    state = State(default='standby')

    def __init__(self, slow=None):
        super(DummyDevice, self).__init__()
        self._position = 1 * q.mm
        self._value = 0
        self._slow = slow
        self._sleep_time = 0.5 * q.s

    async def _get_sleep_time(self):
        return self._sleep_time

    async def _set_sleep_time(self, value):
        self._sleep_time = value

    async def _get_position(self):
        return self._position

    async def _set_position(self, value):
        self._position = value

    async def _get_slow(self):
        try:
            LOG.log(AIODEBUG, 'get slow start %s', self._slow)
            await asyncio.sleep((await self.get_sleep_time()).magnitude)
            LOG.log(AIODEBUG, 'get slow finish %s', self._slow)
            return self._slow
        except asyncio.CancelledError:
            LOG.log(AIODEBUG, 'get slow cancelled %s', self._slow)
            raise
        except KeyboardInterrupt:
            # do not scream
            LOG.debug("KeyboardInterrupt caught while getting")

    async def _set_slow(self, value):
        try:
            LOG.log(AIODEBUG, 'set slow start %s', value)
            await asyncio.sleep((await self.get_sleep_time()).magnitude)
            LOG.log(AIODEBUG, 'set slow finish %s', value)
            self._slow = value
        except asyncio.CancelledError:
            LOG.log(AIODEBUG, 'set slow cancelled %s', value)
            raise

    async def _get_value(self):
        """Get the real value."""
        return self._value

    async def _get_target_value(self):
        """Get the real value."""
        return self._value + 1

    @check(source='standby', target=['standby', 'hard-limit'])
    @transition(immediate='moving', target='standby')
    async def _set_value(self, value):
        """The real value setter."""
        self._value = value

    async def _get_cvalue(self):
        """The real value setter."""
        return self._value

    async def _set_cvalue(self, value):
        """The real value setter."""
        self._value = value

    @background
    async def do_nothing(self, value=None):
        """Do nothing. For testing task canellation."""
        await self._do_nothing(value=value)

    async def _do_nothing(self, value=None):
        LOG.log(AIODEBUG, f'Start doing nothing: {value}')
        try:
            await asyncio.sleep(1)
            LOG.log(AIODEBUG, f'Stop doing nothing: {value}')
            return value
        except asyncio.CancelledError:
            LOG.log(AIODEBUG, f'Doing nothing cancelled: {value}')
            raise

    async def _emergency_stop(self):
        LOG.debug('Emergency stop on a dummy device')
        await asyncio.sleep(0.5)
        self._state_value = 'aborted'
Exemple #29
0
class ImageViewerBase(ViewerBase):

    """Backend-free base class for displaying images.

    .. py:attribute:: limits

        minimum and maximum gray value (black and white points). Can be a tuple (min, max), 'auto'
        or 'stream'. When 'auto', limits are adjusted for every shown image, when 'stream', limits
        are adjusted on every __call__.

    .. py:attribute:: downsampling

        Display every n-th pixel, which can speed up the viewer

    .. py:attribute:: title

        Image title

    .. py:attribute:: show_refresh_rate

        Whether or not to show refresh rate text directly embedded into the displayed image
    """

    show_refresh_rate = Parameter(help='Display current refresh rate')
    limits = Parameter(help='Black and white point')
    downsampling = Parameter(help='Display only every n-th pixel')

    def __init__(self, limits: str = 'stream', downsampling: int = 1, title: str = "",
                 show_refresh_rate: bool = False, force: bool = False):
        super().__init__(force=force)
        self._show_refresh_rate = show_refresh_rate
        self._title = title
        self._downsampling = downsampling
        self._limits = limits

    @background
    async def __call__(self, producer: Callable, size: int = None, force: bool = None):
        # In case limits are set to 'stream' we need to reset clim
        self._queue.put(('clim', self._limits))
        return await super().__call__(producer, size=None, force=force)

    def _show(self, item):
        self._queue.put(('image', item[::self._downsampling, ::self._downsampling]))

    async def _get_downsampling(self):
        return self._downsampling

    async def _set_downsampling(self, value):
        if value not in range(1, 21):
            raise ValueError('Downsampling must be from interval [1, 20]')
        self._downsampling = value

    async def _get_limits(self):
        return self._limits

    async def _set_limits(self, limits):
        """
        Minimum and maximum gray value (black and white points). Can be a tuple (min, max), 'auto'
        or 'stream'. When 'auto', limits are adjusted for every shown image, when 'stream', limits
        are adjusted on every __call__.
        """
        if not (limits == 'auto' or limits == 'stream' or len(limits) == 2):
            raise ViewerError("limits can be a tuple (min, max), 'auto' or 'stream'")
        self._queue.put(('clim', limits))
        self._limits = limits

    async def _get_show_refresh_rate(self):
        return self._show_refresh_rate

    async def _set_show_refresh_rate(self, value):
        if not isinstance(value, bool):
            raise ValueError('boolean value expected')
        self._queue.put(('show-fps', value))
        self._show_refresh_rate = value
Exemple #30
0
class PyplotViewer(ViewerBase):

    """
    Dynamic plot viewer using matplotlib.

    .. py:attribute:: style

        One of matplotlib's linestyle format strings

    .. py:attribute:: plt_kwargs

        Keyword arguments accepted by matplotlib's plot()

    .. py:attribute:: autoscale

        If True, the axes limits will be expanded as needed by the new data,
        otherwise the user needs to rescale the axes

    .. py:attribute:: title

        Plot title
    """

    style = Parameter(help='Line style')
    autoscale = Parameter(help='Autoscale view')

    def __init__(self, style: str = "o", plot_kwargs: dict = None, autoscale: bool = True,
                 title: str = "", force: bool = False):
        super().__init__(force=force)
        self._autoscale = autoscale
        self._style = style
        self._plot_kwargs = plot_kwargs
        self._title = title

    def _show(self, item):
        """Unravel the *item* for x and y so that it is plotted correctly."""
        try:
            if len(item) != 2:
                raise ValueError('Plotting accepts only (x, y) pairs')
        except TypeError as exc:
            raise ValueError('Plotting accepts only (x, y) pairs') from exc

        if isinstance(item, q.Quantity):
            item = item.magnitude
        first, second = item
        if isinstance(first, q.Quantity):
            first = first.magnitude
        if isinstance(second, q.Quantity):
            second = second.magnitude
        item = (first, second)

        self._queue.put(('plot', item))

    def _make_updater(self):
        return _PyplotUpdater(self._queue, self._style, self._plot_kwargs,
                              self._autoscale, title=self._title)

    def reset(self):
        """Clear the plotted data."""
        self._queue.put(('clear', None))

    async def _get_style(self):
        return self._style

    async def _set_style(self, style):
        """Set line style to *style*."""
        self._queue.put(('style', style))
        self._style = style

    async def _get_autoscale(self):
        return self._autoscale

    async def _set_autoscale(self, autoscale):
        """Set *autoscale* on the axes, can be True or False."""
        self._queue.put(('autoscale', autoscale))
        self._autoscale = autoscale