Merge tag 'unstable/2.7.0_rc2' into pkg/debian_rc

This commit is contained in:
2023-11-02 17:27:59 +01:00
5 changed files with 206 additions and 26 deletions

View File

@@ -3,4 +3,4 @@
__author__ = "Sven Sager <akira@revpimodio.org>"
__copyright__ = "Copyright (C) 2023 Sven Sager"
__license__ = "LGPLv2"
__version__ = "2.7.0rc1"
__version__ = "2.7.0rc2"

View File

@@ -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.

View File

@@ -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 <class 'IOBase'> 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 <class 'IOBase'> 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 = "<BIIII"
self.__ioctl_arg = struct.pack(
self.__ioctl_arg_format,
parentdevice._position,
0,
0,
0,
0,
)
def get_switching_cycles(self):
"""
Get the number of switching cycles from this relay.
If each relay output is represented as BOOL, this function returns a
single integer value. If all relays are displayed as a BYTE, this
function returns a tuple that contains the values of all relay outputs.
The setting is determined by PiCtory and the selected output variant by
the RO device.
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
"""
# Using ioctl request K+29 = 19229
if self._parentdevice._modio._run_on_pi:
# IOCTL to piControl on the RevPi
with self._parentdevice._modio._myfh_lck:
try:
ioctl_return_value = ioctl(
self._parentdevice._modio._myfh,
19229,
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)
elif hasattr(self._parentdevice._modio._myfh, "ioctl"):
# IOCTL over network
"""
The ioctl function over the network does not return a value. Only the successful
execution of the ioctl call is checked and reported back. If a new function has been
implemented in RevPiPyLoad, the subsequent source code can be activated.
with self._parentdevice._modio._myfh_lck:
try:
ioctl_return_value = self._parentdevice._modio._myfh.ioctl(
19229, self.__ioctl_arg
)
except Exception as e:
self._parentdevice._modio._gotioerror("net_rocounter", e)
"""
raise RuntimeError("Can not be called over network via RevPiNetIO")
else:
# Simulate IOCTL on a regular file returns the value of relais index
ioctl_return_value = self.__ioctl_arg
if self._bitaddress == -1:
# Return cycle values of all relais as tuple, if this is a BYTE output
# Remove fist element, which is the ioctl request value
return struct.unpack(self.__ioctl_arg_format, ioctl_return_value)[1:]
else:
# Return cycle value of just one relais as int, if this is a BOOL output
# Increase bit-address bei 1 to ignore fist element, which is the ioctl request value
return struct.unpack(self.__ioctl_arg_format, ioctl_return_value)[self._bitaddress + 1]
switching_cycles = property(get_switching_cycles)
class IntRelaisOutput(IntIO, RelaisOutput):
"""
Class for relais outputs to access the cycle counters.
This class combines the function of <class 'IntIO'> and
<class 'RelaisOutput'> 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.

View File

@@ -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,
)

View File

@@ -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,
)