migrated secop_psi drivers to new syntax

- includes all changes up to 'fix inheritance order' from git_mlz
  6a32ecf342

Change-Id: Ie3ceee3dbd0a9284b47b1d5b5dbe262eebe8f283
This commit is contained in:
2021-02-24 16:15:23 +01:00
parent bc5edec06f
commit 41baf5805f
79 changed files with 2610 additions and 3952 deletions

View File

@ -20,65 +20,53 @@
# *****************************************************************************
"""WAVE FUNCTION LECROY XX: SIGNAL GENERATOR"""
from secop.core import Readable, Parameter, Override, Command, FloatRange, TupleOf, \
HasIodev, StringIO, Done, Attached, IntRange, BoolType, EnumType, StringType, Module, \
Property
from secop.core import Readable, Parameter, FloatRange, \
HasIodev, IntRange, BoolType, EnumType, Module, Property
class Channel(Module):
properties = {
'channel':Property('choose channel to manipulate',IntRange(1,2)),
}
parameters = {
'freq':
Parameter('frequency', FloatRange(1e-6,20e6,unit='Hz'),
poll=True, initwrite=True, default=1000),
'amp':
Parameter('exc_volt_int', FloatRange(0.00,5,unit='Vrms'),
poll=True, readonly=False, initwrite=True, default=0.1),
'offset':
Parameter('offset_volt_int', FloatRange(0.00,10,unit='V'),
poll = True, readonly = False, initwrite = True, default = 0.0),
'wave':
Parameter ('type of wavefunction',
EnumType('WaveFunction', SINE=1, SQUARE=2, RAMP=3, PULSE=4, NOISE=5, ARB=6, DC=7),
poll=True, readonly=False, default='SINE'),
'phase':
Parameter('signal phase', FloatRange(0,360,unit='deg'),
poll=True, readonly=False, initwrite=True, default=0),
'enabled':
Parameter('enable output channel', datatype=EnumType('OnOff', OFF=0, ON=1),
readonly=False, default='OFF'),
'symm':
Parameter('wavefunction symmetry', FloatRange(0,100, unit=''),
poll=True, readonly =False, default=0),
}
class Channel(HasIodev, Module):
channel = Property('choose channel to manipulate', IntRange(1, 2))
freq = Parameter('frequency', FloatRange(1e-6, 20e6, unit='Hz'),
poll=True, initwrite=True, default=1000)
amp = Parameter('exc_volt_int', FloatRange(0.00, 5, unit='Vrms'),
poll=True, readonly=False, initwrite=True, default=0.1)
offset = Parameter('offset_volt_int', FloatRange(0.00, 10, unit='V'),
poll=True, readonly=False, initwrite=True, default=0.0)
wave = Parameter('type of wavefunction',
EnumType('WaveFunction', SINE=1, SQUARE=2, RAMP=3, PULSE=4, NOISE=5, ARB=6, DC=7),
poll=True, readonly=False, default='SINE'),
phase = Parameter('signal phase', FloatRange(0, 360, unit='deg'),
poll=True, readonly=False, initwrite=True, default=0)
enabled = Parameter('enable output channel', datatype=EnumType('OnOff', OFF=0, ON=1),
readonly=False, default='OFF')
symm = Parameter('wavefunction symmetry', FloatRange(0, 100, unit=''),
poll=True, readonly=False, default=0)
def read_value(self):
return self.sendRecv('C%d:BSWV FRQ?' % self.channel)
def write_target(self,value):
def write_target(self, value):
self.sendRecv('C%d:BSWV FRQ, %g' % (self.channel, str(value)+'Hz'))
return value
#signal wavefunction parameter
# signal wavefunction parameter
def read_wave(self):
return self.sendRecv('C%d:BSWV WVTP?' % self.channel)
def write_wave(self,value): #string value
def write_wave(self, value): # string value
self.sendRecv('C%d:BSWV WVTP, %s' % (self.channel, value.name))
return value
#signal amplitude parameter
# signal amplitude parameter
def read_amp(self):
return self.sendRecv('C%d:BSWV AMP?' % self.channel)
def write_amp(self,value):
def write_amp(self, value):
self.sendRecv('C%d:BSWV AMP, %g' % (self.channel, value))
return value
#offset value parameter
# offset value parameter
def read_offset(self):
return self.sendRecv('C%d:BSWV OFST?' % self.channel)
@ -86,44 +74,41 @@ class Channel(Module):
self.sendRecv('C%d:BSWV OFST %g' % (self.channel, value))
return value
# channel symmetry
# channel symmetry
def read_symm(self):
return self.sendRecv('C%d:BSWV SYM?' % self.channel)
def write_symm(self, value):
self.comm('C%d:BSWV SYM %g' % (self.channel, value))
self.sendRecv('C%d:BSWV SYM %g' % (self.channel, value))
return value
# wave phase parameter
# wave phase parameter
def read_phase(self):
return self.sendRecv('C%d:BSWV PHSE?' % self.channel)
def write_phase(self, value):
self.sendRecv('C%d:BSWV PHSE %g' % (self.channel, str(value)))
return value
# dis/enable output channel
# dis/enable output channel
def read_enabled(self):
return self.sendRecv('C%d: OUTP?' % self.channel)
def write_enabled(self, value):
self.sendRecv('C%d: OUTP %s' % (self.channel, value.name))
return value
# devices are defined as arg less output enable what is defined as arg2
# devices are defined as arg less output enable what is defined as arg2
class arg(Readable):
pollerClass = None
parameters = {
'value': Override(datatype=FloatRange(unit='')),
}
value = Parameter(datatype=FloatRange(unit=''))
class arg2(Readable):
pollerClass = None
parameters = {
'value': Override(datatype=BoolType(unit='')),
}
value = Parameter(datatype=BoolType())

View File

@ -20,262 +20,29 @@
# *****************************************************************************
"""SIGNAL RECOVERY SR7270: lOCKIN AMPLIFIER FOR AC SUSCEPTIBILITY"""
from secop.core import Readable, Parameter, Override, Command, FloatRange, TupleOf, \
HasIodev, StringIO, Done, Attached, IntRange, BoolType, EnumType
from secop.core import FloatRange, HasIodev, \
Parameter, Readable, StringIO, TupleOf
class SR7270(StringIO):
end_of_line = b'\x00'
def do_communicate(self, command): #remove dash from terminator
reply = StringIO.do_communicate(self, command)
status = self._conn.readbytes(2, 0.1) # get the 2 status bytes
# print('comm=',command,'reply=',reply,'status=',status)
return reply + ';%d;%d' % tuple(status)
# end_of_line = '\x00' #termination line from maanual page 6.8
end_of_line = '\n'
class XY(HasIodev, Readable):
value = Parameter('X, Y', datatype=TupleOf(FloatRange(unit='V'), FloatRange(unit='V')))
freq = Parameter('exc_freq_int', FloatRange(0.001,250e3,unit='Hz'), readonly=False, default=100)
class XY(HasIodev, Readable):
properties = {
'x': Attached(),
'y': Attached(),
'freq_arg': Attached(),
'amp_arg': Attached(),
'tc_arg': Attached(),
'phase_arg': Attached(),
'dac_arg': Attached(),
}#parameters required an initial value but initwrite write the default value for polled parameters
parameters = {
'value': Override('X, Y', datatype=TupleOf(FloatRange(unit='V'), FloatRange(unit='V'))),
'freq': Parameter('exc_freq_int',
FloatRange(0.001,250e3,unit='Hz'),
poll=True, readonly=False, initwrite=True, default=1000),
'amp': Parameter('exc_volt_int',
FloatRange(0.00,5,unit='Vrms'),
poll=True, readonly=False, initwrite=True, default=0.1),
'range': Parameter('sensitivity value', FloatRange(0.00,1,unit='V'), poll=True, default=1),
'irange': Parameter('sensitivity index', IntRange(0,27), poll=True, readonly=False, default=25),
'autorange': Parameter('autorange_on', EnumType('autorange', off=0, soft=1, hard=2), readonly=False, default=0, initwrite=True),
'tc': Parameter('time constant value', FloatRange(10e-6,100,unit='s'), poll=True, default=0.1),
'itc': Parameter('time constant index', IntRange(0,30), poll=True, readonly=False, initwrite=True, default=14),
'nm': Parameter ('noise mode',BoolType(), readonly=False, default=0),
'phase': Parameter('Reference phase control', FloatRange(-360,360,unit='deg'), poll=True, readonly=False, initwrite=True, default=0),
'vmode' : Parameter('Voltage input configuration', IntRange(0,3), readonly=False, default=3),
# 'dac': Parameter ('output DAC channel value', datatype=TupleOf(IntRange(1,4), FloatRange(0.00,5000,unit='mV')), poll=True, readonly=False, initwrite=True, default=(3,0)),
'dac': Parameter ('output DAC channel value', FloatRange(-10000,10000,unit='mV'), poll=True, readonly=False, initwrite=True, default=0),
}
commands = {
'aphase': Command('auto phase'),
}
iodevClass = SR7270
def comm(self, command):
reply, status, overload = self.sendRecv(command).split(';')
if overload != '0':
self.status = self.Status.WARN, 'overload %s' % overload
else:
self.status = self.Status.IDLE, ''
return reply
def read_value(self):
reply = self.comm('XY.').split(',')
x = float(reply[0])
y = float(reply[1])
if self.autorange == 1: # soft
if max(abs(x), abs(y)) >= 0.9*self.range and self.irange < 27:
self.write_irange(self.irange+1)
elif max(abs(x), abs(y)) <= 0.3*self.range and self.irange > 1:
self.write_irange(self.irange-1)
self._x.value = x # to update X,Y classes which will be the collected data.
self._y.value = y
# print(x,y)
self._freq_arg.value = self.freq
self._amp_arg.value = self.amp
self._tc_arg.value = self.tc
self._phase_arg.value = self.phase
self._dac_arg.value = self.dac
return x,y
reply = self.sendRecv('XY.').split('\x00')[-1]
return reply.split(',')
def read_freq(self):
reply = self.comm('OF.')
reply = self.sendRecv('OF.').split('\x00')[-1]
return reply
def write_freq(self,value):
self.comm('OF. %g' % value)
self.sendRecv('OF. %g' % value)
return value
def write_autorange(self, value):
if value == 2: # hard
self.comm('AS') # put hardware autorange on
self.comm('AUTOMATIC. 1')
else:
self.comm('AUTOMATIC. 0')
return value
def read_autorange(self):
reply=self.comm('AUTOMATIC')
# determine hardware autorange
if reply == 1: #"hardware auto range is on":
return 2 # hard
if self.autorange == 0: # soft
return self.autorange() #read autorange
return reply # off
#oscillator amplitude module
def read_amp(self):
reply = self.comm('OA.')
return reply
def write_amp(self,value):
self.comm('OA. %g' % value)
return value
#external output DAC
def read_dac(self):
# reply = self.comm('DAC %g' % channel) # failed to add the DAC channel you want to control
reply = self.comm('DAC 3') #stack to channel 3
return reply
def write_dac(self,value):
#self.comm('DAC %g %g' % channel % value)
self.comm('DAC 3 %g' % value)
return value
#sensitivity module
def read_range(self):
reply = self.comm('SEN.')
return reply
def write_irange(self,value):
self.comm('SEN %g' % value)
self.read_range()
return value
def read_irange(self):
reply = self.comm('SEN')
return reply
#time constant module/ noisemode off or 0 allows to use all the time constant range
def read_nm(self):
reply = self.comm('NOISEMODE')
return reply
def write_nm(self,value):
self.comm('NOISEMODE %d' % int(value))
self.read_nm()
return value
def read_tc(self):
reply = self.comm('TC.')
return reply
def write_itc(self,value):
self.comm('TC %g' % value)
self.read_tc()
return value
def read_itc(self):
reply = self.comm('TC')
return reply
#phase and autophase
def read_phase(self):
reply = self.comm('REFP.')
return reply
def write_phase(self,value):
self.comm('REFP %d' % round(1000*value,0))
self.read_phase()
return value
def do_aphase(self):
self.read_phase()
reply = self.comm('AQN')
self.read_phase()
#voltage input configuration 0:grounded,1=A,2=B,3=A-B
# def read_vmode(self):
# reply = self.comm('VMODE')
# return reply
def write_vmode(self,value):
self.comm('VMODE %d' % value)
# self.read_vmode()
return value
class Comp(Readable):
pollerClass = None
parameters = {
'value': Override(datatype=FloatRange(unit='V')),
}
class arg(Readable):
pollerClass = None
parameters = {
'value': Override(datatype=FloatRange(unit='')),
}
# parameters = {
# 'valueX': Override('X, Y', datatype=TupleOf(FloatRange(unit='V'), FloatRange(unit='V'))),
#}
#iodevClass = SR7270
# def read_valueX(self):
# reply = self.sendRecv('XY.')
# return reply.split(',')[0]
# def read_valueY(self):
# reply = self.sendRecv('XY.')
# return reply.split(',')[1]
#class aphase(self):
# reply = self.sendRecv('ASM')
# return reply
# def asens(self):
# reply = self.sendRecv('AS')
# return reply
# def write_Fstart(self,value):
# self.sendRecv('FSTART. %g' % value)
# return value
# def write_Fstop(self,value):
# self.sendRecv('FSTOP. %g' % value)
# return value
# def write_Fstep(self,value):
# self.sendRecv('FSTEP. %g' % value)
# return value
# def write_Astart(self,value):
# self.sendRecv('ASTART. %g' % value')
# return value
# def write_Astop(self,value):
# self.sendRecv('ASTOP. %g' % value)
# return value
# def write_Astep(self,value):
# self.sendRecv('ASTEP. %g' % value)
# return value

View File

@ -20,7 +20,7 @@
# *****************************************************************************
"""Andeen Hagerling capacitance bridge"""
from secop.core import Readable, Parameter, Override, FloatRange, HasIodev, StringIO, Done
from secop.core import Done, FloatRange, HasIodev, Parameter, Readable, StringIO
class Ah2700IO(StringIO):
@ -29,12 +29,12 @@ class Ah2700IO(StringIO):
class Capacitance(HasIodev, Readable):
parameters = {
'value': Override('capacitance', FloatRange(unit='pF'), poll=True),
'freq': Parameter('frequency', FloatRange(unit='Hz'), readonly=False, default=0),
'voltage': Parameter('voltage', FloatRange(unit='V'), readonly=False, default=0),
'loss': Parameter('loss', FloatRange(unit='deg'), default=0),
}
value = Parameter('capacitance', FloatRange(unit='pF'), poll=True)
freq = Parameter('frequency', FloatRange(unit='Hz'), readonly=False, default=0)
voltage = Parameter('voltage', FloatRange(unit='V'), readonly=False, default=0)
loss = Parameter('loss', FloatRange(unit='deg'), default=0)
iodevClass = Ah2700IO
def parse_reply(self, reply):

View File

@ -20,7 +20,7 @@
# *****************************************************************************
"""Delay generator stanford 645"""
from secop.core import Module, Parameter, Override, FloatRange, HasIodev, StringIO, Done
from secop.core import FloatRange, HasIodev, Module, Parameter, StringIO
class DG645(StringIO):
@ -28,12 +28,12 @@ class DG645(StringIO):
class Delay(HasIodev, Module):
parameters = {
'on1': Parameter('on delay 1', FloatRange(unit='sec'), readonly=False, default=0),
'off1': Parameter('off delay 1', FloatRange(unit='sec'), readonly=False, default=60e-9),
'on2': Parameter('on delay 2', FloatRange(unit='sec'), readonly=False, default=0),
'off2': Parameter('off delay 2', FloatRange(unit='sec'), readonly=False, default=150e-9),
}
on1 = Parameter('on delay 1', FloatRange(unit='sec'), readonly=False, default=0)
off1 = Parameter('off delay 1', FloatRange(unit='sec'), readonly=False, default=60e-9)
on2 = Parameter('on delay 2', FloatRange(unit='sec'), readonly=False, default=0)
off2 = Parameter('off delay 2', FloatRange(unit='sec'), readonly=False, default=150e-9)
iodevClass = DG645
def read_on1(self):

View File

@ -22,8 +22,8 @@
not tested yet"""
from secop.core import Writable, Module, Parameter, Override, Attached,\
BoolType, FloatRange, EnumType, HasIodev, StringIO
from secop.core import Attached, BoolType, EnumType, FloatRange, \
HasIodev, Module, Parameter, StringIO, Writable
class K2601bIO(StringIO):
@ -42,13 +42,13 @@ SOURCECMDS = {
class SourceMeter(HasIodev, Module):
parameters = {
'resistivity': Parameter('readback resistivity', FloatRange(unit='Ohm'), poll=True),
'power': Parameter('readback power', FloatRange(unit='W'), poll=True),
'mode': Parameter('measurement mode', EnumType(off=0, current=1, voltage=2),
readonly=False, default=0),
'active': Parameter('output enable', BoolType(), readonly=False, poll=True),
}
resistivity = Parameter('readback resistivity', FloatRange(unit='Ohm'), poll=True)
power = Parameter('readback power', FloatRange(unit='W'), poll=True)
mode = Parameter('measurement mode', EnumType(off=0, current=1, voltage=2),
readonly=False, default=0)
active = Parameter('output enable', BoolType(), readonly=False, poll=True)
iodevClass = K2601bIO
def read_resistivity(self):
@ -74,15 +74,12 @@ class SourceMeter(HasIodev, Module):
class Current(HasIodev, Writable):
properties = {
'sourcemeter': Attached(),
}
parameters = {
'value': Override('measured current', FloatRange(unit='A'), poll=True),
'target': Override('set current', FloatRange(unit='A'), poll=True),
'active': Parameter('current is controlled', BoolType(), default=False), # polled from Current/Voltage
'limit': Parameter('current limit', FloatRange(0, 2.0, unit='A'), default=2, poll=True),
}
sourcemeter = Attached()
value = Parameter('measured current', FloatRange(unit='A'), poll=True)
target = Parameter('set current', FloatRange(unit='A'), poll=True)
active = Parameter('current is controlled', BoolType(), default=False) # polled from Current/Voltage
limit = Parameter('current limit', FloatRange(0, 2.0, unit='A'), default=2, poll=True)
def read_value(self):
return self.sendRecv('print(smua.measure.i())')
@ -120,15 +117,12 @@ class Current(HasIodev, Writable):
class Voltage(HasIodev, Writable):
properties = {
'sourcemeter': Attached(),
}
parameters = {
'value': Override('measured voltage', FloatRange(unit='V'), poll=True),
'target': Override('set voltage', FloatRange(unit='V'), poll=True),
'active': Parameter('voltage is controlled', BoolType(), poll=True),
'limit': Parameter('current limit', FloatRange(0, 2.0, unit='V'), default=2, poll=True),
}
sourcemeter = Attached()
value = Parameter('measured voltage', FloatRange(unit='V'), poll=True)
target = Parameter('set voltage', FloatRange(unit='V'), poll=True)
active = Parameter('voltage is controlled', BoolType(), poll=True)
limit = Parameter('current limit', FloatRange(0, 2.0, unit='V'), default=2, poll=True)
def read_value(self):
return self.sendRecv('print(smua.measure.v())')
@ -159,7 +153,7 @@ class Voltage(HasIodev, Writable):
def write_active(self, value):
if self._sourcemeter.mode != 2:
if value:
self._sourcemeter.write_mode(2) # switch to voltage
self._sourcemeter.write_mode(2) # switch to voltage
else:
return 0
return self._sourcemeter.write_active(value)

View File

@ -22,13 +22,13 @@
import time
from secop.modules import Readable, Drivable, Parameter, Override, Property, Attached
from secop.metaclass import Done
from secop.datatypes import FloatRange, IntRange, EnumType, BoolType
from secop.stringio import HasIodev
from secop.poller import Poller, REGULAR
from secop.lib import formatStatusBits
import secop.iohandler
from secop.datatypes import BoolType, EnumType, FloatRange, IntRange
from secop.lib import formatStatusBits
from secop.modules import Attached, Done, \
Drivable, Parameter, Property, Readable
from secop.poller import REGULAR, Poller
from secop.stringio import HasIodev
Status = Drivable.Status
@ -59,19 +59,18 @@ class StringIO(secop.stringio.StringIO):
class Main(HasIodev, Drivable):
parameters = {
'value': Override('the current channel', poll=REGULAR, datatype=IntRange(0, 17)),
'target': Override('channel to select', datatype=IntRange(0, 17)),
'autoscan':
Parameter('whether to scan automatically', datatype=BoolType(), readonly=False, default=False),
'pollinterval': Override('sleeptime between polls', default=1),
}
value = Parameter('the current channel', poll=REGULAR, datatype=IntRange(0, 17))
target = Parameter('channel to select', datatype=IntRange(0, 17))
autoscan = Parameter('whether to scan automatically', datatype=BoolType(), readonly=False, default=False)
pollinterval = Parameter('sleeptime between polls', default=1)
pollerClass = Poller
iodevClass = StringIO
_channel_changed = 0 # time of last channel change
_channels = None # dict <channel no> of <module object>
def earlyInit(self):
self._channel_changed = 0
self._channels = {}
def register_channel(self, modobj):
@ -85,10 +84,8 @@ class Main(HasIodev, Drivable):
def read_value(self):
channel, auto = scan.send_command(self)
# response = self.sendRecv('SCAN?').strip().split(',')
# channel, auto = (int(s) for s in response)
if channel not in self._channels:
return channel
return channel
if not self._channels[channel].enabled:
# channel was disabled recently, but still selected
nextchannel = 0
@ -129,61 +126,42 @@ class ResChannel(HasIodev, Readable):
RES_RANGE = {key: i+1 for i, key in list(
enumerate(mag % val for mag in ['%gmOhm', '%gOhm', '%gkOhm', '%gMOhm']
for val in [2, 6.32, 20, 63.2, 200, 632]))[:-2]}
for val in [2, 6.32, 20, 63.2, 200, 632]))[:-2]}
RES_SCALE = [2 * 10 ** (0.5 * i) for i in range(-7, 16)] # RES_SCALE[0] is not used
CUR_RANGE = {key: i + 1 for i, key in list(
enumerate(mag % val for mag in ['%gpA', '%gnA', '%guA', '%gmA']
for val in [1, 3.16, 10, 31.6, 100, 316]))[:-2]}
for val in [1, 3.16, 10, 31.6, 100, 316]))[:-2]}
VOLT_RANGE = {key: i + 1 for i, key in list(
enumerate(mag % val for mag in ['%guV', '%gmV']
for val in [2, 6.32, 20, 63.2, 200, 632]))}
for val in [2, 6.32, 20, 63.2, 200, 632]))}
pollerClass = Poller
iodevClass = StringIO
_main = None # main module
_last_range_change = 0 # time of last range change
properties = {
'channel':
Property('the Lakeshore channel', datatype=IntRange(1, 16), export=False),
'main':
Attached()
}
channel = Property('the Lakeshore channel', datatype=IntRange(1, 16), export=False)
main = Attached()
parameters = {
'value':
Override(datatype=FloatRange(unit='Ohm')),
'pollinterval':
Override(visibility=3),
'range':
Parameter('reading range', readonly=False,
datatype=EnumType(**RES_RANGE), handler=rdgrng),
'minrange':
Parameter('minimum range for software autorange', readonly=False, default=1,
datatype=EnumType(**RES_RANGE)),
'autorange':
Parameter('autorange', datatype=EnumType(off=0, hard=1, soft=2),
readonly=False, handler=rdgrng, default=2),
'iexc':
Parameter('current excitation', datatype=EnumType(off=0, **CUR_RANGE), readonly=False, handler=rdgrng),
'vexc':
Parameter('voltage excitation', datatype=EnumType(off=0, **VOLT_RANGE), readonly=False, handler=rdgrng),
'enabled':
Parameter('is this channel enabled?', datatype=BoolType(), readonly=False, handler=inset),
'pause':
Parameter('pause after channel change', datatype=FloatRange(3, 60), readonly=False, handler=inset),
'dwell':
Parameter('dwell time with autoscan', datatype=FloatRange(1, 200), readonly=False, handler=inset),
'filter':
Parameter('filter time', datatype=FloatRange(1, 200), readonly=False, handler=filterhdl),
}
value = Parameter(datatype=FloatRange(unit='Ohm'))
pollinterval = Parameter(visibility=3)
range = Parameter('reading range', readonly=False,
datatype=EnumType(**RES_RANGE), handler=rdgrng)
minrange = Parameter('minimum range for software autorange', readonly=False, default=1,
datatype=EnumType(**RES_RANGE))
autorange = Parameter('autorange', datatype=EnumType(off=0, hard=1, soft=2),
readonly=False, handler=rdgrng, default=2)
iexc = Parameter('current excitation', datatype=EnumType(off=0, **CUR_RANGE), readonly=False, handler=rdgrng)
vexc = Parameter('voltage excitation', datatype=EnumType(off=0, **VOLT_RANGE), readonly=False, handler=rdgrng)
enabled = Parameter('is this channel enabled?', datatype=BoolType(), readonly=False, handler=inset)
pause = Parameter('pause after channel change', datatype=FloatRange(3, 60), readonly=False, handler=inset)
dwell = Parameter('dwell time with autoscan', datatype=FloatRange(1, 200), readonly=False, handler=inset)
filter = Parameter('filter time', datatype=FloatRange(1, 200), readonly=False, handler=filterhdl)
def initModule(self):
self._main = self.DISPATCHER.get_module(self.main)
self._main.register_channel(self)
def startModule(self, started_callback):
self._last_range_change = 0
super().startModule(started_callback)
def read_value(self):
if self.channel != self._main.value:
return Done
@ -195,7 +173,7 @@ class ResChannel(HasIodev, Readable):
if self.autorange == 'soft':
now = time.time()
if now > self._last_range_change + self.pause:
rng = int(max(self.minrange, self.range)) # convert from enum to int
rng = int(max(self.minrange, self.range)) # convert from enum to int
if self.status[1] == '':
if abs(result) > self.RES_SCALE[rng]:
if rng < 22:
@ -236,8 +214,6 @@ class ResChannel(HasIodev, Readable):
result = dict(range=rng)
if autorange:
result['autorange'] = 'hard'
#elif self.autorange == 'hard':
# result['autorange'] = 'soft'
# else: do not change autorange
self.log.info('%s range %r %r %r' % (self.name, rng, autorange, self.autorange))
if excoff:

View File

@ -22,6 +22,7 @@
from secop.modules import Communicator
class Ls370Sim(Communicator):
CHANNEL_COMMANDS = [
('RDGR?%d', '1.0'),
@ -32,9 +33,8 @@ class Ls370Sim(Communicator):
]
OTHER_COMMANDS = [
('*IDN?', 'LSCI,MODEL370,370184,05302003'),
('SCAN?', '1,1'),
('SCAN?', '3,1'),
]
channel = [None]
def earlyInit(self):
self._data = dict(self.OTHER_COMMANDS)
@ -43,7 +43,7 @@ class Ls370Sim(Communicator):
self._data[fmt % chan] = v
# mkthread(self.run)
def do_communicate(self, command):
def communicate(self, command):
# simulation part, time independent
for channel in range(1,17):
_, _, _, _, excoff = self._data['RDGRNG?%d' % channel].split(',')

View File

@ -31,20 +31,19 @@ Polling of value and status is done commonly for all modules. For each registere
<module>.update_value_status() is called in order to update their value and status.
"""
import time
import threading
import time
from secop.modules import Module, Readable, Drivable, Parameter, Override,\
Communicator, Property, Attached
from secop.datatypes import EnumType, FloatRange, IntRange, StringType,\
BoolType, StatusType
from secop.lib.enum import Enum
from secop.lib import clamp
from secop.errors import HardwareError
from secop.poller import Poller
import secop.iohandler
from secop.datatypes import BoolType, EnumType, \
FloatRange, IntRange, StatusType, StringType
from secop.errors import HardwareError
from secop.lib import clamp
from secop.lib.enum import Enum
from secop.modules import Attached, Communicator, Done, \
Drivable, Parameter, Property, Readable
from secop.poller import Poller
from secop.stringio import HasIodev
from secop.metaclass import Done
try:
import secop_psi.ppmswindows as ppmshw
@ -73,19 +72,14 @@ class IOHandler(secop.iohandler.IOHandler):
class Main(Communicator):
"""ppms communicator module"""
parameters = {
'pollinterval': Parameter('poll interval', readonly=False,
datatype=FloatRange(), default=2),
'communicate': Override('GBIP command'),
'data': Parameter('internal', poll=True, export=True, # export for test only
default="", readonly=True, datatype=StringType()),
}
properties = {
'class_id': Property('Quantum Design class id', export=False,
datatype=StringType()),
}
pollinterval = Parameter('poll interval', FloatRange(), readonly=False, default=2)
data = Parameter('internal', StringType(), poll=True, export=True, # export for test only
default="", readonly=True)
_channel_names = ['packed_status', 'temp', 'field', 'position', 'r1', 'i1', 'r2', 'i2',
class_id = Property('Quantum Design class id', StringType(), export=False)
_channel_names = [
'packed_status', 'temp', 'field', 'position', 'r1', 'i1', 'r2', 'i2',
'r3', 'i3', 'r4', 'i4', 'v1', 'v2', 'digital', 'cur1', 'pow1', 'cur2', 'pow2',
'p', 'u20', 'u21', 'u22', 'ts', 'u24', 'u25', 'u26', 'u27', 'u28', 'u29']
assert len(_channel_names) == 30
@ -102,7 +96,8 @@ class Main(Communicator):
def register(self, other):
self.modules[other.channel] = other
def do_communicate(self, command):
def communicate(self, command):
"""GPIB command"""
with self.lock:
reply = self._ppms_device.send(command)
self.log.debug("%s|%s", command, reply)
@ -114,7 +109,7 @@ class Main(Communicator):
if channel.enabled:
mask |= 1 << self._channel_to_index.get(channelname, 0)
# send, read and convert to floats and ints
data = self.do_communicate('GETDAT? %d' % mask)
data = self.communicate('GETDAT? %d' % mask)
reply = data.split(',')
mask = int(reply.pop(0))
reply.pop(0) # pop timestamp
@ -133,23 +128,23 @@ class Main(Communicator):
return data # return data as string
class PpmsMixin(HasIodev, Module):
"""common methods for ppms modules"""
parameters = {
'pollinterval': None,
}
class PpmsBase(HasIodev, Readable):
"""common base for all ppms modules"""
iodev = Attached()
pollerClass = Poller
enabled = True # default, if no parameter enable is defined
_last_settings = None # used by several modules
slow_pollfactor = 1
# as this pollinterval affects only the polling of settings
# it would be confusing to export it.
pollinterval = Parameter(export=False)
def initModule(self):
self._iodev.register(self)
def startModule(self, started_callback):
""""""
# no polls except on main module
started_callback()
@ -160,8 +155,8 @@ class PpmsMixin(HasIodev, Module):
def read_status(self):
# polling is done by the main module
# and PPMS does not deliver really fresh status values anyway:
# e.g. the status is not changed immediately after a target change!
# and PPMS does not deliver really fresh status values anyway: the status is not
# changed immediately after a target change!
return Done
def update_value_status(self, value, packed_status):
@ -177,29 +172,22 @@ class PpmsMixin(HasIodev, Module):
self.status = (self.Status.IDLE, '')
class Channel(PpmsMixin, Readable):
class Channel(PpmsBase):
"""channel base class"""
parameters = {
'value':
Override('main value of channels', poll=True),
'enabled':
Parameter('is this channel used?', readonly=False, poll=False,
datatype=BoolType(), default=False),
}
properties = {
'channel':
Property('channel name',
datatype=StringType(), export=False, default=''),
'no':
Property('channel number',
datatype=IntRange(1, 4), export=False),
}
value = Parameter('main value of channels', poll=True)
enabled = Parameter('is this channel used?', readonly=False, poll=False,
datatype=BoolType(), default=False)
channel = Property('channel name',
datatype=StringType(), export=False, default='')
no = Property('channel number',
datatype=IntRange(1, 4), export=False)
def earlyInit(self):
Readable.earlyInit(self)
if not self.channel:
self.properties['channel'] = self.name
self.channel = self.name
def get_settings(self, pname):
return ''
@ -208,15 +196,12 @@ class Channel(PpmsMixin, Readable):
class UserChannel(Channel):
"""user channel"""
properties = {
'no':
Property('*(unused)*',
datatype=IntRange(0, 0), export=False, default=0),
'linkenable':
Property('name of linked channel for enabling',
datatype=StringType(), export=False, default=''),
# pollinterval = Parameter(visibility=3)
}
no = Property('channel number',
datatype=IntRange(0, 0), export=False, default=0)
linkenable = Property('name of linked channel for enabling',
datatype=StringType(), export=False, default='')
def write_enabled(self, enabled):
other = self._iodev.modules.get(self.linkenable, None)
@ -230,14 +215,11 @@ class DriverChannel(Channel):
drvout = IOHandler('drvout', 'DRVOUT? %(no)d', '%d,%g,%g')
parameters = {
'current':
Parameter('driver current', readonly=False, handler=drvout,
datatype=FloatRange(0., 5000., unit='uA')),
'powerlimit':
Parameter('power limit', readonly=False, handler=drvout,
datatype=FloatRange(0., 1000., unit='uW')),
}
current = Parameter('driver current', readonly=False, handler=drvout,
datatype=FloatRange(0., 5000., unit='uA'))
powerlimit = Parameter('power limit', readonly=False, handler=drvout,
datatype=FloatRange(0., 1000., unit='uW'))
# pollinterval = Parameter(visibility=3)
def analyze_drvout(self, no, current, powerlimit):
if self.no != no:
@ -255,25 +237,19 @@ class BridgeChannel(Channel):
bridge = IOHandler('bridge', 'BRIDGE? %(no)d', '%d,%g,%g,%d,%d,%g')
# pylint: disable=invalid-name
ReadingMode = Enum('ReadingMode', standard=0, fast=1, highres=2)
parameters = {
'enabled':
Override(handler=bridge),
'excitation':
Parameter('excitation current', readonly=False, handler=bridge,
datatype=FloatRange(0.01, 5000., unit='uA')),
'powerlimit':
Parameter('power limit', readonly=False, handler=bridge,
datatype=FloatRange(0.001, 1000., unit='uW')),
'dcflag':
Parameter('True when excitation is DC (else AC)', readonly=False, handler=bridge,
datatype=BoolType()),
'readingmode':
Parameter('reading mode', readonly=False, handler=bridge,
datatype=EnumType(ReadingMode)),
'voltagelimit':
Parameter('voltage limit', readonly=False, handler=bridge,
datatype=FloatRange(0.0001, 100., unit='mV')),
}
enabled = Parameter(handler=bridge)
excitation = Parameter('excitation current', readonly=False, handler=bridge,
datatype=FloatRange(0.01, 5000., unit='uA'))
powerlimit = Parameter('power limit', readonly=False, handler=bridge,
datatype=FloatRange(0.001, 1000., unit='uW'))
dcflag = Parameter('True when excitation is DC (else AC)', readonly=False, handler=bridge,
datatype=BoolType())
readingmode = Parameter('reading mode', readonly=False, handler=bridge,
datatype=EnumType(ReadingMode))
voltagelimit = Parameter('voltage limit', readonly=False, handler=bridge,
datatype=FloatRange(0.0001, 100., unit='mV'))
# pollinterval = Parameter(visibility=3)
def analyze_bridge(self, no, excitation, powerlimit, dcflag, readingmode, voltagelimit):
if self.no != no:
@ -294,23 +270,22 @@ class BridgeChannel(Channel):
return self.no, 0, 0, change.dcflag, change.readingmode, 0
class Level(PpmsMixin, Readable):
class Level(PpmsBase):
"""helium level"""
level = IOHandler('level', 'LEVEL?', '%g,%d')
parameters = {
'value': Override(datatype=FloatRange(unit='%'), handler=level),
'status': Override(handler=level),
}
value = Parameter(datatype=FloatRange(unit='%'), handler=level)
status = Parameter(handler=level)
# pollinterval = Parameter(visibility=3)
channel = 'level'
def update_value_status(self, value, packed_status):
pass
# must be a no-op
# when called from Main.read_data, value is always None
# value and status is polled via settings
pass
def analyze_level(self, level, status):
# ignore 'old reading' state of the flag, as this happens only for a short time
@ -318,7 +293,7 @@ class Level(PpmsMixin, Readable):
return dict(value=level, status=(self.Status.IDLE, ''))
class Chamber(PpmsMixin, Drivable):
class Chamber(PpmsBase, Drivable):
"""sample chamber handling
value is an Enum, which is redundant with the status text
@ -351,14 +326,13 @@ class Chamber(PpmsMixin, Drivable):
venting_continuously=9,
general_failure=15,
)
parameters = {
'value':
Override(description='chamber state', handler=chamber,
datatype=EnumType(StatusCode)),
'target':
Override(description='chamber command', handler=chamber,
datatype=EnumType(Operation)),
}
value = Parameter(description='chamber state', handler=chamber,
datatype=EnumType(StatusCode))
target = Parameter(description='chamber command', handler=chamber,
datatype=EnumType(Operation))
# pollinterval = Parameter(visibility=3)
STATUS_MAP = {
StatusCode.purged_and_sealed: (Status.IDLE, 'purged and sealed'),
StatusCode.vented_and_sealed: (Status.IDLE, 'vented and sealed'),
@ -387,44 +361,40 @@ class Chamber(PpmsMixin, Drivable):
return dict(target=target)
def change_chamber(self, change):
# write settings, combining <pname>=<value> and current attributes
# and request updated settings
if change.target == self.Operation.noop:
return None
return (change.target,)
class Temp(PpmsMixin, Drivable):
class Temp(PpmsBase, Drivable):
"""temperature"""
temp = IOHandler('temp', 'TEMP?', '%g,%g,%d')
Status = Enum(Drivable.Status,
RAMPING = 370,
STABILIZING = 380,
Status = Enum(
Drivable.Status,
RAMPING=370,
STABILIZING=380,
)
# pylint: disable=invalid-name
ApproachMode = Enum('ApproachMode', fast_settle=0, no_overshoot=1)
parameters = {
'value':
Override(datatype=FloatRange(unit='K'), poll=True),
'status':
Override(datatype=StatusType(Status), poll=True),
'target':
Override(datatype=FloatRange(1.7, 402.0, unit='K'), poll=False, needscfg=False),
'setpoint':
Parameter('intermediate set point',
datatype=FloatRange(1.7, 402.0, unit='K'), handler=temp),
'ramp':
Parameter('ramping speed', readonly=False, default=0,
datatype=FloatRange(0, 20, unit='K/min')),
'workingramp':
Parameter('intermediate ramp value',
datatype=FloatRange(0, 20, unit='K/min'), handler=temp),
'approachmode':
Parameter('how to approach target!', readonly=False, handler=temp,
datatype=EnumType(ApproachMode)),
'timeout':
Parameter('drive timeout, in addition to ramp time', readonly=False,
datatype=FloatRange(0, unit='sec'), default=3600),
}
value = Parameter(datatype=FloatRange(unit='K'), poll=True)
status = Parameter(datatype=StatusType(Status), poll=True)
target = Parameter(datatype=FloatRange(1.7, 402.0, unit='K'), poll=False, needscfg=False)
setpoint = Parameter('intermediate set point',
datatype=FloatRange(1.7, 402.0, unit='K'), handler=temp)
ramp = Parameter('ramping speed', readonly=False, default=0,
datatype=FloatRange(0, 20, unit='K/min'))
workingramp = Parameter('intermediate ramp value',
datatype=FloatRange(0, 20, unit='K/min'), handler=temp)
approachmode = Parameter('how to approach target!', readonly=False, handler=temp,
datatype=EnumType(ApproachMode))
# pollinterval = Parameter(visibility=3)
timeout = Parameter('drive timeout, in addition to ramp time', readonly=False,
datatype=FloatRange(0, unit='sec'), default=3600)
# pylint: disable=invalid-name
TempStatus = Enum(
'TempStatus',
@ -449,17 +419,14 @@ class Temp(PpmsMixin, Drivable):
14: (Status.ERROR, 'can not complete'),
15: (Status.ERROR, 'general failure'),
}
properties = {
'general_stop': Property('respect general stop', datatype=BoolType(),
export=True, default=True)
}
general_stop = Property('respect general stop', datatype=BoolType(),
default=True, value=False)
channel = 'temp'
_stopped = False
_expected_target_time = 0
_last_change = 0 # 0 means no target change is pending
_last_target = None # last reached target
general_stop = False
_cool_deadline = 0
_wait_at10 = False
_ramp_at_limit = False
@ -573,7 +540,7 @@ class Temp(PpmsMixin, Drivable):
def calc_expected(self, target, ramp):
self._expected_target_time = time.time() + abs(target - self.value) * 60.0 / max(0.1, ramp)
def do_stop(self):
def stop(self):
if not self.isDriving():
return
if self.status[0] != self.Status.STABILIZING:
@ -586,37 +553,31 @@ class Temp(PpmsMixin, Drivable):
self._stopped = True
class Field(PpmsMixin, Drivable):
class Field(PpmsBase, Drivable):
"""magnetic field"""
field = IOHandler('field', 'FIELD?', '%g,%g,%d,%d')
Status = Enum(Drivable.Status,
PREPARED = 150,
PREPARING = 340,
RAMPING = 370,
FINALIZING = 390,
Status = Enum(
Drivable.Status,
PREPARED=150,
PREPARING=340,
RAMPING=370,
FINALIZING=390,
)
# pylint: disable=invalid-name
PersistentMode = Enum('PersistentMode', persistent=0, driven=1)
ApproachMode = Enum('ApproachMode', linear=0, no_overshoot=1, oscillate=2)
parameters = {
'value':
Override(datatype=FloatRange(unit='T'), poll=True),
'status':
Override(datatype=StatusType(Status), poll=True),
'target':
Override(datatype=FloatRange(-15, 15, unit='T'), handler=field),
'ramp':
Parameter('ramping speed', readonly=False, handler=field,
datatype=FloatRange(0.064, 1.19, unit='T/min')),
'approachmode':
Parameter('how to approach target', readonly=False, handler=field,
datatype=EnumType(ApproachMode)),
'persistentmode':
Parameter('what to do after changing field', readonly=False, handler=field,
datatype=EnumType(PersistentMode)),
}
value = Parameter(datatype=FloatRange(unit='T'), poll=True)
status = Parameter(datatype=StatusType(Status), poll=True)
target = Parameter(datatype=FloatRange(-15, 15, unit='T'), handler=field)
ramp = Parameter('ramping speed', readonly=False, handler=field,
datatype=FloatRange(0.064, 1.19, unit='T/min'))
approachmode = Parameter('how to approach target', readonly=False, handler=field,
datatype=EnumType(ApproachMode))
persistentmode = Parameter('what to do after changing field', readonly=False, handler=field,
datatype=EnumType(PersistentMode))
# pollinterval = Parameter(visibility=3)
STATUS_MAP = {
1: (Status.IDLE, 'persistent mode'),
@ -652,7 +613,7 @@ class Field(PpmsMixin, Drivable):
else:
status = (self.Status.WARN, 'timeout when ramping leads')
elif now > self._last_change + 5:
self._last_change = 0 # give up waiting for driving
self._last_change = 0 # give up waiting for driving
elif self.isDriving(status) and status != self._status_before_change:
self._last_change = 0
self.log.debug('time needed to change to busy: %.3g', now - self._last_change)
@ -718,7 +679,7 @@ class Field(PpmsMixin, Drivable):
return Done
return None # do not execute FIELD command, as this would trigger a ramp up of leads current
def do_stop(self):
def stop(self):
if not self.isDriving():
return
newtarget = clamp(self._last_target, self.value, self.target)
@ -729,23 +690,20 @@ class Field(PpmsMixin, Drivable):
self._stopped = True
class Position(PpmsMixin, Drivable):
class Position(PpmsBase, Drivable):
"""rotator position"""
move = IOHandler('move', 'MOVE?', '%g,%g,%g')
Status = Drivable.Status
parameters = {
'value':
Override(datatype=FloatRange(unit='deg'), poll=True),
'target':
Override(datatype=FloatRange(-720., 720., unit='deg'), handler=move),
'enabled':
Parameter('is this channel used?', readonly=False, poll=False,
datatype=BoolType(), default=True),
'speed':
Parameter('motor speed', readonly=False, handler=move,
datatype=FloatRange(0.8, 12, unit='deg/sec')),
}
value = Parameter(datatype=FloatRange(unit='deg'), poll=True)
target = Parameter(datatype=FloatRange(-720., 720., unit='deg'), handler=move)
enabled = Parameter('is this channel used?', readonly=False, poll=False,
datatype=BoolType(), default=True)
speed = Parameter('motor speed', readonly=False, handler=move,
datatype=FloatRange(0.8, 12, unit='deg/sec'))
# pollinterval = Parameter(visibility=3)
STATUS_MAP = {
1: (Status.IDLE, 'at target'),
5: (Status.BUSY, 'moving'),
@ -824,7 +782,7 @@ class Position(PpmsMixin, Drivable):
self.speed = value
return None # do not execute MOVE command, as this would trigger an unnecessary move
def do_stop(self):
def stop(self):
if not self.isDriving():
return
newtarget = clamp(self._last_target, self.value, self.target)

View File

@ -20,9 +20,9 @@
# *****************************************************************************
"""PPMS mf proxy"""
from secop.core import Enum, FloatRange, EnumType, Override, Parameter, Drivable
from secop.datatypes import StatusType
import secop_psi.ppms
from secop.core import Drivable, Enum, EnumType, FloatRange, Override, Parameter
from secop.datatypes import StatusType
from secop.proxy import proxy_class

View File

@ -18,9 +18,10 @@
# Module authors:
# Markus Zolliker <markus.zolliker@psi.ch>
# *****************************************************************************
import time
import json
import math
import time
def num(string):
return json.loads(string)

View File

@ -32,21 +32,21 @@ t1:raw tt t1/raw /tt/t1/raw tt t1 raw /tt/t1
rx:bla rx bla /some/rx_a/bla rx bla /some/rx_a
"""
import json
import threading
import time
import json
from os.path import join, expanduser
from os.path import expanduser, join
from secop.modules import Module, Parameter, Command, Override, Drivable, Readable, Writable, Property, Attached
from secop.datatypes import StringType, FloatRange, ArrayOf, BoolType, IntRange, EnumType
from secop.lib import mkthread, getGeneralConfig
from secop.lib.asynconn import AsynConn, ConnectionClosed
from secop.metaclass import ModuleMeta, Done
from secop.errors import HardwareError, secop_error, ConfigError
from secop.client import ProxyClient
from secop.datatypes import ArrayOf, BoolType, \
EnumType, FloatRange, IntRange, StringType
from secop.errors import ConfigError, HardwareError, secop_error
from secop.lib import getGeneralConfig, mkthread
from secop.lib.asynconn import AsynConn, ConnectionClosed
from secop.modules import Attached, Command, Done, Drivable, \
Module, Parameter, Property, Readable, Writable
from secop.protocol.dispatcher import make_update
CFG_HEADER = """[NODE]
id = %(samenv)s.psi.ch
description = %(samenv)s over SEA
@ -76,7 +76,7 @@ def get_sea_port(instance):
for line in f:
linesplit = line.split()
if len(linesplit) == 3:
cmd, var, value = line.split()
_, var, value = line.split()
if var == 'serverport':
return value
except FileNotFoundError:
@ -87,23 +87,10 @@ def get_sea_port(instance):
class SeaClient(ProxyClient, Module):
"""connection to SEA"""
properties = {
'json_path': Property('path to SEA json descriptors',
datatype=StringType(),
default=join(expanduser('~'), 'sea/tcl/json'))
}
parameters = {
'uri':
Parameter('hostname:portnumber', datatype=StringType(), default='localhost:5000'),
'timeout':
Parameter('timeout', datatype=FloatRange(0), default=10),
}
commands = {
'communicate':
Command('send a command to SEA', argument=StringType(), result=StringType()),
'describe':
Command('save objects (and sub-objects) description', result=StringType()),
}
json_path = Property('path to SEA json descriptors', StringType())
uri = Parameter('hostname:portnumber', datatype=StringType(), default='localhost:5000')
timeout = Parameter('timeout', datatype=FloatRange(0), default=10)
def __init__(self, name, log, opts, srv):
instance = srv.node_cfg['name'].rsplit('_', 1)[0]
@ -198,7 +185,7 @@ class SeaClient(ProxyClient, Module):
if msg.startswith('_E '):
try:
_, path, readerror = msg.split(None, 2)
except Exception as e:
except ValueError:
continue
else:
continue
@ -241,11 +228,15 @@ class SeaClient(ProxyClient, Module):
# do not update unchanged values within 0.1 sec
self.updateValue(module, param, value, now, readerror)
def do_communicate(self, command):
@Command
def communicate(self, command):
"""send a command to SEA"""
reply = self.request(command)
return reply
def do_describe(self):
@Command(result=StringType())
def describe(self):
"""save objects (and sub-objects) description"""
reply = self.request('describe_all')
reply = ''.join('' if line.startswith('WARNING') else line for line in reply.split('\n'))
samenv, reply = json.loads(reply)
@ -288,9 +279,7 @@ def get_datatype(paramdesc):
class SeaModule(Module):
properties = {
'iodev': Attached(),
}
iodev = Attached()
# pollerClass=None
path2param = None
@ -329,8 +318,7 @@ class SeaModule(Module):
else: # take all
main = ''
path2param = {}
parameters = {}
attributes = dict(sea_object=sea_object, path2param=path2param, parameters=parameters)
attributes = dict(sea_object=sea_object, path2param=path2param)
for paramdesc in descr:
path = paramdesc['path']
readonly = paramdesc.get('readonly', True)
@ -351,6 +339,7 @@ class SeaModule(Module):
else:
kwds['group'] = pathlist[-2]
# flatten path to parameter name
key = None
for i in reversed(range(len(pathlist))):
key = '_'.join(pathlist[i:])
if not key in cls.accessibles:
@ -361,12 +350,12 @@ class SeaModule(Module):
if key in cls.accessibles:
if key == 'target':
kwds['readonly'] = False
pobj = Override(**kwds)
pobj = cls.accessibles[key].override(**kwds)
datatype = kwds.get('datatype', cls.accessibles[key].datatype)
else:
pobj = Parameter(**kwds)
datatype = pobj.datatype
parameters[key] = pobj
attributes[key] = pobj
if not hasattr(cls, 'read_' + key):
def rfunc(self, cmd='hval /sics/%s/%s' % (sea_object, path)):
print('READ', cmd)
@ -395,19 +384,20 @@ class SeaModule(Module):
return Done
attributes['write_' + key] = wfunc
# create standard parameters like value and status, if not yet there
for pname, pobj in cls.accessibles.items():
if pname == 'pollinterval':
parameters[pname] = Override(export=False)
elif pname not in parameters and isinstance(pobj, Parameter):
parameters[pname] = Override(poll=False, needscfg=False)
attributes[pname] = pobj.override(export=False)
elif pname not in attributes and isinstance(pobj, Parameter):
attributes[pname] = pobj.override(poll=False, needscfg=False)
classname = '%s_%s' % (cls.__name__, sea_object)
newcls = ModuleMeta.__new__(ModuleMeta, classname, (cls,), attributes)
newcls = type(classname, (cls,), attributes)
return Module.__new__(newcls)
def __init__(self, name, logger, cfgdict, dispatcher):
Module.__init__(self, name, logger, cfgdict, dispatcher)
# def __init__(self, name, logger, cfgdict, dispatcher):
# Module.__init__(self, name, logger, cfgdict, dispatcher)
def updateEvent(self, module, parameter, value, timestamp, readerror):
upd = getattr(self, 'update_' + parameter, None)
@ -442,9 +432,9 @@ class SeaReadable(SeaModule, Readable):
if readerror:
value = repr(readerror)
if value == '':
self.status = [self.Status.IDLE, '']
self.status = (self.Status.IDLE, '')
else:
self.status = [self.Status.ERROR, value]
self.status = (self.Status.ERROR, value)
def read_status(self):
return self.status
@ -485,11 +475,11 @@ class SeaDrivable(SeaModule, Drivable):
def updateStatus(self):
if self._sea_status:
self.status = [self.Status.ERROR, self._sea_status]
self.status = (self.Status.ERROR, self._sea_status)
elif self._is_running:
self.status = [self.Status.BUSY, 'driving']
self.status = (self.Status.BUSY, 'driving')
else:
self.status = [self.Status.IDLE, '']
self.status = (self.Status.IDLE, '')
def updateTarget(self, module, parameter, value, timestamp, readerror):
if value is not None:

View File

@ -21,33 +21,33 @@
"""senis hall sensor"""
import time
import threading
import time
import numpy as np
from serial import Serial
from secop.core import Property, Parameter, Override, Readable, BoolType, \
FloatRange, TupleOf, StringType, IntRange, Attached
from secop.core import Attached, BoolType, FloatRange, IntRange, \
Parameter, Property, Readable, StringType, TupleOf
class Temperature(Readable):
pollerClass = None
parameters = {
'value': Override(datatype=FloatRange(unit='degC')),
}
value = Parameter(datatype=FloatRange(unit='degC'))
class Bcomp(Readable):
pollerClass = None
parameters = {
'value': Override(datatype=FloatRange(unit='T')),
'range': Parameter('working range', FloatRange(unit='T'), default=0),
}
value = Parameter(datatype=FloatRange(unit='T'))
range = Parameter('working range', FloatRange(unit='T'), default=0)
class Raw(Readable):
pollerClass = None
parameters = {
'value': Override(datatype=FloatRange()),
}
value = Parameter(datatype=FloatRange())
class TeslameterBase(Readable):
@ -58,18 +58,15 @@ class TeslameterBase(Readable):
the B components (and temperatures for 3MH6) are implemented as separate modules
"""
properties = {
'x': Attached(),
'y': Attached(),
'z': Attached(),
}
parameters = {
'value': Override('B vector', poll=True,
datatype=TupleOf(FloatRange(unit='T'), FloatRange(unit='T'), FloatRange(unit='T'))),
'usb': Parameter('usb device', StringType(), readonly=False),
'enabled': Parameter('enable data acq', datatype=BoolType(), readonly=False, default=True),
'nsample': Parameter('number of samples for average', datatype=IntRange(1, 1000), readonly=False, default=1),
}
x = Attached()
y = Attached()
z = Attached()
value = Parameter('B vector', poll=True,
datatype=TupleOf(FloatRange(unit='T'), FloatRange(unit='T'), FloatRange(unit='T')))
usb = Parameter('usb device', StringType(), readonly=False)
enabled = Parameter('enable data acq', datatype=BoolType(), readonly=False, default=True)
nsample = Parameter('number of samples for average', datatype=IntRange(1, 1000), readonly=False, default=1)
def init_serial(self, baud):
self._conn = Serial(self.usb, baud, timeout=0.1)
@ -103,9 +100,7 @@ class Teslameter3MH3(TeslameterBase):
remark: no query for the sample rate is possible, therefore set always to
a default rate (therefore initwrite=True on the rate parameter)
"""
properties = {
'range': Property('full scale', datatype=FloatRange(), default=2),
}
range = Property('full scale', datatype=FloatRange(), default=2)
def earlyInit(self):
self.init_serial(115200)
@ -122,7 +117,7 @@ class Teslameter3MH3(TeslameterBase):
s.timeout = 0.1 + 0.02 * self.nsample
for _ in range(2):
self.write_bytes(b'B')
t = time.time()
# t = time.time()
reply = self.read_bytes(8 * self.nsample)
s.timeout = 0.1
self.stop_reading()
@ -147,21 +142,19 @@ class Teslameter3MH3(TeslameterBase):
class Teslameter3MH6(TeslameterBase):
"""luxury model with probe and box temperature and autorange"""
properties = {
'x_direct': Attached(),
'y_direct': Attached(),
'z_direct': Attached(),
'probe_temp': Attached(),
'box_temp': Attached(),
'probe_temp_direct': Attached(),
'box_temp_direct': Attached(),
}
parameters = {
'range': Parameter('range or 0 for autorange', FloatRange(0, 20, unit='T'), readonly=False, default=0),
'rate': Parameter('sampling rate', datatype=FloatRange(10, 15000, unit='Hz'),
readonly=False, poll=True),
'avtime': Parameter('data acquisition time', FloatRange(), default=0),
}
x_direct = Attached()
y_direct = Attached()
z_direct = Attached()
probe_temp = Attached()
box_temp = Attached()
probe_temp_direct = Attached()
box_temp_direct = Attached()
range = Parameter('range or 0 for autorange', FloatRange(0, 20, unit='T'), readonly=False, default=0)
rate = Parameter('sampling rate', datatype=FloatRange(10, 15000, unit='Hz'),
readonly=False, poll=True)
avtime = Parameter('data acquisition time', FloatRange(), default=0)
SAMPLING_RATES = {0xe0: 15000, 0xd0: 7500, 0xc0: 3750, 0xb0: 2000, 0xa1: 1000,
0x92: 500, 0x82: 100, 0x72: 60, 0x63: 50, 0x53: 30, 0x23: 10}
RANGES = dict(zip(b'1234', [0.1, 0.5, 2, 20]))
@ -183,7 +176,7 @@ class Teslameter3MH6(TeslameterBase):
chk = np.frombuffer(reply, dtype='i1,23i1,i1')
if not np.all(np.sum(chk['f1'], axis=1) % 256 == 0):
status = 'checksum error'
continue
continue
# first byte must be 'B' and last byte must be CR
if np.all(chk['f0'] == ord(b'B')) and np.all(chk['f2'] == 13):
break
@ -219,7 +212,7 @@ class Teslameter3MH6(TeslameterBase):
self._z.value = mean['z'] * 0.001
self._probe_temp.value = mean['thc']
self._box_temp.value = mean['tec']
self.write_bytes(b'D') # put into NONcalibrated mode
if self.read_bytes(1) != b'd':
self.log.error('missing response to D command')

View File

@ -20,13 +20,14 @@
# *****************************************************************************
"""Software calibration"""
import os
from os.path import join, exists, basename
import math
import numpy as np
from scipy.interpolate import splrep, splev # pylint: disable=import-error
import os
from os.path import basename, exists, join
from secop.core import Readable, Parameter, Override, Attached, StringType, BoolType
import numpy as np
from scipy.interpolate import splev, splrep # pylint: disable=import-error
from secop.core import Attached, BoolType, Parameter, Readable, StringType
def linear(x):
@ -102,6 +103,7 @@ class CalCurve:
sensopt = calibspec.split(',')
calibname = sensopt.pop(0)
_, dot, ext = basename(calibname).rpartition('.')
kind = None
for path in os.environ.get('FRAPPY_CALIB_PATH', '').split(','):
# first try without adding kind
filename = join(path.strip(), calibname)
@ -109,8 +111,8 @@ class CalCurve:
kind = ext if dot else None
break
# then try adding all kinds as extension
for kind in KINDS:
for nam in {calibname, calibname.upper(), calibname.lower()}:
for nam in calibname, calibname.upper(), calibname.lower():
for kind in KINDS:
filename = join(path.strip(), '%s.%s' % (nam, kind))
if exists(filename):
break
@ -150,16 +152,14 @@ class CalCurve:
class Sensor(Readable):
properties = {
'rawsensor': Attached(),
}
parameters = {
'calib': Parameter('calibration name', datatype=StringType(), readonly=False),
'abs': Parameter('True: take abs(raw) before calib', datatype=BoolType(), readonly=False, default=True),
'value': Override(unit='K'),
'pollinterval': Override(export=False),
'status': Override(default=(Readable.Status.ERROR, 'unintialized'))
}
rawsensor = Attached()
calib = Parameter('calibration name', datatype=StringType(), readonly=False)
abs = Parameter('True: take abs(raw) before calib', datatype=BoolType(), readonly=False, default=True)
value = Parameter(unit='K')
pollinterval = Parameter(export=False)
status = Parameter(default=(Readable.Status.ERROR, 'unintialized'))
pollerClass = None
description = 'a calibrated sensor value'
_value_error = None
@ -179,7 +179,7 @@ class Sensor(Readable):
self._value_error = None
def error_update_value(self, err):
if self.abs and str(err) == 'R_UNDER':
if self.abs and str(err) == 'R_UNDER': # hack: ignore R_UNDER from ls370
self._value_error = None
return None
self._value_error = repr(err)

View File

@ -20,40 +20,38 @@
# *****************************************************************************
"""Test command arguments"""
from secop.core import Module, Parameter, Command, FloatRange, StringType, BoolType, TupleOf, StructOf, ArrayOf
from secop.core import ArrayOf, BoolType, Command, FloatRange, \
Module, Parameter, StringType, StructOf, TupleOf
class TestCmd(Module):
commands = {
'arg':
Command('5 args',
argument=TupleOf(StringType(), FloatRange(), BoolType(), TupleOf(BoolType()), StructOf(a=StringType())),
result=StringType()),
'keyed':
Command('keyworded arg', argument=StructOf(a=StringType(), b=FloatRange(), c=BoolType(), optional=['b']), result=StringType()),
'one':
Command('1 arg', argument=FloatRange(), result=StringType()),
'none':
Command('no arg', result=StringType()),
}
parameters = {
'struct': Parameter('struct', StructOf(a=StringType(), b=FloatRange(), c=BoolType(), optional=['b']),
readonly=False, default=dict(a='',c=True)),
'array': Parameter('array', ArrayOf(BoolType()),
readonly=False, default=[]),
'tuple': Parameter('tuple', TupleOf(StringType(), FloatRange(), BoolType(), TupleOf(BoolType()), StructOf(a=StringType())),
readonly=False, default=('',0,False,(False,),dict(a=''))),
}
struct = Parameter('struct', StructOf(a=StringType(), b=FloatRange(), c=BoolType(), optional=['b']),
readonly=False, default=dict(a='', c=True))
array = Parameter('array', ArrayOf(BoolType()),
readonly=False, default=[])
tuple = Parameter('tuple', TupleOf(StringType(), FloatRange(), BoolType(),
TupleOf(BoolType()), StructOf(a=StringType())),
readonly=False, default=('', 0, False, (False,), dict(a='')))
def do_arg(self, arg):
@Command(argument=TupleOf(StringType(), FloatRange(), BoolType(), TupleOf(BoolType()), StructOf(a=StringType())),
result=StringType())
def arg(self, *arg):
"""5 args"""
return repr(arg)
def do_keyed(self, arg):
@Command(argument=StructOf(a=StringType(), b=FloatRange(), c=BoolType(), optional=['b']),
result=StringType())
def keyed(self, **arg):
"""keyworded arg"""
return repr(arg)
def do_one(self, arg):
@Command(argument=FloatRange(), result=StringType())
def one(self, arg):
"""1 arg"""
return repr(arg)
def do_none(self):
@Command(result=StringType())
def none(self):
"""no arg"""
return repr(None)

View File

@ -20,31 +20,23 @@
# *****************************************************************************
"""Temp"""
from secop.modules import Readable, Drivable, Parameter, Override
from secop.datatypes import FloatRange, IntRange, StringType
from secop.modules import Drivable, Parameter, Readable
from secop.stringio import HasIodev
Status = Drivable.Status
class TempLoop(HasIodev, Drivable):
'''temperature channel on Lakeshore 336'''
parameters = {
'value':
Override(datatype=FloatRange(unit='K'), default=0, poll=True),
'status':
Override(poll=False),
'target':
Override(datatype=FloatRange(1.0, 402.0, unit='K'), default=1.3, poll=True),
'tolerance':
Parameter('the tolerance', FloatRange(-400,400), default=1, readonly=False),
'pollinterval':
Override(visibility=3),
'channel':
Parameter('the Lakeshore channel', datatype=StringType(), export=False),
'loop':
Parameter('the Lakeshore loop number', datatype=IntRange(1,3), export=False),
}
class TempLoop(HasIodev, Drivable):
"""temperature channel on Lakeshore 336"""
value = Parameter(datatype=FloatRange(unit='K'), default=0, poll=True)
status = Parameter(poll=False)
target = Parameter(datatype=FloatRange(1.0, 402.0, unit='K'), default=1.3, poll=True)
tolerance = Parameter('the tolerance', FloatRange(-400, 400), default=1, readonly=False)
pollinterval = Parameter(visibility=3)
channel = Parameter('the Lakeshore channel', datatype=StringType(), export=False)
loop = Parameter('the Lakeshore loop number', datatype=IntRange(1, 3), export=False)
def earlyInit(self):
super(TempLoop, self).earlyInit()
@ -67,24 +59,18 @@ class TempLoop(HasIodev, Drivable):
float('x')
return result
def do_stop(self):
def stop(self):
self.target = self.value
self.status = [Status.IDLE, 'stopped']
class TempChannel(HasIodev, Readable):
'''temperature channel on Lakeshore 336'''
"""temperature channel on Lakeshore 336"""
parameters = {
'value':
Override(datatype=FloatRange(unit='K'), default=0, poll=True),
'status':
Override(poll=False, constant=[Status.IDLE, 'idle']),
'pollinterval':
Override(visibility=3),
'channel':
Parameter('the Lakeshore channel', datatype=StringType(), export=False),
}
value = Parameter(datatype=FloatRange(unit='K'), default=0, poll=True)
status = Parameter(poll=False, constant=[Status.IDLE, 'idle'])
pollinterval = Parameter(visibility=3)
channel = Parameter('the Lakeshore channel', datatype=StringType(), export=False)
def read_value(self):
result = self.sendRecv('KRDG?%s' % self.channel)

View File

@ -20,17 +20,19 @@
# *****************************************************************************
"""frappy support for ultrasound"""
import math
#import serial
import os
import math
import time
from secop.core import Readable, Parameter, Override, FloatRange, BoolType, StringIO, \
Done, Attached, TupleOf, StringType, IntRange, EnumType, HasIodev, Module
from secop.properties import Property
from adq_mr import Adq
import iqplot
import numpy as np
import iqplot
from adq_mr import Adq
from secop.core import Attached, BoolType, Done, FloatRange, HasIodev, \
IntRange, Module, Parameter, Readable, StringIO, StringType
from secop.properties import Property
def fname_from_time(t, extension):
tm = time.localtime(t)
@ -43,32 +45,27 @@ def fname_from_time(t, extension):
class Roi(Readable):
properties = {
'main': Attached(),
}
parameters = {
'value': Override('amplitude', FloatRange(), default=0),
'phase': Parameter('phase', FloatRange(unit='deg'), default=0),
'i': Parameter('in phase', FloatRange(), default=0),
'q': Parameter('out of phase', FloatRange(), default=0),
'time': Parameter('start time', FloatRange(unit='nsec'),
readonly=False),
'size': Parameter('interval (symmetric around time)', FloatRange(unit='nsec'),
readonly=False),
'enable': Parameter('calculate this roi', BoolType(), readonly=False, default=True),
#'status': Override(export=False),
'pollinterval': Override(export=False),
}
main = Attached()
value = Parameter('amplitude', FloatRange(), default=0)
phase = Parameter('phase', FloatRange(unit='deg'), default=0)
i = Parameter('in phase', FloatRange(), default=0)
q = Parameter('out of phase', FloatRange(), default=0)
time = Parameter('start time', FloatRange(unit='nsec'), readonly=False)
size = Parameter('interval (symmetric around time)', FloatRange(unit='nsec'), readonly=False)
enable = Parameter('calculate this roi', BoolType(), readonly=False, default=True)
#status = Parameter(export=False)
pollinterval = Parameter(export=False)
interval = (0,0)
def initModule(self):
self._main.register_roi(self)
self.calc_interval()
def calc_interval(self):
self.interval = (self.time - 0.5 * self.size, self.time + 0.5 * self.size)
def write_time(self, value):
self.time = value
self.calc_interval()
@ -83,53 +80,49 @@ class Roi(Readable):
class Pars(Module):
description = 'relevant parameters from SEA'
parameters = {
'timestamp': Parameter('unix timestamp', StringType(), default='0', readonly=False),
'temperature': Parameter('T', FloatRange(unit='K'), default=0, readonly=False),
'mf': Parameter('field', FloatRange(unit='T'), default=0, readonly=False),
'sr': Parameter('rotaion angle', FloatRange(unit='deg'), default=0, readonly=False),
}
timestamp = Parameter('unix timestamp', StringType(), default='0', readonly=False)
temperature = Parameter('T', FloatRange(unit='K'), default=0, readonly=False)
mf = Parameter('field', FloatRange(unit='T'), default=0, readonly=False)
sr = Parameter('rotaion angle', FloatRange(unit='deg'), default=0, readonly=False)
class FreqStringIO(StringIO):
end_of_line = '\r'
class Frequency(HasIodev, Readable):
properties = {
'pars': Attached(),
'sr': Property('samples per record', datatype=IntRange(), default=16384),
'maxy': Property('plot y scale', datatype=FloatRange(), default=0.5),
}
parameters = {
'value': Override('frequency@I,q', datatype=FloatRange(unit='Hz'), default=0),
'basefreq': Parameter('base frequency', FloatRange(unit='Hz'), readonly=False),
'nr': Parameter('number of records', datatype=IntRange(1,10000), default=500),
'freq': Parameter('target frequency', FloatRange(unit='Hz'), readonly=False, poll=True),
'amp': Parameter('amplitude', FloatRange(unit='dBm'), readonly=False, poll=True),
'control': Parameter('control loop on?', BoolType(), readonly=False, default=True),
'time': Parameter('pulse start time', FloatRange(unit='nsec'),
readonly=False),
'size': Parameter('pulse length (starting from time)', FloatRange(unit='nsec'),
readonly=False),
'pulselen': Parameter('adjusted pulse length (integer number of periods)', FloatRange(unit='nsec'), default=1),
'maxstep': Parameter('max frequency step', FloatRange(unit='Hz'), readonly=False,
default=10000),
'minstep': Parameter('min frequency step for slope calculation', FloatRange(unit='Hz'),
readonly=False, default=4000),
'slope': Parameter('inphase/frequency slope', FloatRange(), readonly=False,
default=1e6),
'plot': Parameter('create plot images', BoolType(), readonly=False, default=True),
'save': Parameter('save data', BoolType(), readonly=False, default=True),
'pollinterval': Override(datatype=FloatRange(0,120)),
}
pars = Attached()
sr = Property('samples per record', datatype=IntRange(), default=16384)
maxy = Property('plot y scale', datatype=FloatRange(), default=0.5)
value = Parameter('frequency@I,q', datatype=FloatRange(unit='Hz'), default=0)
basefreq = Parameter('base frequency', FloatRange(unit='Hz'), readonly=False)
nr = Parameter('number of records', datatype=IntRange(1,10000), default=500)
freq = Parameter('target frequency', FloatRange(unit='Hz'), readonly=False, poll=True)
amp = Parameter('amplitude', FloatRange(unit='dBm'), readonly=False, poll=True)
control = Parameter('control loop on?', BoolType(), readonly=False, default=True)
time = Parameter('pulse start time', FloatRange(unit='nsec'),
readonly=False)
size = Parameter('pulse length (starting from time)', FloatRange(unit='nsec'),
readonly=False)
pulselen = Parameter('adjusted pulse length (integer number of periods)', FloatRange(unit='nsec'), default=1)
maxstep = Parameter('max frequency step', FloatRange(unit='Hz'), readonly=False,
default=10000)
minstep = Parameter('min frequency step for slope calculation', FloatRange(unit='Hz'),
readonly=False, default=4000)
slope = Parameter('inphase/frequency slope', FloatRange(), readonly=False,
default=1e6)
plot = Parameter('create plot images', BoolType(), readonly=False, default=True)
save = Parameter('save data', BoolType(), readonly=False, default=True)
pollinterval = Parameter(datatype=FloatRange(0,120))
iodevClass = FreqStringIO
lastfreq = None
old = None
starttime = None
interval = (0,0)
def earlyInit(self):
#assert self.iodev.startswith('serial:')
#self._iodev = serial.Serial(self.iodev[7:])
@ -142,30 +135,30 @@ class Frequency(HasIodev, Readable):
def calc_interval(self):
self.interval = (self.time, self.time + self.size)
def write_time(self, value):
self.time = value
self.calc_interval()
return Done
def write_size(self, value):
self.size = value
self.calc_interval()
return Done
def write_nr(self, value):
# self.pollinterval = value * 0.0001
return value
def register_roi(self, roi):
self.roilist.append(roi)
def set_freq(self):
freq = self.freq + self.basefreq
reply = self.sendRecv('FREQ %.15g;FREQ?' % freq)
self.sendRecv('FREQ %.15g;FREQ?' % freq)
#self._iodev.readline().decode('ascii')
return freq
def write_amp(self, amp):
reply = self.sendRecv('AMPR %g;AMPR?' % amp)
return float(reply)
@ -173,11 +166,11 @@ class Frequency(HasIodev, Readable):
def read_amp(self):
reply = self.sendRecv('AMPR?')
return float(reply)
def write_freq(self, value):
self.skipctrl = 2 # suppress control for the 2 next steps
return value
def read_freq(self):
"""used as main polling loop body"""
if self.lastfreq is None:
@ -197,7 +190,7 @@ class Frequency(HasIodev, Readable):
self.adq.start() # start next acq
times.append(('start',time.time()))
roilist = [r for r in self.roilist if r.enable]
gates = self.adq.gates_and_curves(data, freq, self.interval,
[r.interval for r in roilist])
if self.save: