예제 #1
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'),
    }
예제 #2
0
class DetectorMixin(DeviceMixinBase):
    """Provide the size parameter."""
    parameters = {
        'size':
        Param('size of the active detector area',
              type=tupleof(floatrange(0), floatrange(0)),
              settable=False,
              mandatory=False,
              unit='mm',
              default=(1000., 1000.)),
    }
예제 #3
0
파일: sample.py 프로젝트: umithardal/nicos
class Sample(BaseSample):
    """Refsans specific sample."""

    parameters = {
        'width': Param('width of sample lateral',
                       type=floatrange(0, 100), settable=True, default=100,
                       unit='mm', fmtstr='%.1f', category='sample'),
        'length': Param('length of sample in beam',
                        type=floatrange(0, 100), settable=True, default=100,
                        unit='mm', fmtstr='%.1f', category='sample'),
    }
예제 #4
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)))
예제 #5
0
class MoveName(HasLimits, ReadName, Moveable):

    parameters = {
        'speed': Param('speed for specific action',
                       type=floatrange(0, 600), unit='cm x cm/min',
                       default=600, settable=True),
    }

    def _command(self, ss):
        self.log.debug('_command: >%s<', ss)
        res = self._attached_comm.communicate(ss)
        if res != 'ack':
            self.log.error('unexpected result >%s<', res)
        else:
            self.log.debug('result >%s<', res)

    def doStart(self, pos):
        self._command('%s:%f:%f' % (self.name[5:], pos, self.speed))

    def doStop(self):
        self._command('stop')

    def open(self):
        self._command('open')

    def close(self):
        self._command('close')
예제 #6
0
class HasPrecision(DeviceMixinBase):
    """
    Mixin class for Readable and Moveable devices that want to provide a
    'precision' parameter.

    This is mainly useful for user info, and for high-level devices that have
    to work with limited-precision subordinate devices.

    The class also implements a default `doIsAtTarget` method, which checks
    the value is within the precision.
    """
    parameters = {
        'precision':
        Param(
            'Precision of the device value (allowed deviation '
            'of stable values from target)',
            unit='main',
            fmtstr='main',
            type=floatrange(0),
            settable=True,
            category='precisions'),
    }

    def doIsAtTarget(self, pos):
        if self.target is None:
            return True  # avoid bootstrapping problems
        return abs(self.target - pos) <= self.precision
예제 #7
0
class HTFTemperatureController(TemperatureController):

    attached_devices = {
        'maxheater': Attach('Maximum heater output device', AnalogOutput),
    }

    parameters = {
        'maxheateroutput':
        Param('Maximum heater output',
              type=floatrange(0, 100),
              userparam=True,
              settable=True,
              volatile=True,
              unit='%'),
        'sensortype':
        Param('Currently used sensor type',
              type=str,
              userparam=True,
              settable=False,
              volatile=True,
              mandatory=False),
    }

    def doWriteMaxheateroutput(self, value):
        self._attached_maxheater.move(value)

    def doReadMaxheateroutput(self):
        return self._attached_maxheater.read(0)

    def doReadSensortype(self):
        return self._dev.sensortype
예제 #8
0
class DimetixLaser(CanDisable, HasOffset, Readable):

    attached_devices = {
        'signal': Attach('signal strength device', Readable),
        'value': Attach('value device', Readable),
    }

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

    parameters = {
        'signallimit':
        Param('minimal signal strength for valid reading',
              type=floatrange(0.),
              default=8000),
        'invalidvalue':
        Param('value to indicate invalid value',
              type=intrange(-2000, -2000),
              default=-2000),
    }

    def doRead(self, maxage=0):
        if self._attached_signal.read(maxage) < self.signallimit:
            return self.invalidvalue
        return self._attached_value.read(maxage)

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

    def doPoll(self, n, maxage):
        self._attached_signal.poll(n, maxage)
        self._attached_signal.poll(n, maxage)
예제 #9
0
def test_oneofdict_or():
    m = dict(a=1, b=2)
    v = oneofdict_or(m, floatrange(0, 10))
    assert v('a') == 1.0
    assert v('b') == 2.0
    assert v(5) == 5.0
    assert raises(ValueError, v, 'c')
    assert raises(ValueError, v, 11)
예제 #10
0
class PseudoNOK(DeviceMixinBase):
    """Placeholder device, doing nothing, but storing some locational data."""

    parameters = {
        'nok_start':
        Param('Start of the  NOK (beginning from NLE2b)',
              type=floatrange(-1),
              settable=False,
              mandatory=False,
              unit='mm',
              default=-1),
        'nok_end':
        Param('End of the NOK (beginning from NLE2b)',
              type=floatrange(-1),
              default=-1,
              settable=False,
              mandatory=False,
              unit='mm'),
        'nok_gap':
        Param('Gap after the NOK (beginning from NLE2b)',
              type=floatrange(0),
              default=0,
              settable=False,
              mandatory=False,
              unit='mm'),
        'masks':
        Param('Masks for this NOK', type=dict, settable=False, default={}),
        'length':
        Param('NOK length',
              type=floatrange(0),
              settable=False,
              mandatory=False,
              volatile=True,
              unit='mm'),
    }

    def doReadLength(self):
        return self.nok_end - self.nok_start
예제 #11
0
파일: ipc.py 프로젝트: umithardal/nicos
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)
예제 #12
0
class SampleChanger(IsController, BaseSequencer):
    """The PGAA sample changer device."""

    hardware_access = False

    valuetype = intrange(1, 16)

    attached_devices = {
        'motor': Attach('Stage rotation', Moveable),
        'push': Attach('Moving sample to rotation stage', Moveable),
    }

    parameters = {
        'delay':
        Param('Time to wait until the push device is finished',
              type=floatrange(0, 2),
              default=2,
              settable=False,
              unit='s'),
    }

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

    def isAdevTargetAllowed(self, dev, target):
        if dev == self._attached_motor:
            if self._attached_push._attached_sensort.read(0) in ['down', 0]:
                return False, '"push" is not in top position or moving'
        elif dev == self._attached_push:
            if self._attached_motor.status(0)[0] == status.BUSY:
                return False, 'motor moving'
            if self._attached_motor.read(0) not in list(range(1, 17)):
                return False, 'invalid motor position'
        return True, ''

    def _generateSequence(self, target):
        seq = []
        if target != self.doRead(0):
            seq.append(SeqDev(self._attached_push, 'up', stoppable=False))
            seq.append(SeqSleep(self.delay))
            seq.append(
                SeqSampleMotor(self._attached_motor, target, stoppable=False))
            seq.append(SeqSleep(self.delay))
            seq.append(SeqDev(self._attached_push, 'down', stoppable=False))
        return seq

    def doRead(self, maxage=0):
        return round(self._attached_motor.read(maxage))
예제 #13
0
def test_floatrange():
    assert floatrange(0, 10)(5) == 5.0
    assert floatrange(1, 3)() == 1.0
    assert raises(ValueError, floatrange(0, 10), 15.)
    assert raises(ValueError, floatrange(0, 10), 'x')
    assert raises(ValueError, floatrange, 2, 1)

    assert floatrange(0)(5) == 5.0
    assert raises(ValueError, floatrange(0), -5)
예제 #14
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
예제 #15
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'),
    }
예제 #16
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.)
예제 #17
0
class CPULoad(Readable):

    parameters = {
        'interval':
        Param(
            'Interval for load detection',
            type=floatrange(0.1, 60),
            default=0.1,
            settable=False,
        ),
        'lastvalue':
        Param('Last obtained value',
              type=float,
              internal=True,
              mandatory=False,
              default=0.0),
    }

    def doInit(self, mode):
        if mode == SIMULATION:
            return
        # create only run ONE thread: in the poller
        # it may look stupid, as the poller already has a thread polling read()
        # now imagine several such devices in a setup.... not so stupid anymore
        if session.sessiontype == POLLER:
            self._thread = createThread('measure cpuload', self._run)

    def doWriteInterval(self, value):
        self.pollinterval = max(1, 2 * value)
        self.maxage = max(3, 5 * value)

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

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

    def _run(self):
        while True:
            self._setROParam('lastvalue', psutil.cpu_percent(self.interval))
예제 #18
0
class MirrorSample(Sample):

    parameters = {
        'length': Param('Length of the mirror',
                        type=floatrange(0), settable=True, userparam=True,
                        unit='mm', default=200),
        'thickness': Param('Thickness of the mirror',
                           type=floatrange(0), settable=True, userparam=True,
                           unit='mm', default=8),
        'height': Param('Height of the mirror',
                        type=floatrange(0), settable=True, userparam=True,
                        unit='mm', default=32),
        'm': Param('Reflection index (m)',
                   type=floatrange(1), settable=True, userparam=True,
                   unit='', default=2),
        'alfa': Param('Linear decrease of reflectivity m > 1',
                      type=floatrange(0), settable=True, userparam=True,
                      unit='', default=4.25),
        'waviness': Param('Waviness of the mirror surface',
                          type=floatrange(0), settable=True, userparam=True,
                          unit='deg', default=0.01),
        'rflfile': Param('Reflectivity file',
                         type=oneof(None, *rfl_filenames), settable=True,
                         userparam=True, default=None),
    }

    def getReflectivityFile(self):
        return rfl_filenames.get(self.rflfile, None)

    def _applyParams(self, number, parameters):
        """Apply sample parameters.
        """
        Sample._applyParams(self, number, parameters)
        if 'length' in parameters:
            self.length = parameters['length']
        if 'thickness' in parameters:
            self.thickness = parameters['thickness']
        if 'height' in parameters:
            self.height = parameters['height']
        if 'm' in parameters:
            self.m = parameters['m']
        if 'alfa' in parameters:
            self.alfa = parameters['alfa']
        if 'waviness' in parameters:
            self.waviness = parameters['waviness']
        if 'rflfile' in parameters:
            self.rflfile = parameters['rflfile']
예제 #19
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)
예제 #20
0
class Sharpness(PostprocessPassiveChannel):

    parameters = {
        'thr3':
        Param('Threshold for 3x3 pixels',
              type=intrange(0, 1000),
              settable=True,
              userparam=True,
              default=25),
        'thr5':
        Param('Threshold for 5x5 pixels',
              type=intrange(0, 1000),
              settable=True,
              userparam=True,
              default=100),
        'thr7':
        Param('Threshold for 7x7 pixels',
              type=intrange(0, 1000),
              settable=True,
              userparam=True,
              default=400),
        'sig_log':
        Param('Sig log',
              type=floatrange(0, 1),
              settable=True,
              userparam=True,
              default=0.8),
    }

    def valueInfo(self):
        return Value(name=self.name, type='counter', fmtstr='%.3f'),

    def getReadResult(self, arrays, results, quality):
        arr = np.array(arrays[0], dtype=int)
        return [
            scharr_filter(
                gam_rem_adp_log(arr, self.thr3, self.thr5, self.thr7,
                                self.sig_log))
        ]
예제 #21
0
파일: secop.py 프로젝트: umithardal/nicos
 def _double(min=None, max=None, **kwds):  # pylint: disable=redefined-builtin
     if max is None:
         if min is None:
             return float
         return floatrange(min)
     return floatrange("float('-inf')" if min is None else min, max)
예제 #22
0
class AndorHFRCamera(PyTangoDevice, UsesFastshutter, ImageChannelMixin,
                     ActiveChannel):
    """Camera communicating with Andor Basic script."""

    TRIGGER_MODES = {
        'internal': 0,
        'external': 1,
        'external start': 6,
        'external exposure': 7,
        'external exposure FVB': 9,
    }

    SHUTTER_MODES = {
        'rolling': 0,
        'global': 1,
    }

    parameters = {
        'bin':
        Param('Binning (x,y)',
              type=tupleof(intrange(1, 64), intrange(1, 64)),
              settable=True,
              default=(1, 1),
              category='general'),
        'triggermode':
        Param('Triggering signal definition',
              type=oneof(*TRIGGER_MODES),
              settable=True,
              default='internal',
              category='general'),
        'shuttermode':
        Param('Shutter mode setting',
              type=oneof(*SHUTTER_MODES),
              settable=True,
              default='rolling',
              category='general'),
        'spooldirectory':
        Param('Path to spool the images on the remote '
              'machine',
              type=absolute_win_path,
              category='general'),
        'extratime':
        Param('Extra sleeping time to sync with Andors Basic',
              type=floatrange(0),
              default=3,
              settable=True),
        '_started':
        Param('Cached counting start flag',
              type=none_or(float),
              default=None,
              settable=True,
              internal=True),
    }

    def doPreinit(self, mode):
        PyTangoDevice.doPreinit(self, mode)
        self.log.info('Checking if camera script is ready!')
        try:
            msg = self._dev.communicate('ready?')
            if msg.strip() != 'ready!':
                raise CommunicationError(
                    self, 'Camera script gave wrong '
                    'answer - please check!')
        except NicosError:
            self.log.warning('Camera is not responding - please start '
                             'tomography script on camera first!')
            raise

    def doReset(self):
        self.log.info('Checking if camera script is ready!')
        try:
            msg = self._dev.communicate('ready?')
            if msg.strip() != 'ready!':
                raise CommunicationError(
                    self, 'Camera script gave wrong '
                    'answer - please check!')
        except NicosError:
            self.log.warning('Camera is not responding - please start '
                             'tomography script on camera first!')
            raise

    def doInit(self, mode):
        self._started = None

    def valueInfo(self):
        # no readresult by default
        return ()

    def doStart(self):
        self.bin = self.bin
        self.shuttermode = self.shuttermode
        self.triggermode = self.triggermode
        self.doWriteSpooldirectory(self.spooldirectory)
        kct = float(self._query_value('GetKineticCycleTime'))
        self.log.info('kct: %s', kct)
        self._counttime = self._knumber * kct + 3
        self.log.info('measure time = %s s', self._counttime)
        self.openFastshutter()
        self._write_command('count')
        self._started = currenttime()
        self.log.debug('started: %s', self._started)

    def doSetPreset(self, **presets):
        for preset, val in presets.items():
            self._write_presets(preset, val)
        self._write_presets(
            'spoolfile',
            presets.get('spoolfile', session.experiment.sample.samplename))

    def presetInfo(self):
        return ['exptime', 'number', 'cycletime', 'spoolfile']

    def doStatus(self, maxage=0):
        if self._started:
            if (self._started + self._counttime) > currenttime():
                return status.BUSY, 'counting'
        return status.OK, 'idle'

    def doStop(self):
        self._started = None
        self.status(0)
        self._attached_fastshutter.maw('closed')

    def doFinish(self):
        self._started = None
        # self._attached_fastshutter.maw('closed')

    def doWriteTriggermode(self, value):
        self._write_command('SetTriggerMode %d' % self.TRIGGER_MODES[value])

    def doWriteShuttermode(self, value):
        self._write_command('SetShutterMode %d' % self.SHUTTER_MODES[value])

    def doWriteSpooldirectory(self, value):
        self._write_command('SetSpoolDirectory %s' % value)

    def doWriteBin(self, value):
        self._write_command('SetHBin %d' % value[0])
        self._write_command('SetVBin %d' % value[1])

    def _write_command(self, command):
        ret = self._dev.communicate(command)
        if not ret.startswith('ACK'):
            if ret.startswith('ERR'):
                raise InvalidValueError(self,
                                        'Command: %s is invalid ' % command)
            raise InvalidValueError(
                self, 'Command: %s has invalid '
                'parameters' % command)

    def _query_value(self, request):
        return self._dev.communicate(request)

    def _write_presets(self, preset, value):
        if preset == 'exptime':
            self._write_command('SetExposureTime %f' % value)
        elif preset == 'number':
            self._write_command('SetKineticNumber %d' % value)
            self._knumber = int(value)
        elif preset == 'cycletime':
            self._write_command('SetKineticCycleTime %f' % value)
        elif preset == 'spoolfile':
            self._write_command('SetSpoolFileName %s' % value)
예제 #23
0
class DoubleMotorNOK(SequencerMixin, CanReference, PseudoNOK, HasPrecision,
                     Moveable):
    """NOK using two axes.

    If backlash is negative, approach form the negative side (default),
    else approach from the positive side.
    If backlash is zero, don't mind and just go to the target.
    """

    attached_devices = {
        'motor_r': Attach('NOK moving motor, reactor side', Moveable),
        'motor_s': Attach('NOK moving motor, sample side', Moveable),
    }

    parameters = {
        'mode':
        Param('Beam mode',
              type=oneof(*MODES),
              settable=True,
              userparam=True,
              default='ng',
              category='experiment'),
        'nok_motor':
        Param('Position of the motor for this NOK',
              type=tupleof(float, float),
              settable=False,
              unit='mm',
              category='general'),
        'inclinationlimits':
        Param('Allowed range for the positional '
              'difference',
              type=limits,
              mandatory=True),
        'backlash':
        Param('Backlash correction in phys. units',
              type=float,
              default=0.,
              unit='main'),
        'offsets':
        Param('Offsets of NOK-Motors (reactor side, sample side)',
              type=tupleof(float, float),
              default=(0., 0.),
              settable=False,
              unit='main',
              category='offsets'),
    }

    parameter_overrides = {
        'precision':
        Override(type=floatrange(0, 100)),
        'masks':
        Override(type=dictwith(**{name: float
                                  for name in MODES}),
                 unit='',
                 mandatory=True),
    }

    valuetype = tupleof(float, float)
    _honor_stop = True

    @lazy_property
    def _devices(self):
        return self._attached_motor_r, self._attached_motor_s

    def doInit(self, mode):
        for dev in self._devices:
            if hasattr(dev, 'backlash') and dev.backlash != 0:
                self.log.warning(
                    'Attached Device %s should not have a '
                    'non-zero backlash!', dev)

    def doRead(self, maxage=0):
        return [
            dev.read(maxage) - ofs - self.masks[self.mode]
            for dev, ofs in zip(self._devices, self.offsets)
        ]

    def doIsAllowed(self, targets):
        target_r, target_s = targets
        target_r += self.offsets[0]
        target_s += self.offsets[1]

        incmin, incmax = self.inclinationlimits

        inclination = target_s - target_r
        if not incmin <= inclination <= incmax:
            return False, 'Inclination %.2f out of limit (%.2f, %.2f)!' % (
                inclination, incmin, incmax)

        for dev in self._devices:
            res = dev.isAllowed(target_r)
            if not res[0]:
                return res

        # no problems detected, so it should be safe to go there....
        return True, ''

    def doIsAtTarget(self, targets):
        traveldists = [
            target - (akt + ofs)
            for target, akt, ofs in zip(targets, self.read(0), self.offsets)
        ]
        self.log.debug('doIsAtTarget', targets, 'traveldists', traveldists)
        return max(abs(v) for v in traveldists) <= self.precision

    def doStop(self):
        SequencerMixin.doStop(self)
        for dev in self._devices:
            dev.stop()
        try:
            self.wait()
        finally:
            self.reset()

    def doStart(self, targets):
        """Generate and start a sequence if none is running.

        The sequence is optimised for negative backlash.
        It will first move both motors to the lowest value of
        (target + backlash, current_position) and then
        to the final target.
        So, inbetween, the NOK should be parallel to the beam.
        """
        if self._seq_is_running():
            raise MoveError(self, 'Cannot start device, it is still moving!')

        # check precision, only move if needed!
        if self.isAtTarget(targets):
            return

        devices = self._devices

        # XXX: backlash correction and repositioning later

        # build a moving sequence
        sequence = []

        # now go to target
        sequence.append([
            SeqDev(d, t + ofs + self.masks[self.mode], stoppable=True)
            for d, t, ofs in zip(devices, targets, self.offsets)
        ])

        # now go to target again
        sequence.append([
            SeqDev(d, t + ofs + self.masks[self.mode], stoppable=True)
            for d, t, ofs in zip(devices, targets, self.offsets)
        ])

        self.log.debug('Seq: %r', sequence)
        self._startSequence(sequence)

    def doReset(self):
        multiReset(self._motors)
예제 #24
0
class HasWindowTimeout(HasPrecision, HasTimeout):
    """
    Mixin class for devices needing a more fancy timeout handling than
    `HasTimeout`.

    Basically we keep a (length limited) history of past values and check if
    they are close enough to the target (deviation is smaller than
    `precision`). The length of that history is determined by
    :attr:`~HasWindowTimeout.window`.
    In any case the last read value is used to determine `isAtTarget`.
    If the value is outside the defined window for longer than
    :attr:`~HasTimeout.timeout` seconds after the HW is no longer busy.
    Also we add a stabilising phase in the timeouttimes list.
    """
    parameters = {
        'window':
        Param('Time window for checking stabilization',
              unit='s',
              default=60.0,
              fmtstr='%.1f',
              settable=True,
              category='general'),
    }

    parameter_overrides = {
        'precision': Override(mandatory=True, type=floatrange(1e-8)),
    }

    @lazy_property
    def _history(self):
        if self._cache:
            self._cache.addCallback(self, 'value', self._cacheCB)
            self._subscriptions.append(('value', self._cacheCB))
            t = currenttime()
            return self._cache.history(self, 'value', t - self.window, t)
        return []

    # use values determined by poller or waitForCompletion loop
    # to fill our history
    def _cacheCB(self, key, value, time):
        self._history.append((time, value))
        # clean out stale values, if more than one
        stale = None
        for i, entry in enumerate(self._history):
            t, _ = entry
            if t >= time - self.window:
                stale = i
                break
        else:
            return
        # remove oldest entries, but keep one stale
        if stale > 1:
            del self._history[:stale - 1]

    def _getTimeoutTimes(self, current_pos, target_pos, current_time):
        """Calculates timestamps for timeouts

        returns an iterable of tuples (status_string, timestamp) with ascending
        timestamps.
        First timestamp has to be `current_time` which is the only argument to
        this.
        The last timestamp will be used as the final timestamp to determine if
        the device's movement timed out or not.
        Additional timestamps (in between) may be set if need for
        _combinedStatus returning individual status text's (e.g. to
        differentiate between 'ramping' and 'stabilization').
        """
        res = HasTimeout._getTimeoutTimes(self, current_pos, target_pos,
                                          current_time)
        if not res:
            return None
        # we just append the window time after the timeout time
        # logically wrong order, but nobody uses the strings anyway
        res.append(('', res[-1][1] + self.window))
        return res

    def isAtTarget(self, val):
        ct = currenttime()
        self._cacheCB('value', val, ct)
        if self.target 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.target) <= 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 doTime(self, old, new):
        if old is None or new is None or old == new:
            return 0.
        if 'speed' in self.parameters and self.speed != 0:
            return abs(new - old) / self.speed + self.window
        elif 'ramp' in self.parameters and self.ramp != 0:
            return abs(new - old) / (self.ramp / 60.) + self.window
        return self.window

    def doEstimateTime(self, elapsed):
        if self.status()[0] != status.BUSY:
            return None
        if self.target is None:
            return None
        if 'setpoint' not in self.parameters or self.setpoint == self.target:
            # ramp finished, look at history to estimate from last point
            # outside
            now = currenttime()
            for t, v in reversed(list(self._history)):
                if abs(v - self.target) > self.precision:
                    return max(0, t + self.window - now + 1)
                if t < now - self.window:
                    break
            return 0.0
        # ramp unfinished, estimate ramp + window
        return self.doTime(self.read(), self.target)

    def _targetReached(self):
        """Do not call `_clearTimeout` as supposed by `HasTimeout` in order
예제 #25
0
class GuideField(MappedMoveable):
    """Guidefield object.

    Needs to be switched to (re-)calculate the required currents for the coils.
    Calibration is done with a matrix giving the field components created with
    a given current.

    Needs the alpha virtual motor for calculations.
    """
    attached_devices = {
        'alpha':
        Attach('Device which provides the current \\alpha', AlphaStorage),
        'coils':
        Attach('List of 3 devices used for the vector field',
               VectorCoil,
               multiple=3),
    }
    parameter_overrides = {
        'mapping':
        Override(mandatory=False,
                 type=dict,
                 default={
                     'off': None,
                     'perp': (1., 0., 0.),
                     '-perp': (-1., 0., 0.),
                     'par': (0., 1., 0.),
                     '-par': (0., -1., 0.),
                     'z': (0., 0., 1.),
                     '-z': (0., 0., -1.),
                     'up': (0., 0., 1.),
                     'down': (0., 0., -1.),
                     '0': (0., 0., 0.),
                 }),
        'precision':
        Override(mandatory=False),
    }
    parameters = {
        'background':
        Param(
            'Static magnetic field which is always present and'
            ' should be corrected',
            type=tupleof(float, float, float),
            unit='mT',
            settable='True',
            default=(0., 0., 0.),
            category='general'),
        'field':
        Param('Absolute value of the desired field at the '
              'sample position',
              type=floatrange(0.1, 100),
              unit='mT',
              settable='True',
              default=25.,
              category='general'),
    }

    _currentmatrix = None
    _currentmatrix_inv = None

    @lazy_property
    def coils(self):
        return self._attached_coils

    @lazy_property
    def alpha(self):
        return self._attached_alpha

    def doInit(self, mode):
        MappedMoveable.doInit(self, mode)
        M = np.zeros([3, 3])
        for i in range(3):
            for j in range(3):
                M[i, j] = (self.coils[j].orientation[i] /
                           self.coils[j].calibrationcurrent)
        self._currentmatrix = M
        self._currentmatrix_inv = np.linalg.inv(M)
        self.alpha._callback = self._alphaCallBack

    def doShutdown(self):
        self.alpha._callback = None

    def _alphaCallBack(self):
        if self.target in self.mapping:
            self.doStart(self.target)

    def _startRaw(self, orient):
        if orient:
            orient = np.array(orient)
            # set requested field (may try to compensate background)
            self._setfield(self.field * orient)
        else:  # switch off completely
            self.coils[0].doStart(0.0)
            self.coils[1].doStart(0.0)
            self.coils[2].doStart(0.0)

    # no _readRaw, as self.target is the unmapped (Higher level) value
    def doRead(self, maxage=0):
        return self.target

    def _B2I(self, B=np.array([0.0, 0.0, 0.0])):
        r"""Rotate the requested field around z-axis by beta.

        First we get alpha from the spectrometer: alpha is the angle between
        X-axis and \\vec{Q} and is in degrees.
        """
        # read alpha, calculate beta
        alpha = self.alpha.read(0)
        beta = np.radians(90 - alpha)
        R = np.array([[np.cos(beta), -np.sin(beta), 0.0],
                      [np.sin(beta), np.cos(beta), 0.0], [0.0, 0.0, 1.0]])
        temp = np.dot(self._currentmatrix_inv, np.dot(R, B))
        return temp

    def _I2B(self, I=np.array([0.0, 0.0, 0.0])):
        """Calculate field from currents and rotate field around z-axis by
        -beta.
        """
        # read alpha, calculate beta
        alpha = self.alpha.read(0)
        beta = np.radians(90 - alpha)
        RR = np.array([[np.cos(beta), -np.sin(beta), 0.0],
                       [np.sin(beta), np.cos(beta), 0.0], [0.0, 0.0, 1.0]])
        return np.dot(RR, np.dot(self._currentmatrix, I))

    def _setfield(self, B=np.array([0, 0, 0])):
        r"""Set the given field.

        Field components are:

        * Bqperp: component perpendicular to q, but within the scattering plane
        * Bqpar:  component parallel to q (within scattering plane)
        * Bz:     component perpendicular to the scattering plane

        (If TwoTheta==0 & \\hbar\\omega=0 then this coordinate-system is the
        same as the XYZ of the coils.)
        """
        # subtract offset (The field, which is already there, doesn't need to
        # be generated....)
        B = B - np.array(self.background)
        F = self._B2I(B)  # compute currents for requested field

        # now check limits
        valueOk = True
        for i, d in enumerate(self.coils):
            check = d.isAllowed(F[i])
            if not check[0]:
                self.log.error('Can\'t set %s to %s: %s', d,
                               d.format(F[i], unit=True), check[1])
                valueOk = False

        if not valueOk:
            raise LimitError(check[1])

        # go there
        for i, d in enumerate(self.coils):
            d.start(F[i])
예제 #26
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
예제 #27
0
class DetAngle(HasLimits, Moveable):
    """Angle of the outgoing (centered) beam to detector."""

    attached_devices = {
        'tubeangle': Attach('Angle of the tube to the ground', Moveable),
        'tubepos': Attach('Position of detector inside tube', Readable),
        'pivot': Attach('Position of the pivot point', Readable),
        'theta': Attach('Tilt angle of the sample', Readable, optional=True),
    }

    parameters = {
        'pivot1pos':
        Param('Distance of the pivot point 1 from wall',
              type=floatrange(0),
              mandatory=False,
              default=290,
              unit='mm'),
        'b2pos':
        Param('Distance of B2 aperture from wall',
              type=floatrange(0),
              mandatory=False,
              default=165,
              unit='mm'),
        'b3pos':
        Param('Distance of B3 aperture from wall',
              type=floatrange(0),
              mandatory=False,
              default=285,
              unit='mm'),
        'samplepos':
        Param('Distance of sample from B3',
              type=floatrange(0),
              mandatory=False,
              default=50,
              unit='mm'),
        'detheight':
        Param('Height of the detector',
              type=floatrange(0),
              mandatory=False,
              default=533.715,
              unit='mm'),
        # calculated from the beamheight - pivot.height - (256 - 160) * 2.093 mm
        # Beam height at 0 deg is in pixel 160 counted from top pixel (256)
        # times pixel size (2.093 mm)
        'detoffset':
        Param('Offset from virtual tube base to lower edge of '
              'detector',
              type=floatrange(0),
              mandatory=False,
              default=619.772,
              unit='mm'),
        'beamheight':
        Param('Height of beam above ground level',
              type=floatrange(0),
              mandatory=False,
              default=1193.7,
              unit='mm'),
        'range':
        Param('range of angles between upper and lower detector edge',
              type=tupleof(float, float),
              volatile=True,
              unit='deg, deg',
              internal=True,
              preinit=False),
    }

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

    def doPreinit(self, mode):
        # center of the detector in respect to the tube base line
        self._detcenter = self.detheight / 2 + self.detoffset
        self._yoffset = self.beamheight - self._attached_pivot.height

    def doInit(self, mode):
        self._func = np.vectorize(self._alpha)

    def doRead(self, maxage=0):
        self._update(maxage)
        beta = self._attached_tubeangle.read(maxage)
        self.log.debug('Tube angle: %.3f', beta)
        alphaf = self._alpha(beta)
        if self._attached_theta:
            alphaf -= self._attached_theta.read(maxage)
        return alphaf

    def _update(self, maxage=0):
        pivot = self._attached_pivot
        # Calculate the distance between sample and pivot point
        self._xoffset = self.pivot1pos + pivot.grid * (
            pivot.read(maxage) - 1) - (self.samplepos + self.b3pos)
        self.log.debug('Sample pivot distance : %.1f', self._xoffset)
        self._tpos = self._attached_tubepos.read(maxage)

    def _alpha(self, beta):
        beta = radians(beta)
        # calculate the detector center position in respect to sample
        x = self._xoffset + self._tpos * cos(beta) - self._detcenter * sin(
            beta)

        # calculate the height of the detector center in respect to the ground
        y = self._tpos * sin(beta) + self._detcenter * cos(
            beta) - self._yoffset
        return degrees(atan2(y, x))

    def doStart(self, target):
        self._update(0)
        x = np.arange(self.absmin, self.absmax + 0.01, 0.01)
        y = self._func(x)
        if self._attached_theta:
            target += self._attached_theta.read(0)
        val = np.interp(target, y, x)
        self.log.debug('new beta: %f', val)
        self._attached_tubeangle.start(val)

    def doReadAbslimits(self):
        return self._attached_tubeangle.abslimits

    def doReadRange(self):
        alpha = self.doRead(0)
        opening = degrees(atan2(self.detheight, self._tpos)) / 2.
        return (alpha - opening, alpha + opening)
예제 #28
0
class Network(Readable):

    parameters = {
        'interval':
        Param(
            'Interval for load detection',
            type=floatrange(0.1, 60),
            default=0.1,
            settable=True,
        ),
        'interface':
        Param(
            'Network interface device (None=all)',
            type=oneof(None,
                       *net_io_counters(True).keys()),
            default=None,
            settable=True,
        ),
        'direction':
        Param(
            'Transport direction',
            type=oneof('tx', 'rx'),
            default='tx',
            settable=True,
        ),
        'lastvalue':
        Param('Last obtained value',
              type=float,
              internal=True,
              mandatory=False,
              default=0.0),
    }

    def doInit(self, mode):
        if mode == SIMULATION:
            return
        # create only run ONE thread: in the poller
        # it may look stupid, as the poller already has a thread polling read()
        # now imagine several such devices in a setup.... not so stupid anymore
        if session.sessiontype == POLLER:
            self._thread = createThread('measure networkload', self._run)

    def doWriteInterval(self, value):
        self.pollinterval = max(1, 2 * value)
        self.maxage = max(3, 5 * value)

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

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

    def _getData(self):
        if not self.interface:
            data = net_io_counters()
        else:
            data = net_io_counters(True)[self.interface]
        return data.bytes_sent if self.direction == 'tx' else data.bytes_recv

    def _run(self):
        old = self._getData()
        while True:
            timeslept = 0
            while timeslept < self.interval:
                # interval may be changed while we sleep!
                sleeptime = max(0.1, self.interval / 10)
                time.sleep(sleeptime)
                timeslept += sleeptime
            new = self._getData()
            self._setROParam('lastvalue', (new - old) / timeslept)
            old = new