Pep8 improvements + cleanup
Change-Id: I9052e703b58e93b639c027521b47f693ae853f6e
This commit is contained in:
@ -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)
|
||||
|
Reference in New Issue
Block a user