Polishing
- unify (encoded) Errormessages. They now always contain the offeding request. - adopt keywords to current spec - fixed formatting of timestamp in WriteReply - minor bugfixing + polishing Change-Id: I0ab8836756551399643bdce3d062eedd345509f1
This commit is contained in:
parent
d442da0789
commit
8e3d0da5dd
@ -146,9 +146,9 @@ Events can be emitted any time from the SEC-node (except if they would interrupt
|
||||
|
||||
examples:
|
||||
|
||||
* 'event T1:value [3.479, {"t":"149128925.914882", "e":0.01924}]
|
||||
* 'event T1:p [12, {"t":"149128927.193725"}]'
|
||||
* 'event Vector:value [[0.01, 12.49, 3.92], {"t":"149128925.914882"}]'
|
||||
* 'update T1:value [3.479, {"t":"149128925.914882", "e":0.01924}]
|
||||
* 'update T1:p [12, {"t":"149128927.193725"}]'
|
||||
* 'update Vector:value [[0.01, 12.49, 3.92], {"t":"149128925.914882"}]'
|
||||
|
||||
|
||||
ERROR
|
||||
|
@ -189,28 +189,30 @@ class Client(object):
|
||||
while not self.stopflag:
|
||||
line = self.connection.readline()
|
||||
self.log.debug('got answer %r' % line)
|
||||
if line.startswith(('SECoP', 'Sine2020WP7')):
|
||||
if line.startswith(('SECoP', 'SINE2020&ISSE,SECoP')):
|
||||
self.log.info('connected to: ' + line.strip())
|
||||
self.secop_id = line
|
||||
continue
|
||||
msgtype, spec, data = self._decode_message(line)
|
||||
if msgtype in ('event', 'changed'):
|
||||
if msgtype in ('update', 'changed'):
|
||||
# handle async stuff
|
||||
self._handle_event(spec, data)
|
||||
if msgtype != 'event':
|
||||
if msgtype != 'update':
|
||||
# handle sync stuff
|
||||
if msgtype in self.expected_replies:
|
||||
entry = self.expected_replies[msgtype]
|
||||
entry.extend([msgtype, spec, data])
|
||||
# wake up calling process
|
||||
entry[0].set()
|
||||
elif msgtype == "ERROR":
|
||||
elif msgtype == "error":
|
||||
# XXX: hack!
|
||||
if len(self.expected_replies) == 1:
|
||||
entry = self.expected_replies.values()[0]
|
||||
entry.extend([msgtype, spec, data])
|
||||
# wake up calling process
|
||||
entry[0].set()
|
||||
else: # try to find the right request....
|
||||
print data[0] # should be the origin request
|
||||
# XXX: make an assignment of ERROR to an expected reply.
|
||||
self.log.error('TODO: handle ERROR replies!')
|
||||
else:
|
||||
@ -312,12 +314,12 @@ class Client(object):
|
||||
}
|
||||
if self.stopflag:
|
||||
raise RuntimeError('alreading stopping!')
|
||||
if msgtype == 'poll':
|
||||
if msgtype == 'read':
|
||||
# send a poll request and then check incoming events
|
||||
if ':' not in spec:
|
||||
spec = spec + ':value'
|
||||
event = threading.Event()
|
||||
result = ['polled', spec]
|
||||
result = ['update', spec]
|
||||
self.single_shots.setdefault(spec, set()).add(
|
||||
lambda d: (result.append(d), event.set()))
|
||||
self.connection.writeline(
|
||||
|
@ -32,6 +32,7 @@ import types
|
||||
import inspect
|
||||
import threading
|
||||
|
||||
from secop.lib.parsing import format_time
|
||||
from secop.errors import ConfigError, ProgrammingError
|
||||
from secop.protocol import status
|
||||
from secop.validators import enum, vector, floatrange
|
||||
@ -73,7 +74,7 @@ class PARAM(object):
|
||||
unit=self.unit,
|
||||
readonly=self.readonly,
|
||||
value=self.value,
|
||||
timestamp=self.timestamp,
|
||||
timestamp=format_time(self.timestamp) if self.timestamp else None,
|
||||
validator=str(self.validator) if not isinstance(
|
||||
self.validator, type) else self.validator.__name__
|
||||
)
|
||||
|
@ -43,7 +43,7 @@ import threading
|
||||
|
||||
from messages import *
|
||||
from errors import *
|
||||
|
||||
from secop.lib.parsing import format_time
|
||||
|
||||
class Dispatcher(object):
|
||||
|
||||
@ -84,15 +84,14 @@ class Dispatcher(object):
|
||||
reply = handler(conn, msg)
|
||||
except SECOPError as err:
|
||||
self.log.exception(err)
|
||||
reply = ErrorMessage(errorclass=err.__class__.__name__,
|
||||
reply = msg.get_error(errorclass=err.__class__.__name__,
|
||||
errorinfo=[repr(err), str(msg)])
|
||||
except (ValueError, TypeError) as err:
|
||||
# self.log.exception(err)
|
||||
reply = ErrorMessage(errorclass='BadValue',
|
||||
reply = msg.get_error(errorclass='BadValue',
|
||||
errorinfo=[repr(err), str(msg)])
|
||||
except Exception as err:
|
||||
self.log.exception(err)
|
||||
reply = ErrorMessage(errorclass='InternalError',
|
||||
reply = msg.get_error(errorclass='InternalError',
|
||||
errorinfo=[repr(err), str(msg)])
|
||||
else:
|
||||
self.log.debug('Can not handle msg %r' % msg)
|
||||
@ -159,8 +158,11 @@ class Dispatcher(object):
|
||||
self._export.append(modulename)
|
||||
|
||||
def get_module(self, modulename):
|
||||
module = self._modules.get(modulename, modulename)
|
||||
return module
|
||||
if modulename in self._modules:
|
||||
return self._modules[modulename]
|
||||
elif modulename in self._modules.values():
|
||||
return modulename
|
||||
raise NoSuchModuleError(module=str(modulename))
|
||||
|
||||
def remove_module(self, modulename_or_obj):
|
||||
moduleobj = self.get_module(modulename_or_obj) or modulename_or_obj
|
||||
@ -226,7 +228,7 @@ class Dispatcher(object):
|
||||
|
||||
moduleobj = self.get_module(modulename)
|
||||
if moduleobj is None:
|
||||
raise NoSuchmoduleError(module=modulename)
|
||||
raise NoSuchModuleError(module=modulename)
|
||||
|
||||
cmdspec = moduleobj.CMDS.get(command, None)
|
||||
if cmdspec is None:
|
||||
@ -249,7 +251,7 @@ class Dispatcher(object):
|
||||
def _setParamValue(self, modulename, pname, value):
|
||||
moduleobj = self.get_module(modulename)
|
||||
if moduleobj is None:
|
||||
raise NoSuchmoduleError(module=modulename)
|
||||
raise NoSuchModuleError(module=modulename)
|
||||
|
||||
pobj = moduleobj.PARAMS.get(pname, None)
|
||||
if pobj is None:
|
||||
@ -267,7 +269,7 @@ class Dispatcher(object):
|
||||
return WriteReply(
|
||||
module=modulename, parameter=pname, value=[
|
||||
pobj.value, dict(
|
||||
t=pobj.timestamp)])
|
||||
t=format_time(pobj.timestamp))])
|
||||
return WriteReply(
|
||||
module=modulename,
|
||||
parameter=pname,
|
||||
@ -389,5 +391,5 @@ class Dispatcher(object):
|
||||
(no handle_<messagename> method was defined)
|
||||
"""
|
||||
self.log.error('IGN: got unhandled request %s' % msg)
|
||||
return ErrorMessage(errorclass="InternalError",
|
||||
errorstring='Got Unhandled Request %r' % msg)
|
||||
return msg.get_error(errorclass="InternalError",
|
||||
errorinfo="Unhandled Request")
|
||||
|
@ -45,8 +45,9 @@ DEMO_RE = re.compile(
|
||||
#"""
|
||||
# messagetypes:
|
||||
IDENTREQUEST = '*IDN?' # literal
|
||||
# literal! first part 'SECoP' is fixed!
|
||||
IDENTREPLY = 'SECoP, SECoPTCP, V2016-11-30, rc1'
|
||||
# literal! first part is fixed!
|
||||
#IDENTREPLY = 'SECoP, SECoPTCP, V2016-11-30, rc1'
|
||||
IDENTREPLY = 'SINE2020&ISSE,SECoP,V2016-11-30,rc1'
|
||||
DESCRIPTIONSREQUEST = 'describe' # literal
|
||||
DESCRIPTIONREPLY = 'describing' # +<id> +json
|
||||
ENABLEEVENTSREQUEST = 'activate' # literal
|
||||
@ -61,11 +62,11 @@ WRITEREQUEST = 'change'
|
||||
# +module[:parameter] +json_value # send with the read back value
|
||||
WRITEREPLY = 'changed'
|
||||
# +module[:parameter] -> NO direct reply, calls TRIGGER internally!
|
||||
TRIGGERREQUEST = 'poll'
|
||||
EVENT = 'event' # +module[:parameter] +json_value (value, qualifiers_as_dict)
|
||||
TRIGGERREQUEST = 'read'
|
||||
EVENT = 'update' # +module[:parameter] +json_value (value, qualifiers_as_dict)
|
||||
HEARTBEATREQUEST = 'ping' # +nonce_without_space
|
||||
HEARTBEATREPLY = 'pong' # +nonce_without_space
|
||||
ERRORREPLY = 'ERROR' # +errorclass +json_extended_info
|
||||
ERRORREPLY = 'error' # +errorclass +json_extended_info
|
||||
HELPREQUEST = 'help' # literal
|
||||
HELPREPLY = 'helping' # +line number +json_text
|
||||
ERRORCLASSES = ['NoSuchDevice', 'NoSuchParameter', 'NoSuchCommand',
|
||||
@ -88,6 +89,10 @@ def encode_value_data(vobj):
|
||||
q['t'] = format_time(q['t'])
|
||||
return vobj.value, q
|
||||
|
||||
def encode_error_msg(emsg):
|
||||
# note: result is JSON-ified....
|
||||
return [emsg.origin, dict( (k,getattr(emsg, k)) for k in emsg.ARGS if k != 'origin')]
|
||||
|
||||
|
||||
class DemoEncoder(MessageEncoder):
|
||||
# map of msg to msgtype string as defined above.
|
||||
@ -103,12 +108,12 @@ class DemoEncoder(MessageEncoder):
|
||||
CommandRequest: (COMMANDREQUEST, lambda msg: "%s:%s" % (msg.module, msg.command), 'arguments',),
|
||||
CommandReply: (COMMANDREPLY, lambda msg: "%s:%s" % (msg.module, msg.command), encode_cmd_result,),
|
||||
WriteRequest: (WRITEREQUEST, lambda msg: "%s:%s" % (msg.module, msg.parameter) if msg.parameter else msg.module, 'value',),
|
||||
WriteReply: (WRITEREPLY, lambda msg: "%s:%s" % (msg.module, msg.parameter) if msg.parameter else msg.module, 'value',),
|
||||
WriteReply: (WRITEREPLY, lambda msg: "%s:%s" % (msg.module, msg.parameter) if msg.parameter else msg.module, 'value', ),
|
||||
PollRequest: (TRIGGERREQUEST, lambda msg: "%s:%s" % (msg.module, msg.parameter) if msg.parameter else msg.module, ),
|
||||
HeartbeatRequest: (HEARTBEATREQUEST, 'nonce',),
|
||||
HeartbeatReply: (HEARTBEATREPLY, 'nonce',),
|
||||
HelpMessage: (HELPREQUEST, ),
|
||||
ErrorMessage: (ERRORREPLY, 'errorclass', 'errorinfo',),
|
||||
ErrorMessage: (ERRORREPLY, "errorclass", encode_error_msg,),
|
||||
Value: (EVENT, lambda msg: "%s:%s" % (msg.module, msg.parameter or (msg.command + '()'))
|
||||
if msg.parameter or msg.command else msg.module,
|
||||
encode_value_data,),
|
||||
@ -185,7 +190,8 @@ class DemoEncoder(MessageEncoder):
|
||||
if msgspec is None and data:
|
||||
return ErrorMessage(errorclass='InternalError',
|
||||
errorinfo='Regex matched json, but not spec!',
|
||||
is_request=True)
|
||||
is_request=True,
|
||||
origin=encoded)
|
||||
|
||||
if msgtype in self.DECODEMAP:
|
||||
if msgspec and ':' in msgspec:
|
||||
@ -197,13 +203,17 @@ class DemoEncoder(MessageEncoder):
|
||||
data = json.loads(data)
|
||||
except ValueError as err:
|
||||
return ErrorMessage(errorclass='BadValue',
|
||||
errorinfo=[repr(err), str(encoded)])
|
||||
return self.DECODEMAP[msgtype](msgspec, data)
|
||||
errorinfo=[repr(err), str(encoded)],
|
||||
origin=encoded)
|
||||
msg = self.DECODEMAP[msgtype](msgspec, data)
|
||||
msg.setvalue("origin",encoded)
|
||||
return msg
|
||||
return ErrorMessage(
|
||||
errorclass='SyntaxError',
|
||||
errorinfo='%r: No Such Messagetype defined!' %
|
||||
encoded,
|
||||
is_request=True)
|
||||
is_request=True,
|
||||
origin=encoded)
|
||||
|
||||
def tests(self):
|
||||
print "---- Testing encoding -----"
|
||||
|
@ -128,7 +128,7 @@ class TCPServer(SocketServer.ThreadingTCPServer):
|
||||
# and encoders (to en/decode messages from frames)
|
||||
self.framingCLS = FRAMERS[interfaceopts.pop('framing', 'none')]
|
||||
self.encodingCLS = ENCODERS[interfaceopts.pop('encoding', 'pickle')]
|
||||
self.log.debug("TCPServer binding to %s:%d" % (bindto, portnum))
|
||||
self.log.info("TCPServer binding to %s:%d" % (bindto, portnum))
|
||||
self.log.debug("TCPServer using framing=%s" % self.framingCLS.__name__)
|
||||
self.log.debug("TCPServer using encoding=%s" %
|
||||
self.encodingCLS.__name__)
|
||||
|
@ -29,6 +29,7 @@ class Message(object):
|
||||
is_reply = False
|
||||
is_error = False
|
||||
qualifiers = {}
|
||||
origin = "<unknown source>"
|
||||
|
||||
def __init__(self, **kwds):
|
||||
self.ARGS = set()
|
||||
@ -68,11 +69,39 @@ class Value(object):
|
||||
devspec = '%s:%s()' % (devspec, self.command)
|
||||
return '%s:Value(%s)' % (devspec, ', '.join(
|
||||
[repr(self.value)] +
|
||||
['%s=%s' % (k, repr(v)) for k, v in self.qualifiers.items()]))
|
||||
['%s=%s' % (k, format_time(v) if k=="timestamp" else repr(v)) for k, v in self.qualifiers.items()]))
|
||||
|
||||
|
||||
class IdentifyRequest(Message):
|
||||
class Request(Message):
|
||||
is_request = True
|
||||
def get_reply(self):
|
||||
"""returns a Reply object prefilled with the attributes from this request."""
|
||||
m = Message()
|
||||
m.is_request = False
|
||||
m.is_reply = True
|
||||
m.is_error = False
|
||||
m.qualifiers = self.qualifiers
|
||||
m.origin = self.origin
|
||||
for k in self.ARGS:
|
||||
m.setvalue(k, self.__dict__[k])
|
||||
return m
|
||||
|
||||
def get_error(self, errorclass, errorinfo):
|
||||
"""returns a Reply object prefilled with the attributes from this request."""
|
||||
m = ErrorMessage()
|
||||
m.qualifiers = self.qualifiers
|
||||
m.origin = self.origin
|
||||
for k in self.ARGS:
|
||||
m.setvalue(k, self.__dict__[k])
|
||||
m.setvalue("errorclass", errorclass[:-5]
|
||||
if errorclass.endswith('rror')
|
||||
else errorclass)
|
||||
m.setvalue("errorinfo", errorinfo)
|
||||
return m
|
||||
|
||||
|
||||
class IdentifyRequest(Request):
|
||||
pass
|
||||
|
||||
|
||||
class IdentifyReply(Message):
|
||||
@ -80,8 +109,8 @@ class IdentifyReply(Message):
|
||||
version_string = None
|
||||
|
||||
|
||||
class DescribeRequest(Message):
|
||||
is_request = True
|
||||
class DescribeRequest(Request):
|
||||
pass
|
||||
|
||||
|
||||
class DescribeReply(Message):
|
||||
@ -90,24 +119,23 @@ class DescribeReply(Message):
|
||||
description = None
|
||||
|
||||
|
||||
class ActivateRequest(Message):
|
||||
is_request = True
|
||||
class ActivateRequest(Request):
|
||||
pass
|
||||
|
||||
|
||||
class ActivateReply(Message):
|
||||
is_reply = True
|
||||
|
||||
|
||||
class DeactivateRequest(Message):
|
||||
is_request = True
|
||||
class DeactivateRequest(Request):
|
||||
pass
|
||||
|
||||
|
||||
class DeactivateReply(Message):
|
||||
is_reply = True
|
||||
|
||||
|
||||
class CommandRequest(Message):
|
||||
is_request = True
|
||||
class CommandRequest(Request):
|
||||
command = ''
|
||||
arguments = []
|
||||
|
||||
@ -118,8 +146,7 @@ class CommandReply(Message):
|
||||
result = None
|
||||
|
||||
|
||||
class WriteRequest(Message):
|
||||
is_request = True
|
||||
class WriteRequest(Request):
|
||||
module = None
|
||||
parameter = None
|
||||
value = None
|
||||
@ -132,14 +159,13 @@ class WriteReply(Message):
|
||||
value = None
|
||||
|
||||
|
||||
class PollRequest(Message):
|
||||
class PollRequest(Request):
|
||||
is_request = True
|
||||
module = None
|
||||
parameter = None
|
||||
|
||||
|
||||
class HeartbeatRequest(Message):
|
||||
is_request = True
|
||||
class HeartbeatRequest(Request):
|
||||
nonce = 'alive'
|
||||
|
||||
|
||||
@ -163,16 +189,7 @@ class ErrorMessage(Message):
|
||||
errorinfo = None
|
||||
|
||||
|
||||
class HelpMessage(Message):
|
||||
is_reply = True
|
||||
is_request = True
|
||||
class HelpMessage(Request):
|
||||
is_reply = True #!sic!
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Minimal testing of messages....")
|
||||
m = Message(MSGTYPE='test', a=1, b=2, c='x')
|
||||
print m
|
||||
print ReadMessage(devs=['a'], result=[Value(12.3)])
|
||||
|
||||
print "OK"
|
||||
print
|
||||
|
Loading…
x
Reference in New Issue
Block a user