diff --git a/src/revpi_middleware/daemon.py b/src/revpi_middleware/daemon.py new file mode 100644 index 0000000..41ca9a5 --- /dev/null +++ b/src/revpi_middleware/daemon.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +# SPDX-FileCopyrightText: 2025 KUNBUS GmbH +# SPDX-License-Identifier: GPL-2.0-or-later +"""Main daemon of revpi-middleware.""" + +from logging import getLogger +from threading import Event +from time import perf_counter + +from . import proginit as pi +from .dbus_middleware1.bus_provider import BusProvider + +log = getLogger(__name__) + + +class MiddlewareDaemon: + """Main program of MiddlewareDaemon class.""" + + def __init__(self): + """Init MiddlewareDaemon class.""" + log.debug("enter MiddlewareDaemon.__init__") + + self._cycle_time = 1.0 + self.do_cycle = Event() + self._reconfigure = False + self._running = True + + self.bus_provider = None + + self._configure() + log.debug("leave MiddlewareDaemon.__init__") + + def _configure(self) -> None: + """Load the configuration file and set values.""" + log.debug("enter MiddlewareDaemon._configure") + pi.reload_conf() + + log.debug("leave MiddlewareDaemon._configure") + + def dbus_start(self): + log.debug("enter MiddlewareDaemon.dbus_start") + if self.bus_provider and self.bus_provider.is_alive(): + return + + self.bus_provider = BusProvider() + self.bus_provider.start() + + log.debug("leave MiddlewareDaemon.dbus_start") + + def dbus_stop(self): + log.debug("enter MiddlewareDaemon.dbus_stop") + + if self.bus_provider: + self.bus_provider.stop() + self.bus_provider.join(timeout=10.0) + if self.bus_provider.is_alive(): + log.warning("dbus provider thread is still alive") + + log.debug("leave MiddlewareDaemon.dbus_stop") + + def reload_config(self) -> None: + """Reload configuration file.""" + log.debug("enter MiddlewareDaemon.reload_config") + + self._reconfigure = True + self.do_cycle.set() + + log.debug("leave MiddlewareDaemon.reload_config") + + @staticmethod + def rotate_logfile() -> None: + """Start a new logfile.""" + log.debug("enter MiddlewareDaemon.rotate_logfile") + + pi.reconfigure_logger() + log.warning("start new logfile") + + log.debug("leave MiddlewareDaemon.rotate_logfile") + + def start(self) -> int: + """Blocking mainloop of program.""" + log.debug("enter MiddlewareDaemon.start") + error_code = 0 + + # Startup tasks + self.dbus_start() + + pi.startup_complete() + # Go into mainloop of daemon + while self._running: + ot = perf_counter() + self.do_cycle.clear() + + # Early tasks of the cycle loop + if self._reconfigure: + self._configure() + self._reconfigure = False + pi.startup_complete() + + # Cycle time calculation + dm = divmod(ot - perf_counter(), self._cycle_time) + # For float the result is (q, a % b), where q is usually math.floor(a / b) but may be 1 less than that. + if dm[0] == -1.0: # Cycle time isn't exceeded + self.do_cycle.wait(dm[1]) + else: + log.debug("Cycle time exceeded about {0:.0f} times".format(abs(dm[0]))) + + # Cleanup tasks + self.dbus_stop() + + log.debug("leave MiddlewareDaemon.start") + return error_code + + def stop(self) -> None: + """Set stop request for mainloop.""" + log.debug("enter MiddlewareDaemon.stop") + + self._running = False + self.do_cycle.set() + + log.debug("leave MiddlewareDaemon.stop")