mercury, ips, sea, triton, convergence

after gerrit

Change-Id: Iff14047ecc476589aef10c96fae9970133b8bd14
This commit is contained in:
2023-05-09 14:57:34 +02:00
parent 750b5a7794
commit 8039351395
12 changed files with 426 additions and 357 deletions

View File

@ -21,7 +21,7 @@
"""oxford instruments mercury IPS power supply"""
import time
from frappy.core import Parameter, EnumType, FloatRange, BoolType, IntRange, StringType, Property, BUSY
from frappy.core import Parameter, EnumType, FloatRange, BoolType, IntRange, Property, Module
from frappy.lib.enum import Enum
from frappy.errors import BadValueError, HardwareError
from frappy_psi.magfield import Magfield, SimpleMagfield, Status
@ -41,16 +41,16 @@ class SimpleField(MercuryChannel, SimpleMagfield):
voltage = Parameter('leads voltage', FloatRange(unit='V'), default=0)
atob = Parameter('field to amp', FloatRange(0, unit='A/T'), default=0)
working_ramp = Parameter('effective ramp', FloatRange(0, unit='T/min'), default=0)
channel_type = 'PSU'
kind = 'PSU'
slave_currents = None
classdict = {}
def __new__(cls, name, logger, cfgdict, srv):
base = cls.__bases__[1]
def __new__(cls, name, logger, cfgdict, srv): # pylint: disable=arguments-differ
nunits = cfgdict.get('nunits', 1)
if isinstance(nunits, dict):
nunits = nunits['value']
if nunits == 1:
obj = object.__new__(cls)
return obj
return Module.__new__(cls, name, logger, cfgdict, srv)
classname = cls.__name__ + str(nunits)
newclass = cls.classdict.get(classname)
if not newclass:
@ -62,8 +62,7 @@ class SimpleField(MercuryChannel, SimpleMagfield):
newclass = type(classname, (cls,), attrs)
cls.classdict[classname] = newclass
obj = object.__new__(newclass)
return obj
return Module.__new__(newclass, name, logger, cfgdict, srv)
def initModule(self):
super().initModule()
@ -73,34 +72,34 @@ class SimpleField(MercuryChannel, SimpleMagfield):
self.log.error('can not set to hold %r', e)
def read_value(self):
return self.query('PSU:SIG:FLD')
return self.query('DEV::PSU:SIG:FLD')
def read_ramp(self):
return self.query('PSU:SIG:RFST')
return self.query('DEV::PSU:SIG:RFST')
def write_ramp(self, value):
return self.change('PSU:SIG:RFST', value)
return self.change('DEV::PSU:SIG:RFST', value)
def read_action(self):
return self.query('PSU:ACTN', hold_rtoz_rtos_clmp)
return self.query('DEV::PSU:ACTN', hold_rtoz_rtos_clmp)
def write_action(self, value):
return self.change('PSU:ACTN', value, hold_rtoz_rtos_clmp)
return self.change('DEV::PSU:ACTN', value, hold_rtoz_rtos_clmp)
def read_atob(self):
return self.query('PSU:ATOB')
return self.query('DEV::PSU:ATOB')
def read_voltage(self):
return self.query('PSU:SIG:VOLT')
return self.query('DEV::PSU:SIG:VOLT')
def read_working_ramp(self):
return self.query('PSU:SIG:RFLD')
return self.query('DEV::PSU:SIG:RFLD')
def read_setpoint(self):
return self.query('PSU:SIG:FSET')
return self.query('DEV::PSU:SIG:FSET')
def set_and_go(self, value):
self.setpoint = self.change('PSU:SIG:FSET', value)
self.setpoint = self.change('DEV::PSU:SIG:FSET', value)
assert self.write_action(Action.hold) == Action.hold
assert self.write_action(Action.run_to_set) == Action.run_to_set
@ -133,7 +132,7 @@ class SimpleField(MercuryChannel, SimpleMagfield):
class Field(SimpleField, Magfield):
persistent_field = Parameter(
'persistent field', FloatRange(unit='$'), readonly=False)
'persistent field at last switch off', FloatRange(unit='$'), readonly=False)
wait_switch_on = Parameter(
'wait time to ensure switch is on', FloatRange(0, unit='s'), readonly=True, default=60)
wait_switch_off = Parameter(
@ -142,7 +141,7 @@ class Field(SimpleField, Magfield):
'manual indication that persistent field is bad', BoolType(), readonly=False, default=False)
_field_mismatch = None
__init = True
__persistent_field = None # internal value of persistent field
__switch_fixed_until = 0
def doPoll(self):
@ -154,33 +153,41 @@ class Field(SimpleField, Magfield):
# will complain and this will be handled in start_ramp_to_field
self.switch_on_time = 0
self.switch_off_time = 0
self.switch_heater = self.query('PSU:SIG:SWHT', off_on)
self.switch_heater = self.query('DEV::PSU:SIG:SWHT', off_on)
super().startModule(start_events)
def read_value(self):
current = self.query('PSU:SIG:FLD')
pf = self.query('PSU:SIG:PFLD')
if self.__init:
self.__init = False
self.persistent_field = pf
if self.switch_heater == self.switch_heater.on or self._field_mismatch is None:
current = self.query('DEV::PSU:SIG:FLD')
if self.switch_heater == self.switch_heater.on:
self.__persistent_field = current
self.forced_persistent_field = False
self._field_mismatch = False
return current
self._field_mismatch = abs(self.persistent_field - pf) > self.tolerance
return pf
pf = self.query('DEV::PSU:SIG:PFLD')
if self.__persistent_field is None:
self.__persistent_field = pf
self._field_mismatch = False
else:
self._field_mismatch = abs(self.__persistent_field - pf) > self.tolerance * 10
self.persistent_field = self.__persistent_field
return self.__persistent_field
def _check_adr(self, adr):
"""avoid complains about bad slot"""
if adr.startswith('DEV:PSU.M'):
return
super()._check_adr(adr)
def read_current(self):
if self.slave_currents is None:
self.slave_currents = [[] for _ in range(self.nunits + 1)]
if self.nunits > 1:
for i in range(1, self.nunits + 1):
curri = self.query('DEV:PSU.M%d:PSU:SIG:CURR' % i)
volti = self.query('DEV:PSU.M%d:PSU:SIG:VOLT' % i)
setattr(self, 'I%d' % i, curri)
setattr(self, 'V%d' % i, volti)
curri = self.query(f'DEV:PSU.M{i}:PSU:SIG:CURR')
volti = self.query(f'DEV:PSU.M{i}:PSU:SIG:VOLT')
setattr(self, f'I{i}', curri)
setattr(self, f'V{i}', volti)
self.slave_currents[i].append(curri)
current = self.query('PSU:SIG:CURR')
current = self.query('DEV::PSU:SIG:CURR')
self.slave_currents[0].append(current)
min_ = min(self.slave_currents[0]) / self.nunits
max_ = max(self.slave_currents[0]) / self.nunits
@ -194,14 +201,15 @@ class Field(SimpleField, Magfield):
if min_i - 0.1 > max_ or min_ > max_i + 0.1: # use an arbitrary 0.1 A tolerance
self.log.warning('individual currents mismatch %r', self.slave_currents)
else:
current = self.query('PSU:SIG:CURR')
current = self.query('DEV::PSU:SIG:CURR')
if self.atob:
return current / self.atob
return 0
def write_persistent_field(self, value):
if self.forced_persistent_field:
if self.forced_persistent_field or abs(self.__persistent_field - value) <= self.tolerance * 10:
self._field_mismatch = False
self.__persistent_field = value
return value
raise BadValueError('changing persistent field needs forced_persistent_field=True')
@ -212,7 +220,7 @@ class Field(SimpleField, Magfield):
return super().write_target(target)
def read_switch_heater(self):
value = self.query('PSU:SIG:SWHT', off_on)
value = self.query('DEV::PSU:SIG:SWHT', off_on)
now = time.time()
if value != self.switch_heater:
if now < self.__switch_fixed_until:
@ -226,10 +234,10 @@ class Field(SimpleField, Magfield):
return value
def read_wait_switch_on(self):
return self.query('PSU:SWONT') * 0.001
return self.query('DEV::PSU:SWONT') * 0.001
def read_wait_switch_off(self):
return self.query('PSU:SWOFT') * 0.001
return self.query('DEV::PSU:SWOFT') * 0.001
def write_switch_heater(self, value):
if value == self.read_switch_heater():
@ -238,20 +246,20 @@ class Field(SimpleField, Magfield):
return value
self.__switch_fixed_until = time.time() + 10
self.log.debug('switch time fixed for 10 sec')
result = self.change('PSU:SIG:SWHT', value, off_on, n_retry=0) # no readback check
result = self.change('DEV::PSU:SIG:SWHT', value, off_on, n_retry=0) # no readback check
return result
def start_ramp_to_field(self, sm):
if abs(self.current - self.persistent_field) <= self.tolerance:
self.log.info('leads %g are already at %g', self.current, self.persistent_field)
if abs(self.current - self.__persistent_field) <= self.tolerance:
self.log.info('leads %g are already at %g', self.current, self.__persistent_field)
return self.ramp_to_field
try:
self.set_and_go(self.persistent_field)
self.set_and_go(self.__persistent_field)
except (HardwareError, AssertionError) as e:
if self.switch_heater:
self.log.warn('switch is already on!')
return self.ramp_to_field
self.log.warn('wait first for switch off current=%g pf=%g %r', self.current, self.persistent_field, e)
self.log.warn('wait first for switch off current=%g pf=%g %r', self.current, self.__persistent_field, e)
sm.after_wait = self.ramp_to_field
return self.wait_for_switch
return self.ramp_to_field
@ -274,7 +282,7 @@ class Field(SimpleField, Magfield):
sm.try_cnt -= 1
if sm.try_cnt < 0:
raise
self.set_and_go(sm.persistent_field)
self.set_and_go(self.__persistent_field)
return Retry
def wait_for_switch(self, sm):
@ -283,14 +291,14 @@ class Field(SimpleField, Magfield):
try:
self.log.warn('try again')
# try again
self.set_and_go(self.persistent_field)
except (HardwareError, AssertionError) as e:
self.set_and_go(self.__persistent_field)
except (HardwareError, AssertionError):
return Retry
return sm.after_wait
def wait_for_switch_on(self, sm):
self.read_switch_heater() # trigger switch_on/off_time
if self.switch_heater == self.switch_heater.OFF:
if self.switch_heater == self.switch_heater.off:
if sm.init: # avoid too many states chained
return Retry
self.log.warning('switch turned off manually?')
@ -299,7 +307,7 @@ class Field(SimpleField, Magfield):
def wait_for_switch_off(self, sm):
self.read_switch_heater()
if self.switch_heater == self.switch_heater.ON:
if self.switch_heater == self.switch_heater.on:
if sm.init: # avoid too many states chained
return Retry
self.log.warning('switch turned on manually?')
@ -307,6 +315,9 @@ class Field(SimpleField, Magfield):
return super().wait_for_switch_off(sm)
def start_ramp_to_zero(self, sm):
pf = self.query('DEV::PSU:SIG:PFLD')
if abs(pf - self.value) > self.tolerance * 10:
self.log.warning('persistent field %g does not match %g after switch off', pf, self.value)
try:
assert self.write_action(Action.hold) == Action.hold
assert self.write_action(Action.run_to_zero) == Action.run_to_zero