7 Commits

Author SHA1 Message Date
Sven Sager
db9d325235 chore: Bump version to 0.0.6
Signed-off-by: Sven Sager <s.sager@kunbus.com>
2026-03-19 08:29:42 +01:00
Sven Sager
c3417c38a3 feat(systemd): Add revpi-iosbus.service files
Signed-off-by: Sven Sager <s.sager@kunbus.com>
2026-03-17 09:59:44 +01:00
Sven Sager
1415055483 feat: Enhance MiddlewareDaemon with driver reset handling
Refactored `MiddlewareDaemon` to integrate `ResetDriverWatchdog` for
monitoring `procimg` changes and trigger D-Bus restarts when necessary.
Removed debus signal `NotifyDriverReset` handling from
`BusProviderIos1`.

Signed-off-by: Sven Sager <s.sager@kunbus.com>
2026-03-17 09:02:40 +01:00
Sven Sager
fbfda04d7c feat: Add support for configurable procimg path
Introduced a new `--procimg` argument to specify the process image path.
Updated `MiddlewareDaemon` to dynamically reset D-Bus when `procimg`
changes and pass the configured path to bus providers.

Signed-off-by: Sven Sager <s.sager@kunbus.com>
2026-03-17 08:37:24 +01:00
Sven Sager
9e5a9e0141 chore: Add logging for D-Bus publishing and connection closure
Added informational log messages to track when `REVPI_DBUS_NAME` is
published and its connection is closed in both `BusProviderIos1` and
`BusProviderMiddleware1`.

Signed-off-by: Sven Sager <s.sager@kunbus.com>
2026-03-13 09:07:46 +01:00
Sven Sager
f6f83e236e feat: Introduce configurable bus_provider for MiddlewareDaemon
Added support for a configurable `bus_provider` via command-line
arguments, enabling either "middleware" or "ios" as valid options.
Refactored `MiddlewareDaemon` to dynamically handle the selected bus
provider and streamline D-Bus management.

Signed-off-by: Sven Sager <s.sager@kunbus.com>
2026-03-13 09:03:59 +01:00
Sven Sager
8db3af9ca1 feat: Add name property to BusProvider classes
Introduced a `name` property returning `REVPI_DBUS_NAME` in classes
`BusProviderMiddleware1` and `BusProviderIos1`.

Signed-off-by: Sven Sager <s.sager@kunbus.com>
2026-03-13 08:55:24 +01:00
9 changed files with 131 additions and 54 deletions

View File

@@ -0,0 +1,8 @@
# Additional options that are passed to revpi-ios.
# add '-f /var/log/revpi-ios.log' to write logs to own log file
# add '-v' or '-vv' for verbose logging
DAEMON_OPTS=""
# In addition to journalctl, use your own additional log file
# DAEMON_OPTS="-f /var/log/revpi-ios.log"

View File

@@ -0,0 +1,14 @@
/var/log/revpi-ios.log
{
rotate 6
weekly
maxsize 1M
compress
delaycompress
missingok
notifempty
sharedscripts
postrotate
systemctl kill --signal=SIGUSR1 revpi-ios > /dev/null 2>&1 || true
endscript
}

View File

@@ -0,0 +1,12 @@
[Unit]
Description=D-Bus interface for Inputs/Outputs of Revolution Pi
[Service]
EnvironmentFile=-/etc/default/revpi-ios
Type=notify
NotifyAccess=all
ExecStart=/usr/sbin/revpi-middleware ios $DAEMON_OPTS
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,11 @@
[Unit]
Description=D-Bus interface for Inputs/Outputs of Revolution Pi
[Service]
EnvironmentFile=-/etc/default/revpi-ios
Type=notify-reload
NotifyAccess=all
ExecStart=/usr/sbin/revpi-middleware ios $DAEMON_OPTS
[Install]
WantedBy=multi-user.target

View File

@@ -6,4 +6,4 @@
__author__ = "Sven Sager"
__copyright__ = "Copyright (C) 2025 KUNBUS GmbH"
__license__ = " GPL-2.0-or-later"
__version__ = "0.0.1"
__version__ = "0.0.6"

View File

@@ -3,35 +3,43 @@
# SPDX-License-Identifier: GPL-2.0-or-later
"""Main daemon of revpi-middleware."""
from enum import Enum
from logging import getLogger
from os import getuid, environ
from os import environ
from threading import Event
from time import perf_counter
from typing import List
from gi.repository import Gio
from . import proginit as pi
from .dbus_ios1 import BusProviderIos1
from .dbus_middleware1 import BusProviderMiddleware1
from .dbus_middleware1.process_image.process_image_helper import ResetDriverWatchdog
log = getLogger(__name__)
class BusProvider(Enum):
middleware = "middleware"
ios = "ios"
class MiddlewareDaemon:
"""Main program of MiddlewareDaemon class."""
def __init__(self):
def __init__(self, bus_provider: BusProvider):
"""Init MiddlewareDaemon class."""
log.debug("enter MiddlewareDaemon.__init__")
self._cycle_time = 1.0
self.do_cycle = Event()
self._force_dbus_restart = False
self._reconfigure = False
self._running = True
self.bp_middleware1 = None
self.bp_ios1 = None
self.bus_provider = None
self.bus_provider_selected = bus_provider
self.wd_reset = ResetDriverWatchdog("")
self._configure()
log.debug("leave MiddlewareDaemon.__init__")
@@ -41,6 +49,17 @@ class MiddlewareDaemon:
log.debug("enter MiddlewareDaemon._configure")
pi.reload_conf()
if self._force_dbus_restart:
self.dbus_stop()
self._force_dbus_restart = False
if pi.pargs.procimg != self.wd_reset.procimg:
self.dbus_stop()
self.wd_reset.stop()
self.wd_reset = ResetDriverWatchdog(pi.pargs.procimg)
self.wd_reset.register_call(self._reset_driver_callack)
log.debug("leave MiddlewareDaemon._configure")
@staticmethod
@@ -54,44 +73,44 @@ class MiddlewareDaemon:
return environ.get(environ_name, bus_address)
def _reset_driver_callack(self, *args) -> None:
log.debug("enter MiddlewareDaemon._reset_driver_callack")
if self.bus_provider_selected is BusProvider.ios:
self._force_dbus_restart = True
self.reload_config()
log.debug("leave MiddlewareDaemon._reset_driver_callack")
def dbus_start(self):
log.debug("enter MiddlewareDaemon.dbus_start")
dbus_middleware1_running = self.bp_middleware1 and self.bp_middleware1.is_alive()
if not dbus_middleware1_running:
self.bp_middleware1 = BusProviderMiddleware1(self._get_bus_address())
self.bp_middleware1.start()
dbus_ios1_running = self.bp_ios1 and self.bp_ios1.is_alive()
if not dbus_ios1_running:
if self.bp_middleware1.published.wait(timeout=10.0):
self.bp_ios1 = BusProviderIos1(self._get_bus_address())
self.bp_ios1.start()
dbus_running = self.bus_provider and self.bus_provider.is_alive()
if not dbus_running:
if self.bus_provider_selected is BusProvider.middleware:
self.bus_provider = BusProviderMiddleware1(
dbus_address=self._get_bus_address(),
picontrol_device=pi.pargs.procimg,
)
elif self.bus_provider_selected is BusProvider.ios:
self.bus_provider = BusProviderIos1(
dbus_address=self._get_bus_address(),
picontrol_device=pi.pargs.procimg,
)
else:
log.error("dbus middleware1 provider thread is not alive - can not start ios1 bus")
raise ValueError("Unknown bus provider")
self.bus_provider.start()
log.debug("leave MiddlewareDaemon.dbus_start")
def dbus_stop(self, bus_filter: List[object] = None):
def dbus_stop(self):
log.debug("enter MiddlewareDaemon.dbus_stop")
if bus_filter is None:
bus_filter = [
self.bp_middleware1,
self.bp_ios1,
]
if self.bp_middleware1 and self.bp_middleware1 in bus_filter:
self.bp_middleware1.stop()
self.bp_middleware1.join(timeout=10.0)
if self.bp_middleware1.is_alive():
log.warning("dbus middleware1 provider thread is still alive")
if self.bp_ios1 and self.bp_ios1 in bus_filter:
self.bp_ios1.stop()
self.bp_ios1.join(timeout=10.0)
if self.bp_ios1.is_alive():
log.warning("dbus ios1 provider thread is still alive")
if self.bus_provider:
self.bus_provider.stop()
self.bus_provider.join(timeout=10.0)
if self.bus_provider.is_alive():
log.warning(f"dbus {self.bus_provider.name} thread is still alive")
log.debug("leave MiddlewareDaemon.dbus_stop")
@@ -122,7 +141,6 @@ class MiddlewareDaemon:
# Startup tasks
self.dbus_start()
pi.startup_complete()
# Go into mainloop of daemon
while self._running:
ot = perf_counter()
@@ -132,19 +150,15 @@ class MiddlewareDaemon:
if self._reconfigure:
self._configure()
self._reconfigure = False
pi.startup_complete()
# Monitor bus providers for errors and restart them
restart = False
if not (self.bp_middleware1 and self.bp_middleware1.is_alive()):
log.warning("dbus middleware1 provider thread is not alive - restarting")
restart = True
if not (self.bp_ios1 and self.bp_ios1.is_alive()):
log.warning("dbus ios1 provider thread is not alive - restarting")
restart = True
if restart:
if not (self.bus_provider and self.bus_provider.is_alive()):
log.warning(f"dbus {self.bus_provider.name} thread is not alive - restarting")
self.dbus_start()
if self.bus_provider and self.bus_provider.published.is_set():
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.

View File

@@ -92,16 +92,10 @@ class BusProviderIos1(Thread):
*lst_interfaces,
)
self.published.set()
log.info(f"published {REVPI_DBUS_NAME} on {self._bus_address}")
except Exception as e:
log.error(f"can not publish dbus {REVPI_DBUS_NAME}: {e}")
# Subscribe to NotifyDriverReset on middleware1 dbus
iface_pi_control = bus.get(
"com.revolutionpi.middleware1",
"/com/revolutionpi/middleware1",
)["com.revolutionpi.middleware1.PiControl"]
iface_pi_control.onNotifyDriverReset = self.stop
try:
self._loop.run()
except Exception as e:
@@ -109,6 +103,7 @@ class BusProviderIos1(Thread):
bus.con.close()
self._modio.cleanup()
log.info(f"closed {REVPI_DBUS_NAME} connection to {self._bus_address}")
log.debug("leave BusProviderIos1.run")
@@ -117,6 +112,10 @@ class BusProviderIos1(Thread):
self._loop.quit()
log.debug("leave BusProviderIos1.stop")
@property
def name(self) -> str:
return REVPI_DBUS_NAME
@property
def running(self):
return self._loop.is_running()

View File

@@ -55,6 +55,7 @@ class BusProviderMiddleware1(Thread):
*lst_interfaces,
)
self.published.set()
log.info(f"published {REVPI_DBUS_NAME} on {self._bus_address}")
except Exception as e:
log.error(f"can not publish dbus {REVPI_DBUS_NAME}: {e}")
@@ -64,6 +65,7 @@ class BusProviderMiddleware1(Thread):
log.error(f"can not run dbus mainloop: {e}")
bus.con.close()
log.info(f"closed {REVPI_DBUS_NAME} connection to {self._bus_address}")
# Clean up all interfaces
for interface in lst_interfaces:
@@ -78,6 +80,10 @@ class BusProviderMiddleware1(Thread):
self._loop.quit()
log.debug("leave BusProviderMiddleware1.stop")
@property
def name(self) -> str:
return REVPI_DBUS_NAME
@property
def running(self):
return self._loop.is_running()

View File

@@ -5,7 +5,7 @@
from logging import getLogger
from .daemon import MiddlewareDaemon
from .daemon import MiddlewareDaemon, BusProvider
from . import proginit as pi
log = getLogger(__name__)
@@ -27,6 +27,19 @@ pi.parser.add_argument(
default="/etc/{0}/{0}.conf".format(pi.programname),
help="application configuration file",
)
pi.parser.add_argument(
"--procimg",
dest="procimg",
default="/dev/piControl0",
help="Path to process image",
)
pi.parser.add_argument(
"bus_provider",
default="middleware",
nargs="?",
choices=["middleware", "ios"],
help="bus provider to use",
)
def main() -> int:
@@ -35,7 +48,7 @@ def main() -> int:
# Parse command line arguments
pi.init_app()
root = MiddlewareDaemon()
root = MiddlewareDaemon(BusProvider(pi.pargs.bus_provider))
# Set signals
signal.signal(signal.SIGHUP, lambda n, f: root.reload_config())