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:
parent
38c6729c84
commit
eeac276601
6
doc/source/playground/server/protocol/encoding.rst
Normal file
6
doc/source/playground/server/protocol/encoding.rst
Normal file
@ -0,0 +1,6 @@
|
||||
SECoP Encoding
|
||||
==============
|
||||
|
||||
.. automodule:: secop.protocol.encoding.secop
|
||||
:members:
|
||||
|
@ -1,6 +0,0 @@
|
||||
Demo V2
|
||||
=======
|
||||
|
||||
.. automodule:: secop.protocol.encoding.demo_v2
|
||||
:members:
|
||||
|
@ -1,6 +0,0 @@
|
||||
Demo V3
|
||||
=======
|
||||
|
||||
.. automodule:: secop.protocol.encoding.demo_v2
|
||||
:members:
|
||||
|
@ -1,6 +0,0 @@
|
||||
Demo V4
|
||||
=======
|
||||
|
||||
.. automodule:: secop.protocol.encoding.demo_v2
|
||||
:members:
|
||||
|
@ -1,6 +0,0 @@
|
||||
Demo V5
|
||||
=======
|
||||
|
||||
.. automodule:: secop.protocol.encoding.demo_v2
|
||||
:members:
|
||||
|
@ -1,11 +0,0 @@
|
||||
Encodings
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
demo_v2
|
||||
demo_v3
|
||||
demo_v4
|
||||
demo_v5
|
||||
|
@ -1,10 +1,10 @@
|
||||
Different protocols
|
||||
===================
|
||||
protocol stack
|
||||
==============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
encoding/index
|
||||
encoding
|
||||
framing/index
|
||||
interface/index
|
||||
|
||||
|
@ -19,7 +19,7 @@ bindto=0.0.0.0
|
||||
bindport=10767
|
||||
# protocol to use for this interface
|
||||
framing=eol
|
||||
encoding=demo
|
||||
encoding=secop
|
||||
|
||||
[device enable]
|
||||
class=secop_mlz.entangle.NamedDigitalOutput
|
||||
|
@ -11,7 +11,7 @@ bindto=0.0.0.0
|
||||
bindport=10767
|
||||
# protocol to use for this interface
|
||||
framing=eol
|
||||
encoding=demo
|
||||
encoding=secop
|
||||
|
||||
[device automatik]
|
||||
class=secop_mlz.entangle.NamedDigitalOutput
|
||||
|
@ -11,7 +11,7 @@ bindto=0.0.0.0
|
||||
bindport=10769
|
||||
# protocol to use for this interface
|
||||
framing=eol
|
||||
encoding=demo
|
||||
encoding=secop
|
||||
|
||||
|
||||
[device cryo]
|
||||
|
@ -7,7 +7,7 @@ bindto=0.0.0.0
|
||||
bindport=10767
|
||||
# protocol to use for this interface
|
||||
framing=eol
|
||||
encoding=demo
|
||||
encoding=secop
|
||||
|
||||
[device heatswitch]
|
||||
class=secop_demo.modules.Switch
|
||||
|
@ -6,7 +6,7 @@ connectto=0.0.0.0
|
||||
port=10767
|
||||
interface = tcp
|
||||
framing=eol
|
||||
encoding=text
|
||||
encoding=secop
|
||||
|
||||
[interface testing]
|
||||
interface=tcp
|
||||
@ -14,7 +14,7 @@ bindto=0.0.0.0
|
||||
bindport=10767
|
||||
# protocol to use for this interface
|
||||
framing=eol
|
||||
encoding=demo
|
||||
encoding=secop
|
||||
|
||||
[device tc1]
|
||||
class=secop_demo.demo.CoilTemp
|
||||
|
@ -14,7 +14,7 @@ bindto=0.0.0.0
|
||||
bindport=10768
|
||||
# protocol to use for this interface
|
||||
framing=eol
|
||||
encoding=demo
|
||||
encoding=secop
|
||||
|
||||
|
||||
[device LN2]
|
||||
|
@ -26,20 +26,37 @@
|
||||
|
||||
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 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
|
||||
# <json> ]] \lf
|
||||
|
||||
# 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>.*))?)?$""",
|
||||
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.
|
||||
ENCODEMAP = {
|
||||
IdentifyRequest: (IDENTREQUEST, ),
|
||||
@ -170,7 +187,7 @@ class DemoEncoder(MessageEncoder):
|
||||
DECODEMAP = {
|
||||
IDENTREQUEST: lambda spec, data: IdentifyRequest(),
|
||||
# handled specially, listed here for completeness
|
||||
IDENTREPLY: lambda spec, data: IdentifyReply(encoded),
|
||||
# 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(),
|
||||
@ -187,7 +204,8 @@ class DemoEncoder(MessageEncoder):
|
||||
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 {}),
|
||||
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):
|
||||
@ -235,7 +253,7 @@ class DemoEncoder(MessageEncoder):
|
||||
|
||||
def decode(self, encoded):
|
||||
# first check beginning
|
||||
match = DEMO_RE.match(encoded)
|
||||
match = SECOP_RE.match(encoded)
|
||||
if not match:
|
||||
print(repr(encoded), repr(IDENTREPLY))
|
||||
if encoded == IDENTREPLY: # XXX:better just check the first 2 parts...
|
||||
@ -277,7 +295,7 @@ class DemoEncoder(MessageEncoder):
|
||||
|
||||
def tests(self):
|
||||
print("---- Testing encoding -----")
|
||||
for msgclass, parts in sorted(self.ENCODEMAP.items()):
|
||||
for msgclass in sorted(self.ENCODEMAP):
|
||||
print(msgclass)
|
||||
e = self.encode(
|
||||
msgclass(
|
||||
@ -305,3 +323,10 @@ class DemoEncoder(MessageEncoder):
|
||||
print(self.encode(d))
|
||||
print()
|
||||
print("---- Testing done -----")
|
||||
|
||||
|
||||
ENCODERS = {
|
||||
'secop': SECoPEncoder,
|
||||
}
|
||||
|
||||
__ALL__ = ['ENCODERS']
|
@ -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']
|
@ -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'
|
||||
]
|
@ -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)
|
@ -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 -----")
|
@ -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)
|
@ -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)
|
@ -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()
|
Loading…
x
Reference in New Issue
Block a user