From 8a63a6c63f80c248be1f756d6e3d5c03639fee8c Mon Sep 17 00:00:00 2001 From: Enrico Faulhaber Date: Mon, 10 Jul 2017 16:33:37 +0200 Subject: [PATCH] Switch descriptive data to new format Change-Id: Ic8afe976564824d14394ed6a1b4b36df226648df --- secop/client/baseclient.py | 63 ++++++++++++++++++++++++++++++------ secop/lib/__init__.py | 10 ++++-- secop/protocol/dispatcher.py | 22 +++++++++++++ 3 files changed, 83 insertions(+), 12 deletions(-) diff --git a/secop/client/baseclient.py b/secop/client/baseclient.py index 0150c7e..080a63a 100644 --- a/secop/client/baseclient.py +++ b/secop/client/baseclient.py @@ -27,6 +27,7 @@ import serial from select import select import threading import Queue +from collections import OrderedDict import mlzlog @@ -151,7 +152,17 @@ class Client(object): stopflag = False def __init__(self, opts, autoconnect=True): - self.log = mlzlog.log.getChild('client', True) + if 'testing' not in opts: + self.log = mlzlog.log.getChild('client', True) + else: + class logStub(object): + def info(self, *args): + pass + debug = info + error = info + warning = info + exception = info + self.log = logStub() self._cache = dict() if 'device' in opts: # serial port @@ -161,11 +172,15 @@ class Client(object): self.connection = serial.Serial( devport, baudrate=baudrate, timeout=1) self.connection.callbacks = [] - else: + elif 'testing' not in opts: host = opts.pop('connectto', 'localhost') port = int(opts.pop('port', 10767)) self.contactPoint = "tcp://%s:%d" % (host, port) self.connection = TCPConnection(host, port) + else: + self.contactPoint = 'testing' + self.connection = opts.pop('testing') + # maps an expected reply to a list containing a single Event() # upon rcv of that reply, entry is appended with False and # the data of the reply. @@ -305,15 +320,43 @@ class Client(object): def _getDescribingParameterData(self, module, parameter): return self._getDescribingModuleData(module)['parameters'][parameter] - def _issueDescribe(self): - _, self.equipment_id, self.describing_data = self._communicate( - 'describe') + def _decode_list_to_ordereddict(self, data): + # takes a list of 2*N , entries and + # return an orderedDict from it + result = OrderedDict() + while len(data) > 1: + key = data.pop(0) + value = data.pop(0) + result[key] = value + return result - for module, moduleData in self.describing_data['modules'].items(): - for parameter, parameterData in moduleData['parameters'].items(): - datatype = get_datatype(parameterData['datatype']) - self.describing_data['modules'][module]['parameters'] \ - [parameter]['datatype'] = datatype + def _decode_substruct(self, specialkeys=[], data={}): + # take a dict and move all keys which are not in specialkeys + # into a 'properties' subdict + # specialkeys entries are converted from list to ordereddict + result = {} + for k in specialkeys: + result[k] = self._decode_list_to_ordereddict(data.pop(k, [])) + result['properties'] = data + return result + + def _issueDescribe(self): + _, self.equipment_id, describing_data = self._communicate('describe') + try: + describing_data = self._decode_substruct(['modules'], describing_data) + for modname, module in describing_data['modules'].items(): + describing_data['modules'][modname] = self._decode_substruct(['parameters', 'commands'], module) + + self.describing_data = describing_data + + for module, moduleData in self.describing_data['modules'].items(): + for parameter, parameterData in moduleData['parameters'].items(): + datatype = get_datatype(parameterData['datatype']) + self.describing_data['modules'][module]['parameters'] \ + [parameter]['datatype'] = datatype + except Exception as exc: + print formatException(verbose=True) + raise def register_callback(self, module, parameter, cb): self.log.debug('registering callback %r for %s:%s' % diff --git a/secop/lib/__init__.py b/secop/lib/__init__.py index 49df8d9..f8d64f8 100644 --- a/secop/lib/__init__.py +++ b/secop/lib/__init__.py @@ -79,7 +79,11 @@ def formatExtendedFrame(frame): ret.append('\n') return ret -def formatExtendedTraceback(etype, value, tb): +def formatExtendedTraceback(exc_info=None): + if exc_info is None: + etype, value, tb = sys.exc_info() + else: + etype, value, tb = exc_info ret = ['Traceback (most recent call last):\n'] while tb is not None: frame = tb.tb_frame @@ -116,10 +120,12 @@ def formatExtendedStack(level=1): f = f.f_back return ''.join(ret).rstrip('\n') -def formatException(cut=0, exc_info=None): +def formatException(cut=0, exc_info=None, verbose=False): """Format an exception with traceback, but leave out the first `cut` number of frames. """ + if verbose: + return formatExtendedTraceback(exc_info) if exc_info is None: typ, val, tb = sys.exc_info() else: diff --git a/secop/protocol/dispatcher.py b/secop/protocol/dispatcher.py index bca9385..adb9c46 100644 --- a/secop/protocol/dispatcher.py +++ b/secop/protocol/dispatcher.py @@ -208,6 +208,28 @@ class Dispatcher(object): return {} def get_descriptive_data(self): + """returns a python object which upon serialisation results in the descriptive data""" + # XXX: be lazy and cache this? + # format: {[{[{[, specific entries first + result = {'modules': []} + for modulename in self._export: + module = self.get_module(modulename) + # some of these need rework ! + mod_desc = {'parameters':[], 'commands':[]} + for pname, param in self.list_module_params(modulename, only_static=True).items(): + mod_desc['parameters'].extend([pname, param]) + for cname, cmd in self.list_module_cmds(modulename).items(): + mod_desc['commands'].extend([cname, cmd]) + for propname, prop in module.PROPERTIES.items(): + mod_desc[propname] = prop + result['modules'].extend([modulename, mod_desc]) + result['equipment_id'] = self.equipment_id + result['firmware'] = 'The SECoP playground' + result['version'] = "2017.07" + # XXX: what else? + return result + + def get_descriptive_data_old(self): # XXX: be lazy and cache this? result = {'modules': {}} for modulename in self._export: