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:
parent
79b8cd7b2d
commit
766f15beee
@ -288,6 +288,24 @@ class Field(SimpleField, Magfield):
|
|||||||
return Retry
|
return Retry
|
||||||
return sm.after_wait
|
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):
|
def start_ramp_to_zero(self, sm):
|
||||||
try:
|
try:
|
||||||
assert self.write_action(Action.hold) == Action.hold
|
assert self.write_action(Action.hold) == Action.hold
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
import time
|
import time
|
||||||
from secop.core import Drivable, Parameter, Done, IDLE, BUSY, ERROR
|
from secop.core import Drivable, Parameter, Done, IDLE, BUSY, ERROR
|
||||||
from secop.datatypes import FloatRange, EnumType, ArrayOf, TupleOf, StatusType
|
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.errors import ConfigError, ProgrammingError, HardwareError, BadValueError
|
||||||
from secop.lib.enum import Enum
|
from secop.lib.enum import Enum
|
||||||
from secop.states import Retry, HasStates, status_code, Start
|
from secop.states import Retry, HasStates, status_code, Start
|
||||||
@ -48,7 +48,7 @@ OFF = 0
|
|||||||
ON = 1
|
ON = 1
|
||||||
|
|
||||||
|
|
||||||
class SimpleMagfield(HasStates, HasLimits, Drivable):
|
class SimpleMagfield(HasStates, HasTargetLimits, Drivable):
|
||||||
value = Parameter('magnetic field', datatype=FloatRange(unit='T'))
|
value = Parameter('magnetic field', datatype=FloatRange(unit='T'))
|
||||||
ramp = Parameter(
|
ramp = Parameter(
|
||||||
'wanted ramp rate for field', FloatRange(unit='$/min'), readonly=False)
|
'wanted ramp rate for field', FloatRange(unit='$/min'), readonly=False)
|
||||||
@ -63,6 +63,9 @@ class SimpleMagfield(HasStates, HasLimits, Drivable):
|
|||||||
readonly=False, default=(0, 0))
|
readonly=False, default=(0, 0))
|
||||||
wait_stable_field = Parameter(
|
wait_stable_field = Parameter(
|
||||||
'wait time to ensure field is stable', FloatRange(0, unit='s'), readonly=False, default=31)
|
'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
|
_last_target = None
|
||||||
|
|
||||||
@ -80,6 +83,23 @@ class SimpleMagfield(HasStates, HasLimits, Drivable):
|
|||||||
# let the state machine do the needed steps to finish
|
# let the state machine do the needed steps to finish
|
||||||
self.write_target(self.value)
|
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):
|
def write_target(self, target):
|
||||||
self.check_limits(target)
|
self.check_limits(target)
|
||||||
self.start_machine(self.start_field_change, target=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)
|
self.init_progress(sm, self.value)
|
||||||
# Remarks: assume there is a ramp limiting feature
|
# Remarks: assume there is a ramp limiting feature
|
||||||
if abs(self.value - sm.target) > self.tolerance:
|
if abs(self.value - sm.target) > self.tolerance:
|
||||||
if self.get_progress(sm, self.value):
|
if self.get_progress(sm, self.value) > self.ramp_tmo:
|
||||||
return Retry
|
|
||||||
raise HardwareError('no progress')
|
raise HardwareError('no progress')
|
||||||
|
sm.stabilize_start = None # force reset
|
||||||
|
return Retry
|
||||||
sm.stabilize_start = time.time()
|
sm.stabilize_start = time.time()
|
||||||
return self.stabilize_field
|
return self.stabilize_field
|
||||||
|
|
||||||
@ -127,7 +148,7 @@ class SimpleMagfield(HasStates, HasLimits, Drivable):
|
|||||||
class Magfield(SimpleMagfield):
|
class Magfield(SimpleMagfield):
|
||||||
status = Parameter(datatype=StatusType(Status))
|
status = Parameter(datatype=StatusType(Status))
|
||||||
mode = Parameter(
|
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),
|
switch_heater = Parameter('switch heater', EnumType(off=OFF, on=ON),
|
||||||
readonly=False, default=0)
|
readonly=False, default=0)
|
||||||
current = Parameter(
|
current = Parameter(
|
||||||
@ -152,26 +173,15 @@ class Magfield(SimpleMagfield):
|
|||||||
leads_ramp_tmo = Parameter(
|
leads_ramp_tmo = Parameter(
|
||||||
'timeout for leads ramp progress',
|
'timeout for leads ramp progress',
|
||||||
FloatRange(0, unit='s'), readonly=False, default=30)
|
FloatRange(0, unit='s'), readonly=False, default=30)
|
||||||
ramp_tmo = Parameter(
|
init_persistency = True
|
||||||
'timeout for field ramp progress',
|
|
||||||
FloatRange(0, unit='s'), readonly=False, default=30)
|
|
||||||
__init_persistency = True
|
|
||||||
switch_on_time = None
|
switch_on_time = None
|
||||||
switch_off_time = None
|
switch_off_time = None
|
||||||
|
|
||||||
def doPoll(self):
|
def doPoll(self):
|
||||||
if self.__init_persistency:
|
if self.init_persistency:
|
||||||
if self.__init_persistency is True:
|
if self.read_switch_heater() and self.mode != Mode.DRIVEN:
|
||||||
self._last_target = self.value
|
self.start_machine(self.go_persistent_soon, mode=self.mode)
|
||||||
self.__init_persistency = time.time() + 60
|
self.init_persistency = False
|
||||||
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
|
|
||||||
super().doPoll()
|
super().doPoll()
|
||||||
|
|
||||||
def initModule(self):
|
def initModule(self):
|
||||||
@ -179,8 +189,8 @@ class Magfield(SimpleMagfield):
|
|||||||
self.registerCallbacks(self) # for update_switch_heater
|
self.registerCallbacks(self) # for update_switch_heater
|
||||||
|
|
||||||
def write_mode(self, value):
|
def write_mode(self, value):
|
||||||
self.__init_persistency = False
|
self.init_persistency = False
|
||||||
target = self.value
|
target = self.last_target()
|
||||||
func = self.start_field_change
|
func = self.start_field_change
|
||||||
if value == Mode.DISABLED:
|
if value == Mode.DISABLED:
|
||||||
target = 0
|
target = 0
|
||||||
@ -188,11 +198,12 @@ class Magfield(SimpleMagfield):
|
|||||||
func = self.start_switch_off
|
func = self.start_switch_off
|
||||||
elif value == Mode.PERSISTENT:
|
elif value == Mode.PERSISTENT:
|
||||||
func = self.start_switch_off
|
func = self.start_switch_off
|
||||||
|
self.target = target
|
||||||
self.start_machine(func, target=target, mode=value)
|
self.start_machine(func, target=target, mode=value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def write_target(self, target):
|
def write_target(self, target):
|
||||||
self.__init_persistency = False
|
self.init_persistency = False
|
||||||
if self.mode == Mode.DISABLED:
|
if self.mode == Mode.DISABLED:
|
||||||
if target == 0:
|
if target == 0:
|
||||||
return 0
|
return 0
|
||||||
@ -208,14 +219,22 @@ class Magfield(SimpleMagfield):
|
|||||||
if sm.mode != Mode.DRIVEN:
|
if sm.mode != Mode.DRIVEN:
|
||||||
self.log.warning('turn switch heater off')
|
self.log.warning('turn switch heater off')
|
||||||
self.write_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)
|
@status_code(Status.PREPARING)
|
||||||
def start_field_change(self, sm):
|
def start_field_change(self, sm):
|
||||||
self.setFastPoll(True, 1.0)
|
self.setFastPoll(True, 1.0)
|
||||||
if sm.target == self.value or (
|
if (sm.target == self.last_target() and
|
||||||
sm.target == self._last_target and
|
abs(sm.target - self.value) <= self.tolerance and
|
||||||
abs(sm.target - self.value) <= self.tolerance): # short cut
|
abs(self.current - self.value) < self.tolerance and
|
||||||
|
(self.mode != Mode.DRIVEN or self.switch_heater == ON)): # short cut
|
||||||
return self.check_switch_off
|
return self.check_switch_off
|
||||||
if self.switch_heater == ON:
|
if self.switch_heater == ON:
|
||||||
return self.start_switch_on
|
return self.start_switch_on
|
||||||
@ -267,28 +286,19 @@ class Magfield(SimpleMagfield):
|
|||||||
|
|
||||||
@status_code(Status.PREPARING)
|
@status_code(Status.PREPARING)
|
||||||
def start_switch_on(self, sm):
|
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:
|
if self.read_switch_heater() == OFF:
|
||||||
self.status = Status.PREPARING, 'turn switch heater on'
|
|
||||||
try:
|
try:
|
||||||
self.write_switch_heater(ON)
|
self.write_switch_heater(ON)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log.warning('write_switch_heater %r', e)
|
self.log.warning('write_switch_heater %r', e)
|
||||||
return Retry
|
return Retry
|
||||||
else:
|
|
||||||
self.status = Status.PREPARING, 'wait for heater on'
|
|
||||||
return self.wait_for_switch_on
|
return self.wait_for_switch_on
|
||||||
|
|
||||||
@status_code(Status.PREPARING)
|
@status_code(Status.PREPARING)
|
||||||
def wait_for_switch_on(self, sm):
|
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.now - self.switch_on_time < self.wait_switch_on:
|
||||||
if sm.delta(10):
|
if sm.delta(10):
|
||||||
self.log.info('waited for %g sec', sm.now - self.switch_on_time)
|
self.log.info('waited for %g sec', sm.now - self.switch_on_time)
|
||||||
@ -342,12 +352,6 @@ class Magfield(SimpleMagfield):
|
|||||||
|
|
||||||
@status_code(Status.FINALIZING)
|
@status_code(Status.FINALIZING)
|
||||||
def wait_for_switch_off(self, sm):
|
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:
|
if sm.now - self.switch_off_time < self.wait_switch_off:
|
||||||
return Retry
|
return Retry
|
||||||
if abs(self.value) > self.persistent_limit:
|
if abs(self.value) > self.persistent_limit:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user