Merge branch 'wip' of gitlab.psi.ch-samenv:samenv/frappy into wip
This commit is contained in:
commit
726665ebd8
20
cfg/main/haakeuro_cfg.py
Normal file
20
cfg/main/haakeuro_cfg.py
Normal 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',
|
||||
)
|
160
cfg/sea/haakeuro.config.json
Normal file
160
cfg/sea/haakeuro.config.json
Normal 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"}]}}
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
||||
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'))
|
||||
|
||||
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 = ()
|
||||
_ramp_status = None
|
||||
_last_time = None
|
||||
_buffer = 0
|
||||
|
||||
def register_input(self, name, control_off):
|
||||
"""register input
|
||||
def doPoll(self):
|
||||
super().doPoll() # suppose that this is reading value and status
|
||||
self.ramp_step(self.target)
|
||||
|
||||
: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 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()
|
||||
|
||||
def self_controlled(self):
|
||||
"""method to change controlled_by to self
|
||||
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)
|
||||
|
||||
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 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
|
||||
|
||||
def write_ramp_used(self, used):
|
||||
if used != self.ramp_used:
|
||||
self.ramp_used = used
|
||||
if self._ramp_status:
|
||||
self.write_target(self.target)
|
||||
|
||||
class HasOutputModule(Writable):
|
||||
"""mixin for modules having an output module
|
||||
def write_setpoint(self, setpoint):
|
||||
super().write_target(setpoint)
|
||||
return setpoint
|
||||
|
||||
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 read_target(self):
|
||||
if not self._ramp_status:
|
||||
return super().read_target()
|
||||
return self.target
|
||||
|
||||
def initModule(self):
|
||||
super().initModule()
|
||||
if self.output_module:
|
||||
self.output_module.register_input(self.name, self.control_off)
|
||||
|
||||
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
99
frappy_psi/parmod.py
Normal 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
|
@ -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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user