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:
@ -34,12 +34,14 @@ import mlzlog
|
||||
import serial
|
||||
|
||||
from secop.datatypes import CommandType, EnumType, get_datatype
|
||||
#from secop.protocol.encoding import ENCODERS
|
||||
#from secop.protocol.framing import FRAMERS
|
||||
#from secop.protocol.messages import *
|
||||
from secop.errors import EXCEPTIONS
|
||||
from secop.lib import formatException, formatExtendedStack, mkthread
|
||||
from secop.lib.parsing import format_time, parse_time
|
||||
from secop.protocol.messages import BUFFERREQUEST, COMMANDREQUEST, \
|
||||
DESCRIPTIONREPLY, DESCRIPTIONREQUEST, DISABLEEVENTSREQUEST, \
|
||||
ENABLEEVENTSREQUEST, ERRORPREFIX, EVENTREPLY, \
|
||||
HEARTBEATREQUEST, HELPREQUEST, IDENTREQUEST, READREPLY, \
|
||||
READREQUEST, REQUEST2REPLY, WRITEREPLY, WRITEREQUEST
|
||||
|
||||
try:
|
||||
# py3
|
||||
@ -76,8 +78,8 @@ class TCPConnection(object):
|
||||
try:
|
||||
data = u''
|
||||
while True:
|
||||
try:
|
||||
newdata = b''
|
||||
try:
|
||||
dlist = [self._io.fileno()]
|
||||
rlist, wlist, xlist = select(dlist, dlist, dlist, 1)
|
||||
if dlist[0] in rlist + wlist:
|
||||
@ -114,7 +116,7 @@ class TCPConnection(object):
|
||||
try:
|
||||
return self._readbuffer.get(block=True, timeout=1)
|
||||
except queue.Empty:
|
||||
continue
|
||||
pass
|
||||
if not block:
|
||||
i -= 1
|
||||
|
||||
@ -244,7 +246,7 @@ class Client(object):
|
||||
self.secop_id = line
|
||||
continue
|
||||
msgtype, spec, data = self.decode_message(line)
|
||||
if msgtype in ('event', 'update', 'changed'):
|
||||
if msgtype in (EVENTREPLY, READREPLY, WRITEREPLY):
|
||||
# handle async stuff
|
||||
self._handle_event(spec, data)
|
||||
# handle sync stuff
|
||||
@ -252,23 +254,23 @@ class Client(object):
|
||||
|
||||
def _handle_sync_reply(self, msgtype, spec, data):
|
||||
# handle sync stuff
|
||||
if msgtype == "error":
|
||||
if msgtype.startswith(ERRORPREFIX):
|
||||
# find originating msgtype and map to expected_reply_type
|
||||
# errormessages carry to offending request as the first
|
||||
# result in the resultist
|
||||
_msgtype, _spec, _data = self.decode_message(data[0])
|
||||
_reply = self._get_reply_from_request(_msgtype)
|
||||
request = msgtype[len(ERRORPREFIX):]
|
||||
reply = REQUEST2REPLY.get(request, request)
|
||||
|
||||
entry = self.expected_replies.get((_reply, _spec), None)
|
||||
entry = self.expected_replies.get((reply, spec), None)
|
||||
if entry:
|
||||
self.log.error("request %r resulted in Error %r" %
|
||||
(data[0], spec))
|
||||
entry.extend([True, EXCEPTIONS[spec](*data)])
|
||||
("%s %s" % (request, spec), (data[0], data[1])))
|
||||
entry.extend([True, EXCEPTIONS[data[0]](*data[1:])])
|
||||
entry[0].set()
|
||||
return
|
||||
self.log.error("got an unexpected error %s %r" % (spec, data[0]))
|
||||
self.log.error("got an unexpected %s %r" % (msgtype,data[0:1]))
|
||||
return
|
||||
if msgtype == "describing":
|
||||
if msgtype == DESCRIPTIONREPLY:
|
||||
entry = self.expected_replies.get((msgtype, ''), None)
|
||||
else:
|
||||
entry = self.expected_replies.get((msgtype, spec), None)
|
||||
@ -291,7 +293,7 @@ class Client(object):
|
||||
return req
|
||||
|
||||
def decode_message(self, msg):
|
||||
"""return a decoded message tripel"""
|
||||
"""return a decoded message triple"""
|
||||
msg = msg.strip()
|
||||
if ' ' not in msg:
|
||||
return msg, '', None
|
||||
@ -359,7 +361,7 @@ class Client(object):
|
||||
raise RuntimeError('Error decoding substruct of descriptive data: %r\n%r' % (err, data))
|
||||
|
||||
def _issueDescribe(self):
|
||||
_, _, describing_data = self._communicate('describe')
|
||||
_, _, describing_data = self._communicate(DESCRIPTIONREQUEST)
|
||||
try:
|
||||
describing_data = self._decode_substruct(
|
||||
['modules'], describing_data)
|
||||
@ -368,12 +370,6 @@ class Client(object):
|
||||
['accessibles'], module)
|
||||
|
||||
self.describing_data = describing_data
|
||||
# import pprint
|
||||
# def r(stuff):
|
||||
# if isinstance(stuff, dict):
|
||||
# return dict((k,r(v)) for k,v in stuff.items())
|
||||
# return stuff
|
||||
# pprint.pprint(r(describing_data))
|
||||
|
||||
for module, moduleData in self.describing_data['modules'].items():
|
||||
for aname, adata in moduleData['accessibles'].items():
|
||||
@ -403,21 +399,6 @@ class Client(object):
|
||||
def register_shutdown_callback(self, func, arg):
|
||||
self.connection.callbacks.append((func, arg))
|
||||
|
||||
def _get_reply_from_request(self, requesttype):
|
||||
# maps each (sync) request to the corresponding reply
|
||||
# XXX: should go to the encoder! and be imported here
|
||||
REPLYMAP = { # pylint: disable=C0103
|
||||
"describe": "describing",
|
||||
"do": "done",
|
||||
"change": "changed",
|
||||
"activate": "active",
|
||||
"deactivate": "inactive",
|
||||
"read": "update",
|
||||
#"*IDN?": "SECoP,", # XXX: !!!
|
||||
"ping": "pong",
|
||||
}
|
||||
return REPLYMAP.get(requesttype, requesttype)
|
||||
|
||||
def communicate(self, msgtype, spec='', data=None):
|
||||
# only return the data portion....
|
||||
return self._communicate(msgtype, spec, data)[2]
|
||||
@ -426,15 +407,17 @@ class Client(object):
|
||||
self.log.debug('communicate: %r %r %r' % (msgtype, spec, data))
|
||||
if self.stopflag:
|
||||
raise RuntimeError('alreading stopping!')
|
||||
if msgtype == "*IDN?":
|
||||
if msgtype == IDENTREQUEST:
|
||||
return self.secop_id
|
||||
|
||||
# sanitize input
|
||||
msgtype = str(msgtype)
|
||||
spec = str(spec)
|
||||
|
||||
if msgtype not in ('*IDN?', 'describe', 'activate', 'deactivate', 'do',
|
||||
'change', 'read', 'ping', 'help'):
|
||||
if msgtype not in (DESCRIPTIONREQUEST, ENABLEEVENTSREQUEST,
|
||||
DISABLEEVENTSREQUEST, COMMANDREQUEST,
|
||||
WRITEREQUEST, BUFFERREQUEST,
|
||||
READREQUEST, HEARTBEATREQUEST, HELPREQUEST):
|
||||
raise EXCEPTIONS['Protocol'](args=[
|
||||
self.encode_message(msgtype, spec, data),
|
||||
dict(
|
||||
@ -443,13 +426,13 @@ class Client(object):
|
||||
])
|
||||
|
||||
# handle syntactic sugar
|
||||
if msgtype == 'change' and ':' not in spec:
|
||||
if msgtype == WRITEREQUEST and ':' not in spec:
|
||||
spec = spec + ':target'
|
||||
if msgtype == 'read' and ':' not in spec:
|
||||
if msgtype == READREQUEST and ':' not in spec:
|
||||
spec = spec + ':value'
|
||||
|
||||
# check if such a request is already out
|
||||
rply = self._get_reply_from_request(msgtype)
|
||||
rply = REQUEST2REPLY[msgtype]
|
||||
if (rply, spec) in self.expected_replies:
|
||||
raise RuntimeError(
|
||||
"can not have more than one requests of the same type at the same time!"
|
||||
@ -487,7 +470,7 @@ class Client(object):
|
||||
|
||||
def quit(self):
|
||||
# after calling this the client is dysfunctional!
|
||||
self.communicate('deactivate')
|
||||
self.communicate(DISABLEEVENTSREQUEST)
|
||||
self.stopflag = True
|
||||
if self._thread and self._thread.is_alive():
|
||||
self.thread.join(self._thread)
|
||||
@ -495,10 +478,10 @@ class Client(object):
|
||||
def startup(self, _async=False):
|
||||
self._issueDescribe()
|
||||
# always fill our cache
|
||||
self.communicate('activate')
|
||||
self.communicate(ENABLEEVENTSREQUEST)
|
||||
# deactivate updates if not wanted
|
||||
if not _async:
|
||||
self.communicate('deactivate')
|
||||
self.communicate(DISABLEEVENTSREQUEST)
|
||||
|
||||
def queryCache(self, module, parameter=None):
|
||||
result = self._cache.get(module, {})
|
||||
@ -509,7 +492,7 @@ class Client(object):
|
||||
return result
|
||||
|
||||
def getParameter(self, module, parameter):
|
||||
return self.communicate('read', '%s:%s' % (module, parameter))
|
||||
return self.communicate(READREQUEST, '%s:%s' % (module, parameter))
|
||||
|
||||
def setParameter(self, module, parameter, value):
|
||||
datatype = self._getDescribingParameterData(module,
|
||||
@ -517,7 +500,7 @@ class Client(object):
|
||||
|
||||
value = datatype.from_string(value)
|
||||
value = datatype.export_value(value)
|
||||
self.communicate('change', '%s:%s' % (module, parameter), value)
|
||||
self.communicate(WRITEREQUEST, '%s:%s' % (module, parameter), value)
|
||||
|
||||
@property
|
||||
def describingData(self):
|
||||
@ -559,7 +542,7 @@ class Client(object):
|
||||
|
||||
def execCommand(self, module, command, args):
|
||||
# ignore reply message + reply specifier, only return data
|
||||
return self._communicate('do', '%s:%s' % (module, command), list(args) if args else None)[2]
|
||||
return self._communicate(COMMANDREQUEST, '%s:%s' % (module, command), list(args) if args else None)[2]
|
||||
|
||||
def getProperties(self, module, parameter):
|
||||
return self.describing_data['modules'][module]['accessibles'][parameter]
|
||||
@ -574,4 +557,4 @@ class Client(object):
|
||||
|
||||
def ping(self, pingctr=[0]): # pylint: disable=W0102
|
||||
pingctr[0] = pingctr[0] + 1
|
||||
self.communicate("ping", pingctr[0])
|
||||
self.communicate(HEARTBEATREQUEST, pingctr[0])
|
||||
|
@ -121,9 +121,10 @@ class ModuleMeta(type):
|
||||
if isinstance(v.datatype, EnumType) and not v.datatype._enum.name:
|
||||
v.datatype._enum.name = k
|
||||
|
||||
# newtype.accessibles will be used in 2 places only:
|
||||
# newtype.accessibles will be used in 3 places only:
|
||||
# 1) for inheritance (see above)
|
||||
# 2) for the describing message
|
||||
# 3) by code needing to access the Parameter/Command object (i.e. checking datatypes)
|
||||
newtype.accessibles = OrderedDict(sorted(accessibles.items(), key=lambda item: item[1].ctr))
|
||||
|
||||
# check validity of Parameter entries
|
||||
@ -143,7 +144,12 @@ class ModuleMeta(type):
|
||||
def wrapped_rfunc(self, maxage=0, pname=pname, rfunc=rfunc):
|
||||
if rfunc:
|
||||
self.log.debug("rfunc(%s): call %r" % (pname, rfunc))
|
||||
try:
|
||||
value = rfunc(self, maxage)
|
||||
except Exception as e:
|
||||
pobj = self.accessibles[pname]
|
||||
self.DISPATCHER.announce_update_error(self, pname, pobj, e)
|
||||
raise e
|
||||
else:
|
||||
# return cached value
|
||||
self.log.debug("rfunc(%s): return cached value" % pname)
|
||||
@ -170,7 +176,11 @@ class ModuleMeta(type):
|
||||
value = pobj.datatype.validate(value)
|
||||
if wfunc:
|
||||
self.log.debug('calling %r(%r)' % (wfunc, value))
|
||||
try:
|
||||
returned_value = wfunc(self, value)
|
||||
except Exception as e:
|
||||
self.DISPATCHER.announce_update_error(self, pname, pobj, e)
|
||||
raise e
|
||||
if returned_value is not None:
|
||||
value = returned_value
|
||||
# XXX: use setattr or direct manipulation
|
||||
|
@ -26,14 +26,14 @@ from __future__ import division, print_function
|
||||
import sys
|
||||
import time
|
||||
|
||||
from secop.datatypes import EnumType, FloatRange, StringType, TupleOf, \
|
||||
get_datatype
|
||||
from secop.datatypes import EnumType, FloatRange, \
|
||||
StringType, TupleOf, get_datatype
|
||||
from secop.errors import ConfigError, ProgrammingError
|
||||
from secop.lib import formatException, formatExtendedStack, mkthread, \
|
||||
unset_value
|
||||
from secop.lib import formatException, \
|
||||
formatExtendedStack, mkthread, unset_value
|
||||
from secop.lib.enum import Enum
|
||||
from secop.metaclass import ModuleMeta, add_metaclass
|
||||
from secop.params import Command, Override, Parameter, PREDEFINED_ACCESSIBLES
|
||||
from secop.params import PREDEFINED_ACCESSIBLES, Command, Override, Parameter
|
||||
|
||||
# XXX: connect with 'protocol'-Modules.
|
||||
# Idea: every Module defined herein is also a 'protocol'-Module,
|
||||
|
@ -27,8 +27,6 @@ from secop.datatypes import CommandType, DataType
|
||||
from secop.errors import ProgrammingError
|
||||
from secop.lib import unset_value
|
||||
|
||||
EVENT_ONLY_ON_CHANGED_VALUES = False
|
||||
|
||||
|
||||
class CountedObj(object):
|
||||
ctr = [0]
|
||||
@ -193,6 +191,9 @@ class Override(CountedObj):
|
||||
if isinstance(obj, Accessible):
|
||||
props = obj.__dict__.copy()
|
||||
for key in self.kwds:
|
||||
if key == 'unit':
|
||||
# XXX: HACK!
|
||||
continue
|
||||
if key not in props and key not in type(obj).valid_properties:
|
||||
raise ProgrammingError( "%s is not a valid %s property" %
|
||||
(key, type(obj).__name__))
|
||||
|
@ -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:
|
||||
|
@ -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,28 +114,22 @@ 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(),
|
||||
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(),
|
||||
result = (ERRORPREFIX + msg[0], msg[1], ['InternalError', str(err),
|
||||
{'exception': formatException(),
|
||||
'traceback': formatExtendedStack()}])
|
||||
print('--------------------')
|
||||
print(formatException())
|
||||
|
@ -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)
|
||||
|
@ -28,7 +28,7 @@ from math import atan
|
||||
|
||||
from secop.datatypes import EnumType, FloatRange, TupleOf
|
||||
from secop.lib import clamp, mkthread
|
||||
from secop.modules import Drivable, Parameter, Command, Override
|
||||
from secop.modules import Command, Drivable, Override, Parameter
|
||||
|
||||
# test custom property (value.test can be changed in config file)
|
||||
Parameter.add_property('test')
|
||||
|
@ -28,7 +28,7 @@ import pytest
|
||||
|
||||
from secop.datatypes import ArrayOf, BLOBType, BoolType, \
|
||||
DataType, EnumType, FloatRange, IntRange, ProgrammingError, \
|
||||
StringType, StructOf, TupleOf, get_datatype, ScaledInteger
|
||||
ScaledInteger, StringType, StructOf, TupleOf, get_datatype
|
||||
|
||||
|
||||
def test_DataType():
|
||||
|
Reference in New Issue
Block a user