diff --git a/frappy/protocol/dispatcher.py b/frappy/protocol/dispatcher.py index afec362..e95c43f 100644 --- a/frappy/protocol/dispatcher.py +++ b/frappy/protocol/dispatcher.py @@ -265,9 +265,9 @@ class Dispatcher: modulename, exportedname = specifier, None if ':' in specifier: modulename, exportedname = specifier.split(':', 1) - if modulename not in self.secnode.export: - raise NoSuchModuleError(f'Module {modulename!r} does not exist') moduleobj = self.secnode.get_module(modulename) + if moduleobj is None or not moduleobj.export: + raise NoSuchModuleError(f'Module {modulename!r} does not exist') if exportedname is not None: pname = moduleobj.accessiblename2attr.get(exportedname, True) if pname and pname not in moduleobj.accessibles: @@ -281,7 +281,7 @@ class Dispatcher: else: # activate all modules self._active_connections.add(conn) - modules = [(m, None) for m in self.secnode.export] + modules = [(m, None) for m in self.secnode.get_exported_modules()] # send updates for all subscribed values. # note: The initial poll already happend before the server is active diff --git a/frappy/secnode.py b/frappy/secnode.py index 73cb56b..73ff271 100644 --- a/frappy/secnode.py +++ b/frappy/secnode.py @@ -44,8 +44,6 @@ class SecNode: self.nodeprops = {} # map ALL modulename -> moduleobj self.modules = {} - # list of EXPORTED modules - self.export = [] self.log = logger self.srv = srv # set of modules that failed creation @@ -193,60 +191,62 @@ class SecNode: modname, len(pinata_modules)) todos.extend(pinata_modules) - def export_accessibles(self, modulename): - self.log.debug('export_accessibles(%r)', modulename) - if modulename in self.export: - # omit export=False params! - res = OrderedDict() - for aobj in self.get_module(modulename).accessibles.values(): - if aobj.export: - res[aobj.export] = aobj.for_export() - self.log.debug('list accessibles for module %s -> %r', - modulename, res) - return res - self.log.debug('-> module is not to be exported!') - return OrderedDict() + def export_accessibles(self, modobj): + self.log.debug('export_accessibles(%r)', modobj.name) + # omit export=False params! + res = OrderedDict() + for aobj in modobj.accessibles.values(): + if aobj.export: + res[aobj.export] = aobj.for_export() + self.log.debug('list accessibles for module %s -> %r', + modobj.name, res) + return res + + def build_descriptive_data(self): + modules = {} + result = {'modules': modules} + for modulename in self.modules: + modobj = self.get_module(modulename) + if not modobj.export: + continue + # some of these need rework ! + mod_desc = {'accessibles': self.export_accessibles(modobj)} + mod_desc.update(modobj.exportProperties()) + mod_desc.pop('export', None) + modules[modulename] = mod_desc + result['equipment_id'] = self.equipment_id + result['firmware'] = 'FRAPPY ' + get_version() + result['description'] = self.nodeprops['description'] + for prop, propvalue in self.nodeprops.items(): + if prop.startswith('_'): + result[prop] = propvalue + self.descriptive_data = result def get_descriptive_data(self, specifier): """returns a python object which upon serialisation results in the descriptive data""" specifier = specifier or '' - modules = {} - result = {'modules': modules} - for modulename in self.export: - module = self.get_module(modulename) - if not module.export: - continue - # some of these need rework ! - mod_desc = {'accessibles': self.export_accessibles(modulename)} - mod_desc.update(module.exportProperties()) - mod_desc.pop('export', False) - modules[modulename] = mod_desc modname, _, pname = specifier.partition(':') + modules = self.descriptive_data['modules'] if modname in modules: # extension to SECoP standard: description of a single module result = modules[modname] if pname in result['accessibles']: # extension to SECoP standard: description of a single accessible # command is also accepted - result = result['accessibles'][pname] - elif pname: + return result['accessibles'][pname] + if pname: raise NoSuchParameterError(f'Module {modname!r} ' f'has no parameter {pname!r}') - elif not modname or modname == '.': - result['equipment_id'] = self.equipment_id - result['firmware'] = 'FRAPPY ' + get_version() - result['description'] = self.nodeprops['description'] - for prop, propvalue in self.nodeprops.items(): - if prop.startswith('_'): - result[prop] = propvalue - else: - raise NoSuchModuleError(f'Module {modname!r} does not exist') - return result + return result + if not modname or modname == '.': + return self.descriptive_data + raise NoSuchModuleError(f'Module {modname!r} does not exist') + + def get_exported_modules(self): + return [m for m, o in self.modules.items() if o.export] def add_module(self, module, modulename): """Adds a named module object to this SecNode.""" self.modules[modulename] = module - if module.export: - self.export.append(modulename) # def remove_module(self, modulename_or_obj): # moduleobj = self.get_module(modulename_or_obj) diff --git a/frappy/server.py b/frappy/server.py index 71aba9c..15a13e0 100644 --- a/frappy/server.py +++ b/frappy/server.py @@ -289,7 +289,6 @@ class Server: If there are errors that occur, they will be collected and emitted together in the end. """ - errors = [] opts = dict(self.node_cfg) cls = get_class(opts.pop('cls')) self.secnode = SecNode(self.name, self.log.getChild('secnode'), opts, self) @@ -301,10 +300,9 @@ class Server: self.secnode.add_secnode_property(k, opts.pop(k)) self.secnode.create_modules() - # initialize all modules by getting them with Dispatcher.get_module, - # which is done in the get_descriptive data - # TODO: caching, to not make this extra work - self.secnode.get_descriptive_data('') + # initialize modules by calling self.secnode.get_module for all of them + # this is done in build_descriptive_data even for unexported modules + self.secnode.build_descriptive_data() # =========== All modules are initialized =========== # all errors from initialization process