polishing for a demo
+ adopting additional requests Change-Id: If5ca29b5d247f1bc429ca101b0081b1d14f6e6f1
This commit is contained in:
@@ -19,7 +19,6 @@
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Define SECoP Device classes
|
||||
|
||||
"""
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Dispatcher for SECoP Messages
|
||||
|
||||
Interface to the service offering part:
|
||||
@@ -47,7 +46,6 @@ from secop.lib.parsing import format_time
|
||||
|
||||
|
||||
class Dispatcher(object):
|
||||
|
||||
def __init__(self, logger, options):
|
||||
self.equipment_id = options.pop('equipment_id')
|
||||
self.log = logger
|
||||
@@ -85,15 +83,18 @@ class Dispatcher(object):
|
||||
reply = handler(conn, msg)
|
||||
except SECOPError as err:
|
||||
self.log.exception(err)
|
||||
reply = msg.get_error(errorclass=err.__class__.__name__,
|
||||
errorinfo=[repr(err), str(msg)])
|
||||
reply = msg.get_error(
|
||||
errorclass=err.__class__.__name__,
|
||||
errorinfo=[repr(err), str(msg)])
|
||||
except (ValueError, TypeError) as err:
|
||||
reply = msg.get_error(errorclass='BadValue',
|
||||
errorinfo=[repr(err), str(msg)])
|
||||
reply = msg.get_error(
|
||||
errorclass='BadValue',
|
||||
errorinfo=[repr(err), str(msg)])
|
||||
except Exception as err:
|
||||
self.log.exception(err)
|
||||
reply = msg.get_error(errorclass='InternalError',
|
||||
errorinfo=[repr(err), str(msg)])
|
||||
reply = msg.get_error(
|
||||
errorclass='InternalError',
|
||||
errorinfo=[repr(err), str(msg)])
|
||||
else:
|
||||
self.log.debug('Can not handle msg %r' % msg)
|
||||
reply = self.unhandled(conn, msg)
|
||||
@@ -106,8 +107,8 @@ class Dispatcher(object):
|
||||
listeners = self._connections
|
||||
else:
|
||||
if getattr(msg, 'command', None) is None:
|
||||
eventname = '%s:%s' % (
|
||||
msg.module, msg.parameter if msg.parameter else 'value')
|
||||
eventname = '%s:%s' % (msg.module, msg.parameter
|
||||
if msg.parameter else 'value')
|
||||
else:
|
||||
eventname = '%s:%s()' % (msg.module, msg.command)
|
||||
listeners = self._subscriptions.get(eventname, [])
|
||||
@@ -198,8 +199,7 @@ class Dispatcher(object):
|
||||
res = {}
|
||||
for cmdname, cmdobj in self.get_module(modulename).CMDS.items():
|
||||
res[cmdname] = cmdobj.as_dict()
|
||||
self.log.debug('list cmds for module %s -> %r' %
|
||||
(modulename, res))
|
||||
self.log.debug('list cmds for module %s -> %r' % (modulename, res))
|
||||
return res
|
||||
self.log.debug('-> module is not to be exported!')
|
||||
return {}
|
||||
@@ -210,12 +210,13 @@ class Dispatcher(object):
|
||||
for modulename in self._export:
|
||||
module = self.get_module(modulename)
|
||||
# some of these need rework !
|
||||
dd = {'class': module.__class__.__name__,
|
||||
'bases': [b.__name__ for b in module.__class__.__bases__],
|
||||
'parameters': self.list_module_params(modulename),
|
||||
'commands': self.list_module_cmds(modulename),
|
||||
'baseclass': 'Readable',
|
||||
}
|
||||
dd = {
|
||||
'class': module.__class__.__name__,
|
||||
'bases': [b.__name__ for b in module.__class__.__bases__],
|
||||
'parameters': self.list_module_params(modulename),
|
||||
'commands': self.list_module_cmds(modulename),
|
||||
'interfaceclass': 'Readable',
|
||||
}
|
||||
result['modules'][modulename] = dd
|
||||
result['equipment_id'] = self.equipment_id
|
||||
result['firmware'] = 'The SECoP playground'
|
||||
@@ -244,8 +245,11 @@ class Dispatcher(object):
|
||||
# note: exceptions are handled in handle_request, not here!
|
||||
func = getattr(moduleobj, 'do' + command)
|
||||
res = func(*arguments)
|
||||
res = CommandReply(module=modulename, command=command,
|
||||
result=res, qualifiers=dict(t=time.time()))
|
||||
res = CommandReply(
|
||||
module=modulename,
|
||||
command=command,
|
||||
result=res,
|
||||
qualifiers=dict(t=time.time()))
|
||||
# res = Value(modulename, command=command, value=func(*arguments), t=time.time())
|
||||
return res
|
||||
|
||||
@@ -268,15 +272,11 @@ class Dispatcher(object):
|
||||
setattr(moduleobj, pname, value)
|
||||
if pobj.timestamp:
|
||||
return WriteReply(
|
||||
module=modulename, parameter=pname, value=[
|
||||
pobj.value, dict(
|
||||
t=format_time(pobj.timestamp))])
|
||||
module=modulename,
|
||||
parameter=pname,
|
||||
value=[pobj.value, dict(t=format_time(pobj.timestamp))])
|
||||
return WriteReply(
|
||||
module=modulename,
|
||||
parameter=pname,
|
||||
value=[
|
||||
pobj.value,
|
||||
{}])
|
||||
module=modulename, parameter=pname, value=[pobj.value, {}])
|
||||
|
||||
def _getParamValue(self, modulename, pname):
|
||||
moduleobj = self.get_module(modulename)
|
||||
@@ -370,9 +370,12 @@ class Dispatcher(object):
|
||||
except SECOPError as e:
|
||||
self.log.error('decide what to do here!')
|
||||
self.log.exception(e)
|
||||
res = Value(module=modulename, parameter=pname,
|
||||
value=pobj.value, t=pobj.timestamp,
|
||||
unit=pobj.unit)
|
||||
res = Value(
|
||||
module=modulename,
|
||||
parameter=pname,
|
||||
value=pobj.value,
|
||||
t=pobj.timestamp,
|
||||
unit=pobj.unit)
|
||||
if res.value != Ellipsis: # means we do not have a value at all so skip this
|
||||
self.broadcast_event(res)
|
||||
conn.queue_async_reply(ActivateReply(**msg.as_dict()))
|
||||
@@ -392,5 +395,5 @@ class Dispatcher(object):
|
||||
(no handle_<messagename> method was defined)
|
||||
"""
|
||||
self.log.error('IGN: got unhandled request %s' % msg)
|
||||
return msg.get_error(errorclass="InternalError",
|
||||
errorinfo="Unhandled Request")
|
||||
return msg.get_error(
|
||||
errorclass="InternalError", errorinfo="Unhandled Request")
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Messages"""
|
||||
|
||||
# implement as class as they may need some internal 'state' later on
|
||||
@@ -39,6 +38,7 @@ class MessageEncoder(object):
|
||||
"""decodes the given frame to a message object"""
|
||||
raise NotImplemented
|
||||
|
||||
|
||||
from demo_v2 import DemoEncoder as DemoEncoderV2
|
||||
from demo_v3 import DemoEncoder as DemoEncoderV3
|
||||
from demo_v4 import DemoEncoder as DemoEncoderV4
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Messages"""
|
||||
|
||||
# implement as class as they may need some internal 'state' later on
|
||||
@@ -32,11 +31,11 @@ from secop.lib.parsing import *
|
||||
import re
|
||||
|
||||
DEMO_RE = re.compile(
|
||||
r'^([!+-])?(\*|[a-z_][a-z_0-9]*)?(?:\:(\*|[a-z_][a-z_0-9]*))?(?:\:(\*|[a-z_][a-z_0-9]*))?(?:\=(.*))?')
|
||||
r'^([!+-])?(\*|[a-z_][a-z_0-9]*)?(?:\:(\*|[a-z_][a-z_0-9]*))?(?:\:(\*|[a-z_][a-z_0-9]*))?(?:\=(.*))?'
|
||||
)
|
||||
|
||||
|
||||
class DemoEncoder(MessageEncoder):
|
||||
|
||||
def decode(sef, encoded):
|
||||
# match [!][*|devicename][: *|paramname [: *|propname]] [=value]
|
||||
match = DEMO_RE.match(encoded)
|
||||
@@ -46,8 +45,8 @@ class DemoEncoder(MessageEncoder):
|
||||
print "parsing", assign,
|
||||
assign = parse_args(assign)
|
||||
print "->", assign
|
||||
return messages.DemoRequest(
|
||||
novalue, devname, pname, propname, assign)
|
||||
return messages.DemoRequest(novalue, devname, pname, propname,
|
||||
assign)
|
||||
return messages.HelpRequest()
|
||||
|
||||
def encode(self, msg):
|
||||
@@ -66,8 +65,13 @@ class DemoEncoder(MessageEncoder):
|
||||
return '~InternalError~'
|
||||
return result
|
||||
|
||||
def _encode_AsyncDataUnit(self, devname, pname, value, timestamp,
|
||||
error=None, unit=''):
|
||||
def _encode_AsyncDataUnit(self,
|
||||
devname,
|
||||
pname,
|
||||
value,
|
||||
timestamp,
|
||||
error=None,
|
||||
unit=''):
|
||||
return '#%s:%s=%s;t=%.3f' % (devname, pname, value, timestamp)
|
||||
|
||||
def _encode_Error(self, error):
|
||||
@@ -98,5 +102,7 @@ class DemoEncoder(MessageEncoder):
|
||||
return '~InvalidValueForParamError~ %s:%s=%r' % (device, param, value)
|
||||
|
||||
def _encode_HelpReply(self):
|
||||
return ['Help not yet implemented!',
|
||||
'ask Markus Zolliker about the protocol']
|
||||
return [
|
||||
'Help not yet implemented!',
|
||||
'ask Markus Zolliker about the protocol'
|
||||
]
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Messages"""
|
||||
|
||||
# implement as class as they may need some internal 'state' later on
|
||||
@@ -93,7 +92,6 @@ DEMO_RE_OTHER = re.compile(
|
||||
|
||||
|
||||
class DemoEncoder(MessageEncoder):
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
MessageEncoder.__init__(self, *args, **kwds)
|
||||
self.result = [] # for decoding
|
||||
@@ -112,8 +110,8 @@ class DemoEncoder(MessageEncoder):
|
||||
r.append("help ... more to come")
|
||||
return '\n'.join(r)
|
||||
|
||||
if isinstance(msg, (ListMessage, SubscribeMessage,
|
||||
UnsubscribeMessage, TriggerMessage)):
|
||||
if isinstance(msg, (ListMessage, SubscribeMessage, UnsubscribeMessage,
|
||||
TriggerMessage)):
|
||||
msgtype = msg.MSGTYPE
|
||||
if msg.result:
|
||||
if msg.devs:
|
||||
@@ -145,9 +143,7 @@ class DemoEncoder(MessageEncoder):
|
||||
# encode 1..N replies
|
||||
result.append(
|
||||
encode_value(
|
||||
val,
|
||||
'write',
|
||||
targetvalue=msg.target))
|
||||
val, 'write', targetvalue=msg.target))
|
||||
if not msg.result:
|
||||
# encode a request (no results -> reply, else an error would
|
||||
# have been sent)
|
||||
@@ -164,28 +160,17 @@ class DemoEncoder(MessageEncoder):
|
||||
encode_value(
|
||||
val,
|
||||
'command',
|
||||
cmd='%s(%s)' %
|
||||
(msg.cmd,
|
||||
','.join(
|
||||
msg.args))))
|
||||
cmd='%s(%s)' % (msg.cmd, ','.join(msg.args))))
|
||||
if not msg.result:
|
||||
# encode a request (no results -> reply, else an error would
|
||||
# have been sent)
|
||||
result.append(
|
||||
'%s:%s(%s)' %
|
||||
(devspec(
|
||||
msg, 'command'), msg.cmd, ','.join(
|
||||
msg.args)))
|
||||
result.append('%s:%s(%s)' % (devspec(msg, 'command'), msg.cmd,
|
||||
','.join(msg.args)))
|
||||
return '\n'.join(result)
|
||||
|
||||
if isinstance(msg, ErrorMessage):
|
||||
return (
|
||||
'%s %s' %
|
||||
(devspec(
|
||||
msg,
|
||||
'error %s' %
|
||||
msg.errortype),
|
||||
msg.errorstring)).strip()
|
||||
return ('%s %s' % (devspec(msg, 'error %s' % msg.errortype),
|
||||
msg.errorstring)).strip()
|
||||
|
||||
return 'Can not handle object %r!' % msg
|
||||
|
||||
@@ -246,6 +231,7 @@ class DemoEncoder(MessageEncoder):
|
||||
if sep in stuff:
|
||||
return stuff.split(sep)
|
||||
return [stuff]
|
||||
|
||||
devs = helper(mgroups.pop('devs'))
|
||||
pars = helper(mgroups.pop('pars'))
|
||||
props = helper(mgroups.pop('props'))
|
||||
@@ -272,12 +258,8 @@ class DemoEncoder(MessageEncoder):
|
||||
# reformat qualifiers
|
||||
print mgroups
|
||||
quals = dict(
|
||||
qual.split(
|
||||
'=',
|
||||
1) for qual in helper(
|
||||
mgroups.pop(
|
||||
'qualifiers',
|
||||
';')))
|
||||
qual.split('=', 1)
|
||||
for qual in helper(mgroups.pop('qualifiers', ';')))
|
||||
|
||||
# reformat value
|
||||
result = []
|
||||
@@ -296,48 +278,49 @@ class DemoEncoder(MessageEncoder):
|
||||
|
||||
# construct messageobj
|
||||
if msgtype in MESSAGE:
|
||||
return MESSAGE[msgtype](
|
||||
devs=devs,
|
||||
pars=pars,
|
||||
props=props,
|
||||
result=result,
|
||||
**mgroups)
|
||||
return MESSAGE[msgtype](devs=devs,
|
||||
pars=pars,
|
||||
props=props,
|
||||
result=result,
|
||||
**mgroups)
|
||||
|
||||
return ErrorMessage(errortype="SyntaxError",
|
||||
errorstring="Can't handle %r" % encoded)
|
||||
return ErrorMessage(
|
||||
errortype="SyntaxError", errorstring="Can't handle %r" % encoded)
|
||||
|
||||
def tests(self):
|
||||
testmsg = ['list',
|
||||
'list *',
|
||||
'list device',
|
||||
'list device:param1,param2',
|
||||
'list *:*',
|
||||
'list *=ts,tcoil,mf,lhe,ln2;',
|
||||
'read blub=12;t=3',
|
||||
'command ts:stop()',
|
||||
'command mf:quench(1,"now")',
|
||||
'error GibbetNich query x:y:z=9 "blubub blah"',
|
||||
'#3',
|
||||
'read blub:a=12;t=3',
|
||||
'read blub:b=13;t=3.1',
|
||||
'read blub:c=14;t=3.3',
|
||||
]
|
||||
testmsg = [
|
||||
'list',
|
||||
'list *',
|
||||
'list device',
|
||||
'list device:param1,param2',
|
||||
'list *:*',
|
||||
'list *=ts,tcoil,mf,lhe,ln2;',
|
||||
'read blub=12;t=3',
|
||||
'command ts:stop()',
|
||||
'command mf:quench(1,"now")',
|
||||
'error GibbetNich query x:y:z=9 "blubub blah"',
|
||||
'#3',
|
||||
'read blub:a=12;t=3',
|
||||
'read blub:b=13;t=3.1',
|
||||
'read blub:c=14;t=3.3',
|
||||
]
|
||||
for m in testmsg:
|
||||
print repr(m)
|
||||
print self.decode(m)
|
||||
print
|
||||
|
||||
|
||||
DEMO_RE_MZ = re.compile(r"""^(?P<type>[a-z]+)? # request type word (read/write/list/...)
|
||||
DEMO_RE_MZ = re.compile(
|
||||
r"""^(?P<type>[a-z]+)? # request type word (read/write/list/...)
|
||||
\ ? # optional space
|
||||
(?P<device>[a-z][a-z0-9_]*)? # optional devicename
|
||||
(?:\:(?P<param>[a-z0-9_]*) # optional ':'+paramname
|
||||
(?:\:(?P<prop>[a-z0-9_]*))?)? # optinal ':' + propname
|
||||
(?:(?P<op>[=\?])(?P<value>[^;]+)(?:\;(?P<quals>.*))?)?$""", re.X)
|
||||
(?:(?P<op>[=\?])(?P<value>[^;]+)(?:\;(?P<quals>.*))?)?$""",
|
||||
re.X)
|
||||
|
||||
|
||||
class DemoEncoder_MZ(MessageEncoder):
|
||||
|
||||
def decode(sef, encoded):
|
||||
m = DEMO_RE_MZ.match(encoded)
|
||||
if m:
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Messages"""
|
||||
|
||||
# implement as class as they may need some internal 'state' later on
|
||||
@@ -47,7 +46,8 @@ DEMO_RE = re.compile(
|
||||
IDENTREQUEST = '*IDN?' # literal
|
||||
# literal! first part is fixed!
|
||||
#IDENTREPLY = 'SECoP, SECoPTCP, V2016-11-30, rc1'
|
||||
IDENTREPLY = 'SINE2020&ISSE,SECoP,V2016-11-30,rc1'
|
||||
#IDENTREPLY = 'SINE2020&ISSE,SECoP,V2016-11-30,rc1'
|
||||
IDENTREPLY = 'SINE2020&ISSE,SECoP,V2017-01-25,rc1'
|
||||
DESCRIPTIONSREQUEST = 'describe' # literal
|
||||
DESCRIPTIONREPLY = 'describing' # +<id> +json
|
||||
ENABLEEVENTSREQUEST = 'activate' # literal
|
||||
@@ -69,10 +69,22 @@ HEARTBEATREPLY = 'pong' # +nonce_without_space
|
||||
ERRORREPLY = 'error' # +errorclass +json_extended_info
|
||||
HELPREQUEST = 'help' # literal
|
||||
HELPREPLY = 'helping' # +line number +json_text
|
||||
ERRORCLASSES = ['NoSuchDevice', 'NoSuchParameter', 'NoSuchCommand',
|
||||
'CommandFailed', 'ReadOnly', 'BadValue', 'CommunicationFailed',
|
||||
'IsBusy', 'IsError', 'ProtocolError', 'InternalError',
|
||||
'CommandRunning', 'Disabled', ]
|
||||
ERRORCLASSES = [
|
||||
'NoSuchDevice',
|
||||
'NoSuchParameter',
|
||||
'NoSuchCommand',
|
||||
'CommandFailed',
|
||||
'ReadOnly',
|
||||
'BadValue',
|
||||
'CommunicationFailed',
|
||||
'IsBusy',
|
||||
'IsError',
|
||||
'ProtocolError',
|
||||
'InternalError',
|
||||
'CommandRunning',
|
||||
'Disabled',
|
||||
]
|
||||
|
||||
# note: above strings need to be unique in the sense, that none is/or
|
||||
# starts with another
|
||||
|
||||
@@ -93,33 +105,61 @@ def encode_value_data(vobj):
|
||||
|
||||
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')]
|
||||
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.
|
||||
ENCODEMAP = {
|
||||
IdentifyRequest: (IDENTREQUEST,),
|
||||
IdentifyReply: (IDENTREPLY,),
|
||||
DescribeRequest: (DESCRIPTIONSREQUEST,),
|
||||
DescribeReply: (DESCRIPTIONREPLY, 'equipment_id', 'description',),
|
||||
ActivateRequest: (ENABLEEVENTSREQUEST,),
|
||||
ActivateReply: (ENABLEEVENTSREPLY,),
|
||||
DeactivateRequest: (DISABLEEVENTSREQUEST,),
|
||||
DeactivateReply: (DISABLEEVENTSREPLY,),
|
||||
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', ),
|
||||
PollRequest: (TRIGGERREQUEST, lambda msg: "%s:%s" % (msg.module, msg.parameter) if msg.parameter else msg.module, ),
|
||||
HeartbeatRequest: (HEARTBEATREQUEST, 'nonce',),
|
||||
HeartbeatReply: (HEARTBEATREPLY, 'nonce',),
|
||||
IdentifyRequest: (IDENTREQUEST, ),
|
||||
IdentifyReply: (IDENTREPLY, ),
|
||||
DescribeRequest: (DESCRIPTIONSREQUEST, ),
|
||||
DescribeReply: (
|
||||
DESCRIPTIONREPLY,
|
||||
'equipment_id',
|
||||
'description', ),
|
||||
ActivateRequest: (ENABLEEVENTSREQUEST, ),
|
||||
ActivateReply: (ENABLEEVENTSREPLY, ),
|
||||
DeactivateRequest: (DISABLEEVENTSREQUEST, ),
|
||||
DeactivateReply: (DISABLEEVENTSREPLY, ),
|
||||
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', ),
|
||||
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", 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,),
|
||||
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, ),
|
||||
}
|
||||
DECODEMAP = {
|
||||
IDENTREQUEST: lambda spec, data: IdentifyRequest(),
|
||||
@@ -177,8 +217,10 @@ class DemoEncoder(MessageEncoder):
|
||||
for msgcls, parts in self.ENCODEMAP.items():
|
||||
if isinstance(msg, msgcls):
|
||||
# resolve lambdas
|
||||
parts = [parts[0]] + [p(msg) if callable(p)
|
||||
else getattr(msg, p) for p in parts[1:]]
|
||||
parts = [parts[0]] + [
|
||||
p(msg) if callable(p) else getattr(msg, p)
|
||||
for p in parts[1:]
|
||||
]
|
||||
if len(parts) > 1:
|
||||
parts[1] = str(parts[1])
|
||||
if len(parts) == 3:
|
||||
@@ -199,10 +241,11 @@ class DemoEncoder(MessageEncoder):
|
||||
# is_request=True)
|
||||
msgtype, msgspec, data = match.groups()
|
||||
if msgspec is None and data:
|
||||
return ErrorMessage(errorclass='Internal',
|
||||
errorinfo='Regex matched json, but not spec!',
|
||||
is_request=True,
|
||||
origin=encoded)
|
||||
return ErrorMessage(
|
||||
errorclass='Internal',
|
||||
errorinfo='Regex matched json, but not spec!',
|
||||
is_request=True,
|
||||
origin=encoded)
|
||||
|
||||
if msgtype in self.DECODEMAP:
|
||||
if msgspec and ':' in msgspec:
|
||||
@@ -213,16 +256,16 @@ class DemoEncoder(MessageEncoder):
|
||||
try:
|
||||
data = json.loads(data)
|
||||
except ValueError as err:
|
||||
return ErrorMessage(errorclass='BadValue',
|
||||
errorinfo=[repr(err), str(encoded)],
|
||||
origin=encoded)
|
||||
return ErrorMessage(
|
||||
errorclass='BadValue',
|
||||
errorinfo=[repr(err), str(encoded)],
|
||||
origin=encoded)
|
||||
msg = self.DECODEMAP[msgtype](msgspec, data)
|
||||
msg.setvalue("origin", encoded)
|
||||
return msg
|
||||
return ErrorMessage(
|
||||
errorclass='Protocol',
|
||||
errorinfo='%r: No Such Messagetype defined!' %
|
||||
encoded,
|
||||
errorinfo='%r: No Such Messagetype defined!' % encoded,
|
||||
is_request=True,
|
||||
origin=encoded)
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Messages"""
|
||||
|
||||
# implement as class as they may need some internal 'state' later on
|
||||
@@ -36,7 +35,6 @@ except ImportError:
|
||||
|
||||
|
||||
class PickleEncoder(MessageEncoder):
|
||||
|
||||
def encode(self, messageobj):
|
||||
"""msg object -> transport layer message"""
|
||||
return pickle.dumps(messageobj)
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Messages"""
|
||||
|
||||
# implement as class as they may need some internal 'state' later on
|
||||
@@ -32,13 +31,12 @@ from secop.lib.parsing import *
|
||||
import re
|
||||
import ast
|
||||
|
||||
|
||||
SCPMESSAGE = re.compile(
|
||||
r'^(?:(?P<errorcode>[0-9@])\ )?(?P<device>[a-zA-Z0-9_\*]*)(?:/(?P<param>[a-zA-Z0-9_\*]*))+(?P<op>[-+=\?\ ])?(?P<value>.*)')
|
||||
r'^(?:(?P<errorcode>[0-9@])\ )?(?P<device>[a-zA-Z0-9_\*]*)(?:/(?P<param>[a-zA-Z0-9_\*]*))+(?P<op>[-+=\?\ ])?(?P<value>.*)'
|
||||
)
|
||||
|
||||
|
||||
class SCPEncoder(MessageEncoder):
|
||||
|
||||
def encode(self, msg):
|
||||
"""msg object -> transport layer message"""
|
||||
# fun for Humans
|
||||
@@ -48,20 +46,24 @@ class SCPEncoder(MessageEncoder):
|
||||
r.append("'/version?' to query the current version")
|
||||
r.append("'/modules?' to query the list of modules")
|
||||
r.append(
|
||||
"'<module>/parameters?' to query the list of params of a module")
|
||||
"'<module>/parameters?' to query the list of params of a module"
|
||||
)
|
||||
r.append("'<module>/value?' to query the value of a module")
|
||||
r.append("'<module>/status?' to query the status of a module")
|
||||
r.append("'<module>/target=<new_value>' to move a module")
|
||||
r.append("replies copy the request and are prefixed with an errorcode:")
|
||||
r.append(
|
||||
"0=OK,3=NoSuchCommand,4=NosuchDevice,5=NoSuchParam,6=SyntaxError,7=BadValue,8=Readonly,9=Forbidden,@=Async")
|
||||
"replies copy the request and are prefixed with an errorcode:")
|
||||
r.append(
|
||||
"0=OK,3=NoSuchCommand,4=NosuchDevice,5=NoSuchParam,6=SyntaxError,7=BadValue,8=Readonly,9=Forbidden,@=Async"
|
||||
)
|
||||
r.append("extensions: @-prefix as error-code,")
|
||||
r.append("'<module>/+' subscribe all params of module")
|
||||
r.append("'<module>/<param>+' subscribe a param of a module")
|
||||
r.append("use '-' instead of '+' to unsubscribe")
|
||||
r.append("'<module>/commands?' list of commands")
|
||||
r.append(
|
||||
"'<module>/<command>@[possible args] execute command (ex. 'stop@')")
|
||||
"'<module>/<command>@[possible args] execute command (ex. 'stop@')"
|
||||
)
|
||||
return '\n'.join(r)
|
||||
|
||||
return {
|
||||
@@ -117,7 +119,7 @@ class SCPEncoder(MessageEncoder):
|
||||
def decode(self, encoded):
|
||||
"""transport layer message -> msg object"""
|
||||
match = SCPMESSAGE.match(encoded)
|
||||
if not(match):
|
||||
if not (match):
|
||||
return HelpRequest()
|
||||
err, dev, par, op, val = match.groups()
|
||||
if val is not None:
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Messages"""
|
||||
|
||||
# implement as class as they may need some internal 'state' later on
|
||||
@@ -31,7 +30,6 @@ from secop.lib.parsing import *
|
||||
|
||||
|
||||
class TextEncoder(MessageEncoder):
|
||||
|
||||
def __init__(self):
|
||||
# build safe namespace
|
||||
ns = dict()
|
||||
|
||||
@@ -19,12 +19,10 @@
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Define (internal) SECoP Errors"""
|
||||
|
||||
|
||||
class SECOPError(RuntimeError):
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
self.args = args
|
||||
for k, v in kwds.items():
|
||||
@@ -91,8 +89,7 @@ EXCEPTIONS = dict(
|
||||
BadValue=BadValueError,
|
||||
Readonly=ReadonlyError,
|
||||
CommandFailed=CommandFailedError,
|
||||
InvalidParam=InvalidParamValueError,
|
||||
)
|
||||
InvalidParam=InvalidParamValueError, )
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Minimal testing of errors....")
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Frames"""
|
||||
|
||||
|
||||
@@ -45,7 +44,6 @@ class Framer(object):
|
||||
"""resets the de/encoding stage (clears internal information)"""
|
||||
raise NotImplemented
|
||||
|
||||
|
||||
# now some Implementations
|
||||
from null import NullFramer
|
||||
from eol import EOLFramer
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Frames"""
|
||||
|
||||
from secop.protocol.framing import Framer
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Frames"""
|
||||
|
||||
from secop.protocol.framing import Framer
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Frames"""
|
||||
|
||||
from secop.protocol.framing import Framer
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Frames"""
|
||||
|
||||
from secop.protocol.framing import Framer
|
||||
|
||||
@@ -23,9 +23,7 @@
|
||||
|
||||
from tcp import TCPServer
|
||||
|
||||
INTERFACES = {
|
||||
'tcp': TCPServer,
|
||||
}
|
||||
INTERFACES = {'tcp': TCPServer, }
|
||||
|
||||
# for 'from protocol.interface import *' to only import the dict
|
||||
__ALL__ = ['INTERFACES']
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""provides tcp interface to the SECoP Server"""
|
||||
|
||||
import os
|
||||
@@ -36,7 +35,6 @@ from secop.protocol.messages import HelpMessage
|
||||
|
||||
|
||||
class TCPRequestHandler(SocketServer.BaseRequestHandler):
|
||||
|
||||
def setup(self):
|
||||
self.log = self.server.log
|
||||
self._queue = collections.deque(maxlen=100)
|
||||
@@ -49,13 +47,13 @@ class TCPRequestHandler(SocketServer.BaseRequestHandler):
|
||||
mysocket = self.request
|
||||
clientaddr = self.client_address
|
||||
serverobj = self.server
|
||||
self.log.debug("handling new connection from %s" % repr(clientaddr))
|
||||
self.log.info("handling new connection from %s:%d" % clientaddr)
|
||||
|
||||
# notify dispatcher of us
|
||||
serverobj.dispatcher.add_connection(self)
|
||||
|
||||
mysocket.settimeout(.3)
|
||||
# mysocket.setblocking(False)
|
||||
# mysocket.setblocking(False)
|
||||
# start serving
|
||||
while True:
|
||||
# send replys fist, then listen for requests, timing out after 0.1s
|
||||
@@ -66,7 +64,10 @@ class TCPRequestHandler(SocketServer.BaseRequestHandler):
|
||||
outmsg = self._queue.popleft()
|
||||
outframes = self.encoding.encode(outmsg)
|
||||
outdata = self.framing.encode(outframes)
|
||||
mysocket.sendall(outdata)
|
||||
try:
|
||||
mysocket.sendall(outdata)
|
||||
except Exception:
|
||||
return
|
||||
|
||||
# XXX: improve: use polling/select here?
|
||||
try:
|
||||
@@ -101,6 +102,7 @@ class TCPRequestHandler(SocketServer.BaseRequestHandler):
|
||||
|
||||
def finish(self):
|
||||
"""called when handle() terminates, i.e. the socket closed"""
|
||||
self.log.info('closing connection from %s:%d' % self.client_address)
|
||||
# notify dispatcher
|
||||
self.server.dispatcher.remove_connection(self)
|
||||
# close socket
|
||||
@@ -132,7 +134,6 @@ class TCPServer(SocketServer.ThreadingTCPServer):
|
||||
self.log.debug("TCPServer using framing=%s" % self.framingCLS.__name__)
|
||||
self.log.debug("TCPServer using encoding=%s" %
|
||||
self.encodingCLS.__name__)
|
||||
SocketServer.ThreadingTCPServer.__init__(self, (bindto, portnum),
|
||||
TCPRequestHandler,
|
||||
bind_and_activate=True)
|
||||
SocketServer.ThreadingTCPServer.__init__(
|
||||
self, (bindto, portnum), TCPRequestHandler, bind_and_activate=True)
|
||||
self.log.info("TCPServer initiated")
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Define SECoP Messages"""
|
||||
|
||||
|
||||
@@ -51,8 +50,11 @@ class Message(object):
|
||||
|
||||
|
||||
class Value(object):
|
||||
|
||||
def __init__(self, module, parameter=None, command=None, value=Ellipsis,
|
||||
def __init__(self,
|
||||
module,
|
||||
parameter=None,
|
||||
command=None,
|
||||
value=Ellipsis,
|
||||
**qualifiers):
|
||||
self.module = module
|
||||
self.parameter = parameter
|
||||
@@ -67,9 +69,10 @@ class Value(object):
|
||||
devspec = '%s:%s' % (devspec, self.parameter)
|
||||
elif self.command:
|
||||
devspec = '%s:%s()' % (devspec, self.command)
|
||||
return '%s:Value(%s)' % (devspec, ', '.join(
|
||||
[repr(self.value)] +
|
||||
['%s=%s' % (k, format_time(v) if k == "timestamp" else repr(v)) for k, v in self.qualifiers.items()]))
|
||||
return '%s:Value(%s)' % (devspec, ', '.join([repr(self.value)] + [
|
||||
'%s=%s' % (k, format_time(v) if k == "timestamp" else repr(v))
|
||||
for k, v in self.qualifiers.items()
|
||||
]))
|
||||
|
||||
|
||||
class Request(Message):
|
||||
@@ -95,8 +98,7 @@ class Request(Message):
|
||||
for k in self.ARGS:
|
||||
m.setvalue(k, self.__dict__[k])
|
||||
m.setvalue("errorclass", errorclass[:-5]
|
||||
if errorclass.endswith('rror')
|
||||
else errorclass)
|
||||
if errorclass.endswith('rror') else errorclass)
|
||||
m.setvalue("errorinfo", errorinfo)
|
||||
return m
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Define SECoP Messages"""
|
||||
|
||||
# Request Types
|
||||
@@ -79,8 +78,9 @@ class Message(object):
|
||||
r = 'Device' if self.devs != ['*'] else 'Devices'
|
||||
|
||||
t = ''
|
||||
if self.MSGTYPE in [LIST, READ, WRITE, COMMAND,
|
||||
POLL, SUBSCRIBE, UNSUBSCRIBE, HELP]:
|
||||
if self.MSGTYPE in [
|
||||
LIST, READ, WRITE, COMMAND, POLL, SUBSCRIBE, UNSUBSCRIBE, HELP
|
||||
]:
|
||||
t = 'Request' if not self.result else 'Reply'
|
||||
|
||||
if self.errortype is None:
|
||||
@@ -95,7 +95,6 @@ class Message(object):
|
||||
|
||||
|
||||
class Value(object):
|
||||
|
||||
def __init__(self, value=Ellipsis, qualifiers=None, **kwds):
|
||||
self.dev = ''
|
||||
self.param = ''
|
||||
@@ -111,9 +110,9 @@ class Value(object):
|
||||
if self.prop:
|
||||
devspec = '%s:%s' % (devspec, self.prop)
|
||||
return '%s:Value(%s)' % (
|
||||
devspec, ', '.join(
|
||||
[repr(self.value)] +
|
||||
['%s=%r' % (k, v) for k, v in self.qualifiers.items()]))
|
||||
devspec,
|
||||
', '.join([repr(self.value)] +
|
||||
['%s=%r' % (k, v) for k, v in self.qualifiers.items()]))
|
||||
|
||||
|
||||
class ListMessage(Message):
|
||||
@@ -167,28 +166,29 @@ class HelpMessage(Message):
|
||||
|
||||
|
||||
class NoSuchDeviceError(ErrorMessage):
|
||||
|
||||
def __init__(self, *devs):
|
||||
ErrorMessage.__init__(
|
||||
self, devs=devs, errorstring="Device %r does not exist" %
|
||||
devs[0], errortype='NoSuchDevice')
|
||||
self,
|
||||
devs=devs,
|
||||
errorstring="Device %r does not exist" % devs[0],
|
||||
errortype='NoSuchDevice')
|
||||
|
||||
|
||||
class NoSuchParamError(ErrorMessage):
|
||||
|
||||
def __init__(self, dev, *params):
|
||||
ErrorMessage.__init__(
|
||||
self, devs=(dev,),
|
||||
params=params, errorstring="Device %r has no parameter %r" %
|
||||
(dev, params[0]),
|
||||
self,
|
||||
devs=(dev, ),
|
||||
params=params,
|
||||
errorstring="Device %r has no parameter %r" % (dev, params[0]),
|
||||
errortype='NoSuchParam')
|
||||
|
||||
|
||||
class ParamReadonlyError(ErrorMessage):
|
||||
|
||||
def __init__(self, dev, *params):
|
||||
ErrorMessage.__init__(
|
||||
self, devs=(dev,),
|
||||
self,
|
||||
devs=(dev, ),
|
||||
params=params,
|
||||
errorstring="Device %r, parameter %r is not writeable!" %
|
||||
(dev, params[0]),
|
||||
@@ -196,30 +196,28 @@ class ParamReadonlyError(ErrorMessage):
|
||||
|
||||
|
||||
class InvalidParamValueError(ErrorMessage):
|
||||
|
||||
def __init__(self, dev, param, value, e):
|
||||
ErrorMessage.__init__(
|
||||
self, devs=(dev,),
|
||||
params=params, values=(value),
|
||||
self,
|
||||
devs=(dev, ),
|
||||
params=params,
|
||||
values=(value),
|
||||
errorstring=str(e),
|
||||
errortype='InvalidParamValueError')
|
||||
|
||||
|
||||
class InternalError(ErrorMessage):
|
||||
|
||||
def __init__(self, err, **kwds):
|
||||
ErrorMessage.__init__(
|
||||
self, errorstring=str(err),
|
||||
errortype='InternalError', **kwds)
|
||||
self, errorstring=str(err), errortype='InternalError', **kwds)
|
||||
|
||||
|
||||
MESSAGE = dict(
|
||||
(cls.MSGTYPE, cls)
|
||||
for cls
|
||||
in
|
||||
[HelpMessage, ErrorMessage, EventMessage, TriggerMessage,
|
||||
UnsubscribeMessage, SubscribeMessage, PollMessage, CommandMessage,
|
||||
WriteMessage, ReadMessage, ListMessage])
|
||||
MESSAGE = dict((cls.MSGTYPE, cls)
|
||||
for cls in [
|
||||
HelpMessage, ErrorMessage, EventMessage, TriggerMessage,
|
||||
UnsubscribeMessage, SubscribeMessage, PollMessage,
|
||||
CommandMessage, WriteMessage, ReadMessage, ListMessage
|
||||
])
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Minimal testing of messages....")
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
|
||||
# could also be some objects
|
||||
OK = 100
|
||||
BUSY = 200
|
||||
WARN = 300
|
||||
UNSTABLE = 350
|
||||
WARN = 200
|
||||
UNSTABLE = 250
|
||||
BUSY = 300
|
||||
ERROR = 400
|
||||
UNKNOWN = -1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user