Example #1
0
    def doPreinit(self, mode):
        tango.TemperatureController.doPreinit(self, mode)

        if mode != SIMULATION:
            self._controller = self._createPyTangoDevice(self.controller)
        else:
            self._controller = HardwareStub(self)
Example #2
0
 def doPreinit(self, mode):
     if self.loglevel == 'debug':
         self._taco_guard = self._taco_guard_log
     if self.taco_class is None:
         raise ProgrammingError('missing taco_class attribute in class ' +
                                self.__class__.__name__)
     if mode != SIMULATION:
         self._dev = self._create_client()
     else:
         self._dev = HardwareStub(self)
Example #3
0
 def _setMode(self, mode):
     super()._setMode(mode)
     # remove the TACO device on entering simulation mode, to prevent
     # accidental access to the hardware
     if mode == SIMULATION:
         # keep the device instance around to avoid destruction (which can
         # mess with the TACO connections in the main process if simulation
         # has been forked off)
         self._orig_dev = self._dev
         self._dev = HardwareStub(self)
Example #4
0
 def doInit(self, mode):
     NamedDigitalOutput.doInit(self, mode)
     # Don't create PyTango device in simulation mode
     if mode != SIMULATION:
         self._remote = self._createPyTangoDevice(devname=self.remote)
         self._readback = self._createPyTangoDevice(devname=self.readback)
         self._error = self._createPyTangoDevice(devname=self.error)
         self._sleeptime = 0.1
     else:
         self._remote = HardwareStub(self)
         self._readback = HardwareStub(self)
         self._error = HardwareStub(self)
Example #5
0
    def doPreinit(self, mode):
        # Wrap PyTango client creation (so even for the ctor, logging and
        # exception mapping is enabled).
        self._createPyTangoDevice = self._applyGuardToFunc(
            self._createPyTangoDevice, 'constructor')

        self._dev = None

        # Don't create PyTango device in simulation mode
        if mode != SIMULATION:
            self._dev = self._createPyTangoDevice(self.tangodevice)
        else:
            self._dev = HardwareStub(self)
Example #6
0
 def _setMode(self, mode):
     super(EpicsDevice, self)._setMode(mode)
     # remove the PVs on entering simulation mode, to prevent
     # accidental access to the hardware
     if mode == SIMULATION:
         for key in self._pvs:
             self._pvs[key] = HardwareStub(self)
Example #7
0
    def doPreinit(self, mode):
        # Don't create PVs in simulation mode
        self._pvs = {}
        self._pvctrls = {}
        if mode != SIMULATION:
            # in case we get started in a thread, make sure to use the global
            # CA context in that thread
            if epics.ca.current_context() is None:
                epics.ca.use_initial_context()

            # When there are standard names for PVs (see motor record), the PV
            # names may be derived from some prefix. To make this more flexible,
            # the pv_parameters are obtained via a method that can be overridden
            # in subclasses.
            pv_parameters = self._get_pv_parameters()
            for pvparam in pv_parameters:

                # Retrieve the actual PV-name from (potentially overridden) method
                pvname = self._get_pv_name(pvparam)
                if not pvname:
                    raise ConfigurationError(self, 'PV for parameter %s was '
                                                   'not found!' % pvparam)
                pv = self._pvs[pvparam] = epics.pv.PV(
                    pvname, connection_timeout=self.epicstimeout)
                pv.connect()
                if not pv.wait_for_connection(timeout=self.epicstimeout):
                    raise CommunicationError(self, 'could not connect to PV %r'
                                             % pvname)

                self._pvctrls[pvparam] = pv.get_ctrlvars() or {}
        else:
            for pvparam in self._get_pv_parameters():
                self._pvs[pvparam] = HardwareStub(self)
                self._pvctrls[pvparam] = {}
Example #8
0
class PressureController(tango.TemperatureController):
    """Device to be able to set the pressure manually.
    Pressure is set via the controller, which is supposed to handle the limits
    within which setting pressure is allowed.
    """

    parameters = {
        'controller':
        Param('SEController device name',
              type=tangodev,
              mandatory=True,
              preinit=True),
        'pressuretolerance':
        Param('Tolerance for the adjustment of the '
              'pressure',
              settable=True,
              volatile=True)
    }

    def doPreinit(self, mode):
        tango.TemperatureController.doPreinit(self, mode)

        if mode != SIMULATION:
            self._controller = self._createPyTangoDevice(self.controller)
        else:
            self._controller = HardwareStub(self)

    def doStart(self, value):
        self._controller.setPressure(value)

    def doStop(self):
        pass

    def doReadPressuretolerance(self):
        return self._dev.pressure_tolerance

    def doWritePressuretolerance(self, value):
        self._dev.pressure_tolerance = value
Example #9
0
    def doPreinit(self, mode):
        self._epics_wrapper = PvaWrapper()

        # Don't create PVs in simulation mode
        self._pvs = {}

        if mode != SIMULATION:
            for pvparam in self._get_pv_parameters():
                # Retrieve the actual PV name
                pvname = self._get_pv_name(pvparam)
                if not pvname:
                    raise ConfigurationError(
                        self, 'PV for parameter %s was '
                        'not found!' % pvparam)
                # Check pv exists - throws if cannot connect
                self._epics_wrapper.connect_pv(pvname, self.epicstimeout)
                self._pvs[pvparam] = pvname
            self._register_pv_callbacks()
        else:
            for pvparam in self._get_pv_parameters():
                self._pvs[pvparam] = HardwareStub(self)
Example #10
0
    def doPreinit(self, mode):
        # Don't create PVs in simulation mode
        self._pvs = {}
        self._pvctrls = {}

        if mode != SIMULATION:
            # When there are standard names for PVs (see motor record), the PV names
            # may be derived from some prefix. To make this more flexible, the pv_parameters
            # are obtained via a method that can be overridden in subclasses.
            pv_parameters = self._get_pv_parameters()

            # For cases where for example readpv and writepv are the same, this dict makes
            # sure that only one Channel object is created per PV.
            pv_names = {}

            for pvparam in pv_parameters:
                # Retrieve the actual PV-name from (potentially overridden) method
                pv_name = self._get_pv_name(pvparam)

                try:
                    pv = pv_names.setdefault(pv_name, self._create_pv(pv_name))
                    self._pvs[pvparam] = pv

                    pv.setTimeout(self.epicstimeout)

                    self._pvctrls[pvparam] = pv.get('display').toDict().get('display')
                    if self._pvctrls[pvparam] is None:
                        self._pvctrls[pvparam] = pv.get('control').toDict().get('control', {})

                except pvaccess.PvaException:
                    raise CommunicationError(self, 'could not connect to PV %r'
                                             % pv_name)
        else:
            for pvparam in self._get_pv_parameters():
                self._pvs[pvparam] = HardwareStub(self)
                self._pvctrls[pvparam] = {}
Example #11
0
class TacoDevice(HasCommunication):
    """Mixin class for TACO devices.

    Use it in concrete device classes like this::

        class Counter(TacoDevice, Measurable):
            taco_class = IO.Counter

            # more overwritten methods

    i.e., put TacoDevice first in the base class list.

    TacoDevice provides the following methods already:

    * `.doVersion` (returns TACO device version)
    * `.doPreinit` (creates the TACO device from the `tacodevice` parameter)
    * `.doRead` (reads the TACO device)
    * `.doStatus` (returns status.OK for ON and DEVICE_NORMAL, ERROR otherwise)
    * `.doReset` (resets the TACO device)
    * `.doReadUnit` (reads the unit parameter from the TACO device if not
      configured in setup)

    You can however override them and provide your own specialized
    implementation.

    TacoDevice subclasses will automatically log all calls to TACO if their
    loglevel is DEBUG.

    TacoDevice also has the following class attributes, which can be overridden
    in derived classes:

    * `taco_class` -- the Python class to use for the TACO client
    * `taco_resetok` -- a boolean value indicating if the device can be reset
      during connection if it is in error state
    * `taco_errorcodes` -- a dictionary mapping TACO error codes to NICOS
      exception classes

    The following utility methods are provided:

    .. automethod:: _taco_guard
    .. automethod:: _taco_update_resource
    .. automethod:: _create_client
    """

    parameters = {
        'tacodevice':
        Param('TACO device name', type=tacodev, mandatory=True, preinit=True),
        'tacotimeout':
        Param('TACO network timeout for this process',
              unit='s',
              type=floatrange(0.0, 1200),
              default=3,
              settable=True,
              preinit=True),
    }

    parameter_overrides = {
        # the unit isn't mandatory -- TACO usually knows it already
        'unit': Override(mandatory=False),
    }

    _TACO_STATUS_MAPPING = {
        # OK states
        TACOStates.ON:
        status.OK,
        TACOStates.DEVICE_NORMAL: (status.OK, 'idle'),
        TACOStates.POSITIVE_ENDSTOP: (status.OK, 'limit switch +'),
        TACOStates.NEGATIVE_ENDSTOP: (status.OK, 'limit switch -'),
        TACOStates.STOPPED: (status.OK, 'idle or paused'),
        TACOStates.PRESELECTION_REACHED:
        status.OK,
        TACOStates.DISABLED:
        status.OK,
        # BUSY states
        # explicit ramp string as there seem to be some inconsistencies
        TACOStates.RAMP: (status.BUSY, 'ramping'),
        TACOStates.MOVING:
        status.BUSY,
        TACOStates.STOPPING:
        status.BUSY,
        TACOStates.INIT: (status.BUSY, 'initializing taco device / hardware'),
        TACOStates.RESETTING:
        status.BUSY,
        TACOStates.STOP_REQUESTED:
        status.BUSY,
        TACOStates.COUNTING:
        status.BUSY,
        TACOStates.STARTED:
        status.BUSY,
        # NOTREACHED states
        TACOStates.UNDEFINED:
        status.NOTREACHED,
        # WARN states
        TACOStates.ALARM:
        status.WARN,
        # ERROR states
        TACOStates.FAULT:
        status.ERROR,
        TACOStates.BLOCKED:
        status.ERROR,
        TACOStates.TRIPPED:
        status.ERROR,
        TACOStates.OVERFLOW:
        status.ERROR,
        TACOStates.OFF:
        status.ERROR,
        TACOStates.DEVICE_OFF:
        status.ERROR,
        TACOStates.ON_NOT_REACHED:
        status.ERROR,
    }

    # the TACO client class to instantiate
    taco_class = None
    # whether to call deviceReset() if the initial switch-on fails
    taco_resetok = True
    # additional TACO error codes mapping to Nicos exception classes
    taco_errorcodes = {}
    # TACO device instance
    _dev = None

    def doPreinit(self, mode):
        if self.loglevel == 'debug':
            self._taco_guard = self._taco_guard_log
        if self.taco_class is None:
            raise ProgrammingError('missing taco_class attribute in class ' +
                                   self.__class__.__name__)
        if mode != SIMULATION:
            self._dev = self._create_client()
        else:
            self._dev = HardwareStub(self)

    def doShutdown(self):
        if self._dev:
            self._dev.disconnectClient()
            del self._dev

    def _setMode(self, mode):
        super()._setMode(mode)
        # remove the TACO device on entering simulation mode, to prevent
        # accidental access to the hardware
        if mode == SIMULATION:
            # keep the device instance around to avoid destruction (which can
            # mess with the TACO connections in the main process if simulation
            # has been forked off)
            self._orig_dev = self._dev
            self._dev = HardwareStub(self)

    def doVersion(self):
        return [(self.tacodevice, self._taco_guard(self._dev.deviceVersion))]

    def doRead(self, maxage=0):
        return self._taco_guard(self._dev.read)

    def doStatus(self, maxage=0):
        for i in range(self.comtries or 1):
            if i:
                session.delay(self.comdelay)
            tacoState = self._taco_guard(self._dev.deviceState)
            if tacoState != TACOStates.FAULT:
                break
        state = self._TACO_STATUS_MAPPING.get(tacoState, status.ERROR)

        if isinstance(state, tuple):
            return state

        statusStr = self._taco_guard(self._dev.deviceStatus)
        return (state, statusStr)

    def doReset(self):
        self._taco_reset(self._dev)

    def doReadUnit(self):
        # explicitly configured unit has precendence
        if 'unit' in self._config:
            return self._config['unit']
        if hasattr(self._dev, 'unit'):
            return self._taco_guard(self._dev.unit)
        return self.parameters['unit'].default

    def doWriteUnit(self, value):
        if hasattr(self._dev, 'setUnit'):
            self._taco_guard(self._dev.setUnit, value)
        if 'unit' in self._config:
            if self._config['unit'] != value:
                self.log.warning(
                    'configured unit %r in configuration differs '
                    'from current unit %r', self._config['unit'], value)

    def doUpdateTacotimeout(self, value):
        if not self._sim_intercept and self._dev:
            if value != 3.0:
                self.log.warning(
                    '%r: client network timeout changed to: '
                    '%.2f s', self.tacodevice, value)
            self._taco_guard(self._dev.setClientNetworkTimeout, value)

    def doUpdateLoglevel(self, value):
        super().doUpdateLoglevel(value)
        self._taco_guard = value == 'debug' and self._taco_guard_log or \
            self._taco_guard_nolog

    # internal utilities

    def _create_client(self,
                       devname=None,
                       class_=None,
                       resetok=None,
                       timeout=None):
        """Create a new TACO client to the device given by *devname*, using the
        Python class *class_*.  Initialize the device in a consistent state,
        handling eventual errors.

        If no arguments are given, the values of *devname*, *class_*, *resetok*
        and *timeout* are taken from the class attributes *taco_class* and
        *taco_resetok* as well as the device parameters *tacodevice* and
        *tacotimeout*.  This is done during `.doPreinit`, so that you usually
        don't have to call this method in TacoDevice subclasses.

        You can use this method to create additional TACO clients in a device
        implementation that uses more than one TACO device.
        """
        if devname is None:
            devname = self.tacodevice
        if class_ is None:
            class_ = self.taco_class
        if resetok is None:
            resetok = self.taco_resetok
        if timeout is None:
            timeout = self.tacotimeout

        self.log.debug('creating %s TACO device', class_.__name__)

        try:
            dev = class_(devname)
            self._check_server_running(dev)
        except TACOError as err:
            self._raise_taco(
                err, 'Could not connect to device %r; make sure '
                'the device server is running' % devname)

        try:
            if timeout != 0:
                if timeout != 3.0:
                    self.log.warning(
                        'client network timeout changed to: '
                        '%.2f s', timeout)
                dev.setClientNetworkTimeout(timeout)
        except TACOError as err:
            self.log.warning(
                'Setting TACO network timeout failed: '
                '[TACO %d] %s', err.errcode, err)

        try:
            if dev.isDeviceOff():
                dev.deviceOn()
        except TACOError as err:
            self.log.warning(
                'Switching TACO device %r on failed: '
                '[TACO %d] %s', devname, err.errcode, err)
            try:
                if dev.deviceState() == TACOStates.FAULT:
                    if resetok:
                        dev.deviceReset()
                dev.deviceOn()
            except TACOError as err:
                self._raise_taco(
                    err, 'Switching device %r on after '
                    'reset failed' % devname)

        return dev

    def _check_server_running(self, dev):
        dev.deviceVersion()

    def _taco_guard_log(self, function, *args):
        """Like _taco_guard(), but log the call."""
        self.log.debug('TACO call: %s%r', function.__name__, args)
        if not self._dev:
            raise NicosError(self, 'TACO Device not initialised')
        self._com_lock.acquire()
        try:
            ret = function(*args)
        except TACOError as err:
            # for performance reasons, starting the loop and querying
            # self.comtries only triggers in the error case
            if self.comtries > 1 or err == DevErr_RPCTimedOut:
                tries = 2 if err == DevErr_RPCTimedOut and self.comtries == 1 \
                    else self.comtries - 1
                self.log.warning('TACO %s failed, retrying up to %d times',
                                 function.__name__,
                                 tries,
                                 exc=1)
                while True:
                    session.delay(self.comdelay)
                    tries -= 1
                    try:
                        if self.taco_resetok and \
                           self._dev.deviceState() == TACOStates.FAULT:
                            self._dev.deviceInit()
                            session.delay(self.comdelay)
                        ret = function(*args)
                        self.log.debug('TACO return: %r', ret)
                        return ret
                    except TACOError as err:
                        if tries == 0:
                            break  # and fall through to _raise_taco
                        self.log.warning('TACO %s failed again',
                                         function.__name__,
                                         exc=True)
            self.log.debug('TACO exception: %r', err)
            self._raise_taco(err)
        else:
            self.log.debug('TACO return: %r', ret)
            return ret
        finally:
            self._com_lock.release()

    def _taco_guard_nolog(self, function, *args):
        """Try running the TACO function, and raise a NicosError on exception.

        A more specific NicosError subclass is chosen if appropriate.  For
        example, database-related errors are converted to
        `.CommunicationError`.
        A TacoDevice subclass can add custom error code to exception class
        mappings by using the `.taco_errorcodes` class attribute.

        If the `comtries` parameter is > 1, the call is retried accordingly.
        """
        if not self._dev:
            raise NicosError(self, 'TACO device not initialised')
        self._com_lock.acquire()
        try:
            return function(*args)
        except TACOError as err:
            # for performance reasons, starting the loop and querying
            # self.comtries only triggers in the error case
            if self.comtries > 1 or err == DevErr_RPCTimedOut:
                tries = 2 if err == DevErr_RPCTimedOut and self.comtries == 1 \
                    else self.comtries - 1
                self.log.warning('TACO %s failed, retrying up to %d times',
                                 function.__name__, tries)
                while True:
                    session.delay(self.comdelay)
                    tries -= 1
                    try:
                        return function(*args)
                    except TACOError as err:
                        if tries == 0:
                            break  # and fall through to _raise_taco
            self._raise_taco(err, '%s%r' % (function.__name__, args))
        finally:
            self._com_lock.release()

    _taco_guard = _taco_guard_nolog

    def _taco_update_resource(self, resname, value):
        """Update the TACO resource *resname* to *value* (both must be strings),
        switching the device off and on.
        """
        if not self._dev:
            raise NicosError(self, 'TACO device not initialised')
        self._com_lock.acquire()
        try:
            self.log.debug('TACO resource update: %s %s', resname, value)
            self._dev.deviceOff()
            self._dev.deviceUpdateResource(resname, value)
            self._dev.deviceOn()
            self.log.debug('TACO resource update successful')
        except TACOError as err:
            self._raise_taco(err, 'While updating %s resource' % resname)
        finally:
            self._com_lock.release()

    def _raise_taco(self, err, addmsg=None):
        """Raise a suitable NicosError for a given TACOError instance."""
        tb = sys.exc_info()[2]
        code = err.errcode
        cls = NicosError
        if code in self.taco_errorcodes:
            cls = self.taco_errorcodes[code]
        elif code < 50:
            # error numbers 0-50: RPC call errors
            cls = CommunicationError
        elif 400 <= code < 500:
            # error number 400-499: database system error messages
            cls = CommunicationError
        elif code == DevErr_RangeError:
            cls = LimitError
        elif code in (DevErr_InvalidValue, DevErr_ExecutionDenied):
            cls = InvalidValueError
        elif code in (DevErr_IOError, DevErr_InternalError,
                      DevErr_RuntimeError, DevErr_SystemError):
            cls = CommunicationError
        msg = '[TACO %d] %s' % (err.errcode, err)
        if addmsg is not None:
            msg = addmsg + ': ' + msg
        exc = cls(self, msg, tacoerr=err.errcode)
        raise exc.with_traceback(tb)

    def _taco_reset(self, client, resetcall='deviceReset'):
        try:
            hostname = client.getServerHost()
            servername = client.getServerProcessName()
            personalname = client.getServerPersonalName()
            self.log.info(
                'Resetting TACO device; if this does not help try '
                'restarting the %s named %s on host %s.', servername,
                personalname, hostname)
        except AttributeError:  # older version without these API calls
            self.log.info('Resetting TACO device; if this does not help try '
                          'restarting the server.')
        try:
            if resetcall == 'deviceReset':
                self._taco_guard(client.deviceReset)
            else:
                self._taco_guard(client.deviceInit)
        except Exception as err:
            self.log.warning('%s failed with %s', resetcall, err)
        if self._taco_guard(client.isDeviceOff):
            self._taco_guard(client.deviceOn)
Example #12
0
 def _setMode(self, mode):
     super(PyTangoDevice, self)._setMode(mode)
     # remove the Tango device on entering simulation mode, to prevent
     # accidental access to the hardware
     if mode == SIMULATION:
         self._dev = HardwareStub(self)
Example #13
0
class PyTangoDevice(HasCommunication):
    """
    Basic PyTango device.

    The PyTangoDevice uses an internal PyTango.DeviceProxy but wraps command
    execution and attribute operations with logging and exception mapping.
    """

    hardware_access = True

    parameters = {
        'tangodevice':  Param('Tango device name', type=tangodev,
                              mandatory=True, preinit=True),
        'tangotimeout': Param('TANGO network timeout for this process',
                              unit='s', type=floatrange(0.0, 1200), default=3,
                              settable=True, preinit=True),
    }
    parameter_overrides = {
        'unit': Override(mandatory=False),
    }

    tango_status_mapping = {
        PyTango.DevState.ON:     status.OK,
        PyTango.DevState.ALARM:  status.WARN,
        PyTango.DevState.OFF:    status.DISABLED,
        PyTango.DevState.FAULT:  status.ERROR,
        PyTango.DevState.MOVING: status.BUSY,
    }

    # Since each DeviceProxy leaks a few Python objects, we can't just
    # drop them when the device fails to initialize, and create another one.
    # It is also not required since they reconnect automatically.
    proxy_cache = {}

    def doPreinit(self, mode):
        # Wrap PyTango client creation (so even for the ctor, logging and
        # exception mapping is enabled).
        self._createPyTangoDevice = self._applyGuardToFunc(
            self._createPyTangoDevice, 'constructor')

        self._dev = None

        # Don't create PyTango device in simulation mode
        if mode != SIMULATION:
            self._dev = self._createPyTangoDevice(self.tangodevice)
        else:
            self._dev = HardwareStub(self)

    def doStatus(self, maxage=0):
        # Map Tango state to NICOS status
        nicosState = self.tango_status_mapping.get(self._dev.State(),
                                                   status.UNKNOWN)
        return (nicosState, self._dev.Status())

    def _hw_wait(self):
        """Wait until hardware status is not BUSY."""
        while PyTangoDevice.doStatus(self, 0)[0] == status.BUSY:
            session.delay(self._base_loop_delay)

    def doVersion(self):
        return [(self.tangodevice, self._dev.version)]

    def doReset(self):
        self._dev.Reset()
        while self._dev.State() == PyTango.DevState.INIT:
            session.delay(self._base_loop_delay)

    def _setMode(self, mode):
        super(PyTangoDevice, self)._setMode(mode)
        # remove the Tango device on entering simulation mode, to prevent
        # accidental access to the hardware
        if mode == SIMULATION:
            self._dev = HardwareStub(self)

    def _getProperty(self, name, dev=None):
        """
        Utility function for getting a property by name easily.
        """
        if dev is None:
            dev = self._dev
        # return value is: [name, value, name, value, ...]
        props = dev.GetProperties()
        propnames = props[::2]
        return props[2*propnames.index(name) + 1] \
            if name in propnames else None

    def doReadUnit(self):
        """For devices with a unit attribute."""
        attrInfo = self._dev.attribute_query('value')
        # prefer configured unit if nothing is set on the Tango device
        if attrInfo.unit == 'No unit':
            return self._config.get('unit', '')
        return attrInfo.unit

    def _createPyTangoDevice(self, address):  # pylint: disable=E0202
        """
        Creates the PyTango DeviceProxy and wraps command execution and
        attribute operations with logging and exception mapping.
        """
        check_tango_host_connection(self.tangodevice, self.tangotimeout)
        proxy_key = (self._name, address)
        if proxy_key not in PyTangoDevice.proxy_cache:
            PyTangoDevice.proxy_cache[proxy_key] = PyTango.DeviceProxy(address)
        device = PyTangoDevice.proxy_cache[proxy_key]
        device.set_timeout_millis(int(self.tangotimeout * 1000))
        # detect not running and not exported devices early, because that
        # otherwise would lead to attribute errors later
        try:
            device.State
        except AttributeError:
            raise NicosError(self, 'connection to Tango server failed, '
                             'is the server running?')
        return self._applyGuardsToPyTangoDevice(device)

    def _applyGuardsToPyTangoDevice(self, dev):
        """
        Wraps command execution and attribute operations of the given
        device with logging and exception mapping.
        """
        dev.command_inout = self._applyGuardToFunc(dev.command_inout)
        dev.write_attribute = self._applyGuardToFunc(dev.write_attribute,
                                                     'attr_write')
        dev.read_attribute = self._applyGuardToFunc(dev.read_attribute,
                                                    'attr_read')
        dev.attribute_query = self._applyGuardToFunc(dev.attribute_query,
                                                     'attr_query')
        return dev

    def _applyGuardToFunc(self, func, category='cmd'):
        """
        Wrap given function with logging and exception mapping.
        """
        def wrap(*args, **kwds):
            info = category + ' ' + args[0] if args else category

            # handle different types for better debug output
            if category == 'cmd':
                self.log.debug('[Tango] command: %s%r', args[0], args[1:])
            elif category == 'attr_read':
                self.log.debug('[Tango] read attribute: %s', args[0])
            elif category == 'attr_write':
                self.log.debug('[Tango] write attribute: %s => %r',
                               args[0], args[1:])
            elif category == 'attr_query':
                self.log.debug('[Tango] query attribute properties: %s',
                               args[0])
            elif category == 'constructor':
                self.log.debug('[Tango] device creation: %s', args[0])
                try:
                    result = func(*args, **kwds)
                    return self._com_return(result, info)
                except Exception as err:
                    self._com_raise(err, info)

            elif category == 'internal':
                self.log.debug('[Tango integration] internal: %s%r',
                               func.__name__, args)
            else:
                self.log.debug('[Tango] call: %s%r', func.__name__, args)

            return self._com_retry(info, func, *args, **kwds)

        # hide the wrapping
        wrap.__name__ = func.__name__

        return wrap

    def _com_return(self, result, info):
        # explicit check for loglevel to avoid expensive reprs
        if self.loglevel == 'debug':
            if isinstance(result, PyTango.DeviceAttribute):
                the_repr = repr(result.value)[:300]
            else:
                # This line explicitly logs '=> None' for commands which
                # does not return a value. This indicates that the command
                # execution ended.
                the_repr = repr(result)[:300]
            self.log.debug('\t=> %s', the_repr)
        return result

    def _tango_exc_desc(self, err):
        exc = str(err)
        if err.args:
            exc = err.args[0]  # Can be str or DevError
            if isinstance(exc, PyTango.DevError):
                return describe_dev_error(exc)
        return exc

    def _tango_exc_reason(self, err):
        if err.args and isinstance(err.args[0], PyTango.DevError):
            return err.args[0].reason.strip()
        return ''

    def _com_warn(self, retries, name, err, info):
        if self._tango_exc_reason(err) in FATAL_REASONS:
            self._com_raise(err, info)
        if retries == self.comtries - 1:
            self.log.warning('%s failed, retrying up to %d times: %s',
                             info, retries, self._tango_exc_desc(err))

    def _com_raise(self, err, info):
        reason = self._tango_exc_reason(err)
        exclass = REASON_MAPPING.get(
            reason, EXC_MAPPING.get(type(err), NicosError))
        fulldesc = self._tango_exc_desc(err)
        self.log.debug('[Tango] error: %s', fulldesc)
        raise exclass(self, fulldesc)
Example #14
0
 def _setMode(self, mode):
     IPCModBus._setMode(self, mode)
     if mode == SIMULATION:
         self._connection = HardwareStub(self)