support write_ method on readonly param and more
- write method may be used internally on a readonly parameter + add IDLE, WARN, BUSY and ERROR to secop.core + secop.datatype.EnumType: allow 'self' as member name + secop.lib.statemachine: log Restart and Stop exceptions only on debug level + secop_psi.ccu4.CCU4: explicit conversion to float + secop.proxy: remove superfluos and erroneous make_secop_error Change-Id: I2f13d31ceacd2bde65eab64f8eae4225556c18f5 Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/27963 Tested-by: Jenkins Automated Tests <pedersen+jenkins@frm2.tum.de> Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de> Reviewed-by: Markus Zolliker <markus.zolliker@psi.ch>
This commit is contained in:
parent
3c0c60615a
commit
60c62a340d
@ -162,7 +162,7 @@ max-line-length=132
|
|||||||
no-space-check=trailing-comma,dict-separator
|
no-space-check=trailing-comma,dict-separator
|
||||||
|
|
||||||
# Maximum number of lines in a module
|
# Maximum number of lines in a module
|
||||||
max-module-lines=1200
|
max-module-lines=1000
|
||||||
|
|
||||||
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||||
# tab).
|
# tab).
|
||||||
|
@ -7,7 +7,7 @@ Module Base Classes
|
|||||||
.. autodata:: secop.modules.Done
|
.. autodata:: secop.modules.Done
|
||||||
|
|
||||||
.. autoclass:: secop.modules.Module
|
.. autoclass:: secop.modules.Module
|
||||||
:members: earlyInit, initModule, startModule, pollerClass
|
:members: earlyInit, initModule, startModule
|
||||||
|
|
||||||
.. autoclass:: secop.modules.Readable
|
.. autoclass:: secop.modules.Readable
|
||||||
:members: Status
|
:members: Status
|
||||||
|
@ -31,7 +31,7 @@ from secop.datatypes import ArrayOf, BLOBType, BoolType, EnumType, \
|
|||||||
from secop.iohandler import IOHandler, IOHandlerBase
|
from secop.iohandler import IOHandler, IOHandlerBase
|
||||||
from secop.lib.enum import Enum
|
from secop.lib.enum import Enum
|
||||||
from secop.modules import Attached, Communicator, \
|
from secop.modules import Attached, Communicator, \
|
||||||
Done, Drivable, Module, Readable, Writable
|
Done, Drivable, Module, Readable, Writable, HasAccessibles
|
||||||
from secop.params import Command, Parameter
|
from secop.params import Command, Parameter
|
||||||
from secop.properties import Property
|
from secop.properties import Property
|
||||||
from secop.proxy import Proxy, SecNode, proxy_class
|
from secop.proxy import Proxy, SecNode, proxy_class
|
||||||
@ -39,3 +39,8 @@ from secop.io import HasIO, StringIO, BytesIO, HasIodev # TODO: remove HasIodev
|
|||||||
from secop.persistent import PersistentMixin, PersistentParam
|
from secop.persistent import PersistentMixin, PersistentParam
|
||||||
from secop.rwhandler import ReadHandler, WriteHandler, CommonReadHandler, \
|
from secop.rwhandler import ReadHandler, WriteHandler, CommonReadHandler, \
|
||||||
CommonWriteHandler, nopoll
|
CommonWriteHandler, nopoll
|
||||||
|
|
||||||
|
ERROR = Drivable.Status.ERROR
|
||||||
|
WARN = Drivable.Status.WARN
|
||||||
|
BUSY = Drivable.Status.BUSY
|
||||||
|
IDLE = Drivable.Status.IDLE
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
# *****************************************************************************
|
# *****************************************************************************
|
||||||
"""Define validated data types."""
|
"""Define validated data types."""
|
||||||
|
|
||||||
# pylint: disable=abstract-method
|
# pylint: disable=abstract-method, too-many-lines
|
||||||
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
@ -454,7 +454,10 @@ class EnumType(DataType):
|
|||||||
super().__init__()
|
super().__init__()
|
||||||
if members is not None:
|
if members is not None:
|
||||||
kwds.update(members)
|
kwds.update(members)
|
||||||
self._enum = Enum(enum_or_name, **kwds)
|
if isinstance(enum_or_name, str):
|
||||||
|
self._enum = Enum(enum_or_name, kwds) # allow 'self' as name
|
||||||
|
else:
|
||||||
|
self._enum = Enum(enum_or_name, **kwds)
|
||||||
self.default = self._enum[self._enum.members[0]]
|
self.default = self._enum[self._enum.members[0]]
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
|
@ -178,7 +178,9 @@ class StateMachine:
|
|||||||
if self.stop_exc:
|
if self.stop_exc:
|
||||||
raise self.stop_exc
|
raise self.stop_exc
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.log.info('%r raised in state %r', e, self.status_string)
|
# Stop and Restart are not unusual -> no warning
|
||||||
|
log = self.log.debug if isinstance(e, Stop) else self.log.warning
|
||||||
|
log('%r raised in state %r', e, self.status_string)
|
||||||
self.last_error = e
|
self.last_error = e
|
||||||
ret = self.cleanup(self)
|
ret = self.cleanup(self)
|
||||||
self.log.debug('cleanup %r %r %r', self.cleanup, self.last_error, ret)
|
self.log.debug('cleanup %r %r %r', self.cleanup, self.last_error, ret)
|
||||||
|
@ -161,8 +161,8 @@ class HasAccessibles(HasProperties):
|
|||||||
new_rfunc.wrapped = True # indicate to subclasses that no more wrapping is needed
|
new_rfunc.wrapped = True # indicate to subclasses that no more wrapping is needed
|
||||||
setattr(cls, 'read_' + pname, new_rfunc)
|
setattr(cls, 'read_' + pname, new_rfunc)
|
||||||
|
|
||||||
if not pobj.readonly:
|
wfunc = getattr(cls, 'write_' + pname, None)
|
||||||
wfunc = getattr(cls, 'write_' + pname, None)
|
if not pobj.readonly or wfunc: # allow write_ method even when pobj is not readonly
|
||||||
wrapped = getattr(wfunc, 'wrapped', False) # meaning: wrapped or auto generated
|
wrapped = getattr(wfunc, 'wrapped', False) # meaning: wrapped or auto generated
|
||||||
if (wfunc is None or wrapped) and pobj.handler:
|
if (wfunc is None or wrapped) and pobj.handler:
|
||||||
# ignore the handler, if a write function is present
|
# ignore the handler, if a write function is present
|
||||||
@ -392,9 +392,8 @@ class Module(HasAccessibles):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
if pname in cfgdict:
|
if pname in cfgdict:
|
||||||
if not pobj.readonly and pobj.initwrite is not False:
|
if pobj.initwrite is not False and hasattr(self, 'write_' + pname):
|
||||||
# parameters given in cfgdict have to call write_<pname>
|
# parameters given in cfgdict have to call write_<pname>
|
||||||
# TODO: not sure about readonly (why not a parameter which can only be written from config?)
|
|
||||||
try:
|
try:
|
||||||
pobj.value = pobj.datatype(cfgdict[pname])
|
pobj.value = pobj.datatype(cfgdict[pname])
|
||||||
self.writeDict[pname] = pobj.value
|
self.writeDict[pname] = pobj.value
|
||||||
@ -417,10 +416,8 @@ class Module(HasAccessibles):
|
|||||||
except BadValueError as e:
|
except BadValueError as e:
|
||||||
# this should not happen, as the default is already checked in Parameter
|
# this should not happen, as the default is already checked in Parameter
|
||||||
raise ProgrammingError('bad default for %s:%s: %s' % (name, pname, e)) from None
|
raise ProgrammingError('bad default for %s:%s: %s' % (name, pname, e)) from None
|
||||||
if pobj.initwrite and not pobj.readonly:
|
if pobj.initwrite and hasattr(self, 'write_' + pname):
|
||||||
# we will need to call write_<pname>
|
# we will need to call write_<pname>
|
||||||
# if this is not desired, the default must not be given
|
|
||||||
# TODO: not sure about readonly (why not a parameter which can only be written from config?)
|
|
||||||
pobj.value = value
|
pobj.value = value
|
||||||
self.writeDict[pname] = value
|
self.writeDict[pname] = value
|
||||||
else:
|
else:
|
||||||
|
@ -75,7 +75,7 @@ class PersistentMixin(HasAccessibles):
|
|||||||
self.initData = {}
|
self.initData = {}
|
||||||
for pname in self.parameters:
|
for pname in self.parameters:
|
||||||
pobj = self.parameters[pname]
|
pobj = self.parameters[pname]
|
||||||
if not pobj.readonly and getattr(pobj, 'persistent', 0):
|
if hasattr(self, 'write_' + pname) and getattr(pobj, 'persistent', 0):
|
||||||
self.initData[pname] = pobj.value
|
self.initData[pname] = pobj.value
|
||||||
if pobj.persistent == 'auto':
|
if pobj.persistent == 'auto':
|
||||||
def cb(value, m=self):
|
def cb(value, m=self):
|
||||||
|
@ -23,8 +23,7 @@
|
|||||||
|
|
||||||
from secop.client import SecopClient, decode_msg, encode_msg_frame
|
from secop.client import SecopClient, decode_msg, encode_msg_frame
|
||||||
from secop.datatypes import StringType
|
from secop.datatypes import StringType
|
||||||
from secop.errors import BadValueError, \
|
from secop.errors import BadValueError, CommunicationFailedError, ConfigError
|
||||||
CommunicationFailedError, ConfigError, make_secop_error
|
|
||||||
from secop.lib import get_class
|
from secop.lib import get_class
|
||||||
from secop.modules import Drivable, Module, Readable, Writable
|
from secop.modules import Drivable, Module, Readable, Writable
|
||||||
from secop.params import Command, Parameter
|
from secop.params import Command, Parameter
|
||||||
@ -47,8 +46,6 @@ class ProxyModule(HasIO, Module):
|
|||||||
if parameter not in self.parameters:
|
if parameter not in self.parameters:
|
||||||
return # ignore unknown parameters
|
return # ignore unknown parameters
|
||||||
# should be done here: deal with clock differences
|
# should be done here: deal with clock differences
|
||||||
if readerror:
|
|
||||||
readerror = make_secop_error(*readerror)
|
|
||||||
self.announceUpdate(parameter, value, readerror, timestamp)
|
self.announceUpdate(parameter, value, readerror, timestamp)
|
||||||
|
|
||||||
def initModule(self):
|
def initModule(self):
|
||||||
@ -201,7 +198,7 @@ def proxy_class(remote_class, name=None):
|
|||||||
def wfunc(self, value, pname=aname):
|
def wfunc(self, value, pname=aname):
|
||||||
value, _, readerror = self._secnode.setParameter(self.name, pname, value)
|
value, _, readerror = self._secnode.setParameter(self.name, pname, value)
|
||||||
if readerror:
|
if readerror:
|
||||||
raise make_secop_error(*readerror)
|
raise readerror
|
||||||
return value
|
return value
|
||||||
|
|
||||||
attrs['write_' + aname] = wfunc
|
attrs['write_' + aname] = wfunc
|
||||||
|
@ -72,7 +72,7 @@ class HeLevel(HasIO, Readable):
|
|||||||
"""
|
"""
|
||||||
name, txtvalue = self.communicate(cmd).split('=')
|
name, txtvalue = self.communicate(cmd).split('=')
|
||||||
assert name == cmd.split('=')[0] # check that we got a reply to our command
|
assert name == cmd.split('=')[0] # check that we got a reply to our command
|
||||||
return txtvalue # Frappy will automatically convert the string to the needed data type
|
return float(txtvalue)
|
||||||
|
|
||||||
def read_value(self):
|
def read_value(self):
|
||||||
return self.query('h')
|
return self.query('h')
|
||||||
|
@ -64,6 +64,7 @@ def test_EnumMember():
|
|||||||
assert a != 3
|
assert a != 3
|
||||||
assert a == 1
|
assert a == 1
|
||||||
|
|
||||||
|
|
||||||
def test_Enum():
|
def test_Enum():
|
||||||
e1 = Enum('e1')
|
e1 = Enum('e1')
|
||||||
e2 = Enum('e2', e1, a=1, b=3)
|
e2 = Enum('e2', e1, a=1, b=3)
|
||||||
@ -75,3 +76,4 @@ def test_Enum():
|
|||||||
assert e2.b > e3.a
|
assert e2.b > e3.a
|
||||||
assert e3.c >= e2.a
|
assert e3.c >= e2.a
|
||||||
assert e3.b <= e2.b
|
assert e3.b <= e2.b
|
||||||
|
assert Enum({'self': 0, 'other': 1})('self') == 0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user