Esempio n. 1
0
    def create_device(self, setup=None):
        """
        Creates a device object according to the provided setup. If no setup is provided,
        the default setup is used. If the setup can't be found, a LewisException is raised.
        This can also happen if the device type specified in the setup is invalid.

        :param setup: Name of the setup from which to create device.
        :return: Device object initialized according to the provided setup.
        """
        setup_name = setup if setup is not None else 'default'

        if setup_name not in self.setups:
            raise LewisException(
                'Failed to find setup \'{}\' for device \'{}\'. '
                'Available setups are:\n    {}'.format(
                    setup, self.name, '\n    '.join(self.setups.keys())))

        setup_data = self.setups[setup_name]
        device_type = setup_data.get('device_type') or self.default_device_type

        self.log.debug(
            'Trying to create device \'%s\' (setup: %s, device type: %s)',
            self.name, setup_name, device_type.__name__ if device_type else '')

        try:
            return self._create_device_instance(
                device_type, **setup_data.get('parameters', {}))
        except RuntimeError:
            raise LewisException(
                'The setup \'{}\' you tried to load does not specify a valid device type, but the '
                'device module \'{}\' provides multiple device types so that no meaningful '
                'default can be deduced.'.format(setup_name, self.name))
Esempio n. 2
0
    def __init__(self, object_map, connection_string):
        super(ControlServer, self).__init__()

        try:
            host, port = connection_string.split(":")
        except ValueError:
            raise LewisException(
                "'{}' is not a valid control server initialization string. "
                'A string of the form "host:port" is expected.'.format(
                    connection_string))

        try:
            self.host = socket.gethostbyname(host)
        except socket.gaierror:
            raise LewisException(
                "Could not resolve control server host: {}".format(host))

        self.port = port

        if isinstance(object_map, ExposedObject):
            self._exposed_object = object_map
        else:
            self._exposed_object = ExposedObjectCollection(object_map)

        self._socket = None
Esempio n. 3
0
    def device_builder(self, name, strict_versions=None):
        """
        Returns a :class:`DeviceBuilder` instance that can be used to create device objects
        based on setups, as well as device interfaces. If the device name is not stored
        in the internal map, a LewisException is raised.

        Each DeviceBuilder has a ``framework_version``-member, which specifies the version
        of Lewis the device has been written for. If the version does not match the current
        framework version, it is only possible to obtain those device builders calling the
        method with ``strict_versions`` set to ``False``, otherwise a
        :class:`~lewis.core.exceptions.LewisException` is raised. A warning message is logged
        in all cases. If ``framework_version`` is ``None`` (e.g. not specified at all), it
        is accepted unless ``strict_versions`` is set to ``True``.

        :param name: Name of the device.
        :param strict_versions: If ``True`` or ``None``, raise an exception when version of device
                                does not match framework version.
        :return: :class:`DeviceBuilder`-object for requested device.
        """
        try:
            builder = self._devices[name]

            compatible = is_compatible_with_framework(
                builder.framework_version)

            if not compatible:
                self.log.warning(
                    'Device \'%s\' is specified for a different framework version '
                    '(required: %s, current: %s). This means that the device might not work '
                    'as expected. Contact the device author about updating the device or use a '
                    'different version of lewis to run this device.',
                    builder.name, builder.framework_version, __version__)

                if strict_versions or (compatible is not None
                                       and strict_versions is None):
                    raise LewisException(
                        'Not loading device \'{}\' with different framework version '
                        '(required: {}, current: {}) in strict mode. Use the --ignore-versions '
                        'option of lewis to load the device anyway.'.format(
                            builder.name, builder.framework_version,
                            __version__))

            return builder

        except KeyError:
            raise LewisException(
                'No device with the name \'{}\' could be found. '
                'Possible names are:\n    {}\n'
                'See also the -k option to add inspect a different module.'.
                format(name, '\n    '.join(self.devices)))
Esempio n. 4
0
    def device_builder(self, name):
        """
        Returns a :class:`DeviceBuilder` instance that can be used to create device objects
        based on setups, as well as device interfaces. If the device name is not stored
        in the internal map, a LewisException is raised.

        Each DeviceBuilder has a ``framework_version``-member, which specifies the version
        of Lewis the device has been written for. If the version does not match the current
        framework version, it is only possible to obtain those device builders calling the
        method with ``strict_versions`` set to ``False``, otherwise a
        :class:`~lewis.core.exceptions.LewisException` is raised. A warning message is logged
        in all cases. If ``framework_version`` is ``None`` (e.g. not specified at all), it
        is accepted unless ``strict_versions`` is set to ``True``.

        :param name: Name of the device.
        :return: :class:`DeviceBuilder`-object for requested device.
        """
        try:
            return self._devices[name]
        except KeyError:
            raise LewisException(
                "No device with the name '{}' could be found. "
                "Possible names are:\n    {}\n"
                "See also the -k option to add inspect a different module.".format(
                    name, "\n    ".join(self.devices)
                )
            )
Esempio n. 5
0
    def _bind_device(self):
        """
        This method transforms the ``self.pvs`` dict of :class:`PV` objects ``self.bound_pvs``,
        a dict of :class:`BoundPV` objects, the keys are always the PV-names that are exposed
        via ChannelAccess.

        In the transformation process, the method tries to find whether the attribute specified by
        PV's ``property`` (and ``meta_data_property``) is part of the internally stored device
        or the interface and constructs a BoundPV, which acts as a forwarder to the appropriate
        objects.
        """
        self.bound_pvs = {}

        for pv_name, pv in self.pvs.items():
            try:
                self.bound_pvs[pv_name] = pv.bind(self, self.device)
            except (AttributeError, RuntimeError) as e:
                self.log.debug(
                    "An exception was caught during the binding step of PV '%s'.",
                    pv_name,
                    exc_info=e,
                )
                raise LewisException(
                    "The binding step for PV '{}' failed, please check the interface-"
                    "definition or contact the device author. More information is "
                    "available with debug-level logging (-o debug).".format(
                        pv_name))
Esempio n. 6
0
def parse_adapter_options(raw_adapter_options):
    if not raw_adapter_options:
        return {None: {}}

    protocols = {}

    for option_string in raw_adapter_options:
        try:
            adapter_options = yaml.load(option_string)
        except yaml.YAMLError:
            raise LewisException(
                'It was not possible to parse this adapter option specification:\n'
                '    %s\n'
                'Correct formats for the -p argument are:\n'
                '    -p protocol\n'
                '    -p "protocol: {option: \'val\', option2: 34}"\n'
                'The spaces after the colons are significant!' % option_string)

        if isinstance(adapter_options, string_types):
            protocols[adapter_options] = {}
        else:
            protocol = list(adapter_options.keys())[0]
            protocols[protocol] = adapter_options.get(protocol, {})

    return protocols
Esempio n. 7
0
    def __init__(self, device_module):
        try:
            self._device_module = importlib.import_module(device_module)
        except ImportError:
            raise LewisException(
                'Failed to import module \'{}\' for device discovery. '
                'Make sure that it is in the PYTHONPATH.\n'
                'See also the -a option of lewis.'.format(device_module))

        self._devices = {name: DeviceBuilder(module) for name, module in
                         get_submodules(self._device_module).items()}
Esempio n. 8
0
    def __init__(self, module, exception=None):
        self._module = module

        if exception is None:
            exception = ("The optional dependency '{}' is required for the "
                         "functionality you tried to use.".format(
                             self._module))

        if isinstance(exception, str):
            exception = LewisException(exception)

        if not isinstance(exception, BaseException):
            raise RuntimeError(
                "The exception parameter has to be either a string or a an instance of an "
                "exception type (derived from BaseException).")

        self._exception = exception
Esempio n. 9
0
    def device_builder(self, name):
        """
        Returns a :class:`DeviceBuilder` instance that can be used to create device objects
        based on setups, as well as device interfaces. If the device name is not stored
        in the internal map, a LewisException is raised.

        :param name: Name of the device.
        :return: :class:`DeviceBuilder`-object for requested device.
        """
        try:
            return self._devices[name]
        except KeyError:
            raise LewisException(
                'No device with the name \'{}\' could be found. '
                'Possible names are:\n    {}\n'
                'See also the -k option to add inspect a different module.'.format(
                    name, '\n    '.join(self.devices)))
Esempio n. 10
0
    def _start_server(self, adapter):
        if adapter.protocol not in self._threads:
            self.log.info("Connecting device interface for protocol '%s'",
                          adapter.protocol)

            adapter_thread = threading.Thread(target=self._adapter_loop,
                                              args=(adapter, 0.01))
            adapter_thread.daemon = True

            self._threads[adapter.protocol] = adapter_thread
            self._running[adapter.protocol] = threading.Event()

            adapter_thread.start()

            # Block until server is actually listening
            self._running[adapter.protocol].wait(2.0)
            if not self._running[adapter.protocol].is_set():
                raise LewisException("Adapter for '%s' failed to start!" %
                                     adapter.protocol)
Esempio n. 11
0
    def __init__(self, device_module):
        try:
            self._device_module = importlib.import_module(device_module)
        except ImportError:
            raise LewisException(
                "Failed to import module '{}' for device discovery. "
                "Make sure that it is in the PYTHONPATH.\n"
                "See also the -a option of lewis.".format(device_module))

        self._devices = {
            name: DeviceBuilder(module)
            for name, module in get_submodules(self._device_module).items()
        }

        self.log.debug(
            "Devices loaded from '%s': %s",
            device_module,
            ", ".join(self._devices.keys()),
        )
Esempio n. 12
0
    def __init__(self, options=None):
        super(Adapter, self).__init__()
        self._interface = None

        self.device_lock = NoLock()

        options = options or {}
        combined_options = dict(self.default_options)

        try:
            dict_strict_update(combined_options, options)
        except RuntimeError as e:
            raise LewisException(
                "Invalid options found: {}. Valid options are: {}".format(
                    ", ".join(e.args[1]),
                    ", ".join(self.default_options.keys())))

        options_type = namedtuple("adapter_options",
                                  list(combined_options.keys()))
        self._options = options_type(**combined_options)
Esempio n. 13
0
    def create_interface(self, protocol=None, *args, **kwargs):
        """
        Returns an interface that implements the provided protocol. If the protocol is not
        known, a LewisException is raised. All additional arguments are forwarded
        to the interface constructor (see :class:`~lewis.adapters.Adapter` for details).

        :param protocol: Protocol which the interface must implement.
        :param args: Positional arguments that are passed on to the interface.
        :param kwargs: Keyword arguments that are passed on to the interface.
        :return: Instance of the interface type.
        """
        protocol = protocol if protocol is not None else self.default_protocol

        try:
            return self.interfaces[protocol](*args, **kwargs)
        except KeyError:
            raise LewisException(
                'Failed to find protocol \'{}\' for device \'{}\'. '
                'Available protocols are: \n    {}'.format(
                    protocol, self.name, '\n    {}'.join(self.interfaces.keys())))
Esempio n. 14
0
    def create_interface(self, protocol=None, *args, **kwargs):
        """
        Returns an interface that implements the provided protocol. If the protocol is not
        known, a LewisException is raised. All additional arguments are forwarded
        to the interface constructor (see :class:`~lewis.adapters.Adapter` for details).

        :param protocol: Protocol which the interface must implement.
        :param args: Positional arguments that are passed on to the interface.
        :param kwargs: Keyword arguments that are passed on to the interface.
        :return: Instance of the interface type.
        """
        protocol = protocol if protocol is not None else self.default_protocol

        self.log.debug("Trying to create interface for protocol '%s'", protocol)

        try:
            return self.interfaces[protocol](*args, **kwargs)
        except KeyError:
            raise LewisException(
                "'{}' is not a valid protocol for device '{}', select one via the -p option.\n"
                "Available protocols are: \n    {}".format(
                    protocol, self.name, "\n    ".join(self.interfaces.keys())
                )
            )
Esempio n. 15
0
from lewis.core.devices import InterfaceBase
from lewis.core.exceptions import (
    AccessViolationException,
    LewisException,
    LimitViolationException,
)
from lewis.core.logging import has_log
from lewis.core.utils import FromOptionalDependency, format_doc_text, seconds_since

# pcaspy might not be available. To make EPICS-based adapters show up
# in the listed adapters anyway dummy types are created in this case
# and the failure is postponed to runtime, where a more appropriate
# LewisException can be raised.
missing_pcaspy_exception = LewisException(
    "In order to use EPICS-interfaces, pcaspy must be installed:\n"
    "\tpip install pcaspy\n"
    "A fully working installation of EPICS-base is required for this package. "
    "Please refer to the documentation for advice.")

Driver, SimpleServer = FromOptionalDependency(
    "pcaspy", missing_pcaspy_exception).do_import("Driver", "SimpleServer")

pcaspy_manager = FromOptionalDependency(
    "pcaspy.driver", missing_pcaspy_exception).do_import("manager")


class BoundPV:
    """
    Class to represent PVs that are bound to an adapter

    This class is very similar to :class:`~lewis.adapters.stream.Func`, in that