rework message syntax to conform to latest decisions

needs a bigger rework, since READREPLY and EVENTREPLY are now different....
Also the format of the error-reply got changed :(

Change-Id: I1760743238227730ee49aaf92b54e0ff5f25423b
Reviewed-on: https://forge.frm2.tum.de/review/20246
Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
This commit is contained in:
Enrico Faulhaber
2019-03-27 15:29:19 +01:00
parent 94619723a9
commit 94959f2e9b
9 changed files with 107 additions and 101 deletions

View File

@ -42,15 +42,15 @@ import threading
from time import time as currenttime
from secop.errors import SECoPServerError as InternalError
from secop.errors import BadValueError, NoSuchCommandError, \
NoSuchModuleError, NoSuchParameterError, ProtocolError, ReadOnlyError
from secop.errors import BadValueError, NoSuchCommandError, NoSuchModuleError, \
NoSuchParameterError, ProtocolError, ReadOnlyError, SECoPError
from secop.params import Parameter
from secop.protocol.messages import COMMANDREPLY, DESCRIPTIONREPLY, \
DISABLEEVENTSREPLY, ENABLEEVENTSREPLY, EVENTREPLY, \
HEARTBEATREPLY, IDENTREPLY, IDENTREQUEST, WRITEREPLY
DISABLEEVENTSREPLY, ENABLEEVENTSREPLY, ERRORPREFIX, EVENTREPLY, \
HEARTBEATREPLY, IDENTREPLY, IDENTREQUEST, READREPLY, WRITEREPLY
try:
unicode('a')
unicode
except NameError:
# no unicode on py3
unicode = str # pylint: disable=redefined-builtin
@ -104,6 +104,19 @@ class Dispatcher(object):
[pobj.export_value(), dict(t=pobj.timestamp)])
self.broadcast_event(msg)
def announce_update_error(self, moduleobj, pname, pobj, err):
"""called by modules param setters/getters to notify subscribers
of problems
"""
# argument pname is no longer used here - should we remove it?
if not isinstance(err, SECoPError):
err = InternalError(err)
msg = (ERRORPREFIX + EVENTREPLY, u'%s:%s' % (moduleobj.name, pobj.export),
# error-report !
[err.name, repr(err), dict(t=currenttime())])
self.broadcast_event(msg)
def subscribe(self, conn, eventname):
self._subscriptions.setdefault(eventname, set()).add(conn)
@ -140,7 +153,7 @@ class Dispatcher(object):
return self._modules[modulename]
elif modulename in list(self._modules.values()):
return modulename
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
raise NoSuchModuleError(u'Module does not exist on this SEC-Node!')
def remove_module(self, modulename_or_obj):
moduleobj = self.get_module(modulename_or_obj)
@ -188,18 +201,18 @@ class Dispatcher(object):
result[u'modules'].append([modulename, mod_desc])
result[u'equipment_id'] = self.equipment_id
result[u'firmware'] = u'FRAPPY - The Python Framework for SECoP'
result[u'version'] = u'2018.09'
result[u'version'] = u'2019.03'
result.update(self.nodeprops)
return result
def _execute_command(self, modulename, command, argument=None):
moduleobj = self.get_module(modulename)
if moduleobj is None:
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
raise NoSuchModuleError(u'Module does not exist on this SEC-Node!')
cmdspec = moduleobj.accessibles.get(command, None)
if cmdspec is None:
raise NoSuchCommandError('Module has no such command!')
raise NoSuchCommandError(u'Module has no such command!')
if argument is None and cmdspec.datatype.argtype is not None:
raise BadValueError(u'Command needs an argument!')
@ -216,16 +229,16 @@ class Dispatcher(object):
def _setParameterValue(self, modulename, exportedname, value):
moduleobj = self.get_module(modulename)
if moduleobj is None:
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
raise NoSuchModuleError(u'Module does not exist on this SEC-Node!')
pname = moduleobj.accessiblename2attr.get(exportedname, None)
pobj = moduleobj.accessibles.get(pname, None)
if pobj is None or not isinstance(pobj, Parameter):
raise NoSuchParameterError('Module has no such parameter on this SEC-Node!')
raise NoSuchParameterError(u'Module has no such parameter on this SEC-Node!')
if pobj.constant is not None:
raise ReadOnlyError('This parameter is constant and can not be accessed remotely.')
raise ReadOnlyError(u'This parameter is constant and can not be accessed remotely.')
if pobj.readonly:
raise ReadOnlyError('This parameter can not be changed remotely.')
raise ReadOnlyError(u'This parameter can not be changed remotely.')
writefunc = getattr(moduleobj, u'write_%s' % pname, None)
# note: exceptions are handled in handle_request, not here!
@ -240,14 +253,14 @@ class Dispatcher(object):
def _getParameterValue(self, modulename, exportedname):
moduleobj = self.get_module(modulename)
if moduleobj is None:
raise NoSuchModuleError('Module does not exist on this SEC-Node!')
raise NoSuchModuleError(u'Module does not exist on this SEC-Node!')
pname = moduleobj.accessiblename2attr.get(exportedname, None)
pobj = moduleobj.accessibles.get(pname, None)
if pobj is None or not isinstance(pobj, Parameter):
raise NoSuchParameterError('Module has no such parameter on this SEC-Node!')
raise NoSuchParameterError(u'Module has no such parameter on this SEC-Node!')
if pobj.constant is not None:
raise ReadOnlyError('This parameter is constant and can not be accessed remotely.')
raise ReadOnlyError(u'This parameter is constant and can not be accessed remotely.')
readfunc = getattr(moduleobj, u'read_%s' % pname, None)
if readfunc:
@ -297,12 +310,12 @@ class Dispatcher(object):
def handle_read(self, conn, specifier, data):
if data:
raise ProtocolError('poll request don\'t take data!')
raise ProtocolError('read requests don\'t take data!')
modulename, pname = specifier, u'value'
if ':' in specifier:
modulename, pname = specifier.split(':', 1)
# XXX: trigger polling and force sending event ???
return (EVENTREPLY, specifier, list(self._getParameterValue(modulename, pname)))
return (READREPLY, specifier, list(self._getParameterValue(modulename, pname)))
def handle_change(self, conn, specifier, data):
modulename, pname = specifier, u'value'
@ -318,12 +331,12 @@ class Dispatcher(object):
def handle_ping(self, conn, specifier, data):
if data:
raise ProtocolError('poll request don\'t take data!')
raise ProtocolError('ping requests don\'t take data!')
return (HEARTBEATREPLY, specifier, [None, {u't':currenttime()}])
def handle_activate(self, conn, specifier, data):
if data:
raise ProtocolError('activate request don\'t take data!')
raise ProtocolError('activate requests don\'t take data!')
if specifier:
modulename, exportedname = specifier, None
if ':' in specifier:
@ -368,6 +381,8 @@ class Dispatcher(object):
return (ENABLEEVENTSREPLY, specifier, None) if specifier else (ENABLEEVENTSREPLY, None, None)
def handle_deactivate(self, conn, specifier, data):
if data:
raise ProtocolError('deactivate requests don\'t take data!')
if specifier:
self.unsubscribe(conn, specifier)
else:

View File

@ -29,7 +29,8 @@ from secop.errors import SECoPError
from secop.lib import formatException, \
formatExtendedStack, formatExtendedTraceback
from secop.protocol.interface import decode_msg, encode_msg_frame, get_msg
from secop.protocol.messages import HELPREPLY, HELPREQUEST, HelpMessage
from secop.protocol.messages import ERRORPREFIX, \
HELPREPLY, HELPREQUEST, HelpMessage
try:
import socketserver # py3
@ -113,29 +114,23 @@ class TCPRequestHandler(socketserver.BaseRequestHandler):
if origin is None:
break # no more messages to process
origin = origin.strip()
if origin and origin[0] == CR:
origin = origin[1:]
if origin and origin[-1] == CR:
origin = origin[:-1]
if origin in (HELPREQUEST, ''): # empty string -> send help message
for idx, line in enumerate(HelpMessage.splitlines()):
self.queue_async_reply((HELPREPLY, '%d' % (idx+1), line))
continue
msg = decode_msg(origin)
result = None
try:
msg = decode_msg(origin)
result = serverobj.dispatcher.handle_request(self, msg)
if (msg[0] == 'read') and result:
# read should only trigger async_replies
self.queue_async_reply(('error', 'InternalError', [origin,
'read should only trigger async data units']))
except SECoPError as err:
result = ('error', err.name, [origin, str(err), {'exception': formatException(),
'traceback': formatExtendedStack()}])
result = (ERRORPREFIX + msg[0], msg[1], [err.name, str(err),
{'exception': formatException(),
'traceback': formatExtendedStack()}])
except Exception as err:
# create Error Obj instead
result = ('error', 'InternalError', [origin, str(err), {'exception': formatException(),
'traceback': formatExtendedStack()}])
result = (ERRORPREFIX + msg[0], msg[1], ['InternalError', str(err),
{'exception': formatException(),
'traceback': formatExtendedStack()}])
print('--------------------')
print(formatException())
print('--------------------')

View File

@ -26,7 +26,7 @@ from __future__ import division, print_function
IDENTREQUEST = u'*IDN?' # literal
# literal! first part is fixed!
IDENTREPLY = u'SINE2020&ISSE,SECoP,V2018-11-07,v1.0\\beta'
IDENTREPLY = u'SINE2020&ISSE,SECoP,V2019-03-20,v1.0 RC1'
DESCRIPTIONREQUEST = u'describe' # literal
DESCRIPTIONREPLY = u'describing' # +<id> +json
@ -52,13 +52,15 @@ BUFFERREQUEST = u'buffer'
BUFFERREPLY = u'buffered'
# +module[:parameter] -> NO direct reply, calls POLL internally!
POLLREQUEST = u'read'
READREQUEST = u'read'
READREPLY = u'reply' # See Issue 54
EVENTREPLY = u'update' # +module[:parameter] +json_value (value, qualifiers_as_dict)
HEARTBEATREQUEST = u'ping' # +nonce_without_space
HEARTBEATREPLY = u'pong' # +nonce_without_space
ERRORREPLY = u'error' # +errorclass +json_extended_info
ERRORPREFIX = u'error_' # + specifier + json_extended_info(error_report)
HELPREQUEST = u'help' # literal
HELPREPLY = u'helping' # +line number +json_text
@ -72,7 +74,7 @@ REQUEST2REPLY = {
COMMANDREQUEST: COMMANDREPLY,
WRITEREQUEST: WRITEREPLY,
BUFFERREQUEST: BUFFERREPLY,
POLLREQUEST: EVENTREPLY,
READREQUEST: READREPLY,
HEARTBEATREQUEST: HEARTBEATREPLY,
HELPREQUEST: HELPREPLY,
}
@ -88,6 +90,6 @@ HelpMessage = u"""Try one of the following:
'%s <nonce>' to request a heartbeat response
'%s' to activate async updates
'%s' to deactivate updates
""" % (IDENTREQUEST, DESCRIPTIONREQUEST, POLLREQUEST,
""" % (IDENTREQUEST, DESCRIPTIONREQUEST, READREQUEST,
WRITEREQUEST, COMMANDREQUEST, HEARTBEATREQUEST,
ENABLEEVENTSREQUEST, DISABLEEVENTSREQUEST)