Ejemplo n.º 1
0
def test_dictof():
    assert dictof(int, str)({1: 0, 2: 1}) == {1: '0', 2: '1'}
    assert dictof(int, str)() == {}
    assert raises(ValueError, dictof(int, str), ('a', 'b'))
    assert raises(ValueError, dictof(int, str), {'x': 'y'})
    # test that the dict is read-only
    assert raises(TypeError, dictof(int, str)({1: 'x'}).pop, 1)
Ejemplo n.º 2
0
class NamedDigitalOutput(DigitalOutput):
    """
    A DigitalOutput with numeric values mapped to names.
    """

    parameters = {
        'mapping':
        Param('A dictionary mapping state names to integer values',
              type=dictof(str, int),
              mandatory=True),
    }

    def doInit(self, mode):
        self._reverse = {v: k for (k, v) in self.mapping.items()}
        # oneofdict: allows both types of values (string/int), but normalizes
        # them into the string form
        self.valuetype = oneofdict(self._reverse)

    def doStart(self, target):
        value = self.mapping.get(target, target)
        if DigitalOutput.doRead(self, 0) != value:
            DigitalOutput.doStart(self, value)

    def doRead(self, maxage=0):
        value = DigitalOutput.doRead(self, maxage)
        return self._reverse.get(value, value)
Ejemplo n.º 3
0
class FocusRing(Axis):

    parameters = {
        'lenses': Param('Defintion of userlimits for each of the lenses.',
                        type=dictof(str, limits), settable=False,
                        mandatory=True, userparam=False),
        'lens': Param('Currently used lens',
                      type=str, userparam=True, settable=True, default=''),
    }

    def doPreInit(self):
        pass

    def doReference(self):
        """Do a reference drive.

        Since the motor has no limit switches the motor has to move to the
        lowest position. It will stop due to the mechanical movement limitation
        and this position will be set to 'absmin'.
        """
        if self._hascoder:
            self.log.error('This is an encoded axis, no need to reference')
            return
        motor = self._attached_motor
        _userlimits = motor.userlimits  # store user limits
        # The use of _setROParam suppresses output to inform users about
        # changing of the user limits
        motor._setROParam('userlimits', motor.abslimits)  # open limits
        try:
            motor.setPosition(motor.absmax)
            motor.maw(motor.absmin)
        finally:
            motor._setROParam('userlimits', _userlimits)  # restore user limits

    def doWriteLens(self, value):
        if value not in self.lenses:
            raise ValueError('Lens is not defined. Possible lenses are: %s' %
                             ', '.join(["'%s'" % x
                                        for x in list(self.lenses.keys())]))
        self.userlimits = self.lenses[value]
Ejemplo n.º 4
0
class MappingCacheForwarder(CacheForwarder):
    """Forwards cache updates to another cache, while remapping
    the device part of the key.
    """

    parameters = {
        'map': Param('Mapping for devices', type=dictof(str, str),
                     mandatory=True),
    }

    def _putChange(self, time, ttl, key, value):
        if not self._checkKey(key):
            return
        dev, slash, sub = key.partition('/')
        dev = self.map.get(dev, dev)
        if value is None:
            msg = '%s@%s%s%s\n' % (time, self._prefix, dev + slash + sub,
                                   OP_TELLOLD)
        else:
            msg = '%s%s@%s%s%s%s\n' % (time, ttl, self._prefix,
                                       dev + slash + sub, OP_TELL, value)
        self._queue.put(msg)
Ejemplo n.º 5
0
class HasMapping(DeviceMixinBase):
    """
    Mixin class for devices that use a finite mapping between user supplied
    input and internal representation.

    This is mainly useful for devices which can only yield certain values or go
    to positions from a predefined set, like switching devices.

    Abstract classes that use this mixin are implemented in
    `nicos.devices.abstract.MappedReadable` and `.MappedMoveable`.
    """
    parameters = {
        'mapping':
        Param('Mapping of device values to raw (internal) values',
              unit='',
              settable=False,
              mandatory=True,
              type=dictof(str, anytype)),
        'fallback':
        Param(
            'Readback value if the raw device value is not in '
            'the mapping or None to disable',
            default=None,
            unit='',
            type=anytype,
            settable=False),
    }

    # mapped values usually are string constants and have no unit
    parameter_overrides = {
        'unit': Override(mandatory=False),
    }

    def doIsAllowed(self, target):
        if target not in self.mapping:
            return False, 'unknown value: %r, must be one of %s' % \
                (target, ', '.join(map(repr, sorted(self.mapping))))
        return True, ''
Ejemplo n.º 6
0
class Configuration(PyTangoDevice, PassiveChannel):
    """Channel that allows to configure various parameters of the DECTRIS
    Pilatus detector.

    Without this channel you cannot access any parameter of the Pilatus
    detector except for the exposure time (TimerChannel) out of NICOS.

    You can attach devices to this channel in order to read out their values
    and store them in the Pilatus image header via the ``mxsettings``
    parameter.
    """

    # TODO: add proper descriptions
    attached_devices = {
        'detector_distance': Attach(
            'Current detector distance to sample.',
            Readable, optional=True,
        ),
        'detector_voffset': Attach(
            'Current vertical detector translation.',
            Readable,  optional=True,
        ),
        'flux': Attach(
            'Current photon flux in cps.',
            Readable, optional=True,
        ),
        'filter_transmission': Attach(
            'Current transmission filter factor.',
            Readable, optional=True,
        ),
        'start_angle': Attach(
            '',
            Readable, optional=True,
        ),
        'detector_2theta': Attach(
            'Current two-theta position.',
            Readable, optional=True,
        ),
        'polarization': Attach(
            '',
            Readable, optional=True,
        ),
        'alpha': Attach(
            'Current alpha position.',
            Readable, optional=True,
        ),
        'kappa': Attach(
            'Current kappa position.',
            Readable, optional=True,
        ),
        'phi': Attach(
            'Current phi position.',
            Readable, optional=True,
        ),
        'chi': Attach(
            'Current chi position.',
            Readable, optional=True,
        ),
        'omega': Attach(
            'Current omega position.',
            Readable, optional=True,
        ),
        'start_position': Attach(
            '',
            Readable, optional=True,
        ),
        'shutter_time': Attach(
            '',
            Readable, optional=True,
        ),
    }

    parameters = {
        'energy': Param(
            'X-ray and threshold energy in kilo electron volt. Set to "None" '
            'if the energy is either not set yet not configurable for this '
            'detector.',
            type=none_or(
                dictwith(**dict((p, float) for p in ENERGY_PARAMETERS))),
            settable=True,
            volatile=True,
            unit='keV',
            prefercache=False,
            chatty=True,
        ),
        'exposures': Param(
            'Number of exposures to accumulate per frame/readout.',
            type=intrange(1, 2**32 - 1),
            settable=True,
            volatile=True,
            userparam=False,
            unit='',
        ),
        'imageheader': Param(
            'String to be included in the image header.',
            type=str,
            settable=True,
            volatile=True,
            unit='',
            chatty=True,
        ),
        'images': Param(
            'Number of images for an automatic sequence.',
            type=intrange(1, 2**16 - 1),
            settable=True,
            volatile=True,
            userparam=False,
            unit='',
        ),
        'mxsettings': Param(
            'Crystallographic parameters reported in the image header.',
            type=dictof(oneof(*MX_PARAMETERS), anytype),
            settable=True,
            volatile=True,
            unit='',
            prefercache=False,
        ),
        'period': Param(
            'Exposure period in seconds (must be longer than exposure time + '
            '2.28 ms readout time).',
            type=floatrange(1.0015, 60 * 24 * 60 * 60),  # maximum: 60 days
            settable=True,
            volatile=True,
            userparam=False,
            unit='s',
        ),
        'sensorvalues': Param(
            'Relative humidity and temperature sensor values on all channels.',
            type=dictof(str, str),
            unit='',
            volatile=True,
        ),
    }

    parameter_overrides = {
        'lowlevel': Override(default=True),
    }

    def _poll_all_channels(self):
        # update the status of all pilatus detector channels
        for detector in session.experiment.detectors:
            if isinstance(detector, Detector):
                detector.poll()

    def doRead(self, maxage=0):
        return []

    def valueInfo(self):
        return ()

    def doStatus(self, maxage=0):
        return PyTangoDevice.doStatus(self, maxage)[0], ''

    def doPrepare(self):
        self.doUpdateMxsettings({})

    def doReadEnergy(self):
        values = self._dev.energy
        return dict(zip(ENERGY_PARAMETERS, values)) if all(values) else None

    def _write_energy(self, value):
        # only send the energy parameters to the hardware if they have changed
        if self.energy:
            for param in ENERGY_PARAMETERS:
                if abs(value[param] - self.energy[param]) > 0.001:
                    self._dev.energy = [value['xray'], value['threshold']]
                    return

    def doWriteEnergy(self, value):
        self._write_energy(value)
        self._poll_all_channels()

    def doUpdateEnergy(self, value):
        # only necessary for transmitting setup values to the hardware
        if self.doStatus()[0] == status.OK:
            self._write_energy(value)

    def doReadExposures(self):
        return self._dev.exposures

    def doReadImages(self):
        return self._dev.images

    def doWriteImages(self, value):
        self._dev.images = value

    def doReadImageheader(self):
        return self._dev.imageHeader

    def doWriteImageHeader(self, value):
        self._dev.imageHeader = value

    def doReadMxsettings(self):
        mx_settings = self._dev.mxSettings
        if not mx_settings:
            return {}
        # create dict {k1: v1, k2: v2, ...} from list [k1, v1, k2, v2, ...]
        mx_settings = {mx_settings[2 * i]: mx_settings[2 * i + 1]
                       for i in range(len(mx_settings) // 2)}
        # convert values to tuple, float or int
        return {k: MX_PARAMETERS[k](v) for k, v in mx_settings.items()}

    def doWriteMxsettings(self, value):
        start_time = time()
        # energy update must be completed after maximum 15 seconds
        while time() < start_time + 15:
            if self.doStatus()[0] == status.OK:
                break
            self.log.info('waiting until the detector is ready')
            session.delay(1.5)
        else:
            self.log.error('mxsettings update could not be performed: '
                           'pilatus detector is still busy')
            return
        # flatten dict {k1: v1, k2: v2, ...} to [k1, v1, k2, v2, ...]
        self._dev.mxSettings = [str(v) for v in list(sum(value.items(), ()))]

    def doUpdateMxsettings(self, value):
        value = dict(value)  # convert to writable dict
        for name, adev in ((k, v) for k, v in self._adevs.items() if v):
            adev_value = adev.read()
            if name == 'filter_transmission':
                adev_value = 1 / adev_value
            value[name] = str(adev_value)
        if value:
            self.doWriteMxsettings(value)

    def doReadPeriod(self):
        return self._dev.period

    def doWritePeriod(self, value):
        self._dev.period = value

    def doReadSensorvalues(self):
        sensor_values = self._dev.sensorValues
        # create dict {k1: v1, k2: v2, ...} from list [k1, v1, k2, v2, ...]
        return {sensor_values[2 * i]: sensor_values[2 * i + 1]
                for i in range(len(sensor_values) // 2)}

    @usermethod
    @requires(level=USER)
    def setEnergy(self, radiation=None, **value):
        """Either load the predefined settings that are suitable for usage with
        silver, chromium, copper, iron oder molybdenum radiation or set
        the x-ray and threshold energy to any other appropriate values.

        :param str radiation: 'Ag', 'Cr', 'Cu', 'Fe' or 'Mo' (case insensitive)
        :param dict[str, float] value: {'xray': x, 'threshold': y}
        """
        if not (radiation or value) or radiation and value:
            raise InvalidValueError('call either dev.SetEnergy("<radiation>") '
                                    'or dev.SetEnergy(xray=x, threshold=y)')
        if radiation:
            self._dev.LoadEnergySettings(radiation)
            self._poll_all_channels()
        else:
            self.energy = value
Ejemplo n.º 7
0
class HVSwitch(SequencerMixin, MappedMoveable):
    """High voltage convenience switching device for the CHARM detector."""

    valuetype = oneof('on', 'off', 'safe')

    attached_devices = {
        'anodes':
        Attach('HV channels for the anodes', Moveable, multiple=[2, 9]),
        'banodes':
        Attach('HV channels for the boundary anodes',
               Moveable,
               multiple=[1, 8]),
        'cathodes':
        Attach('HV channels for the boundary cathodes', Moveable, multiple=2),
        'window':
        Attach('HV channel for the window', Moveable, multiple=1),
        'trip':
        Attach('Devices signaling a trip on the hardware', Readable),
    }

    parameters = {
        '_tripped':
        Param('Indicator for hardware trip',
              type=bool,
              internal=True,
              default=False),
    }

    parameter_overrides = {
        'unit': Override(default='', mandatory=False),
        'fallback': Override(default='unknown'),
        'mapping': Override(type=dictof(str, dictof(str, float))),
    }

    def doInit(self, mode):
        if self.fallback in self.mapping:
            raise ConfigurationError(
                self, 'Value of fallback parameter is '
                'not allowed to be in the mapping!')
        self._devices = {
            dev.name: dev
            for dev in (self._attached_anodes + self._attached_banodes +
                        self._attached_cathodes + self._attached_window)
        }

        if len(self._attached_anodes) != len(self._attached_banodes) + 1:
            raise ConfigurationError(
                self, 'Number of anode devices must be '
                'the number of boundary anodes + 1: %d, '
                '%d' % (len(self.anodes), len(self.banodes)))
        # if not self.relax_mapping:
        #     self.valuetype = oneof(*sorted(self.mapping, key=num_sort))

        for value in sorted(self.mapping, key=num_sort):
            try:
                self.valuetype(value)
            except ValueError as err:
                raise ConfigurationError(
                    self, '%r not allowed as key in mapping. %s' %
                    (value, err)) from err

    def doIsAllowed(self, target):
        if target == 'off':
            ok = True
        else:
            ok = not self._tripped and not self._attached_trip.read(0)
        return ok, '' if ok else 'hardware is tripped'

    def doStop(self):
        if not self._tripped:
            SequencerMixin.doStop(self)
        else:
            raise ModeError(self, "can't be stopped, device is tripped.")

    def doReset(self):
        if self._tripped:
            if self.doStatus(0)[0] == status.BUSY or self._hardware_tripped():
                raise ModeError(self,
                                "can't reset device. Hardware is tripped")
        SequencerMixin.doReset(self)
        self._setROParam('_tripped', False)

    def doPoll(self, n, maxage):
        if not self._tripped and session.sessiontype == POLLER and \
           self._hardware_tripped():
            self._setROParam('_tripped', True)

    def _hardware_tripped(self):
        return self._attached_trip.read(0) == 'Trip'

    def _generateSequence(self, target):
        anodes = self._attached_anodes + self._attached_banodes
        seq = [
            [
                SeqDev(dev, self.mapping[target][dev.name])
                for dev in self._attached_window
            ],
            [SeqDev(dev, self.mapping[target][dev.name]) for dev in anodes],
            [
                SeqDev(dev, self.mapping[target][dev.name])
                for dev in self._attached_cathodes
            ],
        ]
        return seq

    def _is_at_target(self, pos, target):
        # if values are exact the same
        if pos == target:
            return True
        for dev in pos:
            if not self._devices[dev].isAtTarget(pos[dev], target[dev]):
                return False
        return True

    def _mapReadValue(self, value):
        for val in self.mapping:
            if self._is_at_target(value, self.mapping[val]):
                return val
        if self.fallback is not None:
            return self.fallback
        else:
            raise PositionError(self, 'unknown unmapped position %r' % value)

    def _readRaw(self, maxage=0):
        return {dev.name: dev.read(maxage) for dev in self._devices.values()}

    def _startRaw(self, target):
        ramp = 60 * self.mapping[self.target]['ramp']
        seq = self._generateSequence(self.target)
        if self.target in ['off', 'safe']:
            seq.reverse()
        self._startSequence([
            SeqParam(dev, 'ramp', ramp)
            for dev in self._attached_anodes + self._attached_banodes +
            self._attached_cathodes + self._attached_window
        ] + seq)
Ejemplo n.º 8
0
class TofChannel(PyTangoDevice, ImageChannelMixin, PassiveChannel):
    """Basic Tango Device for TofDetector."""

    STRSHAPE = ['x', 'y', 'z', 't']

    parameters = {
        'detshape':     Param('Shape of tof detector', type=dictof(str, int)),
        'timechannels': Param('Number of time channels - if set to 1 TOF mode '
                              'is disabled', type=intrange(1, 1024),
                              settable=True),
        'divisor':      Param('Width of a time channel',
                              type=int, unit='0.1us', settable=True),
        'delay':        Param('Offset delay in measure begin', type=int,
                              unit='0.1us', settable=True),
        'readchannels': Param('Tuple of (start, end) channel numbers will be '
                              'returned by a read', type=tupleof(int, int),
                              default=(0, 0), settable=True, mandatory=True),
        'readtimechan': Param('Tuple of (start, end) integrated time channels '
                              'will be returned by a read',
                              type=tupleof(int, int),
                              default=(0, 0), settable=True, mandatory=True),
    }

    def doInit(self, mode):
        self.arraydesc = ArrayDesc('data',
                                   (self.detshape.get('x', 1),
                                    self.detshape.get('t', 1)),
                                   numpy.uint32)
        if mode != SIMULATION:
            self._dev.set_timeout_millis(10000)

    def doPrepare(self):
        self._dev.Clear()
        PyTangoDevice._hw_wait(self)
        self.log.debug("Detector cleared")
        self._dev.Prepare()

    def doStart(self):
        start, end = self.readchannels
        self.readresult = [0] * (end - start + 1)
        self._dev.Start()
        self.log.debug("Detector started")

    def doFinish(self):
        self._dev.Stop()
        session.delay(0.2)
        PyTangoDevice._hw_wait(self)

    def doStop(self):
        self._dev.Stop()

    def doPause(self):
        self.doFinish()
        return True

    def doResume(self):
        self.doStart()

    def doReadTimechannels(self):
        return self._dev.timeChannels

    def doWriteTimechannels(self, value):
        self._dev.timeChannels = value
        self._pollParam('detshape')

    def doReadDivisor(self):
        return self._dev.timeInterval

    def doWriteDivisor(self, value):
        self._dev.timeInterval = value

    def doReadDelay(self):
        return self._dev.delay

    def doWriteDelay(self, value):
        self._dev.delay = value

    def doReadDetshape(self):
        shvalue = self._dev.detectorSize
        return {'x': shvalue[0], 't': shvalue[3]}

    def valueInfo(self):
        start, end = self.readchannels
        return tuple(Value("chan-%d" % i, unit="cts", errors="sqrt",
                           type="counter", fmtstr="%d")
                     for i in range(start, end + 1))

    def doReadArray(self, quality):
        self.log.debug("Tof Detector read image")
        start, end = self.readchannels
        # get current data array from detector
        array = numpy.asarray(self._dev.value, numpy.uint32)
        array = array.reshape(self.detshape['t'], self.detshape['x'])
        if self.timechannels > 1:
            startT, endT = self.readtimechan
            res = array[startT:endT+1].sum(axis=0)[start:end+1]
        else:
            res = array[0, start:end+1]
        self.readresult = res.tolist()
        return array