diff --git a/.idea/misc.xml b/.idea/misc.xml index ada67d1..b35f69e 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,9 +1,6 @@ - diff --git a/.idea/pyLspTools.xml b/.idea/pyLspTools.xml new file mode 100644 index 0000000..400aa9d --- /dev/null +++ b/.idea/pyLspTools.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/src/revpimodio2/__about__.py b/src/revpimodio2/__about__.py index 1a22831..af3612d 100644 --- a/src/revpimodio2/__about__.py +++ b/src/revpimodio2/__about__.py @@ -4,4 +4,4 @@ __author__ = "Sven Sager " __copyright__ = "Copyright (C) 2023 Sven Sager" __license__ = "LGPLv2" -__version__ = "2.9.0rc1" +__version__ = "2.9.0rc2" diff --git a/src/revpimodio2/io.py b/src/revpimodio2/io.py index e7ca912..e153233 100644 --- a/src/revpimodio2/io.py +++ b/src/revpimodio2/io.py @@ -1131,8 +1131,12 @@ class IntIOCounter(IntIO): # Load base class super().__init__(parentdevice, valuelist, iotype, byteorder, signed) - def reset(self) -> None: - """Resets the counter of the input.""" + def reset(self) -> bool: + """ + Resets the counter of the input. + + :return: True if reset was successful, False otherwise + """ if self._parentdevice._modio._monitoring: raise RuntimeError("can not reset counter, while system is in monitoring mode") if self._parentdevice._modio._simulator: @@ -1146,6 +1150,7 @@ class IntIOCounter(IntIO): ioctl(self._parentdevice._modio._myfh, 19220, self.__ioctl_arg) except Exception as e: self._parentdevice._modio._gotioerror("iorst", e) + return False elif hasattr(self._parentdevice._modio._myfh, "ioctl"): # IOCTL over network @@ -1154,6 +1159,7 @@ class IntIOCounter(IntIO): self._parentdevice._modio._myfh.ioctl(19220, self.__ioctl_arg) except Exception as e: self._parentdevice._modio._gotioerror("net_iorst", e) + return False else: # Simulate IOCTL in file @@ -1162,6 +1168,9 @@ class IntIOCounter(IntIO): self._parentdevice._modio._simulate_ioctl(19220, self.__ioctl_arg) except Exception as e: self._parentdevice._modio._gotioerror("file_iorst", e) + return False + + return True class IntIOReplaceable(IntIO): @@ -1276,7 +1285,8 @@ class RelaisOutput(IOBase): This function is only available locally on a Revolution Pi. This function cannot be used via RevPiNetIO. - :return: Integer of switching cycles as single value or tuple of all + :return: Integer of switching cycles as single value or tuple of all. The + value -1 indicates an error. """ # Using ioctl request K+29 = 19229 if self._parentdevice._modio._run_on_pi: @@ -1289,10 +1299,14 @@ class RelaisOutput(IOBase): self.__ioctl_arg, ) except Exception as e: - # If not implemented, we return the max value and set an error - ioctl_return_value = b"\xff" * struct.calcsize(self.__ioctl_arg_format) self._parentdevice._modio._gotioerror("rocounter", e) + # To report an error, return -1 values + if self._bitaddress == -1: + return -1, -1, -1, -1 + else: + return -1 + elif hasattr(self._parentdevice._modio._myfh, "ioctl"): # IOCTL over network """ diff --git a/tests/io_tests/config_ro_multi.rsc b/tests/io_tests/config_ro_multi.rsc new file mode 100644 index 0000000..d58b8bc --- /dev/null +++ b/tests/io_tests/config_ro_multi.rsc @@ -0,0 +1,309 @@ +{ + "App": { + "name": "PiCtory", + "version": "2.17.3", + "saveTS": "20260901080641", + "language": "en", + "layout": { + "north": { + "size": 70, + "initClosed": false, + "initHidden": false + }, + "south": { + "size": 353, + "initClosed": false, + "initHidden": false, + "children": { + "layout1": { + "east": { + "size": 500, + "initClosed": false, + "initHidden": false + } + } + } + }, + "east": { + "size": 70, + "initClosed": true, + "initHidden": false, + "children": {} + }, + "west": { + "size": 340, + "initClosed": false, + "initHidden": false, + "children": { + "layout1": {} + } + } + } + }, + "Summary": { + "inpTotal": 7, + "outTotal": 8 + }, + "Devices": [ + { + "GUID": "fa3b69a8-77f1-7f15-d9f0-339646253ace", + "id": "device_RevPiConnect5_20240315_1_0_001", + "type": "BASE", + "productType": "138", + "position": "0", + "name": "RevPi Connect 5", + "bmk": "RevPi Connect 5", + "inpVariant": 0, + "outVariant": 0, + "comment": "This is a RevPi Connect 5 Device", + "offset": 0, + "inp": { + "0": [ + "RevPiStatus", + "0", + "8", + "0", + true, + "0000", + "", + "" + ], + "1": [ + "RevPiIOCycle", + "0", + "8", + "1", + true, + "0001", + "", + "" + ], + "2": [ + "RS485ErrorCnt", + "0", + "16", + "2", + false, + "0002", + "", + "" + ], + "3": [ + "Core_Temperature", + "0", + "8", + "4", + false, + "0003", + "", + "" + ], + "4": [ + "Core_Frequency", + "0", + "8", + "5", + false, + "0004", + "", + "" + ] + }, + "out": { + "0": [ + "RevPiReservedByte", + "0", + "8", + "6", + false, + "0005", + "", + "" + ], + "1": [ + "RS485ErrorLimit1", + "10", + "16", + "7", + false, + "0006", + "", + "" + ], + "2": [ + "RS485ErrorLimit2", + "1000", + "16", + "9", + false, + "0007", + "", + "" + ], + "3": [ + "RevPiLED", + "0", + "16", + "11", + true, + "0008", + "", + "" + ] + }, + "mem": {}, + "extend": {} + }, + { + "GUID": "53c792e0-ec3d-8047-bb0c-1afa6772873f", + "id": "device_RevPiRO_20231018_1_0_001", + "type": "LEFT_RIGHT", + "productType": "137", + "position": "32", + "name": "RevPi RO", + "bmk": "RevPi RO", + "inpVariant": 0, + "outVariant": 0, + "comment": "", + "offset": 13, + "inp": { + "0": [ + "Status", + "0", + "8", + "0", + false, + "0000", + "", + "" + ] + }, + "out": { + "0": [ + "RelayOutput_1", + "0", + "1", + "1", + true, + "0001", + "", + "0" + ], + "1": [ + "RelayOutput_2", + "0", + "1", + "1", + true, + "0002", + "", + "1" + ], + "2": [ + "RelayOutput_3", + "0", + "1", + "1", + true, + "0003", + "", + "2" + ], + "3": [ + "RelayOutput_4", + "0", + "1", + "1", + true, + "0004", + "", + "3" + ], + "4": [ + "RelayOutputPadding_5", + "0", + "1", + "1", + true, + "0005", + "", + "4" + ], + "5": [ + "RelayOutputPadding_6", + "0", + "1", + "1", + true, + "0006", + "", + "5" + ], + "6": [ + "RelayOutputPadding_7", + "0", + "1", + "1", + true, + "0007", + "", + "6" + ], + "7": [ + "RelayOutputPadding_8", + "0", + "1", + "1", + true, + "0008", + "", + "7" + ] + }, + "mem": { + "0": [ + "RelayCycleWarningThreshold_1", + "0", + "32", + "2", + false, + "0009", + "", + "" + ], + "1": [ + "RelayCycleWarningThreshold_2", + "0", + "32", + "6", + false, + "0010", + "", + "" + ], + "2": [ + "RelayCycleWarningThreshold_3", + "0", + "32", + "10", + false, + "0011", + "", + "" + ], + "3": [ + "RelayCycleWarningThreshold_4", + "0", + "32", + "14", + false, + "0012", + "", + "" + ] + }, + "extend": {} + } + ], + "Connections": [] +} diff --git a/tests/io_tests/config_ro_single.rsc b/tests/io_tests/config_ro_single.rsc new file mode 100644 index 0000000..a899aca --- /dev/null +++ b/tests/io_tests/config_ro_single.rsc @@ -0,0 +1,239 @@ +{ + "App": { + "name": "PiCtory", + "version": "2.17.3", + "saveTS": "20260901080758", + "language": "en", + "layout": { + "north": { + "size": 70, + "initClosed": false, + "initHidden": false + }, + "south": { + "size": 353, + "initClosed": false, + "initHidden": false, + "children": { + "layout1": { + "east": { + "size": 500, + "initClosed": false, + "initHidden": false + } + } + } + }, + "east": { + "size": 70, + "initClosed": true, + "initHidden": false, + "children": {} + }, + "west": { + "size": 340, + "initClosed": false, + "initHidden": false, + "children": { + "layout1": {} + } + } + } + }, + "Summary": { + "inpTotal": 7, + "outTotal": 8 + }, + "Devices": [ + { + "GUID": "fa3b69a8-77f1-7f15-d9f0-339646253ace", + "id": "device_RevPiConnect5_20240315_1_0_001", + "type": "BASE", + "productType": "138", + "position": "0", + "name": "RevPi Connect 5", + "bmk": "RevPi Connect 5", + "inpVariant": 0, + "outVariant": 0, + "comment": "This is a RevPi Connect 5 Device", + "offset": 0, + "inp": { + "0": [ + "RevPiStatus", + "0", + "8", + "0", + true, + "0000", + "", + "" + ], + "1": [ + "RevPiIOCycle", + "0", + "8", + "1", + true, + "0001", + "", + "" + ], + "2": [ + "RS485ErrorCnt", + "0", + "16", + "2", + false, + "0002", + "", + "" + ], + "3": [ + "Core_Temperature", + "0", + "8", + "4", + false, + "0003", + "", + "" + ], + "4": [ + "Core_Frequency", + "0", + "8", + "5", + false, + "0004", + "", + "" + ] + }, + "out": { + "0": [ + "RevPiReservedByte", + "0", + "8", + "6", + false, + "0005", + "", + "" + ], + "1": [ + "RS485ErrorLimit1", + "10", + "16", + "7", + false, + "0006", + "", + "" + ], + "2": [ + "RS485ErrorLimit2", + "1000", + "16", + "9", + false, + "0007", + "", + "" + ], + "3": [ + "RevPiLED", + "0", + "16", + "11", + true, + "0008", + "", + "" + ] + }, + "mem": {}, + "extend": {} + }, + { + "GUID": "53c792e0-ec3d-8047-bb0c-1afa6772873f", + "id": "device_RevPiRO_20231018_1_0_001", + "type": "LEFT_RIGHT", + "productType": "137", + "position": "32", + "name": "RevPi RO", + "bmk": "RevPi RO", + "inpVariant": 0, + "outVariant": 1, + "comment": "", + "offset": 13, + "inp": { + "0": [ + "Status_i02", + "0", + "8", + "0", + false, + "0000", + "", + "" + ] + }, + "out": { + "0": [ + "RelayOutputs", + "0", + "8", + "1", + true, + "0001", + "", + "" + ] + }, + "mem": { + "0": [ + "RelayCycleWarningThreshold_1_i02", + "0", + "32", + "2", + false, + "0002", + "", + "" + ], + "1": [ + "RelayCycleWarningThreshold_2_i02", + "0", + "32", + "6", + false, + "0003", + "", + "" + ], + "2": [ + "RelayCycleWarningThreshold_3_i02", + "0", + "32", + "10", + false, + "0004", + "", + "" + ], + "3": [ + "RelayCycleWarningThreshold_4_i02", + "0", + "32", + "14", + false, + "0005", + "", + "" + ] + }, + "extend": {} + } + ], + "Connections": [] +} diff --git a/tests/io_tests/test_ios.py b/tests/io_tests/test_ios.py index c8488b1..edd36fb 100644 --- a/tests/io_tests/test_ios.py +++ b/tests/io_tests/test_ios.py @@ -105,3 +105,30 @@ class TestIos(TestRevPiModIO): self.assertEqual(rpi.device[65]._ba_devdata[32:38], b"\x20\x00\x00\x00\x00\x00") rpi.io.OutBit_48.value = True self.assertEqual(rpi.device[65]._ba_devdata[32:38], b"\x20\x00\x00\x00\x00\x80") + + def test_get_switching_cycles(self): + rpi = self.modio(configrsc="config_ro_multi.rsc") + self.assertEqual(rpi.io.RelayOutput_1.get_switching_cycles(), 0) + self.assertEqual(rpi.io.RelayOutput_2.get_switching_cycles(), 0) + self.assertEqual(rpi.io.RelayOutput_3.get_switching_cycles(), 0) + self.assertEqual(rpi.io.RelayOutput_4.get_switching_cycles(), 0) + + # This will use ioctl calls + rpi._run_on_pi = True + + with self.assertWarnsRegex(RuntimeWarning, r"'rocounter' and count \d"): + self.assertEqual(rpi.io.RelayOutput_1.get_switching_cycles(), -1) + self.assertEqual(rpi.io.RelayOutput_2.get_switching_cycles(), -1) + self.assertEqual(rpi.io.RelayOutput_3.get_switching_cycles(), -1) + self.assertEqual(rpi.io.RelayOutput_4.get_switching_cycles(), -1) + self.assertEqual(rpi.ioerrors, 4) + + rpi = self.modio(configrsc="config_ro_single.rsc") + self.assertEqual(rpi.io.RelayOutputs.get_switching_cycles(), (0, 0, 0, 0)) + + # This will use ioctl calls + rpi._run_on_pi = True + + with self.assertWarnsRegex(RuntimeWarning, r"'rocounter' and count \d"): + self.assertEqual(rpi.io.RelayOutputs.get_switching_cycles(), (-1, -1, -1, -1)) + self.assertEqual(rpi.ioerrors, 1)