Remove obsolete protocol implementations

Change-Id: I9342ff3d00666238b6412b41a3785dadd96a7778
Reviewed-on: https://forge.frm2.tum.de/review/16273
Tested-by: JenkinsCodeReview <bjoern_pedersen@frm2.tum.de>
Reviewed-by: Enrico Faulhaber <enrico.faulhaber@frm2.tum.de>
This commit is contained in:
Enrico Faulhaber 2017-09-18 14:39:03 +02:00
parent 38c6729c84
commit eeac276601
21 changed files with 52 additions and 1243 deletions

View File

@ -0,0 +1,6 @@
SECoP Encoding
==============
.. automodule:: secop.protocol.encoding.secop
:members:

View File

@ -1,6 +0,0 @@
Demo V2
=======
.. automodule:: secop.protocol.encoding.demo_v2
:members:

View File

@ -1,6 +0,0 @@
Demo V3
=======
.. automodule:: secop.protocol.encoding.demo_v2
:members:

View File

@ -1,6 +0,0 @@
Demo V4
=======
.. automodule:: secop.protocol.encoding.demo_v2
:members:

View File

@ -1,6 +0,0 @@
Demo V5
=======
.. automodule:: secop.protocol.encoding.demo_v2
:members:

View File

@ -1,11 +0,0 @@
Encodings
=========
.. toctree::
:maxdepth: 3
demo_v2
demo_v3
demo_v4
demo_v5

View File

@ -1,10 +1,10 @@
Different protocols protocol stack
=================== ==============
.. toctree:: .. toctree::
:maxdepth: 3 :maxdepth: 3
encoding/index encoding
framing/index framing/index
interface/index interface/index

View File

@ -19,7 +19,7 @@ bindto=0.0.0.0
bindport=10767 bindport=10767
# protocol to use for this interface # protocol to use for this interface
framing=eol framing=eol
encoding=demo encoding=secop
[device enable] [device enable]
class=secop_mlz.entangle.NamedDigitalOutput class=secop_mlz.entangle.NamedDigitalOutput

View File

@ -11,7 +11,7 @@ bindto=0.0.0.0
bindport=10767 bindport=10767
# protocol to use for this interface # protocol to use for this interface
framing=eol framing=eol
encoding=demo encoding=secop
[device automatik] [device automatik]
class=secop_mlz.entangle.NamedDigitalOutput class=secop_mlz.entangle.NamedDigitalOutput

View File

@ -11,7 +11,7 @@ bindto=0.0.0.0
bindport=10769 bindport=10769
# protocol to use for this interface # protocol to use for this interface
framing=eol framing=eol
encoding=demo encoding=secop
[device cryo] [device cryo]

View File

@ -7,7 +7,7 @@ bindto=0.0.0.0
bindport=10767 bindport=10767
# protocol to use for this interface # protocol to use for this interface
framing=eol framing=eol
encoding=demo encoding=secop
[device heatswitch] [device heatswitch]
class=secop_demo.modules.Switch class=secop_demo.modules.Switch

View File

@ -6,7 +6,7 @@ connectto=0.0.0.0
port=10767 port=10767
interface = tcp interface = tcp
framing=eol framing=eol
encoding=text encoding=secop
[interface testing] [interface testing]
interface=tcp interface=tcp
@ -14,7 +14,7 @@ bindto=0.0.0.0
bindport=10767 bindport=10767
# protocol to use for this interface # protocol to use for this interface
framing=eol framing=eol
encoding=demo encoding=secop
[device tc1] [device tc1]
class=secop_demo.demo.CoilTemp class=secop_demo.demo.CoilTemp

View File

@ -14,7 +14,7 @@ bindto=0.0.0.0
bindport=10768 bindport=10768
# protocol to use for this interface # protocol to use for this interface
framing=eol framing=eol
encoding=demo encoding=secop
[device LN2] [device LN2]

View File

@ -26,20 +26,37 @@
from __future__ import print_function from __future__ import print_function
#from secop.lib.parsing import format_time
from secop.protocol.encoding import MessageEncoder
from secop.protocol.messages import *
#from secop.protocol.errors import ProtocolError
import ast # Base class
class MessageEncoder(object):
"""en/decode a single Messageobject"""
def encode(self, msg):
"""encodes the given message object into a frame"""
raise NotImplementedError
def decode(self, encoded):
"""decodes the given frame to a message object"""
raise NotImplementedError
import re import re
import json import json
#from secop.lib.parsing import format_time
from secop.protocol.messages import Value, IdentifyRequest, IdentifyReply, \
DescribeRequest, DescribeReply, ActivateRequest, ActivateReply, \
DeactivateRequest, DeactivateReply, CommandRequest, CommandReply, \
WriteRequest, WriteReply, PollRequest, HeartbeatRequest, HeartbeatReply, \
ErrorMessage, HelpMessage
#from secop.protocol.errors import ProtocolError
# each message is like <messagetype> [ \space <messageargs> [ \space # each message is like <messagetype> [ \space <messageargs> [ \space
# <json> ]] \lf # <json> ]] \lf
# note: the regex allow <> for spec for testing only! # note: the regex allow <> for spec for testing only!
DEMO_RE = re.compile( SECOP_RE = re.compile(
r"""^(?P<msgtype>[\*\?\w]+)(?:\s(?P<spec>[\w:<>]+)(?:\s(?P<json>.*))?)?$""", r"""^(?P<msgtype>[\*\?\w]+)(?:\s(?P<spec>[\w:<>]+)(?:\s(?P<json>.*))?)?$""",
re.X) re.X)
@ -113,7 +130,7 @@ def encode_error_msg(emsg):
] ]
class DemoEncoder(MessageEncoder): class SECoPEncoder(MessageEncoder):
# map of msg to msgtype string as defined above. # map of msg to msgtype string as defined above.
ENCODEMAP = { ENCODEMAP = {
IdentifyRequest: (IDENTREQUEST, ), IdentifyRequest: (IDENTREQUEST, ),
@ -170,7 +187,7 @@ class DemoEncoder(MessageEncoder):
DECODEMAP = { DECODEMAP = {
IDENTREQUEST: lambda spec, data: IdentifyRequest(), IDENTREQUEST: lambda spec, data: IdentifyRequest(),
# handled specially, listed here for completeness # handled specially, listed here for completeness
IDENTREPLY: lambda spec, data: IdentifyReply(encoded), # IDENTREPLY: lambda spec, data: IdentifyReply(encoded),
DESCRIPTIONSREQUEST: lambda spec, data: DescribeRequest(), DESCRIPTIONSREQUEST: lambda spec, data: DescribeRequest(),
DESCRIPTIONREPLY: lambda spec, data: DescribeReply(equipment_id=spec[0], description=data), DESCRIPTIONREPLY: lambda spec, data: DescribeReply(equipment_id=spec[0], description=data),
ENABLEEVENTSREQUEST: lambda spec, data: ActivateRequest(), ENABLEEVENTSREQUEST: lambda spec, data: ActivateRequest(),
@ -187,7 +204,8 @@ class DemoEncoder(MessageEncoder):
HELPREQUEST: lambda spec, data: HelpMessage(), HELPREQUEST: lambda spec, data: HelpMessage(),
# HELPREPLY: lambda spec, data:None, # ignore this # HELPREPLY: lambda spec, data:None, # ignore this
ERRORREPLY: lambda spec, data: ErrorMessage(errorclass=spec[0], errorinfo=data), ERRORREPLY: lambda spec, data: ErrorMessage(errorclass=spec[0], errorinfo=data),
EVENT: lambda spec, data: Value(module=spec[0], parameter=spec[1], value=data[0], qualifiers=data[1] if len(data) > 1 else {}), EVENT: 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): def __init__(self, *args, **kwds):
@ -235,7 +253,7 @@ class DemoEncoder(MessageEncoder):
def decode(self, encoded): def decode(self, encoded):
# first check beginning # first check beginning
match = DEMO_RE.match(encoded) match = SECOP_RE.match(encoded)
if not match: if not match:
print(repr(encoded), repr(IDENTREPLY)) print(repr(encoded), repr(IDENTREPLY))
if encoded == IDENTREPLY: # XXX:better just check the first 2 parts... if encoded == IDENTREPLY: # XXX:better just check the first 2 parts...
@ -277,7 +295,7 @@ class DemoEncoder(MessageEncoder):
def tests(self): def tests(self):
print("---- Testing encoding -----") print("---- Testing encoding -----")
for msgclass, parts in sorted(self.ENCODEMAP.items()): for msgclass in sorted(self.ENCODEMAP):
print(msgclass) print(msgclass)
e = self.encode( e = self.encode(
msgclass( msgclass(
@ -305,3 +323,10 @@ class DemoEncoder(MessageEncoder):
print(self.encode(d)) print(self.encode(d))
print() print()
print("---- Testing done -----") print("---- Testing done -----")
ENCODERS = {
'secop': SECoPEncoder,
}
__ALL__ = ['ENCODERS']

View File

@ -1,61 +0,0 @@
#!/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 .demo_v5 import DemoEncoder as DemoEncoderV5
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_v5': DemoEncoderV5,
'demo': DemoEncoderV5,
'scp': SCPEncoder,
}
__ALL__ = ['ENCODERS']

View File

@ -1,111 +0,0 @@
#!/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 __future__ import print_function
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_NoSuchModuleError(self, device):
return '~NoSuchModuleError~ %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'
]

View File

@ -1,387 +0,0 @@
#!/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 __future__ import print_function
from secop.protocol.encoding import MessageEncoder
from secop.protocol.messages import *
from secop.protocol.errors import ProtocolError
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 {
ListModulesRequest: lambda msg: "list",
ListModulesReply: lambda msg: "list=%s" % ','.join(sorted(msg.list_of_devices)),
GetVersionRequest: lambda msg: "version",
GetVersionReply: lambda msg: "version=%r" % msg.version,
ListModuleParamsRequest: lambda msg: "list %s" % msg.device,
# do not include a '.' as param name!
ListModuleParamsReply: 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
ReadAllModulesRequest: lambda msg: "",
ReadAllModulesReply: 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: "",
ProtocolError: 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),
NoSuchModuleError: 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)

View File

@ -1,307 +0,0 @@
#!/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 __future__ import print_function
from secop.lib.parsing import format_time
from secop.protocol.encoding import MessageEncoder
from secop.protocol.messages import *
#from secop.protocol.errors import ProtocolError
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
# literal! first part is fixed!
#IDENTREPLY = 'SECoP, SECoPTCP, 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
ENABLEEVENTSREPLY = 'active' # literal, is end-of-initial-data-transfer
DISABLEEVENTSREQUEST = 'deactivate' # literal
DISABLEEVENTSREPLY = 'inactive' # literal
COMMANDREQUEST = 'do' # +module:command +json args (if needed)
# +module:command +json args (if needed) # send after the command finished !
COMMANDREPLY = 'done'
# +module[:parameter] +json_value -> NO direct reply, calls TRIGGER internally!
WRITEREQUEST = 'change'
# +module[:parameter] +json_value # send with the read back value
WRITEREPLY = 'changed'
# +module[:parameter] -> NO direct reply, calls TRIGGER internally!
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
HELPREQUEST = 'help' # literal
HELPREPLY = 'helping' # +line number +json_text
ERRORCLASSES = [
'NoSuchModule',
'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
def encode_cmd_result(msgobj):
q = msgobj.qualifiers.copy()
if 't' in q:
q['t'] = format_time(q['t'])
return msgobj.result, q
def encode_value_data(vobj):
q = vobj.qualifiers.copy()
if 't' in q:
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.
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', ),
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, ),
}
DECODEMAP = {
IDENTREQUEST: lambda spec, data: IdentifyRequest(),
# handled specially, listed here for completeness
IDENTREPLY: lambda spec, data: IdentifyReply(encoded),
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], result=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),
EVENT: 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]))
if isinstance(msg, HeartbeatRequest):
if msg.nonce:
return 'ping %s' % msg.nonce
return 'ping'
if isinstance(msg, HeartbeatReply):
if msg.nonce:
return 'pong %s' % msg.nonce
return 'pong'
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='Protocol',
# errorinfo='Regex did not match!',
# 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)
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)],
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,
is_request=True,
origin=encoded)
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 == EVENT:
msg = '%s a:b [3,{"t":193868}]' % msgtype
print(msg)
d = self.decode(msg)
print(d)
print(self.encode(d))
print()
print("---- Testing done -----")

View File

@ -1,45 +0,0 @@
#!/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)

View File

@ -1,212 +0,0 @@
#!/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=NosuchModule,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 {
ListModulesRequest: lambda msg: "devices?",
ListModulesReply: lambda msg: "0 devices=" + repr(list(msg.list_of_devices)),
GetVersionRequest: lambda msg: "version?",
GetVersionReply: lambda msg: "0 version=%r" % msg.version,
ListModuleParamsRequest: lambda msg: "%s/parameters?" % msg.device,
ListModuleParamsReply: 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
ReadAllModulesRequest: lambda msg: "*/value?",
ReadAllModulesReply: 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....
NoSuchModuleError: 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 ListModulesRequest()
elif par == 'version':
return GetVersionRequest()
return ProtocolError()
if par == 'parameters':
return ListModuleParamsRequest(dev)
elif par == 'value':
return ReadValueRequest(dev)
elif dev == '*' and par == 'value':
return ReadAllModulesRequest()
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 ListModulesReply(val)
elif par == 'version':
return GetVersionReply(val)
return ProtocolError(encoded)
if par == 'parameters':
return ListModuleParamsReply(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 NoSuchModuleError(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)

View File

@ -1,64 +0,0 @@
#!/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()