removed old style syntax

- removed secop/metaclass.py
- moved code from ModuleMeta to modules.HasAccessibles.__init_subclass__
- reworked properties:
  assignment obj.property = value now always allowed
- reworked Parameters and Command to be true descriptors
- Command must now be solely used as decorator
- renamed 'usercommand' to 'Command'
- command methods no longer start with 'do_'
- reworked mechanism to determine accessible order:
  the attribute paramOrder, if given, determines order of accessibles
+ fixed some issues makeing the IDE more happy
+ simplified code for StatusType and added a test for it

Change-Id: I8045cf38ee6f4d4862428272df0b12a7c8abaca7
Reviewed-on: https://forge.frm2.tum.de/review/c/sine2020/secop/playground/+/25049
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:
2021-02-12 18:37:04 +01:00
parent f9a2152883
commit 07b758c3dd
34 changed files with 1678 additions and 1978 deletions

View File

@ -27,11 +27,10 @@ import time
import threading
import re
from secop.lib.asynconn import AsynConn, ConnectionClosed
from secop.modules import Module, Communicator, Parameter, Command, Property, Attached
from secop.modules import Module, Communicator, Parameter, Command, Property, Attached, Done
from secop.datatypes import StringType, FloatRange, ArrayOf, BoolType, TupleOf, ValueType
from secop.errors import CommunicationFailedError, CommunicationSilentError
from secop.errors import CommunicationFailedError, CommunicationSilentError, ConfigError
from secop.poller import REGULAR
from secop.metaclass import Done
class StringIO(Communicator):
@ -39,38 +38,22 @@ class StringIO(Communicator):
self healing is assured by polling the parameter 'is_connected'
"""
properties = {
'uri':
Property('hostname:portnumber', datatype=StringType()),
'end_of_line':
Property('end_of_line character', datatype=ValueType(),
default='\n', settable=True),
'encoding':
Property('used encoding', datatype=StringType(),
default='ascii', settable=True),
'identification':
Property('''
identification
a list of tuples with commands and expected responses as regexp,
to be sent on connect''',
datatype=ArrayOf(TupleOf(StringType(), StringType())), default=[], export=False),
}
parameters = {
'timeout':
Parameter('timeout', datatype=FloatRange(0), default=2),
'wait_before':
Parameter('wait time before sending', datatype=FloatRange(), default=0),
'is_connected':
Parameter('connection state', datatype=BoolType(), readonly=False, poll=REGULAR),
'pollinterval':
Parameter('reconnect interval', datatype=FloatRange(0), readonly=False, default=10),
}
commands = {
'multicomm':
Command('execute multiple commands in one go',
argument=ArrayOf(StringType()), result=ArrayOf(StringType()))
}
uri = Property('hostname:portnumber', datatype=StringType())
end_of_line = Property('end_of_line character', datatype=ValueType(),
default='\n', settable=True)
encoding = Property('used encoding', datatype=StringType(),
default='ascii', settable=True)
identification = Property('''
identification
a list of tuples with commands and expected responses as regexp,
to be sent on connect''',
datatype=ArrayOf(TupleOf(StringType(), StringType())), default=[], export=False)
timeout = Parameter('timeout', datatype=FloatRange(0), default=2)
wait_before = Parameter('wait time before sending', datatype=FloatRange(), default=0)
is_connected = Parameter('connection state', datatype=BoolType(), readonly=False, poll=REGULAR)
pollinterval = Parameter('reconnect interval', datatype=FloatRange(0), readonly=False, default=10)
_reconnectCallbacks = None
@ -105,11 +88,12 @@ class StringIO(Communicator):
self._conn = AsynConn(uri, self._eol_read)
self.is_connected = True
for command, regexp in self.identification:
reply = self.do_communicate(command)
reply = self.communicate(command)
if not re.match(regexp, reply):
self.closeConnection()
raise CommunicationFailedError('bad response: %s does not match %s' %
(reply, regexp))
def closeConnection(self):
"""close connection
@ -125,7 +109,7 @@ class StringIO(Communicator):
self.is_connected is changed only by self.connectStart or self.closeConnection
"""
if self.is_connected:
return Done # no need for intermediate updates
return Done # no need for intermediate updates
try:
self.connectStart()
if self._last_error:
@ -170,7 +154,7 @@ class StringIO(Communicator):
if removeme:
self._reconnectCallbacks.pop(key)
def do_communicate(self, command):
def communicate(self, command):
"""send a command and receive a reply
using end_of_line, encoding and self._lock
@ -179,6 +163,8 @@ class StringIO(Communicator):
"""
if not self.is_connected:
self.read_is_connected() # try to reconnect
if not self._conn:
raise CommunicationSilentError('can not connect to %r' % self.uri)
try:
with self._lock:
# read garbage and wait before send
@ -210,11 +196,13 @@ class StringIO(Communicator):
self.log.error(self._last_error)
raise
def do_multicomm(self, commands):
@Command(ArrayOf(StringType()), result=ArrayOf(StringType()))
def multicomm(self, commands):
"""communicate multiple request/replies in one row"""
replies = []
with self._lock:
for cmd in commands:
replies.append(self.do_communicate(cmd))
replies.append(self.communicate(cmd))
return replies
@ -223,17 +211,15 @@ class HasIodev(Module):
not only StringIO !
"""
properties = {
'iodev': Attached(),
'uri': Property('uri for automatic creation of the attached communication module',
StringType(), default=''),
}
iodev = Attached()
uri = Property('uri for automatic creation of the attached communication module',
StringType(), default='')
iodevDict = {}
def __init__(self, name, logger, opts, srv):
iodev = opts.get('iodev')
super().__init__(name, logger, opts, srv)
Module.__init__(self, name, logger, opts, srv)
if self.uri:
opts = {'uri': self.uri, 'description': 'communication device for %s' % name,
'export': False}
@ -243,7 +229,9 @@ class HasIodev(Module):
iodev = self.iodevClass(ioname, srv.log.getChild(ioname), opts, srv)
srv.modules[ioname] = iodev
self.iodevDict[self.uri] = ioname
self.setProperty('iodev', ioname)
self.iodev = ioname
elif not self.iodev:
raise ConfigError("Module %s needs a value for either 'uri' or 'iodev'" % name)
def initModule(self):
try:
@ -254,4 +242,4 @@ class HasIodev(Module):
super().initModule()
def sendRecv(self, command):
return self._iodev.do_communicate(command)
return self._iodev.communicate(command)