improvements in magfield

- use HasTargetLimits instead of HasLimits
- move ramp_tmo parameter to SompleMagfield
- add last_target method
- fix progress check in SimpleMagfield.ramp_to_target
- better mechanism for setting to persistent mode after restart
- fix switching mode
- fix on_error
- fix condition for shortcut start_field_change -> check_switch_off
- remove direct status updates
- move check for manual switch heater operations to ips_mercury
This commit is contained in:
l_samenv 2022-12-21 10:57:41 +01:00
parent 79b8cd7b2d
commit 766f15beee
2 changed files with 69 additions and 47 deletions

View File

@ -288,6 +288,24 @@ class Field(SimpleField, Magfield):
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 sm.init: # avoid too many states chained
return Retry
self.log.warning('switch turned off manually?')
return self.start_switch_on
return super().wait_for_switch_on(sm)
def wait_for_switch_off(self, sm):
self.read_switch_heater()
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?')
return self.start_switch_off
return super().wait_for_switch_off(sm)
def start_ramp_to_zero(self, sm):
try:
assert self.write_action(Action.hold) == Action.hold

View File

@ -22,7 +22,7 @@
import time
from secop.core import Drivable, Parameter, Done, IDLE, BUSY, ERROR
from secop.datatypes import FloatRange, EnumType, ArrayOf, TupleOf, StatusType
from secop.features import HasLimits
from secop.features import HasTargetLimits
from secop.errors import ConfigError, ProgrammingError, HardwareError, BadValueError
from secop.lib.enum import Enum
from secop.states import Retry, HasStates, status_code, Start
@ -48,7 +48,7 @@ OFF = 0
ON = 1
class SimpleMagfield(HasStates, HasLimits, Drivable):
class SimpleMagfield(HasStates, HasTargetLimits, Drivable):
value = Parameter('magnetic field', datatype=FloatRange(unit='T'))
ramp = Parameter(
'wanted ramp rate for field', FloatRange(unit='$/min'), readonly=False)
@ -63,6 +63,9 @@ class SimpleMagfield(HasStates, HasLimits, Drivable):
readonly=False, default=(0, 0))
wait_stable_field = Parameter(
'wait time to ensure field is stable', FloatRange(0, unit='s'), readonly=False, default=31)
ramp_tmo = Parameter(
'timeout for field ramp progress',
FloatRange(0, unit='s'), readonly=False, default=30)
_last_target = None
@ -80,6 +83,23 @@ class SimpleMagfield(HasStates, HasLimits, Drivable):
# let the state machine do the needed steps to finish
self.write_target(self.value)
def last_target(self):
"""get best known last target
as long as the guessed last target is within tolerance
with repsect to the main value, it is used, as in general
it has better precision
"""
last = self._last_target
if last is None:
try:
last = self.setpoint # get read back from HW, if available
except Exception:
pass
if last is None or abs(last - self.value) > self.tolerance:
return self.value
return last
def write_target(self, target):
self.check_limits(target)
self.start_machine(self.start_field_change, target=target)
@ -108,9 +128,10 @@ class SimpleMagfield(HasStates, HasLimits, Drivable):
self.init_progress(sm, self.value)
# Remarks: assume there is a ramp limiting feature
if abs(self.value - sm.target) > self.tolerance:
if self.get_progress(sm, self.value):
return Retry
if self.get_progress(sm, self.value) > self.ramp_tmo:
raise HardwareError('no progress')
sm.stabilize_start = None # force reset
return Retry
sm.stabilize_start = time.time()
return self.stabilize_field
@ -127,7 +148,7 @@ class SimpleMagfield(HasStates, HasLimits, Drivable):
class Magfield(SimpleMagfield):
status = Parameter(datatype=StatusType(Status))
mode = Parameter(
'persistent mode', EnumType(Mode), readonly=False, default=Mode.PERSISTENT)
'persistent mode', EnumType(Mode), readonly=False, initwrite=False, default=Mode.PERSISTENT)
switch_heater = Parameter('switch heater', EnumType(off=OFF, on=ON),
readonly=False, default=0)
current = Parameter(
@ -152,26 +173,15 @@ class Magfield(SimpleMagfield):
leads_ramp_tmo = Parameter(
'timeout for leads ramp progress',
FloatRange(0, unit='s'), readonly=False, default=30)
ramp_tmo = Parameter(
'timeout for field ramp progress',
FloatRange(0, unit='s'), readonly=False, default=30)
__init_persistency = True
init_persistency = True
switch_on_time = None
switch_off_time = None
def doPoll(self):
if self.__init_persistency:
if self.__init_persistency is True:
self._last_target = self.value
self.__init_persistency = time.time() + 60
self.read_value() # check for persistent field mismatch
elif self.read_switch_heater() and self.mode != Mode.DRIVEN:
if time.time() > self.__init_persistency:
# switch off heater from previous live or manual intervention
self.log.info('fix mode after startup')
self.write_mode(self.mode)
else:
self.__init_persistency = False
if self.init_persistency:
if self.read_switch_heater() and self.mode != Mode.DRIVEN:
self.start_machine(self.go_persistent_soon, mode=self.mode)
self.init_persistency = False
super().doPoll()
def initModule(self):
@ -179,8 +189,8 @@ class Magfield(SimpleMagfield):
self.registerCallbacks(self) # for update_switch_heater
def write_mode(self, value):
self.__init_persistency = False
target = self.value
self.init_persistency = False
target = self.last_target()
func = self.start_field_change
if value == Mode.DISABLED:
target = 0
@ -188,11 +198,12 @@ class Magfield(SimpleMagfield):
func = self.start_switch_off
elif value == Mode.PERSISTENT:
func = self.start_switch_off
self.target = target
self.start_machine(func, target=target, mode=value)
return value
def write_target(self, target):
self.__init_persistency = False
self.init_persistency = False
if self.mode == Mode.DISABLED:
if target == 0:
return 0
@ -208,14 +219,22 @@ class Magfield(SimpleMagfield):
if sm.mode != Mode.DRIVEN:
self.log.warning('turn switch heater off')
self.write_switch_heater(OFF)
return self.on_error(sm)
return super().on_error(sm)
@status_code(Status.WARN)
def go_persistent_soon(self, sm):
if sm.delta(60):
self.target = sm.target = self.last_target()
return self.start_field_change
return Retry
@status_code(Status.PREPARING)
def start_field_change(self, sm):
self.setFastPoll(True, 1.0)
if sm.target == self.value or (
sm.target == self._last_target and
abs(sm.target - self.value) <= self.tolerance): # short cut
if (sm.target == self.last_target() and
abs(sm.target - self.value) <= self.tolerance and
abs(self.current - self.value) < self.tolerance and
(self.mode != Mode.DRIVEN or self.switch_heater == ON)): # short cut
return self.check_switch_off
if self.switch_heater == ON:
return self.start_switch_on
@ -267,28 +286,19 @@ class Magfield(SimpleMagfield):
@status_code(Status.PREPARING)
def start_switch_on(self, sm):
if (sm.target == self.last_target() and
abs(sm.target - self.value) <= self.tolerance): # short cut
return self.check_switch_off
if self.read_switch_heater() == OFF:
self.status = Status.PREPARING, 'turn switch heater on'
try:
self.write_switch_heater(ON)
except Exception as e:
self.log.warning('write_switch_heater %r', e)
return Retry
else:
self.status = Status.PREPARING, 'wait for heater on'
return self.wait_for_switch_on
@status_code(Status.PREPARING)
def wait_for_switch_on(self, sm):
if (sm.target == self._last_target and
abs(sm.target - self.value) <= self.tolerance): # short cut
return self.check_switch_off
self.read_switch_heater() # trigger switch_on/off_time
if self.switch_heater == OFF:
if sm.init: # avoid too many states chained
return Retry
self.log.warning('switch turned off manually?')
return self.start_switch_on
if sm.now - self.switch_on_time < self.wait_switch_on:
if sm.delta(10):
self.log.info('waited for %g sec', sm.now - self.switch_on_time)
@ -342,12 +352,6 @@ class Magfield(SimpleMagfield):
@status_code(Status.FINALIZING)
def wait_for_switch_off(self, sm):
self.read_switch_heater()
if self.switch_heater == ON:
if sm.init: # avoid too many states chained
return Retry
self.log.warning('switch turned on manually?')
return self.start_switch_off
if sm.now - self.switch_off_time < self.wait_switch_off:
return Retry
if abs(self.value) > self.persistent_limit: