make return value 'Done' unneccessary

'Done' was introduced in order to suppress unneccessary
duplicate updates. However, since super calls on access methods are
allowed, it is not nice when such a method returns Done, as this
is not automagically replaced by the current parameter value.
As a consequence:

- using Done is discouraged, but not (yet) removed in all code
- the 'omit_unchanged_within' property is moved from Module to an
  internal Parameter property 'update_unchanged'
- its default is moved from a SEC node property to generalConfig
- the 'update_unchanged' parameter property may be set to
  'never' for parameters where duplicate updates make no sense
- this property might be set to 'always', for measurements, where
  even unchanged values taken from HW should be transmitted

Change-Id: I2847c983ca09c2c4098e402edd08d0c96c3913f4
Reviewed-on: https://forge.frm2.tum.de/review/c/secop/frappy/+/30672
Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de>
Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
2023-03-13 17:16:07 +01:00
parent 9cab6670b9
commit 0d265b9752
22 changed files with 188 additions and 119 deletions

View File

@@ -20,7 +20,7 @@
# *****************************************************************************
"""Andeen Hagerling capacitance bridge"""
from frappy.core import Done, FloatRange, HasIO, Parameter, Readable, StringIO, nopoll
from frappy.core import FloatRange, HasIO, Parameter, Readable, StringIO, nopoll
class Ah2700IO(StringIO):
@@ -43,7 +43,7 @@ class Capacitance(HasIO, Readable):
reply = self.communicate('SI')
if not reply.startswith('F='): # this is probably an error message like "LOSS TOO HIGH"
self.status = [self.Status.ERROR, reply]
return
return self.value
self.status = [self.Status.IDLE, '']
# examples of replies:
# 'F= 1000.0 HZ C= 0.000001 PF L> 0.0 DS V= 15.0 V'
@@ -55,7 +55,6 @@ class Capacitance(HasIO, Readable):
_, freq, _, _, cap, _, _, loss, lossunit, _, volt = reply[:11]
self.freq = freq
self.voltage = volt
self.value = cap
if lossunit == 'DS':
self.loss = loss
else: # the unit was wrong, we want DS = tan(delta), not NS = nanoSiemens
@@ -64,30 +63,30 @@ class Capacitance(HasIO, Readable):
self.loss = reply[7]
except IndexError:
pass # don't worry, loss will be updated next time
return cap
def read_value(self):
self.parse_reply(self.communicate('SI')) # SI = single trigger
return Done
return self.parse_reply(self.communicate('SI')) # SI = single trigger
@nopoll
def read_freq(self):
self.read_value()
return Done
return self.freq
@nopoll
def read_loss(self):
self.read_value()
return Done
return self.loss
@nopoll
def read_voltage(self):
self.read_value()
return Done
return self.voltage
def write_freq(self, value):
self.parse_reply(self.communicate('FR %g;SI' % value))
return Done
self.value = self.parse_reply(self.communicate('FR %g;SI' % value))
return self.freq
def write_voltage(self, value):
self.parse_reply(self.communicate('V %g;SI' % value))
return Done
self.value = self.parse_reply(self.communicate('V %g;SI' % value))
return self.voltage

View File

@@ -39,7 +39,7 @@ switcher=sw
import time
from frappy.datatypes import IntRange, BoolType, FloatRange
from frappy.core import Attached, Property, Drivable, Parameter, Readable, Done
from frappy.core import Attached, Property, Drivable, Parameter, Readable
class ChannelSwitcher(Drivable):
@@ -55,6 +55,7 @@ class ChannelSwitcher(Drivable):
"""
value = Parameter('the current channel number', IntRange(), needscfg=False)
target = Parameter('channel to select', IntRange(), needscfg=False)
status = Parameter(update_unchanged='never')
autoscan = Parameter('whether to scan automatically',
BoolType(), readonly=False, default=True)
pollinterval = Parameter(default=1, export=False)
@@ -108,7 +109,7 @@ class ChannelSwitcher(Drivable):
if self.status[0] == 'BUSY':
chan = self._channels[self.target]
if chan.is_switching(now, self._start_switch, self.switch_delay):
return Done
return self.status
self.setFastPoll(False)
self.status = 'IDLE', 'measure'
self.value = self.target
@@ -116,7 +117,7 @@ class ChannelSwitcher(Drivable):
chan.read_value()
chan.read_status()
if self.measure_delay > self._time_tol:
return Done
return self.status
else:
chan = self._channels[self.value]
self.read_value() # this might modify autoscan or deadline!
@@ -129,7 +130,7 @@ class ChannelSwitcher(Drivable):
chan.read_status()
self._last_measure = next_measure
if not self.autoscan or now + self._time_tol < self._start_measure + self.measure_delay:
return Done
return self.status
next_channel = self.next_channel(self.value)
if next_channel == self.value:
return 'IDLE', 'single channel'

View File

@@ -26,7 +26,7 @@ import re
import time
from frappy.core import Drivable, HasIO, Writable, \
Parameter, Property, Readable, StringIO, Attached, Done, IDLE, nopoll
Parameter, Property, Readable, StringIO, Attached, IDLE, nopoll
from frappy.datatypes import EnumType, FloatRange, StringType, StructOf, BoolType
from frappy.errors import HardwareError
from frappy_psi.convergence import HasConvergence
@@ -70,7 +70,7 @@ class MercuryChannel(HasIO):
example: DB6.T1,DB1.H1
slot ids for sensor (and control output)''',
StringType())
channel_name = Parameter('mercury nick name', StringType(), default='')
channel_name = Parameter('mercury nick name', StringType(), default='', update_unchanged='never')
channel_type = '' #: channel type(s) for sensor (and control) e.g. TEMP,HTR or PRES,AUX
def _complete_adr(self, adr):
@@ -158,7 +158,7 @@ class MercuryChannel(HasIO):
def read_channel_name(self):
if self.channel_name:
return Done # channel name will not change
return self.channel_name # channel name will not change
return self.query('0:NICK', as_string)
@@ -189,14 +189,14 @@ class HasInput(MercuryChannel):
def write_controlled_by(self, value):
if self.controlled_by == value:
return Done
return value
self.controlled_by = value
if value == SELF:
self.log.warning('switch to manual mode')
for input_module in self.input_modules:
if input_module.control_active:
input_module.write_control_active(False)
return Done
return value
class Loop(HasConvergence, MercuryChannel, Drivable):
@@ -261,7 +261,8 @@ class HeaterOutput(HasInput, MercuryChannel, Writable):
"""
channel_type = 'HTR'
value = Parameter('heater output', FloatRange(unit='W'), readonly=False)
target = Parameter('heater output', FloatRange(0, 100, unit='$'), readonly=False)
status = Parameter(update_unchanged='never')
target = Parameter('heater output', FloatRange(0, 100, unit='$'), readonly=False, update_unchanged='never')
resistivity = Parameter('heater resistivity', FloatRange(10, 1000, unit='Ohm'),
readonly=False)
true_power = Parameter('calculate power from measured current', BoolType(), readonly=False, default=True)
@@ -288,13 +289,10 @@ class HeaterOutput(HasInput, MercuryChannel, Writable):
if not self.true_power:
self._volt_target = math.sqrt(self._last_target * self.resistivity)
self.change('HTR:SIG:VOLT', self._volt_target)
return Done
return self.resistivity
def read_status(self):
status = IDLE, ('true power' if self.true_power else 'fixed resistivity')
if self.status != status:
return status
return Done
return IDLE, ('true power' if self.true_power else 'fixed resistivity')
def read_value(self):
if self._last_target is None: # on init
@@ -317,7 +315,7 @@ class HeaterOutput(HasInput, MercuryChannel, Writable):
if self.controlled_by != 0 and self.target:
return 0
if self._last_target is not None:
return Done
return self.target
self._volt_target = self.query('HTR:SIG:VOLT')
self.resistivity = max(10, self.query('HTR:RES'))
self._last_target = self._volt_target ** 2 / max(10, self.resistivity)
@@ -389,7 +387,7 @@ class TemperatureLoop(TemperatureSensor, Loop, Drivable):
self.set_target(value)
else:
self.set_target(target)
return Done
return self.target
def read_enable_ramp(self):
return self.query('TEMP:LOOP:RENA', off_on)
@@ -472,7 +470,7 @@ class PressureLoop(PressureSensor, Loop, Drivable):
def write_target(self, value):
target = self.change('PRES:LOOP:PRST', value)
self.set_target(target)
return Done
return self.target
class HeLevel(MercuryChannel, Readable):

View File

@@ -44,7 +44,7 @@ lowspeed = 50 # speed for final closing / reference run
"""
from frappy.core import Drivable, Parameter, EnumType, Attached, FloatRange, \
Command, IDLE, BUSY, WARN, ERROR, Done, PersistentParam, PersistentMixin
Command, IDLE, BUSY, WARN, ERROR, PersistentParam, PersistentMixin
from frappy.errors import HardwareError
from frappy_psi.trinamic import Motor
from frappy.lib.statemachine import StateMachine, Retry, Stop
@@ -53,7 +53,7 @@ from frappy.lib.statemachine import StateMachine, Retry, Stop
class MotorValve(PersistentMixin, Drivable):
motor = Attached(Motor)
value = Parameter('current state', EnumType(
closed=0, opened=1, undefined=-1), default=-1)
closed=0, opened=1, undefined=-1), default=-1, update_unchanged='never')
target = Parameter('target state', EnumType(close=0, open=1))
turns = Parameter('number of turns to open', FloatRange(), readonly=False, group='settings')
speed = Parameter('speed for far moves', FloatRange(), readonly=False, group='settings')
@@ -75,7 +75,7 @@ class MotorValve(PersistentMixin, Drivable):
raise HardwareError('%s: need refrun' % self.status[1])
self.target = target
self._state.start(self.goto_target, count=3)
return Done
return self.target
def goto_target(self, state):
self.value = 'undefined'
@@ -90,7 +90,7 @@ class MotorValve(PersistentMixin, Drivable):
if self.status[0] == ERROR:
return 'undefined'
if self.motor.isBusy():
return Done
return self.value
motpos = self.motor.read_value()
if self.motor.read_home():
if motpos > 360:

View File

@@ -40,7 +40,7 @@ from frappy.datatypes import BoolType, EnumType, \
from frappy.errors import HardwareError
from frappy.lib import clamp
from frappy.lib.enum import Enum
from frappy.modules import Communicator, Done, \
from frappy.modules import Communicator, \
Drivable, Parameter, Property, Readable
from frappy.io import HasIO
from frappy.rwhandler import CommonReadHandler, CommonWriteHandler
@@ -478,16 +478,14 @@ class Temp(PpmsDrivable):
def write_approachmode(self, value):
if self.isDriving():
self._write_params(self.setpoint, self.ramp, value)
return Done
self.approachmode = value
return Done # do not execute TEMP command, as this would trigger an unnecessary T change
return self.approachmode
return value # do not execute TEMP command, as this would trigger an unnecessary T change
def write_ramp(self, value):
if self.isDriving():
self._write_params(self.setpoint, value, self.approachmode)
return Done
self.ramp = value
return Done # do not execute TEMP command, as this would trigger an unnecessary T change
return self.ramp
return value # do not execute TEMP command, as this would trigger an unnecessary T change
def calc_expected(self, target, ramp):
self._expected_target_time = time.time() + abs(target - self.value) * 60.0 / max(0.1, ramp)
@@ -606,7 +604,7 @@ class Field(PpmsDrivable):
self._last_change = time.time()
self.status = (self.Status.BUSY, 'changed target')
self._write_params(target, self.ramp, self.approachmode, self.persistentmode)
return Done
return self.target
def write_persistentmode(self, mode):
if abs(self.target - self.value) <= 2e-5 and mode == self.persistentmode:
@@ -617,20 +615,18 @@ class Field(PpmsDrivable):
self._stopped = False
self.status = (self.Status.BUSY, 'changed persistent mode')
self._write_params(self.target, self.ramp, self.approachmode, mode)
return Done
return self.persistentmode
def write_ramp(self, value):
self.ramp = value
if self.isDriving():
self._write_params(self.target, value, self.approachmode, self.persistentmode)
return Done
return None # do not execute FIELD command, as this would trigger a ramp up of leads current
return self.ramp
return value # do not execute FIELD command, as this would trigger a ramp up of leads current
def write_approachmode(self, value):
if self.isDriving():
self._write_params(self.target, self.ramp, value, self.persistentmode)
return Done
return None # do not execute FIELD command, as this would trigger a ramp up of leads current
# do not execute FIELD command, as this would trigger a ramp up of leads current
def stop(self):
if not self.isDriving():
@@ -728,14 +724,13 @@ class Position(PpmsDrivable):
self._status_before_change = self.status
self.status = (self.Status.BUSY, 'changed target')
self._write_params(target, self.speed)
return Done
return self.target
def write_speed(self, value):
if self.isDriving():
self._write_params(self.target, value)
return Done
self.speed = value
return None # do not execute MOVE command, as this would trigger an unnecessary move
return self.speed
return value # do not execute MOVE command, as this would trigger an unnecessary move
def stop(self):
if not self.isDriving():

View File

@@ -28,7 +28,7 @@ import numpy as np
from scipy.interpolate import splev, splrep # pylint: disable=import-error
from frappy.core import Attached, BoolType, Parameter, Readable, StringType, \
FloatRange, Done
FloatRange
def linear(x):
@@ -95,7 +95,7 @@ class Parser340(StdParser):
KINDS = {
"340": (Parser340, {}), # lakeshore 340 format
"inp": (StdParser, {}), # M. Zollikers *.inp calcurve format
"caldat": (StdParser, dict(x=1, y=2)), # format from sea/tcl/startup/calib_ext.tcl
"caldat": (StdParser, {'x': 1, 'y': 2}), # format from sea/tcl/startup/calib_ext.tcl
"dat": (StdParser, {}), # lakeshore raw data *.dat format
}
@@ -179,7 +179,7 @@ class Sensor(Readable):
abs = Parameter('True: take abs(raw) before calib', datatype=BoolType(), readonly=False, default=True)
value = Parameter(datatype=FloatRange(unit='K'))
pollinterval = Parameter(export=False)
status = Parameter(default=(Readable.Status.ERROR, 'unintialized'))
status = Parameter(default=(Readable.Status.ERROR, 'unintialized'), update_unchanged='never')
description = 'a calibrated sensor value'
_value_error = None
@@ -227,4 +227,4 @@ class Sensor(Readable):
def read_status(self):
self.update_status(self.rawsensor.status)
return Done
return self.status