feat: Add MiddlewareDaemon implementation to revpi-middleware
Introduce the main daemon class for revpi-middleware with methods for initialization, configuration, DBus handling, and a mainloop. This implementation includes support for configuration reloads, log rotation, and proper thread management for DBus operations.
This commit is contained in:
121
src/revpi_middleware/daemon.py
Normal file
121
src/revpi_middleware/daemon.py
Normal file
@@ -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")
|
||||||
Reference in New Issue
Block a user