improve error messages when attached modules fail on startup

Change-Id: Ic1d2d77de2574043749ddbc00def48a6fe77b2bd
This commit is contained in:
2025-10-27 13:04:21 +01:00
parent ea5fc16a51
commit 7adb4d6f04
2 changed files with 41 additions and 26 deletions

View File

@@ -342,6 +342,7 @@ class Module(HasAccessibles):
self.attachedModules = {} self.attachedModules = {}
self.errors = [] self.errors = []
self._isinitialized = False self._isinitialized = False
self._initfailed = False
self.updateCallback = srv.dispatcher.announce_update self.updateCallback = srv.dispatcher.announce_update
# handle module properties # handle module properties
@@ -612,6 +613,7 @@ class Module(HasAccessibles):
""" """
# we do not need self.errors any longer. should we delete it? # we do not need self.errors any longer. should we delete it?
# del self.errors # del self.errors
self.polledModules = [m for m in self.polledModules if not m._initfailed]
if self.polledModules: if self.polledModules:
self.__poller = mkthread(self.__pollThread, self.polledModules, start_events.get_trigger()) self.__poller = mkthread(self.__pollThread, self.polledModules, start_events.get_trigger())
self.startModuleDone = True self.startModuleDone = True
@@ -723,28 +725,27 @@ class Module(HasAccessibles):
rfunc = getattr(mobj, 'read_' + pname) rfunc = getattr(mobj, 'read_' + pname)
if rfunc.poll: if rfunc.poll:
pinfo.polled_parameters.append((mobj, rfunc, pobj)) pinfo.polled_parameters.append((mobj, rfunc, pobj))
while True: try:
try: for mobj in list(self.modules):
for mobj in modules: if mobj._initfailed:
# TODO when needed: here we might add a call to a method :meth:`beforeWriteInit` modules.remove(mobj)
mobj.writeInitParams() for mobj in modules:
mobj.initialReads() # TODO when needed: here we might add a call to a method :meth:`beforeWriteInit`
# call all read functions a first time mobj.writeInitParams()
for m in polled_modules: mobj.initialReads()
for mobj, rfunc, _ in m.pollInfo.polled_parameters: # call all read functions a first time
mobj.callPollFunc(rfunc, raise_com_failed=True) for m in polled_modules:
# TODO when needed: here we might add calls to a method :meth:`afterInitPolls` for mobj, rfunc, _ in m.pollInfo.polled_parameters:
break mobj.callPollFunc(rfunc, raise_com_failed=True)
except CommunicationFailedError as e: # TODO when needed: here we might add calls to a method :meth:`afterInitPolls`
# when communication failed, probably all parameters and may be more modules are affected. except CommunicationFailedError as e:
# as this would take a lot of time (summed up timeouts), we do not continue # when communication failed, probably all parameters and may be more modules are affected.
# trying and let the server accept connections, further polls might success later # as this would take a lot of time (summed up timeouts), we do not continue
if started_callback: # trying and let the server accept connections, further polls might success later
self.log.error('communication failure on startup: %s', e) if started_callback:
started_callback() self.log.error('communication failure on startup: %s', e)
started_callback = None started_callback()
self.triggerPoll.wait(0.1) # wait for reconnection or max 10 sec. started_callback = None
break
if started_callback: if started_callback:
started_callback() started_callback()
if not polled_modules: # no polls needed - exit thread if not polled_modules: # no polls needed - exit thread

View File

@@ -30,6 +30,10 @@ from frappy.version import get_version
from frappy.modules import Module from frappy.modules import Module
class InitFailed(Exception):
pass
class SecNode: class SecNode:
"""Managing the modules. """Managing the modules.
@@ -85,11 +89,15 @@ class SecNode:
if not modobj.initModuleDone: if not modobj.initModuleDone:
self.errors.append(f'{modobj.initModule.__qualname__} was not ' self.errors.append(f'{modobj.initModule.__qualname__} was not '
f'called, probably missing super call') f'called, probably missing super call')
except InitFailed:
raise
except Exception as e: except Exception as e:
if self.traceback_counter == 0: if self.traceback_counter == 0:
self.log.exception(traceback.format_exc()) self.log.exception(traceback.format_exc())
self.traceback_counter += 1 self.traceback_counter += 1
self.errors.append(f'error initializing {modulename}: {e!r}') self.errors.append(f'error initializing {modulename}: {e!r}')
modobj._initfailed = True
raise InitFailed('try to access erroneous module')
modobj._isinitialized = True modobj._isinitialized = True
self.log.debug('initialized module %r', modulename) self.log.debug('initialized module %r', modulename)
return modobj return modobj
@@ -101,8 +109,11 @@ class SecNode:
When creating a new module, srv.module_config is accessed to get the When creating a new module, srv.module_config is accessed to get the
modules configuration. modules configuration.
""" """
if modulename in self.modules: modobj = self.modules.get(modulename)
return self.modules[modulename] if modobj:
if modobj._initfailed:
raise InitFailed('try to access erroneous module')
return modobj
if modulename in list(self.modules.values()): if modulename in list(self.modules.values()):
# it's actually already the module object # it's actually already the module object
return modulename return modulename
@@ -205,8 +216,11 @@ class SecNode:
def build_descriptive_data(self): def build_descriptive_data(self):
modules = {} modules = {}
result = {'modules': modules} result = {'modules': modules}
for modulename in self.modules: for modulename in list(self.modules):
modobj = self.get_module(modulename) try:
modobj = self.get_module(modulename)
except InitFailed:
continue
if not modobj.export: if not modobj.export:
continue continue
# some of these need rework ! # some of these need rework !