Exemple #1
0
class Resolution(Readable):

    valuetype = (float, float)

    attached_devices = {
        'detector': Attach('Detector device with size information',
                           Detector),
        'beamstop': Attach('Beam stop device with size information',
                           BeamStop),
        'detpos': Attach('Detector position device', Readable),
        'wavelength': Attach('Wavelength device', Readable),
    }

    parameter_overrides = {
        'unit': Override(mandatory=False, volatile=True),
        'fmtstr': Override(default='%3.f - %3.f'),
    }

    def doRead(self, maxage=0):
        bs = self._attached_beamstop
        return qrange(self._attached_wavelength.read(maxage),
                      self._attached_detpos.read(0),
                      bs.slots[bs.shape][1][0] / 2.,
                      self._attached_detector.size[0] / 2.)

    def doReadUnit(self):
        unit = self._attached_wavelength.unit
        return '%s-1' % unit
Exemple #2
0
class TubeAngle(HasLimits, Moveable):
    """Angle of the tube controlled by the yoke."""

    attached_devices = {
        'yoke': Attach('Yoke device', Moveable),
    }

    parameters = {
        'yokepos':
        Param('Position of yoke from pivot point',
              type=floatrange(0, 20000),
              unit='mm',
              default=11000),
    }

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

    def doRead(self, maxage=0):
        return degrees(atan2(self._attached_yoke.read(maxage), self.yokepos))

    def doStart(self, target):
        self._attached_yoke.move(tan(radians(target)) * self.yokepos)

    def doReadAbslimits(self):
        yokelimits = self._attached_yoke.userlimits
        return (degrees(atan2(yokelimits[0], self.yokepos)),
                degrees(atan2(yokelimits[1], self.yokepos)))
Exemple #3
0
class ChopperDiscTranslation(ChopperDiscTranslationBase, VirtualMotor):
    """Position of chopper disc along the x axis.

    Since the chopper disc 2 can be translated, the chopper speed must be low
    enough (around 0, defined by its precision).

    The change of speed must be blocked if the translation device is not at
    a defined position.
    """

    parameter_overrides = {
        'speed': Override(default=0.1),
        'fmtstr': Override(default='%.0f'),
    }

    def doRead(self, maxage=0):
        try:
            val = VirtualMotor.doRead(self, maxage)
            if not self.isAtTarget(val):
                # This is because of cutting to int values. If the target is
                # lower than val the cut would give the wrong result.
                if self.target - val < 0:
                    val += 1.
            return self.valuetype(val)
        except ValueError:
            return self.target
Exemple #4
0
class MasterSlaveMotor(Moveable):
    """Combined master slave motor with possibility to apply a scale to the
    slave motor."""

    attached_devices = {
        "master": Attach("Master motor controlling the movement", Moveable),
        "slave": Attach("Slave motor following master motor movement",
                        Moveable),
    }

    parameters = {
        "scale":
        Param("Factor applied to master target position as slave "
              "position",
              type=float,
              default=1),
    }

    parameter_overrides = {
        "unit": Override(mandatory=False),
        "fmtstr": Override(default="%.3f %.3f"),
    }

    def _slavePos(self, pos):
        return self.scale * pos

    def doRead(self, maxage=0):
        return [
            self._attached_master.read(maxage),
            self._attached_slave.read(maxage)
        ]

    def doStart(self, pos):
        self._attached_master.move(pos)
        self._attached_slave.move(self._slavePos(pos))

    def doIsAllowed(self, pos):
        faultmsgs = []
        messages = []
        for dev in [self._attached_master, self._attached_slave]:
            allowed, msg = dev.isAllowed(pos)
            msg = dev.name + ': ' + msg
            messages += [msg]
            if not allowed:
                faultmsgs += [msg]
        if faultmsgs:
            return False, ', '.join(faultmsgs)
        return True, ', '.join(messages)

    def doReadUnit(self):
        return self._attached_master.unit

    def valueInfo(self):
        return Value(self._attached_master.name, unit=self.unit,
                     fmtstr=self._attached_master.fmtstr), \
               Value(self._attached_slave.name, unit=self.unit,
                     fmtstr=self._attached_slave.fmtstr)
Exemple #5
0
class ListmodeSink(FileSink):
    """Writer for the list mode files via QMesyDAQ itself."""

    attached_devices = {
        'image': Attach('Image device to set the file name', Image),
    }

    parameter_overrides = {
        'settypes': Override(default=[POINT]),
        'filenametemplate': Override(mandatory=False, userparam=False,
                                     default=['D%(pointcounter)07d.mdat']),
    }

    handlerclass = ListmodeSinkHandler
Exemple #6
0
class Deflector(Axis):
    """Special axis to store additional parameters for the deflectors."""

    parameters = {
        'reflectivity':
        Param('Reflectivity of the material',
              type=floatrange(0, 1),
              settable=False,
              userparam=False,
              default=0.9),
        'length':
        Param('Length of the deflector blades',
              type=floatrange(0, None),
              settable=False,
              prefercache=False,
              default=40.,
              unit='mm'),
        'thickness':
        Param('Thickness of the wafer',
              type=floatrange(0, None),
              settable=False,
              userparam=False,
              default=0.55,
              unit='mm'),
    }

    parameter_overrides = {
        'unit': Override(mandatory=False, default='deg'),
    }
Exemple #7
0
class FITSImageSink(ImageSink):
    """Writes data to a FITS (Flexible image transport system) format file.

    NICOS headers are also written into the file using the standard FITS header
    facility, with HIERARCH type keys.

    Requires the pyfits library to be installed.
    """

    parameter_overrides = {
        'filenametemplate': Override(default=['%(pointcounter)08d.fits']),
    }

    handlerclass = FITSImageSinkHandler

    def doPreinit(self, _mode):
        # Stop creation of the FITSImageSink as it would make no sense
        # without pyfits.
        if pyfits is None:
            raise NicosError(
                self, 'pyfits module is not available. Check'
                ' if it is installed and in your PYTHONPATH')

    def isActiveForArray(self, arraydesc):
        return len(arraydesc.shape) == 2
Exemple #8
0
class Experiment(BaseExperiment):
    """TOFTOF experiment.

    The service experiment will be filled up with instrument specific data.
    """

    parameter_overrides = {
        'envlist': Override(category='general'),
    }

    def _newPropertiesHook(self, proposal, kwds):
        if self.proptype == 'service':
            upd = {
                'title': 'Maintenance',
                'users': [{
                    'name': session.instrument.responsible
                }],
                'localcontacts': [{
                    'name': session.instrument.responsible
                }],
                'default_sample': 'Unknown',
            }
            kwds.update(upd)
            return kwds
        return BaseExperiment._newPropertiesHook(self, proposal, kwds)
Exemple #9
0
class NOKPosition(PolynomFit, Coder):
    """Device to read the current Position of a NOK.

    The Position is determined by a ratiometric measurement between two
    analogue voltages measured with i7000 modules via taco.

    As safety measure, the reference voltage obtained is checked to be in some
    configurable limits.
    """

    attached_devices = {
        'measure': Attach('Sensing Device (Poti)', Readable),
        'reference': Attach('Reference Device', Readable),
    }

    parameters = {
        'length': Param('Length... ????', type=float, mandatory=False),
        # fun stuff, not really needed....
        'serial': Param('Serial number', type=int, mandatory=False),
    }

    parameter_overrides = {
        'fmtstr': Override(default='%.3f'),
        'unit': Override(default='mm', mandatory=False),
    }

    def doReset(self):
        multiReset(self._adevs)

    def doRead(self, maxage=0):
        """Read basically two (scaled) voltages.

         - value from the poti (times a sign correction for top mounted potis)
         - ref from a reference voltage, scaled by 2 (Why???)

        Then it calculates the ratio poti / ref.
        Then this is put into a correcting polynom of at least first order
        Result is then offset + mul * <previously calculated value>
        """
        poti = self._attached_measure.read(maxage)
        ref = self._attached_reference.read(maxage)

        self.log.debug('Poti vs. Reference value: %f / %f', poti, ref)
        # apply simple scaling
        return self._fit(poti / ref)
Exemple #10
0
class ChopperDisc2(ChopperDisc2Base, ChopperDisc):
    """Chopper disc 2 device."""
    parameter_overrides = {
        'pos':
        Override(type=intrange(0, 5),
                 volatile=True,
                 settable=True,
                 fmtstr='%d',
                 unit=''),
    }
Exemple #11
0
class NOKMotorIPC(CanReference, IPCMotor):
    """Basically a IPCMotor with referencing."""

    parameters = {
        'refpos':
        Param('Reference position in phys. units',
              unit='main',
              type=none_or(float),
              mandatory=True),
    }

    parameter_overrides = {
        'zerosteps': Override(default=500000, mandatory=False),
        'unit': Override(default='mm', mandatory=False),
        'backlash': Override(type=floatrange(0.0, 0.0)),  # only 0 is allowed!
        'speed': Override(default=10),
        'accel': Override(default=10),
        'slope': Override(default=2000),
        'confbyte': Override(default=48),
        'divider': Override(type=intrange(-1, 7)),
    }

    def doInit(self, mode):
        if mode != SIMULATION:
            self._attached_bus.ping(self.addr)
            if self._hwtype == 'single':
                if self.confbyte != self.doReadConfbyte():
                    self.doWriteConfbyte(self.confbyte)
                    self.log.warning(
                        'Confbyte mismatch between setup and card'
                        ', overriding card value to 0x%02x', self.confbyte)
            # make sure that the card has the right "last steps"
            # This should not applied at REFSANS, since it disturbs the running
            # TACO server settings
            # if self.steps != self.doReadSteps():
            #     self.doWriteSteps(self.steps)
            #     self.log.warning('Resetting stepper position to last known '
            #                      'good value %d', self.steps)
            self._type = 'stepper motor, ' + self._hwtype
        else:
            self._type = 'simulated stepper'

    def doReference(self):
        bus = self._attached_bus
        bus.send(self.addr, 34)  # always go forward (positive)
        bus.send(self.addr, 47, self.speed, 3)  # reference with normal speed
        # may need to sleep a little here....
        session.delay(0.1)
        self.wait()
        self.doSetPosition(self.refpos)
Exemple #12
0
class TIFFLaueSink(ImageSink):
    parameter_overrides = {
        'filenametemplate':
        Override(mandatory=False,
                 settable=False,
                 userparam=False,
                 default=['%(proposal)s_%(pointcounter)07d.tif']),
    }

    handlerclass = TiffLaueImageSinkHandler

    def isActiveForArray(self, arraydesc):
        return len(arraydesc.shape) == 2
Exemple #13
0
class NOKMonitoredVoltage(Sensor):
    """Return a scaled and monitored Analogue value.

    Also checks the value to be within certain limits, if not, complain.
    """

    parameters = {
        'reflimits':
        Param('None or Limits to check the reference: low, warn, '
              'high',
              type=none_or(tupleof(float, float, float)),
              settable=False,
              default=None),
        'scale':
        Param('Scaling factor', type=float, settable=False, default=1.),
    }
    parameter_overrides = {
        'unit': Override(default='V', mandatory=False),
    }

    def doInit(self, mode):
        if self.reflimits is not None:
            if not (0 <= self.reflimits[0] <= self.reflimits[1] <=
                    self.reflimits[2]):
                raise ConfigurationError(
                    self, 'reflimits must be in ascending'
                    ' order!')

    def doRead(self, maxage=0):
        value = self.scale * Sensor.doRead(self, maxage)
        if self.reflimits is not None:
            if abs(value) > self.reflimits[2]:
                raise HardwareError(
                    self, 'Reference voltage (%.2f) above '
                    'maximum (%.2f)' % (value, self.reflimits[2]))
            if abs(value) < self.reflimits[0]:
                raise HardwareError(
                    self, 'Reference voltage (%.2f) below '
                    'minimum (%.2f)' % (value, self.reflimits[0]))
            if abs(value) < self.reflimits[1]:
                self.log.warning(
                    'Reference voltage (%.2f) seems rather low, '
                    'should be above %.2f', value, self.reflimits[1])
        return value

    def doStatus(self, maxage=0):
        try:
            self.doRead(maxage)
            return Sensor.doStatus(self, maxage)
        except HardwareError as err:
            return status.ERROR, repr(err)
Exemple #14
0
class ChopperDisc(ChopperDiscBase, VirtualMotor):
    parameters = {
        'crc': Param('Counter-rotating mode',
                     type=int,
                     default=1,
                     settable=True),
        'slittype': Param('Slit type', type=int, default=1, settable=True),
    }

    parameter_overrides = {
        'jitter': Override(default=2),
        'curvalue': Override(default=1200),
        'speed': Override(default=50),
    }

    def doReadCurrent(self):
        speed = self.read()
        if abs(speed) > self.jitter:
            return 2 + speed * 0.001
        return 0

    def doPoll(self, n, maxage):
        self._pollParam('current')
Exemple #15
0
class DebugDataSink(DataSink):
    """Debug data sink.

    This device only displays some data handling specific information during
    the counts and scans.  It does not persist any data to files.

    .. note:: It should be used only for debugging purposes.
    """

    handlerclass = DebugDataSinkHandler

    parameter_overrides = {
        'settypes': Override(default=[POINT, SCAN, SUBSCAN, BLOCK]),
    }
Exemple #16
0
class DigitalOutput(Pyro4Device, HasTimeout, Moveable):
    """
    A devices that can set and read a digital value corresponding to a
    bitfield.
    """

    valuetype = int
    parameter_overrides = {
        'unit': Override(mandatory=False),
        'timeout': Override(default=1.),
    }

    def doRead(self, maxage=0):
        with globalLock, self._dev:
            return self._dev.afp_state_get()

    def doStart(self, value):
        if self.read(0) != value:
            with globalLock, self._dev:
                self._dev.afp_flip_do()

    def doStatus(self, maxage=0):
        return status.OK, ""
Exemple #17
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()
Exemple #18
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
Exemple #19
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'),
    }
Exemple #20
0
class RobotMotor(Motor):
    """CARESS motor using the CARESS robot devices.

    CARESS uses a special device to control the speed.
    """

    attached_devices = {
        'speedmotor':
        Attach('Device to control the axis speed', Motor, multiple=False),
    }

    parameter_overrides = {
        'speed': Override(volatile=True),
    }

    def doReadSpeed(self):
        return self._attached_speedmotor.read()

    def doWriteSpeed(self, value):
        self._attached_speedmotor.maw(value)
Exemple #21
0
class GeometricBlur(Readable):
    """Special device to calculate geometric blur.

    Calculated from collimation and sample to detector distance."""

    attached_devices = {
        'l': Attach('Distance device', Readable),
        'd': Attach('Pinhole', Readable),
        'sdd': Attach('Sample Detector Distance', Readable),
    }

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

    def doInit(self, mode):
        units = set(d.unit for d in self._adevs.values())
        if len(units) != 1:
            raise ConfigurationError(
                self, 'different units for L, d and sdd '
                '(%s vs %s vs %s)' %
                (self._attached_l.unit, self._attached_d.unit,
                 self._attached_sdd.unit))
        if 'mm' not in units:
            raise ConfigurationError(
                self, "attached devices units have to be "
                "'mm'")

    def doRead(self, maxage=0):
        try:
            ret = float(self._attached_sdd.read(maxage)) * \
                float(self._attached_d.read(maxage)) / \
                float(self._attached_l.read(maxage))
            return 1000 * ret  # convert to um
        except ValueError:
            ret = 0
        return ret

    def doReadUnit(self):
        return 'um'
Exemple #22
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)
Exemple #23
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, ''
Exemple #24
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
Exemple #25
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
Exemple #26
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)]
Exemple #27
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]
Exemple #28
0
class Group(Readable):

    valuetype = bool

    parameters = {
        'bitlist':
        Param(
            'Definition of a bit list',
            type=listof(str),
            settable=False,
            userparam=False,
        ),
        'okmask':
        Param(
            'Mask to define the bits results OK status',
            type=int,
            settable=False,
            userparam=False,
        ),
    }

    attached_devices = {
        'shs': Attach('shs', Readable),
    }

    parameter_overrides = {
        'unit': Override(default='', volatile=True, mandatory=False),
        'fmtstr': Override(default='%s'),
    }

    def doReadUnit(self):
        return ''

    _register = {
        'Shutter': (0, 0),  # 0x0000
        'Ampeltest_inv': (3, 0),  # 0x0001
        'Betreten_Verboten_inv': (3, 1),  # 0x0002
        'Hupentest_inv': (3, 2),  # 0x0004
        'Schluesselschalter_Wartung_inv': (3, 3),  # 0x0008
        'Tuer_PO_auf': (3, 6),  # 0x0040
        'Tuer_PO_zu': (3, 7),  # 0x0080
        'Schnellschluss-Shutter_auf': (3, 8),  # 0x0100
        'Schnellschluss-Shutter_zu': (3, 9),  # 0x0200
        '6-fach-Shutter_auf': (3, 10),  # 0x0400
        '6-fach-Shutter_zu': (3, 11),  # 0x0800
        'Verbindung_zu_Warte_iO': (3, 12),  # 0x1000
        'Freigabe_von_Warte_fuer_ESShutter': (3, 13),  # 0x2000
        'Instrumentenverantwortlicher': (3, 14),  # 0x4000
        'Not-Aus_Kreis_inv': (3, 15),  # 0x8000
        'Verbindungstuer': (4, 8),  # 0x0100
        'Tuer_SR_auf': (4, 10),  # 0x0400
        'Tuer_SR_zu': (4, 11),  # 0x0800
        'externer_User_Kontakt_A': (5, 0),  # 0x0001
        'externer_User_Kontakt_B': (5, 1),  # 0x0002
        'PO-Aus-Schalter_1': (5, 2),  # 0x0004
        'PO-Aus-Schalter_2': (5, 4),  # 0x0008
        'Drucksensor_CB': (6, 0),  # 0x0001
        'Drucksensor_SFK': (6, 1),  # 0x0002
        'Drucksensor_Tube': (6, 2),  # 0x0004
        'Chopper_Drehzahl': (6, 3),  # 0x0008
        'Druck_service_inv': (6, 4),  # 0x0010
        'Personenschluessel_Terminal': (6, 11),  # 0x0800
        'Freigabe_Taster': (6, 12),  # 0x1000
        'Lampentest_inv': (6, 13),  # 0x2000
        'Endschalter_Ex_Shutter_inv': (6, 14),  # 0x4000
        'Handbetrieb_tube_inv': (6, 15),  # 0x8000
        'Probenort_Geraeumt_inv': (14, 2),  # 0x0004
        'Streurohr_Geraeumt_inv': (14, 3),  # 0x0008
        'IV_key_1': (15, 8),  # 0x0100
        'IV_key_2': (15, 9),  # 0x0200
        'gelb_inv': (17, 3),  # 0x0008
        'Freigabe_EIN': (17, 10),  # 0x0400
        'rot_inv': (18, 8),  # 0x0100
        'Warnschilder': (18, 9),  # 0x0200
        'Keine_Freigabe_Hub_Streurohr': (18, 10),  # 0x0400
        # nicht konzeptionell aber geht
        'Freigabe_Hub_Streurohr_inv': (18, 10),  # 0x0400
        'shutterzustand': (18, 11),  # 0x0800
        'gruen_inv': (18, 12),  # 0x0800
    }

    def _do_read_bits(self):
        res = 0
        # take values from cache to avoid to many reads
        raw = self._attached_shs.read()
        for i, key in enumerate(self.bitlist):
            address, bit = self._register[key]
            res |= ((raw[address] & (1 << bit)) >> bit) << i
        return res

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

    def doStatus(self, maxage=0):
        bits = self._do_read_bits()
        if bits == self.okmask:
            return status.OK, ''
        return status.WARN, ', '.join(
            self.format_statusbits(bits ^ self.okmask, self.bitlist))

    def format_statusbits(self, sword, labels, start=0):
        """Return a list of labels according to bit state in `sword` starting
        with bit `start` and the first label in `labels`.
        """
        smsg = []
        for i, lbl in enumerate(labels, start):
            if sword & (1 << i) and lbl:
                smsg.append(lbl)
        return smsg
Exemple #29
0
class SANSNexusSink(NexusSink):
    parameter_overrides = {
        'settypes': Override(settable=True),
    }
Exemple #30
0
class StackedAxis(HasLimits, HasPrecision, Moveable):
    """Device were two axes stacked.

    Both axes can be moved individually but the result of position and target
    is the sum of both position.  One of the axes (at the moment always the
    ``top`` axis) is preferred in use since it is cheaper (in time) to move it.

    ``HasPrecision`` is needed for the TAS monochromator device.
    """

    attached_devices = {
        'bottom': Attach('bottom axis', Moveable),
        'top': Attach('top axis', Moveable),
    }

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

    def doRead(self, maxage=0):
        self.log.debug('doRead: %s', maxage)
        return self._attached_bottom.read(maxage) + \
            self._attached_top.read(maxage)

    def doStart(self, target):
        targets = self._calc_targets(target)
        # The order is normally free to choose, but in the current application
        # the bottom device moves blocking
        self._attached_top.move(targets[1])
        self._attached_bottom.move(targets[0])

    def doIsAllowed(self, target):
        targets = self._calc_targets(target)
        for dev, t in zip([self._attached_bottom, self._attached_top],
                          targets):
            allowed = dev.isAllowed(t)
            if not allowed[0]:
                return allowed
        return True, ''

    def _calc_targets(self, target):
        """Calculate individual target positions from the sum target.

        Due to the limited range of movement of each individual axis the
        targets must be calculated for each axis.  Since the top axis is
        cheaper in time it will preferred in use and even the longer way
        if moving both.
        """
        targets = (0, 0)

        bpos = self._attached_bottom.read(0)
        lt = self._attached_top.userlimits

        # Simply move the top axis
        if lt[0] + bpos <= target <= lt[1] + bpos:
            targets = bpos, target - bpos
        else:
            tpos = self._attached_top.read(0)
            if target > (bpos + tpos):
                targets = target - lt[1], lt[1]
            else:
                targets = target - lt[0], lt[0]
        return targets

    def doReadAbslimits(self):
        return [
            sum(x) for x in zip(self._attached_bottom.abslimits,
                                self._attached_top.abslimits)
        ]