fix new statemachine with ips magfield

+ add on_error, on_restart etc. to states.py
+ add n_retry argument to mercury change/multichange
This commit is contained in:
2022-12-19 15:12:00 +01:00
parent 397ca350f8
commit b7a1f17e5e
4 changed files with 119 additions and 67 deletions

View File

@ -23,9 +23,9 @@ 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.errors import ConfigError, ProgrammingError, HardwareError
from secop.errors import ConfigError, ProgrammingError, HardwareError, BadValueError
from secop.lib.enum import Enum
from secop.states import Retry, HasStates, status_code
from secop.states import Retry, HasStates, status_code, Start
UNLIMITED = FloatRange()
@ -157,54 +157,73 @@ class Magfield(SimpleMagfield):
ramp_tmo = Parameter(
'timeout for field ramp progress',
FloatRange(0, unit='s'), readonly=False, default=30)
__init = True
__init_persistency = True
switch_on_time = None
switch_off_time = None
def doPoll(self):
if self.__init:
self.__init = False
if self.read_switch_heater() and self.mode == Mode.PERSISTENT:
self.read_value() # check for persistent field mismatch
# switch off heater from previous live or manual intervention
self.write_target(self.persistent_field)
else:
if self.__init_persistency:
if self.__init_persistency is True:
self._last_target = self.persistent_field
else:
super().doPoll()
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
super().doPoll()
def initModule(self):
super().initModule()
self.registerCallbacks(self) # for update_switch_heater
def write_mode(self, value):
self.start_machine(self.start_field_change, cleanup=self.cleanup, target=self.target, mode=value)
self.__init_persistency = False
target = self.persistent_field
func = self.start_field_change
if value == Mode.DISABLED:
target = 0
if abs(self.persistent_field) < self.tolerance:
func = self.start_switch_off
elif value == Mode.PERSISTENT:
func = self.start_switch_off
self.start_machine(func, target=target, mode=value)
return value
def write_target(self, target):
self.__init_persistency = False
if self.mode == Mode.DISABLED:
if target == 0:
return 0
self.log.info('raise error %r', target)
raise BadValueError('disabled')
self.check_limits(target)
self.start_machine(self.start_field_change, cleanup=self.cleanup, target=target, mode=self.mode)
self.start_machine(self.start_field_change, target=target, mode=self.mode)
return target
def cleanup(self, sm): # sm is short for statemachine
if self.switch_heater != 0:
def on_error(self, sm): # sm is short for statemachine
if self.switch_heater == ON:
self.persistent_field = self.read_value()
if sm.mode != Mode.DRIVEN:
self.log.warning('turn switch heater off')
self.write_switch_heater(0)
self.write_switch_heater(OFF)
return self.on_error(sm)
@status_code('PREPARING')
@status_code(Status.PREPARING)
def start_field_change(self, sm):
self.setFastPoll(True, 1.0)
if sm.target == self.persistent_field or (
sm.target == self._last_target and
abs(sm.target - self.persistent_field) <= self.tolerance): # short cut
return self.check_switch_off
if self.switch_heater:
if self.switch_heater == ON:
return self.start_switch_on
return self.start_ramp_to_field
@status_code('PREPARING')
@status_code(Status.PREPARING)
def start_ramp_to_field(self, sm):
"""start ramping current to persistent field
@ -213,7 +232,7 @@ class Magfield(SimpleMagfield):
"""
raise NotImplementedError
@status_code('PREPARING', 'ramp leads to match field')
@status_code(Status.PREPARING, 'ramp leads to match field')
def ramp_to_field(self, sm):
if sm.init:
sm.stabilize_start = 0 # in case current is already at field
@ -229,7 +248,7 @@ class Magfield(SimpleMagfield):
sm.stabilize_start = sm.now
return self.stabilize_current
@status_code('PREPARING')
@status_code(Status.PREPARING)
def stabilize_current(self, sm):
if sm.now - sm.stabilize_start < self.wait_stable_leads:
return Retry
@ -237,7 +256,6 @@ class Magfield(SimpleMagfield):
def update_switch_heater(self, value):
"""is called whenever switch heater was changed"""
print('SW', value)
if value == 0:
if self.switch_off_time is None:
self.log.info('restart switch_off_time')
@ -249,12 +267,12 @@ class Magfield(SimpleMagfield):
self.switch_on_time = time.time()
self.switch_off_time = None
@status_code('PREPARING')
@status_code(Status.PREPARING)
def start_switch_on(self, sm):
if self.read_switch_heater() == 0:
if self.read_switch_heater() == OFF:
self.status = Status.PREPARING, 'turn switch heater on'
try:
self.write_switch_heater(True)
self.write_switch_heater(ON)
except Exception as e:
self.log.warning('write_switch_heater %r', e)
return Retry
@ -262,13 +280,15 @@ class Magfield(SimpleMagfield):
self.status = Status.PREPARING, 'wait for heater on'
return self.wait_for_switch_on
@status_code('PREPARING')
@status_code(Status.PREPARING)
def wait_for_switch_on(self, sm):
if (sm.target == self._last_target and
abs(sm.target - self.persistent_field) <= self.tolerance): # short cut
return self.check_switch_off
self.read_switch_heater() # trigger switch_on/off_time
if self.switch_heater == 0:
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:
@ -278,7 +298,7 @@ class Magfield(SimpleMagfield):
self._last_target = sm.target
return self.start_ramp_to_target
@status_code('RAMPING')
@status_code(Status.RAMPING)
def start_ramp_to_target(self, sm):
"""start ramping current to target field
@ -287,7 +307,7 @@ class Magfield(SimpleMagfield):
"""
raise NotImplementedError
@status_code('RAMPING')
@status_code(Status.RAMPING)
def ramp_to_target(self, sm):
self.persistent_field = self.value
dif = abs(self.value - sm.target)
@ -298,6 +318,7 @@ class Magfield(SimpleMagfield):
sm.stabilize_start = sm.now
tdif = self.get_progress(sm, self.value)
if tdif > self.workingramp / self.tolerance * 60 + self.ramp_tmo:
self.log.warn('no progress')
raise HardwareError('no progress')
sm.stabilize_start = None
return Retry
@ -305,10 +326,10 @@ class Magfield(SimpleMagfield):
sm.stabilize_start = sm.now
return self.stabilize_field
@status_code('STABILIZING')
@status_code(Status.STABILIZING)
def stabilize_field(self, sm):
self.persistent_field = self.value
if sm.now > sm.stablize_start + self.wait_stable_field:
if sm.now < sm.stabilize_start + self.wait_stable_field:
return Retry
return self.check_switch_off
@ -317,17 +338,19 @@ class Magfield(SimpleMagfield):
return self.final_status(Status.PREPARED, 'driven')
return self.start_switch_off
@status_code('FINALIZING')
@status_code(Status.FINALIZING)
def start_switch_off(self, sm):
if self.switch_heater == 1:
self.write_switch_heater(False)
if self.switch_heater == ON:
self.write_switch_heater(OFF)
return self.wait_for_switch_off
@status_code('FINALIZING')
@status_code(Status.FINALIZING)
def wait_for_switch_off(self, sm):
self.persistent_field = self.value
self.read_switch_heater()
if self.switch_off_time is None:
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:
@ -336,7 +359,7 @@ class Magfield(SimpleMagfield):
return self.final_status(Status.IDLE, 'leads current at field, switch off')
return self.start_ramp_to_zero
@status_code('FINALIZING')
@status_code(Status.FINALIZING)
def start_ramp_to_zero(self, sm):
"""start ramping current to zero
@ -345,15 +368,15 @@ class Magfield(SimpleMagfield):
"""
raise NotImplementedError
@status_code('FINALIZING')
@status_code(Status.FINALIZING)
def ramp_to_zero(self, sm):
"""ramp field to zero"""
if sm.init:
self.init_progress(sm, self.current)
if abs(self.current) > self.tolerance:
if self.get_progress(sm, self.current, self.ramp) > self.leads_ramp_tmo:
if self.get_progress(sm, self.value) > self.leads_ramp_tmo:
raise HardwareError('no progress')
return Retry
if sm.mode == Mode.DISABLED and self.persistent_field == 0:
if sm.mode == Mode.DISABLED and abs(self.persistent_field) < self.tolerance:
return self.final_status(Status.DISABLED, 'disabled')
return self.final_status(Status.IDLE, 'persistent mode')