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" 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 262283c..4dae8bd 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: @@ -1143,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 9eafcec..38bceb1 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? @@ -311,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 @@ -349,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) @@ -1375,7 +1382,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 +1403,6 @@ class RevPiModIOSelected(RevPiModIO): debug, replace_io_file, shared_procimg, - direct_output, ) if type(deviceselection) is not DevSelect: @@ -1453,7 +1458,6 @@ class RevPiModIODriver(RevPiModIOSelected): debug=True, replace_io_file=None, shared_procimg=False, - direct_output=False, ): """ Instantiiert die Grundfunktionen. @@ -1479,7 +1483,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, )