Beispiel #1
0
class MultiCounter(BaseImageChannel):
    """Channel for QMesyDAQ that allows to access selected channels in a
    multi-channel setup.
    """

    parameters = {
        'channels': Param('Tuple of active channels (1 based)', settable=True,
                          type=listof(int)),
    }

    def doRead(self, maxage=0):
        if self._mode == SIMULATION:
            res = [0] * (max(self.channels) + 3)
        else:
            # read data via Tango and transform it
            val = self._dev.value
            res = val.tolist() if isinstance(val, numpy.ndarray) else val
        expected = 3 + max(self.channels or [0])
        # first 3 values are sizes of dimensions
        if len(res) >= expected:
            data = res[3:]
            # ch is 1 based, data is 0 based
            total = sum([data[ch - 1] for ch in self.channels])
        else:
            self.log.warning('not enough data returned, check config! '
                             '(got %d elements, expected >=%d)',
                             len(res), expected)
            data = None
            total = 0
        resultlist = [total]
        if data is not None:
            for ch in self.channels:
                # ch is 1 based, data is 0 based
                resultlist.append(data[ch - 1])
        return resultlist

    def valueInfo(self):
        resultlist = [Value('ch.sum', unit='cts', errors='sqrt',
                            type='counter', fmtstr='%d')]
        for ch in self.channels:
            resultlist.append(Value('ch%d' % ch, unit='cts', errors='sqrt',
                                    type='counter', fmtstr='%d'))
        return tuple(resultlist)

    def doReadIsmaster(self):
        return False

    def doReadFmtstr(self):
        return ', '.join(['sum %d'] + ['ch%d %%d' % ch for ch in self.channels])
Beispiel #2
0
class NexusSink(FileSink):
    """This is a sink for writing NeXus HDF5 files from a template.

    The template is a dictionary representing the structure of the NeXus file.
    Special elements in this dictionary are responsible for writing the various
    NeXus elements. The actual writing work is done in the NexusSinkHandler.
    This class just initializes the handler properly.
    """
    parameters = {
        'templateclass': Param('Python class implementing '
                               'NexusTemplateProvider',
                               type=str, mandatory=True),
    }

    parameter_overrides = {
        'settypes': Override(type=setof(*(POINT,))),
    }

    handlerclass = NexusSinkHandler
    _handlerObj = None

    def doInit(self, mode):
        if mode == SLAVE:
            return

    # The default implementation creates gazillions of SinkHandlers.
    # For NeXus we do not want this, we want one keeping track of the whole
    # process.  However, we want a handler object per file.

    def createHandlers(self, dataset):
        if self._handlerObj is None:
            if len(dataset.detectors) == 1:
                self._handlerObj = self.handlerclass(self, dataset,
                                                     dataset.detectors[0])
            else:
                self._handlerObj = self.handlerclass(self, dataset, None)
        else:
            self._handlerObj.dataset = dataset
        return [self._handlerObj]

    def end(self):
        self._handlerObj = None

    def loadTemplate(self):
        mod, cls = self.templateclass.rsplit('.', 1)
        mod = importlib.import_module(mod)
        class_ = getattr(mod, cls)
        return class_().getTemplate()
Beispiel #3
0
class FocusPoint(HasLimits, Moveable):
    attached_devices = {
        'table': Attach('table', Moveable),
        'pivot': Attach('pivot', Readable),
    }

    parameters = {
        'gisansdistance':
        Param('GISANS distance', type=floatrange(0, None), default=10700),
    }

    parameter_overrides = {
        'abslimits': Override(mandatory=True, volatile=False),
    }

    def moveToFocus(self):
        self._attached_table.move(self._calculation())

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

    def doStart(self, pos):
        # self.moveToFocus() or move table
        self._attached_table.move(pos)

    def _calculation(self, pivot=None):
        if pivot is None:
            pivot = self._attached_pivot.read(0)
        focus = self.gisansdistance - pivot * self._attached_pivot.grid
        self.log.debug('FocusPoint _calculation focus %f pivot %d', focus,
                       pivot)
        return focus

    def doStatus(self, maxage=0):
        state = self._attached_table.status(maxage)
        if state[0] != status.OK:
            return state
        table = self._attached_table.read()
        focus = self._calculation()
        precision = 0
        if hasattr(self._attached_table, '_attached_device'):
            precision = self._attached_table._attached_device.precision
        elif hasattr(self._attached_table, 'precision'):
            precision = self._attached_table.precision
        else:
            precision = 0
        text = 'focus' if abs(table - focus) <= precision else state[1]
        return status.OK, text
Beispiel #4
0
class Collimator(Axis):
    """Polarization collimator device."""

    parameters = {
        'divergency':
        Param('Divergency of the collimator',
              type=floatrange(0, None),
              settable=False,
              userparam=False,
              default=0.5,
              unit='deg'),
    }

    parameter_overrides = {
        'unit': Override(mandatory=False, default='deg'),
    }
Beispiel #5
0
class SkewMotor(Motor):
    """Device moving by using two axes and having a fixed inclination.

    Both axis have a fixed inclination given by the ``skew`` parameter.  The
    position of the devices is given for the middle between both axis.  The
    ``motor_1`` device has always the smaller position value than the
    ``motor_2`` device.

    pos(motor_1) + skew / 2 == pos == pos(motor_2) - skew / 2.
    """

    attached_devices = {
        'motor_1': Attach('moving motor, 1', Moveable),
        'motor_2': Attach('moving motor, 2', Moveable),
    }

    parameters = {
        'skew':
        Param('Skewness of hardware, difference between both motors',
              type=floatrange(0.),
              default=0.,
              settable=True,
              unit='main'),
    }

    def _read_motors(self, maxage=0):
        return self._attached_motor_1.read(maxage), \
            self._attached_motor_2.read(maxage)

    def doRead(self, maxage=0):
        return sum(self._read_motors(maxage)) / 2.

    def doIsAtTarget(self, pos):
        if self.target is None:
            return True
        if not self._attached_motor_1.isAtTarget(pos - self.skew / 2.) or \
           not self._attached_motor_2.isAtTarget(pos + self.skew / 2.):
            return False
        m1, m2 = self._read_motors()
        self.log.debug('%.3f, %.3f, %.3f, %.3f', m1, m2, (m1 + self.skew / 2.),
                       (m2 - self.skew / 2.))
        return abs(m1 - m2 + self.skew) <= self.precision

    def doStart(self, target):
        self._attached_motor_1.move(target - self.skew / 2.)
        self._attached_motor_2.move(target + self.skew / 2.)
Beispiel #6
0
class HexapodSpecial(PyTangoDevice, Device):
    """Ohe Hexapod Device for Workspace Configuration."""

    parameters = {
        "workspaces": Param("Hexapod workspaces list containing tuples of "
                            "(id, [xn, xp, yn, yp, zn, zp, rzn, rzp, ryn, ryp, "
                            "rxn, rxp, tx, ty, tz, rz, ry, rx])",
                            type=listof(tupleof(int, listof(float))),
                            mandatory=True, settable=False)
    }

    def doInit(self, mode):
        if any(idx < 10 or idx > 19 for idx, _ in self.workspaces):
            raise ConfigurationError("Workspace ids muste be in 10..19 "
                                     "(Jülich workspace range)")
        if mode != SIMULATION:
            workspaces = self._dev.workspaces  # Tango get workspaces
            for wsid, ws in self.workspaces:
                workspaces[wsid] = ws
            self._dev.workspaces = workspaces  # Tango set workspaces
Beispiel #7
0
class SkewMotor(HasOffset, SkewRead, Motor):
    """Device moving by using two axes and having a fixed inclination.

    Both axis have a fixed inclination given by the ``skew`` parameter.  The
    position of the devices is given for the middle between both axis.  The
    ``one`` device has always the smaller position value than the
    ``two`` device.

    pos(one) + skew / 2 == pos == pos(two) - skew / 2.
    """

    parameters = {
        'skew':
        Param('Skewness of hardware, difference between "one" and '
              '"two"',
              type=float,
              default=0.,
              settable=True,
              unit='main'),
    }

    def _read_devices(self, maxage=0):
        return self._attached_one.read(maxage), self._attached_two.read(maxage)

    def doRead(self, maxage=0):
        return sum(self._read_devices(maxage)) / 2.

    def doIsAtTarget(self, pos, target):
        if target is None:
            return True
        m1, m2 = self._read_devices()
        self.log.debug('%.3f, %.3f, %.3f, %.3f', m1, m2, (m1 + self.skew / 2.),
                       (m2 - self.skew / 2.))
        if (not self._attached_one.isAtTarget(m1, pos - self.skew / 2.) or
                not self._attached_two.isAtTarget(m2, pos + self.skew / 2.)):
            return False
        return abs(m1 - m2 + self.skew) <= self.precision

    def doStart(self, target):
        self._attached_one.move(target - self.skew / 2.)
        self._attached_two.move(target + self.skew / 2.)
Beispiel #8
0
class SelectorLambda(_SelectorLambda):

    parameters = {
        'delta': Param('Maximum deviation between requested rpm and limits',
                       unit='rpm', default=10)
    }

    def _adjustValue(self, value):
        speed = int(self._constant() / value)
        amin, amax = self._attached_seldev.abslimits
        if speed > amax and (speed - amax) <= self.delta:
            value = self._constant() / amax
        elif speed < amin and (amin - speed) <= self.delta:
            value = self._constant() / amin
        return value

    def doStart(self, value):
        return _SelectorLambda.doStart(self, self._adjustValue(value))

    def doIsAllowed(self, value):
        return _SelectorLambda.doIsAllowed(self, self._adjustValue(value))
Beispiel #9
0
class Shutter(NamedDigitalOutput):
    """Shutter implements a NamedDigitalOutput which moves to `stoptarget`
    position when the device is stopped. This can be used to close the
    shutter in case of (emergency) stops. If the shutter should not move on
    (emergency) stops please use `NamedDigitalOutput`."""

    parameters = {
        'stoptarget': Param('Target position on Stop', type=str,
                            default='closed', userparam=False)
    }

    def doInit(self, mode):
        NamedDigitalOutput.doInit(self, mode)
        # TODO: remove this code after successful migration
        if self.stoptarget == 'close':
            self._setROParam('stoptarget', CLOSED)
            self.log.warning('stoptarget parameter has been changed to %r',
                             CLOSED)

    def doStop(self):
        self.maw(self.stoptarget)
Beispiel #10
0
class MotorEncoderDifference(Readable):

    attached_devices = {
        'motor': Attach('moving motor', Moveable),
        'analog': Attach('analog encoder maybe poti', Readable),
    }

    parameters = {
        'absolute':
        Param('Value is absolute or signed.',
              type=bool,
              settable=True,
              default=True),
    }

    def doRead(self, maxage=0):
        dif = self._attached_analog.read(maxage) - \
            self._attached_motor.read(maxage)
        return abs(dif) if self.absolute else dif

    def doStatus(self, maxage=0):
        return status.OK, ''
Beispiel #11
0
class ImageChannel(QMesyDAQImage, BaseImageChannel):

    parameters = {
        'readout': Param('Readout mode of the Detector', settable=True,
                         type=oneof('raw', 'mapped', 'amplitude'),
                         default='mapped', mandatory=False, chatty=True),
    }

    def doWriteListmode(self, value):
        self._dev.SetProperties(['writelistmode', '%s' % value])
        return self._getProperty('writelistmode')

    def doWriteHistogram(self, value):
        self._dev.SetProperties('writehistogram', '%s' % value)
        return self._getProperty('writehistogram')

    def doWriteReadout(self, value):
        self._dev.SetProperties(['histogram', '%s' % value])
        return self._getProperty('histogram')

    def doWriteListmodefile(self, value):
        self._dev.SetProperties(['lastlistfile', '%s' % value])
        return self._getProperty('lastlistfile')

#   def doReadListmodefile(self):
#       return self._getProperty('lastlistfile')

    def doWriteHistogramfile(self, value):
        self._taco_update_resource('lasthistfile', '%s' % value)
        return self._getProperty('lasthistfile')

#   def doReadHistogramfile(self):
#       return self._getProperty('lasthistfile')

    def doReadConfigfile(self):
        return self._getProperty('configfile')

    def doReadCalibrationfile(self):
        return self._getProperty('calibrationfile')
Beispiel #12
0
class BasePos(Readable):

    valuetype = int

    attached_devices = {
        'comm': Attach('Communication device', StringIO),
    }

    parameters = {
        'index':
        Param('index to get one side',
              type=intrange(1, 2),
              settable=False,
              userparam=True),
    }

    def doStatus(self, maxage=0):
        return status.OK, ''

    def doRead(self, maxage=0):
        return int(
            self._attached_comm.communicate('$0%d?*\r\n' % self.index)[1:-1])
Beispiel #13
0
class ArmController(IsController, Device):

    parameters = {
        'minangle':
        Param('Minimum angle between two arms',
              type=floatrange(0, None),
              settable=False,
              userparam=False,
              default=50.),
    }

    attached_devices = {
        'arm1': Attach('Arm 1 device', Moveable),
        'arm2': Attach('Arm 2 device', Moveable),
    }

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

    def isAdevTargetAllowed(self, adev, adevtarget):
        self.log.debug('%s: %s', adev, adevtarget)
        if adev == self._attached_arm1:
            target = self._attached_arm2.target
            if target is None:
                target = self._attached_arm2.read(0)
            absdiff = target - adevtarget
        else:
            target = self._attached_arm1.target
            if target is None:
                target = self._attached_arm1.read(0)
            absdiff = adevtarget - target
        if absdiff < 0:
            return False, 'Arms will cross.'
        dist = abs(absdiff)
        if dist >= self.minangle:
            return True, ''
        return False, 'Arms become too close to each other: %.3f deg, min. ' \
            'dist is %.3f' % (dist, self.minangle)
Beispiel #14
0
class AnalogCalc(Coder):

    attached_devices = {
        'device1': Attach('first value for calculation', Readable),
        'device2': Attach('second value for calculation', Readable),
    }

    parameters = {
        'calc':
        Param('choose the calculation',
              type=oneof('mul', 'div', 'add', 'dif'),
              settable=False,
              mandatory=True,
              default='div'),
    }

    def doRead(self, maxage=0):
        """Return a read analogue signal corrected by a polynom.

        A correcting polynom of at least first order is used.
        Result is then offset + mul * <previously calculated value>
        """
        value1 = self._attached_device1.read(maxage)
        value2 = self._attached_device2.read(maxage)

        self.log.info('value 1: %f 2: %f', value1, value2)
        if self.calc == 'mul':
            result = value1 * value2
        elif self.calc == 'div':
            result = value1 / value2
        elif self.calc == 'add':
            result = value1 + value2
        elif self.calc == 'dif':
            result = value1 - value2
        self.log.debug('final result: %f', result)

        return result
Beispiel #15
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)
Beispiel #16
0
class PolynomFit(DeviceMixinBase):
    """Fit values to a polynom."""

    parameters = {
        'poly':
        Param(
            'Polynomial coefficients in ascending order for '
            'potentiometer calibration',
            type=nonemptylistof(float),
            settable=False,
            mandatory=False,
            default=[0., 1.]),
    }

    _fitter = np.polynomial.Polynomial([0., 1.])  # identity conversion

    def doUpdatePoly(self, poly):
        self._fitter = np.polynomial.Polynomial(poly)

    def _fit(self, value):
        self.log.debug('uncorrected value: %f', value)
        result = self._fitter(value)
        self.log.debug('fitted result: %f', result)
        return result
Beispiel #17
0
class DataSink(Device):
    """The base class for data sinks.

    Each sink represents one way of processing incoming data.

    This is a device to be instantiated once per setup so that it can be
    configured easily through NICOS setups.  Actual processing is done by a
    `DataSinkHandler` class, of which one or more instances are created for
    each dataset that is processed with this sink.

    Usually, sinks are specific to a certain type of dataset (e.g. points or
    scans) and therefore override the `settypes` parameter with a default value
    that reflects this.

    .. attribute:: handlerclass

       This class attribute must be set by subclasses.  It selects the subclass
       of `.DataSinkHandler` that is to be used for handling datasets with this
       sink.

    .. attribute:: activeInSimulation

       This is a class attribute that selects whether this sink can be used in
       simulation mode.  This should only be true for sinks that write no data,
       such as a "write scan data to the console" sink.

    .. automethod:: isActive
    """

    parameters = {
        'detectors': Param('List of detector names to activate this sink '
                           '(default is always activated)', type=listof(str)),
        'settypes':  Param('List of dataset types to activate this sink '
                           '(default is for all settypes the sink supports)',
                           type=setof(*SETTYPES)),
    }

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

    # Set to true in subclasses that are safe for simulation.
    activeInSimulation = False

    # Set this to the corresponding Handler class.
    handlerclass = None

    def isActive(self, dataset):
        """Should return True if the sink can and should process this dataset.

        The default implementation, which should always be called in overrides,
        checks for simulation mode and for a match with the settypes and the
        detectors selected by the respective parameters.

        Derived classes can add additional checks, such as the dataset
        producing an array that can be written to an image file.
        """
        if session.mode == SIMULATION and not self.activeInSimulation:
            return False
        if self.settypes and dataset.settype not in self.settypes:
            return False
        if self.detectors and \
           not ({d.name for d in dataset.detectors} & set(self.detectors)):
            return False
        return True

    def createHandlers(self, dataset):
        """Start processing the given dataset (a BaseDataset).

        Creates the corresponding DataSinkHandler instances to use for this
        dataset determined via `handlerclass` and returns them.
        """
        if self.handlerclass is None:
            raise NotImplementedError('Must set an "handlerclass" attribute '
                                      'on %s' % self.__class__)
        # pylint: disable=not-callable
        if dataset.settype == POINT:
            dets = {d.name for d in dataset.detectors}
            if self.detectors:
                dets &= set(self.detectors)
            return [self.handlerclass(self, dataset, session.getDevice(det))
                    for det in dets]
        else:
            return [self.handlerclass(self, dataset, None)]
Beispiel #18
0
class CascadeDetector(VirtualImage):

    _perfoil = 16

    parameters = {
        'mode':
        Param('Data acquisition mode (tof or image)',
              type=oneof('tof', 'image'),
              settable=True,
              default='image',
              category='presets'),
        'roi':
        Param('Region of interest, given as (x1, y1, x2, y2)',
              type=tupleof(int, int, int, int),
              default=(-1, -1, -1, -1),
              settable=True),
        'tofchannels':
        Param('Total number of TOF channels to use',
              type=intrange(1, 1024),
              default=128,
              settable=True,
              category='presets'),
        'foilsorder':
        Param('Usable foils, ordered by number.',
              type=listof(intrange(0, 31)),
              settable=False,
              default=[0, 1, 2, 3, 4, 5, 6, 7],
              category='instrument'),
        'fitfoil':
        Param('Foil for contrast fitting (number BEFORE '
              'resorting)',
              type=int,
              default=0,
              settable=True),
    }

    def doInit(self, mode):
        self._buf = self._generate(0).astype('<u4')

    def doUpdateMode(self, value):
        self._dataprefix = (value == 'image') and 'IMAG' or 'DATA'
        self._datashape = (value == 'image') and self.sizes or \
            (self._perfoil * len(self.foilsorder), ) + self.sizes
        # (self.tofchannels,)
        self._tres = (value == 'image') and 1 or self.tofchannels

    def doStart(self):
        self.readresult = [0, 0]
        VirtualImage.doStart(self)

    def valueInfo(self):
        if self.mode == 'tof':
            return (Value(self.name + '.roi',
                          unit='cts',
                          type='counter',
                          errors='sqrt',
                          fmtstr='%d'),
                    Value(self.name + '.total',
                          unit='cts',
                          type='counter',
                          errors='sqrt',
                          fmtstr='%d'),
                    Value('fit.contrast',
                          unit='',
                          type='other',
                          errors='next',
                          fmtstr='%.3f'),
                    Value('fit.contrastErr',
                          unit='',
                          type='error',
                          errors='none',
                          fmtstr='%.3f'),
                    Value('fit.avg',
                          unit='',
                          type='other',
                          errors='next',
                          fmtstr='%.1f'),
                    Value('fit.avgErr',
                          unit='',
                          type='error',
                          errors='none',
                          fmtstr='%.1f'),
                    Value('roi.contrast',
                          unit='',
                          type='other',
                          errors='next',
                          fmtstr='%.3f'),
                    Value('roi.contrastErr',
                          unit='',
                          type='error',
                          errors='none',
                          fmtstr='%.3f'),
                    Value('roi.avg',
                          unit='',
                          type='other',
                          errors='next',
                          fmtstr='%.1f'),
                    Value('roi.avgErr',
                          unit='',
                          type='error',
                          errors='none',
                          fmtstr='%.1f'))
        return (Value(self.name + '.roi',
                      unit='cts',
                      type='counter',
                      errors='sqrt',
                      fmtstr='%d'),
                Value(self.name + '.total',
                      unit='cts',
                      type='counter',
                      errors='sqrt',
                      fmtstr='%d'))

    @property
    def arraydesc(self):
        if self.mode == 'image':
            return ArrayDesc('data', self._datashape, '<u4', ['X', 'Y'])
        return ArrayDesc('data', self._datashape, '<u4', ['T', 'X', 'Y'])

    def doReadArray(self, quality):
        # get current data array from detector, reshape properly
        data = VirtualImage.doReadArray(self, quality)
        # determine total and roi counts
        total = data.sum()
        if self.roi != (-1, -1, -1, -1):
            x1, y1, x2, y2 = self.roi
            roi = data[..., y1:y2, x1:x2].sum()
        else:
            x1, y1, x2, y2 = 0, 0, data.shape[-1], data.shape[-2]
            roi = total

        if self.mode == 'image':
            self.readresult = [roi, total]
            return data

        data = np.array([data] * self._datashape[0])

        # demux timing into foil + timing
        nperfoil = self._datashape[0] // len(self.foilsorder)
        shaped = data.reshape((len(self.foilsorder), nperfoil) +
                              self._datashape[1:])
        # nperfoil = self.tofchannels // self.foils
        # shaped = data.reshape((self.foils, nperfoil) + self._datashape[1:])

        x = np.arange(nperfoil)

        ty = shaped[self.fitfoil].sum((1, 2))
        ry = shaped[self.fitfoil, :, y1:y2, x1:x2].sum((1, 2))

        self.log.debug('fitting %r and %r' % (ty, ry))

        self.readresult = [
            roi,
            total,
        ]

        # also fit per foil data and pack everything together to be send
        # via cache for display
        payload = []
        for foil in self.foilsorder:
            foil_tot = shaped[foil].sum((1, 2))
            foil_roi = shaped[foil, :, y1:y2, x1:x2].sum((1, 2))
            tpopt, tperr, _ = fit_a_sin_fixed_freq(x, foil_tot)
            rpopt, rperr, _ = fit_a_sin_fixed_freq(x, foil_roi)
            payload.append([
                tpopt, tperr,
                foil_tot.tolist(), rpopt, rperr,
                foil_roi.tolist()
            ])

        self._cache.put(self.name, '_foildata', payload, flag=FLAG_NO_STORE)
        return data
Beispiel #19
0
class BiodiffDetector(Detector):

    attached_devices = {
        "gammashutter": Attach("Gamma shutter", Moveable),
        "photoshutter": Attach("Photo shutter", Moveable),
    }

    parameters = {
        "ctrl_gammashutter": Param("Control gamma shutter?", type=bool,
                                   settable=True, mandatory=False,
                                   default=True),
        "ctrl_photoshutter": Param("Control photo shutter?", type=bool,
                                   settable=True, mandatory=False,
                                   default=True),
    }

    # guard against multiple prepare calls here:
    # * `RScan.preparePoint` has been extended to call `prepare` in oder to
    #   prepare detectors for exposure before moving any devices.
    # * `nicos.core.acquire.acquire` internally calls `prepare` again which
    #   must be ignored.
    _prepared = False

    def doPrepare(self):
        if not self._prepared:
            # close shutter
            if self.ctrl_photoshutter:
                self._attached_photoshutter.maw(CLOSED)
            if self.ctrl_gammashutter:
                self._attached_gammashutter.maw(CLOSED)
            Detector.doPrepare(self)
        self._prepared = True

    def doStart(self):
        # open shutter
        if self.ctrl_gammashutter:
            self._attached_gammashutter.maw(OPEN)
        if self.ctrl_photoshutter:
            self._attached_photoshutter.maw(OPEN)
        Detector.doStart(self)
        self._prepared = False

    def _check_shutter(self):
        if (self.ctrl_photoshutter and
                self._attached_photoshutter.read() == CLOSED):
            raise InvalidValueError(self, 'photo shutter not open after '
                                    'exposure, check safety system')
        if (self.ctrl_gammashutter and
                self._attached_gammashutter.read() == CLOSED):
            raise InvalidValueError(self, 'gamma shutter not open after '
                                    'exposure, check safety system')

    def _getWaiters(self):
        adevs = dict(self._adevs)
        if not self.ctrl_photoshutter:
            adevs.pop(self._attached_photoshutter.name)
        if not self.ctrl_gammashutter:
            adevs.pop(self._attached_gammashutter.name)
        return adevs

    def doFinish(self):
        Detector.doFinish(self)
        self._check_shutter()
        # close shutter
        if self.ctrl_photoshutter:
            self._attached_photoshutter.maw(CLOSED)
        if self.ctrl_gammashutter:
            self._attached_gammashutter.maw(CLOSED)
        self._prepared = False

    def doStop(self):
        Detector.doStop(self)
        # close shutter
        if self.ctrl_photoshutter:
            self._attached_photoshutter.maw(CLOSED)
        if self.ctrl_gammashutter:
            self._attached_gammashutter.maw(CLOSED)
        self._prepared = False
Beispiel #20
0
class SingleDetectors(Measurable):

    attached_devices = {
        'pintimer': Attach('Time for pin measurement', Moveable),
        'pinstate': Attach('Status for pin measurement', NamedDigitalInput),
        'pincontrol': Attach('Status control for pin measurement',
                             DigitalInput),
        'pindiodes': Attach('Integrated pindiodes', AnalogInput, multiple=5),
    }

    parameters = {
        'detector': Param('Detector used for timing',
                          type=str,
                          default='pilatus'),
    }

    def doInit(self, mode):
        self.log.debug('Integral init')
        self._preset = 0
        self._timeout = 3

    def presetInfo(self):
        return ['t']

    def doSetPreset(self, **presets):
        self.log.debug('Integral set preset')
        if 't' in presets:
            self._preset = presets['t']

    def valueInfo(self):
        return tuple(
            Value('%16s' % diode.name,
                  unit='%23s' % 'counts',
                  errors='none',
                  type='counter',
                  fmtstr='%18.10g') for diode in self._attached_pindiodes)

    def doRead(self, maxage=0):
        self.log.debug('Integral read')
        values = [dev.read(0) for dev in self._attached_pindiodes]
        return values

    def doPrepare(self):
        self.log.debug('Pindiode prepare')
        timeval = time.time() + self._timeout
        if self.detector not in session.experiment.detlist:
            self._attached_pintimer.start(0)
            while self._attached_pincontrol.read(0) != 1:
                if time.time() > timeval:
                    self.log.warning('Pinstate timeout in prepare')
                    return
                session.delay(0.02)

    def doStart(self):
        self.log.debug('Integral start')
        timeval = time.time() + self._timeout
        if self.detector not in session.experiment.detlist:
            self._attached_pintimer.start(self._preset)
        while self.status(0)[0] != status.BUSY:
            if time.time() > timeval:
                self.log.warning('Pinstate timeout in start')
                return
            session.delay(0.02)

    def doStop(self):
        self.log.debug('Pintimer stop')
        self._attached_pintimer.start(0)

    def doFinish(self):
        pass

    def doReset(self):
        self.log.debug('Pintimer reset')
        self._attached_pintimer.start(0)

    def doStatus(self, maxage=0):
        self.log.debug('Integral status')
        timeval = time.time() + self._timeout
        state = self._attached_pinstate.read(0)
        self._attached_pintimer.poll()
        if state == 'counting':
            if time.time() > (timeval + self._preset):
                return status.ERROR, 'Timeout in PIN diode device.'
            return status.BUSY, 'The device is in MOVING state.'
        return status.OK, 'The device is in ON state.'
Beispiel #21
0
class SEController(tango.TemperatureController):
    """Controller to set Temperature
    """

    attached_devices = {
        'samplecontroller':
        Attach('Controller of the sampletemperature', TemperatureController),
        'tubecontroller':
        Attach('Controller of the tubetemperature', TemperatureController)
    }

    parameters = {
        'tubeoffset':
        Param('Keep tube this many degrees below the setpoint.',
              volatile=True,
              settable=True),
        'samplestick':
        Param('Sample stick currently in use',
              type=oneof('lt', 'ht'),
              volatile=True,
              settable=True),
        'devtarget':
        Param('Target of the underlying tango device', volatile=True)
    }

    def rushTemperature(self, temperature):
        """Move to temperature as fast as possible
        Due to potential delay when going over 310K this will be handled
        in the underlying Tango server."""
        if session.mode == SIMULATION:
            return

        self._dev.RushTemperature(temperature)

    def _combinedStatus(self, maxage=0):
        state = tango.TemperatureController.doStatus(self, maxage)
        # if there is a warning from the controller, display it.
        if state[0] == WARN:
            return state
        else:
            return tango.TemperatureController._combinedStatus(self, maxage)

    def stopPressure(self):
        self._dev.StopPressureRegulation()

    def doReadTubeoffset(self):
        return self._dev.tube_offset

    def doWriteTubeoffset(self, value):
        self._dev.tube_offset = value

    def doReadSamplestick(self):
        return self._dev.sample_stick

    def doWriteSamplestick(self, value):
        self._dev.sample_stick = value

    def doReadDevtarget(self):
        if not self._dev:
            return None
        return self._dev.target

    def isAtTarget(self, val):
        ct = currenttime()
        self._cacheCB('value', val, ct)
        if self.devtarget is None:
            return True

        # check subset of _history which is in window
        # also check if there is at least one value before window
        # to know we have enough datapoints
        hist = self._history[:]
        window_start = ct - self.window
        hist_in_window = [v for (t, v) in hist if t >= window_start]
        stable = all(
            abs(v - self.devtarget) <= self.precision for v in hist_in_window)
        if 0 < len(hist_in_window) < len(hist) and stable:  # pylint: disable=len-as-condition
            if hasattr(self, 'doIsAtTarget'):
                return self.doIsAtTarget(val)
            return True
        return False

    def doIsAtTarget(self, pos):
        target = self.devtarget
        if target is None:
            return True
        return abs(target - pos) <= self.precision
Beispiel #22
0
def test_param_class():
    assert str(Param('my parameter')) == '<Param info>'
    text = Param('my parameter', prefercache=True).formatDoc()
    assert text == 'Parameter: my parameter\n\n    * Type: float\n    * ' \
                   'Default value: ``0.0``\n    * Not settable at runtime\n' \
                   '    * Prefer value from cache: True'
Beispiel #23
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
Beispiel #24
0
class Detector(GenericDetector):
    """Generic detector that provides access to the image directory and filename
    as well as the diskspace attributes of a DECTRIS Pilatus detector.

    This detector requires exactly one 'other' channel that must be an instance
    of :class:`Configuration`.
    """

    parameters = {
        'diskspace': Param(
             'Available space on the Pilatus computer in gibibytes.',
            type=float,
            unit='GiB',
            fmtstr='%.3f',
            volatile=True,
        ),
        'filename': Param(
            'Name of currently being created (during exposures) or last '
            'created image file.',
            type=str,
            unit='',
            volatile=True,
        ),
        'imagedir': Param(
            'Current target directory on the Pilatus computer where the next '
            'detector image will be stored at.',
            type=str,
            settable=True,
            unit='',
            volatile=True,
            internal=True,
        ),
        'nextfilename': Param(
            'Name of the next created image file.',
            type=str,
            settable=True,
            unit='',
            internal=True,
        ),
    }

    def doPreinit(self, mode):
        if len(self._attached_others) != 1 or not isinstance(
                self._attached_others[0], Configuration):
            raise ConfigurationError(
                'pilatus detectors require exactly one "other" channel that '
                'is an instance of {}'.format(Configuration)
            )
        self._cfg_channel = self._attached_others[0]._dev
        GenericDetector.doPreinit(self, mode)

    def doPoll(self, _n, _maxage):
        self._pollParam('filename')
        self._pollParam('imagedir')

    def doPrepare(self):
        GenericDetector.doPrepare(self)
        self.readArrays(FINAL)  # update readresult of image channels

    def doReadDiskspace(self):
        return self._cfg_channel.diskSpace

    def doReadFilename(self):
        return path.basename(self._cfg_channel.absImagePath)

    def doReadImagedir(self):
        return self._cfg_channel.imageDir

    def doWriteImagedir(self, value):
        self._cfg_channel.imageDir = value

    def doReadNextfilename(self):
        return self._cfg_channel.nextFilename

    def doWriteNextfilename(self, value):
        self._cfg_channel.nextFilename = value
Beispiel #25
0
class SEController(tango.TemperatureController):
    """Controller to set Temperature
    """

    attached_devices = {
        'samplecontroller':
        Attach('Controller of the sampletemperature', TemperatureController),
        'tubecontroller':
        Attach('Controller of the tubetemperature', TemperatureController)
    }

    parameters = {
        'tubeoffset':
        Param('Keep tube this many degrees below the setpoint.',
              volatile=True,
              settable=True),
        'samplestick':
        Param('Sample stick currently in use',
              type=oneof('lt', 'ht'),
              volatile=True,
              settable=True),
        'devtarget':
        Param('Target of the underlying tango device', volatile=True)
    }

    def rushTemperature(self, temperature):
        """Move to temperature as fast as possible
        Due to potential delay when going over 310K this will be handled
        in the underlying Tango server."""
        if session.mode == SIMULATION:
            return

        self._dev.RushTemperature(temperature)

    def _combinedStatus(self, maxage=0):
        state = tango.TemperatureController.doStatus(self, maxage)
        # if there is a warning from the controller, display it.
        if state[0] == WARN:
            return state
        else:
            return tango.TemperatureController._combinedStatus(self, maxage)

    def stopPressure(self):
        self._dev.StopPressureRegulation()

    def doReadTubeoffset(self):
        return self._dev.tube_offset

    def doWriteTubeoffset(self, value):
        self._dev.tube_offset = value

    def doReadSamplestick(self):
        return self._dev.sample_stick

    def doWriteSamplestick(self, value):
        self._dev.sample_stick = value

    def doReadDevtarget(self):
        if not self._dev:
            return None
        return self._dev.target

    def isAtTarget(self, pos=None, target=None):
        if target is None:
            if self.devtarget is None:
                return True
            target = self.devtarget

        return TemperatureController.isAtTarget(self, target=target)
Beispiel #26
0
class Flux(VectorInput):
    """Device which stores the flux averages over the relevant detectors.
    """

    parameters = {
        'fluxvalues':
        Param('Raw flux values', internal=True, type=listof(listof(int)))
    }

    def init(self):
        VectorInput.init(self)
        self._fluxvalues = [[], [], []]

    def doPoll(self, i, maxage):
        val = self.doRead()
        self._pollParam('fluxvalues')

        return self.status(), val

    def doReadFluxvalues(self):
        if not hasattr(self, '_fluxvalues'):
            self.doRead()
        return self._fluxvalues

    def doRead(self, maxage=0):
        flux = self._dev.GetFlux()

        if len(flux) != 16 * 6:
            self.log.warning('SIS returned %d flux values, expected %d',
                             len(flux), 16 * 6)
            return [0, 0, 0]

        # pylint: disable=unbalanced-tuple-unpacking
        cElast, cInelast, cDir, tElast, tInelast, tDir = self.split(flux, 16)
        rDets = self._dev.GetRegularDetectors()

        self._fluxvalues = [cElast, cInelast, cDir]

        elast = [
            sum([x for i, x in enumerate(cElast) if i in rDets]),
            sum([x for i, x in enumerate(tElast) if i in rDets])
        ]
        inelast = [
            sum([x for i, x in enumerate(cInelast) if i in rDets]),
            sum([x for i, x in enumerate(tInelast) if i in rDets])
        ]
        direct = [
            sum([x for i, x in enumerate(cDir) if i in rDets]),
            sum([x for i, x in enumerate(tDir) if i in rDets])
        ]

        if not elast[1]:
            elastic = 0
        else:
            elastic = int(elast[0] / 2e-5 / elast[1])

        if not inelast[1]:
            inelastic = 0
        else:
            inelastic = int(inelast[0] / 2e-5 / inelast[1])
        if not direct[1]:
            direct = 0
        else:
            direct = int(direct[0] / 2e-5 / direct[1])

        return [elastic, inelastic, direct]

    def split(self, inp, chunksize):
        output = []
        index = 0
        listsize = len(inp)

        while index < listsize:
            output.append([int(x) for x in inp[index:index + chunksize]])
            index += chunksize

        return output
Beispiel #27
0
class ImagePlateImage(ImageChannelMixin, PassiveChannel):
    """Represents the client to a MAATEL Image Plate Detector."""

    MAP_SHAPE = {
        125: (900, 10000),
        250: (900, 5000),
        500: (900, 2500),
    }

    attached_devices = {
        "imgdrum":      Attach("Image Plate Detector Drum "
                               "control device.", ImagePlateDrum),
    }

    parameters = {
        "erase": Param("Erase image plate on next start?", type=bool,
                       settable=True, mandatory=False, default=True),
        "roi": Param("Region of interest",
                     type=tupleof(int, int, int, int),
                     default=(0, 0, 0, 0),
                     settable=True, volatile=True,
                     category="general"),
        "pixelsize": Param("Pixel size in microns",
                           type=oneof(125, 250, 500), default=500,
                           settable=True, volatile=True, category="general"),
        "file": Param("Image file location on maatel computer",
                      type=str, settable=True, volatile=True,
                      category="general"),
        "readout_millis": Param("Timeout in ms for the readout",
                                type=int, settable=True, default=60000),
    }

    def doInit(self, mode):
        self.arraydesc = ArrayDesc('data',
                                   self.MAP_SHAPE[self.pixelsize],
                                   numpy.uint16)

    def doPrepare(self):
        # erase and expo position
        if self.erase:
            self._attached_imgdrum.maw(ImagePlateDrum.POS_ERASE)
        self._attached_imgdrum.maw(ImagePlateDrum.POS_EXPO)

    def valueInfo(self):
        # no readresult -> no values
        return ()

    def doReadArray(self, quality):
        if quality == FINAL:
            # start readout
            self._attached_imgdrum.maw(ImagePlateDrum.POS_READ)
            narray = None
            timeout = self._attached_imgdrum._dev.get_timeout_millis()
            self._attached_imgdrum._dev.set_timeout_millis(self.readout_millis)
            try:
                narray = self._attached_imgdrum._dev.Bitmap16Bit
            finally:
                self._attached_imgdrum._dev.set_timeout_millis(timeout)
            return narray
        return None

    def doReadRoi(self):
        return (0, self._attached_imgdrum._dev.InterestZoneY, 1250,
                self._attached_imgdrum._dev.InterestZoneH)

    def doReadPixelsize(self):
        return self._attached_imgdrum._dev.PixelSize

    def doReadFile(self):
        return self._attached_imgdrum._dev.ImageFile

    def doWriteRoi(self, value):
        self.log.warning("setting x offset and width are not supported "
                         "- ignored.")
        self._attached_imgdrum._dev.InterestZoneY = value[1]
        self._attached_imgdrum._dev.InterestZoneH = value[3]

    def doWritePixelsize(self, value):
        self._attached_imgdrum._dev.PixelSize = value
        self.arraydesc = ArrayDesc('data', self.MAP_SHAPE[value], numpy.uint16)

    def doWriteFile(self, value):
        self._attached_imgdrum._dev.ImageFile = value
Beispiel #28
0
class CalibratedMagnet(HasLimits, Moveable):
    """Base clase for magnet supplies having an bipolar current source.

    Use this for magnets which can be calibrated, i.e. where::

        B(I) = Ic0 + c1*erf(c2*I) + c3*atan(c4*I)

    works reasonably well.
    Coefficients c0..c4 are given as 'calibration' parameter.
    """

    attached_devices = {
        'currentsource': Attach('bipolar Powersupply', Moveable),
    }

    parameters = {
        'ramp':
        Param('Target rate of field change per minute',
              unit='main/min',
              mandatory=False,
              settable=True,
              volatile=True),
        'calibration':
        Param(
            'Coefficients for calibration '
            'function: [c0, c1, c2, c3, c4] calculates '
            'B(I) = c0*I + c1*erf(c2*I) + c3*atan(c4*I)'
            ' in T',
            type=tupleof(float, float, float, float, float),
            default=(1.0, 0.0, 0.0, 0.0, 0.0),
            settable=True,
            chatty=True),
    }

    parameter_overrides = {
        'unit': Override(mandatory=False, default='T'),
        'abslimits': Override(mandatory=False, volatile=True),
    }

    def _current2field(self, current, *coefficients):
        """Return field in T for given current in A.

        Should be monotonic and asymetric or _field2current will fail!

        Note: This may be overridden in derived classes.
        """
        v = coefficients or self.calibration
        if len(v) != 5:
            self.log.warning('Wrong number of coefficients in calibration '
                             'data!  Need exactly 5 coefficients!')
        return v[0]*current + v[1]*math.erf(v[2]*current) + \
            v[3]*math.atan(v[4]*current)

    def _field2current(self, field):
        """Return required current in A for requested field in T.

        Note: This may be overridden in derived classes.
        """
        # binary search/bisection
        maxcurr = self._attached_currentsource.abslimits[1]
        mincurr = -maxcurr
        maxfield = self._current2field(maxcurr)
        minfield = self._current2field(mincurr)
        if not minfield <= field <= maxfield:
            raise LimitError(
                self, 'requested field %g %s out of range %g..%g %s' %
                (field, self.unit, minfield, maxfield, self.unit))

        res = fsolve(lambda cur: self._current2field(cur) - field, 0)[0]
        self.log.debug('current for %g %s is %g', field, self.unit, res)
        return res

    def doRead(self, maxage=0):
        return self._current2field(self._attached_currentsource.read(maxage))

    def doStart(self, target):
        self._attached_currentsource.start(self._field2current(target))

    def doStop(self):
        self._attached_currentsource.stop()

    def doReset(self):
        return self._attached_currentsource.reset()

    def doReadRamp(self):
        # This is an approximation!
        return self.calibration[0] * abs(self._attached_currentsource.ramp)

    def doWriteRamp(self, newramp):
        # This is an approximation!
        self._attached_currentsource.ramp = newramp / self.calibration[0]

    def doReadAbslimits(self):
        minfield, maxfield = [
            self._current2field(I)
            for I in self._attached_currentsource.abslimits
        ]
        # include 0 in allowed range
        if minfield > 0:
            minfield = 0
        if maxfield < 0:
            maxfield = 0
        # get configured limits if any, or take max from source
        limits = self._config.get('abslimits', (minfield, maxfield))
        # in any way, clamp limits to what the source can handle
        limits = [clamp(i, minfield, maxfield) for i in limits]
        return min(limits), max(limits)

    def doWriteUserlimits(self, limits):
        HasLimits.doWriteUserlimits(self, limits)
        # all Ok, set source to max of pos/neg field current
        maxcurr = max(self._field2current(i) for i in limits)
        mincurr = min(self._field2current(i) for i in limits)
        self._attached_currentsource.userlimits = (mincurr, maxcurr)

    def doTime(self, startval, target):
        # get difference in current
        delta = abs(
            self._field2current(target) - self._field2current(startval))
        # ramp is per minute, doTime should return seconds
        return 60 * delta / self._attached_currentsource.ramp

    @usermethod
    def calibrate(self, fieldcolumn, *scannumbers):
        """Calibrate the B to I conversion, argument is one or more scan numbers.

        The first argument specifies the name of the device which should
        be used as a measuring device for the field.

        Example:

        >>> B_mira.calibrate(Bf, 351)
        """
        scans = session.experiment.data.getLastScans()
        self.log.info('determining calibration from scans, please wait...')
        Is = []
        Bs = []
        currentcolumn = self._attached_currentsource.name
        # XXX(dataapi): adapt to new Dataset class
        for scan in scans:
            if scan.counter not in scannumbers:
                continue
            if fieldcolumn not in scan.ynames or \
                    currentcolumn not in scan.xnames:
                self.log.info('%s is not a calibration scan', scan.counter)
                continue
            xindex = scan.xnames.index(currentcolumn)
            yindex = scan.ynames.index(fieldcolumn)
            yunit = scan.yunits[yindex]
            if yunit == 'T':
                factor = 1.0
            elif yunit == 'mT':
                factor = 1e-3
            elif yunit == 'uT':
                factor = 1e-6
            elif yunit == 'G':
                factor = 1e-4
            elif yunit == 'kG':
                factor = 1e-1
            else:
                raise NicosError(
                    self, 'unknown unit for B field '
                    'readout: %r' % yunit)
            for xr, yr in zip(scan.xresults, scan.yresults):
                Is.append(xr[xindex])
                Bs.append(yr[yindex] * factor)
        if not Is:
            self.log.error('no calibration data found')
            return
        fit = Fit('calibration', self._current2field,
                  ['c%d' % i for i in range(len(self.calibration))],
                  [1] * len(self.calibration))
        res = fit.run(Is, Bs, [1] * len(Bs))
        if res._failed:
            self.log.warning('fit failed')
            return
        self.calibration = res._pars[1]
Beispiel #29
0
class ImagePlateDrum(PyTangoDevice, Moveable):
    """ImagePlateDrum implements erasing, moving to expo position and readout
    for MAATEL Image Plate Detectors.
    """

    DEFAULT_URL_FMT = "tango://%s/EMBL/Microdiff/General#dbase=no"

    tango_status_mapping = dict(PyTangoDevice.tango_status_mapping)
    tango_status_mapping[DevState.STANDBY] = status.OK
    tango_status_mapping[DevState.ALARM] = status.ERROR

    POS_ERASE = "erase"
    POS_EXPO = "expo"
    POS_READ = "read"

    valuetype = oneof(POS_ERASE, POS_EXPO, POS_READ)

    parameters = {
        "drumpos": Param("Drum position in degree", type=float,
                         settable=True, volatile=True, category="general"),
        "readheadpos": Param("Read head motor position in mm",
                             type=float, settable=True, volatile=True,
                             category="general"),
        "drumexpo": Param("Drum expo position in degree",
                          type=float, settable=True, volatile=True,
                          category="general"),
        "readspeed": Param("Readout velocity for the detector drum "
                           "in rpm", type=float, settable=True,
                           volatile=True, category="general"),
        "erasespeed": Param("Erase velocity for the detector drum "
                            "in rpm", type=float, settable=True,
                            volatile=True, category="general"),
        "freqlaser": Param("Frequency for the laser diode in Hz",
                           type=float, settable=True, volatile=True,
                           category="general"),
        "timeerase": Param("Erasure time in seconds", type=float,
                           settable=True, volatile=True, category="general"),
    }

    parameter_overrides = {
        "unit": Override(default="", mandatory=False, settable=False)
    }

    def doInit(self, mode):
        self._lastStatus = None
        self._moveTo = None
        if mode == SIMULATION:
            self._mapStart = {}
            self._mapStop = {}
            return
        self._mapStart = {
            ImagePlateDrum.POS_ERASE: self._dev.StartErasureProcess,
            ImagePlateDrum.POS_EXPO: self._dev.MoveExpoPosition,
            ImagePlateDrum.POS_READ: self._dev.StartReadProcess,
        }
        self._mapStop = {
            ImagePlateDrum.POS_ERASE: self._dev.AbortErasureProcess,
            ImagePlateDrum.POS_EXPO: self._dev.AbortExposureProcess,
            ImagePlateDrum.POS_READ: self._dev.AbortReadProcess,
        }

    def doStart(self, pos):
        self.log.debug("doStart: pos: %s", pos)
        myStatus = self.status(0)
        if myStatus[0] == status.OK:
            self._moveTo = pos
            self._mapStart[pos]()
        else:
            raise MoveError(self, "Movement not allowed during device status "
                            "'%s'" % (status.statuses[myStatus[0]]))

    def doStop(self):
        self.log.debug("doStop")
        if self._moveTo in self._mapStop:
            self._mapStop[self._moveTo]()
        else:
            myStatus = self.status(0)
            if myStatus[0] == status.OK:
                self.log.warning("Device already stopped.")
            else:
                raise NicosError(self, "Internal moveTo state unknown. "
                                       "Check device status.")

    def doRead(self, maxage=0):
        return self.target

    def doStatus(self, maxage=0):
        # Workaround for status changes from busy to another state although the
        # operation has _not_ been completed.
        st, msg = PyTangoDevice.doStatus(self, maxage)
        if self._lastStatus == status.BUSY and st != status.BUSY:
            self.log.debug("doStatus: leaving busy state (%d)? %d. "
                           "Check again after a short delay.", status.BUSY, st)
            session.delay(5)
            st, msg = PyTangoDevice.doStatus(self, 0)
            self.log.debug("doStatus: recheck result: %d", st)
        self._lastStatus = st
        return st, msg

    def doFinish(self):
        self._moveTo = None

    def doReadDrumpos(self):
        return self._dev.DrumPosition

    def doReadReadheadpos(self):
        return self._dev.ReadHeadPosition

    def doReadDrumexpo(self):
        return self._dev.DrumExpoPosition

    def doReadReadspeed(self):
        return self._dev.ReadingDrumJogSpeed

    def doReadErasespeed(self):
        return self._dev.ErasureDrumJogSpeed

    def doReadFreqlaser(self):
        return self._dev.LaserDiodeLevel

    def doReadTimeerase(self):
        return self._dev.ErasureDuration

    def doWriteDrumpos(self, value):
        self._dev.DrumPosition = value

    def doWriteReadheadpos(self, value):
        self._dev.ReadHeadPosition = value

    def doWriteDrumexpo(self, value):
        self._dev.DrumExpoPosition = value

    def doWriteReadspeed(self, value):
        self._dev.ReadingDrumJogSpeed = value

    def doWriteErasespeed(self, value):
        self._dev.ErasureDrumJogSpeed = value

    def doWriteFreqlaser(self, value):
        self._dev.LaserDiodeLevel = value

    def doWriteTimeerase(self, value):
        self._dev.ErasureDuration = value
Beispiel #30
0
def test_nonemptystring():
    p = Param('nonemptystring', type=nonemptystring)
    assert p.default is None
    assert raises(ValueError, nonemptystring, '')
    assert nonemptystring('text') == 'text'