From 1b92241d1881a8e72b9c029fedc502c9a6db08ce Mon Sep 17 00:00:00 2001 From: Sven Sager Date: Tue, 31 Oct 2023 12:45:04 +0100 Subject: [PATCH 1/5] chore: Increase to RC2 of upcoming Release 2.7.0 Signed-off-by: Sven Sager --- src/revpimodio2/__about__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/revpimodio2/__about__.py b/src/revpimodio2/__about__.py index 35bb03f..1ac724e 100644 --- a/src/revpimodio2/__about__.py +++ b/src/revpimodio2/__about__.py @@ -3,4 +3,4 @@ __author__ = "Sven Sager " __copyright__ = "Copyright (C) 2023 Sven Sager" __license__ = "LGPLv2" -__version__ = "2.7.0rc1" +__version__ = "2.7.0rc2" From f352cb63b6bdb1c29236a123275496e1878a9d29 Mon Sep 17 00:00:00 2001 From: Sven Sager Date: Wed, 1 Nov 2023 12:14:06 +0100 Subject: [PATCH 2/5] feat: Remove deprecated parameter 'direct_output' from ModIO classes The RevPiModIO parameter 'direct_output' was replaced by 'shared_procimg' in the commit c11b435 from 2020-08-08. Users have received a DepricatedWarning since then. With the upcoming version, this parameter will be completely deleted. Refs: c11b435 Signed-off-by: Sven Sager --- src/revpimodio2/modio.py | 15 +-------------- src/revpimodio2/netio.py | 7 ------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/revpimodio2/modio.py b/src/revpimodio2/modio.py index 9eafcec..ef406c6 100644 --- a/src/revpimodio2/modio.py +++ b/src/revpimodio2/modio.py @@ -112,7 +112,6 @@ class RevPiModIO(object): debug=True, replace_io_file=None, shared_procimg=False, - direct_output=False, ): """ Instantiiert die Grundfunktionen. @@ -127,7 +126,6 @@ class RevPiModIO(object): :param replace_io_file: Replace IO Konfiguration aus Datei laden :param shared_procimg: Share process image with other processes, this could be insecure for automation - :param direct_output: Deprecated, use shared_procimg """ # Parameterprüfung acheck( @@ -138,7 +136,6 @@ class RevPiModIO(object): simulator=simulator, debug=debug, shared_procimg=shared_procimg, - direct_output=direct_output, ) acheck( str, @@ -147,19 +144,13 @@ class RevPiModIO(object): replace_io_file_noneok=replace_io_file, ) - # TODO: Remove in next release - if direct_output: - warnings.warn( - DeprecationWarning("direct_output is deprecated - use shared_procimg instead!") - ) - self._autorefresh = autorefresh self._configrsc = configrsc self._monitoring = monitoring self._procimg = "/dev/piControl0" if procimg is None else procimg self._set_device_based_cycle_time = True self._simulator = simulator - self._init_shared_procimg = shared_procimg or direct_output + self._init_shared_procimg = shared_procimg self._syncoutputs = syncoutputs # TODO: bei simulator und procimg prüfen ob datei existiert / anlegen? @@ -1375,7 +1366,6 @@ class RevPiModIOSelected(RevPiModIO): debug=True, replace_io_file=None, shared_procimg=False, - direct_output=False, ): """ Instantiiert nur fuer angegebene Devices die Grundfunktionen. @@ -1397,7 +1387,6 @@ class RevPiModIOSelected(RevPiModIO): debug, replace_io_file, shared_procimg, - direct_output, ) if type(deviceselection) is not DevSelect: @@ -1453,7 +1442,6 @@ class RevPiModIODriver(RevPiModIOSelected): debug=True, replace_io_file=None, shared_procimg=False, - direct_output=False, ): """ Instantiiert die Grundfunktionen. @@ -1479,7 +1467,6 @@ class RevPiModIODriver(RevPiModIOSelected): debug, replace_io_file, shared_procimg, - direct_output, ) diff --git a/src/revpimodio2/netio.py b/src/revpimodio2/netio.py index 2314ac7..69ccec1 100644 --- a/src/revpimodio2/netio.py +++ b/src/revpimodio2/netio.py @@ -714,7 +714,6 @@ class RevPiNetIO(_RevPiModIO): debug=True, replace_io_file=None, shared_procimg=False, - direct_output=False, ): """ Instantiiert die Grundfunktionen. @@ -728,7 +727,6 @@ class RevPiNetIO(_RevPiModIO): :param replace_io_file: Replace IO Konfiguration aus Datei laden :param shared_procimg: Share process image with other processes, this could be insecure for automation - :param direct_output: Deprecated, use shared_procimg """ check_ip = compile(r"^(25[0-5]|(2[0-4]|[01]?\d|)\d)(\.(25[0-5]|(2[0-4]|[01]?\d|)\d)){3}$") @@ -771,7 +769,6 @@ class RevPiNetIO(_RevPiModIO): debug=debug, replace_io_file=replace_io_file, shared_procimg=shared_procimg, - direct_output=direct_output, ) self._set_device_based_cycle_time = False @@ -949,7 +946,6 @@ class RevPiNetIOSelected(RevPiNetIO): debug=True, replace_io_file=None, shared_procimg=False, - direct_output=False, ): """ Instantiiert nur fuer angegebene Devices die Grundfunktionen. @@ -971,7 +967,6 @@ class RevPiNetIOSelected(RevPiNetIO): debug, replace_io_file, shared_procimg, - direct_output, ) if type(deviceselection) is not DevSelect: @@ -1026,7 +1021,6 @@ class RevPiNetIODriver(RevPiNetIOSelected): debug=True, replace_io_file=None, shared_procimg=False, - direct_output=False, ): """ Instantiiert die Grundfunktionen. @@ -1052,7 +1046,6 @@ class RevPiNetIODriver(RevPiNetIOSelected): debug, replace_io_file, shared_procimg, - direct_output, ) From 42151e246cecd874f19c6eae2382580b34922532 Mon Sep 17 00:00:00 2001 From: Sven Sager Date: Tue, 31 Oct 2023 13:21:46 +0100 Subject: [PATCH 3/5] fix: Check offset values of the devices for integers In the event of a misconfiguration in PiCtory, it may happen that the offset values are given as floating point numbers. These devices and their IOs must ignore ModiO that it cannot be ensured which offset is correct. Signed-off-by: Sven Sager --- src/revpimodio2/modio.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/revpimodio2/modio.py b/src/revpimodio2/modio.py index ef406c6..49abefd 100644 --- a/src/revpimodio2/modio.py +++ b/src/revpimodio2/modio.py @@ -302,6 +302,19 @@ class RevPiModIO(object): # Devices initialisieren err_names_check = {} for device in sorted(lst_devices, key=lambda x: x["offset"]): + # Pre-check of values + if float(device.get("offset")) != int(device.get("offset")): + # Offset misconfigured + warnings.warn( + "Offset value {0} of device {1} on position {2} is invalid. " + "This device and all IOs are ignored.".format( + device.get("offset"), + device.get("name"), + device.get("position"), + ) + ) + continue + # VDev alter piCtory Versionen auf KUNBUS-Standard ändern if device["position"] == "adap.": device["position"] = 64 From c87f1cceb66916f3653b159610eb8235035ba922 Mon Sep 17 00:00:00 2001 From: Sven Sager Date: Tue, 31 Oct 2023 17:29:22 +0100 Subject: [PATCH 4/5] fix: Check if the length of the previous IO overlaps with the new IO When applying the PiCtory configuration, all IOs are now checked to see if they overlap with the previous ones. If an overlap is detected, the IO is ignored and a warning is given to the user. Signed-off-by: Sven Sager --- src/revpimodio2/io.py | 50 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/src/revpimodio2/io.py b/src/revpimodio2/io.py index 262283c..b78dfd7 100644 --- a/src/revpimodio2/io.py +++ b/src/revpimodio2/io.py @@ -5,6 +5,7 @@ __copyright__ = "Copyright (C) 2023 Sven Sager" __license__ = "LGPLv2" import struct +import warnings from re import match as rematch from threading import Event @@ -242,11 +243,10 @@ class IOList(object): "attribute {0} already exists - can not set io".format(new_io._name) ) - if type(new_io) is StructIO: + do_replace = type(new_io) is StructIO + if do_replace: self.__private_replace_oldio_with_newio(new_io) - object.__setattr__(self, new_io._name, new_io) - # Bytedict für Adresszugriff anpassen if new_io._bitshift: if len(self.__dict_iobyte[new_io.address]) != 8: @@ -261,10 +261,54 @@ class IOList(object): None, None, ] + # Check for overlapping IOs + if ( + not do_replace + and self.__dict_iobyte[new_io.address][new_io._bitaddress] is not None + ): + warnings.warn( + "ignore io '{0}', as an io already exists at the address '{1} Bit {2}'. " + "this can be caused by an incorrect pictory configuration.".format( + new_io.name, + new_io.address, + new_io._bitaddress, + ), + Warning, + ) + return + self.__dict_iobyte[new_io.address][new_io._bitaddress] = new_io else: + # Search the previous IO to calculate the length + offset_end = new_io.address + search_index = new_io.address + while search_index >= 0: + previous_io = self.__dict_iobyte[search_index] + if len(previous_io) == 8: + # Bits on this address are always 1 byte + offset_end -= 1 + elif len(previous_io) == 1: + # Found IO, calculate offset + length of IO + offset_end = previous_io[0].address + previous_io[0].length + break + search_index -= 1 + + # Check if the length of the previous IO overlaps with the new IO + if offset_end > new_io.address: + warnings.warn( + "ignore io '{0}', as an io already exists at the address '{1}'. " + "this can be caused by an incorrect pictory configuration.".format( + new_io.name, + new_io.address, + ), + Warning, + ) + return + self.__dict_iobyte[new_io.address].append(new_io) + object.__setattr__(self, new_io._name, new_io) + if type(new_io) is StructIO: new_io._parentdevice._update_my_io_list() else: From cc689a58eb443b3f9a51308eb36ced28bcb4b9b3 Mon Sep 17 00:00:00 2001 From: Sven Sager Date: Wed, 1 Nov 2023 11:12:43 +0100 Subject: [PATCH 5/5] feat: Add support for RO device This adds the RO device as its own class. The RO device has a special ioctl function, via which the switching cycles of the relays can be called up. Relay outputs from the RO device get the additional attribute 'cycles', which returns the number of switching cycles. The implementation via RevPiNetIO classes cannot currently be implemented because the server does not support a return value. Signed-off-by: Sven Sager --- src/revpimodio2/device.py | 23 +++++++- src/revpimodio2/io.py | 119 ++++++++++++++++++++++++++++++++++++++ src/revpimodio2/modio.py | 3 + 3 files changed, 144 insertions(+), 1 deletion(-) diff --git a/src/revpimodio2/device.py b/src/revpimodio2/device.py index 5e65de1..263265e 100644 --- a/src/revpimodio2/device.py +++ b/src/revpimodio2/device.py @@ -10,7 +10,7 @@ from threading import Event, Lock, Thread from ._internal import INP, OUT, MEM, PROCESS_IMAGE_SIZE from .helper import ProcimgWriter -from .io import IOBase, IntIO, IntIOCounter, IntIOReplaceable, MemIO +from .io import IOBase, IntIO, IntIOCounter, IntIOReplaceable, MemIO, RelaisOutput, IntRelaisOutput from .pictory import ProductType @@ -333,6 +333,15 @@ class Device(object): if iotype == MEM: # Memory setting io_new = MemIO(self, dict_io[key], iotype, "little", False) + elif isinstance(self, RoModule) and dict_io[key][3] == "1": + # Relais of RO are on device address "1" and has a cycle counter + if dict_io[key][7]: + # Each relais output has a single bit + io_new = RelaisOutput(self, dict_io[key], iotype, "little", False) + else: + # All relais outputs are in one byte + io_new = IntRelaisOutput(self, dict_io[key], iotype, "little", False) + elif bool(dict_io[key][7]): # Bei Bitwerten IOBase verwenden io_new = IOBase(self, dict_io[key], iotype, "little", False) @@ -1918,6 +1927,18 @@ class DioModule(Device): super().__init__(parentmodio, dict_device, simulator=simulator) +class RoModule(Device): + """Relais output (RO) module with""" + + def __init__(self, parentmodio, dict_device, simulator=False): + """ + Relais outputs of this device has a cycle counter for the relais. + + :rev: :func:`Device.__init__()` + """ + super().__init__(parentmodio, dict_device, simulator=simulator) + + class Gateway(Device): """ Klasse fuer die RevPi Gateway-Devices. diff --git a/src/revpimodio2/io.py b/src/revpimodio2/io.py index b78dfd7..4dae8bd 100644 --- a/src/revpimodio2/io.py +++ b/src/revpimodio2/io.py @@ -1187,6 +1187,125 @@ class IntIOReplaceable(IntIO): ) +class RelaisOutput(IOBase): + """ + Class for relais outputs to access the cycle counters. + + This class extends the function of to the function + 'get_cycles' and the property 'cycles' to retrieve the relay cycle + counters. + + :ref: :class:`IOBase` + """ + + def __init__(self, parentdevice, valuelist, iotype, byteorder, signed): + """ + Extend with functions to access cycle counters. + + :ref: :func:`IOBase.__init__(...)` + """ + super().__init__(parentdevice, valuelist, iotype, byteorder, signed) + + """ + typedef struct SROGetCountersStr + { + /* Address of module in current configuration */ + uint8_t i8uAddress; + uint32_t counter[REVPI_RO_NUM_RELAY_COUNTERS]; + } SROGetCounters; + """ + # Device position + padding + four counter with 4 byte each + self.__ioctl_arg_format = " and + to add the function 'get_cycles' and the property + 'cycles' to retrieve the relay cycle counters. + + Since both classes inherit from BaseIO, both __init__ functions are called + and the logic is combined. In this case, there is only one 'self' object of + IOBase, which of both classes in inheritance is extended with this. + + :ref: :class:`IOBase` + """ + + pass + + class StructIO(IOBase): """ Klasse fuer den Zugriff auf Daten ueber ein definierten struct. diff --git a/src/revpimodio2/modio.py b/src/revpimodio2/modio.py index 49abefd..38bceb1 100644 --- a/src/revpimodio2/modio.py +++ b/src/revpimodio2/modio.py @@ -353,6 +353,9 @@ class RevPiModIO(object): if pt == ProductType.DIO or pt == ProductType.DI or pt == ProductType.DO: # DIO / DI / DO dev_new = devicemodule.DioModule(self, device, simulator=self._simulator) + elif pt == ProductType.RO: + # RO + dev_new = devicemodule.RoModule(self, device, simulator=self._simulator) else: # Alle anderen IO-Devices dev_new = devicemodule.Device(self, device, simulator=self._simulator)