Pep8 improvements + cleanup

Change-Id: I9052e703b58e93b639c027521b47f693ae853f6e
This commit is contained in:
Enrico Faulhaber
2016-12-15 14:36:12 +01:00
parent 7320ac1538
commit 78bb3b5f96
20 changed files with 395 additions and 250 deletions

View File

@ -36,10 +36,6 @@ Interface to the modules:
- get_module(modulename) returns the requested module or None
- remove_module(modulename_or_obj): removes the module (during shutdown)
internal stuff which may be called
- list_modules(): return a list of modules + descriptive data as dict
- list_module_params():
return a list of paramnames for this module + descriptive data
"""
import time
@ -50,20 +46,21 @@ from errors import *
class Dispatcher(object):
def __init__(self, logger, options):
self.equipment_id = options.pop('equipment_id')
self.log = logger
# map ALL modulename -> moduleobj
self._dispatcher_modules = {}
self._modules = {}
# list of EXPORTED modules
self._dispatcher_export = []
self._export = []
# list all connections
self._dispatcher_connections = []
self._connections = []
# active (i.e. broadcast-receiving) connections
self._dispatcher_active_connections = set()
self._active_connections = set()
# map eventname -> list of subscribed connections
self._dispatcher_subscriptions = {}
self._dispatcher_lock = threading.RLock()
self._subscriptions = {}
self._lock = threading.RLock()
def handle_request(self, conn, msg):
"""handles incoming request
@ -72,7 +69,7 @@ class Dispatcher(object):
"""
self.log.debug('Dispatcher: handling msg: %r' % msg)
# play thread safe !
with self._dispatcher_lock:
with self._lock:
reply = None
# generate reply (coded and framed)
msgname = msg.__class__.__name__
@ -90,7 +87,7 @@ class Dispatcher(object):
reply = ErrorMessage(errorclass=err.__class__.__name__,
errorinfo=[repr(err), str(msg)])
except (ValueError, TypeError) as err:
# self.log.exception(err)
# self.log.exception(err)
reply = ErrorMessage(errorclass='BadValue',
errorinfo=[repr(err), str(msg)])
except Exception as err:
@ -106,118 +103,123 @@ class Dispatcher(object):
def broadcast_event(self, msg, reallyall=False):
"""broadcasts a msg to all active connections"""
if reallyall:
listeners = self._dispatcher_connections
listeners = self._connections
else:
if getattr(msg, 'command', None) is None:
eventname = '%s:%s' % (msg.module, msg.parameter if msg.parameter else 'value')
eventname = '%s:%s' % (
msg.module, msg.parameter if msg.parameter else 'value')
else:
eventname = '%s:%s()' % (msg.module, msg.command)
listeners = self._dispatcher_subscriptions.get(eventname, [])
listeners += list(self._dispatcher_active_connections)
listeners = self._subscriptions.get(eventname, [])
listeners += list(self._active_connections)
for conn in listeners:
conn.queue_async_reply(msg)
def announce_update(self, moduleobj, pname, pobj):
"""called by modules param setters to notify subscribers of new values
"""
msg = Value(moduleobj.name, parameter=pname, value=pobj.value, t=pobj.timestamp)
msg = Value(
moduleobj.name,
parameter=pname,
value=pobj.value,
t=pobj.timestamp)
self.broadcast_event(msg)
def subscribe(self, conn, modulename, pname='value'):
eventname = '%s:%s' % (modulename, pname)
self._dispatcher_subscriptions.setdefault(eventname, set()).add(conn)
self._subscriptions.setdefault(eventname, set()).add(conn)
def unsubscribe(self, conn, modulename, pname='value'):
eventname = '%s:%s' % (modulename, pname)
if eventname in self._dispatcher_subscriptions:
self._dispatcher_subscriptions.remove(conn)
if eventname in self._subscriptions:
self._subscriptions.remove(conn)
def add_connection(self, conn):
"""registers new connection"""
self._dispatcher_connections.append(conn)
self._connections.append(conn)
def remove_connection(self, conn):
"""removes now longer functional connection"""
if conn in self._dispatcher_connections:
self._dispatcher_connections.remove(conn)
for _evt, conns in self._dispatcher_subscriptions.items():
if conn in self._connections:
self._connections.remove(conn)
for _evt, conns in self._subscriptions.items():
conns.discard(conn)
def activate_connection(self, conn):
self._dispatcher_active_connections.add(conn)
self._active_connections.add(conn)
def deactivate_connection(self, conn):
self._dispatcher_active_connections.discard(conn)
self._active_connections.discard(conn)
def register_module(self, moduleobj, modulename, export=True):
self.log.debug('registering module %r as %s (export=%r)' %
(moduleobj, modulename, export))
self._dispatcher_modules[modulename] = moduleobj
self._modules[modulename] = moduleobj
if export:
self._dispatcher_export.append(modulename)
self._export.append(modulename)
def get_module(self, modulename):
module = self._dispatcher_modules.get(modulename, modulename)
module = self._modules.get(modulename, modulename)
return module
def remove_module(self, modulename_or_obj):
moduleobj = self.get_module(modulename_or_obj) or modulename_or_obj
modulename = moduleobj.name
if modulename in self._dispatcher_export:
self._dispatcher_export.remove(modulename)
self._dispatcher_modules.pop(modulename)
# XXX: also clean _dispatcher_subscriptions
if modulename in self._export:
self._export.remove(modulename)
self._modules.pop(modulename)
# XXX: also clean _subscriptions
def list_module_names(self):
# return a copy of our list
return self._dispatcher_export[:]
def list_modules(self):
dn = []
dd = {}
for modulename in self._dispatcher_export:
dn.append(modulename)
module = self.get_module(modulename)
descriptive_data = {
'class': module.__class__.__name__,
#'bases': module.__bases__,
'parameters': module.PARAMS.keys(),
'commands': module.CMDS.keys(),
# XXX: what else?
}
dd[modulename] = descriptive_data
return dn, dd
def get_descriptive_data(self):
# XXX: be lazy and cache this?
result = {}
for modulename in self._dispatcher_export:
module = self.get_module(modulename)
dd = {'class' : module.__class__.__name__,
'bases' : [b.__name__ for b in module.__class__.__bases__],
'parameters': dict((pn,po.as_dict()) for pn,po in module.PARAMS.items()),
'commands': dict((cn,co.as_dict()) for cn,co in module.CMDS.items()),
'baseclass' : 'Readable',
}
result.setdefault('modules', {})[modulename] = dd
result['equipment_id'] = self.equipment_id
# XXX: what else?
return result
return self._export[:]
def list_module_params(self, modulename):
self.log.debug('list_module_params(%r)' % modulename)
if modulename in self._dispatcher_export:
# XXX: omit export=False params!
if modulename in self._export:
# omit export=False params!
res = {}
for paramname, param in self.get_module(modulename).PARAMS.items():
if param.export == True:
res[paramname] = param
if param.export:
res[paramname] = param.as_dict()
self.log.debug('list params for module %s -> %r' %
(modulename, res))
return res
self.log.debug('-> module is not to be exported!')
return {}
def list_module_cmds(self, modulename):
self.log.debug('list_module_cmds(%r)' % modulename)
if modulename in self._export:
# omit export=False params!
res = {}
for cmdname, cmdobj in self.get_module(modulename).CMDS.items():
res[cmdname] = cmdobj.as_dict()
self.log.debug('list cmds for module %s -> %r' %
(modulename, res))
return res
self.log.debug('-> module is not to be exported!')
return {}
def get_descriptive_data(self):
# XXX: be lazy and cache this?
result = {'modules':{}}
for modulename in self._export:
module = self.get_module(modulename)
# some of these need rework !
dd = {'class': module.__class__.__name__,
'bases': [b.__name__ for b in module.__class__.__bases__],
'parameters': self.list_module_params(modulename),
'commands': self.list_module_cmds(modulename),
'baseclass': 'Readable',
}
result['modules'][modulename] = dd
result['equipment_id'] = self.equipment_id
result['firmware'] = 'The SECoP playground'
result['version'] = "2016.12"
# XXX: what else?
return result
def _execute_command(self, modulename, command, arguments=None):
if arguments is None:
arguments = []
@ -230,13 +232,22 @@ class Dispatcher(object):
if cmdspec is None:
raise NoSuchCommandError(module=modulename, command=command)
if len(cmdspec.arguments) != len(arguments):
raise BadValueError(module=modulename, command=command, reason='Wrong number of arguments!')
raise BadValueError(
module=modulename,
command=command,
reason='Wrong number of arguments!')
# now call func and wrap result as value
# note: exceptions are handled in handle_request, not here!
func = getattr(moduleobj, 'do'+command)
func = getattr(moduleobj, 'do' + command)
res = func(*arguments)
res = CommandReply(module=modulename, command=command, result=[res, dict(t=time.time())])
res = CommandReply(
module=modulename,
command=command,
result=[
res,
dict(
t=time.time())])
# res = Value(modulename, command=command, value=func(*arguments), t=time.time())
return res
@ -258,8 +269,16 @@ class Dispatcher(object):
else:
setattr(moduleobj, pname, value)
if pobj.timestamp:
return WriteReply(module=modulename, parameter=pname, value=[pobj.value, dict(t=pobj.timestamp)])
return WriteReply(module=modulename, parameter=pname, value=[pobj.value, {}])
return WriteReply(
module=modulename, parameter=pname, value=[
pobj.value, dict(
t=pobj.timestamp)])
return WriteReply(
module=modulename,
parameter=pname,
value=[
pobj.value,
{}])
def _getParamValue(self, modulename, pname):
moduleobj = self.get_module(modulename)
@ -276,10 +295,13 @@ class Dispatcher(object):
# note: exceptions are handled in handle_request, not here!
readfunc()
if pobj.timestamp:
return Value(modulename, parameter=pname, value=pobj.value, t=pobj.timestamp)
return Value(
modulename,
parameter=pname,
value=pobj.value,
t=pobj.timestamp)
return Value(modulename, parameter=pname, value=pobj.value)
# now the (defined) handlers for the different requests
def handle_Help(self, conn, msg):
return HelpMessage()
@ -289,21 +311,24 @@ class Dispatcher(object):
def handle_Describe(self, conn, msg):
# XXX:collect descriptive data
return DescribeReply(equipment_id = self.equipment_id, description = self.get_descriptive_data())
return DescribeReply(
equipment_id=self.equipment_id,
description=self.get_descriptive_data())
def handle_Poll(self, conn, msg):
# XXX: trigger polling and force sending event
res = self._getParamValue(msg.module, msg.parameter or 'value')
#self.broadcast_event(res)
if conn in self._dispatcher_active_connections:
# self.broadcast_event(res)
if conn in self._active_connections:
return None # already send to myself
return res # send reply to inactive conns
def handle_Write(self, conn, msg):
# notify all by sending WriteReply
#msg1 = WriteReply(**msg.as_dict())
#self.broadcast_event(msg1)
# try to actually write XXX: should this be done asyncron? we could just return the reply in that case
# self.broadcast_event(msg1)
# try to actually write XXX: should this be done asyncron? we could
# just return the reply in that case
if msg.parameter:
res = self._setParamValue(msg.module, msg.parameter, msg.value)
else:
@ -312,21 +337,22 @@ class Dispatcher(object):
raise ReadonlyError(module=msg.module, parameter=None)
res = self._setParamValue(msg.module, 'target', msg.value)
res.parameter = 'target'
#self.broadcast_event(res)
if conn in self._dispatcher_active_connections:
# self.broadcast_event(res)
if conn in self._active_connections:
return None # already send to myself
return res # send reply to inactive conns
def handle_Command(self, conn, msg):
# notify all by sending CommandReply
#msg1 = CommandReply(**msg.as_dict())
#self.broadcast_event(msg1)
# XXX: should this be done asyncron? we could just return the reply in that case
# self.broadcast_event(msg1)
# XXX: should this be done asyncron? we could just return the reply in
# that case
# try to actually execute command
res = self._execute_command(msg.module, msg.command, msg.arguments)
#self.broadcast_event(res)
#if conn in self._dispatcher_active_connections:
# self.broadcast_event(res)
# if conn in self._active_connections:
# return None # already send to myself
return res # send reply to inactive conns
@ -336,7 +362,7 @@ class Dispatcher(object):
def handle_Activate(self, conn, msg):
self.activate_connection(conn)
# easy approach: poll all values...
for modulename, moduleobj in self._dispatcher_modules.items():
for modulename, moduleobj in self._modules.items():
for pname, pobj in moduleobj.PARAMS.items():
# WARNING: THIS READS ALL PARAMS FROM HW!
# XXX: should we send the cached values instead? (pbj.value)
@ -349,7 +375,7 @@ class Dispatcher(object):
res = Value(module=modulename, parameter=pname,
value=pobj.value, t=pobj.timestamp,
unit=pobj.unit)
if res.value != Ellipsis: # means we do not have a value at all so skip this
if res.value != Ellipsis: # means we do not have a value at all so skip this
self.broadcast_event(res)
conn.queue_async_reply(ActivateReply(**msg.as_dict()))
return None
@ -369,6 +395,4 @@ class Dispatcher(object):
"""
self.log.error('IGN: got unhandled request %s' % msg)
return ErrorMessage(errorclass="InternalError",
errorstring = 'Got Unhandled Request %r' % msg)
errorstring='Got Unhandled Request %r' % msg)

View File

@ -98,7 +98,7 @@ class DemoEncoder(MessageEncoder):
MessageEncoder.__init__(self, *args, **kwds)
self.result = [] # for decoding
self.expect_lines = 1
#self.tests()
# self.tests()
def encode(self, msg):
"""msg object -> transport layer message"""
@ -179,8 +179,13 @@ class DemoEncoder(MessageEncoder):
return '\n'.join(result)
if isinstance(msg, ErrorMessage):
return ('%s %s' % (devspec(msg, 'error %s' %
msg.errortype), msg.errorstring)).strip()
return (
'%s %s' %
(devspec(
msg,
'error %s' %
msg.errortype),
msg.errorstring)).strip()
return 'Can not handle object %r!' % msg
@ -292,7 +297,11 @@ class DemoEncoder(MessageEncoder):
# construct messageobj
if msgtype in MESSAGE:
return MESSAGE[msgtype](
devs=devs, pars=pars, props=props, result=result, **mgroups)
devs=devs,
pars=pars,
props=props,
result=result,
**mgroups)
return ErrorMessage(errortype="SyntaxError",
errorstring="Can't handle %r" % encoded)

View File

@ -34,38 +34,47 @@ import ast
import re
import json
# each message is like <messagetype> [ \space <messageargs> [ \space <json> ]] \lf
# 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)
r"""^(?P<msgtype>[\*\?\w]+)(?:\s(?P<spec>[\w:<>]+)(?:\s(?P<json>.*))?)?$""",
re.X)
#"""
# messagetypes:
IDENTREQUEST = '*IDN?' # literal
IDENTREPLY = 'SECoP, SECoPTCP, V2016-11-30, rc1' # literal! first part 'SECoP' is fixed!
# literal! first part 'SECoP' is fixed!
IDENTREPLY = 'SECoP, SECoPTCP, V2016-11-30, rc1'
DESCRIPTIONSREQUEST = 'describe' # literal
DESCRIPTIONREPLY = 'describing' # +<id> +json
ENABLEEVENTSREQUEST = 'activate' # literal
ENABLEEVENTSREQUEST = 'activate' # literal
ENABLEEVENTSREPLY = 'active' # literal, is end-of-initial-data-transfer
DISABLEEVENTSREQUEST = 'deactivate' # literal
DISABLEEVENTSREPLY = 'inactive' # literal
COMMANDREQUEST = 'do' # +module:command +json args (if needed)
COMMANDREPLY = 'done' # +module:command +json args (if needed) # send after the command finished !
WRITEREQUEST = 'change' # +module[:parameter] +json_value -> NO direct reply, calls TRIGGER internally!
WRITEREPLY = 'changed' # +module[:parameter] +json_value # send with the read back value
TRIGGERREQUEST = 'poll' # +module[:parameter] -> NO direct reply, calls TRIGGER internally!
# +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 = 'poll'
EVENT = 'event' # +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
HELPREQUEST = 'help' # literal
HELPREPLY = 'helping' # +line number +json_text
ERRORCLASSES = ['NoSuchDevice', 'NoSuchParameter', 'NoSuchCommand',
'CommandFailed', 'ReadOnly', 'BadValue', 'CommunicationFailed',
'IsBusy', 'IsError', 'SyntaxError', 'InternalError',
'CommandRunning', 'Disabled',]
# note: above strings need to be unique in the sense, that none is/or starts with another
'CommandRunning', 'Disabled', ]
# note: above strings need to be unique in the sense, that none is/or
# starts with another
def encode_value_data(vobj):
q = vobj.qualifiers.copy()
@ -73,55 +82,57 @@ def encode_value_data(vobj):
q['t'] = format_time(q['t'])
return vobj.value, q
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,),
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), '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',),
DeactivateReply: (DISABLEEVENTSREPLY,),
CommandRequest: (COMMANDREQUEST, lambda msg: "%s:%s" % (msg.module, msg.command), 'arguments',),
CommandReply: (COMMANDREPLY, lambda msg: "%s:%s" % (msg.module, msg.command), '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', 'errorinfo',),
Value: (EVENT, lambda msg: "%s:%s" % (msg.module, msg.parameter or (msg.command+'()'))
if msg.parameter or msg.command else msg.module,
encode_value_data,),
ErrorMessage: (ERRORREPLY, 'errorclass', 'errorinfo',),
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(),
IDENTREPLY : lambda spec, data: IdentifyReply(encoded), # handled specially, listed here for completeness
DESCRIPTIONSREQUEST : lambda spec, data: DescribeRequest(),
DESCRIPTIONREPLY : lambda spec, data: DescribeReply(equipment_id=spec[0], description=data),
ENABLEEVENTSREQUEST : lambda spec, data: ActivateRequest(),
ENABLEEVENTSREPLY: lambda spec, data:ActivateReply(),
DISABLEEVENTSREQUEST: lambda spec, data:DeactivateRequest(),
DISABLEEVENTSREPLY: lambda spec, data:DeactivateReply(),
COMMANDREQUEST: lambda spec, data:CommandRequest(module=spec[0], command=spec[1], arguments=data),
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 {}),
}
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()
# self.tests()
def encode(self, msg):
"""msg object -> transport layer message"""
@ -136,20 +147,21 @@ class DemoEncoder(MessageEncoder):
'%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]))
""" % (IDENTREQUEST, DESCRIPTIONSREQUEST, TRIGGERREQUEST,
WRITEREQUEST, COMMANDREQUEST, HEARTBEATREQUEST,
ENABLEEVENTSREQUEST, DISABLEEVENTSREQUEST)
return '\n'.join('%s %d %s' % (HELPREPLY, i + 1, l.strip())
for i, l in enumerate(text.split('\n')[:-1]))
for msgcls, parts in self.ENCODEMAP.items():
if isinstance(msg, msgcls):
# resolve lambdas
parts = [parts[0]] + [p(msg) if callable(p) else getattr(msg, p) for p in parts[1:]]
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
@ -158,17 +170,17 @@ class DemoEncoder(MessageEncoder):
print repr(encoded), repr(IDENTREPLY)
if encoded == IDENTREPLY: # XXX:better just check the first 2 parts...
return IdentifyReply(version_string=encoded)
return HelpMessage()
return ErrorMessage(errorclass='SyntaxError',
return ErrorMessage(errorclass='SyntaxError',
errorinfo='Regex did not match!',
is_request=True)
msgtype, msgspec, data = match.groups()
if msgspec is None and data:
return ErrorMessage(errorclass='InternalError',
return ErrorMessage(errorclass='InternalError',
errorinfo='Regex matched json, but not spec!',
is_request=True)
if msgtype in self.DECODEMAP:
if msgspec and ':' in msgspec:
msgspec = msgspec.split(':', 1)
@ -181,16 +193,28 @@ class DemoEncoder(MessageEncoder):
return ErrorMessage(errorclass='BadValue',
errorinfo=[repr(err), str(encoded)])
return self.DECODEMAP[msgtype](msgspec, data)
return ErrorMessage(errorclass='SyntaxError',
errorinfo='%r: No Such Messagetype defined!' % encoded,
is_request=True)
return ErrorMessage(
errorclass='SyntaxError',
errorinfo='%r: No Such Messagetype defined!' %
encoded,
is_request=True)
def tests(self):
print "---- Testing encoding -----"
for msgclass, parts in sorted(self.ENCODEMAP.items()):
print msgclass
e=self.encode(msgclass(module='<module>',parameter='<paramname>',value=2.718,equipment_id='<id>',description='descriptive data',command='<cmd>',arguments='<arguments>',nonce='<nonce>',errorclass='InternalError',errorinfo='nix'))
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
@ -200,9 +224,8 @@ class DemoEncoder(MessageEncoder):
if msgtype == EVENT:
msg = '%s a:b [3,{"t":193868}]' % msgtype
print msg
d=self.decode(msg)
d = self.decode(msg)
print d
print self.encode(d)
print
print "---- Testing done -----"

View File

@ -35,7 +35,6 @@ except ImportError:
import pickle
class PickleEncoder(MessageEncoder):
def encode(self, messageobj):

View File

@ -24,9 +24,10 @@
class SECOPError(RuntimeError):
def __init__(self, *args, **kwds):
self.args = args
for k,v in kwds.items():
for k, v in kwds.items():
setattr(self, k, v)

View File

@ -80,5 +80,3 @@ class DemoFramer(Framer):
def reset(self):
self.data = b''
self.decoded = []

View File

@ -70,5 +70,3 @@ class RLEFramer(Framer):
def reset(self):
self.data = b''
self.frames_to_go = 0

View File

@ -34,6 +34,7 @@ from secop.protocol.encoding import ENCODERS
from secop.protocol.framing import FRAMERS
from secop.protocol.messages import HelpMessage
class TCPRequestHandler(SocketServer.BaseRequestHandler):
def setup(self):
@ -49,7 +50,7 @@ class TCPRequestHandler(SocketServer.BaseRequestHandler):
clientaddr = self.client_address
serverobj = self.server
self.log.debug("handling new connection from %s" % repr(clientaddr))
# notify dispatcher of us
serverobj.dispatcher.add_connection(self)
@ -81,7 +82,7 @@ class TCPRequestHandler(SocketServer.BaseRequestHandler):
# dispatcher will queue the reply before returning
frames = self.framing.decode(data)
if frames is not None:
if not frames: # empty list
if not frames: # empty list
self.queue_reply(HelpMessage(MSGTYPE=reply))
for frame in frames:
reply = None
@ -129,7 +130,8 @@ class TCPServer(SocketServer.ThreadingTCPServer):
self.encodingCLS = ENCODERS[interfaceopts.pop('encoding', 'pickle')]
self.log.debug("TCPServer binding to %s:%d" % (bindto, portnum))
self.log.debug("TCPServer using framing=%s" % self.framingCLS.__name__)
self.log.debug("TCPServer using encoding=%s" % self.encodingCLS.__name__)
self.log.debug("TCPServer using encoding=%s" %
self.encodingCLS.__name__)
SocketServer.ThreadingTCPServer.__init__(self, (bindto, portnum),
TCPRequestHandler,
bind_and_activate=True)

View File

@ -45,18 +45,19 @@ class Message(object):
def as_dict(self):
"""returns set parameters as dict"""
return dict(map(lambda k:(k, getattr(self,k)),self.ARGS))
return dict(map(lambda k: (k, getattr(self, k)), self.ARGS))
class Value(object):
def __init__(self, module, parameter=None, command=None, value=Ellipsis, **qualifiers):
def __init__(self, module, parameter=None, command=None, value=Ellipsis,
**qualifiers):
self.module = module
self.parameter = parameter
self.command = command
self.value = value
self.qualifiers = qualifiers
self.msgtype = 'update' # 'changed' or 'done'
self.msgtype = 'update' # 'changed' or 'done'
def __repr__(self):
devspec = self.module
@ -65,79 +66,96 @@ class Value(object):
elif self.command:
devspec = '%s:%s()' % (devspec, self.command)
return '%s:Value(%s)' % (devspec, ', '.join(
[repr(self.value)] + ['%s=%s' % (k, repr(v)) for k, v in self.qualifiers.items()]))
[repr(self.value)] +
['%s=%s' % (k, repr(v)) for k, v in self.qualifiers.items()]))
class IdentifyRequest(Message):
is_request = True
class IdentifyReply(Message):
is_reply = True
version_string = None
class DescribeRequest(Message):
is_request = True
class DescribeReply(Message):
is_reply = True
equipment_id = None
description = None
class ActivateRequest(Message):
is_request = True
class ActivateReply(Message):
is_reply = True
class DeactivateRequest(Message):
is_request = True
class DeactivateReply(Message):
is_reply = True
class CommandRequest(Message):
is_request = True
command = ''
arguments = []
class CommandReply(Message):
is_reply = True
command = ''
result = None
class WriteRequest(Message):
is_request = True
module = None
parameter = None
value = None
class WriteReply(Message):
is_reply = True
module = None
parameter = None
value = None
class PollRequest(Message):
is_request = True
module = None
parameter = None
class HeartbeatRequest(Message):
is_request = True
nonce = 'alive'
class HeartbeatReply(Message):
is_reply = True
nonce = 'undefined'
class EventMessage(Message):
# use Value directly for Replies !
# use Value directly for Replies !
is_reply = True
module = None
parameter = None
command = None
value = None # Value object ! (includes qualifiers!)
class ErrorMessage(Message):
is_error = True
errorclass = 'InternalError'
@ -149,7 +167,6 @@ class HelpMessage(Message):
is_request = True
if __name__ == '__main__':
print("Minimal testing of messages....")
m = Message(MSGTYPE='test', a=1, b=2, c='x')

View File

@ -110,8 +110,10 @@ class Value(object):
devspec = '%s:%s' % (devspec, self.param)
if self.prop:
devspec = '%s:%s' % (devspec, self.prop)
return '%s:Value(%s)' % (devspec, ', '.join(
[repr(self.value)] + ['%s=%r' % (k, v) for k, v in self.qualifiers.items()]))
return '%s:Value(%s)' % (
devspec, ', '.join(
[repr(self.value)] +
['%s=%r' % (k, v) for k, v in self.qualifiers.items()]))
class ListMessage(Message):
@ -165,32 +167,59 @@ class HelpMessage(Message):
class NoSuchDeviceError(ErrorMessage):
def __init__(self, *devs):
ErrorMessage.__init__(self, devs=devs, errorstring="Device %r does not exist" % devs[0], errortype='NoSuchDevice')
ErrorMessage.__init__(
self, devs=devs, errorstring="Device %r does not exist" %
devs[0], errortype='NoSuchDevice')
class NoSuchParamError(ErrorMessage):
def __init__(self, dev, *params):
ErrorMessage.__init__(self, devs=(dev,), params=params, errorstring="Device %r has no parameter %r" % (dev, params[0]), errortype='NoSuchParam')
ErrorMessage.__init__(
self, devs=(dev,),
params=params, errorstring="Device %r has no parameter %r" %
(dev, params[0]),
errortype='NoSuchParam')
class ParamReadonlyError(ErrorMessage):
def __init__(self, dev, *params):
ErrorMessage.__init__(self, devs=(dev,), params=params, errorstring="Device %r, parameter %r is not writeable!" % (dev, params[0]), errortype='ParamReadOnly')
ErrorMessage.__init__(
self, devs=(dev,),
params=params,
errorstring="Device %r, parameter %r is not writeable!" %
(dev, params[0]),
errortype='ParamReadOnly')
class InvalidParamValueError(ErrorMessage):
def __init__(self, dev, param, value, e):
ErrorMessage.__init__(self, devs=(dev,), params=params, values=(value), errorstring=str(e), errortype='InvalidParamValueError')
ErrorMessage.__init__(
self, devs=(dev,),
params=params, values=(value),
errorstring=str(e),
errortype='InvalidParamValueError')
class InternalError(ErrorMessage):
def __init__(self, err, **kwds):
ErrorMessage.__init__(self, errorstring=str(err), errortype='InternalError', **kwds)
ErrorMessage.__init__(
self, errorstring=str(err),
errortype='InternalError', **kwds)
MESSAGE = dict((cls.MSGTYPE, cls) for cls in [HelpMessage, ErrorMessage, EventMessage, TriggerMessage, UnsubscribeMessage, SubscribeMessage,
PollMessage, CommandMessage, WriteMessage, ReadMessage, ListMessage])
MESSAGE = dict(
(cls.MSGTYPE, cls)
for cls
in
[HelpMessage, ErrorMessage, EventMessage, TriggerMessage,
UnsubscribeMessage, SubscribeMessage, PollMessage, CommandMessage,
WriteMessage, ReadMessage, ListMessage])
if __name__ == '__main__':
print("Minimal testing of messages....")