From 1774422dffaa630d6012583f3beddfd0267e5f17 Mon Sep 17 00:00:00 2001 From: Sven Sager Date: Thu, 22 May 2025 11:08:48 +0200 Subject: [PATCH] feat(io): Add dbus bus com.revolutionpi.ios1 Signed-off-by: Sven Sager --- src/revpi_middleware/ios1/__init__.py | 5 + src/revpi_middleware/ios1/bus_provider_io.py | 100 ++++++++++++ src/revpi_middleware/ios1/interface_ios.py | 159 +++++++++++++++++++ src/revpi_middleware/ios1/ios1_helper.py | 37 +++++ 4 files changed, 301 insertions(+) create mode 100644 src/revpi_middleware/ios1/__init__.py create mode 100644 src/revpi_middleware/ios1/bus_provider_io.py create mode 100644 src/revpi_middleware/ios1/interface_ios.py create mode 100644 src/revpi_middleware/ios1/ios1_helper.py diff --git a/src/revpi_middleware/ios1/__init__.py b/src/revpi_middleware/ios1/__init__.py new file mode 100644 index 0000000..7abf777 --- /dev/null +++ b/src/revpi_middleware/ios1/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# SPDX-FileCopyrightText: 2025 KUNBUS GmbH +# SPDX-License-Identifier: GPL-2.0-or-later +"""D-Bus ios version 1 of revpi_middleware.""" +from .ios1_helper import REVPI_DBUS_BASE_PATH, REVPI_DBUS_NAME diff --git a/src/revpi_middleware/ios1/bus_provider_io.py b/src/revpi_middleware/ios1/bus_provider_io.py new file mode 100644 index 0000000..27eb20d --- /dev/null +++ b/src/revpi_middleware/ios1/bus_provider_io.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +# SPDX-FileCopyrightText: 2025 KUNBUS GmbH +# SPDX-License-Identifier: GPL-2.0-or-later +"""D-Bus bus provider for revpi_middleware.""" +from logging import getLogger +from threading import Thread + +import revpimodio2 +from gi.repository import GLib +from pydbus import SessionBus, SystemBus +from revpimodio2 import Cycletools + +from . import REVPI_DBUS_NAME +from .interface_ios import ( + InterfaceIoManager, + InterfaceInpBool, + InterfaceOutBool, + InterfaceInpInt, + InterfaceOutInt, +) + +log = getLogger(__name__) + + +class BusProviderIo(Thread): + + def __init__( + self, + picontrol_device="/dev/piControl0", + config_rsc="/etc/revpi/config.rsc", + use_system_bus=True, + ): + log.debug("enter BusProviderIo.__init__") + super().__init__() + + self._bus = SystemBus() if use_system_bus else SessionBus() + self._dc_io_interfaces = {} + self._loop = GLib.MainLoop() + self._modio = revpimodio2.RevPiModIO( + procimg=picontrol_device, + configrsc=config_rsc, + shared_procimg=True, + ) + + self.picontrol_device = picontrol_device + self.config_rsc = config_rsc + + def run(self): + log.debug("enter BusProviderIo.run") + + self._dc_io_interfaces.clear() + + for io in self._modio.io: + interface = None + value_type = type(io.value) + if value_type is bool: + interface = ( + InterfaceInpBool(self._bus, io) + if io.type == revpimodio2.INP + else InterfaceOutBool(self._bus, io) + ) + elif value_type is int: + interface = ( + InterfaceInpInt(self._bus, io) + if io.type == revpimodio2.INP + else InterfaceOutInt(self._bus, io) + ) + + if interface is not None: + self._dc_io_interfaces[io.name] = interface + + lst_interfaces = [ + (f"io/{io_name}", self._dc_io_interfaces[io_name]) for io_name in self._dc_io_interfaces + ] + try: + self._bus.publish( + REVPI_DBUS_NAME, + InterfaceIoManager(self._modio, self._dc_io_interfaces), + *lst_interfaces, + ) + except Exception as e: + log.error(f"can not publish dbus {REVPI_DBUS_NAME}: {e}") + + try: + self._loop.run() + except Exception as e: + log.error(f"can not run dbus mainloop: {e}") + + self._modio.cleanup() + + log.debug("leave BusProviderIo.run") + + def stop(self): + log.debug("enter BusProviderIo.stop") + self._loop.quit() + log.debug("leave BusProviderIo.stop") + + @property + def running(self): + return self._loop.is_running() diff --git a/src/revpi_middleware/ios1/interface_ios.py b/src/revpi_middleware/ios1/interface_ios.py new file mode 100644 index 0000000..36a0943 --- /dev/null +++ b/src/revpi_middleware/ios1/interface_ios.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +# SPDX-FileCopyrightText: 2025 KUNBUS GmbH +# SPDX-License-Identifier: GPL-2.0-or-later +"""D-Bus interfaces for IOs.""" +from gi.overrides.GLib import Variant +from pydbus.generic import signal +from revpimodio2 import RevPiModIO, Cycletools + +from .ios1_helper import REVPI_DBUS_BASE_PATH, DbusInterfaceIo + + +class InterfaceIoManager: + """ + + + + + + + + + + + + + + + + + + + + + + + """ + + interface_name = "com.revolutionpi.ios1.IoManager" + IoChanged = signal() + + def __init__(self, modio: RevPiModIO, io_interfaces: dict[str, DbusInterfaceIo]): + self._dc_io_interfaces = io_interfaces + self.modio = modio + + self.lst_inp = [] + for dev in self.modio.device: + for io in dev.get_inputs(): + self.lst_inp.append(self._get_io_path(io.name)) + self.lst_out = [] + for dev in self.modio.device: + for io in dev.get_outputs(): + self.lst_out.append(self._get_io_path(io.name)) + + def _modio_cycle(self, ct: Cycletools) -> None: + for io_name in self._dc_io_interfaces: + interface = self._dc_io_interfaces[io_name] + if ct.changed(interface.io): + interface.emit_io_change() + self.IoChanged(interface.io.name, Variant("b", interface.io.value)) + + def _get_io_path(self, io_name: str) -> str: + return f"{REVPI_DBUS_BASE_PATH}/io/{io_name}" + + def GetAllInputs(self) -> list[str]: + return self.lst_inp + + def GetAllOutputs(self) -> list[str]: + return self.lst_out + + def Get(self, io_name) -> str: + if io_name in self.modio.io: + return self._get_io_path(io_name) + + raise KeyError(f"No IO with name '{io_name}' found.") + + def ActivateIoEvents(self) -> None: + if not self.modio._looprunning: + self.modio.autorefresh_all() + self.modio.cycleloop(self._modio_cycle, cycletime=50, blocking=False) + + def DeactivateIoEvents(self) -> None: + self.modio.exit(False) + + +class InterfaceInpBool(DbusInterfaceIo): + """ + + + + + + + + """ + + interface_name = "com.revolutionpi.ios1.InpBool" + + @property + def bmk(self) -> str: + return self.io.bmk + + @property + def name(self) -> str: + return self.io.name + + @property + def value(self) -> bool: + return self.io.value + + +class InterfaceOutBool(InterfaceInpBool): + """ + + + + + + + + """ + + interface_name = "com.revolutionpi.ios1.OutBool" + + @property + def value(self) -> bool: + return super().value + + @value.setter + def value(self, value: bool): + self.io.value = value + self.io._parentdevice._modio.writeprocimg() + + +class InterfaceInpInt(DbusInterfaceIo): + """ + + + + + + + + """ + + interface_name = "com.revolutionpi.ios1.InpInt" + + +class InterfaceOutInt(InterfaceInpInt): + """ + + + + + + + + """ + + interface_name = "com.revolutionpi.ios1.OutInt" diff --git a/src/revpi_middleware/ios1/ios1_helper.py b/src/revpi_middleware/ios1/ios1_helper.py new file mode 100644 index 0000000..82f956a --- /dev/null +++ b/src/revpi_middleware/ios1/ios1_helper.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# SPDX-FileCopyrightText: 2025 KUNBUS GmbH +# SPDX-License-Identifier: GPL-2.0-or-later +"""Helper for io read and write.""" +from logging import getLogger + +from gi.overrides.GLib import Variant +from pydbus import SessionBus, SystemBus +from pydbus.generic import signal +from revpimodio2.io import IOBase + +log = getLogger(__name__) + +REVPI_DBUS_NAME = "com.revolutionpi.ios1" +REVPI_DBUS_BASE_PATH = "/com/revolutionpi/ios1" + + +class DbusInterfaceIo: + interface_name = "" + PropertiesChanged = signal() + + def __init__(self, dbus: SystemBus or SessionBus, io: IOBase): + self.dbus = dbus + self.io = io + self.variant_type = "b" if type(self.io.value) is bool else "i" + + def emit_io_change(self): + if not self.interface_name: + return + + if self.interface_name: + print(type(self.io.value)) + self.PropertiesChanged( + self.interface_name, + {"value": int(self.io.value)}, + [], + )