Provide basic client Object

also improve the describing data and core params

Change-Id: I645444f2a618fdfd40a729e1007c58def24d5ffb
This commit is contained in:
Enrico Faulhaber
2016-12-14 18:26:37 +01:00
parent 0432f01e16
commit 7320ac1538
8 changed files with 509 additions and 28 deletions

View File

@ -116,7 +116,7 @@ class Dispatcher(object):
listeners += list(self._dispatcher_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
"""
@ -142,7 +142,7 @@ class Dispatcher(object):
self._dispatcher_connections.remove(conn)
for _evt, conns in self._dispatcher_subscriptions.items():
conns.discard(conn)
def activate_connection(self, conn):
self._dispatcher_active_connections.add(conn)
@ -188,6 +188,22 @@ class Dispatcher(object):
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
def list_module_params(self, modulename):
self.log.debug('list_module_params(%r)' % modulename)
if modulename in self._dispatcher_export:
@ -205,7 +221,7 @@ class Dispatcher(object):
def _execute_command(self, modulename, command, arguments=None):
if arguments is None:
arguments = []
moduleobj = self.get_module(modulename)
if moduleobj is None:
raise NoSuchmoduleError(module=modulename)
@ -273,8 +289,7 @@ class Dispatcher(object):
def handle_Describe(self, conn, msg):
# XXX:collect descriptive data
# XXX:how to get equipment_id?
return DescribeReply(equipment_id = self.equipment_id, description = self.list_modules())
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
@ -283,7 +298,7 @@ class Dispatcher(object):
if conn in self._dispatcher_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())
@ -301,7 +316,7 @@ class Dispatcher(object):
if conn in self._dispatcher_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())
@ -314,7 +329,7 @@ class Dispatcher(object):
#if conn in self._dispatcher_active_connections:
# return None # already send to myself
return res # send reply to inactive conns
def handle_Heartbeat(self, conn, msg):
return HeartbeatReply(**msg.as_dict())
@ -331,14 +346,14 @@ class Dispatcher(object):
except SECOPError as e:
self.log.error('decide what to do here!')
self.log.exception(e)
res = Value(module=modulename, parameter=pname,
value=pobj.value, t=pobj.timestamp,
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
self.broadcast_event(res)
conn.queue_async_reply(ActivateReply(**msg.as_dict()))
return None
def handle_Deactivate(self, conn, msg):
self.deactivate_connection(conn)
conn.queue_async_reply(DeactivateReply(**msg.as_dict()))
@ -353,7 +368,7 @@ class Dispatcher(object):
(no handle_<messagename> method was defined)
"""
self.log.error('IGN: got unhandled request %s' % msg)
return ErrorMessage(errorclass="InternalError",
return ErrorMessage(errorclass="InternalError",
errorstring = 'Got Unhandled Request %r' % msg)

View File

@ -25,6 +25,7 @@
# implement as class as they may need some internal 'state' later on
# (think compressors)
from secop.lib.parsing import format_time
from secop.protocol.encoding import MessageEncoder
from secop.protocol.messages import *
from secop.protocol.errors import ProtocollError
@ -42,7 +43,7 @@ DEMO_RE = re.compile(
#"""
# messagetypes:
IDENTREQUEST = '*IDN?' # literal
IDENTREPLY = 'Sine2020WP7.1&ISSE, SECoP, V2016-11-30, rc1' # literal
IDENTREPLY = 'SECoP, SECoPTCP, V2016-11-30, rc1' # literal! first part 'SECoP' is fixed!
DESCRIPTIONSREQUEST = 'describe' # literal
DESCRIPTIONREPLY = 'describing' # +<id> +json
ENABLEEVENTSREQUEST = 'activate' # literal
@ -66,6 +67,12 @@ ERRORCLASSES = ['NoSuchDevice', 'NoSuchParameter', 'NoSuchCommand',
'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()
if 't' in q:
q['t'] = format_time(q['t'])
return vobj.value, q
class DemoEncoder(MessageEncoder):
# map of msg to msgtype string as defined above.
ENCODEMAP = {
@ -88,7 +95,7 @@ class DemoEncoder(MessageEncoder):
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,
lambda msg: [msg.value, msg.qualifiers] if msg.qualifiers else [msg.value]),
encode_value_data,),
}
DECODEMAP = {
IDENTREQUEST : lambda spec, data: IdentifyRequest(),