class Spectrum(VirtualImage): parameters = { 'preselection': Param('Preset value for this channel', type=float, settable=True), } parameter_overrides = { 'sizes': Override(type=tupleof(intrange(1, 1), intrange(1, 16384)), default=(1, 16384)), 'ismaster': Override(settable=True), } # set to True to get a simplified doEstimateTime is_timer = False def doEstimateTime(self, elapsed): if not self.ismaster or self.doStatus()[0] != status.BUSY: return None if self.is_timer: return self.preselection - elapsed else: counted = float(self.doRead()[0]) # only estimated if we have more than 3% or at least 100 counts if counted > 100 or counted > 0.03 * self.preselection: if 0 <= counted <= self.preselection: return (self.preselection - counted) * elapsed / counted def doReadArray(self, _quality): return self._buf[0]
class Input(Readable): """IPC I/O card digital input class.""" parameters = { 'addr': Param('Bus address of the card', type=intrange(32, 255), mandatory=True), 'first': Param('First bit to read', type=intrange(0, 15), mandatory=True), 'last': Param('Last bit to read', type=intrange(0, 15), mandatory=True), } parameter_overrides = { 'unit': Override(mandatory=False, default=''), 'fmtstr': Override(default='%d'), } attached_devices = { 'bus': Attach('The communication bus', IPCModBus), } def doInit(self, mode): self._mask = ((1 << (self.last - self.first + 1)) - 1) << self.first def doRead(self, maxage=0): high = self._attached_bus.get(self.addr, 181) << 8 low = self._attached_bus.get(self.addr, 180) return ((high + low) & self._mask) >> self.first def doStatus(self, maxage=0): return status.OK, ''
class TriangleAngle(HasOffset, TriangleMaster): parameters = { 'index': Param('index of return', type=intrange(0, 1), settable=False, volatile=False, userparam=False), 'scale': Param('direction definition (-1, 1)', type=oneof(-1, 1), settable=False, mandatory=True), } parameter_overrides = { 'offset': Override(type=floatrange(-2, 2)), } def doRead(self, maxage=0): try: self.log.debug('index: %d' % self.index) res = self.offset + self.scale * self._read_controller(self.index) self.log.debug('pos: %f' % res) except IndexError: res = 0 return res
class LVPower(Moveable): """Toni TOFTOF-type low-voltage power supplies.""" attached_devices = { 'bus': Attach('Toni communication bus', ToniBus), } parameters = { 'addr': Param('Bus address of the supply controller', type=intrange(0xF0, 0xFF), mandatory=True), } parameter_overrides = { 'unit': Override(mandatory=False, default=''), } valuetype = oneofdict({1: 'on', 0: 'off'}) def doRead(self, maxage=0): sval = self._attached_bus.communicate('S?', self.addr, expect_hex=2) return 'on' if sval >> 7 else 'off' def doStatus(self, maxage=0): sval = self._attached_bus.communicate('S?', self.addr, expect_hex=2) tval = self._attached_bus.communicate('T?', self.addr, expect_hex=2) return status.OK, 'status=%d, temperature=%d' % (sval, tval) @requires(level=ADMIN) def doStart(self, target): self._attached_bus.communicate('P%d' % (target == 'on'), self.addr, expect_ok=True)
class TemperatureSensor(PyTangoDevice, Readable): """The temperature readout device of the IMPAC pyrometer.""" parameters = { 'address': Param( 'Device address', type=intrange(0, 97), default=0, settable=False, ), } parameter_overrides = { 'comtries': Override(default=5), } def doInit(self, mode): pass def doRead(self, maxage=0): # return current temperature what = '%02dms' % self.address temp = float(self._dev.Communicate(what)) if temp > 77769: temp = -0 else: temp /= 10.0 return temp def doStatus(self, maxage=0): return status.OK, 'idle'
class ChopperDisc2(ChopperDisc2Base, ChopperDisc): """Chopper disc 2 device.""" # Due to a limitation of the hardware the position of the chopper disc2 # position can be 0 but it is not a valid position. It indicates that the # hardware is not referenced. The parameter read raises always an error, so # the device can't be initialised. # To overcome the problem the parameter type for the hardware is extended # to the 0 position, but the writing of 0 raises an error. parameter_overrides = { 'pos': Override(type=intrange(0, 5), volatile=True, settable=True, fmtstr='%d', unit=''), } def doWritePos(self, target): if target == 0: raise ConfigurationError( self, "'%d' is an invalid value for " "parameter 'pos'" % target) self._attached_translation.move(target)
class FPGARate(PyTangoDevice, Readable): """Determines the instantaneous count rate of a counter card channel.""" parameters = { 'channel': Param('Channel number', type=intrange(0, 4), settable=False, mandatory=True) } parameter_overrides = { 'unit': Override(mandatory=False, default='cps'), } _last = None def doRead(self, maxage=0): cur = (self._dev.DevFPGACountReadCount(self.channel), self._dev.DevFPGACountReadTime() / 1000.) if cur[1] == 0: res = 0.0 elif self._last is None or self._last[1] > cur[1]: res = cur[0] / cur[1] elif self._last[1] == cur[1]: res = 0.0 else: res = (cur[0] - self._last[0]) / (cur[1] - self._last[1]) self._last = cur return res
class ChopperDisc2(DeviceMixinBase): """Chopper disc device with translation.""" parameter_overrides = { 'pos': Override(settable=True, type=intrange(1, 5), fmtstr='%d', volatile=True, default=intrange(1, 5)(), unit=''), } attached_devices = { 'translation': Attach('Chopper disc device', Moveable), } def doWritePos(self, target): self._attached_translation.move(target) def doReadPos(self): return self._attached_translation.read(0)
class LVPower(Moveable): """Toni TOFTOF-type low-voltage power supplies.""" attached_devices = { 'bus': Attach('Toni communication bus', ToniBus), } parameters = { 'addr': Param('Bus address of the supply controller', type=intrange(0xF0, 0xFF), mandatory=True), 'temperature': Param('Temperature of the module', type=int, settable=False, volatile=True, unit='degC', fmtstr='%d'), } parameter_overrides = { 'unit': Override(mandatory=False, default=''), } valuetype = oneofdict({1: 'on', 0: 'off'}) def doRead(self, maxage=0): sval = self._attached_bus.communicate('S?', self.addr, expect_hex=2) return 'on' if sval >> 7 else 'off' def doReadTemperature(self): return self._attached_bus.communicate('T?', self.addr, expect_hex=2) def doStatus(self, maxage=0): sval = self._attached_bus.communicate('S?', self.addr, expect_hex=2) if sval in (0x0, 0x80): return status.OK, '' msg = [] if sval & 0x1: msg.append('undervoltage +5V') if sval & 0x2: msg.append('overvoltage +5V') if sval & 0x4: msg.append('undervoltage -5V') if sval & 0x8: msg.append('overvoltage -5V') if sval & 0x10: msg.append('undervoltage +12V') if sval & 0x20: msg.append('overvoltage +12V') return status.ERROR, ', '.join(msg) @requires(level=ADMIN) def doStart(self, target): self._attached_bus.communicate('P%d' % (target == 'on'), self.addr, expect_ok=True)
class CPTReadout(HasOffset, JsonBase): parameters = { 'phasesign': Param('Phase sign', type=oneof('unsigned', 'signed'), settable=False, default='unsigned'), 'channel': Param( 'Index of value', type=intrange(-1, 99), ), } def _read_ctrl(self, channel): data = self._read_controller([self.valuekey, 'start_act']) self.log.debug('res: %r', data) self.log.debug('channel %d', channel) if channel == 0: self.log.debug('calc speed') res = 3e9 / data['start_act'] # speed res -= self.offset # should be Zero elif channel == -1: self.log.debug('calc phase in respect to Disk 1 of Disc 1') self.log.debug('offset %.2f', self.offset) res = -360.0 * data[self.valuekey][0] / data['start_act'] res -= self.offset res = self._kreis(res) else: self.log.debug('calc phase in respect to Disk 1') self.log.debug('offset %.2f', self.offset) res = -360.0 * data[self.valuekey][channel] / data['start_act'] res -= self.offset res = self._kreis(res) return res def _kreis(self, phase, kreis=360.0): line = 'kreis phase %.2f' % phase if self.phasesign == 'signed': while phase > kreis / 2: phase -= kreis while phase < -kreis / 2: phase += kreis else: phase = -phase while phase > kreis: phase -= kreis while phase < 0: phase += kreis self.log.debug('%s %.2f', line, phase) return phase def doRead(self, maxage=0): return self._read_ctrl(self.channel)
class Shs(PyTangoDevice, Readable): """ Basic IO Device object for devices in refsans' contains common things for all devices. """ _buffer_old = None hardware_access = True parameters = { 'address': Param('Starting offset (words) of IO area', # type=intrange(0x3000, 0x47ff), type=oneof(1026), mandatory=False, settable=False, userparam=False, default=1026), 'banks': Param('Banks to be read', type=listof(intrange(0, 0xffff)), settable=False, userparam=False, default=[0x300, 0x400]) } def doInit(self, mode): # switch off watchdog, important before doing any write access # if mode != SIMULATION: # self._taco_guard(self._dev.writeSingleRegister, (0, 0x1120, 0)) pass def _readBuffer(self): buf = () for bank in self.banks: Notausgang = 1 while True: self.log.debug('bank: 0x%04X', bank) for _ in range(2): session.delay(0.1) self._dev.WriteOutputWords((self.address, bank)) bu = tuple(self._dev.ReadOutputWords((0, 10))) if bu[2] == bank: self.log.debug('Bank ok %d == %d', bu[2], bank) buf += bu break self.log.debug('Data from wrong bank %d != %d', bu[2], bank) Notausgang += 1 if Notausgang > 6: self.log.info('NOTAUSGANG<') # raise error ??? break session.delay(0.5 * Notausgang) self.log.debug('buffer: %s', buf) self._buffer_old = buf return buf def doRead(self, maxage=0): return self._readBuffer() def doStatus(self, maxage=0): return status.OK, 'DEBUG'
class MCC2Coder(MCC2core, NicosCoder): """Class for the readout of a MCC2-coder""" codertypes = ('none', 'incremental', 'ssi-binary', 'ssi-gray') parameters = { 'slope': Param('Coder units per degree of rotation', type=float, default=1, settable=True, unit='1/main', prefercache=False), 'zerosteps': Param('Coder steps at physical zero', type=int, default=0, settable=True, prefercache=False), 'codertype': Param('Type of encoder', type=oneof(*codertypes), default='none', settable=True, prefercache=False), 'coderbits': Param('Number of bits of ssi-encoder', default=0, type=intrange(0, 31), settable=True, prefercache=False), } def doReset(self): self.comm('XP39S1') # encoder conversion factor set to 1 def doReadCoderbits(self): return int(self.comm('XP35R')) def doWriteCoderbits(self, value): return self.comm('XP35S%d' % int(value)) def doReadCodertype(self): return self.codertypes[int(self.comm('XP34R'))] def doWriteCodertype(self, value): return self.comm('XP34S%d' % self.codertypes.index(value)) def doRead(self, maxage=0): return (float(self.comm('XP22R')) - self.zerosteps) / self.slope def doSetPosition(self, pos): self.comm('XP22S%d' % int(float(pos) * self.slope + self.zerosteps)) return self.doRead()
class PNGLiveFileSink(ImageSink): """Writes (live) data to a PNG file, possibly with a log-10 color scale. Data is normalized to the highest-value pixel, and the used color map is the commonly used "Jet" colormap. This is not intended for data result storage, but to create images of the live measurement for quick view, e.g. in status monitors. """ parameter_overrides = { 'filenametemplate': Override(mandatory=False, userparam=False, default=['']), } parameters = { 'interval': Param('Interval to write file to disk', unit='s', default=5), 'filename': Param('File name for .png image', type=str, mandatory=True), 'log10': Param('Use logarithmic counts for image', type=bool, default=False), 'size': Param('Size of the generated image', unit='pixels', default=256, settable=True), 'rgb': Param('Create RBG image', type=bool, default=True, mandatory=False), 'histrange': Param('Range of histogram for scaling greyscale image', type=limits, default=(0.05, 0.95), settable=True, mandatory=False), 'sumaxis': Param('Axis over which should be summed if data are 3D', type=intrange(1, 3), default=1, settable=False), } handlerclass = PNGLiveFileSinkHandler def doPreinit(self, mode): if PIL is None: self.log.error(_import_error) raise NicosError( self, 'Python Image Library (PIL) is not ' 'available. Please check whether it is installed ' 'and in your PYTHONPATH') def isActiveForArray(self, arraydesc): return len(arraydesc.shape) in (2, 3)
class VirtualReferenceMotor(CanReference, VirtualMotor): """Virtual motor device with reference capability.""" parameters = { 'refpos': Param('Reference position if given', type=none_or(float), settable=False, default=None, unit='main'), 'addr': Param('Bus address of the motor', type=intrange(32, 255), default=71), 'refswitch': Param('Type of the reference switch', type=oneof('high', 'low', 'ref'), default='high', settable=False), } def doReference(self, *args): refswitch = args[0] if args and isinstance(args[0], string_types) \ else None self.log.debug('reference: %s', refswitch) self._setrefcounter() if self.refpos is not None: ret = self.read(0) self.log.debug('%s %r', self.name, self.isAtReference()) return ret return self.refpos def _setrefcounter(self): self.log.debug('in setrefcounter') if self.refpos is not None: self.setPosition(self.refpos) self._setROParam('target', self.refpos) self.log.debug('%r %r', self.refpos, self.target) session.delay(0.1) if not self.isAtReference(): raise UsageError('cannot set reference counter, not at reference ' 'point') def isAtReference(self, refswitch=None): if self.refpos is None: return False pos = self.read(0) is_at_refpos = abs(self.refpos - self.read(0)) <= self.precision if refswitch == 'low': return is_at_refpos and (abs(self.abslimits[0] - pos) < abs(self.abslimits[1] - pos)) elif refswitch == 'high': return is_at_refpos and (abs(self.abslimits[0] - pos) > abs(self.abslimits[1] - pos)) return is_at_refpos
class MeanTemp(PyTangoDevice, Readable): """Returns mean temperature of some channels of a MawiTherm VectorInput.""" parameters = { 'first': Param('First channel to include', type=intrange(1, 8), default=1, settable=True), 'last': Param('Last channel to include', type=intrange(1, 8), default=8, settable=True), } def doRead(self, maxage=0): if self.first > self.last: return 0 allchannels = self._dev.value[self.first - 1:self.last] return float(sum(allchannels) / len(allchannels))
class Valve(Moveable): """Control element for 8 valves.""" attached_devices = { 'bus': Attach('Toni communication bus', ModBus), } parameters = { 'addr': Param('Bus address of the valve control', type=int, mandatory=True), 'channel': Param('Channel of the valve', type=intrange(0, 7), mandatory=True), 'states': Param('Names for the closed/open states', type=listof(str), default=['off', 'on']), 'waittime': Param('Time to wait after switching', type=float, unit='s', default=4), } parameter_overrides = { 'unit': Override(mandatory=False), } _started = 0 def doInit(self, mode): if len(self.states) != 2: raise ConfigurationError( self, 'Valve states must be a list of ' 'two strings for closed/open state') self.valuetype = oneof(*self.states) def doStart(self, value): value = self.states.index(value) self._hw_wait() msg = '%s=%02x' % (value and 'O' or 'C', 1 << self.channel) self._attached_bus.communicate(msg, self.addr, expect_ok=True) self._started = currenttime() def doRead(self, maxage=0): ret = self._attached_bus.communicate('R?', self.addr, expect_hex=2) return self.states[bool(ret & (1 << self.channel))] def doStatus(self, maxage=0): ret = self._attached_bus.communicate('I?', self.addr, expect_hex=2) if ret != 0: return status.BUSY, 'busy' if self._started and self._started + self.waittime < currenttime(): return status.BUSY, 'waiting' else: return status.OK, 'idle'
class ChopperDisc(HasLimits, HasPrecision, DeviceMixinBase): parameters = { 'phase': Param('Phase of chopper disc', type=floatrange(0, 360), settable=True, userparam=True, fmtstr='%.2f', unit='deg', category='status'), 'current': Param('motor current', type=float, settable=False, volatile=True, userparam=True, fmtstr='%.2f', unit='A'), 'mode': Param('Internal mode', type=int, settable=False, userparam=True), 'chopper': Param('chopper number inside controller', type=intrange(1, 6), settable=False, userparam=True), 'reference': Param('reference to Disc one', type=floatrange(-360, 360), settable=False, userparam=True), 'edge': Param('Chopper edge of neutron window', type=oneof('open', 'close'), settable=False, userparam=True), 'gear': Param('Chopper ratio', type=intrange(-6, 6), settable=False, userparam=True, default=0), 'pos': Param('Distance to the disc 1', type=floatrange(0), settable=False, userparam=True, default=0., fmtstr='%.2f', unit='m', category='status'), } parameter_overrides = { 'unit': Override(default='rpm', mandatory=False,), 'precision': Override(default=2), 'abslimits': Override(default=(0, 6000), mandatory=False), 'fmtstr': Override(default='%.f'), } def doPoll(self, n, maxage): self._pollParam('current') def _isStopped(self): return abs(self.read(0)) <= self.precision
class DigitalInput(Moveable): """A test DigitalInput.""" parameters = { '_value': Param('Simulated value', type=intrange(0, 0xFFFFFFFF), default=0, settable=False, internal=True), } parameter_overrides = { 'unit': Override(mandatory=False, settable=False, default=''), 'fmtstr': Override(default='%d'), } valuetype = intrange(0, 0xFFFFFFFF) def doRead(self, maxage=0): return self._value def doStatus(self, maxage=0): return status.OK, 'idle'
class RTE1104(Readable): parameters = { 'channel': Param('calculation channel', type=intrange(1, 8), settable=False, default=1), 'timescale': Param('Time scale setting', type=float, settable=True, userparam=True, volatile=True, category='general'), 'yscale': Param('Y axis scaling', type=float, settable=True, userparam=True, volatile=True, category='general'), } attached_devices = { 'io': Attach('Communication device', StringIO), } def doRead(self, maxage=0): return float( self._attached_io.communicate('MEAS%d:ARES?' % self.channel)) def doStatus(self, maxage=0): return status.OK, '' def doReadTimescale(self): return float(self._attached_io.communicate('TIM:SCAL?')) def doWriteTimescale(self, value): self._attached_io.writeLine('TIM:SCAL %g' % value) def doReadYscale(self): return float( self._attached_io.communicate('CHAN%d:SCAL?' % self.channel)) def doWriteYscale(self, value): self._attached_io.writeLine('CHAN%d:SCAL %g' % (self.channel, value))
class RTE1104YScaleSetting(Moveable): parameters = { 'channel': Param('Input channel', type=intrange(1, 4), settable=False, default=1), } attached_devices = { 'io': Attach('Communication device', StringIO), 'regulator': Attach('regulator for the amplitude', Moveable, optional=True), } parameter_overrides = { 'unit': Override(mandatory=False, volatile=True), } valuetype = float def doReadUnit(self): if self._attached_regulator: return self._attached_regulator.unit return 'V' def doRead(self, maxage=0): # main value is target amplitude if self._attached_regulator: return self._attached_regulator.read(maxage) setting = float( self._attached_io.communicate('CHAN%d:SCAL?' % self.channel)) return setting * 10. / (2.2 * math.sqrt(2)) def doStart(self, value): setting = math.ceil(value * math.sqrt(2.) * 2.2) / 10. self._attached_io.writeLine('CHAN%d:SCAL %g' % (self.channel, setting)) if self._attached_regulator: self._attached_regulator.start(value) def doStatus(self, maxage=0): if self._attached_regulator: return self._attached_regulator.status(maxage) return status.OK, '' def doStop(self): if self._attached_regulator: self._attached_regulator.stop()
class MicroPythonServo(HasOffset, Motor): attached_devices = { 'io': Attach('Comm device', SerComIO), } parameters = { 'channel': Param('Channel (1..4)', type=intrange(1, 4), default=1), } parameter_overrides = { 'precision': Override(default=1), 'fmtstr': Override(default='%d'), } _busytime = 0 def doInit(self, mode): if mode != SIMULATION: io = self._attached_io io.communicate('import pyb') io.communicate('s%d=pyb.Servo(%d)' % (self.channel, self.channel)) io.communicate('s%d.calibration(600,2400,1500,2400,2400)' % self.channel) io.communicate('s%d.angle(%f)' % (self.channel, self.target or 0.)) io.addPollDev(self) def doRead(self, maxage=None): return float( self._attached_io.communicate( 's%d.angle()' % self.channel)) - self.offset def doStart(self, target): duration = abs(self.read(0) - target) / self.speed target = target + self.offset cmd = ("s%d.angle(%f,%d)" % (self.channel, target, int(duration * 1000))) self.log.debug(cmd) self._attached_io.communicate(cmd) self._busytime = time.time() + duration def doStatus(self, maxage=None): if time.time() < self._busytime: return status.BUSY, '' return status.OK, '' def doShutdown(self): self._attached_io.delPollDev(self)
class LensControl(HasTimeout, Moveable): """Control the 3 lenses.""" valuetype = intrange(0, 7) attached_devices = { 'output': Attach('output setter', Moveable), 'input_in': Attach('input for limit switch "in" position', Readable), 'input_out': Attach('input for limit switch "out" position', Readable), 'sync_bit': Attach('sync bit output', Moveable), } parameter_overrides = { 'fmtstr': Override(default='%d'), 'timeout': Override(default=10), 'unit': Override(mandatory=False, default=''), } def doStatus(self, maxage=0): is_in = self._attached_input_in.read(maxage) is_out = self._attached_input_out.read(maxage) # check individual bits for i in range(3): mask = 1 << i if is_in & mask == is_out & mask: # inconsistent state, check switches if is_in & mask: # both switches on? return status.ERROR, 'both switches on for lens ' \ '%d' % (i + 1) return status.BUSY, 'lenses moving' # HasTimeout will check for target reached return status.OK, 'idle' def doRead(self, maxage=0): return self._attached_input_in.read(maxage) def doStart(self, target): self._attached_output.start(target) # without this bit, no outputs will be changed self._attached_sync_bit.start(1)
class ChopperDiscTranslation(CanReference, IsController, DeviceMixinBase): """Position of chopper disc along the x axis. Since the chopper disc 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. """ valuetype = intrange(1, 5) attached_devices = { 'disc': Attach('Chopper disc device', Moveable), } parameter_overrides = { 'unit': Override(default='', mandatory=False), 'abslimits': Override(mandatory=False, default=limits((1, 5))), 'fmtstr': Override(default='%d'), } def doReference(self, *args): self.move(1) def doIsAllowed(self, target): if self._attached_disc._isStopped(): return True, '' return False, 'Disc (%s) speed is too high, %.0f!' % ( self._attached_disc, self.read(0)) def isAdevTargetAllowed(self, dev, target): state = self.status(0) if state[0] == status.OK: return True, '' return False, 'translation is: %s' % state[1] def _getWaiters(self): return []
class FPGACounterChannel(CounterChannelMixin, FPGAChannelBase): """FPGACounterChannel implements one monitor channel for ZEA-2 counter card. """ parameters = { 'channel': Param('Channel number', type=intrange(0, 4), settable=False, mandatory=True) } def _setPreselection(self): self._dev.DevFPGACountSetMinTime(0) self._dev.DevFPGACountSetTimeLimit(3600 * 24 * 1000) # Clamped to 1 for the same reason as the timer channel. self._dev.DevFPGACountSetCountLimit( [self.channel, int(self.preselection) or 1]) def doRead(self, maxage=0): return self._dev.DevFPGACountReadCount(self.channel)
class DelayBox(Moveable): """Toni TOFTOF-type programmable delay box.""" attached_devices = { 'bus': Attach('Toni communication bus', ToniBus), } parameters = { 'addr': Param('Bus address of the supply controller', type=intrange(0xF0, 0xFF), mandatory=True), } parameter_overrides = { 'fmtstr': Override(default='%d'), } valuetype = int def doRead(self, maxage=0): return self._attached_bus.communicate('D?', self.addr, expect_hex=4) def doStart(self, target): self._attached_bus.communicate('D=%04X' % target, self.addr, expect_ok=True) def doIsAllowed(self, target): if 0 <= target <= 65535: return True, '' else: return False, '%r is not in the allowed range [0, 65535], please '\ 'check your delay calculation' % (target,) def doStatus(self, maxage=0): return status.OK, ''
class Ratemeter(Readable): """Toni ratemeter inside a "crate".""" attached_devices = { 'bus': Attach('Toni communication bus', ModBus), } parameters = { 'addr': Param('Bus address of crate', type=intrange(0xF0, 0xFF), mandatory=True), } def doRead(self, maxage=0): bus = self._attached_bus self._cachelock_acquire() try: # ratemeter is on channel 2 bus.communicate('C2', self.addr, expect_ok=True) # send command (T = transmit, X = anything for input buffer update bus.communicate('TX', self.addr, expect_ok=True) # wait until response is ready rlen = -1 t = 0 while 1: sleep(0.05) ret = bus.communicate('R?', self.addr) if rlen == -1 or len(ret) == rlen: return ret t += 1 if t == 10: raise CommunicationError('timeout while waiting for ' 'response') finally: self._cachelock_release()
class McStasImage(ImageChannelMixin, PassiveChannel): """Image channel based on McStas simulation.""" _mythread = None _process = None parameters = { 'size': Param( 'Detector size in pixels (x, y)', settable=False, type=tupleof(intrange(1, 8192), intrange(1, 8192)), default=(1, 1), ), 'mcstasprog': Param('Name of the McStas simulation executable', type=str, settable=False), 'mcstasdir': Param('Directory where McStas stores results', type=str, default='singlecount', settable=False), 'mcstasfile': Param('Name of the McStas data file', type=str, settable=False), 'mcsiminfo': Param('Name for the McStas Siminfo file', settable=False, type=str, default='mccode.sim'), 'ci': Param('Constant ci applied to simulated intensity I', settable=False, type=floatrange(0.), default=1e3) } def doInit(self, mode): self.arraydesc = ArrayDesc(self.name, self.size, '<u4') self._workdir = os.getcwd() def doReadArray(self, quality): self.log.debug('quality: %s', quality) if quality == LIVE: self._send_signal(SIGUSR2) elif quality == FINAL: if self._mythread and self._mythread.is_alive(): self._mythread.join(1.) if self._mythread.is_alive(): self.log.exception("Couldn't join readout thread.") else: self._mythread = None self._readpsd(quality == LIVE) return self._buf def _prepare_params(self): """Return a list of key=value strings. Each entry defines a parameter setting for the mcstas simulation call. examples: param=10 """ raise NotImplementedError('Please implement _prepare_params method') def doPrepare(self): self._mcstas_params = ' '.join(self._prepare_params()) self.log.debug('McStas parameters: %s', self._mcstas_params) self._buf = np.zeros(self.size[::-1]) self.readresult = [0] def valueInfo(self): return (Value(self.name + '.sum', unit='cts', type='counter', errors='sqrt', fmtstr='%d'), ) def doStart(self): self._mythread = createThread('detector %s' % self, self._run) def doStatus(self, maxage=0): if self._mythread and self._mythread.is_alive(): return status.BUSY, 'busy' return status.OK, 'idle' def doFinish(self): self.log.debug('finish') self._send_signal(SIGTERM) def _send_signal(self, sig): if self._process and self._process.is_running(): self._process.send_signal(sig) # wait for mcstas releasing fds datafile = path.join(self._workdir, self.mcstasdir, self.mcstasfile) siminfo = path.join(self._workdir, self.mcstasdir, self.mcsiminfo) try: while self._process and self._process.is_running(): fnames = [f.path for f in self._process.open_files()] if siminfo not in fnames and datafile not in fnames: break session.delay(.01) except (AccessDenied, NoSuchProcess): self.log.debug( 'McStas process already terminated in _send_signal(%r)', sig) self.log.debug('McStas process has written file on signal (%r)', sig) def _run(self): """Run McStas simulation executable. The current settings of the instrument parameters will be transferred to it. """ try: shutil.rmtree(self.mcstasdir) except (IOError, OSError): self.log.info('could not remove old data') command = '%s -n 1e8 -d %s %s' % (self.mcstasprog, self.mcstasdir, self._mcstas_params) self.log.debug('run %s', command) try: self._process = Popen(command.split(), stdout=PIPE, stderr=PIPE, cwd=self._workdir) out, err = self._process.communicate() if out: self.log.debug('McStas output:') for line in out.splitlines(): self.log.debug('[McStas] %s', line) if err: self.log.warning('McStas found some problems:') for line in err.splitlines(): self.log.warning('[McStas] %s', line) except OSError as e: self.log.error('Execution failed: %s', e) self._process.wait() self._process = None def _readpsd(self, ignore_error=False): try: with open( path.join(self._workdir, self.mcstasdir, self.mcstasfile), 'r') as f: lines = f.readlines()[-3 * (self.size[0] + 1):] if lines[0].startswith('# Data') and self.mcstasfile in lines[0]: self._buf = ( np.loadtxt(lines[1:self.size[0] + 1], dtype=np.float32) * self.ci).astype(np.uint32) self.readresult = [self._buf.sum()] elif not ignore_error: raise IOError('Did not find start line: %s' % lines[0]) except IOError: if not ignore_error: self.log.exception('Could not read result file')
class Sans1ColliMotorAllParams(Sans1ColliMotor): """ Device object for a digital output device via a Beckhoff modbus interface. Maximum Parameter Implementation. All Relevant Parameters are accessible and can be configured. """ _paridx = dict( refpos=2, vmax=3, v_max=3, vmin=4, v_min=4, vref=5, v_ref=5, acc=6, a_acc=6, ae=7, a_e=7, microsteps=8, backlash=9, fullsteps_u=10, fullsteps=10, imax=11, i_max=11, iv=12, i_v=12, iv0=12, i_v0=12, imin=12, i_min=12, encodersteps_u=20, features=30, t=40, temp=40, temperature=40, type=250, hw=251, fw=252, reset=255, ) parameters = { # provided by parent class: speed, unit, fmtstr, warnlimits, userlimits, # abslimits, precision and others 'power': Param( 'Power on/off for the motordriver and ' 'enable/disable for the logic', type=oneof('off', 'on'), default='off', settable=True), 'backlash': Param('Backlash correction in physical units', type=float, default=0.0, settable=True, mandatory=False, volatile=True), 'maxcurrent': Param('Max Motor current in A', type=floatrange(0.05, 5), settable=True, volatile=True), 'idlecurrent': Param('Idle Motor current in A', type=floatrange(0.05, 5), settable=True, volatile=True), 'temperature': Param('Temperature of the motor driver', type=float, settable=False, volatile=True), 'minspeed': Param('The minimum motor speed', unit='main/s', settable=True, volatile=True), 'refspeed': Param('The referencing speed', unit='main/s', settable=True, volatile=True), 'accel': Param('Acceleration/Decceleration', unit='main/s**2', settable=True, volatile=True), 'stopaccel': Param('Emergency Decceleration', unit='main/s**2', settable=True, volatile=True), # needed ? Ask the Sans1 people... 'hw_vmax': Param('Maximum Velocity in HW-units', type=intrange(1, 2047), settable=True, volatile=True), 'hw_vmin': Param('Minimum Velocity in HW-units', type=intrange(1, 2047), settable=True, volatile=True), 'hw_vref': Param('Referencing Velocity in HW-units', type=intrange(1, 2047), settable=True, volatile=True), 'hw_accel': Param('Acceleration in HW-units', type=intrange(16, 2047), settable=True, volatile=True), 'hw_accel_e': Param('Acceleration when hitting a limit switch in ' 'HW-units', type=intrange(16, 2047), settable=True, volatile=True), 'hw_backlash': Param( 'Backlash in HW-units', # don't use type=intrange(-32768, 32767), settable=True, volatile=True), 'hw_fullsteps': Param('Motor steps per turn in HW-units', type=intrange(1, 65535), settable=True, volatile=True), 'hw_enc_steps': Param('Encoder steps per turn in HW-units', type=intrange(1, 65535), settable=True, volatile=True), 'hw_features': Param('Value of features register (16 Bit, see docu)', type=intrange(0, 65535), settable=True, volatile=True), 'hw_type': Param('Value of features register (16 Bit, see docu)', type=int, settable=True, volatile=True), 'hw_revision': Param('Value of HW-revision register ' '(16 Bit, see docu)', type=int, settable=True, volatile=True), 'hw_firmware': Param('Value of HW-Firmware register ' '(16 Bit, see docu)', type=int, settable=True, volatile=True), 'hw_coderflt': Param('Input filter for Encoder signals', type=oneofdict({ 1: 'disabled', 0: 'enabled' }), default='enabled', settable=True, volatile=True), 'hw_feedback': Param('Feedback signal for positioning', type=oneofdict({ 0: 'encoder', 1: 'motor' }), default='motor', settable=True, volatile=True), 'hw_invposfb': Param('Turning direction of encoder', type=oneofdict({ 1: 'opposite', 0: 'concordant' }), default='concordant', settable=True, volatile=True), 'hw_ramptype': Param('Shape of accel/deccel ramp', type=oneofdict({ 1: 'exponential', 0: 'linear' }), default='linear', settable=True, volatile=True), 'hw_revin1': Param('Type of input 1', type=oneofdict({ 1: 'nc', 0: 'no' }), default='no', settable=True, volatile=True), 'hw_revin2': Param('Type of input 2', type=oneofdict({ 1: 'nc', 0: 'no' }), default='no', settable=True, volatile=True), 'hw_disin1rev': Param('Use Input 1 as reference input', type=oneofdict({ 1: 'off', 0: 'on' }), default='on', settable=True, volatile=True), 'hw_disin2rev': Param('Use Input 2 as reference input', type=oneofdict({ 1: 'off', 0: 'on' }), default='on', settable=True, volatile=True), 'hw_invrev': Param('Direction of reference drive', type=oneofdict({ 1: 'pos', 0: 'neg' }), default='neg', settable=True, volatile=True), } parameter_overrides = { 'microsteps': Override(mandatory=False, settable=True, volatile=True), 'refpos': Override(settable=True), } # more advanced stuff: setting/getting parameters # only to be used manually at the moment @usermethod @requires(level='user') def readParameter(self, index): self.log.debug('readParameter %d', index) try: index = int(self._paridx.get(index, index)) except ValueError: raise UsageError( self, 'Unknown parameter %r, try one of %s' % (index, ', '.join(self._paridx))) from None if self._readStatusWord() & (1 << 7): raise UsageError( self, 'Can not access Parameters while Motor is ' 'moving, please stop it first!') if self.power == 'on': self.power = 'off' # wait for inactive ACK/NACK self.log.debug('Wait for idle ACK/NACK bits') for _ in range(1000): if self._readStatusWord() & (3 << 14) == 0: break else: raise CommunicationError( self, 'HW still busy, can not read ' 'Parameter, please retry later.') self._writeUpperControlWord((index << 8) | 4) self.log.debug('Wait for ACK/NACK bits') for _ in range(1000): if self._readStatusWord() & (3 << 14) != 0: break else: raise CommunicationError( self, 'ReadPar command not recognized by ' 'HW, please retry later.') if self._readStatusWord() & (1 << 14): raise CommunicationError( self, 'Reading of Parameter %r failed, ' 'got a NACK' % index) return self._readReturn() @usermethod @requires(level='admin') def writeParameter(self, index, value, store2eeprom=False): self.log.debug('writeParameter %d:0x%04x', index, value) if store2eeprom: self.log.warning('writeParameter stores to eeprom !') try: index = int(self._paridx.get(index, index)) except ValueError: UsageError(self, 'Unknown parameter %r' % index) if self._readStatusWord() & (1 << 7): raise UsageError( self, 'Can not access Parameters while Motor is ' 'moving, please stop it first!') if self.power == 'on': self.power = 'off' # wait for inactive ACK/NACK self.log.debug('Wait for idle ACK/NACK bits') for _ in range(1000): if self._readStatusWord() & (3 << 14) == 0: break else: raise CommunicationError( self, 'HW still busy, can not write ' 'Parameter, please retry later.') self._writeDestination(value) if store2eeprom: # store to eeprom self._writeUpperControlWord((index << 8) | 3) else: # store to volatile memory self._writeUpperControlWord((index << 8) | 1) self.log.debug('Wait for ACK/NACK bits') for _ in range(1000): if self._readStatusWord() & (3 << 14) != 0: break else: raise CommunicationError( self, 'WritePar command not recognized ' 'by HW, please retry later.') if self._readStatusWord() & (1 << 14): raise CommunicationError( self, 'Writing of Parameter %r failed, ' 'got a NACK' % index) return self._readReturn() # # Parameter access methods # def doWritePower(self, value): if self._readStatusWord() & (1 << 7): raise UsageError( self, 'Never switch off Power while Motor is ' 'moving !') value = ['off', 'on'].index(value) # docu: bit0 = enable/disable self._writeControlBit(0, value) def doReadPower(self): # docu: bit0 = enable/disable return ['off', 'on'][self._readControlBit(0)] # Parameter 1 : CurrentPosition def doSetPosition(self, value): self.writeParameter(1, self._phys2steps(value)) # Parameter 2 : Refpos def doReadRefpos(self): return self._steps2phys(self.readParameter(2)) def doWriteRefpos(self, value): self.writeParameter(2, self._phys2steps(value), store2eeprom=True) # Parameter 3 : hw_vmax -> speed def doReadHw_Vmax(self): return self.readParameter(3) def doReadSpeed(self): return self._speed2phys(self.hw_vmax) # units per second def doWriteHw_Vmax(self, value): self.writeParameter(3, value) def doWriteSpeed(self, speed): self.hw_vmax = self._phys2speed(speed) # Parameter 4 : hw_vmin -> minspeed def doReadHw_Vmin(self): return self.readParameter(4) def doReadMinspeed(self): return self._speed2phys(self.hw_vmin) # units per second def doWriteHw_Vmin(self, value): self.writeParameter(4, value) def doWriteMinspeed(self, speed): self.hw_vmin = self._phys2speed(speed) # Parameter 5 : hw_vref -> refspeed def doReadHw_Vref(self): return self.readParameter(5) # µSteps per second def doReadRefspeed(self): return self._speed2phys(self.hw_vref) # units per second def doWriteHw_Vref(self, value): self.writeParameter(5, value) def doWriteRefspeed(self, speed): self.hw_vref = self._phys2speed(speed) # Parameter 6 : hw_accel -> accel def doReadHw_Accel(self): return self.readParameter(6) # µSteps per second def doReadAccel(self): return self._speed2phys(self.hw_accel) # units per second def doWriteHw_Accel(self, value): self.writeParameter(6, value) def doWriteAccel(self, accel): self.hw_accel = self._phys2speed(accel) # Parameter 7 : hw_accel_e -> stopaccel def doReadHw_Accel_E(self): return self.readParameter(7) # µSteps per second def doReadStopaccel(self): return self._speed2phys(self.hw_accel_e) # units per second def doWriteHw_Accel_E(self, value): self.writeParameter(7, value) def doWriteStopaccel(self, accel): self.hw_accel_e = self._phys2speed(accel) # Parameter 8 : microsteps def doWriteMicrosteps(self, value): for i in range(7): if value == 2**i: self.writeParameter(8, i) break else: raise InvalidValueError( self, 'This should never happen! value should be one of: ' '1, 2, 4, 8, 16, 32, 64 !') def doReadMicrosteps(self): return 2**self.readParameter(8) # Parameter 9 : hw_backlash -> backlash def doReadHw_Backlash(self): return self.readParameter(9) # µSteps per second def doReadBacklash(self): return self._steps2phys(self.hw_backlash) def doWriteHw_Backlash(self, value): self.writeParameter(9, value) def doWriteBacklash(self, value): self.hw_backlash = self._phys2steps(value) # Parameter 10 : Fullsteps per turn def doReadHw_Fullsteps(self): return self.readParameter(10) def doWriteHw_Fullsteps(self, value): self.writeParameter(10, value) # Parameter 11 : MaxCurrent def doReadMaxcurrent(self): return self.readParameter(11) * 0.05 def doWriteMaxcurrent(self, value): self.writeParameter(11, int(0.5 + value / 0.05)) # Parameter 12 : IdleCurrent def doReadIdlecurrent(self): return self.readParameter(12) * 0.05 def doWriteIdlecurrent(self, value): self.writeParameter(12, int(0.5 + value / 0.05)) # Parameter 20 : Encodersteps per turn def doReadHw_Enc_Steps(self): return self.readParameter(20) def doWriteHw_Enc_Steps(self, value): self.writeParameter(20, value) # Parameter 30 : Features def doReadHw_Features(self): value = self.readParameter(30) self.log.debug('Feature0: Inputfilter for encodersignals: %d', value & 1) self.log.debug( 'Feature1: Positionsrueckfuehrung (0=Encoder, ' '1=Zaehler): %d', (value >> 1) & 1) self.log.debug( 'Feature2: Zaehlrichtung encoder (0=mitlaufend, ' '1=gegenlaufend): %d', (value >> 2) & 1) self.log.debug('Feature3: Bremsrampe (0=linear, 1=exponentiell): %d', (value >> 3) & 1) self.log.debug('Feature4: Eingang1 (0=Schliesser, 1=oeffner): %d', (value >> 4) & 1) self.log.debug('Feature5: Eingang2 (0=Schliesser, 1=oeffner): %d', (value >> 5) & 1) self.log.debug('Feature6: Eingang1 (0=referenz, 1=normal): %d', (value >> 6) & 1) self.log.debug('Feature7: Eingang2 (0=referenz, 1=normal): %d', (value >> 7) & 1) self.log.debug( 'Feature8: Richtung der Referenzfahrt (0=negativ, ' '1=positiv): %d', (value >> 8) & 1) return value def doWriteHw_Features(self, value): self.writeParameter(30, value) # bitwise access def doReadHw_Coderflt(self): return (self.hw_features >> 0) & 1 def doWriteHw_Coderflt(self, value): if value in [0, 1]: self.hw_features = (self.hw_features & ~(1 << 0)) | (value << 0) else: raise InvalidValueError(self, 'hw_disencfltr can only be 0 or 1') def doReadHw_Feedback(self): return (self.hw_features >> 1) & 1 def doWriteHw_Feedback(self, value): if value in [0, 1]: self.hw_features = (self.hw_features & ~(1 << 1)) | (value << 1) else: raise InvalidValueError(self, 'hw_feedback can only be 0 or 1') def doReadHw_Invposfb(self): return (self.hw_features >> 2) & 1 def doWriteHw_Invposfb(self, value): if value in [0, 1]: self.hw_features = (self.hw_features & ~(1 << 2)) | (value << 2) else: raise InvalidValueError(self, 'hw_invposfb can only be 0 or 1') def doReadHw_Ramptype(self): return (self.hw_features >> 3) & 1 def doWriteHw_Ramptype(self, value): if value in [0, 1]: self.hw_features = (self.hw_features & ~(1 << 3)) | (value << 3) else: raise InvalidValueError(self, 'hw_ramptype can only be 0 or 1') def doReadHw_Revin1(self): return (self.hw_features >> 4) & 1 def doWriteHw_Revin1(self, value): if value in [0, 1]: self.hw_features = (self.hw_features & ~(1 << 4)) | (value << 4) else: raise InvalidValueError(self, 'hw_revin1 can only be 0 or 1') def doReadHw_Revin2(self): return (self.hw_features >> 5) & 1 def doWriteHw_Revin2(self, value): if value in [0, 1]: self.hw_features = (self.hw_features & ~(1 << 5)) | (value << 5) else: raise InvalidValueError(self, 'hw_revin2 can only be 0 or 1') def doReadHw_Disin1Rev(self): return (self.hw_features >> 6) & 1 def doWriteHw_Disin1Rev(self, value): if value in [0, 1]: self.hw_features = (self.hw_features & ~(1 << 6)) | (value << 6) else: raise InvalidValueError(self, 'hw_disin1rev can only be 0 or 1') def doReadHw_Disin2Rev(self): return (self.hw_features >> 7) & 1 def doWriteHw_Disin2Rev(self, value): if value in [0, 1]: self.hw_features = (self.hw_features & ~(1 << 7)) | (value << 7) else: raise InvalidValueError(self, 'hw_disin2rev can only be 0 or 1') def doReadHw_Invrev(self): return (self.hw_features >> 8) & 1 def doWriteHw_Invrev(self, value): if value in [0, 1]: self.hw_features = (self.hw_features & ~(1 << 8)) | (value << 8) else: raise InvalidValueError(self, 'hw_invrev can only be 0 or 1') # Parameter 40 : Temperature def doReadTemperature(self): return self.readParameter(40) # Parameter 250 : Klemmentyp def doReadHw_Type(self): return self.readParameter(250) # Parameter 251 : Hardwarestand def doReadHw_Revision(self): return self.readParameter(251) # Parameter 252 : Firmwarestand def doReadHw_Firmware(self): return self.readParameter(252) # Parameter 255 : Factory Reset @usermethod def FactoryReset(self, password): """resets the motorcontroller to factory default values for the right password see docu""" # 0x544B4531 self.writeParameter(255, password)
from numpy import int32, uint16, uint32 from nicos import session from nicos.core import SIMULATION, Attach, CommunicationError, \ ConfigurationError, HasTimeout, InvalidValueError, Moveable, MoveError, \ Override, Param, PositionError, UsageError, floatrange, intrange, \ none_or, oneof, oneofdict, requires, status, usermethod from nicos.core.utils import multiStatus from nicos.devices.abstract import CanReference, Coder, Motor from nicos.devices.generic import Switcher from nicos.devices.generic.sequence import SeqMethod, SequencerMixin from nicos.devices.tango import PyTangoDevice WATCHDOG_REGISTER = 0x1120 WATCHDOG_DISABLE = 0 CODER_VALIDATOR = intrange(0x4000, 0x4800) MOTOR_VALIDATOR = oneof(*range(0x4020, 0x4800, 10)) class Sans1ColliSlit(Switcher): """class for slit mounted onto something moving and thus beeing only effective if the underlying device is in a certain position. """ attached_devices = { 'table': Attach('Guide table this slit is mounted on', Moveable), } parameters = { 'activeposition':
class SXTalBase(Instrument, Moveable): """An instrument class that can move in q space. When setting up a single xtal configuration, use a subclass that reflects the instrument geometry as your instrument device. """ attached_devices = { 'mono': Attach('Monochromator device', Monochromator), } parameters = { 'wavelength': Param('Wavelength', type=float, volatile=True, settable=False), 'scanmode': Param('Scanmode', type=oneof('omega', 't2t'), userparam=True, settable=True, default='omega'), 'scansteps': Param('Scan steps', type=intrange(10, 999), userparam=True, settable=True, default=40), 'scan_uvw': Param('U,V,W Param', type=vec3, userparam=True, settable=True, default=[1.0, 1.0, 1.0]), } parameter_overrides = { 'fmtstr': Override(default='[%6.4f, %6.4f, %6.4f]'), 'unit': Override(default='rlu', mandatory=False, settable=True), } valuetype = tupleof(float, float, float) hardware_access = False _last_calpos = None def doInit(self, mode): self.__dict__['h'] = SXTalIndex('h', unit='rlu', fmtstr='%.3f', index=0, lowlevel=True, sxtal=self) self.__dict__['k'] = SXTalIndex('k', unit='rlu', fmtstr='%.3f', index=1, lowlevel=True, sxtal=self) self.__dict__['l'] = SXTalIndex('l', unit='rlu', fmtstr='%.3f', index=2, lowlevel=True, sxtal=self) self._last_calpos = None def doShutdown(self): for name in ['h', 'k', 'l']: if name in self.__dict__: self.__dict__[name].shutdown() def _calcPos(self, hkl, wavelength=None): """Calculate instrument position object for given HKL position.""" cell = session.experiment.sample.cell cpos = PositionFactory('c', c=cell.CVector(hkl)) return self._convertPos(cpos, wavelength) def _extractPos(self, pos): """Extract values for the attached devices from a position object. Must be implemented in subclasses. """ raise NotImplementedError def _convertPos(self, pos, wavelength=None): """Convert given position to the type usable by this geometry. Must be implemented in subclasses. """ raise NotImplementedError def _readPos(self, maxage=0): """Create a position object with the current values of the attached devices. Must be implemented in subclasses. """ raise NotImplementedError def _createPos(self, **kwds): """Create a position object with the values of the given devices. Must be implemented in subclasses. """ raise NotImplementedError def _calpos(self, hkl, printout=True, checkonly=True): """Implements the calpos() command.""" try: poslist = self._extractPos(self._calcPos(hkl)) except Exception as err: return False, str(err) ok, why = True, '' for devname, value in poslist: dev = self._adevs[devname] if dev is None: continue devok, devwhy = dev.isAllowed(value) if not devok: ok = False why += 'target position %s outside limits for %s: %s -- ' % \ (dev.format(value, unit=True), dev, devwhy) self.log.info('%-14s %8.3f %s', dev.name + ':', value, dev.unit) if ok: self._last_calpos = hkl if checkonly: self.log.info('position allowed') else: if checkonly: self.log.warning('position not allowed: %s', why[:-4]) else: raise LimitError(self, 'position not allowed: ' + why[:-4]) def _reverse_calpos(self, **kwds): """Implements the pos2hkl() command.""" pos = self._createPos(**kwds).asC() hkl = session.experiment.sample.cell.hkl(pos.c) self.log.info('pos: [%.4f, %.4f, %.4f]', *hkl) def doIsAllowed(self, hkl): try: poslist = self._extractPos(self._calcPos(hkl)) except Exception as err: return False, str(err) # check limits for the individual axes for (devname, devvalue) in poslist: dev = self._adevs[devname] if dev is None: continue else: ok, why = dev.isAllowed(devvalue) if not ok: return ok, 'target position %s outside limits for %s: %s' % \ (dev.format(devvalue, unit=True), dev, why) return True, '' def _sim_getMinMax(self): ret = [] if self._sim_min is not None: for i, name in enumerate(['h', 'k', 'l']): ret.append( (name, '%.4f' % self._sim_value[i], '%.4f' % self._sim_min[i], '%.4f' % self._sim_max[i])) return ret def _sim_setValue(self, pos): self._sim_old_value = self._sim_value self._sim_value = pos self._sim_min = tuple(map(min, pos, self._sim_min or pos)) self._sim_max = tuple(map(max, pos, self._sim_max or pos)) def doStart(self, hkl): poslist = self._extractPos(self._calcPos(hkl)) for (devname, devvalue) in poslist: dev = self._adevs[devname] dev.start(devvalue) # store the min and max values of h,k,l, and E for simulation self._sim_setValue(hkl) def doFinish(self): # make sure index members read the latest value for index in (self.h, self.k, self.l): if index._cache: index._cache.invalidate(index, 'value') def valueInfo(self): return Value('h', unit='rlu', fmtstr='%.4f'), \ Value('k', unit='rlu', fmtstr='%.4f'), \ Value('l', unit='rlu', fmtstr='%.4f') def doRead(self, maxage=0): pos = self._readPos(maxage).asC() hkl = session.experiment.sample.cell.hkl(pos.c) return list(hkl) def doReadWavelength(self, maxage=0): # ensure using correct unit oldunit = None if self._attached_mono.unit != 'A': oldunit = self._attached_mono.unit self._attached_mono.unit = 'A' result = self._attached_mono.read(0) if oldunit: self._attached_mono.unit = oldunit return result def getScanWidthFor(self, hkl): """Get scan width for a certain HKL.""" raise NotImplementedError