reworking messages
1) start 'bin/secop-server test' 2) connect to localhost port 10767 3) enter help<enter> 4) enjoy Change-Id: I488d5f9cdca8c91c583691ab23f541a4a8759f4e
This commit is contained in:
59
secop/protocol/encoding/__init__.py
Normal file
59
secop/protocol/encoding/__init__.py
Normal file
@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Messages"""
|
||||
|
||||
# implement as class as they may need some internal 'state' later on
|
||||
# (think compressors)
|
||||
|
||||
# Base classes
|
||||
|
||||
|
||||
class MessageEncoder(object):
|
||||
"""en/decode a single Messageobject"""
|
||||
|
||||
def encode(self, messageobj):
|
||||
"""encodes the given message object into a frame"""
|
||||
raise NotImplemented
|
||||
|
||||
def decode(self, frame):
|
||||
"""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
|
||||
from text import TextEncoder
|
||||
from pickle import PickleEncoder
|
||||
from simplecomm import SCPEncoder
|
||||
|
||||
ENCODERS = {
|
||||
'pickle': PickleEncoder,
|
||||
'text': TextEncoder,
|
||||
'demo_v2': DemoEncoderV2,
|
||||
'demo_v3': DemoEncoderV3,
|
||||
'demo_v4': DemoEncoderV4,
|
||||
'demo': DemoEncoderV4,
|
||||
'scp': SCPEncoder,
|
||||
}
|
||||
|
||||
__ALL__ = ['ENCODERS']
|
102
secop/protocol/encoding/demo_v2.py
Normal file
102
secop/protocol/encoding/demo_v2.py
Normal file
@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Messages"""
|
||||
|
||||
# implement as class as they may need some internal 'state' later on
|
||||
# (think compressors)
|
||||
|
||||
from secop.protocol.encoding import MessageEncoder
|
||||
from secop.protocol import messages
|
||||
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]*))?(?:\=(.*))?')
|
||||
|
||||
|
||||
class DemoEncoder(MessageEncoder):
|
||||
|
||||
def decode(sef, encoded):
|
||||
# match [!][*|devicename][: *|paramname [: *|propname]] [=value]
|
||||
match = DEMO_RE.match(encoded)
|
||||
if match:
|
||||
novalue, devname, pname, propname, assign = match.groups()
|
||||
if assign:
|
||||
print "parsing", assign,
|
||||
assign = parse_args(assign)
|
||||
print "->", assign
|
||||
return messages.DemoRequest(
|
||||
novalue, devname, pname, propname, assign)
|
||||
return messages.HelpRequest()
|
||||
|
||||
def encode(self, msg):
|
||||
if isinstance(msg, messages.DemoReply):
|
||||
return msg.lines
|
||||
handler_name = '_encode_' + msg.__class__.__name__
|
||||
handler = getattr(self, handler_name, None)
|
||||
if handler is None:
|
||||
print "Handler %s not yet implemented!" % handler_name
|
||||
try:
|
||||
args = dict((k, msg.__dict__[k]) for k in msg.ARGS)
|
||||
result = handler(**args)
|
||||
except Exception as e:
|
||||
print "Error encoding %r with %r!" % (msg, handler)
|
||||
print e
|
||||
return '~InternalError~'
|
||||
return result
|
||||
|
||||
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):
|
||||
return '~Error~ %r' % error
|
||||
|
||||
def _encode_InternalError(self, error):
|
||||
return '~InternalError~ %r' % error
|
||||
|
||||
def _encode_ProtocollError(self, msgtype, msgname, msgargs):
|
||||
return '~ProtocolError~ %s.%s.%r' % (msgtype, msgname, msgargs)
|
||||
|
||||
def _encode_NoSuchDeviceError(self, device):
|
||||
return '~NoSuchDeviceError~ %s' % device
|
||||
|
||||
def _encode_NoSuchParamError(self, device, param):
|
||||
return '~NoSuchParameterError~ %s:%s' % (device, param)
|
||||
|
||||
def _encode_ParamReadonlyError(self, device, param):
|
||||
return '~ParamReadOnlyError~ %s:%s' % (device, param)
|
||||
|
||||
def _encode_NoSuchCommandError(self, device, command):
|
||||
return '~NoSuchCommandError~ %s.%s' % (device, command)
|
||||
|
||||
def _encode_CommandFailedError(self, device, command):
|
||||
return '~CommandFailedError~ %s.%s' % (device, command)
|
||||
|
||||
def _encode_InvalidParamValueError(self, device, param, value):
|
||||
return '~InvalidValueForParamError~ %s:%s=%r' % (device, param, value)
|
||||
|
||||
def _encode_HelpReply(self):
|
||||
return ['Help not yet implemented!',
|
||||
'ask Markus Zolliker about the protocol']
|
391
secop/protocol/encoding/demo_v3.py
Normal file
391
secop/protocol/encoding/demo_v3.py
Normal file
@ -0,0 +1,391 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Messages"""
|
||||
|
||||
# implement as class as they may need some internal 'state' later on
|
||||
# (think compressors)
|
||||
|
||||
from secop.protocol.encoding import MessageEncoder
|
||||
from secop.protocol.messages import *
|
||||
from secop.protocol.errors import ProtocollError
|
||||
|
||||
import ast
|
||||
import re
|
||||
|
||||
|
||||
def floatify(s):
|
||||
try:
|
||||
return int(s)
|
||||
except (ValueError, TypeError):
|
||||
try:
|
||||
return float(s)
|
||||
except (ValueError, TypeError):
|
||||
return s
|
||||
|
||||
|
||||
def devspec(msg, result=''):
|
||||
if isinstance(msg, Message):
|
||||
devs = ','.join(msg.devs)
|
||||
pars = ','.join(msg.pars)
|
||||
props = ','.join(msg.props)
|
||||
else:
|
||||
devs = msg.dev
|
||||
pars = msg.par
|
||||
props = msg.prop
|
||||
if devs:
|
||||
result = '%s %s' % (result, devs)
|
||||
if pars:
|
||||
result = '%s:%s' % (result, pars)
|
||||
if props:
|
||||
result = '%s:%s' % (result, props)
|
||||
return result.strip()
|
||||
|
||||
|
||||
def encode_value(value, prefix='', targetvalue='', cmd=''):
|
||||
result = [prefix]
|
||||
if value.dev:
|
||||
result.append(' ')
|
||||
result.append(value.dev)
|
||||
if value.param:
|
||||
result.append(':%s' % value.param)
|
||||
if value.prop:
|
||||
result.append(':%s' % value.prop)
|
||||
# only needed for WriteMessages
|
||||
if targetvalue:
|
||||
result.append('=%s' % repr(targetvalue))
|
||||
# only needed for CommandMessages
|
||||
if cmd:
|
||||
result.append(':%s' % cmd)
|
||||
if value.value != Ellipsis:
|
||||
# results always have a ';'
|
||||
result.append('=%s;' % repr(value.value))
|
||||
result.append(';'.join('%s=%s' % (qn, repr(qv))
|
||||
for qn, qv in value.qualifiers.items()))
|
||||
return ''.join(result).strip()
|
||||
|
||||
|
||||
DEMO_RE_ERROR = re.compile(
|
||||
r"""^error\s(?P<errortype>\w+)\s(?P<msgtype>\w+)?(?:\s(?P<devs>\*|[\w,]+)(?:\:(?P<pars>\*|[\w,]+)(?:\:(?P<props>\*|[\w,]+))?)?)?(?:(?:\=(?P<target>[^=;\s"]*))|(?:\((?P<cmdargs>[^\)]*)\)))?(?:\s"(?P<errorstring>[^"]*)")$""",
|
||||
re.X)
|
||||
DEMO_RE_OTHER = re.compile(
|
||||
r"""^(?P<msgtype>\w+)(?:\s(?P<devs>\*|[\w,]+)(?:\:(?P<pars>\*|[\w,]+)(?:\:(?P<props>\*|[\w,]+))?)?)?(?:(?:\=(?P<target>[^=;]*))|(?::(?P<cmd>\w+)\((?P<args>[^\)]*)\)))?(?:=(?P<readback>[^;]+);(?P<qualifiers>.*))?$""",
|
||||
re.X)
|
||||
|
||||
|
||||
class DemoEncoder(MessageEncoder):
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
MessageEncoder.__init__(self, *args, **kwds)
|
||||
self.result = [] # for decoding
|
||||
self.expect_lines = 1
|
||||
#self.tests()
|
||||
|
||||
def encode(self, msg):
|
||||
"""msg object -> transport layer message"""
|
||||
# fun for Humans
|
||||
if isinstance(msg, HelpMessage):
|
||||
r = ['#5']
|
||||
r.append("help Try one of the following:")
|
||||
r.append("help 'list' to query a list of modules")
|
||||
r.append("help 'read <module>' to read a module")
|
||||
r.append("help 'list <module>' to query a list of parameters")
|
||||
r.append("help ... more to come")
|
||||
return '\n'.join(r)
|
||||
|
||||
if isinstance(msg, (ListMessage, SubscribeMessage,
|
||||
UnsubscribeMessage, TriggerMessage)):
|
||||
msgtype = msg.MSGTYPE
|
||||
if msg.result:
|
||||
if msg.devs:
|
||||
# msg.result is always a list!
|
||||
return "%s=%s" % (devspec(msg, msgtype),
|
||||
','.join(map(str, msg.result)))
|
||||
return "%s=%s" % (msgtype, ','.join(map(str, msg.result)))
|
||||
return devspec(msg, msgtype).strip()
|
||||
|
||||
if isinstance(msg, (ReadMessage, PollMessage, EventMessage)):
|
||||
msgtype = msg.MSGTYPE
|
||||
result = []
|
||||
if len(msg.result or []) > 1:
|
||||
result.append("#%d" % len(msg.result))
|
||||
for val in msg.result or []:
|
||||
# encode 1..N replies
|
||||
result.append(encode_value(val, msgtype))
|
||||
if not msg.result:
|
||||
# encode a request (no results -> reply, else an error would
|
||||
# have been sent)
|
||||
result.append(devspec(msg, msgtype))
|
||||
return '\n'.join(result)
|
||||
|
||||
if isinstance(msg, WriteMessage):
|
||||
result = []
|
||||
if len(msg.result or []) > 1:
|
||||
result.append("#%d" % len(msg.result))
|
||||
for val in msg.result or []:
|
||||
# encode 1..N replies
|
||||
result.append(
|
||||
encode_value(
|
||||
val,
|
||||
'write',
|
||||
targetvalue=msg.target))
|
||||
if not msg.result:
|
||||
# encode a request (no results -> reply, else an error would
|
||||
# have been sent)
|
||||
result.append('%s=%r' % (devspec(msg, 'write'), msg.target))
|
||||
return '\n'.join(result)
|
||||
|
||||
if isinstance(msg, CommandMessage):
|
||||
result = []
|
||||
if len(msg.result or []) > 1:
|
||||
result.append("#%d" % len(msg.result))
|
||||
for val in msg.result or []:
|
||||
# encode 1..N replies
|
||||
result.append(
|
||||
encode_value(
|
||||
val,
|
||||
'command',
|
||||
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)))
|
||||
return '\n'.join(result)
|
||||
|
||||
if isinstance(msg, ErrorMessage):
|
||||
return ('%s %s' % (devspec(msg, 'error %s' %
|
||||
msg.errortype), msg.errorstring)).strip()
|
||||
|
||||
return 'Can not handle object %r!' % msg
|
||||
|
||||
def decode(self, encoded):
|
||||
if encoded.startswith('#'):
|
||||
# XXX: check if last message was complete
|
||||
self.expect_lines = int(encoded[1:])
|
||||
if self.result:
|
||||
# XXX: also flag an error?
|
||||
self.result = []
|
||||
return None
|
||||
|
||||
if encoded == '':
|
||||
return HelpMessage()
|
||||
# now decode the message and append to self.result
|
||||
msg = self.decode_single_message(encoded)
|
||||
if msg:
|
||||
# XXX: check if messagetype is the same as the already existing,
|
||||
# else error
|
||||
self.result.append(msg)
|
||||
else:
|
||||
# XXX: flag an error?
|
||||
return HelpMessage()
|
||||
|
||||
self.expect_lines -= 1
|
||||
if self.expect_lines <= 0:
|
||||
# reconstruct a multi-reply-message from the entries
|
||||
# return the first message, but extend the result list first
|
||||
# if there is only 1 message, just return this
|
||||
res = self.result.pop(0)
|
||||
while self.result:
|
||||
m = self.result.pop(0)
|
||||
res.result.append(m.result[0])
|
||||
self.expect_lines = 1
|
||||
return res
|
||||
|
||||
# no complete message yet
|
||||
return None
|
||||
|
||||
def decode_single_message(self, encoded):
|
||||
# just decode a single message line
|
||||
|
||||
# 1) check for error msgs (more specific first)
|
||||
m = DEMO_RE_ERROR.match(encoded)
|
||||
if m:
|
||||
return ErrorMessage(**m.groupdict())
|
||||
|
||||
# 2) check for 'normal' message
|
||||
m = DEMO_RE_OTHER.match(encoded)
|
||||
if m:
|
||||
mgroups = m.groupdict()
|
||||
msgtype = mgroups.pop('msgtype')
|
||||
|
||||
# reformat devspec
|
||||
def helper(stuff, sep=','):
|
||||
if not stuff:
|
||||
return []
|
||||
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'))
|
||||
|
||||
# sugar for listing stuff:
|
||||
# map list -> list *
|
||||
# map list x -> list x:*
|
||||
# map list x:y -> list x:y:*
|
||||
if msgtype == LIST:
|
||||
if not devs:
|
||||
devs = ['*']
|
||||
elif devs[0] != '*':
|
||||
if not pars:
|
||||
pars = ['*']
|
||||
elif pars[0] != '*':
|
||||
if not props:
|
||||
props = ['*']
|
||||
|
||||
# reformat cmdargs
|
||||
args = ast.literal_eval(mgroups.pop('args') or '()')
|
||||
if msgtype == COMMAND:
|
||||
mgroups['args'] = args
|
||||
|
||||
# reformat qualifiers
|
||||
print mgroups
|
||||
quals = dict(
|
||||
qual.split(
|
||||
'=',
|
||||
1) for qual in helper(
|
||||
mgroups.pop(
|
||||
'qualifiers',
|
||||
';')))
|
||||
|
||||
# reformat value
|
||||
result = []
|
||||
readback = mgroups.pop('readback')
|
||||
if readback or quals:
|
||||
valargs = dict()
|
||||
if devs:
|
||||
valargs['dev'] = devs[0]
|
||||
if pars:
|
||||
valargs['par'] = pars[0]
|
||||
if props:
|
||||
valargs['prop'] = props[0]
|
||||
result = [Value(floatify(readback), quals, **valargs)]
|
||||
if msgtype == LIST and result:
|
||||
result = [n.strip() for n in readback.split(',')]
|
||||
|
||||
# construct messageobj
|
||||
if msgtype in MESSAGE:
|
||||
return MESSAGE[msgtype](
|
||||
devs=devs, pars=pars, props=props, result=result, **mgroups)
|
||||
|
||||
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',
|
||||
]
|
||||
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/...)
|
||||
\ ? # 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)
|
||||
|
||||
|
||||
class DemoEncoder_MZ(MessageEncoder):
|
||||
|
||||
def decode(sef, encoded):
|
||||
m = DEMO_RE_MZ.match(encoded)
|
||||
if m:
|
||||
print "implement me !"
|
||||
return HelpRequest()
|
||||
|
||||
def encode(self, msg):
|
||||
"""msg object -> transport layer message"""
|
||||
# fun for Humans
|
||||
if isinstance(msg, HelpReply):
|
||||
r = []
|
||||
r.append("Try one of the following:")
|
||||
r.append("'list' to query a list of modules")
|
||||
r.append("'read <module>' to read a module")
|
||||
r.append("'list <module>' to query a list of parameters")
|
||||
r.append("... more to come")
|
||||
return '\n'.join(r)
|
||||
|
||||
return {
|
||||
ListDevicesRequest: lambda msg: "list",
|
||||
ListDevicesReply: lambda msg: "list=%s" % ','.join(sorted(msg.list_of_devices)),
|
||||
GetVersionRequest: lambda msg: "version",
|
||||
GetVersionReply: lambda msg: "version=%r" % msg.version,
|
||||
ListDeviceParamsRequest: lambda msg: "list %s" % msg.device,
|
||||
# do not include a '.' as param name!
|
||||
ListDeviceParamsReply: lambda msg: "list %s=%s" % (msg.device, ','.join(sorted(msg.params.keys()))),
|
||||
ReadValueRequest: lambda msg: "read %s" % msg.device,
|
||||
ReadValueReply: lambda msg: "read %s=%r" % (msg.device, msg.value),
|
||||
WriteValueRequest: lambda msg: "write %s=%r" % (msg.device, msg.value),
|
||||
WriteValueReply: lambda msg: "write %s=%r" % (msg.device, msg.readback_value),
|
||||
ReadParamRequest: lambda msg: "read %s:%s" % (msg.device, msg.param),
|
||||
ReadParamReply: lambda msg: "read %s:%s=%r" % (msg.device, msg.param, msg.value),
|
||||
WriteParamRequest: lambda msg: "write %s:%s=%r" % (msg.device, msg.param, msg.value),
|
||||
WriteParamReply: lambda msg: "write %s:%s=%r" % (msg.device, msg.param, msg.readback_value),
|
||||
# extensions
|
||||
ReadAllDevicesRequest: lambda msg: "",
|
||||
ReadAllDevicesReply: lambda msg: "",
|
||||
ListParamPropsRequest: lambda msg: "readprop %s:%s" % (msg.device, msg.param),
|
||||
ListParamPropsReply: lambda msg: ["readprop %s:%s" % (msg.device, msg.param)] + ["%s:%s:%s=%s" % (msg.device, msg.param, k, v) for k, v in sorted(msg.props.items())],
|
||||
ReadPropertyRequest: lambda msg: "readprop %s:%s:%s" % (msg.device, msg.param, msg.prop),
|
||||
ReadPropertyReply: lambda msg: "readprop %s:%s:%s=%s" % (msg.device, msg.param, msg.prop, msg.value),
|
||||
AsyncDataUnit: lambda msg: "",
|
||||
SubscribeRequest: lambda msg: "subscribe %s:%s" % (msg.device, msg.param) if msg.param else ("subscribe %s" % msg.device),
|
||||
SubscribeReply: lambda msg: "subscribe %s:%s" % (msg.device, msg.param) if msg.param else ("subscribe %s" % msg.device),
|
||||
UnSubscribeRequest: lambda msg: "",
|
||||
UnSubscribeReply: lambda msg: "",
|
||||
CommandRequest: lambda msg: "command %s:%s" % (msg.device, msg.command),
|
||||
CommandReply: lambda msg: "command %s:%s" % (msg.device, msg.command),
|
||||
# errors
|
||||
ErrorReply: lambda msg: "",
|
||||
InternalError: lambda msg: "",
|
||||
ProtocollError: lambda msg: "",
|
||||
CommandFailedError: lambda msg: "error CommandError %s:%s %s" % (msg.device, msg.param, msg.error),
|
||||
NoSuchCommandError: lambda msg: "error NoSuchCommand %s:%s" % (msg.device, msg.param, msg.error),
|
||||
NoSuchDeviceError: lambda msg: "error NoSuchModule %s" % msg.device,
|
||||
NoSuchParamError: lambda msg: "error NoSuchParameter %s:%s" % (msg.device, msg.param),
|
||||
ParamReadonlyError: lambda msg: "",
|
||||
UnsupportedFeatureError: lambda msg: "",
|
||||
InvalidParamValueError: lambda msg: "",
|
||||
}[msg.__class__](msg)
|
209
secop/protocol/encoding/demo_v4.py
Normal file
209
secop/protocol/encoding/demo_v4.py
Normal file
@ -0,0 +1,209 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Messages"""
|
||||
|
||||
# implement as class as they may need some internal 'state' later on
|
||||
# (think compressors)
|
||||
|
||||
from secop.protocol.encoding import MessageEncoder
|
||||
from secop.protocol.messages import *
|
||||
from secop.protocol.errors import ProtocollError
|
||||
|
||||
import ast
|
||||
import re
|
||||
import json
|
||||
|
||||
# each message is like <messagetype> [ \space <messageargs> [ \space <json> ]] \lf
|
||||
|
||||
# note: the regex allow <> for spec for testing only!
|
||||
DEMO_RE = re.compile(
|
||||
r"""^(?P<msgtype>[\*\?\w]+)(?:\s(?P<spec>[\w:<>]+)(?:\s(?P<json>.*))?)?$""", re.X)
|
||||
|
||||
#"""
|
||||
# messagetypes:
|
||||
IDENTREQUEST = '*IDN?' # literal
|
||||
IDENTREPLY = 'Sine2020WP7.1&ISSE, SECoP, V2016-11-30, rc1' # literal
|
||||
DESCRIPTIONSREQUEST = 'describe' # literal
|
||||
DESCRIPTIONREPLY = 'describing' # +<id> +json
|
||||
ENABLEEVENTSREQUEST = 'activate' # literal
|
||||
ENABLEEVENTSREPLY = 'active' # literal, is end-of-initial-data-transfer
|
||||
DISABLEEVENTSREQUEST = 'deactivate' # literal
|
||||
DISABLEEVENTSREPLY = 'inactive' # literal
|
||||
COMMANDREQUEST = 'do' # +module:command +json args (if needed)
|
||||
COMMANDREPLY = 'doing' # +module:command +json args (if needed)
|
||||
WRITEREQUEST = 'change' # +module[:parameter] +json_value -> NO direct reply, calls TRIGGER internally!
|
||||
WRITEREPLY = 'changing' # +module[:parameter] +json_value -> NO direct reply, calls TRIGGER internally!
|
||||
TRIGGERREQUEST = 'read' # +module[:parameter] -> NO direct reply, calls TRIGGER internally!
|
||||
HEARTBEATREQUEST = 'ping' # +nonce_without_space
|
||||
HEARTBEATREPLY = 'pong' # +nonce_without_space
|
||||
EVENTTRIGGERREPLY = 'update' # +module[:parameter] +json_result_value_with_qualifiers NO REQUEST (use WRITE/TRIGGER)
|
||||
EVENTCOMMANDREPLY = 'done' # +module:command +json result (if needed)
|
||||
#EVENTWRITEREPLY = 'changed' # +module[:parameter] +json_result_value_with_qualifiers NO REQUEST (use WRITE/TRIGGER)
|
||||
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', 'SyntaxError', 'InternalError',
|
||||
'CommandRunning', 'Disabled',]
|
||||
# note: above strings need to be unique in the sense, that none is/or starts with another
|
||||
|
||||
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), 'arguments',),
|
||||
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, ),
|
||||
# EventMessage : (EVENTREPLY, lambda msg: "%s:%s" % (msg.module, msg.parameter or (msg.command+'()'))
|
||||
# if msg.parameter or msg.command else msg.module, 'value',),
|
||||
ErrorMessage : (ERRORREPLY, 'errorclass', 'errorinfo',),
|
||||
Value: (EVENTTRIGGERREPLY, lambda msg: "%s:%s" % (msg.module, msg.parameter or (msg.command+'()'))
|
||||
if msg.parameter or msg.command else msg.module,
|
||||
lambda msg: [msg.value, msg.qualifiers] if msg.qualifiers else [msg.value]),
|
||||
}
|
||||
DECODEMAP = {
|
||||
IDENTREQUEST : lambda spec, data: IdentifyRequest(),
|
||||
IDENTREPLY : lambda spec, data: IdentifyReply(encoded), # handled specially, listed here for completeness
|
||||
DESCRIPTIONSREQUEST : lambda spec, data: DescribeRequest(),
|
||||
DESCRIPTIONREPLY : lambda spec, data: DescribeReply(equipment_id=spec[0], description=data),
|
||||
ENABLEEVENTSREQUEST : lambda spec, data: ActivateRequest(),
|
||||
ENABLEEVENTSREPLY: lambda spec, data:ActivateReply(),
|
||||
DISABLEEVENTSREQUEST: lambda spec, data:DeactivateRequest(),
|
||||
DISABLEEVENTSREPLY: lambda spec, data:DeactivateReply(),
|
||||
COMMANDREQUEST: lambda spec, data:CommandRequest(module=spec[0], command=spec[1], arguments=data),
|
||||
COMMANDREPLY: lambda spec, data: CommandReply(module=spec[0], command=spec[1], arguments=data),
|
||||
WRITEREQUEST: lambda spec, data: WriteRequest(module=spec[0], parameter=spec[1], value=data),
|
||||
WRITEREPLY:lambda spec, data:WriteReply(module=spec[0], parameter=spec[1], value=data),
|
||||
TRIGGERREQUEST:lambda spec, data:PollRequest(module=spec[0], parameter=spec[1]),
|
||||
HEARTBEATREQUEST:lambda spec, data:HeartbeatRequest(nonce=spec[0]),
|
||||
HEARTBEATREPLY:lambda spec, data:HeartbeatReply(nonce=spec[0]),
|
||||
HELPREQUEST: lambda spec, data:HelpMessage(),
|
||||
# HELPREPLY: lambda spec, data:None, # ignore this
|
||||
ERRORREPLY:lambda spec, data:ErrorMessage(errorclass=spec[0], errorinfo=data),
|
||||
EVENTTRIGGERREPLY:lambda spec, data:Value(module=spec[0], parameter=spec[1], value=data[0], qualifiers=data[1] if len(data)>1 else {}),
|
||||
EVENTCOMMANDREPLY: lambda spec, data:None, # ignore this
|
||||
# EVENTWRITEREPLY:lambda spec, data:Value(module=spec[0], parameter=spec[1], value=data[0], qualifiers=data[1] if len(data)>1 else {}),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
MessageEncoder.__init__(self, *args, **kwds)
|
||||
self.tests()
|
||||
|
||||
def encode(self, msg):
|
||||
"""msg object -> transport layer message"""
|
||||
# fun for Humans
|
||||
if isinstance(msg, HelpMessage):
|
||||
text = """Try one of the following:
|
||||
'%s' to query protocol version
|
||||
'%s' to read the description
|
||||
'%s <module>[:<parameter>]' to request reading a value
|
||||
'%s <module>[:<parameter>] value' to request changing a value
|
||||
'%s <module>[:<command>()]' to execute a command
|
||||
'%s <nonce>' to request a heartbeat response
|
||||
'%s' to activate async updates
|
||||
'%s' to deactivate updates
|
||||
""" %(IDENTREQUEST, DESCRIPTIONSREQUEST, TRIGGERREQUEST,
|
||||
WRITEREQUEST, COMMANDREQUEST, HEARTBEATREQUEST,
|
||||
ENABLEEVENTSREQUEST, DISABLEEVENTSREQUEST)
|
||||
return '\n'.join('%s %d %s' %(HELPREPLY, i+1, l.strip()) for i,l in enumerate(text.split('\n')[:-1]))
|
||||
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:]]
|
||||
if len(parts) > 1:
|
||||
parts[1] = str(parts[1])
|
||||
if len(parts) == 3:
|
||||
parts[2] = json.dumps(parts[2])
|
||||
return ' '.join(parts)
|
||||
|
||||
|
||||
def decode(self, encoded):
|
||||
# first check beginning
|
||||
match = DEMO_RE.match(encoded)
|
||||
if not match:
|
||||
print repr(encoded), repr(IDENTREPLY)
|
||||
if encoded == IDENTREPLY: # XXX:better just check the first 2 parts...
|
||||
return IdentifyReply(version_string=encoded)
|
||||
|
||||
return HelpMessage()
|
||||
return ErrorMessage(errorclass='SyntaxError',
|
||||
errorinfo='Regex did not match!',
|
||||
is_request=True)
|
||||
msgtype, msgspec, data = match.groups()
|
||||
if msgspec is None and data:
|
||||
return ErrorMessage(errorclass='InternalError',
|
||||
errorinfo='Regex matched json, but not spec!',
|
||||
is_request=True)
|
||||
|
||||
if msgtype in self.DECODEMAP:
|
||||
if msgspec and ':' in msgspec:
|
||||
msgspec = msgspec.split(':', 1)
|
||||
else:
|
||||
msgspec = (msgspec, None)
|
||||
if data:
|
||||
try:
|
||||
data = json.loads(data)
|
||||
except ValueError as err:
|
||||
return ErrorMessage(errorclass='BadValue',
|
||||
errorinfo=[repr(err), str(encoded)])
|
||||
return self.DECODEMAP[msgtype](msgspec, data)
|
||||
return ErrorMessage(errorclass='SyntaxError',
|
||||
errorinfo='%r: No Such Messagetype defined!' % encoded,
|
||||
is_request=True)
|
||||
|
||||
|
||||
def tests(self):
|
||||
print "---- Testing encoding -----"
|
||||
for msgclass, parts in sorted(self.ENCODEMAP.items()):
|
||||
print msgclass
|
||||
e=self.encode(msgclass(module='<module>',parameter='<paramname>',value=2.718,equipment_id='<id>',description='descriptive data',command='<cmd>',arguments='<arguments>',nonce='<nonce>',errorclass='InternalError',errorinfo='nix'))
|
||||
print e
|
||||
print self.decode(e)
|
||||
print
|
||||
print "---- Testing decoding -----"
|
||||
for msgtype, _ in sorted(self.DECODEMAP.items()):
|
||||
msg = '%s a:b 3' % msgtype
|
||||
if msgtype in [EVENTTRIGGERREPLY]:#, EVENTWRITEREPLY]:
|
||||
msg = '%s a:b [3,{"t":193868}]' % msgtype
|
||||
print msg
|
||||
d=self.decode(msg)
|
||||
print d
|
||||
print self.encode(d)
|
||||
print
|
||||
print "---- Testing done -----"
|
||||
|
||||
|
||||
|
47
secop/protocol/encoding/pickle.py
Normal file
47
secop/protocol/encoding/pickle.py
Normal file
@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Messages"""
|
||||
|
||||
# implement as class as they may need some internal 'state' later on
|
||||
# (think compressors)
|
||||
|
||||
from secop.protocol.encoding import MessageEncoder
|
||||
from secop.protocol import messages
|
||||
from secop.lib.parsing import *
|
||||
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
|
||||
|
||||
|
||||
class PickleEncoder(MessageEncoder):
|
||||
|
||||
def encode(self, messageobj):
|
||||
"""msg object -> transport layer message"""
|
||||
return pickle.dumps(messageobj)
|
||||
|
||||
def decode(self, encoded):
|
||||
"""transport layer message -> msg object"""
|
||||
return pickle.loads(encoded)
|
209
secop/protocol/encoding/simplecomm.py
Normal file
209
secop/protocol/encoding/simplecomm.py
Normal file
@ -0,0 +1,209 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Messages"""
|
||||
|
||||
# implement as class as they may need some internal 'state' later on
|
||||
# (think compressors)
|
||||
|
||||
from secop.protocol.encoding import MessageEncoder
|
||||
from secop.protocol.messages import *
|
||||
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>.*)')
|
||||
|
||||
|
||||
class SCPEncoder(MessageEncoder):
|
||||
|
||||
def encode(self, msg):
|
||||
"""msg object -> transport layer message"""
|
||||
# fun for Humans
|
||||
if isinstance(msg, HelpReply):
|
||||
r = []
|
||||
r.append("Try one of the following:")
|
||||
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")
|
||||
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")
|
||||
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@')")
|
||||
return '\n'.join(r)
|
||||
|
||||
return {
|
||||
ListDevicesRequest: lambda msg: "devices?",
|
||||
ListDevicesReply: lambda msg: "0 devices=" + repr(list(msg.list_of_devices)),
|
||||
GetVersionRequest: lambda msg: "version?",
|
||||
GetVersionReply: lambda msg: "0 version=%r" % msg.version,
|
||||
ListDeviceParamsRequest: lambda msg: "%s/parameters?" % msg.device,
|
||||
ListDeviceParamsReply: lambda msg: "0 %s/parameters=%r" % (msg.device, list(msg.params)),
|
||||
ReadValueRequest: lambda msg: "%s/value?" % msg.device,
|
||||
ReadValueReply: lambda msg: "0 %s/value?%r" % (msg.device, msg.value),
|
||||
WriteValueRequest: lambda msg: "%s/value=%r" % (msg.device, msg.value),
|
||||
WriteValueReply: lambda msg: "0 %s/value=%r" % (msg.device, msg.value),
|
||||
ReadParamRequest: lambda msg: "%s/%s?" % (msg.device, msg.param),
|
||||
ReadParamReply: lambda msg: "0 %s/%s?%r" % (msg.device, msg.param, msg.value),
|
||||
WriteParamRequest: lambda msg: "%s/%s=%r" % (msg.device, msg.param, msg.value),
|
||||
WriteParamReply: lambda msg: "0 %s/%s=%r" % (msg.device, msg.param, msg.readback_value),
|
||||
# extensions
|
||||
ReadAllDevicesRequest: lambda msg: "*/value?",
|
||||
ReadAllDevicesReply: lambda msg: ["0 %s/value=%s" % (m.device, m.value) for m in msg.readValueReplies],
|
||||
ListParamPropsRequest: lambda msg: "%s/%s/?" % (msg.device, msg.param),
|
||||
ListParamPropsReply: lambda msg: ["0 %s/%s/%s" % (msg.device, msg.param, p) for p in msg.props],
|
||||
AsyncDataUnit: lambda msg: "@ %s/%s=%r" % (msg.devname, msg.pname, msg.value),
|
||||
SubscribeRequest: lambda msg: "%s/%s+" % (msg.devname, msg.pname),
|
||||
# violates spec ! we would need the original request here....
|
||||
SubscribeReply: lambda msg: "0 / %r" % [repr(s) for s in msg.subscriptions],
|
||||
UnSubscribeRequest: lambda msg: "%s/%s+" % (msg.devname, msg.pname),
|
||||
# violates spec ! we would need the original request here....
|
||||
UnSubscribeReply: lambda msg: "0 / %r" % [repr(s) for s in msg.subscriptions],
|
||||
# errors
|
||||
# violates spec ! we would need the original request here....
|
||||
ErrorReply: lambda msg: "1 /%r" % msg.error,
|
||||
# violates spec ! we would need the original request here....
|
||||
InternalError: lambda msg: "1 /%r" % msg.error,
|
||||
# violates spec ! we would need the original request here....
|
||||
ProtocollError: lambda msg: "6 /%r" % msg.error,
|
||||
# violates spec ! we would need the original request here....
|
||||
CommandFailedError: lambda msg: "1 %s/%s" % (msg.device, msg.command),
|
||||
# violates spec ! we would need the original request here....
|
||||
NoSuchCommandError: lambda msg: "3 %s/%s" % (msg.device, msg.command),
|
||||
# violates spec ! we would need the original request here....
|
||||
NoSuchDeviceError: lambda msg: "4 %s/ %r" % (msg.device, msg.error),
|
||||
# violates spec ! we would need the original request here....
|
||||
NoSuchParamError: lambda msg: "5 %s/%s %r" % (msg.device, msg.param, msg.error),
|
||||
# violates spec ! we would need the original request here....
|
||||
ParamReadonlyError: lambda msg: "8 %s/%s %r" % (msg.device, msg.param, msg.error),
|
||||
# violates spec ! we would need the original request here....
|
||||
UnsupportedFeatureError: lambda msg: "3 / %r" % msg.feature,
|
||||
# violates spec ! we would need the original request here....
|
||||
InvalidParamValueError: lambda msg: "7 %s/%s=%r %r" % (msg.device, msg.param, msg.value, msg.error),
|
||||
}[msg.__class__](msg)
|
||||
|
||||
def decode(self, encoded):
|
||||
"""transport layer message -> msg object"""
|
||||
match = SCPMESSAGE.match(encoded)
|
||||
if not(match):
|
||||
return HelpRequest()
|
||||
err, dev, par, op, val = match.groups()
|
||||
if val is not None:
|
||||
try:
|
||||
val = ast.literal_eval(val)
|
||||
except Exception as e:
|
||||
return SyntaxError('while decoding %r: %s' % (encoded, e))
|
||||
if err == '@':
|
||||
# async
|
||||
if op == '=':
|
||||
return AsyncDataUnit(dev, par, val)
|
||||
return ProtocolError("Asyncupdates must have op = '='!")
|
||||
elif err is None:
|
||||
# request
|
||||
if op == '+':
|
||||
# subscribe
|
||||
if dev:
|
||||
if par:
|
||||
return SubscribeRequest(dev, par)
|
||||
return SubscribeRequest(dev, '*')
|
||||
if op == '-':
|
||||
# unsubscribe
|
||||
if dev:
|
||||
if par:
|
||||
return UnsubscribeRequest(dev, par)
|
||||
return UnsubscribeRequest(dev, '*')
|
||||
if op == '?':
|
||||
if dev is None:
|
||||
# 'server' commands
|
||||
if par == 'devices':
|
||||
return ListDevicesRequest()
|
||||
elif par == 'version':
|
||||
return GetVersionRequest()
|
||||
return ProtocolError()
|
||||
if par == 'parameters':
|
||||
return ListDeviceParamsRequest(dev)
|
||||
elif par == 'value':
|
||||
return ReadValueRequest(dev)
|
||||
elif dev == '*' and par == 'value':
|
||||
return ReadAllDevicesRequest()
|
||||
else:
|
||||
return ReadParamRequest(dev, par)
|
||||
elif op == '=':
|
||||
if dev and (par == 'value'):
|
||||
return WriteValueRequest(dev, val)
|
||||
if par.endswith('/') and op == '?':
|
||||
return ListParamPropsRequest(dev, par)
|
||||
return WriteParamRequest(dev, par, val)
|
||||
elif err == '0':
|
||||
# reply
|
||||
if dev == '':
|
||||
if par == 'devices':
|
||||
return ListDevicesReply(val)
|
||||
elif par == 'version':
|
||||
return GetVersionReply(val)
|
||||
return ProtocolError(encoded)
|
||||
if par == 'parameters':
|
||||
return ListDeviceParamsReply(dev, val)
|
||||
if par == 'value':
|
||||
if op == '?':
|
||||
return ReadValueReply(dev, val)
|
||||
elif op == '=':
|
||||
return WriteValueReply(dev, val)
|
||||
return ProtocolError(encoded)
|
||||
if op == '+':
|
||||
return SubscribeReply(ast.literal_eval(dev))
|
||||
if op == '-':
|
||||
return UnSubscribeReply(ast.literal_eval(dev))
|
||||
if op == '?':
|
||||
return ReadParamReply(dev, par, val)
|
||||
if op == '=':
|
||||
return WriteParamReply(dev, par, val)
|
||||
return ProtocolError(encoded)
|
||||
else:
|
||||
# error
|
||||
if err in ('1', '2'):
|
||||
return InternalError(encoded)
|
||||
elif err == '3':
|
||||
return NoSuchCommandError(dev, par)
|
||||
elif err == '4':
|
||||
return NoSuchDeviceError(dev, encoded)
|
||||
elif err == '5':
|
||||
return NoSuchParamError(dev, par, val)
|
||||
elif err == '7':
|
||||
return InvalidParamValueError(dev, par, val, encoded)
|
||||
elif err == '8':
|
||||
return ParamReadonlyError(dev, par, encoded)
|
||||
else: # err == 6 or other stuff
|
||||
return ProtocollError(encoded)
|
65
secop/protocol/encoding/text.py
Normal file
65
secop/protocol/encoding/text.py
Normal file
@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# *****************************************************************************
|
||||
# This program is free software; you can redistribute it and/or modify it under
|
||||
# the terms of the GNU General Public License as published by the Free Software
|
||||
# Foundation; either version 2 of the License, or (at your option) any later
|
||||
# version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along with
|
||||
# this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Module authors:
|
||||
# Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
|
||||
#
|
||||
# *****************************************************************************
|
||||
|
||||
"""Encoding/decoding Messages"""
|
||||
|
||||
# implement as class as they may need some internal 'state' later on
|
||||
# (think compressors)
|
||||
|
||||
from secop.protocol.encoding import MessageEncoder
|
||||
from secop.protocol import messages
|
||||
from secop.lib.parsing import *
|
||||
|
||||
|
||||
class TextEncoder(MessageEncoder):
|
||||
|
||||
def __init__(self):
|
||||
# build safe namespace
|
||||
ns = dict()
|
||||
for n in dir(messages):
|
||||
if n.endswith(('Request', 'Reply')):
|
||||
ns[n] = getattr(messages, n)
|
||||
self.namespace = ns
|
||||
|
||||
def encode(self, messageobj):
|
||||
"""msg object -> transport layer message"""
|
||||
# fun for Humans
|
||||
if isinstance(messageobj, messages.HelpMessage):
|
||||
return "Error: try one of the following requests:\n" + \
|
||||
'\n'.join(['%s(%s)' % (getattr(messages, m).__name__,
|
||||
', '.join(getattr(messages, m).ARGS))
|
||||
for m in dir(messages)
|
||||
if m.endswith('Request') and len(m) > len("Request")])
|
||||
res = []
|
||||
for k in messageobj.ARGS:
|
||||
res.append('%s=%r' % (k, getattr(messageobj, k, None)))
|
||||
result = '%s(%s)' % (messageobj.__class__.__name__, ', '.join(res))
|
||||
return result
|
||||
|
||||
def decode(self, encoded):
|
||||
"""transport layer message -> msg object"""
|
||||
# WARNING: highly unsafe!
|
||||
# think message='import os\nos.unlink('\')\n'
|
||||
try:
|
||||
return eval(encoded, self.namespace, {})
|
||||
except SyntaxError:
|
||||
return messages.HelpMessage()
|
Reference in New Issue
Block a user