Merge branch 'wip' of gitlab.psi.ch-samenv:samenv/frappy into wip

This commit is contained in:
zolliker 2023-05-31 08:43:19 +02:00
commit 726665ebd8
8 changed files with 397 additions and 70 deletions

20
cfg/main/haakeuro_cfg.py Normal file
View File

@ -0,0 +1,20 @@
Node(
description = '''Haake thermostat + Eurotherm controller''',
id = haakeuro.config.sea.psi.ch,
)
Mod('sea_main',
'frappy_psi.sea.SeaClient',
'main sea connection for haakeuro.config',
config = 'haakeuro.config',
service = 'main',
)
Mod('th',
'frappy_psi.sea.SeaDrivable', '',
io = 'sea_main',
sea_object = 'th',
)
Mod('te',
'frappy_psi.sea.SeaDrivable', '',
io = 'sea_main',
sea_object = 'te',
)

View File

@ -0,0 +1,160 @@
{"th": {"base": "/th", "params": [
{"path": "", "type": "float", "readonly": false, "cmd": "run th", "kids": 26},
{"path": "unit", "type": "text", "readonly": false, "cmd": "th unit", "visibility": 3},
{"path": "t2", "type": "float", "visibility": 3},
{"path": "set", "type": "float"},
{"path": "running", "type": "int", "readonly": false, "cmd": "th running", "visibility": 3},
{"path": "extcontrol", "type": "int", "readonly": false, "cmd": "th extcontrol", "visibility": 3},
{"path": "relais", "type": "int", "visibility": 3},
{"path": "overtemp", "type": "int", "visibility": 3},
{"path": "lowlevel", "type": "int", "visibility": 3},
{"path": "pumpalarm", "type": "int", "visibility": 3},
{"path": "externalarm", "type": "int", "visibility": 3},
{"path": "coolalarm", "type": "int", "visibility": 3},
{"path": "sensor1alarm", "type": "int", "visibility": 3},
{"path": "sensor2alarm", "type": "int", "visibility": 3},
{"path": "reset", "type": "int", "readonly": false, "cmd": "th reset", "visibility": 3},
{"path": "with2sensors", "type": "int", "readonly": false, "cmd": "th with2sensors", "visibility": 3},
{"path": "upperLimit", "type": "float", "readonly": false, "cmd": "th upperLimit"},
{"path": "lowerLimit", "type": "float", "readonly": false, "cmd": "th lowerLimit"},
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "th tolerance"},
{"path": "maxwait", "type": "int", "readonly": false, "cmd": "th maxwait"},
{"path": "settle", "type": "int", "readonly": false, "cmd": "th settle"},
{"path": "targetValue", "type": "float"},
{"path": "is_running", "type": "int", "visibility": 3},
{"path": "verbose", "type": "int", "readonly": false, "cmd": "th verbose", "visibility": 3},
{"path": "driver", "type": "text", "visibility": 3},
{"path": "creationCmd", "type": "text", "visibility": 3},
{"path": "status", "type": "text", "readonly": false, "cmd": "th status"}]},
"te": {"base": "/te", "params": [
{"path": "", "type": "float", "readonly": false, "cmd": "run te", "kids": 30},
{"path": "unit", "type": "text", "readonly": false, "cmd": "te unit", "visibility": 3},
{"path": "mode", "type": "int", "readonly": false, "cmd": "te mode"},
{"path": "model", "type": "text", "visibility": 3},
{"path": "pbPow", "type": "float", "visibility": 3},
{"path": "pbMin", "type": "float", "visibility": 3},
{"path": "pbScl", "type": "float", "visibility": 3},
{"path": "output", "type": "float"},
{"path": "position", "type": "float", "readonly": false, "cmd": "te position"},
{"path": "asymmetry", "type": "float", "readonly": false, "cmd": "te asymmetry", "visibility": 3},
{"path": "range", "type": "float", "readonly": false, "cmd": "te range", "visibility": 3},
{"path": "set", "type": "float", "readonly": false, "cmd": "te set"},
{"path": "rdonly", "type": "int", "readonly": false, "cmd": "te rdonly", "visibility": 3},
{"path": "task", "type": "text", "readonly": false, "cmd": "te task"},
{"path": "upperLimit", "type": "float", "readonly": false, "cmd": "te upperLimit"},
{"path": "lowerLimit", "type": "float", "readonly": false, "cmd": "te lowerLimit", "visibility": 3},
{"path": "tolerance", "type": "float", "readonly": false, "cmd": "te tolerance"},
{"path": "maxwait", "type": "int", "readonly": false, "cmd": "te maxwait"},
{"path": "settle", "type": "int", "readonly": false, "cmd": "te settle"},
{"path": "targetValue", "type": "float"},
{"path": "is_running", "type": "int", "visibility": 3},
{"path": "verbose", "type": "int", "readonly": false, "cmd": "te verbose", "visibility": 3},
{"path": "driver", "type": "text", "visibility": 3},
{"path": "creationCmd", "type": "text", "visibility": 3},
{"path": "status", "type": "text", "readonly": false, "cmd": "te status"},
{"path": "pb", "type": "float", "readonly": false, "cmd": "te pb"},
{"path": "ti", "type": "float", "readonly": false, "cmd": "te ti"},
{"path": "td", "type": "float", "readonly": false, "cmd": "te td"},
{"path": "manual", "type": "float", "readonly": false, "cmd": "te manual"},
{"path": "rate", "type": "float", "readonly": false, "cmd": "te rate"},
{"path": "workset", "type": "float", "readonly": false, "cmd": "te workset"}]},
"cc": {"base": "/cc", "params": [
{"path": "", "type": "bool", "kids": 96},
{"path": "send", "type": "text", "readonly": false, "cmd": "cc send", "visibility": 3},
{"path": "status", "type": "text", "visibility": 3},
{"path": "autodevice", "type": "bool", "readonly": false, "cmd": "cc autodevice"},
{"path": "fav", "type": "bool", "readonly": false, "cmd": "cc fav"},
{"path": "f", "type": "float", "visibility": 3},
{"path": "fs", "type": "enum", "enum": {"ok": 0, "no_sens": 1}, "readonly": false, "cmd": "cc fs", "visibility": 3},
{"path": "mav", "type": "bool", "readonly": false, "cmd": "cc mav"},
{"path": "fm", "type": "enum", "enum": {"idle": 0, "opening": 1, "closing": 2, "opened": 3, "closed": 4, "no_motor": 5}, "visibility": 3},
{"path": "fa", "type": "enum", "enum": {"fixed": 0, "controlled": 1, "automatic": 2, "offline": 3}, "readonly": false, "cmd": "cc fa", "visibility": 3},
{"path": "mp", "type": "float", "readonly": false, "cmd": "cc mp", "visibility": 3},
{"path": "msp", "type": "float", "visibility": 3},
{"path": "mmp", "type": "float", "visibility": 3},
{"path": "mc", "type": "float", "readonly": false, "cmd": "cc mc", "visibility": 3},
{"path": "mfc", "type": "float", "readonly": false, "cmd": "cc mfc", "visibility": 3},
{"path": "moc", "type": "float", "readonly": false, "cmd": "cc moc", "visibility": 3},
{"path": "mtc", "type": "float", "readonly": false, "cmd": "cc mtc", "visibility": 3},
{"path": "mtl", "type": "float", "visibility": 3},
{"path": "mft", "type": "float", "readonly": false, "cmd": "cc mft", "visibility": 3},
{"path": "mt", "type": "float", "visibility": 3},
{"path": "mo", "type": "float", "visibility": 3},
{"path": "mcr", "type": "float", "visibility": 3},
{"path": "mot", "type": "float", "visibility": 3},
{"path": "mw", "type": "float", "readonly": false, "cmd": "cc mw", "description": "correction pulse after automatic open", "visibility": 3},
{"path": "hav", "type": "bool", "readonly": false, "cmd": "cc hav"},
{"path": "h", "type": "float", "visibility": 3},
{"path": "hr", "type": "float", "visibility": 3},
{"path": "hc", "type": "float", "visibility": 3},
{"path": "hu", "type": "float", "visibility": 3},
{"path": "hh", "type": "float", "readonly": false, "cmd": "cc hh", "visibility": 3},
{"path": "hl", "type": "float", "readonly": false, "cmd": "cc hl", "visibility": 3},
{"path": "htf", "type": "float", "readonly": false, "cmd": "cc htf", "description": "meas. period in fast mode", "visibility": 3},
{"path": "hts", "type": "float", "readonly": false, "cmd": "cc hts", "description": "meas. period in slow mode", "visibility": 3},
{"path": "hd", "type": "float", "readonly": false, "cmd": "cc hd", "visibility": 3},
{"path": "hwr", "type": "float", "readonly": false, "cmd": "cc hwr", "visibility": 3},
{"path": "hem", "type": "float", "readonly": false, "cmd": "cc hem", "description": "sensor length in mm from top to empty pos.", "visibility": 3},
{"path": "hfu", "type": "float", "readonly": false, "cmd": "cc hfu", "description": "sensor length in mm from top to full pos.", "visibility": 3},
{"path": "hcd", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3, "manual": 7}, "readonly": false, "cmd": "cc hcd", "visibility": 3},
{"path": "hv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4}, "visibility": 3},
{"path": "hsf", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "ha", "type": "bool", "readonly": false, "cmd": "cc ha", "visibility": 3},
{"path": "hm", "type": "bool", "visibility": 3},
{"path": "hf", "type": "enum", "enum": {"slow": 0, "fast": 1}, "readonly": false, "cmd": "cc hf", "visibility": 3},
{"path": "hbe", "type": "bool", "readonly": false, "cmd": "cc hbe", "visibility": 3},
{"path": "hmf", "type": "float", "visibility": 3},
{"path": "hms", "type": "float", "visibility": 3},
{"path": "hit", "type": "float", "readonly": false, "cmd": "cc hit", "visibility": 3},
{"path": "hft", "type": "int", "readonly": false, "cmd": "cc hft", "visibility": 3},
{"path": "hea", "type": "enum", "enum": {"0": 0, "1": 1, "6": 6}, "readonly": false, "cmd": "cc hea"},
{"path": "hch", "type": "int", "readonly": false, "cmd": "cc hch", "visibility": 3},
{"path": "hwr0", "type": "float", "readonly": false, "cmd": "cc hwr0", "visibility": 3},
{"path": "hem0", "type": "float", "readonly": false, "cmd": "cc hem0", "description": "sensor length in mm from top to empty pos.", "visibility": 3},
{"path": "hfu0", "type": "float", "readonly": false, "cmd": "cc hfu0", "description": "sensor length in mm from top to full pos.", "visibility": 3},
{"path": "hd0", "type": "float", "readonly": false, "cmd": "cc hd0", "description": "external sensor drive current (mA)", "visibility": 3},
{"path": "h0", "type": "float", "visibility": 3},
{"path": "hs0", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "h1", "type": "float", "visibility": 3},
{"path": "hs1", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "h2", "type": "float", "visibility": 3},
{"path": "hs2", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "h3", "type": "float", "visibility": 3},
{"path": "hs3", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "h4", "type": "float", "visibility": 3},
{"path": "hs4", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "h5", "type": "float", "visibility": 3},
{"path": "hs5", "type": "enum", "enum": {"sens_ok": 0, "sens_warm": 1, "no_sens": 2, "timeout": 3, "not_yet_read": 4, "disabled": 5}, "visibility": 3},
{"path": "hfb", "type": "float", "visibility": 3},
{"path": "nav", "type": "bool", "readonly": false, "cmd": "cc nav"},
{"path": "nu", "type": "float", "visibility": 3},
{"path": "nl", "type": "float", "visibility": 3},
{"path": "nth", "type": "float", "readonly": false, "cmd": "cc nth", "visibility": 3},
{"path": "ntc", "type": "float", "readonly": false, "cmd": "cc ntc", "visibility": 3},
{"path": "ntm", "type": "float", "readonly": false, "cmd": "cc ntm", "visibility": 3},
{"path": "ns", "type": "enum", "enum": {"sens_ok": 0, "no_sens": 1, "short_circuit": 2, "upside_down": 3, "sens_warm": 4, "empty": 5}, "visibility": 3},
{"path": "na", "type": "bool", "readonly": false, "cmd": "cc na", "visibility": 3},
{"path": "nv", "type": "enum", "enum": {"fill_valve_off": 0, "filling": 1, "no_fill_valve": 2, "timeout": 3, "timeout1": 4, "boost": 5}, "visibility": 3},
{"path": "nc", "type": "enum", "enum": {"stop": 0, "fill": 1, "off": 2, "auto": 3}, "readonly": false, "cmd": "cc nc", "visibility": 3},
{"path": "nfb", "type": "float", "visibility": 3},
{"path": "cda", "type": "float"},
{"path": "cdb", "type": "float"},
{"path": "cba", "type": "float"},
{"path": "cbb", "type": "float"},
{"path": "cvs", "type": "int"},
{"path": "csp", "type": "int"},
{"path": "cdv", "type": "text", "readonly": false, "cmd": "cc cdv"},
{"path": "cic", "type": "text", "readonly": false, "cmd": "cc cic"},
{"path": "cin", "type": "text"},
{"path": "cds", "type": "enum", "enum": {"local": 0, "remote": 1, "loading": 2, "by_code": 3, "by_touch": 4}, "readonly": false, "cmd": "cc cds"},
{"path": "timing", "type": "bool", "readonly": false, "cmd": "cc timing"},
{"path": "tc", "type": "float", "visibility": 3},
{"path": "tn", "type": "float", "visibility": 3},
{"path": "th", "type": "float", "visibility": 3},
{"path": "tf", "type": "float", "visibility": 3},
{"path": "tm", "type": "float", "visibility": 3},
{"path": "tv", "type": "float", "visibility": 3},
{"path": "tq", "type": "float", "visibility": 3},
{"path": "bdl", "type": "float", "readonly": false, "cmd": "cc bdl"}]}}

View File

@ -245,8 +245,10 @@ class ProxyClient:
except UnregisterCallback:
cblist.remove(cbfunc)
except Exception as e:
# the programmer should catch all errors in callbacks
# if not, the log will be flooded with errors
if self.log:
self.log.error('error %r calling %s%r', e, cbfunc.__name__, args)
self.log.exception('error %r calling %s%r', e, cbfunc.__name__, args)
return bool(cblist)
def updateValue(self, module, param, value, timestamp, readerror):
@ -398,6 +400,7 @@ class SecopClient(ProxyClient):
value = data[0]
readerror = None
module, param = module_param
timestamp = min(time.time(), timestamp) # no timestamps in the future!
try:
self.updateValue(module, param, value, timestamp, readerror)
except KeyError:

View File

@ -405,4 +405,8 @@ def merge_status(*args):
texts matching maximal code are joined with ', '
"""
maxcode = max(a[0] for a in args)
return maxcode, ', '.join([a[1] for a in args if a[0] == maxcode and a[1]])
# take status value matching highest status code
merged = [a[1] for a in args if a[0] == maxcode and a[1]]
# merge the split texts. use dict instead of set for keeping order
merged = {m: 0 for mm in merged for m in mm.split(', ')}
return maxcode, ', '.join(merged)

View File

@ -25,7 +25,7 @@ from frappy.core import Readable, Parameter, IntRange, EnumType, FloatRange, \
Attached, StructOf, WARN, Done, BoolType, Enum
from frappy_psi.convergence import HasConvergence
from frappy_psi.mixins import HasOutputModule, HasControlledBy
from frappy.mixins import HasOutputModule, HasControlledBy
class Ls340IO(StringIO):

View File

@ -20,78 +20,120 @@
#
# *****************************************************************************
from frappy.datatypes import BoolType, EnumType, Enum
from frappy.core import Parameter, Writable, Attached
import time
from math import copysign
from frappy.datatypes import BoolType, FloatRange
from frappy.core import Parameter, BUSY
from frappy.lib import merge_status, clamp
from frappy.errors import RangeError
class HasControlledBy(Writable):
"""mixin for modules with controlled_by
class HasRamp:
"""software ramp"""
# make sure it is a drivable
status = Parameter()
target = Parameter()
ramp = Parameter('ramp rate', FloatRange(0, unit='$/min'), default=0, readonly=False)
ramp_used = Parameter('False: infinite ramp', BoolType(), default=False, readonly=False)
setpoint = Parameter('ramping setpoint', FloatRange(unit='$'), readonly=False)
maxdif = Parameter('''max. difference between setpoint and value
in the :meth:`write_target` the hardware action to switch to own control should be done
and in addition self.self_controlled() should be called
"""
controlled_by = Parameter('source of target value', EnumType(members={'self': 0}), default=0)
inputCallbacks = ()
stop ramp then value lags behind setpoint by more than maxdif
maxdif=0: use 'ramp' value (value lags 1 minute behind setpoint)
''',
FloatRange(0, unit='$'), default=0, readonly=False)
rampinterval = Parameter('interval for changing the setpoint', FloatRange(0, unit='s'),
default=1, readonly=False)
workingramp = Parameter('effective ramp', FloatRange(unit='$/min'))
def register_input(self, name, control_off):
"""register input
_ramp_status = None
_last_time = None
_buffer = 0
:param name: the name of the module (for controlled_by enum)
:param control_off: a method on the input module to switch off control
"""
if not self.inputCallbacks:
self.inputCallbacks = {}
self.inputCallbacks[name] = control_off
prev_enum = self.parameters['controlled_by'].datatype.export_datatype()['members']
# add enum member, using autoincrement feature of Enum
self.parameters['controlled_by'].datatype = EnumType(Enum(prev_enum, **{name: None}))
def doPoll(self):
super().doPoll() # suppose that this is reading value and status
self.ramp_step(self.target)
def self_controlled(self):
"""method to change controlled_by to self
def ramp_step(self, target):
now = time.time()
setpoint = self.setpoint
if self._ramp_status is not None:
if setpoint == target:
self._ramp_status = None # at target
self.workingramp = 0
else:
sign = copysign(1, target - setpoint)
prev_t, prev_v = self._last_point
if self.value == prev_v:
return # no reads happened
delay = (now - prev_t) / 60.0 # minutes !
slope = (self.value - prev_v) / max(1e-5, delay)
dif = (setpoint - self.value) * sign
maxdif = self.maxdif or self.ramp
if dif < maxdif:
# reduce ramp when slope is bigger than ramp
ramp = max(2 * self.ramp - sign * slope, 0) + self._buffer
if ramp > self.ramp:
self._buffer = min(ramp - self.ramp, self.ramp)
ramp = self.ramp
else:
self._buffer = 0
setpoint += sign * delay * ramp
if sign * (setpoint - target) >= 0:
self.write_setpoint(setpoint)
self.workingramp = 0
self._ramp_status = None # at target
else:
if ramp != self.workingramp:
self.workingramp = sign * ramp
self.write_setpoint(setpoint)
self._ramp_status = 'ramping'
else:
self._ramp_status = 'holding'
self._last_point = now, self.value
self.read_status()
must be called from the write_target method
"""
if self.controlled_by:
self.controlled_by = 0
for name, control_off in self.inputCallbacks.items():
control_off(self.name)
def read_status(self):
status = super().read_status()
if self._ramp_status is None:
if self.pollInfo.fast_flag:
self.setFastPoll(False)
return status
if self.pollInfo.interval != self.rampinterval:
self.setFastPoll(True, self.rampinterval)
return merge_status((BUSY, self._ramp_status), status)
def write_ramp(self, ramp):
if ramp:
self.write_ramp_used(True)
else:
raise RangeError('ramp must not 0, use ramp_used = False to disable ramping')
return ramp
class HasOutputModule(Writable):
"""mixin for modules having an output module
def write_ramp_used(self, used):
if used != self.ramp_used:
self.ramp_used = used
if self._ramp_status:
self.write_target(self.target)
in the :meth:`write_target` the hardware action to switch to own control should be done
and in addition self.activate_output() should be called
"""
# allow unassigned output module, it should be possible to configure a
# module with fixed control
output_module = Attached(HasControlledBy, mandatory=False)
control_active = Parameter('control mode', BoolType())
def write_setpoint(self, setpoint):
super().write_target(setpoint)
return setpoint
def initModule(self):
super().initModule()
if self.output_module:
self.output_module.register_input(self.name, self.control_off)
def read_target(self):
if not self._ramp_status:
return super().read_target()
return self.target
def activate_output(self):
"""method to switch control_active on
self.activate_output() must be called from the write_target method
"""
out = self.output_module
if out:
for name, control_off in out.inputCallbacks.items():
if name != self.name:
control_off(self.name)
out.controlled_by = self.name
self.control_active = True
def control_off(self, switched_by):
"""control_off is called, when an other module takes over control
if possible avoid hardware access in an overriding method in an overriding method
as this might lead to a deadlock with the modules accessLock
"""
if self.control_active:
self.control_active = False
self.log.warning(f'switched to manual mode by {switched_by}')
def write_target(self, target):
if self.ramp_used:
if self.parameters['setpoint'].readerror:
self.write_setpoint(self.read_value())
self._ramp_status = 'changed target'
self._last_time = time.time()
self.setFastPoll(True, self.rampinterval)
self.ramp_step(target)
return target
self._ramp_status = None
self.write_setpoint(target)
return target

99
frappy_psi/parmod.py Normal file
View File

@ -0,0 +1,99 @@
# -*- coding: utf-8 -*-
# *****************************************************************************
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Module authors:
# Markus Zolliker <markus.zolliker@psi.ch>
#
# *****************************************************************************
"""modules to access parameters"""
from frappy.core import Drivable, IDLE, Attached, StringType, Property, \
Parameter, FloatRange
from frappy.errors import ConfigError
from frappy_psi.convergence import HasConvergence
from frappy_psi.mixins import HasRamp
class Driv(Drivable):
value = Parameter(datatype=FloatRange(unit='$'))
target = Parameter(datatype=FloatRange(unit='$'))
read = Attached(description='<module>.<parameter> for read')
write = Attached(description='<module>.<parameter> for read')
unit = Property('main unit', StringType())
def setProperty(self, key, value):
if key in ('read', 'write'):
value, param = value.split('.')
setattr(self, f'{key}_param', param)
super().setProperty(key, value)
def checkProperties(self):
self.applyMainUnit(self.unit)
if self.read == self.name or self.write == self.name:
raise ConfigError('illegal recursive read/write module')
super().checkProperties()
#def registerUpdates(self):
# self.read.valueCallbacks[self.read_param].append(self.update_value)
# self.write.valueCallbacks[self.write_param].append(self.update_target)
#
#def startModule(self, start_events):
# start_events.queue(self.registerUpdates)
# super().startModule(start_events)
def read_value(self):
return getattr(self.read, f'{self.read_param}')
def read_target(self):
return getattr(self.write, f'{self.write_param}')
def read_status(self):
return IDLE, ''
def write_target(self, target):
return getattr(self.write, f'write_{self.write_param}')(target)
class Converging(HasConvergence, Driv):
"""drivable with convergence"""
pollinterval = 1
def checkProperties(self):
self.parameters['tolerance'].setProperty('unit', self.unit)
super().checkProperties()
#def update_value(self, value):
# print('UV', value)
# self.value = value
#def error_update_value(self, err):
# raise err
#def update_target(self, value):
# self.target = value
#def error_update_target(self, err):
# raise err
def write_target(self, target):
self.convergence_start()
return super().write_target(target)
class RampDriv(HasRamp, Driv):
pass

View File

@ -48,9 +48,8 @@ from frappy.modules import Attached, Command, Done, Drivable, \
from frappy.protocol.dispatcher import make_update
CFG_HEADER = """Node(
description = '''%(nodedescr)s''',
id = %(config)s.sea.psi.ch,
CFG_HEADER = """Node('%(config)s.sea.psi.ch',
'''%(nodedescr)s''',
)
Mod(%(seaconn)r,
'frappy_psi.sea.SeaClient',