class WebhookForwarder(ForwarderBase, Device): """Forwards cache updates to a web service.""" parameters = { 'hook_url': Param('Hook URL endpoint', type=str, mandatory=True), 'prefix': Param('Cache key prefix', type=str, mandatory=True), 'http_mode': Param('HTTP request mode', type=oneof('GET', 'POST'), mandatory=True), 'paramencoding': Param('Parameter encoding', type=oneof('plain', 'json'), mandatory=True), 'jsonname': Param('JSON parameter name (used for GET requests)', type=str, default='json'), } def doInit(self, mode): if requests is None: raise ConfigurationError(self, 'requests package is missing') self._prefix = self.prefix.strip('/') if self._prefix: self._prefix += '/' self._initFilters() self._queue = queue.Queue(1000) self._processor = createThread('webhookprocessor', self._processQueue) def _putChange(self, time, ttl, key, value): if not self._checkKey(key): return pdict = dict(time=time, ttl=ttl, key=self._prefix + key, value=value) retry = 2 while retry: try: self._queue.put(pdict, False) break except queue.Full: self._queue.get() self._queue.task_done() retry -= 1 def _webHookTask(self, pdict): try: if self.paramencoding == 'json': if self.http_mode == 'GET': pdict = {self.jsonname: json.dumps(pdict)} requests.get(self.hook_url, params=pdict, timeout=0.5) elif self.http_mode == 'POST': requests.post(self.hook_url, json=pdict, timeout=0.5) else: if self.http_mode == 'GET': requests.get(self.hook_url, params=pdict, timeout=0.5) elif self.http_mode == 'POST': requests.post(self.hook_url, data=pdict, timeout=0.5) except Exception: self.log.warning('Execption during webhook call', exc=True) def _processQueue(self): while not self._stoprequest: item = self._queue.get() self._webHookTask(item) self._queue.task_done()
def test_oneof(): assert oneof(0, 1)(1) == 1 assert oneof(2, 3)() == 2 assert oneof(None)() is None assert oneof(None)(None) is None assert oneof()() is None assert oneof()(None) is None assert raises(ValueError, oneof(0, 1), '0') assert raises(ValueError, oneof(0, 1), 2) assert raises(ValueError, oneof(0, 1), 'x')
class Polariser(Moveable): """ The polariser is in when segment 1 is on zus, out else """ attached_devices = { 'cols1': Attach('Collimator segment 1', Moveable), } parameter_overrides = { 'unit': Override(mandatory=False), } valuetype = oneof('in', 'out') def doRead(self, maxage=0): pos = self._attached_cols1.read(maxage) if pos == 'zus': return 'in' return 'out' def doStart(self, pos): if pos == 'in': self._attached_cols1.start('zus') else: self._attached_cols1.start('ble') def doStatus(self, maxage=0): return self._attached_cols1.status(maxage)
class HAMEG8131(EpicsMoveable): """ This class takes care of initialising the frequency generator, and switching it on or off """ attached_devices = { 'port': Attach('Port to talk directly to the device', EpicsCommandReply), 'freq': Attach('Flipper frequency', EpicsMoveable), 'amp': Attach('Flipper amplitude', EpicsMoveable), } valuetype = oneof('on', 'off') def doInit(self, mode): if mode == SIMULATION: return inicommands = ['RM1', 'RFI', 'OT0', 'SW0', 'SK0', 'CTM', 'VPP', 'AM0', 'SIN', 'OFS:0E+0', 'FRQ:2.534E+5', 'AMP:2.6E+0'] for com in inicommands: self._attached_port.execute(com) def doIsAllowed(self, target): if target == 'on': freq = self._attached_freq.read(0) amp = self._attached_amp.read(0) if freq < 1. or amp < .1: return False, 'Set frequency and amplitude first' return True, '' def doStart(self, pos): if pos == 'on': self._put_pv('writepv', 1) else: self._put_pv('writepv', 0) def doRead(self, maxage=0): val = self._get_pv('readpv') freq = self._attached_freq.read(maxage) amp = self._attached_amp.read(maxage) if val == 0 or freq < 1. or amp < .1: return 'off' return 'on' def doStatus(self, maxage=0): pos = self.doRead(maxage) if pos == self.target: return status.OK, 'Done' return status.BUSY, 'Moving'
class Flipper(MezeiFlipper): """MezeiFlipper subclass that has values of up/down instead of on/off.""" valuetype = oneof(UP, DOWN) def doRead(self, maxage=0): if (abs(self._attached_corr.read(maxage)) > 0.05 or abs(self._attached_flip.read(maxage)) > 0.05): return DOWN return UP def doStart(self, value): if value == DOWN: self._attached_flip.start(self.currents[0]) self._attached_corr.start(self.currents[1]) else: self._attached_flip.start(0) self._attached_corr.start(0)
class SingleSideRead(Readable): """We need read access to a single side of a nok without mode. Readable is enough! """ attached_devices = { 'device': Attach('access to device with several axis', Readable), } parameters = { 'index': Param('side of nok', type=oneof(0, 1), settable=False, mandatory=True), } def doRead(self, maxage=0): self.log.debug('SingleSiedRead read') return self._attached_device.read(maxage)[self.index] def doStatus(self, maxage=0): self.log.debug('SingleSideRead status') return self._attached_device.status(maxage)
class Beamstop(BeamstopSequencer): """ This is a SANS special device for moving the beamstop in or out. This requires a sequence of operations. Order of driving is important. """ _out_x = 28. _out_y = -543. valuetype = oneof('in', 'out') def doRead(self, maxage): x = self._attached_x.read(maxage) y = self._attached_y.read(maxage) summ = abs(x - self._out_x) summ += abs(y - self._out_y) if summ < 1: return 'out' return 'in' def _generateSequence(self, target): seq = [] s = SeqMethod(self, '_release') seq.append(s) if target == 'out': seq.append(SeqMethod(self, '_save_pos')) seq.append(SeqDev(self._attached_x, self._out_x)) seq.append(SeqLimDev(self._attached_y, self._out_y, self._out_y)) seq.append(SeqMethod(self, '_fix')) else: seq.append(SeqMethod(self, '_release')) seq.append(SeqLimDev(self._attached_y, self._in_y, -450)) seq.append(SeqDev(self._attached_x, self._in_x)) return seq
class NarzissSpin(BaseSequencer): attached_devices = { 'pom': Attach('Polariser rotation', Moveable), 'pmag': Attach('Polariser magnet', Moveable), } valuetype = oneof('+', '-', '0', 'undefined') def _generateSequence(self, target): seq = [] if target == '+': seq.append((SeqDev(self._attached_pom, 0.8), SeqDev(self._attached_pmag, -2.5))) seq.append(SeqSleep(4.)) seq.append(SeqDev(self._attached_pmag, .15)) elif target == '-': seq.append((SeqDev(self._attached_pom, 0.8), SeqDev(self._attached_pmag, 2.5))) seq.append(SeqSleep(4.)) seq.append(SeqDev(self._attached_pmag, 1.)) elif target == '0': seq.append((SeqDev(self._attached_pom, 3), SeqDev(self._attached_pmag, 0))) else: raise ProgrammingError('Invalid value requested') return seq def doRead(self, maxage=0): val = self._attached_pmag.read(maxage) if abs(val - .15) < .02: return '+' if abs(val - 1.) < .05: return '-' if abs(val - 0) < .05: return '0' return 'undefined'
class ImageChannel(QMesyDAQImage, BaseImageChannel): parameters = { 'readout': Param('Readout mode of the Detector', settable=True, type=oneof('raw', 'mapped', 'amplitude'), default='mapped', mandatory=False, chatty=True), } def doWriteListmode(self, value): self._dev.SetProperties(['writelistmode', '%s' % value]) return self._getProperty('writelistmode') def doWriteHistogram(self, value): self._dev.SetProperties('writehistogram', '%s' % value) return self._getProperty('writehistogram') def doWriteReadout(self, value): self._dev.SetProperties(['histogram', '%s' % value]) return self._getProperty('histogram') def doWriteListmodefile(self, value): self._dev.SetProperties(['lastlistfile', '%s' % value]) return self._getProperty('lastlistfile') # def doReadListmodefile(self): # return self._getProperty('lastlistfile') def doWriteHistogramfile(self, value): self._taco_update_resource('lasthistfile', '%s' % value) return self._getProperty('lasthistfile') # def doReadHistogramfile(self): # return self._getProperty('lasthistfile') def doReadConfigfile(self): return self._getProperty('configfile') def doReadCalibrationfile(self): return self._getProperty('calibrationfile')
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 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
class Segment(Moveable): """ This class controls a SANS collimator segment through a host of attached slave devices """ attached_devices = { 'hand': Attach('Switch into remote mode', Moveable), 'ble': Attach('Switch to BLE position', Moveable), 'nl': Attach('Switch to NL position', Moveable), 'zus': Attach('Switch to ZUS position', Moveable), 'mot_error': Attach('Test motor errror', Readable), 'seq_error': Attach('Test sequence error', Readable), 'bolt_error': Attach('Test bolt error', Readable), 'mot_fast': Attach('Motor fast', Readable), 'mot_slow': Attach('Motor slow', Readable) } valuetype = oneof('ble', 'nl', 'zus') parameter_overrides = { 'unit': Override(mandatory=False), } _target = None _start_time = None def doInit(self, mode): self._target = self.read(0) self._attached_hand.start(1) def doRead(self, maxage=0): if self._attached_ble.read(maxage) == 1: return 'ble' if self._attached_nl.read(maxage) == 1: return 'nl' if self._attached_zus.read(maxage) == 1: return 'zus' return 'transit' def _test_error(self): if self._attached_mot_error.read() == 1: return True, 'motor error' if self._attached_seq_error.read() == 1: return True, 'sequence error' if self._attached_bolt_error.read() == 1: return True, 'bolt error' return False, '' def doIsAllowed(self, pos): test, reason = self._test_error() if test: return False, reason return True, '' def doStart(self, pos): # Will only need to be done once (or someone messes with the rack) if self._attached_hand.read(0) == 0: self._attached_hand.start(1) self._target = pos self._start_time = time.time() if pos == 'ble': self._attached_ble.start(1) elif pos == 'nl': self._attached_nl.start(1) elif pos == 'zus': self._attached_zus.start(1) def doStatus(self, maxage=0): test, reason = self._test_error() if test: return status.ERROR, \ 'Collimator SPS broken with %s, reset manually' % reason pos = self.doRead(maxage) if pos == self._target: return status.OK, ' ' else: if time.time() > self._start_time + 360: return status.ERROR, 'Timeout moving segment' if self._attached_mot_fast.read(maxage) == 1: return status.BUSY, 'Moving fast' if self._attached_mot_slow.read(maxage) == 1: return status.BUSY, 'Moving slow' return status.BUSY, 'Locking/Unlocking'
class Changer(BaseSequencer): attached_devices = { 'lift': Attach('Lift moving a mono up or down between 4 ' 'positions', Moveable), 'magazine': Attach('Magazine holding the monos and having 4 ' 'positions', Moveable), 'liftclamp': Attach('Clamp holding the mono in the lift', Moveable), 'magazineclamp': Attach('Clamp holding the mono in the magazine', Moveable), 'tableclamp': Attach('Clamp holding the mono on the table', Moveable), 'inhibitrelay': Attach('Inhibit to the remaining motor controllers', Moveable), 'enable': Attach('To enable operation of the changer', Moveable), 'magazineocc': Attach('Readout for occupancy of the magazine', Readable), 'magazinestatus': Attach('Readout for status of magazine load position', Readable), } parameters = { 'mono_on_table': Param('Name of Mono on the Monotable', type=oneof('PG', 'Si', 'Cu', 'Heusler', 'None', 'empty frame'), default='None', settable=True, internal=True), 'mono_in_lift': Param('Which mono is in the lift', type=oneof('PG', 'Si', 'Cu', 'Heusler', 'None', 'empty frame'), default='None', settable=True, internal=True), 'exchangepos': Param( 'dict of device names to positional values ' 'for changing monos', type=dict, settable=False, userparam=False), 'precisionchange': Param( 'dict of device names and pairs of (normal ' 'value, change value) of the precision', type=dict, settable=False, userparam=False), } parameter_overrides = { 'requires': Override(default={'level': 'admin'}), } positions = ['101', '110', '011', '111'] # CounterClockwise monos = ['Heusler', 'PG', 'Si', 'empty frame'] # assigned monos shields = ['110', '110', '110', '110'] # which magzinslot after changing # (e.g. Put a PE dummy to 101 and set this to ('101,'*4).split(',') valuetype = oneof(*(monos + ['None'])) def PrepareChange(self): '''is checking whether all the conditions for a change of mono are met''' # if not(self.SecShutter_is_closed()): # raise UsageError(self, 'Secondary Shutter needs to be closed, ' # 'please close by hand and retry!') # if self.NotAus(): # raise UsageError(self, 'NotAus (Emergency stop) activated, ' # 'please check and retry!') # read all inputs and check for problems if not (self._attached_magazine.read() in self.positions): raise HWError(self, 'Unknown Magazine-Position !') if self._attached_lift.read() != '2': raise HWError(self, 'Lift not at parking position!') if self._attached_liftclamp.read() != 'close': raise HWError(self, 'liftclamp should be closed!') if self._attached_tableclamp.read() != 'close': raise HWError(self, 'tableclamp should be closed!') if self._attached_magazineclamp.read() != 'close': raise HWError(self, 'magazineclamp should be closed!') if self.mono_in_lift != 'None': raise HWError( self, 'There is mono %s in the lift, please change ' 'manually!' % self.mono_in_lift) # XXX TODO: store old values of positions and offsets someplace to # recover after changing back self.log.info('will change position of several devices to the ' 'changing position, moving back is not yet implemented') # enhance precision of the devices for devname, prec in self.precisionchange.items(): dev = session.getDevice(devname) dev.precision = prec[1] self.log.debug('changing precision of the %s device to the %f', devname, prec[1]) # go to the place where a change is possible devs = [] for devname, pos in self.exchangepos.items(): dev = session.getDevice(devname) # remove after implementation of moving back self.log.info('position of the %s device was %f', devname, dev()) if isinstance(dev, HasOffset): dev.start(pos - dev.offset) self.log.debug( 'move and wait %s to the position %f - offset of %f', devname, pos, dev.offset) dev.wait() else: dev.start(pos) self.log.debug('move and wait %s to the position %f', devname, pos) dev.wait() devs.append(dev) multiWait(devs) # put precision back to normal for devname, prec in self.precisionchange.items(): dev = session.getDevice(devname) dev.precision = prec[0] self.log.debug( 'changing precision of the %s device back to ' 'the %f', devname, prec[0]) try: dev = session.getDevice('focibox') self.mono_on_table = dev.read(0) dev.comm('XMA', forcechannel=False) dev.comm('YMA', forcechannel=False) dev.driverenable = False self.log.info('focus motors disabled') except NicosError as err: self.log.error('Problem disabling foci: %s', err) # switch on inhibit and enable self._attached_enable.maw(0xef16) self._attached_inhibitrelay.maw('on') self.log.info('changer enabled and inhibit active') def FinishChange(self): self._attached_inhibitrelay.maw('off') self._attached_enable.maw(0) self.log.warning('Please restart the daemon or reload the setups to ' 'init the new Mono:') self.log.warning(' > NewSetup()') self.log.info(' > NewSetup()') def CheckMagazinSlotEmpty(self, slot): # checks if the given slot in the magazin is empty self.log.info('checking of there IS NOT mono in magazine slot %r' % slot) if self._attached_magazineocc.status(0)[0] != status.OK: raise UsageError( self, 'Magazine occupancy switches are in warning state!') index = self.positions.index(slot) if not ((self._attached_magazineocc.read() >> index * 2) & 1): raise UsageError(self, 'Position %r is already occupied!' % slot) def CheckMagazinSlotUsed(self, slot): # checks if the given slot in the magazin is used (contains a monoframe) self.log.info('checking of there IS mono in magazine slot %r' % slot) if self._attached_magazineocc.status(0)[0] != status.OK: raise UsageError( self, 'Magazine occupancy switches are in warning state!') index = self.positions.index(slot) if (self._attached_magazineocc.read() >> index * 2) & 1: raise UsageError(self, 'Position %r is empty!' % slot) def _start(self, seq): if self._seq_is_running(): raise MoveError(self, 'Can not start sequence, device is still ' 'busy') self._startSequence(seq) # here is the party going on! def Transfer_Mono_Magazin2Lift(self): self._start(self._gen_mag2lift()) self.wait() def _gen_mag2lift(self, magpos=None): seq = [] if magpos is None: magpos = self._attached_magazine.read(0) else: seq.append(SeqDev(self._attached_magazine, magpos)) # check preconditions seq.append( SeqCall(self.log.info, 'checking preconditions for Magazin2Lift')) seq.append(SeqCheckStatus(self._attached_magazine, status.OK)) seq.append(SeqCheckStatus(self._attached_lift, status.OK)) seq.append(SeqCheckPosition(self._attached_lift, '2')) seq.append(SeqCheckPosition(self._attached_liftclamp, 'close')) seq.append(SeqCheckPosition(self._attached_magazine, magpos)) seq.append(SeqCheckPosition(self._attached_magazineclamp, 'close')) seq.append(SeqMethod(self, 'CheckMagazinSlotUsed', magpos)) seq.append(SeqCheckAttr(self, 'mono_in_lift', 'None')) # transfer mono to lift seq.append( SeqCall(self.log.info, 'transferring mono from magazine to lift')) seq.append(SeqDev(self._attached_liftclamp, 'open')) seq.append(SeqDev(self._attached_lift, '3')) # almost top position seq.append(SeqMethod(self._attached_liftclamp, 'start', 'close')) seq.append(SeqDev(self._attached_lift, '4')) # top position seq.append(SeqDev(self._attached_liftclamp, 'close')) seq.append(SeqMethod(self._attached_magazineclamp, 'start', 'open')) # rattle a little seq.append(SeqCall(self.log.info, 'rattle to release magazine grab')) seq.append(SeqDev(self._attached_lift, '3')) # almost top position seq.append(SeqDev(self._attached_lift, '4')) # top position seq.append(SeqDev(self._attached_lift, '3')) # almost top position seq.append(SeqDev(self._attached_lift, '4')) # top position seq.append(SeqDev(self._attached_magazineclamp, 'open')) seq.append(SeqMethod(self, 'CheckMagazinSlotEmpty', magpos)) # move (with mono) to parking position seq.append( SeqCall(self.log.info, 'moving with mono to parking position')) seq.append( SeqSetAttr(self, 'mono_in_lift', self.monos[self.positions.index(magpos)])) seq.append(SeqDev(self._attached_lift, '2')) # park position seq.append(SeqDev(self._attached_magazineclamp, 'close')) # Magazin should not contain a mono now seq.append(SeqMethod(self, 'CheckMagazinSlotEmpty', magpos)) return seq def Transfer_Mono_Lift2Magazin(self): self._start(self._gen_lift2mag()) self.wait() def _gen_lift2mag(self, magpos=None): seq = [] if magpos is None: magpos = self._attached_magazine.read(0) else: seq.append(SeqDev(self._attached_magazine, magpos)) # check preconditions seq.append( SeqCall(self.log.info, 'checking preconditions for Lift2Magazin')) seq.append(SeqCheckStatus(self._attached_magazine, status.OK)) seq.append(SeqCheckStatus(self._attached_lift, status.OK)) seq.append(SeqCheckPosition(self._attached_lift, '2')) seq.append(SeqCheckPosition(self._attached_liftclamp, 'close')) seq.append(SeqCheckPosition(self._attached_magazine, magpos)) seq.append(SeqCheckPosition(self._attached_magazineclamp, 'close')) seq.append(SeqMethod(self, 'CheckMagazinSlotEmpty', magpos)) # there needs to be a mono in the lift seq.append( SeqCall(self.log.info, 'checking if there is a mono in lift')) seq.append( SeqCheckAttr(self, 'mono_in_lift', values=[m for m in self.monos if m != 'None'])) # prepare magazin seq.append(SeqCall(self.log.info, 'testing magazin grab')) seq.append(SeqDev(self._attached_magazineclamp, 'open')) seq.append(SeqDev(self._attached_magazineclamp, 'close')) seq.append(SeqDev(self._attached_magazineclamp, 'open')) seq.append(SeqDev(self._attached_magazineclamp, 'close')) seq.append(SeqDev(self._attached_magazineclamp, 'open')) # transfer mono to lift seq.append(SeqCall(self.log.info, 'moving lift to top position')) seq.append(SeqDev(self._attached_lift, '4')) # top position seq.append( SeqCall(self.log.info, 'closing the magazin grab and rattling lift')) seq.append(SeqMethod(self._attached_magazineclamp, 'start', 'close')) # rattle a little seq.append(SeqDev(self._attached_lift, '3')) # almost top position seq.append(SeqDev(self._attached_lift, '4')) # top position seq.append(SeqDev(self._attached_lift, '3')) # almost top position seq.append(SeqDev(self._attached_lift, '4')) # top position seq.append(SeqDev(self._attached_lift, '3')) # almost top position seq.append(SeqDev(self._attached_magazineclamp, 'close')) seq.append(SeqMethod(self, 'CheckMagazinSlotUsed', magpos)) seq.append(SeqCall(self.log.info, 'opening lift clamp')) seq.append(SeqMethod(self._attached_liftclamp, 'start', 'open')) seq.append(SeqDev(self._attached_lift, '4')) # top position seq.append(SeqDev(self._attached_lift, '3')) # top position seq.append(SeqDev(self._attached_liftclamp, 'open')) seq.append(SeqCall(self.log.info, 'moving lift to park position')) seq.append(SeqDev(self._attached_lift, '2')) # park position seq.append(SeqCall(self.log.info, 'closing lift clamp')) seq.append(SeqDev(self._attached_liftclamp, 'close')) # move (without mono) to parking position seq.append(SeqSetAttr(self, 'mono_in_lift', 'None')) # Magazin should not contain a mono now seq.append(SeqMethod(self, 'CheckMagazinSlotUsed', magpos)) return seq def Transfer_Mono_Lift2Table(self): self._start(self._gen_lift2table()) self.wait() def _gen_lift2table(self): seq = [] # check preconditions seq.append( SeqCall(self.log.info, 'checking preconditions for Lift2Table')) seq.append(SeqCheckStatus(self._attached_tableclamp, status.OK)) seq.append(SeqCheckStatus(self._attached_liftclamp, status.OK)) seq.append(SeqCheckStatus(self._attached_lift, status.OK)) seq.append(SeqCheckPosition(self._attached_lift, '2')) seq.append(SeqCheckPosition(self._attached_liftclamp, 'close')) seq.append(SeqCheckPosition(self._attached_tableclamp, 'close')) # there shall be a mono in the lift! seq.append(SeqCall(self.log.info, 'check if there is a mono in Lift')) seq.append( SeqCheckAttr(self, 'mono_in_lift', values=[m for m in self.monos if m != 'None'])) seq.append(SeqCheckAttr(self, 'mono_on_table', 'None')) seq.append(SeqCall(self.log.info, 'moving down the lift')) # transfer mono to table seq.append(SeqDev(self._attached_tableclamp, 'open')) seq.append(SeqDev(self._attached_lift, '1')) # bottom position seq.append( SeqCall(self.log.info, 'closing table grab and releasing lift clamp')) seq.append(SeqDev(self._attached_tableclamp, 'close')) # move (without mono) to parking position seq.append(SeqDev(self._attached_liftclamp, 'open')) def func(self): self.mono_on_table, self.mono_in_lift = self.mono_in_lift, 'None' seq.append(SeqCall(func, self)) # seq.append(SeqSetAttr(self, 'mono_on_table', self.mono_in_lift)) # seq.append(SeqSetAttr(self, 'mono_in_lift', 'None')) seq.append(SeqCall(self.log.info, 'moving lift to park position')) seq.append(SeqDev(self._attached_lift, '2')) # park position seq.append(SeqDev(self._attached_liftclamp, 'close')) # TODO: change foci alias and reactivate foci return seq def Transfer_Mono_Table2Lift(self): self._start(self._gen_table2lift()) self.wait() def _gen_table2lift(self): # XXX TODO drive all foci to 0 and switch of motors.... # XXX TODO move mty/mtx to monospecific abholposition # hier nur das reine abholen vom Monotisch seq = [] # check preconditions seq.append( SeqCall(self.log.info, 'checking preconditions for Table2Lift')) seq.append(SeqCheckStatus(self._attached_tableclamp, status.OK)) seq.append(SeqCheckStatus(self._attached_liftclamp, status.OK)) seq.append(SeqCheckStatus(self._attached_lift, status.OK)) seq.append(SeqCheckPosition(self._attached_lift, '2')) seq.append(SeqCheckPosition(self._attached_liftclamp, 'close')) seq.append(SeqCheckPosition(self._attached_tableclamp, 'close')) # there shall be no mono in the lift, but one on the table seq.append( SeqCall(self.log.info, 'checking if there IS NOT a mono in Lift')) seq.append(SeqCheckAttr(self, 'mono_in_lift', 'None')) seq.append( SeqCheckAttr(self, 'mono_on_table', values=[m for m in self.monos if m != 'None'])) # transfer mono to lift seq.append(SeqCall(self.log.info, 'moving down the lift')) seq.append(SeqDev(self._attached_liftclamp, 'open')) seq.append(SeqDev(self._attached_lift, '1')) # bottom position seq.append(SeqCall(self.log.info, 'grabbing the monochromator')) seq.append(SeqDev(self._attached_liftclamp, 'close')) seq.append(SeqDev(self._attached_tableclamp, 'open')) # move (with mono) to parking position def func(self): self.mono_on_table, self.mono_in_lift = 'None', self.mono_on_table seq.append(SeqCall(func, self)) # seq.append(SeqSetAttr(self, 'mono_on_table', 'None')) # seq.append(SeqSetAttr(self, 'mono_in_lift', self.mono_on_table)) seq.append( SeqCall(self.log.info, 'moving the lift with mono to parking position')) seq.append(SeqDev(self._attached_lift, '2')) # park position seq.append(SeqDev(self._attached_tableclamp, 'close')) return seq def doStart(self, target): self.change(self.mono_on_table, target) def change(self, old, whereto): ''' cool kurze Wechselroutine Der Knackpunkt ist in den Hilfsroutinen!''' if not (old in self.monos + ['None']): raise UsageError( self, '\'%s\' is illegal value for Mono, use one ' 'of ' % old + ', '.join(self.monos + ['None'])) if not (whereto in self.monos + ['None']): raise UsageError( self, '\'%s\' is illegal value for Mono, use one ' 'of ' % whereto + ', '.join(self.monos + ['None'])) self.PrepareChange() if self.monos.index(whereto) == self.monos.index(old): # Nothing to do, requested Mono is supposed to be on the table return # Ok, we have a good state, the only thing we do not know is which mono # is on the table...... # for this we use the (cached) parameter mono_on_table if self.mono_on_table != old: raise UsageError( self, 'Mono %s is not supposed to be on the ' 'table, %s is!' % (old, self.mono_on_table)) seq = [] # 0) move magazine to mono position magpos_to_put = self.positions[self.monos.index(old)] seq.append(SeqMethod(self, 'CheckMagazinSlotEmpty', magpos_to_put)) seq.append(SeqDev(self._attached_magazine, magpos_to_put)) # 1) move away the old mono, if any if old != 'None': seq.extend(self._gen_table2lift()) seq.extend( self._gen_lift2mag(self.positions[self.monos.index(old)])) # 2) fetch the new mono (if any) from the magazin if whereto != 'None': seq.extend( self._gen_mag2lift(self.positions[self.monos.index(whereto)])) seq.extend(self._gen_lift2table()) seq.append( SeqDev(self._attached_magazine, self.shields[self.monos.index(whereto)])) # seq.append(SeqDev(self._attached_enable, 0)) - will be done in FinishChange seq.append(SeqMethod(self, 'FinishChange')) self._start(seq) self.wait() @usermethod def printstatusinfo(self): self.log.info( 'PLC is %s', 'enabled' if self._attached_enable.read() == 0xef16 else 'disabled, you need to set enable_word to the ' 'correct value') self.log.info('Inhibit_relay is %s', self._attached_inhibitrelay.read()) liftposnames = { '0': 'unknown, error in switches', '1': 'Monotable loading', '2': 'Park position', '3': 'Bottom storage', '4': 'Upper storage' } self.log.info('lift is at %s', liftposnames[self._attached_lift.read()]) try: magpos = self._attached_magazine.read() self.log.info('magazine is at %r which is assigned to %s', magpos, self.monos[self.positions.index(magpos)]) except Exception: self.log.error('magazine is at an unknown position !!!') for n in 'liftclamp magazineclamp tableclamp'.split(): self.log.info('%s is %s', n, self._adevs[n].read()) occ = self._attached_magazineocc.read(0) for i in range(4): self.log.info( 'magazine slot %r is %sempty and its readouts are %sbroken', self.positions[i], '' if (occ >> (i * 2)) & 1 else 'not ', '' if (occ >> (i * 2 + 1)) & 1 else 'not ') if self._attached_magazinestatus.read() == 'free': self.log.info('magazine is currently free to load monochromator') else: self.log.info('magazine is currently occupied with monochromator ' 'and cannot load another') def doRead(self, maxage=0): return ''
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)
class IDS3010Control(Moveable): """Control interferometer measurement and alignment options. """ _modes = { "system idle": status.OK, "measurement starting": status.BUSY, "measurement running": status.OK, "optics alignment starting": status.BUSY, "optics alignment running": status.OK, "pilot laser enabled": status.OK, } parameters = { 'pilot': Param('Pilot laser for alignment', type=oneof('on', 'off'), settable=True, category='general', chatty=True, ), 'align': Param('Measure in alignment mode', type=oneof('on', 'off'), settable=True, category='general', chatty=True, ), 'contrast1': Param('Measure in alignment mode', type=float, settable=False, category='general', chatty=True, unit='%'), 'contrast2': Param('Measure in alignment mode', type=float, settable=False, category='general', chatty=True, unit='%'), 'contrast3': Param('Measure in alignment mode', type=float, settable=False, category='general', chatty=True, unit='%'), } parameter_overrides = { 'unit': Override(default='', mandatory=False, settable=False, userparam=False), 'target': Override(userparam=False) } attached_devices = { 'server': Attach('Server for communication', IDS3010Server) } valuetype = oneof('on', 'off') def doStatus(self, maxage=0): mode = self._attached_server.communicate('system.getCurrentMode')[0] return self._modes[mode], mode def doRead(self, maxage=0): return 'on' if self.status()[1] == "measurement running" else 'off' def doStart(self, value): self._attached_server.communicate('system.startMeasurement' if value == 'on' else 'system.stopMeasurement') def doReadPilot(self): enabled = self._attached_server.communicate('pilotlaser.isEnabled')[0] return 'on' if enabled else 'off' def doWritePilot(self, value): self._attached_server.communicate('pilotlaser.enable' if value == 'on' else 'pilotlaser.disable') def doReadAlign(self): enabled = self._attached_server.communicate( 'system.getCurrentMode')[0].startswith('optics alignment') return 'on' if enabled else 'off' def doWriteAlign(self, value): if (value == 'on' and self._attached_server.communicate( 'system.getCurrentMode')[0] == "system idle"): self._attached_server.communicate('system.startOpticsAlignment') else: self._attached_server.communicate('system.stopOpticsAlignment') def doReadContrast1(self): return self._attached_server.communicate( 'adjustment.getContrastInPermille', 0)[1] * 0.1 def doReadContrast2(self): return self._attached_server.communicate( 'adjustment.getContrastInPermille', 1)[1] * 0.1 def doReadContrast3(self): return self._attached_server.communicate( 'adjustment.getContrastInPermille', 2)[1] * 0.1 def doReset(self): self._attached_server.com.attocube.system.rebootSystem() def doPoll(self, n, maxage=0): # poll contrast values when in alignment mode if self.align=='on': self._pollParam('contrast1') self._pollParam('contrast2') self._pollParam('contrast3')
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
class DoubleMotorBeckhoff(PseudoNOK, BeckhoffMotorBase): """a double motor Beckhoff knows both axis at once! It comunicats direktly by modbuss """ hardware_access = True parameters = { 'addresses': Param('addresses of each motor', type=tupleof(motoraddress, motoraddress), mandatory=True, settable=False, userparam=False), 'mode': Param('Beam mode', type=oneof(*MODES), settable=True, userparam=True, default='ng', category='experiment'), 'motortemp': Param('Motor temperature', type=tupleof(float, float), settable=False, userparam=True, volatile=True, unit='degC', fmtstr='%.1f, %.1f'), '_rawvalues': Param('Raw positions', type=tupleof(float, float), internal=True, category='experiment'), } parameter_overrides = { 'ruler': Override(type=tupleof(float, float), mandatory=True, settable=False, userparam=False), } valuetype = tupleof(float, float) def doReference(self): """Reference the NOK. Just set the do_reference bit and check for completion """ self.log.error('nope') def doReadMaxvalue(self): return 1111 def doReadMinvalue(self): return -1111 def doReadMotortemp(self): # in degC self.log.debug('DoubleMotorBeckhoff doReadMotortemp') return self._HW_readParameter('motorTemp') def doReadFirmware(self): # TODO self._HW_readParameter('firmwareVersion')) return 'V%.1f' % (0.1 * 0) def doReadVmax(self): # TODO self._speed2phys(self._HW_readParameter('vMax')) return 0 def doReadRefpos(self): # TODO self._steps2phys(self._HW_readParameter('refPos')) return 0 def doReadPotentiometer(self): return [self._fit(a) for a in self._HW_readParameter('potentiometer')][0] def _writeUpperControlWord(self, value): self.log.debug('_writeUpperControlWord 0x%04x', value) value = int(value) & 0xffff self._dev.WriteOutputWord((self.addresses[0] + 1, value)) self._dev.WriteOutputWord((self.addresses[1] + 1, value)) def _readReturn(self): value = [] for i in range(2): valuel = self._dev.ReadOutputWords((self.addresses[i] + 8, 2)) self.log.debug('_readReturn %d: -> %d (0x%08x)', i, valuel, valuel) value.append(struct.unpack('=i', struct.pack('<2H', *valuel))[0]) return value def _HW_readParameter(self, index): if index not in self.HW_readable_Params: raise UsageError('Reading not possible for parameter index %s' % index) index = self.HW_readable_Params.get(index, index) self.log.debug('readParameter %d', index) for i in range(10): self._writeUpperControlWord((index << 8) | 4) session.delay(0.15) stat = self._areadStatusWord(self.addresses[0]) self.log.debug('readStatusWord 0 %d', stat) stat = self._areadStatusWord(self.addresses[0]) self.log.debug('readStatusWord 1 %d', stat) if (stat & 0x8000) != 0: if i > 0: self.log.debug('readParameter %d', i + 1) return self._readReturn() if (stat & 0x4000) != 0: raise UsageError( self, 'NACK ReadPar command not recognized ' 'by HW, please retry later...') raise UsageError( self, 'ReadPar command not recognized by HW, please ' 'retry later ...') def _areadPosition(self, address): value = self._dev.ReadOutputWords((address + 6, 2)) value = struct.unpack('=i', struct.pack('<2H', *value))[0] self.log.debug('_readPosition: -> %d steps', value) return value def _awriteDestination(self, value, address): self.log.debug('_writeDestination %r', value) value = struct.unpack('<2H', struct.pack('=i', value)) self._dev.WriteOutputWords(tuple([address + 2]) + value) def _awriteControlBit(self, bit, value, numbits, address): self.log.debug('_writeControlBit %r, %r', bit, value) self._dev.WriteOutputWord( (address, (value & ((1 << int(numbits)) - 1)) << int(bit))) session.delay(0.1) # work around race conditions.... def _asteps2phys(self, steps, ruler): value = steps / float(self.slope) - ruler self.log.debug('_steps2phys ruler: %r steps -> %s', steps, self.format(value, unit=True)) return value def _aphys2steps(self, value, ruler): steps = int((value + ruler) * float(self.slope)) self.log.debug('_phys2steps ruler: %s -> %r steps', self.format(value, unit=True), steps) return steps def _indexReadPosition(self, i): return self._asteps2phys(self._areadPosition(self.addresses[i]), self.ruler[i]) def _areadStatusWord(self, address): value = self._dev.ReadOutputWords((address + 4, 1))[0] self.log.debug('_areadStatusWord 0x%04x', value) return value def _areadErrorWord(self, address): value = self._dev.ReadOutputWords((address + 5, 1))[0] self.log.debug('_readErrorWord 0x%04x', value) return value def _indexHW_status(self, i): """Used status bits.""" # read HW values errval = self._areadErrorWord(self.addresses[i]) statval = (self._areadStatusWord(self.addresses[i]) ^ self.HW_Status_Inv) & ~self.HW_Status_Ign msg = bitDescription(statval, *self.HW_Statusbits) self.log.debug('HW_status: (%x) %s', statval, msg) if errval: errnum = errval return status.ERROR, 'ERROR %d: %s, %s' % ( errnum, self.HW_Errors.get(errnum, 'Unknown Error {0:d}'.format(errnum)), msg) for mask, stat in self.HW_Status_map: if statval & mask: return stat, msg return status.OK, msg if msg else 'Ready' def _HW_wait_while_HOT(self): sd = 6.5 anz = int(round(self.waittime * 60 / sd)) # Pech bei 2.session for a in range(anz): temp = max(self.motortemp) if temp < self.maxtemp: # wait if temp>33 until temp<26 if a: self.log.info('%d Celsius continue', temp) else: self.log.debug('%d Celsius continue', temp) return True self.log.info('%d Celsius Timeout in: %.1f min', temp, (anz - a) * sd / 60) session.delay(sd) raise NicosTimeoutError('HW still HOT after {0:d} min'.format( self.waittime)) def doRead(self, maxage=0): self.log.debug('DoubleMotorBeckhoff read') res = [self._indexReadPosition(0), self._indexReadPosition(1)] self.log.debug('%s', res) self._setROParam('_rawvalues', res) return res def doStatus(self, maxage=0): self.log.debug('DoubleMotorBeckhoff status') lowlevel = BaseSequencer.doStatus(self, maxage) if lowlevel[0] == status.BUSY: return lowlevel akt = [self._indexHW_status(0), self._indexHW_status(1)] self.log.debug('Status: %s', akt) msg = [st[1] for st in akt] self.log.debug('Status: %s', msg) return (max([st[0] for st in akt]), ', '.join(msg) if msg.count(msg[0]) == 1 else msg[0]) def _generateSequence(self, target, indexes, code): self.log.debug('DoubleMotorBeckhoff Seq generated %s %s 0x%X', target, indexes, code) seq = [SeqMethod(self, '_HW_wait_while_HOT')] for i in indexes: seq.append( SeqMethod(self, '_awriteDestination', self._aphys2steps(target[i], self.ruler[i]), self.addresses[i])) seq.append( SeqMethod(self, '_awriteControlBit', 0, code, 10, self.addresses[i])) return seq def doStart(self, target): self.log.debug('DoubleMotorBeckhoff move to %s', target) if self._seq_is_running(): raise MoveError(self, 'Cannot start device, it is still moving!') self._startSequence(self._generateSequence(target, [0, 1], 0x304))
class BeamstopChanger(BeamstopSequencer): """ A class for changing the beamstop. At SANS this is realised through a sequence of motor movements. """ attached_devices = { 'io': Attach('SPS I/O for reading slot', Readable), } valuetype = oneof(1, 2, 3, 4) _beamstop_pos = { 1: 28.0, 2: -100.5, 3: -220.5, 4: -340.5, } def doInit(self, mode): # Stopping this procedure can mess up things badly self._honor_stop = False def _emergencyFix(self, reason): session.log.error('%s, Fixing motors, Get a manager to fix this', reason) self._fix() def doRead(self, maxage): dio = self._attached_io.read(maxage) test = dio[1] val = 1 count = 0 for i in range(0, 3): if test & 1 << i: val = i + 2 count += 1 if count > 1: self._emergencyFix('Beamstop lost!!!') return val def doIsAllowed(self, pos): if self._attached_y.isAtTarget(-543): return False, 'Cannot change beamstop in OUT position' return True, '' def _testArrival(self, xpos, ypos): if not (self._attached_x.isAtTarget(xpos) and self._attached_y.isAtTarget(ypos)): self._emergencyFix('Beamstop driving failed!!!') def _generateSequence(self, target): seq = [] pos = self.read(0) self._save_pos() xpos = self._beamstop_pos[pos] seq.append((SeqDev(self._attached_y, -450), SeqDev(self._attached_x, xpos))) seq.append(SeqMethod(self, '_testArrival', xpos, -450.)) seq.append(SeqLimDev(self._attached_y, -549, -549)) seq.append(SeqMethod(self, '_testArrival', xpos, -549.)) xpos = self._beamstop_pos[int(target)] seq.append(SeqDev(self._attached_x, xpos)) seq.append(SeqMethod(self, '_testArrival', xpos, -549.)) seq.append(SeqLimDev(self._attached_y, -450, -450)) seq.append(SeqMethod(self, '_testArrival', xpos, -450)) seq.append((SeqDev(self._attached_y, self._in_y), SeqDev(self._attached_x, self._in_x))) return seq def _runFailed(self, step, action, exc_info): self._emergencyFix('Beamstop motor failed to run!!!') BeamstopSequencer._runFailed(self, step, action, exc_info) def doStatus(self, maxage=0): stat = BaseSequencer.doStatus(self, maxage) if stat[0] != status.BUSY and self._seq_is_running(): return status.BUSY, stat[1] return stat
class ImageChannel(QMesyDAQImage, BaseImageChannel): parameters = { 'readout': Param('Readout mode of the Detector', settable=True, type=oneof('raw', 'mapped', 'amplitude'), default='mapped', mandatory=False, chatty=True), 'flipaxes': Param('Flip data along these axes after reading from det', type=listof(int), default=[], unit=''), 'transpose': Param('Whether to transpose the image', type=bool, default=False), } # Use the configuration from QMesyDAQ parameter_overrides = { 'listmode': Override(volatile=True), 'histogram': Override(volatile=True), } def doWriteListmode(self, value): self._dev.SetProperties(['writelistmode', ('%s' % value).lower()]) return self.doReadListmode() def doReadListmode(self): return { 'false': False, 'true': True }[self._getProperty('writelistmode')] def doWriteHistogram(self, value): self._dev.SetProperties(['writehistogram', ('%s' % value).lower()]) return self.doReadHistogram() def doReadHistogram(self): return { 'false': False, 'true': True }[self._getProperty('writehistogram')] def doWriteReadout(self, value): self._dev.SetProperties(['histogram', value]) return self._getProperty('histogram') def doWriteListmodefile(self, value): self._dev.SetProperties(['lastlistfile', value]) return self._getProperty('lastlistfile') # def doReadListmodefile(self): # return self._getProperty('lastlistfile') def doWriteHistogramfile(self, value): self._dev.SetProperties(['lasthistfile', value]) return self._getProperty('lasthistfile') # def doReadHistogramfile(self): # return self._getProperty('lasthistfile') def doReadConfigfile(self): return self._getProperty('configfile') def doReadCalibrationfile(self): return self._getProperty('calibrationfile') def doReadArray(self, quality): narray = BaseImageChannel.doReadArray(self, quality) if self.transpose: narray = np.transpose(narray) for axis in self.flipaxes: narray = np.flip(narray, axis) return narray def doFinish(self): self.doStatus(0) return BaseImageChannel.doFinish(self) def doStop(self): self.doStatus(0) return BaseImageChannel.doStop(self)
class ChopperMaster(CanReference, BaseSequencer): valuetype = dictwith( wlmin=float, wlmax=float, gap=float, chopper2_pos=intrange(1, 6), D=float, ) parameters = { 'mode': Param('Chopper operation mode (normal, virtual6)', type=oneof('normal_mode', 'virtual_disc2_pos_6'), settable=False, category='status'), 'delay': Param('delay for Startsignal in Degree', type=floatrange(-360, 360), settable=True, userparam=True), 'wlmin': Param('Wavelength min', type=floatrange(0, 30), settable=True, userparam=True, unit='AA', category='status'), 'wlmax': Param('Wavelength max', type=floatrange(0, 30), settable=True, userparam=True, unit='AA', category='status'), 'dist': Param('flight path (distance chopper disc 1 to detector)', type=floatrange(0), settable=True, userparam=True, unit='m', category='status'), 'gap': Param('Gap ... ', type=floatrange(0, 100), settable=True, userparam=True, unit='%', category='status'), 'resolution': Param('Resolution ...', type=intrange(1, 6), settable=False, userparam=True, mandatory=False, volatile=True, category='status'), 'speed': Param('Chopper1 speed ... ', type=float, settable=False, userparam=True, mandatory=False, volatile=True, category='status'), } _max_disks = 6 attached_devices = { 'chopper1': Attach('chopper1 defining speed', Moveable), 'chopper2': Attach('chopper2 phase', Moveable), 'chopper3': Attach('chopper3 phase also height', Moveable), 'chopper4': Attach('chopper4 phase also height', Moveable), 'chopper5': Attach('chopper5 phase half speed', Moveable), 'chopper6': Attach('chopper6 phase half speed', Moveable), 'shutter': Attach('Shutter device', Moveable), } def doInit(self, mode): self._choppers = (self._attached_chopper1, self._attached_chopper2, self._attached_chopper3, self._attached_chopper4, self._attached_chopper5, self._attached_chopper6) def _generateSequence(self, target): self.wlmin, self.wlmax = limits( (target.get('wlmin', self.wlmin), target.get('wlmax', self.wlmax))) self.dist = target.get('D', self.dist) self.gap = target.get('gap', self.gap) chopper2_pos = target.get('chopper2_pos') speed, angles = chopper_config(self.wlmin, self.wlmax, self.dist, chopper2_pos, gap=self.gap) self.log.debug('speed: %d, angles = %s', speed, angles) seq = [] shutter_pos = self._attached_shutter.read(0) shutter_ok = self._attached_shutter.status(0)[0] == status.OK if chopper2_pos == 6: self._setROParam('mode', 'virtual_disc2_pos_6') else: self._setROParam('mode', 'normal_mode') chopper2_pos_akt = self._attached_chopper2.pos if chopper2_pos_akt != chopper2_pos: if shutter_ok: seq.append( SeqDev(self._attached_shutter, 'closed', stoppable=True)) seq.append(SeqDev(self._attached_chopper1, 0, stoppable=True)) seq.append( SeqSlowParam(self._attached_chopper2, 'pos', chopper2_pos)) for dev, t in zip(self._choppers[1:], angles[1:]): # The Chopper measures the phase in the opposite direction # as we do this was catered for here, we have moved the # sign conversion to the doWritePhase function # dev.phase = -t # sign by history seq.append(SeqFuzzyParam(dev, 'phase', t, 0.5)) seq.append(SeqDev(self._attached_chopper1, speed, stoppable=True)) if shutter_ok: seq.append( SeqDev(self._attached_shutter, shutter_pos, stoppable=True)) return seq def doRead(self, maxage=0): # TODO: for cfg value = { 'D': self.dist, 'wlmin': self.wlmin, 'wlmax': self.wlmax, 'gap': self.gap, 'chopper2_pos': self._attached_chopper2.pos if self.mode == 'normal_mode' else 6, } return value def _getWaiters(self): return self._choppers def doReadResolution(self): if self.mode == 'normal_mode': return self._attached_chopper2.pos else: return 6 def doReadSpeed(self): return self._attached_chopper1.read(0)
class HVSwitch(SequencerMixin, MappedMoveable): """High voltage convenience switching device for the CHARM detector.""" valuetype = oneof('on', 'off', 'safe') attached_devices = { 'anodes': Attach('HV channels for the anodes', Moveable, multiple=[2, 9]), 'banodes': Attach('HV channels for the boundary anodes', Moveable, multiple=[1, 8]), 'cathodes': Attach('HV channels for the boundary cathodes', Moveable, multiple=2), 'window': Attach('HV channel for the window', Moveable, multiple=1), 'trip': Attach('Devices signaling a trip on the hardware', Readable), } parameters = { '_tripped': Param('Indicator for hardware trip', type=bool, internal=True, default=False), } parameter_overrides = { 'unit': Override(default='', mandatory=False), 'fallback': Override(default='unknown'), 'mapping': Override(type=dictof(str, dictof(str, float))), } def doInit(self, mode): if self.fallback in self.mapping: raise ConfigurationError( self, 'Value of fallback parameter is ' 'not allowed to be in the mapping!') self._devices = { dev.name: dev for dev in (self._attached_anodes + self._attached_banodes + self._attached_cathodes + self._attached_window) } if len(self._attached_anodes) != len(self._attached_banodes) + 1: raise ConfigurationError( self, 'Number of anode devices must be ' 'the number of boundary anodes + 1: %d, ' '%d' % (len(self.anodes), len(self.banodes))) # if not self.relax_mapping: # self.valuetype = oneof(*sorted(self.mapping, key=num_sort)) for value in sorted(self.mapping, key=num_sort): try: self.valuetype(value) except ValueError as err: raise ConfigurationError( self, '%r not allowed as key in mapping. %s' % (value, err)) from err def doIsAllowed(self, target): if target == 'off': ok = True else: ok = not self._tripped and not self._attached_trip.read(0) return ok, '' if ok else 'hardware is tripped' def doStop(self): if not self._tripped: SequencerMixin.doStop(self) else: raise ModeError(self, "can't be stopped, device is tripped.") def doReset(self): if self._tripped: if self.doStatus(0)[0] == status.BUSY or self._hardware_tripped(): raise ModeError(self, "can't reset device. Hardware is tripped") SequencerMixin.doReset(self) self._setROParam('_tripped', False) def doPoll(self, n, maxage): if not self._tripped and session.sessiontype == POLLER and \ self._hardware_tripped(): self._setROParam('_tripped', True) def _hardware_tripped(self): return self._attached_trip.read(0) == 'Trip' def _generateSequence(self, target): anodes = self._attached_anodes + self._attached_banodes seq = [ [ SeqDev(dev, self.mapping[target][dev.name]) for dev in self._attached_window ], [SeqDev(dev, self.mapping[target][dev.name]) for dev in anodes], [ SeqDev(dev, self.mapping[target][dev.name]) for dev in self._attached_cathodes ], ] return seq def _is_at_target(self, pos, target): # if values are exact the same if pos == target: return True for dev in pos: if not self._devices[dev].isAtTarget(pos[dev], target[dev]): return False return True def _mapReadValue(self, value): for val in self.mapping: if self._is_at_target(value, self.mapping[val]): return val if self.fallback is not None: return self.fallback else: raise PositionError(self, 'unknown unmapped position %r' % value) def _readRaw(self, maxage=0): return {dev.name: dev.read(maxage) for dev in self._devices.values()} def _startRaw(self, target): ramp = 60 * self.mapping[self.target]['ramp'] seq = self._generateSequence(self.target) if self.target in ['off', 'safe']: seq.reverse() self._startSequence([ SeqParam(dev, 'ramp', ramp) for dev in self._attached_anodes + self._attached_banodes + self._attached_cathodes + self._attached_window ] + seq)
class DoubleMotorBeckhoffNOK(DoubleMotorBeckhoff): """NOK using two axes. """ 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), 'address': Override(mandatory=False), } valuetype = tupleof(float, float) _honor_stop = True def doInit(self, mode): for name, idx in [('reactor', 0), ('sample', 1)]: self.__dict__[name] = SingleMotorOfADoubleMotorNOK( '%s.%s' % (self.name, name), unit=self.unit, both=self, lowlevel=True, index=idx) def doWriteMode(self, mode): self.log.debug('DoubleMotorBeckhoffNOK arg:%s self:%s', mode, self.mode) target = self.doRead(0) self.log.debug('DoubleMotorBeckhoffNOK target %s', target) target = [pos + self.masks[mode] for pos in target] self.log.debug('DoubleMotorBeckhoffNOK target %s', target) DoubleMotorBeckhoff.doStart(self, target) def doRead(self, maxage=0): self.log.debug('DoubleMotorBeckhoffNOK read') return [ pos - self.masks[self.mode] for pos in DoubleMotorBeckhoff.doRead(self, maxage) ] def doIsAllowed(self, target): self.log.debug('DoubleMotorBeckhoffNOK doIsAllowed') target_r, target_s = (target[0] + self.offsets[0], target[1] + 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) # no problems detected, so it should be safe to go there.... return True, '' def doIsAtTarget(self, pos, target): self.log.debug('DoubleMotorBeckhoffNOK doIsAtTarget') stat = self.status(0) if stat[0] == status.BUSY: return False return True def doStart(self, target): self.log.debug('DoubleMotorBeckhoffNOK doStart') self.log.debug('target %s %s', target, type(target)) target = [pos + self.masks[self.mode] for pos in target] self.log.debug('target %s %s', target, type(target)) DoubleMotorBeckhoff.doStart(self, target)
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)
class ImagePlateImage(ImageChannelMixin, PassiveChannel): """Represents the client to a MAATEL Image Plate Detector.""" MAP_SHAPE = { 125: (900, 10000), 250: (900, 5000), 500: (900, 2500), } attached_devices = { "imgdrum": Attach("Image Plate Detector Drum " "control device.", ImagePlateDrum), } parameters = { "erase": Param("Erase image plate on next start?", type=bool, settable=True, mandatory=False, default=True), "roi": Param("Region of interest", type=tupleof(int, int, int, int), default=(0, 0, 0, 0), settable=True, volatile=True, category="general"), "pixelsize": Param("Pixel size in microns", type=oneof(125, 250, 500), default=500, settable=True, volatile=True, category="general"), "file": Param("Image file location on maatel computer", type=str, settable=True, volatile=True, category="general"), "readout_millis": Param("Timeout in ms for the readout", type=int, settable=True, default=60000), } def doInit(self, mode): self.arraydesc = ArrayDesc('data', self.MAP_SHAPE[self.pixelsize], numpy.uint16) def doPrepare(self): # erase and expo position if self.erase: self._attached_imgdrum.maw(ImagePlateDrum.POS_ERASE) self._attached_imgdrum.maw(ImagePlateDrum.POS_EXPO) def valueInfo(self): # no readresult -> no values return () def doReadArray(self, quality): if quality == FINAL: # start readout self._attached_imgdrum.maw(ImagePlateDrum.POS_READ) narray = None timeout = self._attached_imgdrum._dev.get_timeout_millis() self._attached_imgdrum._dev.set_timeout_millis(self.readout_millis) try: narray = self._attached_imgdrum._dev.Bitmap16Bit finally: self._attached_imgdrum._dev.set_timeout_millis(timeout) return narray return None def doReadRoi(self): return (0, self._attached_imgdrum._dev.InterestZoneY, 1250, self._attached_imgdrum._dev.InterestZoneH) def doReadPixelsize(self): return self._attached_imgdrum._dev.PixelSize def doReadFile(self): return self._attached_imgdrum._dev.ImageFile def doWriteRoi(self, value): self.log.warning("setting x offset and width are not supported " "- ignored.") self._attached_imgdrum._dev.InterestZoneY = value[1] self._attached_imgdrum._dev.InterestZoneH = value[3] def doWritePixelsize(self, value): self._attached_imgdrum._dev.PixelSize = value self.arraydesc = ArrayDesc('data', self.MAP_SHAPE[value], numpy.uint16) def doWriteFile(self, value): self._attached_imgdrum._dev.ImageFile = value
class CascadeDetector(VirtualImage): _perfoil = 16 parameters = { 'mode': Param('Data acquisition mode (tof or image)', type=oneof('tof', 'image'), settable=True, default='image', category='presets'), 'roi': Param('Region of interest, given as (x1, y1, x2, y2)', type=tupleof(int, int, int, int), default=(-1, -1, -1, -1), settable=True), 'tofchannels': Param('Total number of TOF channels to use', type=intrange(1, 1024), default=128, settable=True, category='presets'), 'foilsorder': Param('Usable foils, ordered by number.', type=listof(intrange(0, 31)), settable=False, default=[0, 1, 2, 3, 4, 5, 6, 7], category='instrument'), 'fitfoil': Param('Foil for contrast fitting (number BEFORE ' 'resorting)', type=int, default=0, settable=True), } def doInit(self, mode): self._buf = self._generate(0).astype('<u4') def doUpdateMode(self, value): self._dataprefix = (value == 'image') and 'IMAG' or 'DATA' self._datashape = (value == 'image') and self.sizes or \ (self._perfoil * len(self.foilsorder), ) + self.sizes # (self.tofchannels,) self._tres = (value == 'image') and 1 or self.tofchannels def doStart(self): self.readresult = [0, 0] VirtualImage.doStart(self) def valueInfo(self): if self.mode == 'tof': return (Value(self.name + '.roi', unit='cts', type='counter', errors='sqrt', fmtstr='%d'), Value(self.name + '.total', unit='cts', type='counter', errors='sqrt', fmtstr='%d'), Value('fit.contrast', unit='', type='other', errors='next', fmtstr='%.3f'), Value('fit.contrastErr', unit='', type='error', errors='none', fmtstr='%.3f'), Value('fit.avg', unit='', type='other', errors='next', fmtstr='%.1f'), Value('fit.avgErr', unit='', type='error', errors='none', fmtstr='%.1f'), Value('roi.contrast', unit='', type='other', errors='next', fmtstr='%.3f'), Value('roi.contrastErr', unit='', type='error', errors='none', fmtstr='%.3f'), Value('roi.avg', unit='', type='other', errors='next', fmtstr='%.1f'), Value('roi.avgErr', unit='', type='error', errors='none', fmtstr='%.1f')) return (Value(self.name + '.roi', unit='cts', type='counter', errors='sqrt', fmtstr='%d'), Value(self.name + '.total', unit='cts', type='counter', errors='sqrt', fmtstr='%d')) @property def arraydesc(self): if self.mode == 'image': return ArrayDesc('data', self._datashape, '<u4', ['X', 'Y']) return ArrayDesc('data', self._datashape, '<u4', ['T', 'X', 'Y']) def doReadArray(self, quality): # get current data array from detector, reshape properly data = VirtualImage.doReadArray(self, quality) # determine total and roi counts total = data.sum() if self.roi != (-1, -1, -1, -1): x1, y1, x2, y2 = self.roi roi = data[..., y1:y2, x1:x2].sum() else: x1, y1, x2, y2 = 0, 0, data.shape[-1], data.shape[-2] roi = total if self.mode == 'image': self.readresult = [roi, total] return data data = np.array([data] * self._datashape[0]) # demux timing into foil + timing nperfoil = self._datashape[0] // len(self.foilsorder) shaped = data.reshape((len(self.foilsorder), nperfoil) + self._datashape[1:]) # nperfoil = self.tofchannels // self.foils # shaped = data.reshape((self.foils, nperfoil) + self._datashape[1:]) x = np.arange(nperfoil) ty = shaped[self.fitfoil].sum((1, 2)) ry = shaped[self.fitfoil, :, y1:y2, x1:x2].sum((1, 2)) self.log.debug('fitting %r and %r' % (ty, ry)) self.readresult = [ roi, total, ] # also fit per foil data and pack everything together to be send # via cache for display payload = [] for foil in self.foilsorder: foil_tot = shaped[foil].sum((1, 2)) foil_roi = shaped[foil, :, y1:y2, x1:x2].sum((1, 2)) tpopt, tperr, _ = fit_a_sin_fixed_freq(x, foil_tot) rpopt, rperr, _ = fit_a_sin_fixed_freq(x, foil_roi) payload.append([ tpopt, tperr, foil_tot.tolist(), rpopt, rperr, foil_roi.tolist() ]) self._cache.put(self.name, '_foildata', payload, flag=FLAG_NO_STORE) return data
class ImagePlateDrum(PyTangoDevice, Moveable): """ImagePlateDrum implements erasing, moving to expo position and readout for MAATEL Image Plate Detectors. """ DEFAULT_URL_FMT = "tango://%s/EMBL/Microdiff/General#dbase=no" tango_status_mapping = dict(PyTangoDevice.tango_status_mapping) tango_status_mapping[DevState.STANDBY] = status.OK tango_status_mapping[DevState.ALARM] = status.ERROR POS_ERASE = "erase" POS_EXPO = "expo" POS_READ = "read" valuetype = oneof(POS_ERASE, POS_EXPO, POS_READ) parameters = { "drumpos": Param("Drum position in degree", type=float, settable=True, volatile=True, category="general"), "readheadpos": Param("Read head motor position in mm", type=float, settable=True, volatile=True, category="general"), "drumexpo": Param("Drum expo position in degree", type=float, settable=True, volatile=True, category="general"), "readspeed": Param("Readout velocity for the detector drum " "in rpm", type=float, settable=True, volatile=True, category="general"), "erasespeed": Param("Erase velocity for the detector drum " "in rpm", type=float, settable=True, volatile=True, category="general"), "freqlaser": Param("Frequency for the laser diode in Hz", type=float, settable=True, volatile=True, category="general"), "timeerase": Param("Erasure time in seconds", type=float, settable=True, volatile=True, category="general"), } parameter_overrides = { "unit": Override(default="", mandatory=False, settable=False) } def doInit(self, mode): self._lastStatus = None self._moveTo = None if mode == SIMULATION: self._mapStart = {} self._mapStop = {} return self._mapStart = { ImagePlateDrum.POS_ERASE: self._dev.StartErasureProcess, ImagePlateDrum.POS_EXPO: self._dev.MoveExpoPosition, ImagePlateDrum.POS_READ: self._dev.StartReadProcess, } self._mapStop = { ImagePlateDrum.POS_ERASE: self._dev.AbortErasureProcess, ImagePlateDrum.POS_EXPO: self._dev.AbortExposureProcess, ImagePlateDrum.POS_READ: self._dev.AbortReadProcess, } def doStart(self, pos): self.log.debug("doStart: pos: %s", pos) myStatus = self.status(0) if myStatus[0] == status.OK: self._moveTo = pos self._mapStart[pos]() else: raise MoveError(self, "Movement not allowed during device status " "'%s'" % (status.statuses[myStatus[0]])) def doStop(self): self.log.debug("doStop") if self._moveTo in self._mapStop: self._mapStop[self._moveTo]() else: myStatus = self.status(0) if myStatus[0] == status.OK: self.log.warning("Device already stopped.") else: raise NicosError(self, "Internal moveTo state unknown. " "Check device status.") def doRead(self, maxage=0): return self.target def doStatus(self, maxage=0): # Workaround for status changes from busy to another state although the # operation has _not_ been completed. st, msg = PyTangoDevice.doStatus(self, maxage) if self._lastStatus == status.BUSY and st != status.BUSY: self.log.debug("doStatus: leaving busy state (%d)? %d. " "Check again after a short delay.", status.BUSY, st) session.delay(5) st, msg = PyTangoDevice.doStatus(self, 0) self.log.debug("doStatus: recheck result: %d", st) self._lastStatus = st return st, msg def doFinish(self): self._moveTo = None def doReadDrumpos(self): return self._dev.DrumPosition def doReadReadheadpos(self): return self._dev.ReadHeadPosition def doReadDrumexpo(self): return self._dev.DrumExpoPosition def doReadReadspeed(self): return self._dev.ReadingDrumJogSpeed def doReadErasespeed(self): return self._dev.ErasureDrumJogSpeed def doReadFreqlaser(self): return self._dev.LaserDiodeLevel def doReadTimeerase(self): return self._dev.ErasureDuration def doWriteDrumpos(self, value): self._dev.DrumPosition = value def doWriteReadheadpos(self, value): self._dev.ReadHeadPosition = value def doWriteDrumexpo(self, value): self._dev.DrumExpoPosition = value def doWriteReadspeed(self, value): self._dev.ReadingDrumJogSpeed = value def doWriteErasespeed(self, value): self._dev.ErasureDrumJogSpeed = value def doWriteFreqlaser(self, value): self._dev.LaserDiodeLevel = value def doWriteTimeerase(self, value): self._dev.ErasureDuration = value
class SingleMotorOfADoubleMotorNOK(AutoDevice, Moveable): """ empty class marking a Motor as beeing useable by a (DoubleMotorNok) """ hardware_access = True attached_devices = { 'both': Attach('access to both of them via mode', Moveable), } parameters = { 'index': Param('right side of nok', type=oneof(0, 1), settable=False, mandatory=True), } def doRead(self, maxage=0): self.log.debug('SingleMotorOfADoubleMotorNOK %d', self.index) quick = self._attached_both return quick.read(maxage)[self.index] def doStatus(self, maxage=0): return self._attached_both.status(maxage) def doStart(self, target): self.log.debug('SingleMotorOfADoubleMotorNOK for %s to %s', self.name, target) if self._seq_is_running(): raise MoveError(self, 'Cannot start device, it is still moving!') incmin, incmax = self._attached_both.inclinationlimits both = self._attached_both port = self._attached_both._attached_device if self.index == 1: code = 0x204 inclination = both.read(0)[0] else: code = 0x104 inclination = both.read(0)[1] self.log.debug('code 0x%03X d%d', code, code) inclination = target - inclination if incmin > inclination: brother = target - incmin / 2 elif incmax < inclination: brother = target - incmax / 2 else: self.log.debug('legal driving range') port._startSequence( port._generateSequence([target + both.masks[both.mode]], [self.index], code)) return self.log.debug('brother move %.2f', brother) if self.index == 1: both.move([brother, target]) else: both.move([target, brother])