From 94ac5c9b92fc407e2c01146b70dffbdc389b9419 Mon Sep 17 00:00:00 2001 From: Sven Sager Date: Mon, 28 Oct 2024 15:05:03 +0100 Subject: [PATCH] feat: Add support for RevPi Connect 5 --- src/revpimodio2/device.py | 321 ++++++++++++++++++++----------------- src/revpimodio2/modio.py | 4 + src/revpimodio2/pictory.py | 1 + 3 files changed, 181 insertions(+), 145 deletions(-) diff --git a/src/revpimodio2/device.py b/src/revpimodio2/device.py index b2f92d4..92ce3b6 100644 --- a/src/revpimodio2/device.py +++ b/src/revpimodio2/device.py @@ -1062,11 +1062,8 @@ class Connect(Core): wdautotoggle = property(_get_wdtoggle, _set_wdtoggle) -class Connect4(ModularBase): - """Klasse fuer den RevPi Connect 4. - - Stellt Funktionen fuer die LEDs und den Status zur Verfuegung. - """ +class ModularBaseConnect_4_5(ModularBase): + """Class for overlapping functions of Connect 4/5.""" __slots__ = ( "_slc_output", @@ -1085,8 +1082,6 @@ class Connect4(ModularBase): "a5red", "a5green", "a5blue", - "x2in", - "x2out", ) def __setattr__(self, key, value): @@ -1107,17 +1102,15 @@ class Connect4(ModularBase): "a5red", "a5green", "a5blue", - "x2in", - "x2out", ): raise AttributeError("direct assignment is not supported - use .value Attribute") - super(Connect4, self).__setattr__(key, value) + super().__setattr__(key, value) def __led_calculator(self, led_value: int) -> int: """ - Calculate the LED value of Connect 4. + Calculate the LED value of Connect 4/5. - Only the Connect 4 has swapped LED colors red and green. We have to recalculate that + Only the Connect 4/5 have swapped LED colors red and green. We have to recalculate that values to match our values for GREEN, RED and BLUE. """ led_calculated = led_value & 0b001 @@ -1127,7 +1120,7 @@ class Connect4(ModularBase): return led_calculated def _devconfigure(self) -> None: - """Connect4-Klasse vorbereiten.""" + """Connect 4/5-Klasse vorbereiten.""" super()._devconfigure() self._slc_statusbyte = slice(0, 1) @@ -1178,18 +1171,6 @@ class Connect4(ModularBase): exp_a5green = exp_a1red exp_a5blue = exp_a1red - if len(lst_output) == 8: - # prepared for future extension with wdtoggle - exp_x2out = lst_output[0].export - else: - exp_x2out = lst_output[0].export - - lst_status = lst_myios[self._slc_statusbyte.start] - if len(lst_status) == 8: - exp_x2in = lst_status[6].export - else: - exp_x2in = lst_status[0].export - # Echte IOs erzeugen self.a1red = IOBase( self, @@ -1301,7 +1282,176 @@ class Connect4(ModularBase): False, ) - # IO Objekte für WD und X2 in/out erzeugen + def _get_leda1(self) -> int: + """ + Gibt den Zustand der LED A1 vom Connect zurueck. + + :return: 0=aus, 1=gruen, 2=root, 4=blau, mixed RGB colors + """ + return self.__led_calculator(self._ba_devdata[self._slc_led.start] & 0b00000111) + + def _get_leda2(self) -> int: + """ + Gibt den Zustand der LED A2 vom Core zurueck. + + :return: 0=aus, 1=gruen, 2=root, 4=blau, mixed RGB colors + """ + return self.__led_calculator((self._ba_devdata[self._slc_led.start] & 0b00111000) >> 3) + + def _get_leda3(self) -> int: + """ + Gibt den Zustand der LED A3 vom Core zurueck. + + :return: 0=aus, 1=gruen, 2=root, 4=blau, mixed RGB colors + """ + word_led = self._ba_devdata[self._slc_led] + return self.__led_calculator((unpack("> 6) + + def _get_leda4(self) -> int: + """ + Gibt den Zustand der LED A4 vom Core zurueck. + + :return: 0=aus, 1=gruen, 2=root, 4=blau, mixed RGB colors + """ + return self.__led_calculator((self._ba_devdata[self._slc_led.start + 1] & 0b00001110) >> 1) + + def _get_leda5(self) -> int: + """ + Gibt den Zustand der LED A5 vom Core zurueck. + + :return: 0=aus, 1=gruen, 2=root, 4=blau, mixed RGB colors + """ + return self.__led_calculator((self._ba_devdata[self._slc_led.start + 1] & 0b01110000) >> 4) + + def _set_leda1(self, value: int) -> None: + """ + Setzt den Zustand der LED A1 vom Connect. + + :param: value 0=aus, 1=gruen, 2=rot, 4=blue, mixed RGB colors + """ + if 0 <= value <= 7: + self.a1red(bool(value & 2)) + self.a1green(bool(value & 1)) + self.a1blue(bool(value & 4)) + else: + raise ValueError("led status must be between 0 and 7") + + def _set_leda2(self, value: int) -> None: + """ + Setzt den Zustand der LED A2 vom Connect. + + :param: value 0=aus, 1=gruen, 2=rot, 4=blue, mixed RGB colors + """ + if 0 <= value <= 7: + self.a2red(bool(value & 2)) + self.a2green(bool(value & 1)) + self.a2blue(bool(value & 4)) + else: + raise ValueError("led status must be between 0 and 7") + + def _set_leda3(self, value: int) -> None: + """ + Setzt den Zustand der LED A3 vom Connect. + + :param: value 0=aus, 1=gruen, 2=rot, 4=blue, mixed RGB colors + """ + if 0 <= value <= 7: + self.a3red(bool(value & 2)) + self.a3green(bool(value & 1)) + self.a3blue(bool(value & 4)) + else: + raise ValueError("led status must be between 0 and 7") + + def _set_leda4(self, value: int) -> None: + """ + Setzt den Zustand der LED A4 vom Connect. + + :param: value 0=aus, 1=gruen, 2=rot, 4=blue, mixed RGB colors + """ + if 0 <= value <= 7: + self.a4red(bool(value & 2)) + self.a4green(bool(value & 1)) + self.a4blue(bool(value & 4)) + else: + raise ValueError("led status must be between 0 and 7") + + def _set_leda5(self, value: int) -> None: + """ + Setzt den Zustand der LED A5 vom Connect. + + :param: value 0=aus, 1=gruen, 2=rot, 4=blue, mixed RGB colors + """ + if 0 <= value <= 7: + self.a5red(bool(value & 2)) + self.a5green(bool(value & 1)) + self.a5blue(bool(value & 4)) + else: + raise ValueError("led status must be between 0 and 7") + + def wd_toggle(self): + """Toggle watchdog bit to prevent a timeout.""" + raise NotImplementedError( + "On the Connect 4/5, the hardware watchdog was removed from the process image by " + "KUNBUS. This function is no longer available on Connect 4/5 devices." + ) + + A1 = property(_get_leda1, _set_leda1) + A2 = property(_get_leda2, _set_leda2) + A3 = property(_get_leda3, _set_leda3) + A4 = property(_get_leda4, _set_leda4) + A5 = property(_get_leda5, _set_leda5) + + +class Connect5(ModularBaseConnect_4_5, GatewayMixin): + """Klasse fuer den RevPi Connect 5. + + Stellt Funktionen fuer die LEDs und den Status zur Verfuegung. + """ + + pass + + +class Connect4(ModularBaseConnect_4_5): + """Klasse fuer den RevPi Connect 4. + + Stellt Funktionen fuer die LEDs und den Status zur Verfuegung. + """ + + __slots__ = ( + "x2in", + "x2out", + ) + + def __setattr__(self, key, value): + """Verhindert Ueberschreibung der speziellen IOs.""" + if hasattr(self, key) and key in ( + "x2in", + "x2out", + ): + raise AttributeError("direct assignment is not supported - use .value Attribute") + super().__setattr__(key, value) + + def _devconfigure(self) -> None: + """Connect4-Klasse vorbereiten.""" + super()._devconfigure() + + # Exportflags prüfen (Byte oder Bit) + lst_myios = self._modio.io[self._slc_devoff] + lst_output = lst_myios[self._slc_output.start] + + if len(lst_output) == 8: + # prepared for future extension with wdtoggle + exp_x2out = lst_output[0].export + else: + exp_x2out = lst_output[0].export + + lst_status = lst_myios[self._slc_statusbyte.start] + if len(lst_status) == 8: + exp_x2in = lst_status[6].export + else: + exp_x2in = lst_status[0].export + + # IO Objekte für X2 in/out erzeugen self.x2in = IOBase( self, ["core.x2in", 0, 1, self._slc_statusbyte.start, exp_x2in, None, "Connect_X2_IN", "6"], @@ -1317,125 +1467,6 @@ class Connect4(ModularBase): False, ) - def _get_leda1(self) -> int: - """ - Gibt den Zustand der LED A1 vom Connect zurueck. - - :return: 0=aus, 1=gruen, 2=root, 4=blau - """ - return self.__led_calculator(self._ba_devdata[self._slc_led.start] & 0b00000111) - - def _get_leda2(self) -> int: - """ - Gibt den Zustand der LED A2 vom Core zurueck. - - :return: 0=aus, 1=gruen, 2=root, 4=blau - """ - return self.__led_calculator((self._ba_devdata[self._slc_led.start] & 0b00111000) >> 3) - - def _get_leda3(self) -> int: - """ - Gibt den Zustand der LED A3 vom Core zurueck. - - :return: 0=aus, 1=gruen, 2=root, 4=blau - """ - word_led = self._ba_devdata[self._slc_led] - return self.__led_calculator((unpack("> 6) - - def _get_leda4(self) -> int: - """ - Gibt den Zustand der LED A4 vom Core zurueck. - - :return: 0=aus, 1=gruen, 2=root, 4=blau - """ - return self.__led_calculator((self._ba_devdata[self._slc_led.start + 1] & 0b00001110) >> 1) - - def _get_leda5(self) -> int: - """ - Gibt den Zustand der LED A5 vom Core zurueck. - - :return: 0=aus, 1=gruen, 2=root, 4=blau - """ - return self.__led_calculator((self._ba_devdata[self._slc_led.start + 1] & 0b01110000) >> 4) - - def _set_leda1(self, value: int) -> None: - """ - Setzt den Zustand der LED A1 vom Connect. - - :param: value 0=aus, 1=gruen, 2=rot, 4=blue - """ - if 0 <= value <= 7: - self.a1red(bool(value & 2)) - self.a1green(bool(value & 1)) - self.a1blue(bool(value & 4)) - else: - raise ValueError("led status must be between 0 and 7") - - def _set_leda2(self, value: int) -> None: - """ - Setzt den Zustand der LED A2 vom Connect. - - :param: value 0=aus, 1=gruen, 2=rot, 4=blue - """ - if 0 <= value <= 7: - self.a2red(bool(value & 2)) - self.a2green(bool(value & 1)) - self.a2blue(bool(value & 4)) - else: - raise ValueError("led status must be between 0 and 7") - - def _set_leda3(self, value: int) -> None: - """ - Setzt den Zustand der LED A3 vom Connect. - - :param: value 0=aus, 1=gruen, 2=rot, 4=blue - """ - if 0 <= value <= 7: - self.a3red(bool(value & 2)) - self.a3green(bool(value & 1)) - self.a3blue(bool(value & 4)) - else: - raise ValueError("led status must be between 0 and 7") - - def _set_leda4(self, value: int) -> None: - """ - Setzt den Zustand der LED A4 vom Connect. - - :param: value 0=aus, 1=gruen, 2=rot, 4=blue - """ - if 0 <= value <= 7: - self.a4red(bool(value & 2)) - self.a4green(bool(value & 1)) - self.a4blue(bool(value & 4)) - else: - raise ValueError("led status must be between 0 and 7") - - def _set_leda5(self, value: int) -> None: - """ - Setzt den Zustand der LED A5 vom Connect. - - :param: value 0=aus, 1=gruen, 2=rot, 4=blue - """ - if 0 <= value <= 7: - self.a5red(bool(value & 2)) - self.a5green(bool(value & 1)) - self.a5blue(bool(value & 4)) - else: - raise ValueError("led status must be between 0 and 7") - - def wd_toggle(self): - """Toggle watchdog bit to prevent a timeout.""" - raise NotImplementedError( - "On the Connect 4, the hardware watchdog was removed from the process image by " - "KUNBUS. This function is no longer available on Connect 4 devices." - ) - - A1 = property(_get_leda1, _set_leda1) - A2 = property(_get_leda2, _set_leda2) - A3 = property(_get_leda3, _set_leda3) - A4 = property(_get_leda4, _set_leda4) - A5 = property(_get_leda5, _set_leda5) - class Compact(Base): """ diff --git a/src/revpimodio2/modio.py b/src/revpimodio2/modio.py index d7c73b1..b6828de 100644 --- a/src/revpimodio2/modio.py +++ b/src/revpimodio2/modio.py @@ -368,6 +368,10 @@ class RevPiModIO(object): # RevPi Connect 4 dev_new = devicemodule.Connect4(self, device, simulator=self._simulator) self.core = dev_new + elif pt == ProductType.REVPI_CONNECT_5: + # RevPi Connect 5 + dev_new = devicemodule.Connect5(self, device, simulator=self._simulator) + self.core = dev_new elif pt == ProductType.REVPI_COMPACT: # RevPi Compact dev_new = devicemodule.Compact(self, device, simulator=self._simulator) diff --git a/src/revpimodio2/pictory.py b/src/revpimodio2/pictory.py index 01dcdbc..fd7c55d 100644 --- a/src/revpimodio2/pictory.py +++ b/src/revpimodio2/pictory.py @@ -50,6 +50,7 @@ class ProductType: REVPI_CONNECT = 105 REVPI_FLAT = 135 REVPI_CONNECT_4 = 136 + REVPI_CONNECT_5 = 138 VIRTUAL_CLOUD = 24584 VIRTUAL_MODBUS_TCP_SERVER = 24577