diff --git a/src/revpimodio2/__init__.py b/src/revpimodio2/__init__.py index 3dcbbd1..656c4a4 100644 --- a/src/revpimodio2/__init__.py +++ b/src/revpimodio2/__init__.py @@ -1,16 +1,16 @@ # -*- coding: utf-8 -*- """ -Stellt alle Klassen fuer den RevolutionPi zur Verfuegung. +Provides all classes for the RevolutionPi. Webpage: https://revpimodio.org/ -Stellt Klassen fuer die einfache Verwendung des Revolution Pis der -KUNBUS GmbH (https://revolution.kunbus.de/) zur Verfuegung. Alle I/Os werden -aus der piCtory Konfiguration eingelesen und mit deren Namen direkt zugreifbar -gemacht. Fuer Gateways sind eigene IOs ueber mehrere Bytes konfigurierbar -Mit den definierten Namen greift man direkt auf die gewuenschten Daten zu. -Auf alle IOs kann der Benutzer Funktionen als Events registrieren. Diese -fuehrt das Modul bei Datenaenderung aus. +Provides classes for easy use of the Revolution Pi from +KUNBUS GmbH (https://revolutionpi.com/) . All I/Os are +read from the piCtory configuration and made directly accessible by their names. +For gateways, custom IOs can be configured across multiple bytes. +With the defined names, the desired data is accessed directly. +The user can register functions as events for all IOs. The module +executes these when data changes. """ __all__ = [ "IOEvent", diff --git a/src/revpimodio2/_internal.py b/src/revpimodio2/_internal.py index e28752f..afb2b82 100644 --- a/src/revpimodio2/_internal.py +++ b/src/revpimodio2/_internal.py @@ -45,12 +45,12 @@ def acheck(check_type, **kwargs) -> None: def consttostr(value) -> str: """ - Gibt fuer Konstanten zurueck. + Returns for constants. - Diese Funktion ist erforderlich, da enum in Python 3.2 nicht existiert. + This function is required because enum does not exist in Python 3.2. - :param value: Konstantenwert - :return: Name der Konstanten + :param value: Constant value + :return: Name of the constant """ if value == 0: return "OFF" diff --git a/src/revpimodio2/app.py b/src/revpimodio2/app.py index 25cf844..54db85b 100644 --- a/src/revpimodio2/app.py +++ b/src/revpimodio2/app.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""Bildet die App Sektion von piCtory ab.""" +"""Maps the App section from piCtory.""" __author__ = "Sven Sager" __copyright__ = "Copyright (C) 2023 Sven Sager" __license__ = "LGPLv2" @@ -8,13 +8,13 @@ from time import gmtime, strptime class App: - """Bildet die App Sektion der config.rsc ab.""" + """Maps the App section of config.rsc.""" __slots__ = "name", "version", "language", "layout", "savets" def __init__(self, app: dict): """ - Instantiiert die App-Klasse. + Instantiates the App class. :param app: piCtory Appinformationen """ @@ -36,5 +36,5 @@ class App: except Exception: self.savets = gmtime(0) - # TODO: Layout untersuchen und anders abbilden + # TODO: Examine layout and map differently self.layout = app.get("layout", {}) diff --git a/src/revpimodio2/device.py b/src/revpimodio2/device.py index 92ce3b6..0400650 100644 --- a/src/revpimodio2/device.py +++ b/src/revpimodio2/device.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""Modul fuer die Verwaltung der Devices.""" +"""Module for managing devices.""" __author__ = "Sven Sager" __copyright__ = "Copyright (C) 2023 Sven Sager" __license__ = "LGPLv2" @@ -15,7 +15,7 @@ from .pictory import ProductType class DeviceList(object): - """Basisklasse fuer direkten Zugriff auf Device Objekte.""" + """Base class for direct access to device objects.""" def __init__(self): """Init DeviceList class.""" @@ -23,10 +23,10 @@ class DeviceList(object): def __contains__(self, key): """ - Prueft ob Device existiert. + Checks if device exists. - :param key: DeviceName / Positionsnummer - :return: True, wenn Device vorhanden + :param key: DeviceName / Position number + :return: True if device exists """ if type(key) == int: return key in self.__dict_position @@ -37,25 +37,25 @@ class DeviceList(object): def __delattr__(self, key, delcomplete=True): """ - Entfernt angegebenes Device. + Removes specified device. - :param key: Device zum entfernen - :param delcomplete: Wenn True wird Device komplett entfernt + :param key: Device to remove + :param delcomplete: If True, device will be removed completely """ if delcomplete: - # Device finden + # Find device if type(key) == int: dev_del = self.__dict_position[key] key = dev_del._name else: dev_del = getattr(self, key) - # Reinigungsjobs + # Cleanup jobs dev_del.autorefresh(False) for io in dev_del: delattr(dev_del._modio.io, io._name) - # Device aus dict löschen + # Delete device from dict del self.__dict_position[dev_del._position] if hasattr(self, key): @@ -63,9 +63,9 @@ class DeviceList(object): def __delitem__(self, key): """ - Entfernt Device an angegebener Position. + Removes device at specified position. - :param key: Deviceposition zum entfernen + :param key: Device position to remove """ if isinstance(key, Device): key = key._position @@ -73,10 +73,10 @@ class DeviceList(object): def __getitem__(self, key): """ - Gibt angegebenes Device zurueck. + Returns specified device. - :param key: DeviceName / Positionsnummer - :return: Gefundenes -Objekt + :param key: DeviceName / Position number + :return: Found object """ if type(key) == int: if key not in self.__dict_position: @@ -87,29 +87,29 @@ class DeviceList(object): def __iter__(self): """ - Gibt Iterator aller Devices zurueck. + Returns iterator of all devices. - Die Reihenfolge ist nach Position im Prozessabbild sortiert und nicht - nach Positionsnummer (Dies entspricht der Positionierung aus piCtory)! + The order is sorted by position in the process image and not + by position number (this corresponds to the positioning from piCtory)! - :return: aller Devices + :return: of all devices """ for dev in sorted(self.__dict_position, key=lambda key: self.__dict_position[key]._offset): yield self.__dict_position[dev] def __len__(self): """ - Gibt Anzahl der Devices zurueck. + Returns number of devices. - :return: Anzahl der Devices""" + :return: Number of devices""" return len(self.__dict_position) def __setattr__(self, key, value): """ - Setzt Attribute nur wenn Device. + Sets attributes only if device. - :param key: Attributname - :param value: Attributobjekt + :param key: Attribute name + :param value: Attribute object """ if isinstance(value, Device): object.__setattr__(self, key, value) @@ -120,11 +120,11 @@ class DeviceList(object): class Device(object): """ - Basisklasse fuer alle Device-Objekte. + Base class for all device objects. - Die Basisfunktionalitaet generiert bei Instantiierung alle IOs und - erweitert den Prozessabbildpuffer um die benoetigten Bytes. Sie verwaltet - ihren Prozessabbildpuffer und sorgt fuer die Aktualisierung der IO-Werte. + The base functionality generates all IOs upon instantiation and + extends the process image buffer by the required bytes. It manages + its process image buffer and ensures the updating of IO values. """ __slots__ = ( @@ -161,11 +161,11 @@ class Device(object): def __init__(self, parentmodio, dict_device, simulator=False): """ - Instantiierung der Device-Klasse. + Instantiation of the Device class. :param parentmodio: RevpiModIO parent object - :param dict_device: fuer dieses Device aus piCotry - :param simulator: Laedt das Modul als Simulator und vertauscht IOs + :param dict_device: for this device from piCtory + :param simulator: Loads the module as simulator and swaps IOs """ self._modio = parentmodio @@ -178,7 +178,7 @@ class Device(object): self._shared_procimg = False self._shared_write = set() - # Wertzuweisung aus dict_device + # Value assignment from dict_device self._name = dict_device.get("name") self._offset = int(dict_device.get("offset")) self._position = int(dict_device.get("position")) @@ -193,7 +193,7 @@ class Device(object): "".format(self._name, parentmodio.length, self._offset), Warning, ) - # IOM-Objekte erstellen und Adressen in SLCs speichern + # Create IOM objects and store addresses in SLCs if simulator: self._slc_inp = self._buildio(dict_device.get("out"), INP) self._slc_out = self._buildio(dict_device.get("inp"), OUT) @@ -202,7 +202,7 @@ class Device(object): self._slc_out = self._buildio(dict_device.get("out"), OUT) self._slc_mem = self._buildio(dict_device.get("mem"), MEM) - # SLCs mit offset berechnen + # Calculate SLCs with offset self._slc_devoff = slice(self._offset, self._offset + self.length) self._slc_inpoff = slice( self._slc_inp.start + self._offset, @@ -217,7 +217,7 @@ class Device(object): self._slc_mem.stop + self._offset, ) - # Alle restlichen attribute an Klasse anhängen + # Attach all remaining attributes to class self.bmk = dict_device.get("bmk", "") self.catalognr = dict_device.get("catalogNr", "") self.comment = dict_device.get("comment", "") @@ -228,29 +228,29 @@ class Device(object): self.outvariant = dict_device.get("outVariant", 0) self.type = dict_device.get("type", "") - # Spezielle Konfiguration von abgeleiteten Klassen durchführen + # Perform special configuration from derived classes self._devconfigure() - # IO Liste aktualisieren für schnellen Indexzugriff + # Update IO list for fast index access self._update_my_io_list() def __bytes__(self): """ - Gibt alle Daten des Devices als zurueck. + Returns all device data as . - :return: Devicedaten als + :return: Device data as """ return bytes(self._ba_devdata) def __contains__(self, key): """ - Prueft ob IO auf diesem Device liegt. + Checks if IO is on this device. :param key: IO-Name / IO-Bytenummer - :return: True, wenn IO auf Device vorhanden + :return: True if IO is present on device """ if isinstance(key, IOBase): - # Umwandlung für key + # Conversion for key key = key._name if type(key) == int: @@ -264,32 +264,32 @@ class Device(object): def __getitem__(self, key): """ - Gibt IO an angegebener Stelle zurueck. + Returns IO at specified position. - :param key: Index des IOs auf dem device als - :return: Gefundenes IO-Objekt + :param key: Index of the IO on the device as + :return: Found IO object """ return self.__my_io_list[key] def __int__(self): """ - Gibt die Positon im RevPi Bus zurueck. + Returns the position on the RevPi bus. - :return: Positionsnummer + :return: Position number """ return self._position def __iter__(self): """ - Gibt Iterator aller IOs zurueck. + Returns iterator of all IOs. - :return: aller IOs + :return: of all IOs """ return self.__getioiter(self._slc_devoff, None) def __len__(self): """ - Gibt Anzahl der Bytes zurueck, die dieses Device belegt. + Returns number of bytes occupied by this device. :return: """ @@ -297,19 +297,19 @@ class Device(object): def __str__(self): """ - Gibt den Namen des Devices zurueck. + Returns the name of the device. - :return: Devicename + :return: Device name """ return self._name def __getioiter(self, ioslc: slice, export): """ - Gibt mit allen IOs zurueck. + Returns with all IOs. - :param ioslc: IO Abschnitt - :param export: Filter fuer 'Export' Flag in piCtory - :return: IOs als Iterator + :param ioslc: IO section + :param export: Filter for 'Export' flag in piCtory + :return: IOs as Iterator """ for lst_io in self._modio.io[ioslc]: for io in lst_io: @@ -318,23 +318,23 @@ class Device(object): def _buildio(self, dict_io: dict, iotype: int) -> slice: """ - Erstellt aus der piCtory-Liste die IOs fuer dieses Device. + Creates the IOs for this device from the piCtory list. - :param dict_io: -Objekt aus piCtory Konfiguration - :param iotype: Wert - :return: mit Start und Stop Position dieser IOs + :param dict_io: object from piCtory configuration + :param iotype: value + :return: with start and stop position of these IOs """ if len(dict_io) <= 0: return slice(0, 0) int_min, int_max = PROCESS_IMAGE_SIZE, 0 for key in sorted(dict_io, key=lambda x: int(x)): - # Neuen IO anlegen + # Create new IO 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 + # Relays 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) @@ -343,10 +343,10 @@ class Device(object): io_new = IntRelaisOutput(self, dict_io[key], iotype, "little", False) elif bool(dict_io[key][7]): - # Bei Bitwerten IOBase verwenden + # Use IOBase for bit values io_new = IOBase(self, dict_io[key], iotype, "little", False) elif isinstance(self, DioModule) and dict_io[key][3] in self._lst_counter: - # Counter IO auf einem DI oder DIO + # Counter IO on a DI or DIO io_new = IntIOCounter( self._lst_counter.index(dict_io[key][3]), self, @@ -356,7 +356,7 @@ class Device(object): False, ) elif isinstance(self, Gateway): - # Ersetzbare IOs erzeugen + # Create replaceable IOs io_new = IntIOReplaceable(self, dict_io[key], iotype, "little", False) else: io_new = IntIO( @@ -364,7 +364,7 @@ class Device(object): dict_io[key], iotype, "little", - # Bei AIO (103) signed auf True setzen + # Set signed to True for AIO (103) self._producttype == ProductType.AIO, ) @@ -374,10 +374,10 @@ class Device(object): Warning, ) else: - # IO registrieren + # Register IO self._modio.io._private_register_new_io_object(io_new) - # Kleinste und größte Speicheradresse ermitteln + # Determine smallest and largest memory address if io_new._slc_address.start < int_min: int_min = io_new._slc_address.start if io_new._slc_address.stop > int_max: @@ -387,143 +387,143 @@ class Device(object): return slice(int_min, int_max) def _devconfigure(self): - """Funktion zum ueberschreiben von abgeleiteten Klassen.""" + """Function to override in derived classes.""" pass def _get_offset(self) -> int: """ - Gibt den Deviceoffset im Prozessabbild zurueck. + Returns the device offset in the process image. - :return: Deviceoffset + :return: Device offset """ return self._offset def _get_producttype(self) -> int: """ - Gibt den Produkttypen des device zurueck. + Returns the product type of the device. - :return: Deviceprodukttyp + :return: Device product type """ return self._producttype def _update_my_io_list(self) -> None: - """Erzeugt eine neue IO Liste fuer schnellen Zugriff.""" + """Creates a new IO list for fast access.""" self.__my_io_list = list(self.__iter__()) def autorefresh(self, activate=True) -> None: """ - Registriert dieses Device fuer die automatische Synchronisierung. + Registers this device for automatic synchronization. - :param activate: Default True fuegt Device zur Synchronisierung hinzu + :param activate: Default True adds device to synchronization """ if activate and self not in self._modio._lst_refresh: - # Daten bei Aufnahme direkt einlesen! + # Read data directly when adding! self._modio.readprocimg(self) - # Datenkopie anlegen + # Create data copy with self._filelock: self._ba_datacp = self._ba_devdata[:] self._selfupdate = True - # Sicher in Liste einfügen + # Safely insert into list with self._modio._imgwriter.lck_refresh: self._modio._lst_refresh.append(self) - # Thread starten, wenn er noch nicht läuft + # Start thread if it is not yet running if not self._modio._imgwriter.is_alive(): - # Alte Einstellungen speichern + # Save old settings imgrefresh = self._modio._imgwriter.refresh - # ImgWriter mit alten Einstellungen erstellen + # Create ImgWriter with old settings self._modio._imgwriter = ProcimgWriter(self._modio) self._modio._imgwriter.refresh = imgrefresh self._modio._imgwriter.start() elif not activate and self in self._modio._lst_refresh: - # Sicher aus Liste entfernen + # Safely remove from list with self._modio._imgwriter.lck_refresh: self._modio._lst_refresh.remove(self) self._selfupdate = False - # Beenden, wenn keien Devices mehr in Liste sind + # Terminate if no more devices are in the list if len(self._modio._lst_refresh) == 0: self._modio._imgwriter.stop() - # Daten beim Entfernen noch einmal schreiben + # Write data once more when removing if not self._modio._monitoring: self._modio.writeprocimg(self) def get_allios(self, export=None) -> list: """ - Gibt eine Liste aller Inputs und Outputs zurueck, keine MEMs. + Returns a list of all inputs and outputs, no MEMs. - Bleibt Parameter 'export' auf None werden alle Inputs und Outputs - zurueckgegeben. Wird 'export' auf True/False gesetzt, werden nur Inputs - und Outputs zurueckgegeben, bei denen der Wert 'Export' in piCtory - uebereinstimmt. + If parameter 'export' remains None, all inputs and outputs will be + returned. If 'export' is set to True/False, only inputs + and outputs will be returned for which the 'Export' value in piCtory + matches. - :param export: Nur In-/Outputs mit angegebenen 'Export' Wert in piCtory - :return: Input und Output, keine MEMs + :param export: Only in-/outputs with specified 'Export' value in piCtory + :return: Input and Output, no MEMs """ return list(self.__getioiter(slice(self._slc_inpoff.start, self._slc_outoff.stop), export)) def get_inputs(self, export=None) -> list: """ - Gibt eine Liste aller Inputs zurueck. + Returns a list of all inputs. - Bleibt Parameter 'export' auf None werden alle Inputs zurueckgegeben. - Wird 'export' auf True/False gesetzt, werden nur Inputs zurueckgegeben, - bei denen der Wert 'Export' in piCtory uebereinstimmt. + If parameter 'export' remains None, all inputs will be returned. + If 'export' is set to True/False, only inputs will be returned + for which the 'Export' value in piCtory matches. - :param export: Nur Inputs mit angegebenen 'Export' Wert in piCtory + :param export: Only inputs with specified 'Export' value in piCtory :return: Inputs """ return list(self.__getioiter(self._slc_inpoff, export)) def get_outputs(self, export=None) -> list: """ - Gibt eine Liste aller Outputs zurueck. + Returns a list of all outputs. - Bleibt Parameter 'export' auf None werden alle Outputs zurueckgegeben. - Wird 'export' auf True/False gesetzt, werden nur Outputs - zurueckgegeben, bei denen der Wert 'Export' in piCtory uebereinstimmt. + If parameter 'export' remains None, all outputs will be returned. + If 'export' is set to True/False, only outputs + returned, for which the value 'Export' in piCtory matches. - :param export: Nur Outputs mit angegebenen 'Export' Wert in piCtory + :param export: Only outputs with specified 'Export' value in piCtory :return: Outputs """ return list(self.__getioiter(self._slc_outoff, export)) def get_memories(self, export=None) -> list: """ - Gibt eine Liste aller Memoryobjekte zurueck. + Returns a list of all memory objects. - Bleibt Parameter 'export' auf None werden alle Mems zurueckgegeben. - Wird 'export' auf True/False gesetzt, werden nur Mems zurueckgegeben, - bei denen der Wert 'Export' in piCtory uebereinstimmt. + If parameter 'export' remains None, all mems will be returned. + If 'export' is set to True/False, only mems will be returned + for which the 'Export' value in piCtory matches. - :param export: Nur Mems mit angegebenen 'Export' Wert in piCtory + :param export: Only mems with specified 'Export' value in piCtory :return: Mems """ return list(self.__getioiter(self._slc_memoff, export)) def readprocimg(self) -> bool: """ - Alle Inputs fuer dieses Device vom Prozessabbild einlesen. + Read all inputs for this device from process image. Same see - :return: True, wenn erfolgreich ausgefuehrt + :return: True if successfully executed :ref: :func:`revpimodio2.modio.RevPiModIO.readprocimg()` """ return self._modio.readprocimg(self) def setdefaultvalues(self) -> None: """ - Alle Outputbuffer fuer dieses Device auf default Werte setzen. + Set all output buffers for this device to default values. - :return: True, wenn erfolgreich ausgefuehrt + :return: True if successfully executed :ref: :func:`revpimodio2.modio.RevPiModIO.setdefaultvalues()` """ self._modio.setdefaultvalues(self) @@ -540,18 +540,18 @@ class Device(object): def syncoutputs(self) -> bool: """ - Lesen aller Outputs im Prozessabbild fuer dieses Device. + Read all outputs in process image for this device. - :return: True, wenn erfolgreich ausgefuehrt + :return: True if successfully executed :ref: :func:`revpimodio2.modio.RevPiModIO.syncoutputs()` """ return self._modio.syncoutputs(self) def writeprocimg(self) -> bool: """ - Schreiben aller Outputs dieses Devices ins Prozessabbild. + Write all outputs of this device to process image. - :return: True, wenn erfolgreich ausgefuehrt + :return: True if successfully executed :ref: :func:`revpimodio2.modio.RevPiModIO.writeprocimg()` """ return self._modio.writeprocimg(self) @@ -564,7 +564,7 @@ class Device(object): class Base(Device): - """Klasse fuer alle Base-Devices wie Core / Connect usw.""" + """Class for all base devices like Core / Connect etc.""" __slots__ = () @@ -575,27 +575,27 @@ class GatewayMixin: @property def leftgate(self) -> bool: """ - Statusbit links vom RevPi ist ein piGate Modul angeschlossen. + Statusbit links vom RevPi is a piGate Modul angeschlossen. - :return: True, wenn piGate links existiert + :return: True if piGate left exists """ return bool(int.from_bytes(self._ba_devdata[self._slc_statusbyte], byteorder="little") & 16) @property def rightgate(self) -> bool: """ - Statusbit rechts vom RevPi ist ein piGate Modul angeschlossen. + Statusbit rechts vom RevPi is a piGate Modul angeschlossen. - :return: True, wenn piGate rechts existiert + :return: True if piGate right exists """ return bool(int.from_bytes(self._ba_devdata[self._slc_statusbyte], byteorder="little") & 32) class ModularBase(Base): """ - Klasse fuer alle modularen Base-Devices wie Core / Connect usw.. + Class for all modular base devices like Core / Connect etc.. - Stellt Funktionen fuer den Status zur Verfuegung. + Provides functions for the status. """ __slots__ = ( @@ -611,10 +611,10 @@ class ModularBase(Base): def __errorlimit(self, slc_io: slice, errorlimit: int) -> None: """ - Verwaltet das Schreiben der ErrorLimits. + Manages writing the error limits. :param slc_io: Byte Slice vom ErrorLimit - :return: Aktuellen ErrorLimit oder None wenn nicht verfuegbar + :return: Current ErrorLimit or None if not available """ if 0 <= errorlimit <= 65535: self._ba_devdata[slc_io] = errorlimit.to_bytes(2, byteorder="little") @@ -623,54 +623,54 @@ class ModularBase(Base): def _get_status(self) -> int: """ - Gibt den RevPi Core Status zurueck. + Returns the RevPi Core status. - :return: Status als + :return: Status as """ return int.from_bytes(self._ba_devdata[self._slc_statusbyte], byteorder="little") @property def picontrolrunning(self) -> bool: """ - Statusbit fuer piControl-Treiber laeuft. + Statusbit for piControl-Treiber laeuft. - :return: True, wenn Treiber laeuft + :return: True, if Treiber laeuft """ return bool(int.from_bytes(self._ba_devdata[self._slc_statusbyte], byteorder="little") & 1) @property def unconfdevice(self) -> bool: """ - Statusbit fuer ein IO-Modul nicht mit PiCtory konfiguriert. + Status bit for an IO module not configured with piCtory. - :return: True, wenn IO Modul nicht konfiguriert + :return: True if IO module is not configured """ return bool(int.from_bytes(self._ba_devdata[self._slc_statusbyte], byteorder="little") & 2) @property def missingdeviceorgate(self) -> bool: """ - Statusbit fuer ein IO-Modul fehlt oder piGate konfiguriert. + Status bit for an IO module missing or piGate configured. - :return: True, wenn IO-Modul fehlt oder piGate konfiguriert + :return: True if IO module is missing or piGate is configured """ return bool(int.from_bytes(self._ba_devdata[self._slc_statusbyte], byteorder="little") & 4) @property def overunderflow(self) -> bool: """ - Statusbit Modul belegt mehr oder weniger Speicher als konfiguriert. + Status bit: Module occupies more or less memory than configured. - :return: True, wenn falscher Speicher belegt ist + :return: True if wrong memory is occupied """ return bool(int.from_bytes(self._ba_devdata[self._slc_statusbyte], byteorder="little") & 8) @property def iocycle(self) -> int: """ - Gibt Zykluszeit der Prozessabbildsynchronisierung zurueck. + Returns cycle time of process image synchronization. - :return: Zykluszeit in ms ( -1 wenn nicht verfuegbar) + :return: Cycle time in ms (-1 if not available) """ return ( -1 @@ -681,9 +681,9 @@ class ModularBase(Base): @property def temperature(self) -> int: """ - Gibt CPU-Temperatur zurueck. + Returns CPU temperature. - :return: CPU-Temperatur in Celsius (-273 wenn nich verfuegbar) + :return: CPU temperature in Celsius (-273 if not available) """ return ( -273 @@ -694,9 +694,9 @@ class ModularBase(Base): @property def frequency(self) -> int: """ - Gibt CPU Taktfrequenz zurueck. + Returns CPU clock frequency. - :return: CPU Taktfrequenz in MHz (-1 wenn nicht verfuegbar) + :return: CPU clock frequency in MHz (-1 if not available) """ return ( -1 @@ -707,9 +707,9 @@ class ModularBase(Base): @property def ioerrorcount(self) -> int: """ - Gibt Fehleranzahl auf RS485 piBridge Bus zurueck. + Returns error count on RS485 piBridge bus. - :return: Fehleranzahl der piBridge (-1 wenn nicht verfuegbar) + :return: Number of errors of the piBridge (-1 if not available) """ return ( -1 @@ -720,9 +720,9 @@ class ModularBase(Base): @property def errorlimit1(self) -> int: """ - Gibt RS485 ErrorLimit1 Wert zurueck. + Returns RS485 ErrorLimit1 value. - :return: Aktueller Wert fuer ErrorLimit1 (-1 wenn nicht verfuegbar) + :return: Current value for ErrorLimit1 (-1 if not available) """ return ( -1 @@ -733,9 +733,9 @@ class ModularBase(Base): @errorlimit1.setter def errorlimit1(self, value: int) -> None: """ - Setzt RS485 ErrorLimit1 auf neuen Wert. + Sets RS485 ErrorLimit1 to new value. - :param value: Neuer ErrorLimit1 Wert + :param value: Neuer ErrorLimit1 value """ if self._slc_errorlimit1 is None: raise RuntimeError("selected core item in piCtory does not support errorlimit1") @@ -745,9 +745,9 @@ class ModularBase(Base): @property def errorlimit2(self) -> int: """ - Gibt RS485 ErrorLimit2 Wert zurueck. + Returns RS485 ErrorLimit2 value. - :return: Aktueller Wert fuer ErrorLimit2 (-1 wenn nicht verfuegbar) + :return: Current value for ErrorLimit2 (-1 if not available) """ return ( -1 @@ -758,9 +758,9 @@ class ModularBase(Base): @errorlimit2.setter def errorlimit2(self, value: int) -> None: """ - Setzt RS485 ErrorLimit2 auf neuen Wert. + Sets RS485 ErrorLimit2 to new value. - :param value: Neuer ErrorLimit2 Wert + :param value: Neuer ErrorLimit2 value """ if self._slc_errorlimit2 is None: raise RuntimeError("selected core item in piCtory does not support errorlimit2") @@ -772,25 +772,25 @@ class ModularBase(Base): class Core(ModularBase, GatewayMixin): """ - Klasse fuer den RevPi Core. + Class for the RevPi Core. - Stellt Funktionen fuer die LEDs und den Status zur Verfuegung. + Provides functions for the LEDs and the status. """ __slots__ = "a1green", "a1red", "a2green", "a2red", "wd" def __setattr__(self, key, value): - """Verhindert Ueberschreibung der LEDs.""" + """Prevents overwriting the LEDs.""" if hasattr(self, key) and key in ("a1green", "a1red", "a2green", "a2red", "wd"): raise AttributeError("direct assignment is not supported - use .value Attribute") else: object.__setattr__(self, key, value) def _devconfigure(self) -> None: - """Core-Klasse vorbereiten.""" + """Prepare Core class.""" super()._devconfigure() - # Statische IO Verknüpfungen je nach Core-Variante + # Static IO links depending on Core variant # 2 Byte = Core1.0 self._slc_statusbyte = slice(0, 1) self._slc_led = slice(1, 2) @@ -818,7 +818,7 @@ class Core(ModularBase, GatewayMixin): self._slc_errorlimit1 = slice(7, 9) self._slc_errorlimit2 = slice(9, 11) - # Exportflags prüfen (Byte oder Bit) + # Check export flags (Byte or Bit) lst_led = self._modio.io[self._slc_devoff][self._slc_led.start] if len(lst_led) == 8: exp_a1green = lst_led[0].export @@ -831,7 +831,7 @@ class Core(ModularBase, GatewayMixin): exp_a2green = exp_a1green exp_a2red = exp_a1green - # Echte IOs erzeugen + # Create actual IOs self.a1green = IOBase( self, ["core.a1green", 0, 1, self._slc_led.start, exp_a1green, None, "LED_A1_GREEN", "0"], @@ -872,27 +872,27 @@ class Core(ModularBase, GatewayMixin): def _get_leda1(self) -> int: """ - Gibt den Zustand der LED A1 vom Core zurueck. + Returns the state of LED A1 from the Core. - :return: 0=aus, 1=gruen, 2=rot + :return: 0=from, 1=gruen, 2=rot """ # 0b00000011 = 3 return self._ba_devdata[self._slc_led.start] & 3 def _get_leda2(self) -> int: """ - Gibt den Zustand der LED A2 vom Core zurueck. + Returns the state of LED A2 from the Core. - :return: 0=aus, 1=gruen, 2=rot + :return: 0=from, 1=gruen, 2=rot """ # 0b00001100 = 12 return (self._ba_devdata[self._slc_led.start] & 12) >> 2 def _set_leda1(self, value: int) -> None: """ - Setzt den Zustand der LED A1 vom Core. + Sets the state of LED A1 from the Core. - :param value: 0=aus, 1=gruen, 2=rot + :param value: 0=from, 1=gruen, 2=rot """ if 0 <= value <= 3: self.a1green(bool(value & 1)) @@ -902,9 +902,9 @@ class Core(ModularBase, GatewayMixin): def _set_leda2(self, value: int) -> None: """ - Setzt den Zustand der LED A2 vom Core. + Sets the state of LED A2 from the Core. - :param value: 0=aus, 1=gruen, 2=rot + :param value: 0=from, 1=gruen, 2=rot """ if 0 <= value <= 3: self.a2green(bool(value & 1)) @@ -921,32 +921,32 @@ class Core(ModularBase, GatewayMixin): class Connect(Core): - """Klasse fuer den RevPi Connect. + """Class for the RevPi Connect. - Stellt Funktionen fuer die LEDs, Watchdog und den Status zur Verfuegung. + Provides functions for the LEDs, watchdog and the status. """ __slots__ = "__evt_wdtoggle", "__th_wdtoggle", "a3green", "a3red", "x2in", "x2out" def __setattr__(self, key, value): - """Verhindert Ueberschreibung der speziellen IOs.""" + """Prevents overwriting the special IOs.""" if hasattr(self, key) and key in ("a3green", "a3red", "x2in", "x2out"): raise AttributeError("direct assignment is not supported - use .value Attribute") super(Connect, self).__setattr__(key, value) def __wdtoggle(self) -> None: - """WD Ausgang alle 10 Sekunden automatisch toggeln.""" + """WD Ausgang all 10 Sekunden automatisch toggeln.""" while not self.__evt_wdtoggle.wait(10): self.wd.value = not self.wd.value def _devconfigure(self) -> None: - """Connect-Klasse vorbereiten.""" + """Prepare Connect class.""" super()._devconfigure() self.__evt_wdtoggle = Event() self.__th_wdtoggle = None - # Exportflags prüfen (Byte oder Bit) + # Check export flags (Byte or Bit) lst_myios = self._modio.io[self._slc_devoff] lst_led = lst_myios[self._slc_led.start] if len(lst_led) == 8: @@ -965,7 +965,7 @@ class Connect(Core): else: exp_x2in = lst_status[0].export - # Echte IOs erzeugen + # Create actual IOs self.a3green = IOBase( self, ["core.a3green", 0, 1, self._slc_led.start, exp_a3green, None, "LED_A3_GREEN", "4"], @@ -981,7 +981,7 @@ class Connect(Core): False, ) - # IO Objekte für WD und X2 in/out erzeugen + # Create IO objects for WD and X2 in/out self.x2in = IOBase( self, ["core.x2in", 0, 1, self._slc_statusbyte.start, exp_x2in, None, "Connect_X2_IN", "6"], @@ -1002,26 +1002,26 @@ class Connect(Core): def _get_leda3(self) -> int: """ - Gibt den Zustand der LED A3 vom Connect zurueck. + Returns the Zustand the LED A3 vom Connect. - :return: 0=aus, 1=gruen, 2=rot + :return: 0=from, 1=gruen, 2=rot """ # 0b00110000 = 48 return (self._ba_devdata[self._slc_led.start] & 48) >> 4 def _get_wdtoggle(self) -> bool: """ - Ruft den Wert fuer Autowatchdog ab. + Retrieves the value for Autowatchdog. - :return: True, wenn Autowatchdog aktiv ist + :return: True if autowatchdog is active """ return self.__th_wdtoggle is not None and self.__th_wdtoggle.is_alive() def _set_leda3(self, value: int) -> None: """ - Setzt den Zustand der LED A3 vom Connect. + Sets the state of LED A3 on the Connect. - :param: value 0=aus, 1=gruen, 2=rot + :param: value 0=from, 1=gruen, 2=rot """ if 0 <= value <= 3: self.a3green(bool(value & 1)) @@ -1031,18 +1031,14 @@ class Connect(Core): def _set_wdtoggle(self, value: bool) -> None: """ - Setzt den Wert fuer Autowatchdog. + Sets the value for autowatchdog. - Wird dieser Wert auf True gesetzt, wechselt im Hintergrund das noetige - Bit zum toggeln des Watchdogs alle 10 Sekunden zwichen True und False. - Dieses Bit wird bei autorefresh=True natuerlich automatisch in das - Prozessabbild geschrieben. + If this value is set to True, the necessary bit to toggle the watchdog is switched between True and False every 10 seconds in the background. + This bit is automatically written to the process image with autorefresh=True. - WICHTIG: Sollte autorefresh=False sein, muss zyklisch - .writeprocimg() aufgerufen werden, um den Wert in das - Prozessabbild zu schreiben!!! + IMPORTANT: If autorefresh=False, .writeprocimg() must be called cyclically to write the value to the process image!!! - :param value: True zum aktivieren, False zum beenden + :param value: True to activate, False to terminate """ if self._modio._monitoring: raise RuntimeError("can not toggle watchdog, while system is in monitoring mode") @@ -1053,7 +1049,7 @@ class Connect(Core): self.__evt_wdtoggle.set() elif not self._get_wdtoggle(): - # Watchdogtoggler erstellen + # Create watchdog toggler self.__evt_wdtoggle.clear() self.__th_wdtoggle = Thread(target=self.__wdtoggle, daemon=True) self.__th_wdtoggle.start() @@ -1085,7 +1081,7 @@ class ModularBaseConnect_4_5(ModularBase): ) def __setattr__(self, key, value): - """Verhindert Ueberschreibung der speziellen IOs.""" + """Prevents overwriting the special IOs.""" if hasattr(self, key) and key in ( "a1red", "a1green", @@ -1120,7 +1116,7 @@ class ModularBaseConnect_4_5(ModularBase): return led_calculated def _devconfigure(self) -> None: - """Connect 4/5-Klasse vorbereiten.""" + """Prepare Connect 4/5 class.""" super()._devconfigure() self._slc_statusbyte = slice(0, 1) @@ -1133,7 +1129,7 @@ class ModularBaseConnect_4_5(ModularBase): self._slc_errorlimit2 = slice(9, 11) self._slc_led = slice(11, 13) - # Exportflags prüfen (Byte oder Bit) + # Check export flags (Byte or Bit) lst_myios = self._modio.io[self._slc_devoff] lst_led = lst_myios[self._slc_led.start] lst_output = lst_myios[self._slc_output.start] @@ -1171,7 +1167,7 @@ class ModularBaseConnect_4_5(ModularBase): exp_a5green = exp_a1red exp_a5blue = exp_a1red - # Echte IOs erzeugen + # Create actual IOs self.a1red = IOBase( self, ["core.a1red", 0, 1, self._slc_led.start, exp_a1red, None, "LED_A1_RED", "0"], @@ -1284,50 +1280,50 @@ class ModularBaseConnect_4_5(ModularBase): def _get_leda1(self) -> int: """ - Gibt den Zustand der LED A1 vom Connect zurueck. + Returns the Zustand the LED A1 vom Connect. - :return: 0=aus, 1=gruen, 2=root, 4=blau, mixed RGB colors + :return: 0=from, 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. + Returns the state of LED A2 from the Core. - :return: 0=aus, 1=gruen, 2=root, 4=blau, mixed RGB colors + :return: 0=from, 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. + Returns the Zustand the LED A3 vom Core. - :return: 0=aus, 1=gruen, 2=root, 4=blau, mixed RGB colors + :return: 0=from, 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. + Returns the Zustand the LED A4 vom Core. - :return: 0=aus, 1=gruen, 2=root, 4=blau, mixed RGB colors + :return: 0=from, 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. + Returns the Zustand the LED A5 vom Core. - :return: 0=aus, 1=gruen, 2=root, 4=blau, mixed RGB colors + :return: 0=from, 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. + Sets the state of LED A1 on the Connect. - :param: value 0=aus, 1=gruen, 2=rot, 4=blue, mixed RGB colors + :param: value 0=from, 1=gruen, 2=rot, 4=blue, mixed RGB colors """ if 0 <= value <= 7: self.a1red(bool(value & 2)) @@ -1338,9 +1334,9 @@ class ModularBaseConnect_4_5(ModularBase): def _set_leda2(self, value: int) -> None: """ - Setzt den Zustand der LED A2 vom Connect. + Sets the state of LED A2 on the Connect. - :param: value 0=aus, 1=gruen, 2=rot, 4=blue, mixed RGB colors + :param: value 0=from, 1=gruen, 2=rot, 4=blue, mixed RGB colors """ if 0 <= value <= 7: self.a2red(bool(value & 2)) @@ -1351,9 +1347,9 @@ class ModularBaseConnect_4_5(ModularBase): def _set_leda3(self, value: int) -> None: """ - Setzt den Zustand der LED A3 vom Connect. + Sets the state of LED A3 on the Connect. - :param: value 0=aus, 1=gruen, 2=rot, 4=blue, mixed RGB colors + :param: value 0=from, 1=gruen, 2=rot, 4=blue, mixed RGB colors """ if 0 <= value <= 7: self.a3red(bool(value & 2)) @@ -1364,9 +1360,9 @@ class ModularBaseConnect_4_5(ModularBase): def _set_leda4(self, value: int) -> None: """ - Setzt den Zustand der LED A4 vom Connect. + Sets the state of LED A4 on the Connect. - :param: value 0=aus, 1=gruen, 2=rot, 4=blue, mixed RGB colors + :param: value 0=from, 1=gruen, 2=rot, 4=blue, mixed RGB colors """ if 0 <= value <= 7: self.a4red(bool(value & 2)) @@ -1377,9 +1373,9 @@ class ModularBaseConnect_4_5(ModularBase): def _set_leda5(self, value: int) -> None: """ - Setzt den Zustand der LED A5 vom Connect. + Sets the state of LED A5 on the Connect. - :param: value 0=aus, 1=gruen, 2=rot, 4=blue, mixed RGB colors + :param: value 0=from, 1=gruen, 2=rot, 4=blue, mixed RGB colors """ if 0 <= value <= 7: self.a5red(bool(value & 2)) @@ -1403,18 +1399,18 @@ class ModularBaseConnect_4_5(ModularBase): class Connect5(ModularBaseConnect_4_5, GatewayMixin): - """Klasse fuer den RevPi Connect 5. + """Class for the RevPi Connect 5. - Stellt Funktionen fuer die LEDs und den Status zur Verfuegung. + Provides functions for the LEDs and the status. """ pass class Connect4(ModularBaseConnect_4_5): - """Klasse fuer den RevPi Connect 4. + """Class for the RevPi Connect 4. - Stellt Funktionen fuer die LEDs und den Status zur Verfuegung. + Provides functions for the LEDs and the status. """ __slots__ = ( @@ -1423,7 +1419,7 @@ class Connect4(ModularBaseConnect_4_5): ) def __setattr__(self, key, value): - """Verhindert Ueberschreibung der speziellen IOs.""" + """Prevents overwriting the special IOs.""" if hasattr(self, key) and key in ( "x2in", "x2out", @@ -1432,10 +1428,10 @@ class Connect4(ModularBaseConnect_4_5): super().__setattr__(key, value) def _devconfigure(self) -> None: - """Connect4-Klasse vorbereiten.""" + """Prepare Connect4 class.""" super()._devconfigure() - # Exportflags prüfen (Byte oder Bit) + # Check export flags (Byte or Bit) lst_myios = self._modio.io[self._slc_devoff] lst_output = lst_myios[self._slc_output.start] @@ -1451,7 +1447,7 @@ class Connect4(ModularBaseConnect_4_5): else: exp_x2in = lst_status[0].export - # IO Objekte für X2 in/out erzeugen + # Create IO objects for X2 in/out self.x2in = IOBase( self, ["core.x2in", 0, 1, self._slc_statusbyte.start, exp_x2in, None, "Connect_X2_IN", "6"], @@ -1470,10 +1466,10 @@ class Connect4(ModularBaseConnect_4_5): class Compact(Base): """ - Klasse fuer den RevPi Compact. + Class for the RevPi Compact. - Stellt Funktionen fuer die LEDs zur Verfuegung. Auf IOs wird ueber das .io - Objekt zugegriffen. + Provides functions for the LEDs. IOs are accessed via the .io + object zugegriffen. """ __slots__ = ( @@ -1488,22 +1484,22 @@ class Compact(Base): ) def __setattr__(self, key, value): - """Verhindert Ueberschreibung der LEDs.""" + """Prevents overwriting the LEDs.""" if hasattr(self, key) and key in ("a1green", "a1red", "a2green", "a2red", "wd"): raise AttributeError("direct assignment is not supported - use .value Attribute") else: object.__setattr__(self, key, value) def _devconfigure(self) -> None: - """Core-Klasse vorbereiten.""" + """Prepare Core class.""" super()._devconfigure() - # Statische IO Verknüpfungen des Compacts + # Statische IO Verknüpfungen of the Compacts self._slc_led = slice(23, 24) self._slc_temperature = slice(0, 1) self._slc_frequency = slice(1, 2) - # Exportflags prüfen (Byte oder Bit) + # Check export flags (Byte or Bit) lst_led = self._modio.io[self._slc_devoff][self._slc_led.start] if len(lst_led) == 8: exp_a1green = lst_led[0].export @@ -1516,7 +1512,7 @@ class Compact(Base): exp_a2green = exp_a1green exp_a2red = exp_a1green - # Echte IOs erzeugen + # Create actual IOs self.a1green = IOBase( self, ["core.a1green", 0, 1, self._slc_led.start, exp_a1green, None, "LED_A1_GREEN", "0"], @@ -1557,27 +1553,27 @@ class Compact(Base): def _get_leda1(self) -> int: """ - Gibt den Zustand der LED A1 vom Compact zurueck. + Returns the Zustand the LED A1 vom Compact. - :return: 0=aus, 1=gruen, 2=rot + :return: 0=from, 1=gruen, 2=rot """ # 0b00000011 = 3 return self._ba_devdata[self._slc_led.start] & 3 def _get_leda2(self) -> int: """ - Gibt den Zustand der LED A2 vom Compact zurueck. + Returns the Zustand the LED A2 vom Compact. - :return: 0=aus, 1=gruen, 2=rot + :return: 0=from, 1=gruen, 2=rot """ # 0b00001100 = 12 return (self._ba_devdata[self._slc_led.start] & 12) >> 2 def _set_leda1(self, value: int) -> None: """ - Setzt den Zustand der LED A1 vom Compact. + Sets the state of LED A1 on the Compact. - :param value: 0=aus, 1=gruen, 2=rot + :param value: 0=from, 1=gruen, 2=rot """ if 0 <= value <= 3: self.a1green(bool(value & 1)) @@ -1587,9 +1583,9 @@ class Compact(Base): def _set_leda2(self, value: int) -> None: """ - Setzt den Zustand der LED A2 vom Compact. + Sets the state of LED A2 on the Compact. - :param value: 0=aus, 1=gruen, 2=rot + :param value: 0=from, 1=gruen, 2=rot """ if 0 <= value <= 3: self.a2green(bool(value & 1)) @@ -1607,9 +1603,9 @@ class Compact(Base): @property def temperature(self) -> int: """ - Gibt CPU-Temperatur zurueck. + Returns CPU temperature. - :return: CPU-Temperatur in Celsius (-273 wenn nich verfuegbar) + :return: CPU temperature in Celsius (-273 if not available) """ return ( -273 @@ -1620,9 +1616,9 @@ class Compact(Base): @property def frequency(self) -> int: """ - Gibt CPU Taktfrequenz zurueck. + Returns CPU clock frequency. - :return: CPU Taktfrequenz in MHz (-1 wenn nicht verfuegbar) + :return: CPU clock frequency in MHz (-1 if not available) """ return ( -1 @@ -1633,10 +1629,10 @@ class Compact(Base): class Flat(Base): """ - Klasse fuer den RevPi Flat. + Class for the RevPi Flat. - Stellt Funktionen fuer die LEDs zur Verfuegung. Auf IOs wird ueber das .io - Objekt zugegriffen. + Provides functions for the LEDs. IOs are accessed via the .io + object zugegriffen. """ __slots__ = ( @@ -1661,7 +1657,7 @@ class Flat(Base): ) def __setattr__(self, key, value): - """Verhindert Ueberschreibung der LEDs.""" + """Prevents overwriting the LEDs.""" if hasattr(self, key) and key in ( "a1green", "a1red", @@ -1682,17 +1678,17 @@ class Flat(Base): object.__setattr__(self, key, value) def _devconfigure(self) -> None: - """Core-Klasse vorbereiten.""" + """Prepare Core class.""" super()._devconfigure() - # Statische IO Verknüpfungen des Compacts + # Statische IO Verknüpfungen of the Compacts self._slc_led = slice(7, 9) self._slc_temperature = slice(4, 5) self._slc_frequency = slice(5, 6) self._slc_switch = slice(6, 7) self._slc_dout = slice(11, 12) - # Exportflags prüfen (Byte oder Bit) + # Check export flags (Byte or Bit) lst_led = self._modio.io[self._slc_devoff][self._slc_led.start] if len(lst_led) == 8: exp_a1green = lst_led[0].export @@ -1720,7 +1716,7 @@ class Flat(Base): exp_a5green = exp_a1green exp_a5red = exp_a1green - # Echte IOs erzeugen + # Create actual IOs self.a1green = IOBase( self, ["core.a1green", 0, 1, self._slc_led.start, exp_a1green, None, "LED_A1_GREEN", "0"], @@ -1936,9 +1932,9 @@ class Flat(Base): @property def temperature(self) -> int: """ - Gibt CPU-Temperatur zurueck. + Returns CPU temperature. - :return: CPU-Temperatur in Celsius (-273 wenn nich verfuegbar) + :return: CPU temperature in Celsius (-273 if not available) """ return ( -273 @@ -1949,9 +1945,9 @@ class Flat(Base): @property def frequency(self) -> int: """ - Gibt CPU Taktfrequenz zurueck. + Returns CPU clock frequency. - :return: CPU Taktfrequenz in MHz (-1 wenn nicht verfuegbar) + :return: CPU clock frequency in MHz (-1 if not available) """ return ( -1 @@ -1961,20 +1957,20 @@ class Flat(Base): class DioModule(Device): - """Stellt ein DIO / DI / DO Modul dar.""" + """Represents a DIO / DI / DO module.""" __slots__ = "_lst_counter" def __init__(self, parentmodio, dict_device, simulator=False): """ - Erweitert Device-Klasse zum Erkennen von IntIOCounter. + Extends Device class for detecting IntIOCounter. :rev: :func:`Device.__init__()` """ - # Stringliste der Byteadressen (alle Module sind gleich) + # Stringliste the Byteadressen (all Module are gleich) self._lst_counter = list(map(str, range(6, 70, 4))) - # Basisklasse laden + # Load base class super().__init__(parentmodio, dict_device, simulator=simulator) @@ -1992,13 +1988,12 @@ class RoModule(Device): class Gateway(Device): """ - Klasse fuer die RevPi Gateway-Devices. + Class for the RevPi Gateway-Devices. - Stellt neben den Funktionen von RevPiDevice weitere Funktionen fuer die - Gateways bereit. IOs auf diesem Device stellen die replace_io Funktion - zur verfuegung, ueber die eigene IOs definiert werden, die ein - RevPiStructIO-Objekt abbilden. - Dieser IO-Typ kann Werte ueber mehrere Bytes verarbeiten und zurueckgeben. + Provides additional functions for the RevPi Gateway devices besides the functions from RevPiDevice. + Gateways are ready. IOs on this device provide the replace_io function, + which allows defining custom IOs that map to a RevPiStructIO object. + This IO type can process and return values via multiple bytes. :ref: :func:`revpimodio2.io.IntIOReplaceable.replace_io()` """ @@ -2007,7 +2002,7 @@ class Gateway(Device): def __init__(self, parent, dict_device, simulator=False): """ - Erweitert Device-Klasse um get_rawbytes-Funktionen. + Extends Device class with get_rawbytes functions. :ref: :func:`Device.__init__()` """ @@ -2021,21 +2016,20 @@ class Gateway(Device): def get_rawbytes(self) -> bytes: """ - Gibt die Bytes aus, die dieses Device verwendet. + Returns the bytes used by this device. - :return: des Devices + :return: of the Devices """ return bytes(self._ba_devdata) class Virtual(Gateway): """ - Klasse fuer die RevPi Virtual-Devices. + Class for the RevPi Virtual-Devices. - Stellt die selben Funktionen wie Gateway zur Verfuegung. Es koennen - ueber die reg_*-Funktionen eigene IOs definiert werden, die ein - RevPiStructIO-Objekt abbilden. - Dieser IO-Typ kann Werte ueber mehrere Bytes verarbeiten und zurueckgeben. + Provides the same functions as Gateway. Custom IOs can be + defined via the replace_io functions that map to RevPiStructIO objects. + This IO type can process and return values via multiple bytes. :ref: :func:`Gateway` """ @@ -2044,15 +2038,12 @@ class Virtual(Gateway): def writeinputdefaults(self): """ - Schreibt fuer ein virtuelles Device piCtory Defaultinputwerte. + Writes piCtory default input values for a virtual device. - Sollten in piCtory Defaultwerte fuer Inputs eines virtuellen Devices - angegeben sein, werden diese nur beim Systemstart oder einem piControl - Reset gesetzt. Sollte danach das Prozessabbild mit NULL ueberschrieben, - gehen diese Werte verloren. - Diese Funktion kann nur auf virtuelle Devices angewendet werden! + If default values for inputs of a virtual device are specified in piCtory, these are only set at system startup or a piControl reset. If the process image is subsequently overwritten with NULL, these values will be lost. + This function can only be applied to virtual devices! - :return: True, wenn Arbeiten am virtuellen Device erfolgreich waren + :return: True if operations on the virtual device were successful """ if self._modio._monitoring: raise RuntimeError("can not write process image, while system is in monitoring mode") @@ -2063,7 +2054,7 @@ class Virtual(Gateway): for io in self.get_inputs(): self._ba_devdata[io._slc_address] = io._defaultvalue - # Inputs auf Bus schreiben + # Inputs to Bus schreiben self._modio._myfh_lck.acquire() try: self._modio._myfh.seek(self._slc_inpoff.start) diff --git a/src/revpimodio2/helper.py b/src/revpimodio2/helper.py index 0938cef..676b9ad 100644 --- a/src/revpimodio2/helper.py +++ b/src/revpimodio2/helper.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""RevPiModIO Helperklassen und Tools.""" +"""RevPiModIO helper classes and tools.""" __author__ = "Sven Sager" __copyright__ = "Copyright (C) 2023 Sven Sager" __license__ = "LGPLv2" @@ -16,22 +16,14 @@ from .io import IOBase class EventCallback(Thread): - """Thread fuer das interne Aufrufen von Event-Funktionen. + """Thread for internal calling of event functions. - Der Eventfunktion, welche dieser Thread aufruft, wird der Thread selber - als Parameter uebergeben. Darauf muss bei der definition der Funktion - geachtet werden z.B. "def event(th):". Bei umfangreichen Funktionen kann - dieser ausgewertet werden um z.B. doppeltes Starten zu verhindern. - Ueber EventCallback.ioname kann der Name des IO-Objekts abgerufen werden, - welches das Event ausgeloest hast. EventCallback.iovalue gibt den Wert des - IO-Objekts zum Ausloesezeitpunkt zurueck. - Der Thread stellt das EventCallback.exit Event als Abbruchbedingung fuer - die aufgerufene Funktion zur Verfuegung. - Durch Aufruf der Funktion EventCallback.stop() wird das exit-Event gesetzt - und kann bei Schleifen zum Abbrechen verwendet werden. - Mit dem .exit() Event auch eine Wartefunktion realisiert - werden: "th.exit.wait(0.5)" - Wartet 500ms oder bricht sofort ab, wenn - fuer den Thread .stop() aufgerufen wird. + The event function that this thread calls will receive the thread itself as a parameter. This must be considered when defining the function, e.g., "def event(th):". For extensive functions, this can be evaluated to prevent duplicate starts. + The name of the IO object can be retrieved via EventCallback.ioname, + which triggered the event. EventCallback.iovalue returns the value of the IO object at the time of triggering. + The thread provides the EventCallback.exit event as an abort condition for the called function. + By calling the EventCallback.stop() function, the exit event is set and can be used to abort loops. + A wait function can also be implemented with the .exit() event: "th.exit.wait(0.5)" - waits 500ms or aborts immediately if .stop() is called on the thread. while not th.exit.is_set(): # IO-Arbeiten @@ -44,9 +36,9 @@ class EventCallback(Thread): """ Init EventCallback class. - :param func: Funktion die beim Start aufgerufen werden soll + :param func: Function that should be called at startup :param name: IO-Name - :param value: IO-Value zum Zeitpunkt des Events + :param value: IO value at the time of the event """ super().__init__() self.daemon = True @@ -56,35 +48,33 @@ class EventCallback(Thread): self.iovalue = value def run(self): - """Ruft die registrierte Funktion auf.""" + """Calls the registered function.""" self.func(self) def stop(self): - """Setzt das exit-Event mit dem die Funktion beendet werden kann.""" + """Sets the exit event that can be used to terminate the function.""" self.exit.set() class Cycletools: """ - Werkzeugkasten fuer Cycleloop-Funktion. + Toolbox for cycle loop function. - Diese Klasse enthaelt Werkzeuge fuer Zyklusfunktionen, wie Taktmerker - und Flankenmerker. - Zu beachten ist, dass die Flankenmerker beim ersten Zyklus alle den Wert - True haben! Ueber den Merker Cycletools.first kann ermittelt werden, - ob es sich um den ersten Zyklus handelt. + This class contains tools for cycle functions, such as clock flags and edge flags. + Note that all edge flags have the value True on the first cycle! The Cycletools.first + flag can be used to determine if it is the first cycle. - Taktmerker flag1c, flag5c, flag10c, usw. haben den als Zahl angegebenen - Wert an Zyklen jeweils False und True. - Beispiel: flag5c hat 5 Zyklen den Wert False und in den naechsten 5 Zyklen - den Wert True. + Clock flags flag1c, flag5c, flag10c, etc. have the numerically specified + value for the specified number of cycles, alternating between False and True. - Flankenmerker flank5c, flank10c, usw. haben immer im, als Zahl angebenen - Zyklus fuer einen Zyklusdurchlauf den Wert True, sonst False. - Beispiel: flank5c hat immer alle 5 Zyklen den Wert True. + Example: flag5c has the value False for 5 cycles and True for the next 5 cycles. - Diese Merker koennen z.B. verwendet werden um, an Outputs angeschlossene, - Lampen synchron blinken zu lassen. + Edge flags flank5c, flank10c, etc. always have the value True for one cycle + at the numerically specified cycle, otherwise False. + + Example: flank5c always has the value True every 5 cycles. + + These flags can be used, for example, to make lamps connected to outputs blink synchronously. """ __slots__ = ( @@ -129,7 +119,7 @@ class Cycletools: self.device = revpi_object.device self.io = revpi_object.io - # Taktmerker + # Clock flags self.first = True self.flag1c = False self.flag5c = False @@ -138,28 +128,28 @@ class Cycletools: self.flag20c = False self.last = False - # Flankenmerker + # Edge flags self.flank5c = True self.flank10c = True self.flank15c = True self.flank20c = True - # Benutzerdaten + # User data class Var: - """Hier remanente Variablen anfuegen.""" + """Add remanent variables here.""" pass self.var = Var() def _docycle(self) -> None: - """Zyklusarbeiten.""" - # Einschaltverzoegerung + """Cycle operations.""" + # Turn-off delay for tof in self.__dict_tof: if self.__dict_tof[tof] > 0: self.__dict_tof[tof] -= 1 - # Ausschaltverzoegerung + # Turn-on delay for ton in self.__dict_ton: if self.__dict_ton[ton][1]: if self.__dict_ton[ton][0] > 0: @@ -168,7 +158,7 @@ class Cycletools: else: self.__dict_ton[ton][0] = -1 - # Impuls + # Pulse for tp in self.__dict_tp: if self.__dict_tp[tp][1]: if self.__dict_tp[tp][0] > 0: @@ -178,7 +168,7 @@ class Cycletools: else: self.__dict_tp[tp][0] = -1 - # Flankenmerker + # Edge flags self.flank5c = False self.flank10c = False self.flank15c = False @@ -188,7 +178,7 @@ class Cycletools: self.first = False self.flag1c = not self.flag1c - # Berechnete Flags + # Calculated flags self.__cycle += 1 if self.__cycle == 5: self.__ucycle += 1 @@ -239,64 +229,64 @@ class Cycletools: def get_tof(self, name: str) -> bool: """ - Wert der Ausschaltverzoegerung. + Value of the off-delay. - :param name: Eindeutiger Name des Timers - :return: Wert der Ausschaltverzoegerung + :param name: Unique name of the timer + :return: Value of the off-delay """ return self.__dict_tof.get(name, 0) > 0 def get_tofc(self, name: str) -> bool: """ - Wert der Ausschaltverzoegerung. + Value of the off-delay. - :param name: Eindeutiger Name des Timers - :return: Wert der Ausschaltverzoegerung + :param name: Unique name of the timer + :return: Value of the off-delay """ return self.__dict_tof.get(name, 0) > 0 def set_tof(self, name: str, milliseconds: int) -> None: """ - Startet bei Aufruf einen ausschaltverzoegerten Timer. + Starts an off-delay timer when called. - :param name: Eindeutiger Name fuer Zugriff auf Timer - :param milliseconds: Verzoegerung in Millisekunden + :param name: Unique name for accessing the timer + :param milliseconds: Delay in milliseconds """ self.__dict_tof[name] = ceil(milliseconds / self.__cycletime) def set_tofc(self, name: str, cycles: int) -> None: """ - Startet bei Aufruf einen ausschaltverzoegerten Timer. + Starts an off-delay timer when called. - :param name: Eindeutiger Name fuer Zugriff auf Timer - :param cycles: Zyklusanzahl, der Verzoegerung wenn nicht neu gestartet + :param name: Unique name for accessing the timer + :param cycles: Number of cycles for the delay if not restarted """ self.__dict_tof[name] = cycles def get_ton(self, name: str) -> bool: """ - Einschaltverzoegerung. + On-delay. - :param name: Eindeutiger Name des Timers - :return: Wert der Einschaltverzoegerung + :param name: Unique name of the timer + :return: Value of the on-delay """ return self.__dict_ton.get(name, [-1])[0] == 0 def get_tonc(self, name: str) -> bool: """ - Einschaltverzoegerung. + On-delay. - :param name: Eindeutiger Name des Timers - :return: Wert der Einschaltverzoegerung + :param name: Unique name of the timer + :return: Value of the on-delay """ return self.__dict_ton.get(name, [-1])[0] == 0 def set_ton(self, name: str, milliseconds: int) -> None: """ - Startet einen einschaltverzoegerten Timer. + Starts an on-delay timer. - :param name: Eindeutiger Name fuer Zugriff auf Timer - :param milliseconds: Millisekunden, der Verzoegerung wenn neu gestartet + :param name: Unique name for accessing the timer + :param milliseconds: Milliseconds for the delay if restarted """ if self.__dict_ton.get(name, [-1])[0] == -1: self.__dict_ton[name] = [ceil(milliseconds / self.__cycletime), True] @@ -305,10 +295,10 @@ class Cycletools: def set_tonc(self, name: str, cycles: int) -> None: """ - Startet einen einschaltverzoegerten Timer. + Starts an on-delay timer. - :param name: Eindeutiger Name fuer Zugriff auf Timer - :param cycles: Zyklusanzahl, der Verzoegerung wenn neu gestartet + :param name: Unique name for accessing the timer + :param cycles: Number of cycles for the delay if restarted """ if self.__dict_ton.get(name, [-1])[0] == -1: self.__dict_ton[name] = [cycles, True] @@ -317,28 +307,28 @@ class Cycletools: def get_tp(self, name: str) -> bool: """ - Impulstimer. + Pulse timer. - :param name: Eindeutiger Name des Timers - :return: Wert des Impulses + :param name: Unique name of the timer + :return: Value of the pulse """ return self.__dict_tp.get(name, [-1])[0] > 0 def get_tpc(self, name: str) -> bool: """ - Impulstimer. + Pulse timer. - :param name: Eindeutiger Name des Timers - :return: Wert des Impulses + :param name: Unique name of the timer + :return: Value of the pulse """ return self.__dict_tp.get(name, [-1])[0] > 0 def set_tp(self, name: str, milliseconds: int) -> None: """ - Startet einen Impuls Timer. + Starts a pulse timer. - :param name: Eindeutiger Name fuer Zugriff auf Timer - :param milliseconds: Millisekunden, die der Impuls anstehen soll + :param name: Unique name for accessing the timer + :param milliseconds: Milliseconds the pulse should be active """ if self.__dict_tp.get(name, [-1])[0] == -1: self.__dict_tp[name] = [ceil(milliseconds / self.__cycletime), True] @@ -347,10 +337,10 @@ class Cycletools: def set_tpc(self, name: str, cycles: int) -> None: """ - Startet einen Impuls Timer. + Starts a pulse timer. - :param name: Eindeutiger Name fuer Zugriff auf Timer - :param cycles: Zyklusanzahl, die der Impuls anstehen soll + :param name: Unique name for accessing the timer + :param cycles: Number of cycles the pulse should be active """ if self.__dict_tp.get(name, [-1])[0] == -1: self.__dict_tp[name] = [cycles, True] @@ -371,11 +361,9 @@ class Cycletools: class ProcimgWriter(Thread): """ - Klasse fuer Synchroniseriungs-Thread. + Class for synchronization thread. - Diese Klasse wird als Thread gestartet, wenn das Prozessabbild zyklisch - synchronisiert werden soll. Diese Funktion wird hauptsaechlich fuer das - Event-Handling verwendet. + This class is started as a thread if the process image should be synchronized cyclically. This function is mainly used for event handling. """ __slots__ = ( @@ -409,7 +397,7 @@ class ProcimgWriter(Thread): self.newdata = Event() def __check_change(self, dev) -> None: - """Findet Aenderungen fuer die Eventueberwachung.""" + """Finds changes for event monitoring.""" for io_event in dev._dict_events: if dev._ba_datacp[io_event._slc_address] == dev._ba_devdata[io_event._slc_address]: continue @@ -435,7 +423,7 @@ class ProcimgWriter(Thread): else: self._eventq.put((regfunc, io_event._name, io_event.value), False) else: - # Verzögertes Event in dict einfügen + # Insert delayed event into dict tup_fire = ( regfunc, io_event._name, @@ -454,7 +442,7 @@ class ProcimgWriter(Thread): else: self._eventq.put((regfunc, io_event._name, io_event.value), False) else: - # Verzögertes Event in dict einfügen + # Insert delayed event into dict tup_fire = ( regfunc, io_event._name, @@ -464,11 +452,11 @@ class ProcimgWriter(Thread): if regfunc.overwrite or tup_fire not in self.__dict_delay: self.__dict_delay[tup_fire] = ceil(regfunc.delay / 1000 / self._refresh) - # Nach Verarbeitung aller IOs die Bytes kopieren (Lock ist noch drauf) + # Copy the bytes after processing all IOs (lock is still active) dev._ba_datacp = dev._ba_devdata[:] def __exec_th(self) -> None: - """Laeuft als Thread, der Events als Thread startet.""" + """Runs as thread that starts events as threads.""" while self.__eventwork: try: tup_fireth = self._eventqth.get(timeout=1) @@ -480,15 +468,15 @@ class ProcimgWriter(Thread): def _collect_events(self, value: bool) -> bool: """ - Aktiviert oder Deaktiviert die Eventueberwachung. + Enables or disables event monitoring. - :param value: True aktiviert / False deaktiviert - :return: True, wenn Anforderung erfolgreich war + :param value: True activates / False deactivates + :return: True, if request was successful """ if type(value) != bool: raise TypeError("value must be ") - # Nur starten, wenn System läuft + # Only start if system is running if not self.is_alive(): self.__eventwork = False return False @@ -497,12 +485,12 @@ class ProcimgWriter(Thread): with self.lck_refresh: self.__eventwork = value if not value: - # Nur leeren beim deaktivieren + # Only empty when deactivating self._eventqth = queue.Queue() self._eventq = queue.Queue() self.__dict_delay = {} - # Threadmanagement + # Thread management if value and not self.__eventth.is_alive(): self.__eventth = Thread(target=self.__exec_th) self.__eventth.daemon = True @@ -512,14 +500,14 @@ class ProcimgWriter(Thread): def get_refresh(self) -> int: """ - Gibt Zykluszeit zurueck. + Returns cycle time. - :return: Zykluszeit in Millisekunden + :return: cycle time in milliseconds """ return int(self._refresh * 1000) def run(self): - """Startet die automatische Prozessabbildsynchronisierung.""" + """Starts automatic process image synchronization.""" fh = self._modio._create_myfh() mrk_delay = self._refresh @@ -537,7 +525,7 @@ class ProcimgWriter(Thread): RuntimeWarning, ) mrk_delay = self._refresh - # Nur durch cycleloop erreichbar - keine verzögerten Events + # Only reachable through cycleloop - no delayed events continue try: @@ -558,7 +546,7 @@ class ProcimgWriter(Thread): bytesbuff[dev._slc_devoff] = fh.read(len(dev._ba_devdata)) if self._modio._monitoring or dev._shared_procimg: - # Inputs und Outputs in Puffer + # Inputs and outputs in buffer dev._ba_devdata[:] = bytesbuff[dev._slc_devoff] if ( self.__eventwork @@ -567,7 +555,7 @@ class ProcimgWriter(Thread): ): self.__check_change(dev) else: - # Inputs in Puffer, Outputs in Prozessabbild + # Inputs in buffer, outputs in process image dev._ba_devdata[dev._slc_inp] = bytesbuff[dev._slc_inpoff] if ( self.__eventwork @@ -601,12 +589,12 @@ class ProcimgWriter(Thread): ) mrk_warn = True - # Alle aufwecken + # Wake all self.lck_refresh.release() self.newdata.set() finally: - # Verzögerte Events prüfen + # Check delayed events if self.__eventwork: for tup_fire in tuple(self.__dict_delay.keys()): if tup_fire[0].overwrite and tup_fire[3].value != tup_fire[2]: @@ -614,7 +602,7 @@ class ProcimgWriter(Thread): else: self.__dict_delay[tup_fire] -= 1 if self.__dict_delay[tup_fire] <= 0: - # Verzögertes Event übernehmen und löschen + # Accept and delete delayed event if tup_fire[0].as_thread: self._eventqth.put(tup_fire, False) else: @@ -633,18 +621,18 @@ class ProcimgWriter(Thread): # Sleep and not .wait (.wait uses system clock) sleep(self._refresh - mrk_delay) - # Alle am Ende erneut aufwecken + # Wake all again at the end self._collect_events(False) self.newdata.set() fh.close() def stop(self): - """Beendet die automatische Prozessabbildsynchronisierung.""" + """Terminates automatic process image synchronization.""" self._work.set() def set_refresh(self, value): - """Setzt die Zykluszeit in Millisekunden. - @param value Millisekunden""" + """Sets the cycle time in milliseconds. + @param value Milliseconds""" if type(value) == int and 5 <= value <= 2000: self._refresh = value / 1000 else: diff --git a/src/revpimodio2/io.py b/src/revpimodio2/io.py index 5aa46b8..632271a 100644 --- a/src/revpimodio2/io.py +++ b/src/revpimodio2/io.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""RevPiModIO Modul fuer die Verwaltung der IOs.""" +"""RevPiModIO module for managing IOs.""" __author__ = "Sven Sager" __copyright__ = "Copyright (C) 2023 Sven Sager" __license__ = "LGPLv2" @@ -12,14 +12,14 @@ from threading import Event from ._internal import consttostr, RISING, FALLING, BOTH, INP, OUT, MEM, PROCESS_IMAGE_SIZE try: - # Funktioniert nur auf Unix + # Only works on Unix from fcntl import ioctl except Exception: ioctl = None class IOEvent(object): - """Basisklasse fuer IO-Events.""" + """Base class for IO events.""" __slots__ = "as_thread", "delay", "edge", "func", "overwrite", "prefire" @@ -34,7 +34,7 @@ class IOEvent(object): class IOList(object): - """Basisklasse fuer direkten Zugriff auf IO Objekte.""" + """Base class for direct access to IO objects.""" def __init__(self, modio): """Init IOList class.""" @@ -44,10 +44,10 @@ class IOList(object): def __contains__(self, key): """ - Prueft ob IO existiert. + Checks if IO exists. - :param key: IO-Name oder Bytenummer - :return: True, wenn IO vorhanden / Byte belegt + :param key: IO name or byte number + :return: True if IO exists / byte is occupied """ if type(key) == int: return len(self.__dict_iobyte.get(key, [])) > 0 @@ -56,16 +56,16 @@ class IOList(object): def __delattr__(self, key): """ - Entfernt angegebenen IO. + Removes specified IO. - :param key: IO zum entfernen + :param key: IO to remove """ io_del = object.__getattribute__(self, key) - # Alte Events vom Device löschen + # Delete old events from device io_del.unreg_event() - # IO aus Byteliste und Attributen entfernen + # Remove IO from byte list and attributes if io_del._bitshift: self.__dict_iobyte[io_del.address][io_del._bitaddress] = None @@ -128,10 +128,10 @@ class IOList(object): def __getattr__(self, key): """ - Verwaltet geloeschte IOs (Attribute, die nicht existieren). + Manages deleted IOs (attributes that do not exist). - :param key: Name oder Byte eines alten IOs - :return: Alten IO, wenn in Ref-Listen + :param key: Name or byte of an old IO + :return: Old IO if in ref lists """ if key in self.__dict_iorefname: return self.__dict_iorefname[key] @@ -140,16 +140,15 @@ class IOList(object): def __getitem__(self, key): """ - Ruft angegebenen IO ab. + Retrieves specified IO. - Wenn der Key ist, wird ein einzelner IO geliefert. Wird - der Key als uebergeben, wird eine - geliefert mit 0, 1 oder 8 Eintraegen. - Wird als Key gegeben, werden die Listen in einer Liste - zurueckgegeben. + If the key is , a single IO is returned. If the key + is passed as , a is returned with 0, 1 + or 8 entries. If a is given as key, the lists are + returned in a list. - :param key: IO Name als oder Byte als . - :return: IO Objekt oder Liste der IOs + :param key: IO name as or byte as . + :return: IO object or list of IOs """ if type(key) == int: if key not in self.__dict_iobyte: @@ -165,9 +164,9 @@ class IOList(object): def __iter__(self): """ - Gibt Iterator aller IOs zurueck. + Returns iterator of all IOs. - :return: Iterator aller IOs + :return: Iterator of all IOs """ for int_io in sorted(self.__dict_iobyte): for io in self.__dict_iobyte[int_io]: @@ -176,9 +175,9 @@ class IOList(object): def __len__(self): """ - Gibt die Anzahl aller IOs zurueck. + Returns the number of all IOs. - :return: Anzahl aller IOs + :return: Number of all IOs """ int_ios = 0 for int_io in self.__dict_iobyte: @@ -188,7 +187,7 @@ class IOList(object): return int_ios def __setattr__(self, key, value): - """Verbietet aus Leistungsguenden das direkte Setzen von Attributen.""" + """Prohibits direct setting of attributes for performance reasons.""" if key in ( "_IOList__dict_iobyte", "_IOList__dict_iorefname", @@ -200,11 +199,11 @@ class IOList(object): def __private_replace_oldio_with_newio(self, io) -> None: """ - Ersetzt bestehende IOs durch den neu Registrierten. + Replaces existing IOs with the newly registered one. - :param io: Neuer IO der eingefuegt werden soll + :param io: New IO to be inserted """ - # Scanbereich festlegen + # Define scan range if io._bitshift: scan_start = io._parentio_address scan_stop = scan_start + io._parentio_length @@ -212,13 +211,13 @@ class IOList(object): scan_start = io.address scan_stop = scan_start + (1 if io._length == 0 else io._length) - # Defaultvalue über mehrere Bytes sammeln + # Collect default value over multiple bytes calc_defaultvalue = b"" for i in range(scan_start, scan_stop): for oldio in self.__dict_iobyte[i]: if type(oldio) == StructIO: - # Hier gibt es schon einen neuen IO + # There is already a new IO here if oldio._bitshift: if ( io._bitshift == oldio._bitshift @@ -230,28 +229,28 @@ class IOList(object): ) ) else: - # Bereits überschriebene bytes sind ungültig + # Already overwritten bytes are invalid raise MemoryError( "new io '{0}' overlaps memory of '{1}'".format(io._name, oldio._name) ) elif oldio is not None: - # IOs im Speicherbereich des neuen IO merken + # Remember IOs in the memory area of the new IO if io._bitshift: - # ios für ref bei bitaddress speichern + # Store IOs for ref at bitaddress self.__dict_iorefname[oldio._name] = DeadIO(oldio) else: - # Defaultwert berechnen + # Calculate default value oldio.byteorder = io._byteorder if io._byteorder == "little": calc_defaultvalue += oldio._defaultvalue else: calc_defaultvalue = oldio._defaultvalue + calc_defaultvalue - # ios aus listen entfernen + # Remove IOs from lists delattr(self, oldio._name) if io._defaultvalue is None: - # Nur bei StructIO und keiner gegebenen defaultvalue übernehmen + # Only take over for StructIO and no given defaultvalue if io._bitshift: io_byte_address = io._parentio_address - io.address io._defaultvalue = bool(io._parentio_defaultvalue[io_byte_address] & io._bitshift) @@ -260,9 +259,9 @@ class IOList(object): def _private_register_new_io_object(self, new_io) -> None: """ - Registriert neues IO Objekt unabhaenging von __setattr__. + Registers new IO object independently of __setattr__. - :param new_io: Neues IO Objekt + :param new_io: New IO object """ if isinstance(new_io, IOBase): if hasattr(self, new_io._name): @@ -274,10 +273,10 @@ class IOList(object): if do_replace: self.__private_replace_oldio_with_newio(new_io) - # Bytedict für Adresszugriff anpassen + # Adapt byte dict for address access if new_io._bitshift: if len(self.__dict_iobyte[new_io.address]) != 8: - # "schnell" 8 Einträge erstellen da es BIT IOs sind + # "Quickly" create 8 entries since these are BIT IOs self.__dict_iobyte[new_io.address] += [ None, None, @@ -343,21 +342,21 @@ class IOList(object): class DeadIO(object): - """Klasse, mit der ersetzte IOs verwaltet werden.""" + """Class for managing replaced IOs.""" __slots__ = "__deadio" def __init__(self, deadio): """ - Instantiierung der DeadIO-Klasse. + Instantiation of the DeadIO class. - :param deadio: IO, der ersetzt wurde + :param deadio: IO that was replaced """ self.__deadio = deadio def replace_io(self, name: str, frm: str, **kwargs) -> None: """ - Stellt Funktion fuer weiter Bit-Ersetzungen bereit. + Provides function for further bit replacements. :ref: :func:IntIOReplaceable.replace_io() """ @@ -368,16 +367,15 @@ class DeadIO(object): class IOBase(object): """ - Basisklasse fuer alle IO-Objekte. + Base class for all IO objects. - Die Basisfunktionalitaet ermoeglicht das Lesen und Schreiben der Werte - als oder . Dies entscheidet sich bei der - Instantiierung. - Wenn eine Bittadresse angegeben wird, werden -Werte erwartet - und zurueckgegeben, ansonsten . + The basic functionality enables reading and writing of values as + or . This is decided during instantiation. + If a bit address is specified, values are expected and + returned, otherwise . - Diese Klasse dient als Basis fuer andere IO-Klassen mit denen die Werte - auch als verwendet werden koennen. + This class serves as a basis for other IO classes with which the values + can also be used as . """ __slots__ = ( @@ -401,24 +399,24 @@ class IOBase(object): def __init__(self, parentdevice, valuelist: list, iotype: int, byteorder: str, signed: bool): """ - Instantiierung der IOBase-Klasse. + Instantiation of the IOBase class. - :param parentdevice: Parentdevice auf dem der IO liegt - :param valuelist: Datenliste fuer Instantiierung + :param parentdevice: Parent device on which the IO is located + :param valuelist: Data list for instantiation ["name","defval","bitlen","startaddrdev",exp,"idx","bmk","bitaddr"] - :param iotype: Wert - :param byteorder: Byteorder 'little'/'big' fuer Berechnung - :param signed: Intberechnung mit Vorzeichen durchfuehren + :param iotype: value + :param byteorder: Byteorder 'little'/'big' for calculation + :param signed: Perform int calculation with sign """ # ["name","defval","bitlen","startaddrdev",exp,"idx","bmk","bitaddr"] # [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] self._parentdevice = parentdevice - # Bitadressen auf Bytes aufbrechen und umrechnen + # Break down bit addresses to bytes and convert self._bitaddress = -1 if valuelist[7] == "" else int(valuelist[7]) % 8 self._bitshift = None if self._bitaddress == -1 else 1 << self._bitaddress - # Längenberechnung + # Length calculation self._bitlength = int(valuelist[2]) self._length = 1 if self._bitaddress == 0 else int(self._bitlength / 8) @@ -434,11 +432,11 @@ class IOBase(object): int_startaddress = int(valuelist[3]) if self._bitshift: - # Höhere Bits als 7 auf nächste Bytes umbrechen + # Wrap bits higher than 7 to next bytes int_startaddress += int(int(valuelist[7]) / 8) self._slc_address = slice(int_startaddress, int_startaddress + 1) - # Defaultvalue ermitteln, sonst False + # Determine default value, otherwise False if valuelist[1] is None and type(self) == StructIO: self._defaultvalue = None else: @@ -447,21 +445,21 @@ class IOBase(object): except Exception: self._defaultvalue = False - # Ioctl für Bitsetzung setzen + # Set ioctl for bit setting self.__bit_ioctl_off = struct.pack("-Wert der Klasse. + value of the class. - :return: Nur False wenn False oder 0 sonst True + :return: Only False if False or 0, otherwise True """ if self._bitshift: return bool(self._parentdevice._ba_devdata[self._slc_address.start] & self._bitshift) @@ -508,17 +506,17 @@ class IOBase(object): def __len__(self): """ - Gibt die Bytelaenge des IO zurueck. + Returns the byte length of the IO. - :return: Bytelaenge des IO - 0 bei BITs + :return: Byte length of the IO - 0 for BITs """ return 0 if self._bitaddress > 0 else self._length def __str__(self): """ - -Wert der Klasse. + value of the class. - :return: Namen des IOs + :return: Name of the IO """ return self._name @@ -526,16 +524,16 @@ class IOBase(object): self, func, delay: int, edge: int, as_thread: bool, overwrite: bool, prefire: bool ) -> None: """ - Verwaltet reg_event und reg_timerevent. + Manages reg_event and reg_timerevent. - :param func: Funktion die bei Aenderung aufgerufen werden soll - :param delay: Verzoegerung in ms zum Ausloesen - auch bei Wertaenderung - :param edge: Ausfuehren bei RISING, FALLING or BOTH Wertaenderung - :param as_thread: Bei True, Funktion als EventCallback-Thread ausfuehren - :param overwrite: Wenn True, wird Event bei ueberschrieben - :param prefire: Ausloesen mit aktuellem Wert, wenn mainloop startet + :param func: Function to be called on change + :param delay: Delay in ms for triggering - also on value change + :param edge: Execute on RISING, FALLING or BOTH value change + :param as_thread: If True, execute function as EventCallback thread + :param overwrite: If True, event will be overwritten + :param prefire: Trigger with current value when mainloop starts """ - # Prüfen ob Funktion callable ist + # Check if function is callable if not callable(func): raise ValueError("registered function '{0}' is not callable".format(func)) if type(delay) != int or delay < 0: @@ -551,10 +549,10 @@ class IOBase(object): IOEvent(func, edge, as_thread, delay, overwrite, prefire) ] else: - # Prüfen ob Funktion schon registriert ist + # Check if function is already registered for regfunc in self._parentdevice._dict_events[self]: if regfunc.func != func: - # Nächsten Eintrag testen + # Test next entry continue if edge == BOTH or regfunc.edge == BOTH: @@ -576,7 +574,7 @@ class IOBase(object): "already in list".format(self._name, func, consttostr(edge)) ) - # Eventfunktion einfügen + # Insert event function with self._parentdevice._filelock: self._parentdevice._dict_events[self].append( IOEvent(func, edge, as_thread, delay, overwrite, prefire) @@ -584,15 +582,15 @@ class IOBase(object): def _get_address(self) -> int: """ - Gibt die absolute Byteadresse im Prozessabbild zurueck. + Returns the absolute byte address in the process image. - :return: Absolute Byteadresse + :return: Absolute byte address """ return self._parentdevice._offset + self._slc_address.start def _get_byteorder(self) -> str: """ - Gibt konfigurierte Byteorder zurueck. + Returns configured byteorder. :return: Byteorder """ @@ -604,7 +602,7 @@ class IOBase(object): def _get_iotype(self) -> int: """ - Gibt io type zurueck. + Returns io type. :return: io type """ @@ -631,10 +629,10 @@ class IOBase(object): # Write single bit to process image value = self._parentdevice._ba_devdata[self._slc_address.start] & self._bitshift if self._parentdevice._modio._run_on_pi: - # IOCTL auf dem RevPi + # IOCTL on the RevPi with self._parentdevice._modio._myfh_lck: try: - # Set value durchführen (Funktion K+16) + # Perform set value (function K+16) ioctl( self._parentdevice._modio._myfh, 19216, @@ -645,7 +643,7 @@ class IOBase(object): return False elif hasattr(self._parentdevice._modio._myfh, "ioctl"): - # IOCTL über Netzwerk + # IOCTL over network with self._parentdevice._modio._myfh_lck: try: self._parentdevice._modio._myfh.ioctl( @@ -657,9 +655,9 @@ class IOBase(object): return False else: - # IOCTL in Datei simulieren + # Simulate IOCTL in file try: - # Set value durchführen (Funktion K+16) + # Execute set value (function K+16) self._parentdevice._modio._simulate_ioctl( 19216, self.__bit_ioctl_on if value else self.__bit_ioctl_off, @@ -685,17 +683,17 @@ class IOBase(object): def get_defaultvalue(self): """ - Gibt die Defaultvalue von piCtory zurueck. + Returns the default value from piCtory. - :return: Defaultvalue als oder + :return: Default value as or """ return self._defaultvalue def get_value(self): """ - Gibt den Wert des IOs zurueck. + Returns the value of the IO. - :return: IO-Wert als oder + :return: IO value as or """ if self._bitshift: return bool(self._parentdevice._ba_devdata[self._slc_address.start] & self._bitshift) @@ -704,50 +702,49 @@ class IOBase(object): def reg_event(self, func, delay=0, edge=BOTH, as_thread=False, prefire=False): """ - Registriert fuer IO ein Event bei der Eventueberwachung. + Registers an event for the IO in the event monitoring. - Die uebergebene Funktion wird ausgefuehrt, wenn sich der IO Wert - aendert. Mit Angabe von optionalen Parametern kann das - Ausloeseverhalten gesteuert werden. + The passed function is executed when the IO value changes. With + specification of optional parameters, the trigger behavior can be + controlled. - HINWEIS: Die delay-Zeit muss in die .cycletime passen, ist dies nicht - der Fall, wird IMMER aufgerundet! + NOTE: The delay time must fit into .cycletime, if not, it will + ALWAYS be rounded up! - :param func: Funktion die bei Aenderung aufgerufen werden soll - :param delay: Verzoegerung in ms zum Ausloesen wenn Wert gleich bleibt - :param edge: Ausfuehren bei RISING, FALLING or BOTH Wertaenderung - :param as_thread: Bei True, Funktion als EventCallback-Thread ausfuehren - :param prefire: Ausloesen mit aktuellem Wert, wenn mainloop startet + :param func: Function to be called on change + :param delay: Delay in ms for triggering if value stays the same + :param edge: Execute on RISING, FALLING or BOTH value change + :param as_thread: If True, execute function as EventCallback thread + :param prefire: Trigger with current value when mainloop starts """ self.__reg_xevent(func, delay, edge, as_thread, True, prefire) def reg_timerevent(self, func, delay, edge=BOTH, as_thread=False, prefire=False): """ - Registriert fuer IO einen Timer, welcher nach delay func ausfuehrt. + Registers a timer for the IO which executes func after delay. - Der Timer wird gestartet, wenn sich der IO Wert aendert und fuehrt die - uebergebene Funktion aus - auch wenn sich der IO Wert in der - zwischenzeit geaendert hat. Sollte der Timer nicht abelaufen sein und - die Bedingugn erneut zutreffen, wird der Timer NICHT auf den delay Wert - zurueckgesetzt oder ein zweites Mal gestartet. Fuer dieses Verhalten - kann .reg_event(..., delay=wert) verwendet werden. + The timer is started when the IO value changes and executes the passed + function - even if the IO value has changed in the meantime. If the + timer has not expired and the condition is met again, the timer is NOT + reset to the delay value or started a second time. For this behavior, + .reg_event(..., delay=value) can be used. - HINWEIS: Die delay-Zeit muss in die .cycletime passen, ist dies nicht - der Fall, wird IMMER aufgerundet! + NOTE: The delay time must fit into .cycletime, if not, it will + ALWAYS be rounded up! - :param func: Funktion die bei Aenderung aufgerufen werden soll - :param delay: Verzoegerung in ms zum Ausloesen - auch bei Wertaenderung - :param edge: Ausfuehren bei RISING, FALLING or BOTH Wertaenderung - :param as_thread: Bei True, Funktion als EventCallback-Thread ausfuehren - :param prefire: Ausloesen mit aktuellem Wert, wenn mainloop startet + :param func: Function to be called on change + :param delay: Delay in ms for triggering - also on value change + :param edge: Execute on RISING, FALLING or BOTH value change + :param as_thread: If True, execute function as EventCallback thread + :param prefire: Trigger with current value when mainloop starts """ self.__reg_xevent(func, delay, edge, as_thread, False, prefire) def set_value(self, value) -> None: """ - Setzt den Wert des IOs. + Sets the value of the IO. - :param value: IO-Wert als oder + :param value: IO value as or """ if self._read_only_io: if self._iotype == INP: @@ -762,27 +759,27 @@ class IOBase(object): raise RuntimeError("the io object '{0}' is read only".format(self._name)) if self._bitshift: - # Versuchen egal welchen Typ in Bool zu konvertieren + # Try to convert any type to bool value = bool(value) - # Für Bitoperationen sperren + # Lock for bit operations self._parentdevice._filelock.acquire() if self._parentdevice._shared_procimg: # Mark this IO for write operations self._parentdevice._shared_write.add(self) - # Hier gibt es immer nur ein byte, als int holen + # There is always only one byte here, get as int int_byte = self._parentdevice._ba_devdata[self._slc_address.start] - # Aktuellen Wert vergleichen und ggf. setzen + # Compare current value and set if necessary if not bool(int_byte & self._bitshift) == value: if value: int_byte += self._bitshift else: int_byte -= self._bitshift - # Zurückschreiben wenn verändert + # Write back if changed self._parentdevice._ba_devdata[self._slc_address.start] = int_byte self._parentdevice._filelock.release() @@ -810,10 +807,10 @@ class IOBase(object): def unreg_event(self, func=None, edge=None) -> None: """ - Entfernt ein Event aus der Eventueberwachung. + Removes an event from event monitoring. - :param func: Nur Events mit angegebener Funktion - :param edge: Nur Events mit angegebener Funktion und angegebener Edge + :param func: Only events with specified function + :param edge: Only events with specified function and specified edge """ if self in self._parentdevice._dict_events: if func is None: @@ -825,7 +822,7 @@ class IOBase(object): if regfunc.func != func or edge is not None and regfunc.edge != edge: newlist.append(regfunc) - # Wenn Funktionen übrig bleiben, diese übernehmen + # If functions remain, take them over with self._parentdevice._filelock: if len(newlist) > 0: self._parentdevice._dict_events[self] = newlist @@ -834,48 +831,48 @@ class IOBase(object): def wait(self, edge=BOTH, exitevent=None, okvalue=None, timeout=0) -> int: """ - Wartet auf Wertaenderung eines IOs. + Waits for value change of an IO. - Die Wertaenderung wird immer uerberprueft, wenn fuer Devices - mit aktiviertem autorefresh neue Daten gelesen wurden. + The value change is always checked when new data has been read for devices + with autorefresh enabled. - Bei Wertaenderung, wird das Warten mit 0 als Rueckgabewert beendet. + On value change, waiting ends with 0 as return value. - HINWEIS: Wenn keine neuen Daten liefert, wird - bis in die Ewigkeit gewartet (nicht bei Angabe von "timeout"). + NOTE: If does not deliver new data, + it will wait forever (not when "timeout" is specified). - Wenn edge mit RISING oder FALLING angegeben wird, muss diese Flanke - ausgeloest werden. Sollte der Wert 1 sein beim Eintritt mit Flanke - RISING, wird das Warten erst bei Aenderung von 0 auf 1 beendet. + If edge is specified with RISING or FALLING, this edge must be + triggered. If the value is 1 when entering with edge + RISING, the wait will only end when changing from 0 to 1. - Als exitevent kann ein -Objekt uebergeben - werden, welches das Warten bei is_set() sofort mit 1 als Rueckgabewert - beendet. + A object can be passed as exitevent, + which ends the waiting immediately with 1 as return value + when is_set(). - Wenn der Wert okvalue an dem IO fuer das Warten anliegt, wird - das Warten sofort mit -1 als Rueckgabewert beendet. + If the value okvalue is present at the IO for waiting, the + waiting ends immediately with -1 as return value. - Der Timeoutwert bricht beim Erreichen das Warten sofort mit - Wert 2 Rueckgabewert ab. (Das Timeout wird ueber die Zykluszeit - der autorefresh Funktion berechnet, entspricht also nicht exakt den - angegeben Millisekunden! Es wird immer nach oben gerundet!) + The timeout value aborts the waiting immediately when reached with + value 2 as return value. (The timeout is calculated via the cycle time + of the autorefresh function, so it does not correspond exactly to the + specified milliseconds! It is always rounded up!) - :param edge: Flanke RISING, FALLING, BOTH die eintreten muss - :param exitevent: fuer vorzeitiges Beenden - :param okvalue: IO-Wert, bei dem das Warten sofort beendet wird - :param timeout: Zeit in ms nach der abgebrochen wird - :return: erfolgreich Werte <= 0 + :param edge: Edge RISING, FALLING, BOTH that must occur + :param exitevent: for early termination + :param okvalue: IO value at which waiting ends immediately + :param timeout: Time in ms after which to abort + :return: successful values <= 0 - - Erfolgreich gewartet - - Wert 0: IO hat den Wert gewechselt - - Wert -1: okvalue stimmte mit IO ueberein - - Fehlerhaft gewartet - - Wert 1: exitevent wurde gesetzt - - Wert 2: timeout abgelaufen - - Wert 100: Devicelist.exit() wurde aufgerufen + - Successfully waited + - Value 0: IO has changed value + - Value -1: okvalue matched IO + - Erroneously waited + - Value 1: exitevent was set + - Value 2: timeout expired + - Value 100: Devicelist.exit() was called """ - # Prüfen ob Device in autorefresh ist + # Check if device is in autorefresh if not self._parentdevice._selfupdate: raise RuntimeError( "autorefresh is not activated for device '{0}|{1}' - there " @@ -895,7 +892,7 @@ class IOBase(object): if edge != BOTH and not self._bitshift: raise ValueError("parameter 'edge' can be used with bit Inputs only") - # Abbruchwert prüfen + # Check abort value if okvalue == self.value: return -1 @@ -933,15 +930,15 @@ class IOBase(object): elif bool_timecount: flt_timecount += 2.5 - # Abbruchevent wurde gesetzt + # Abort event was set if exitevent.is_set(): return 1 - # RevPiModIO mainloop wurde verlassen + # RevPiModIO mainloop was exited if self._parentdevice._modio._waitexit.is_set(): return 100 - # Timeout abgelaufen + # Timeout expired return 2 address = property(_get_address) @@ -956,12 +953,12 @@ class IOBase(object): class IntIO(IOBase): """ - Klasse fuer den Zugriff auf die Daten mit Konvertierung in int. + Class for accessing data with conversion to int. - Diese Klasse erweitert die Funktion von um Funktionen, - ueber die mit Werten gearbeitet werden kann. Fuer die - Umwandlung koennen 'Byteorder' (Default 'little') und 'signed' (Default - False) als Parameter gesetzt werden. + This class extends the functionality of with functions + for working with values. For the + conversion, 'byteorder' (default 'little') and 'signed' (default + False) can be set as parameters. :ref: :class:`IOBase` """ @@ -970,9 +967,9 @@ class IntIO(IOBase): def __int__(self): """ - Gibt IO-Wert zurueck mit Beachtung byteorder/signed. + Returns IO value considering byteorder/signed. - :return: IO-Wert als + :return: IO value as """ return int.from_bytes( self._parentdevice._ba_devdata[self._slc_address], @@ -1006,15 +1003,15 @@ class IntIO(IOBase): def _get_signed(self) -> bool: """ - Ruft ab, ob der Wert Vorzeichenbehaftet behandelt werden soll. + Retrieves whether the value should be treated as signed. - :return: True, wenn Vorzeichenbehaftet + :return: True if signed """ return self._signed def _set_byteorder(self, value: str) -> None: """ - Setzt Byteorder fuer Umwandlung. + Sets byteorder for conversion. :param value: 'little' or 'big' """ @@ -1026,9 +1023,9 @@ class IntIO(IOBase): def _set_signed(self, value: bool) -> None: """ - Left fest, ob der Wert Vorzeichenbehaftet behandelt werden soll. + Sets whether the value should be treated as signed. - :param value: True, wenn mit Vorzeichen behandel + :param value: True if to be treated as signed """ if type(value) != bool: raise TypeError("signed must be True or False") @@ -1036,17 +1033,17 @@ class IntIO(IOBase): def get_intdefaultvalue(self) -> int: """ - Gibt die Defaultvalue als zurueck. + Returns the default value as . - :return: Defaultvalue + :return: Default value """ return int.from_bytes(self._defaultvalue, byteorder=self._byteorder, signed=self._signed) def get_intvalue(self) -> int: """ - Gibt IO-Wert zurueck mit Beachtung byteorder/signed. + Returns IO value considering byteorder/signed. - :return: IO-Wert als + :return: IO value as """ return int.from_bytes( self._parentdevice._ba_devdata[self._slc_address], @@ -1056,9 +1053,9 @@ class IntIO(IOBase): def set_intvalue(self, value: int) -> None: """ - Setzt IO mit Beachtung byteorder/signed. + Sets IO considering byteorder/signed. - :param value: Wert + :param value: Value """ if type(value) == int: self.set_value( @@ -1081,15 +1078,15 @@ class IntIO(IOBase): class IntIOCounter(IntIO): - """Erweitert die IntIO-Klasse um die .reset() Funktion fuer Counter.""" + """Extends the IntIO class with the .reset() function for counters.""" __slots__ = ("__ioctl_arg",) def __init__(self, counter_id, parentdevice, valuelist, iotype, byteorder, signed): """ - Instantiierung der IntIOCounter-Klasse. + Instantiation of the IntIOCounter class. - :param counter_id: ID fuer den Counter, zu dem der IO gehoert (0-15) + :param counter_id: ID for the counter to which the IO belongs (0-15) :ref: :func:`IOBase.__init__(...)` """ if not isinstance(counter_id, int): @@ -1097,7 +1094,7 @@ class IntIOCounter(IntIO): if not 0 <= counter_id <= 15: raise ValueError("counter_id must be 0 - 15") - # Deviceposition + leer + Counter_ID + # Device position + empty + Counter_ID # ID-Bits: 7|6|5|4|3|2|1|0|15|14|13|12|11|10|9|8 self.__ioctl_arg = ( parentdevice._position.to_bytes(1, "little") @@ -1106,9 +1103,9 @@ class IntIOCounter(IntIO): ) """ - IOCTL fuellt dieses struct, welches durch padding im Speicher nach - uint8_t ein byte frei hat. Es muessen also 4 Byte uebergeben werden - wobei das Bitfield die Byteorder little hat!!! + IOCTL fills this struct, which has one byte free in memory after + uint8_t due to padding. Therefore 4 bytes must be passed + where the bitfield has little byteorder!!! typedef struct SDIOResetCounterStr { @@ -1117,27 +1114,27 @@ class IntIOCounter(IntIO): } SDIOResetCounter; """ - # Basisklasse laden + # Load base class super().__init__(parentdevice, valuelist, iotype, byteorder, signed) def reset(self) -> None: - """Setzt den Counter des Inputs zurueck.""" + """Resets the counter of the input.""" if self._parentdevice._modio._monitoring: raise RuntimeError("can not reset counter, while system is in monitoring mode") if self._parentdevice._modio._simulator: raise RuntimeError("can not reset counter, while system is in simulator mode") if self._parentdevice._modio._run_on_pi: - # IOCTL auf dem RevPi + # IOCTL on the RevPi with self._parentdevice._modio._myfh_lck: try: - # Counter reset durchführen (Funktion K+20) + # Execute counter reset (function K+20) ioctl(self._parentdevice._modio._myfh, 19220, self.__ioctl_arg) except Exception as e: self._parentdevice._modio._gotioerror("iorst", e) elif hasattr(self._parentdevice._modio._myfh, "ioctl"): - # IOCTL über Netzwerk + # IOCTL over network with self._parentdevice._modio._myfh_lck: try: self._parentdevice._modio._myfh.ioctl(19220, self.__ioctl_arg) @@ -1145,62 +1142,62 @@ class IntIOCounter(IntIO): self._parentdevice._modio._gotioerror("net_iorst", e) else: - # IOCTL in Datei simulieren + # Simulate IOCTL in file try: - # Set value durchführen (Funktion K+20) + # Execute set value (function K+20) self._parentdevice._modio._simulate_ioctl(19220, self.__ioctl_arg) except Exception as e: self._parentdevice._modio._gotioerror("file_iorst", e) class IntIOReplaceable(IntIO): - """Erweitert die IntIO-Klasse um die .replace_io Funktion.""" + """Extends the IntIO class with the .replace_io function.""" __slots__ = () def replace_io(self, name: str, frm: str, **kwargs) -> None: """ - Ersetzt bestehenden IO mit Neuem. + Replaces existing IO with new one. - Wenn die kwargs fuer byteorder und defaultvalue nicht angegeben werden, - uebernimmt das System die Daten aus dem ersetzten IO. + If the kwargs for byteorder and defaultvalue are not specified, + the system takes the data from the replaced IO. - Es darf nur ein einzelnes Formatzeichen 'frm' uebergeben werden. Daraus - wird dann die benoetigte Laenge an Bytes berechnet und der Datentyp - festgelegt. Moeglich sind: + Only a single format character 'frm' may be passed. From this, + the required length in bytes is calculated and the data type + is determined. Possible values are: - Bits / Bytes: ?, c, s - Integer : bB, hH, iI, lL, qQ - Float : e, f, d - Eine Ausnahme ist die Formatierung 's'. Hier koennen mehrere Bytes - zu einem langen IO zusammengefasst werden. Die Formatierung muss - '8s' fuer z.B. 8 Bytes sein - NICHT 'ssssssss'! + An exception is the 's' format. Here, multiple bytes + can be combined into one long IO. The formatting must be + '8s' for e.g. 8 bytes - NOT 'ssssssss'! - Wenn durch die Formatierung mehr Bytes benoetigt werden, als - der urspruenglige IO hat, werden die nachfolgenden IOs ebenfalls - verwendet und entfernt. + If more bytes are needed by the formatting than + the original IO has, the following IOs will also be + used and removed. - :param name: Name des neuen Inputs - :param frm: struct formatierung (1 Zeichen) oder 'ANZAHLs' z.B. '8s' - :param kwargs: Weitere Parameter + :param name: Name of the new input + :param frm: struct formatting (1 character) or 'NUMBERs' e.g. '8s' + :param kwargs: Additional parameters - - bmk: interne Bezeichnung fuer IO - - bit: Registriert IO als am angegebenen Bit im Byte - - byteorder: Byteorder fuer den IO, Standardwert=little - - wordorder: Wordorder wird vor byteorder angewendet - - defaultvalue: Standardwert fuer IO - - event: Funktion fuer Eventhandling registrieren - - delay: Verzoegerung in ms zum Ausloesen wenn Wert gleich bleibt - - edge: Event ausfuehren bei RISING, FALLING or BOTH Wertaenderung - - as_thread: Fuehrt die event-Funktion als RevPiCallback-Thread aus - - prefire: Ausloesen mit aktuellem Wert, wenn mainloop startet + - bmk: internal designation for IO + - bit: Registers IO as at the specified bit in the byte + - byteorder: Byteorder for the IO, default=little + - wordorder: Wordorder is applied before byteorder + - defaultvalue: Default value for IO + - event: Register function for event handling + - delay: Delay in ms for triggering when value remains the same + - edge: Execute event on RISING, FALLING or BOTH value change + - as_thread: Executes the event function as RevPiCallback thread + - prefire: Trigger with current value when mainloop starts ``_ """ - # StructIO erzeugen + # Create StructIO io_new = StructIO(self, name, frm, **kwargs) - # StructIO in IO-Liste einfügen + # Insert StructIO into IO list self._parentdevice._modio.io._private_register_new_io_object(io_new) # Optional Event eintragen @@ -1309,7 +1306,7 @@ class RelaisOutput(IOBase): 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 + # Increase bit-address by 1 to ignore first 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) @@ -1335,10 +1332,10 @@ class IntRelaisOutput(IntIO, RelaisOutput): class StructIO(IOBase): """ - Klasse fuer den Zugriff auf Daten ueber ein definierten struct. + Class for accessing data via a defined struct. - Sie stellt ueber struct die Werte in der gewuenschten Formatierung - bereit. Der struct-Formatwert wird bei der Instantiierung festgelegt. + It provides the values in the desired formatting via struct. + The struct format value is defined during instantiation. """ __slots__ = ( @@ -1352,30 +1349,30 @@ class StructIO(IOBase): def __init__(self, parentio, name: str, frm: str, **kwargs): """ - Erstellt einen IO mit struct-Formatierung. + Creates an IO with struct formatting. - :param parentio: ParentIO Objekt, welches ersetzt wird - :param name: Name des neuen IO - :param frm: struct formatierung (1 Zeichen) oder 'ANZAHLs' z.B. '8s' - :param kwargs: Weitere Parameter: - - bmk: Bezeichnung fuer IO - - bit: Registriert IO als am angegebenen Bit im Byte - - byteorder: Byteorder fuer IO, Standardwert vom ersetzten IO - - wordorder: Wordorder wird vor byteorder angewendet - - defaultvalue: Standardwert fuer IO, Standard vom ersetzten IO + :param parentio: ParentIO object that will be replaced + :param name: Name of the new IO + :param frm: struct formatting (1 character) or 'NUMBERs' e.g. '8s' + :param kwargs: Additional parameters: + - bmk: Description for IO + - bit: Registers IO as at specified bit in byte + - byteorder: Byteorder for IO, default from replaced IO + - wordorder: Wordorder is applied before byteorder + - defaultvalue: Default value for IO, default from replaced IO """ - # Structformatierung prüfen + # Check struct formatting regex = rematch("^([0-9]*s|[cbB?hHiIlLqQefd])$", frm) if regex is not None: - # Byteorder prüfen und übernehmen + # Check and take over byteorder byteorder = kwargs.get("byteorder", parentio._byteorder) if byteorder not in ("little", "big"): raise ValueError("byteorder must be 'little' or 'big'") bofrm = "<" if byteorder == "little" else ">" self._wordorder = kwargs.get("wordorder", None) - # Namen des parent fuer export merken + # Remember parent name for export self._parentio_name = parentio._name if frm == "?": @@ -1389,7 +1386,7 @@ class StructIO(IOBase): ) bitlength = 1 - # Bitweise Ersetzung erfordert diese Informationen zusätzlich + # Bitwise replacement requires this information additionally if parentio._byteorder == byteorder: self._parentio_defaultvalue = parentio._defaultvalue else: @@ -1414,7 +1411,7 @@ class StructIO(IOBase): # [name,default,anzbits,adressbyte,export,adressid,bmk,bitaddress] valuelist = [ name, - # Darf nur bei StructIO None sein, wird nur dann berechnet + # May only be None for StructIO, is only calculated then kwargs.get("defaultvalue", None), bitlength, parentio._slc_address.start, @@ -1429,7 +1426,7 @@ class StructIO(IOBase): "or 'COUNTs' e.g. '8s'" ) - # Basisklasse instantiieren + # Instantiate base class super().__init__( parentio._parentdevice, valuelist, parentio._iotype, byteorder, frm == frm.lower() ) @@ -1442,7 +1439,7 @@ class StructIO(IOBase): # export, so use parent settings for the new IO self._export = parentio._export - # Platz für neuen IO prüfen + # Check space for new IO if not ( self._slc_address.start >= parentio._parentdevice._dict_slc[parentio._iotype].start and self._slc_address.stop <= parentio._parentdevice._dict_slc[parentio._iotype].stop @@ -1471,15 +1468,15 @@ class StructIO(IOBase): def _get_frm(self) -> str: """ - Ruft die struct Formatierung ab. + Retrieves the struct formatting. - :return: struct Formatierung + :return: struct formatting """ return self.__frm[1:] def _get_signed(self) -> bool: """ - Ruft ab, ob der Wert Vorzeichenbehaftet behandelt werden soll. + Retrieves whether the value should be treated as signed. :return: True, wenn Vorzeichenbehaftet """ @@ -1504,9 +1501,9 @@ class StructIO(IOBase): def get_structdefaultvalue(self): """ - Gibt die Defaultvalue mit struct Formatierung zurueck. + Returns the default value with struct formatting. - :return: Defaultvalue vom Typ der struct-Formatierung + :return: Default value of struct formatting type """ if self._bitshift: return self._defaultvalue @@ -1519,7 +1516,7 @@ class StructIO(IOBase): def get_wordorder(self) -> str: """ - Gibt die wordorder für diesen IO zurück. + Returns the wordorder for this IO. :return: "little", "big" or "ignored" """ @@ -1527,9 +1524,9 @@ class StructIO(IOBase): def get_structvalue(self): """ - Gibt den Wert mit struct Formatierung zurueck. + Returns the value with struct formatting. - :return: Wert vom Typ der struct-Formatierung + :return: Value of struct formatting type """ if self._bitshift: return self.get_value() @@ -1542,9 +1539,9 @@ class StructIO(IOBase): def set_structvalue(self, value): """ - Setzt den Wert mit struct Formatierung. + Sets the value with struct formatting. - :param value: Wert vom Typ der struct-Formatierung + :param value: Value of struct formatting type """ if self._bitshift: self.set_value(value) @@ -1562,11 +1559,11 @@ class StructIO(IOBase): class MemIO(IOBase): """ - Erstellt einen IO für die Memory Werte in piCtory. + Creates an IO for the memory values in piCtory. - Dieser Typ ist nur für lesenden Zugriff vorgesehen und kann verschiedene - Datentypen über .value zurückgeben. Damit hat man nun auch Zugriff - auf Strings, welche in piCtory vergeben werden. + This type is only intended for read access and can return various + data types via .value. This also provides access to strings that + are assigned in piCtory. """ def get_variantvalue(self): diff --git a/src/revpimodio2/modio.py b/src/revpimodio2/modio.py index b6828de..a35a137 100644 --- a/src/revpimodio2/modio.py +++ b/src/revpimodio2/modio.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""RevPiModIO Hauptklasse fuer piControl0 Zugriff.""" +"""RevPiModIO main class for piControl0 access.""" __author__ = "Sven Sager" __copyright__ = "Copyright (C) 2023 Sven Sager" __license__ = "LGPLv2" @@ -55,14 +55,14 @@ class DevSelect: class RevPiModIO(object): """ - Klasse fuer die Verwaltung der piCtory Konfiguration. + Class for managing the piCtory configuration. - Diese Klasse uebernimmt die gesamte Konfiguration aus piCtory und - laedt die Devices und IOs. Sie uebernimmt die exklusive Verwaltung des - Prozessabbilds und stellt sicher, dass die Daten synchron sind. - Sollten nur einzelne Devices gesteuert werden, verwendet man - RevPiModIOSelected() und uebergibt bei Instantiierung eine Liste mit - Device Positionen oder Device Namen. + This class takes over the entire configuration from piCtory and + loads the devices and IOs. It takes over exclusive management of the + process image and ensures that the data is synchronized. + If only individual devices are to be controlled, use + RevPiModIOSelected() and pass a list with + device positions or device names during instantiation. """ __slots__ = ( @@ -115,20 +115,20 @@ class RevPiModIO(object): shared_procimg=False, ): """ - Instantiiert die Grundfunktionen. + Instantiates the basic functions. - :param autorefresh: Wenn True, alle Devices zu autorefresh hinzufuegen - :param monitoring: In- und Outputs werden gelesen, niemals geschrieben - :param syncoutputs: Aktuell gesetzte Outputs vom Prozessabbild einlesen - :param procimg: Abweichender Pfad zum Prozessabbild - :param configrsc: Abweichender Pfad zur piCtory Konfigurationsdatei - :param simulator: Laedt das Modul als Simulator und vertauscht IOs - :param debug: Gibt alle Warnungen inkl. Zyklusprobleme aus - :param replace_io_file: Replace IO Konfiguration aus Datei laden + :param autorefresh: If True, add all devices to autorefresh + :param monitoring: Inputs and outputs are read, never written + :param syncoutputs: Read currently set outputs from process image + :param procimg: Alternative path to process image + :param configrsc: Alternative path to piCtory configuration file + :param simulator: Loads the module as simulator and swaps IOs + :param debug: Output all warnings including cycle problems + :param replace_io_file: Load replace IO configuration from file :param shared_procimg: Share process image with other processes, this could be insecure for automation """ - # Parameterprüfung + # Parameter validation acheck( bool, autorefresh=autorefresh, @@ -155,9 +155,9 @@ class RevPiModIO(object): self._init_shared_procimg = shared_procimg self._syncoutputs = syncoutputs - # TODO: bei simulator und procimg prüfen ob datei existiert / anlegen? + # TODO: check if file exists for simulator and procimg / create it? - # Private Variablen + # Private variables self.__cleanupfunc = None self._buffedwrite = False self._debug = 1 @@ -176,19 +176,19 @@ class RevPiModIO(object): self._th_mainloop = None self._waitexit = Event() - # Modulvariablen + # Module variables self.core = None - # piCtory Klassen + # piCtory classes self.app = None self.device = None self.io = None self.summary = None - # Event für Benutzeraktionen + # Event for user actions self.exitsignal = Event() - # Wert über setter setzen + # Set value via setter self.debug = debug try: @@ -196,12 +196,12 @@ class RevPiModIO(object): except Exception: self._run_on_pi = False - # Nur Konfigurieren, wenn nicht vererbt + # Only configure if not inherited if type(self) == RevPiModIO: self._configure(self.get_jconfigrsc()) def __del__(self): - """Zerstoert alle Klassen um aufzuraeumen.""" + """Destroys all classes to clean up.""" if hasattr(self, "_exit"): self.exit(full=True) if self._myfh is not None: @@ -239,10 +239,10 @@ class RevPiModIO(object): def __evt_exit(self, signum, sigframe) -> None: """ - Eventhandler fuer Programmende. + Event handler for program termination. - :param signum: Signalnummer - :param sigframe: Signalframe + :param signum: Signal number + :param sigframe: Signal frame """ signal(SIGINT, SIG_DFL) signal(SIGTERM, SIG_DFL) @@ -252,15 +252,15 @@ class RevPiModIO(object): def __exit_jobs(self): """Shutdown sub systems.""" if self._exit_level & 1: - # Nach Ausführung kann System weiter verwendet werden + # System can continue to be used after execution self._exit_level ^= 1 - # ProcimgWriter beenden und darauf warten + # Stop ProcimgWriter and wait for it if self._imgwriter is not None and self._imgwriter.is_alive(): self._imgwriter.stop() self._imgwriter.join(2.5) - # Alle Devices aus Autorefresh entfernen + # Remove all devices from autorefresh while len(self._lst_refresh) > 0: dev = self._lst_refresh.pop() dev._selfupdate = False @@ -285,15 +285,15 @@ class RevPiModIO(object): def _configure(self, jconfigrsc: dict) -> None: """ - Verarbeitet die piCtory Konfigurationsdatei. + Processes the piCtory configuration file. :param jconfigrsc: Data to build IOs as of JSON """ - # Filehandler konfigurieren, wenn er noch nicht existiert + # Configure file handler if it doesn't exist yet if self._myfh is None: self._myfh = self._create_myfh() - # App Klasse instantiieren + # Instantiate App class self.app = appmodule.App(jconfigrsc["App"]) # Apply device filter @@ -324,14 +324,14 @@ class RevPiModIO(object): lst_devices.append(dev) else: - # Devices aus JSON übernehmen + # Take devices from JSON lst_devices = jconfigrsc["Devices"] - # Device und IO Klassen anlegen + # Create Device and IO classes self.device = devicemodule.DeviceList() self.io = IOList(self) - # Devices initialisieren + # Initialize devices err_names_check = {} for device in sorted(lst_devices, key=lambda x: x["offset"]): # Pre-check of values @@ -347,14 +347,14 @@ class RevPiModIO(object): ) continue - # VDev alter piCtory Versionen auf KUNBUS-Standard ändern + # Change VDev of old piCtory versions to KUNBUS standard if device["position"] == "adap.": device["position"] = 64 while device["position"] in self.device: device["position"] += 1 if device["type"] == DeviceType.BASE: - # Basedevices + # Base devices pt = int(device["productType"]) if pt == ProductType.REVPI_CORE: # RevPi Core @@ -381,7 +381,7 @@ class RevPiModIO(object): dev_new = devicemodule.Flat(self, device, simulator=self._simulator) self.core = dev_new else: - # Base immer als Fallback verwenden + # Always use Base as fallback dev_new = devicemodule.Base(self, device, simulator=self._simulator) elif device["type"] == DeviceType.LEFT_RIGHT: # IOs @@ -393,7 +393,7 @@ class RevPiModIO(object): # RO dev_new = devicemodule.RoModule(self, device, simulator=self._simulator) else: - # Alle anderen IO-Devices + # All other IO devices dev_new = devicemodule.Device(self, device, simulator=self._simulator) elif device["type"] == DeviceType.VIRTUAL: # Virtuals @@ -405,7 +405,7 @@ class RevPiModIO(object): # Connectdevice dev_new = None else: - # Device-Type nicht gefunden + # Device type not found warnings.warn( "device type '{0}' on position {1} unknown" "".format(device["type"], device["position"]), @@ -414,7 +414,7 @@ class RevPiModIO(object): dev_new = None if dev_new is not None: - # Offset prüfen, muss mit Länge übereinstimmen + # Check offset, must match length if self._length < dev_new.offset: self._length = dev_new.offset @@ -428,7 +428,7 @@ class RevPiModIO(object): # Set shared_procimg mode, if requested on instantiation dev_new.shared_procimg(self._init_shared_procimg) - # DeviceList für direkten Zugriff aufbauen + # Build DeviceList for direct access setattr(self.device, dev_new.name, dev_new) # Check equal device names and destroy name attribute of device class @@ -443,18 +443,18 @@ class RevPiModIO(object): Warning, ) - # ImgWriter erstellen + # Create ImgWriter self._imgwriter = helpermodule.ProcimgWriter(self) if self._set_device_based_cycle_time: - # Refreshzeit CM1 25 Hz / CM3 50 Hz + # Refresh time CM1 25 Hz / CM3 50 Hz self._imgwriter.refresh = 20 if cpu_count() > 1 else 40 - # Aktuellen Outputstatus von procimg einlesen + # Read current output status from procimg if self._syncoutputs: self.syncoutputs() - # Für RS485 errors am core defaults laden sollte procimg NULL sein + # For RS485 errors at core, load defaults if procimg should be NULL if isinstance(self.core, devicemodule.Core) and not (self._monitoring or self._simulator): if self.core._slc_errorlimit1 is not None: io = self.io[self.core.offset + self.core._slc_errorlimit1.start][0] @@ -463,26 +463,25 @@ class RevPiModIO(object): io = self.io[self.core.offset + self.core._slc_errorlimit2.start][0] io.set_value(io._defaultvalue) - # RS485 errors schreiben + # Write RS485 errors self.writeprocimg(self.core) # Set replace IO before autostart to prevent cycle time exhausting self._configure_replace_io(self._get_cpreplaceio()) - # Optional ins autorefresh aufnehmen + # Optionally add to autorefresh if self._autorefresh: self.autorefresh_all() - # Summary Klasse instantiieren + # Instantiate Summary class self.summary = summarymodule.Summary(jconfigrsc["Summary"]) def _configure_replace_io(self, creplaceio: ConfigParser) -> None: """ - Importiert ersetzte IOs in diese Instanz. + Imports replaced IOs into this instance. - Importiert ersetzte IOs, welche vorher mit .export_replaced_ios(...) - in eine Datei exportiert worden sind. Diese IOs werden in dieser - Instanz wiederhergestellt. + Imports replaced IOs that were previously exported to a file using + .export_replaced_ios(...). These IOs are restored in this instance. :param creplaceio: Data to replace ios as """ @@ -490,10 +489,10 @@ class RevPiModIO(object): if io == "DEFAULT": continue - # IO prüfen + # Check IO parentio = creplaceio[io].get("replace", "") - # Funktionsaufruf vorbereiten + # Prepare function call dict_replace = { "frm": creplaceio[io].get("frm"), "byteorder": creplaceio[io].get("byteorder", "little"), @@ -558,11 +557,11 @@ class RevPiModIO(object): "".format(io, creplaceio[io]["defaultvalue"]) ) - # IO ersetzen + # Replace IO try: self.io[parentio].replace_io(name=io, **dict_replace) except Exception as e: - # NOTE: Bei Selected/Driver kann nicht geprüft werden + # NOTE: Cannot be checked for Selected/Driver if len(self._devselect.values) == 0: raise RuntimeError( "replace_io_file: can not replace '{0}' with '{1}' " @@ -571,7 +570,7 @@ class RevPiModIO(object): def _create_myfh(self): """ - Erstellt FileObject mit Pfad zum procimg. + Creates FileObject with path to procimg. :return: FileObject """ @@ -582,15 +581,15 @@ class RevPiModIO(object): """ Getter function. - :return: Pfad der verwendeten piCtory Konfiguration + :return: Path of the used piCtory configuration """ return self._configrsc def _get_cpreplaceio(self) -> ConfigParser: """ - Laedt die replace_io_file Konfiguration und verarbeitet sie. + Loads the replace_io_file configuration and processes it. - :return: der replace io daten + :return: of the replace io data """ cp = ConfigParser() @@ -608,17 +607,17 @@ class RevPiModIO(object): def _get_cycletime(self) -> int: """ - Gibt Aktualisierungsrate in ms der Prozessabbildsynchronisierung aus. + Returns the refresh rate in ms of the process image synchronization. - :return: Millisekunden + :return: Milliseconds """ return self._imgwriter.refresh def _get_debug(self) -> bool: """ - Gibt Status des Debugflags zurueck. + Returns the status of the debug flag. - :return: Status des Debugflags + :return: Status of the debug flag """ return self._debug == 1 @@ -626,7 +625,7 @@ class RevPiModIO(object): """ Getter function. - :return: Aktuelle Anzahl gezaehlter Fehler + :return: Current number of counted errors """ return self._ioerror @@ -634,7 +633,7 @@ class RevPiModIO(object): """ Getter function. - :return: Laenge in Bytes der Devices + :return: Length in bytes of the devices """ return self._length @@ -642,7 +641,7 @@ class RevPiModIO(object): """ Getter function. - :return: Anzahl erlaubte Fehler + :return: Number of allowed errors """ return self._maxioerrors @@ -650,7 +649,7 @@ class RevPiModIO(object): """ Getter function. - :return: True, wenn als Monitoring gestartet + :return: True if started as monitoring """ return self._monitoring @@ -658,15 +657,15 @@ class RevPiModIO(object): """ Getter function. - :return: Pfad des verwendeten Prozessabbilds + :return: Path of the used process image """ return self._procimg def _get_replace_io_file(self) -> str: """ - Gibt Pfad zur verwendeten replace IO Datei aus. + Returns the path to the used replace IO file. - :return: Pfad zur replace IO Datei + :return: Path to the replace IO file """ return self._replace_io_file @@ -674,17 +673,17 @@ class RevPiModIO(object): """ Getter function. - :return: True, wenn als Simulator gestartet + :return: True if started as simulator """ return self._simulator def _gotioerror(self, action: str, e=None, show_warn=True) -> None: """ - IOError Verwaltung fuer Prozessabbildzugriff. + IOError management for process image access. - :param action: Zusatzinformationen zum loggen + :param action: Additional information for logging :param e: Exception to log if debug is enabled - :param show_warn: Warnung anzeigen + :param show_warn: Show warning """ self._ioerror += 1 if self._maxioerrors != 0 and self._ioerror >= self._maxioerrors: @@ -706,9 +705,9 @@ class RevPiModIO(object): def _set_cycletime(self, milliseconds: int) -> None: """ - Setzt Aktualisierungsrate der Prozessabbild-Synchronisierung. + Sets the refresh rate of the process image synchronization. - :param milliseconds: in Millisekunden + :param milliseconds: in milliseconds """ if self._looprunning: raise RuntimeError("can not change cycletime when cycleloop or mainloop is running") @@ -717,14 +716,14 @@ class RevPiModIO(object): def _set_debug(self, value: bool) -> None: """ - Setzt debugging Status um mehr Meldungen zu erhalten oder nicht. + Sets debugging status to get more messages or not. - :param value: Wenn True, werden umfangreiche Medungen angezeigt + :param value: If True, extensive messages are displayed """ if type(value) == bool: value = int(value) if not type(value) == int: - # Wert -1 ist zum kompletten deaktivieren versteckt + # Value -1 is hidden for complete deactivation raise TypeError("value must be or ") if not -1 <= value <= 1: raise ValueError("value must be True/False or -1, 0, 1") @@ -740,9 +739,9 @@ class RevPiModIO(object): def _set_maxioerrors(self, value: int) -> None: """ - Setzt Anzahl der maximal erlaubten Fehler bei Prozessabbildzugriff. + Sets the number of maximum allowed errors for process image access. - :param value: Anzahl erlaubte Fehler + :param value: Number of allowed errors """ if type(value) == int and value >= 0: self._maxioerrors = value @@ -751,18 +750,18 @@ class RevPiModIO(object): def _simulate_ioctl(self, request: int, arg=b"") -> None: """ - Simuliert IOCTL Funktionen auf procimg Datei. + Simulates IOCTL functions on procimg file. :param request: IO Request :param arg: Request argument """ if request == 19216: - # Einzelnes Bit setzen + # Set single bit byte_address = int.from_bytes(arg[:2], byteorder="little") bit_address = arg[2] new_value = bool(0 if len(arg) <= 3 else arg[3]) - # Simulatonsmodus schreibt direkt in Datei + # Simulation mode writes directly to file with self._myfh_lck: self._myfh.seek(byte_address) int_byte = int.from_bytes(self._myfh.read(1), byteorder="little") @@ -780,7 +779,7 @@ class RevPiModIO(object): self._myfh.flush() elif request == 19220: - # Counterwert auf 0 setzen + # Set counter value to 0 dev_position = arg[0] bit_field = int.from_bytes(arg[2:], byteorder="little") io_byte = -1 @@ -802,64 +801,61 @@ class RevPiModIO(object): self._myfh.flush() def autorefresh_all(self) -> None: - """Setzt alle Devices in autorefresh Funktion.""" + """Sets all devices to autorefresh function.""" for dev in self.device: dev.autorefresh() def cleanup(self) -> None: - """Beendet autorefresh und alle Threads.""" + """Terminates autorefresh and all threads.""" self._exit_level |= 2 self.exit(full=True) def cycleloop(self, func, cycletime=50, blocking=True): """ - Startet den Cycleloop. + Starts the cycle loop. - Der aktuelle Programmthread wird hier bis Aufruf von - .exit() "gefangen". Er fuehrt nach jeder Aktualisierung - des Prozessabbilds die uebergebene Funktion "func" aus und arbeitet sie - ab. Waehrend der Ausfuehrung der Funktion wird das Prozessabbild nicht - weiter aktualisiert. Die Inputs behalten bis zum Ende den aktuellen - Wert. Gesetzte Outputs werden nach Ende des Funktionsdurchlaufs in das - Prozessabbild geschrieben. + The current program thread is "trapped" here until .exit() is called. + After each update of the process image, it executes the passed + function "func" and processes it. During execution of the function, + the process image is not further updated. The inputs retain their + current value until the end. Set outputs are written to the process + image after the function run completes. - Verlassen wird der Cycleloop, wenn die aufgerufene Funktion einen - Rueckgabewert nicht gleich None liefert (z.B. return True), oder durch - Aufruf von .exit(). + The cycle loop is left when the called function returns a value + not equal to None (e.g. return True), or by calling .exit(). - HINWEIS: Die Aktualisierungszeit und die Laufzeit der Funktion duerfen - die eingestellte autorefresh Zeit, bzw. uebergebene cycletime nicht - ueberschreiten! + NOTE: The refresh time and the runtime of the function must not + exceed the set autorefresh time or passed cycletime! - Ueber den Parameter cycletime wird die gewuenschte Zukluszeit der - uebergebenen Funktion gesetzt. Der Standardwert betraegt - 50 Millisekunden, in denen das Prozessabild eingelesen, die uebergebene - Funktion ausgefuert und das Prozessabbild geschrieben wird. + The cycletime parameter sets the desired cycle time of the passed + function. The default value is 50 milliseconds, in which the process + image is read, the passed function is executed, and the process image + is written. - :param func: Funktion, die ausgefuehrt werden soll - :param cycletime: Zykluszeit in Millisekunden - Standardwert 50 ms - :param blocking: Wenn False, blockiert das Programm hier NICHT + :param func: Function to be executed + :param cycletime: Cycle time in milliseconds - default 50 ms + :param blocking: If False, the program does NOT block here :return: None or the return value of the cycle function """ # Check for context manager if self._context_manager: raise RuntimeError("Can not start cycleloop inside a context manager (with statement)") - # Prüfen ob ein Loop bereits läuft + # Check if a loop is already running if self._looprunning: raise RuntimeError("can not start multiple loops mainloop/cycleloop") - # Prüfen ob Devices in autorefresh sind + # Check if devices are in autorefresh if len(self._lst_refresh) == 0: raise RuntimeError( "no device with autorefresh activated - use autorefresh=True " "or call .autorefresh_all() before entering cycleloop" ) - # Prüfen ob Funktion callable ist + # Check if function is callable if not callable(func): raise RuntimeError("registered function '{0}' ist not callable".format(func)) - # Thread erstellen, wenn nicht blockieren soll + # Create thread if it should not block if not blocking: self._th_mainloop = Thread( target=self.cycleloop, @@ -869,7 +865,7 @@ class RevPiModIO(object): self._th_mainloop.start() return - # Zykluszeit übernehmen + # Take over cycle time old_cycletime = self._imgwriter.refresh if not cycletime == self._imgwriter.refresh: # Set new cycle time and wait one imgwriter cycle to sync fist cycle @@ -877,10 +873,10 @@ class RevPiModIO(object): self._imgwriter.newdata.clear() self._imgwriter.newdata.wait(self._imgwriter._refresh) - # Benutzerevent + # User event self.exitsignal.clear() - # Cycleloop starten + # Start cycle loop self._exit.clear() self._looprunning = True cycleinfo = helpermodule.Cycletools(self._imgwriter.refresh, self) @@ -889,7 +885,7 @@ class RevPiModIO(object): self._imgwriter.newdata.clear() try: while ec is None and not cycleinfo.last: - # Auf neue Daten warten und nur ausführen wenn set() + # Wait for new data and only execute if set() if not self._imgwriter.newdata.wait(2.5): if not self._imgwriter.is_alive(): self.exit(full=False) @@ -905,18 +901,18 @@ class RevPiModIO(object): self._imgwriter.newdata.clear() - # Vor Aufruf der Funktion autorefresh sperren + # Lock autorefresh before calling the function self._imgwriter.lck_refresh.acquire() - # Vorbereitung für cycleinfo + # Preparation for cycleinfo cycleinfo._start_timer = default_timer() cycleinfo.last = self._exit.is_set() - # Funktion aufrufen und auswerten + # Call and evaluate function ec = func(cycleinfo) cycleinfo._docycle() - # autorefresh freigeben + # Release autorefresh self._imgwriter.lck_refresh.release() except Exception as ex: if self._imgwriter.lck_refresh.locked(): @@ -925,17 +921,17 @@ class RevPiModIO(object): self.exit(full=False) e = ex finally: - # Cycleloop beenden + # End cycle loop self._looprunning = False self._th_mainloop = None - # Alte autorefresh Zeit setzen + # Set old autorefresh time self._imgwriter.refresh = old_cycletime - # Exitstrategie ausführen + # Execute exit strategy self.__exit_jobs() - # Auf Fehler prüfen die im loop geworfen wurden + # Check for errors that were thrown in the loop if e is not None: raise e @@ -943,23 +939,23 @@ class RevPiModIO(object): def exit(self, full=True) -> None: """ - Beendet mainloop() und optional autorefresh. + Terminates mainloop() and optionally autorefresh. - Wenn sich das Programm im mainloop() befindet, wird durch Aufruf - von exit() die Kontrolle wieder an das Hauptprogramm zurueckgegeben. + If the program is in mainloop(), calling exit() returns control + to the main program. - Der Parameter full ist mit True vorbelegt und entfernt alle Devices aus - dem autorefresh. Der Thread fuer die Prozessabbildsynchronisierung - wird dann gestoppt und das Programm kann sauber beendet werden. + The full parameter defaults to True and removes all devices from + autorefresh. The thread for process image synchronization is then + stopped and the program can be terminated cleanly. - :param full: Entfernt auch alle Devices aus autorefresh + :param full: Also removes all devices from autorefresh """ self._exit_level |= 1 if full else 0 - # Echten Loopwert vor Events speichern + # Save actual loop value before events full = full and not self._looprunning - # Benutzerevent + # User event self.exitsignal.set() self._exit.set() @@ -974,14 +970,13 @@ class RevPiModIO(object): def export_replaced_ios(self, filename="replace_ios.conf") -> None: """ - Exportiert ersetzte IOs dieser Instanz. + Exports replaced IOs of this instance. - Exportiert alle ersetzten IOs, welche mit .replace_io(...) angelegt - wurden. Die Datei kann z.B. fuer RevPiPyLoad verwendet werden um Daten - in den neuen Formaten per MQTT zu uebertragen oder mit RevPiPyControl - anzusehen. + Exports all replaced IOs that were created with .replace_io(...). + The file can be used, for example, for RevPiPyLoad to transfer data + in the new formats via MQTT or to view with RevPiPyControl. - @param filename Dateiname fuer Exportdatei + @param filename Filename for export file """ acheck(str, filename=filename) @@ -1019,18 +1014,18 @@ class RevPiModIO(object): def get_jconfigrsc(self) -> dict: """ - Laedt die piCtory Konfiguration und erstellt ein . + Loads the piCtory configuration and creates a . - :return: der piCtory Konfiguration + :return: of the piCtory configuration """ - # piCtory Konfiguration prüfen + # Check piCtory configuration if self._configrsc is not None: if not access(self._configrsc, F_OK | R_OK): raise RuntimeError( "can not access pictory configuration at {0}".format(self._configrsc) ) else: - # piCtory Konfiguration an bekannten Stellen prüfen + # Check piCtory configuration at known locations lst_rsc = ["/etc/revpi/config.rsc", "/opt/KUNBUS/config.rsc"] for rscfile in lst_rsc: if access(rscfile, F_OK | R_OK): @@ -1055,26 +1050,25 @@ class RevPiModIO(object): def handlesignalend(self, cleanupfunc=None) -> None: """ - Signalhandler fuer Programmende verwalten. + Manage signal handler for program termination. - Wird diese Funktion aufgerufen, uebernimmt RevPiModIO die SignalHandler - fuer SIGINT und SIGTERM. Diese werden Empfangen, wenn das - Betriebssystem oder der Benutzer das Steuerungsprogramm sauber beenden - will. + When this function is called, RevPiModIO takes over the SignalHandler + for SIGINT and SIGTERM. These are received when the operating system + or the user wants to cleanly terminate the control program. - Die optionale Funktion "cleanupfunc" wird als letztes nach dem letzten - Einlesen der Inputs ausgefuehrt. Dort gesetzte Outputs werden nach - Ablauf der Funktion ein letztes Mal geschrieben. - Gedacht ist dies fuer Aufraeumarbeiten, wie z.B. das abschalten der - LEDs am RevPi-Core. + The optional function "cleanupfunc" is executed last after the last + reading of the inputs. Outputs set there are written one last time + after the function completes. + This is intended for cleanup work, such as switching off the + LEDs on the RevPi-Core. - Nach einmaligem Empfangen eines der Signale und dem Beenden der - RevPiModIO Thrads / Funktionen werden die SignalHandler wieder - freigegeben. + After receiving one of the signals once and terminating the + RevPiModIO threads / functions, the SignalHandler are released + again. - :param cleanupfunc: Funktion wird nach dem Beenden ausgefuehrt + :param cleanupfunc: Function to be executed after termination """ - # Prüfen ob Funktion callable ist + # Check if function is callable if not (cleanupfunc is None or callable(cleanupfunc)): raise RuntimeError("registered function '{0}' ist not callable".format(cleanupfunc)) self.__cleanupfunc = cleanupfunc @@ -1083,55 +1077,54 @@ class RevPiModIO(object): def mainloop(self, blocking=True) -> None: """ - Startet den Mainloop mit Eventueberwachung. + Starts the mainloop with event monitoring. - Der aktuelle Programmthread wird hier bis Aufruf von - RevPiDevicelist.exit() "gefangen" (es sei denn blocking=False). Er - durchlaeuft die Eventueberwachung und prueft Aenderungen der, mit - einem Event registrierten, IOs. Wird eine Veraenderung erkannt, - fuert das Programm die dazugehoerigen Funktionen der Reihe nach aus. + The current program thread is "trapped" here until + RevPiDevicelist.exit() is called (unless blocking=False). It + runs through the event monitoring and checks for changes of + registered IOs with an event. If a change is detected, + the program executes the associated functions in sequence. - Wenn der Parameter "blocking" mit False angegeben wird, aktiviert - dies die Eventueberwachung und blockiert das Programm NICHT an der - Stelle des Aufrufs. Eignet sich gut fuer die GUI Programmierung, wenn - Events vom RevPi benoetigt werden, aber das Programm weiter ausgefuehrt - werden soll. + If the parameter "blocking" is specified as False, this activates + event monitoring and does NOT block the program at the point of call. + Well suited for GUI programming when events from the RevPi are needed + but the program should continue to execute. - :param blocking: Wenn False, blockiert das Programm hier NICHT + :param blocking: If False, the program does NOT block here """ # Check for context manager if self._context_manager: raise RuntimeError("Can not start mainloop inside a context manager (with statement)") - # Prüfen ob ein Loop bereits läuft + # Check if a loop is already running if self._looprunning: raise RuntimeError("can not start multiple loops mainloop/cycleloop") - # Prüfen ob Devices in autorefresh sind + # Check if devices are in autorefresh if len(self._lst_refresh) == 0: raise RuntimeError( "no device with autorefresh activated - use autorefresh=True " "or call .autorefresh_all() before entering mainloop" ) - # Thread erstellen, wenn nicht blockieren soll + # Create thread if it should not block if not blocking: self._th_mainloop = Thread(target=self.mainloop, kwargs={"blocking": True}) self._th_mainloop.start() return - # Benutzerevent + # User event self.exitsignal.clear() - # Event säubern vor Eintritt in Mainloop + # Clean event before entering mainloop self._exit.clear() self._looprunning = True - # Beim Eintritt in mainloop Bytecopy erstellen und prefire anhängen + # Create byte copy and attach prefire when entering mainloop for dev in self._lst_refresh: with dev._filelock: dev._ba_datacp = dev._ba_devdata[:] - # Prefire Events vorbereiten + # Prepare prefire events for io in dev._dict_events: for regfunc in dev._dict_events[io]: if not regfunc.prefire: @@ -1149,28 +1142,28 @@ class RevPiModIO(object): else: self._imgwriter._eventq.put((regfunc, io._name, io.value), False) - # ImgWriter mit Eventüberwachung aktivieren + # Activate ImgWriter with event monitoring self._imgwriter._collect_events(True) e = None runtime = -1 if self._debug == -1 else 0 while not self._exit.is_set(): - # Laufzeit der Eventqueue auf 0 setzen + # Set runtime of event queue to 0 if self._imgwriter._eventq.qsize() == 0: runtime = -1 if self._debug == -1 else 0 try: tup_fire = self._imgwriter._eventq.get(timeout=1) - # Messung Laufzeit der Queue starten + # Start measuring runtime of the queue if runtime == 0: runtime = default_timer() - # Direct callen da Prüfung in io.IOBase.reg_event ist + # Call directly since check is in io.IOBase.reg_event tup_fire[0].func(tup_fire[1], tup_fire[2]) self._imgwriter._eventq.task_done() - # Laufzeitprüfung + # Runtime check if runtime != -1 and default_timer() - runtime > self._imgwriter._refresh: runtime = -1 warnings.warn( @@ -1186,28 +1179,28 @@ class RevPiModIO(object): e = ex break - # Mainloop verlassen + # Leave mainloop self._imgwriter._collect_events(False) self._looprunning = False self._th_mainloop = None - # Auf Fehler prüfen die im loop geworfen wurden + # Check for errors that were thrown in the loop if e is not None: self.exit(full=False) self.__exit_jobs() raise e - # Exitstrategie ausführen + # Execute exit strategy self.__exit_jobs() def readprocimg(self, device=None) -> bool: """ - Einlesen aller Inputs aller/eines Devices vom Prozessabbild. + Read all inputs of all/one device from the process image. - Devices mit aktiverem autorefresh werden ausgenommen! + Devices with active autorefresh are excluded! - :param device: nur auf einzelnes Device anwenden - :return: True, wenn Arbeiten an allen Devices erfolgreich waren + :param device: Only apply to single device + :return: True if work on all devices was successful """ if device is None: mylist = self.device @@ -1225,7 +1218,7 @@ class RevPiModIO(object): ) mylist = [dev] - # Daten komplett einlesen + # Read data completely self._myfh_lck.acquire() try: self._myfh.seek(0) @@ -1238,14 +1231,14 @@ class RevPiModIO(object): for dev in mylist: if not dev._selfupdate: - # FileHandler sperren + # Lock file handler dev._filelock.acquire() if self._monitoring or dev._shared_procimg: - # Alles vom Bus einlesen + # Read everything from the bus dev._ba_devdata[:] = bytesbuff[dev._slc_devoff] else: - # Inputs vom Bus einlesen + # Read inputs from the bus dev._ba_devdata[dev._slc_inp] = bytesbuff[dev._slc_inpoff] dev._filelock.release() @@ -1253,14 +1246,14 @@ class RevPiModIO(object): return True def resetioerrors(self) -> None: - """Setzt aktuellen IOError-Zaehler auf 0 zurueck.""" + """Resets current IOError counter to 0.""" self._ioerror = 0 def setdefaultvalues(self, device=None) -> None: """ - Alle Outputbuffer werden auf die piCtory default Werte gesetzt. + All output buffers are set to the piCtory default values. - :param device: nur auf einzelnes Device anwenden + :param device: Only apply to single device """ if self._monitoring: raise RuntimeError("can not set default values, while system is in monitoring mode") @@ -1281,12 +1274,12 @@ class RevPiModIO(object): def syncoutputs(self, device=None) -> bool: """ - Lesen aller aktuell gesetzten Outputs im Prozessabbild. + Read all currently set outputs in the process image. - Devices mit aktiverem autorefresh werden ausgenommen! + Devices with active autorefresh are excluded! - :param device: nur auf einzelnes Device anwenden - :return: True, wenn Arbeiten an allen Devices erfolgreich waren + :param device: Only apply to single device + :return: True if work on all devices was successful """ if device is None: mylist = self.device @@ -1324,12 +1317,12 @@ class RevPiModIO(object): def writeprocimg(self, device=None) -> bool: """ - Schreiben aller Outputs aller Devices ins Prozessabbild. + Write all outputs of all devices to the process image. - Devices mit aktiverem autorefresh werden ausgenommen! + Devices with active autorefresh are excluded! - :param device: nur auf einzelnes Device anwenden - :return: True, wenn Arbeiten an allen Devices erfolgreich waren + :param device: Only apply to single device + :return: True if work on all devices was successful """ if self._monitoring: raise RuntimeError("can not write process image, while system is in monitoring mode") @@ -1364,7 +1357,7 @@ class RevPiModIO(object): global_ex = IOError("error on shared procimg while write") dev._shared_write.clear() else: - # Outpus auf Bus schreiben + # Write outputs to bus self._myfh_lck.acquire() try: self._myfh.seek(dev._slc_outoff.start) @@ -1402,12 +1395,12 @@ class RevPiModIO(object): class RevPiModIOSelected(RevPiModIO): """ - Klasse fuer die Verwaltung einzelner Devices aus piCtory. + Class for managing individual devices from piCtory. - Diese Klasse uebernimmt nur angegebene Devices der piCtory Konfiguration - und bildet sie inkl. IOs ab. Sie uebernimmt die exklusive Verwaltung des - Adressbereichs im Prozessabbild an dem sich die angegebenen Devices - befinden und stellt sicher, dass die Daten synchron sind. + This class only takes over specified devices from the piCtory configuration + and maps them including IOs. It takes over exclusive management of the + address range in the process image where the specified devices are located + and ensures that the data is synchronized. """ __slots__ = () @@ -1426,13 +1419,12 @@ class RevPiModIOSelected(RevPiModIO): shared_procimg=False, ): """ - Instantiiert nur fuer angegebene Devices die Grundfunktionen. + Instantiates the basic functions only for specified devices. - Der Parameter deviceselection kann eine einzelne - Device Position / einzelner Device Name sein oder eine Liste mit - mehreren Positionen / Namen + The deviceselection parameter can be a single device position / + single device name or a list with multiple positions / names. - :param deviceselection: Positionsnummer oder Devicename + :param deviceselection: Position number or device name :ref: :func:`RevPiModIO.__init__(...)` """ super().__init__( @@ -1480,12 +1472,11 @@ class RevPiModIOSelected(RevPiModIO): class RevPiModIODriver(RevPiModIOSelected): """ - Klasse um eigene Treiber fuer die virtuellen Devices zu erstellen. + Class to create custom drivers for virtual devices. - Mit dieser Klasse werden nur angegebene Virtuelle Devices mit RevPiModIO - verwaltet. Bei Instantiierung werden automatisch die Inputs und Outputs - verdreht, um das Schreiben der Inputs zu ermoeglichen. Die Daten koennen - dann ueber logiCAD an den Devices abgerufen werden. + With this class, only specified virtual devices are managed with RevPiModIO. + During instantiation, inputs and outputs are automatically swapped to allow + writing of inputs. The data can then be retrieved from the devices via logiCAD. """ __slots__ = () @@ -1502,15 +1493,15 @@ class RevPiModIODriver(RevPiModIOSelected): shared_procimg=False, ): """ - Instantiiert die Grundfunktionen. + Instantiates the basic functions. - Parameter 'monitoring' und 'simulator' stehen hier nicht zur - Verfuegung, da diese automatisch gesetzt werden. + Parameters 'monitoring' and 'simulator' are not available here + as they are set automatically. - :param virtdev: Virtuelles Device oder mehrere als + :param virtdev: Virtual device or multiple as :ref: :func:`RevPiModIO.__init__()` """ - # Parent mit monitoring=False und simulator=True laden + # Load parent with monitoring=False and simulator=True if type(virtdev) not in (list, tuple): virtdev = (virtdev,) dev_select = DevSelect(DeviceType.VIRTUAL, "", virtdev) diff --git a/src/revpimodio2/netio.py b/src/revpimodio2/netio.py index 69ccec1..2d2d9a5 100644 --- a/src/revpimodio2/netio.py +++ b/src/revpimodio2/netio.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""RevPiModIO Hauptklasse fuer Netzwerkzugriff.""" +"""RevPiModIO main class for network access.""" __author__ = "Sven Sager" __copyright__ = "Copyright (C) 2023 Sven Sager" __license__ = "LGPLv2" @@ -17,16 +17,16 @@ from .errors import DeviceNotFoundError from .modio import DevSelect, RevPiModIO as _RevPiModIO from .pictory import DeviceType -# Synchronisierungsbefehl +# Synchronization command _syssync = b"\x01\x06\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17" # Disconnectbefehl _sysexit = b"\x01EX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17" -# DirtyBytes von Server entfernen -_sysdeldirty = b"\x01EY\x00\x00\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x17" -# piCtory Konfiguration laden +# Remove DirtyBytes from server +_sysdeldirty = b"\x01EY\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x17" +# Load piCtory configuration _syspictory = b"\x01PI\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17" _syspictoryh = b"\x01PH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17" -# ReplaceIO Konfiguration laden +# Load ReplaceIO configuration _sysreplaceio = b"\x01RP\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17" _sysreplaceioh = b"\x01RH\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17" # Hashvalues @@ -37,24 +37,23 @@ HEADER_STOP = b"\x17" class AclException(Exception): - """Probleme mit Berechtigungen.""" + """Problems with permissions.""" pass class ConfigChanged(Exception): - """Aenderung der piCtory oder replace_ios Datei.""" + """Change to the piCtory or replace_ios file.""" pass class NetFH(Thread): """ - Netzwerk File Handler fuer das Prozessabbild. + Network file handler for the process image. - Dieses FileObject-like Object verwaltet das Lesen und Schriben des - Prozessabbilds ueber das Netzwerk. Ein entfernter Revolution Pi kann - so gesteuert werden. + This file-object-like object manages reading and writing of the + process image via the network. A remote Revolution Pi can be controlled this way. """ __slots__ = ( @@ -84,9 +83,9 @@ class NetFH(Thread): """ Init NetFH-class. - :param address: IP Adresse, Port des RevPi als - :param check_replace_ios: Prueft auf Veraenderungen der Datei - :param timeout: Timeout in Millisekunden der Verbindung + :param address: IP address, port of the RevPi as + :param check_replace_ios: Checks for changes to the file + :param timeout: Timeout in milliseconds for the connection """ super().__init__() self.daemon = True @@ -109,38 +108,38 @@ class NetFH(Thread): self._address = address self._serversock = None # type: socket.socket - # Parameterprüfung + # Parameter validation if not isinstance(address, tuple): raise TypeError("parameter address must be ('IP', PORT)") if not isinstance(timeout, int): raise TypeError("parameter timeout must be ") - # Verbindung herstellen + # Establish connection self.__set_systimeout(timeout) self._connect() if self._serversock is None: raise FileNotFoundError("can not connect to revpi server") - # NetFH konfigurieren + # Configure NetFH self.__position = 0 self.start() def __del__(self): - """NetworkFileHandler beenden.""" + """Terminate NetworkFileHandler.""" self.close() def __check_acl(self, bytecode: bytes) -> None: """ - Pueft ob ACL auf RevPi den Vorgang erlaubt. + Checks if ACL allows the operation on RevPi. - Ist der Vorgang nicht zulässig, wird der Socket sofort geschlossen - und eine Exception geworfen. + If the operation is not permitted, the socket is immediately closed + and an exception is thrown. - :param bytecode: Antwort, die geprueft werden solll + :param bytecode: Response to be checked """ if bytecode == b"\x18": - # Alles beenden, wenn nicht erlaubt + # Terminate everything if not permitted self.__sockend.set() self.__sockerr.set() self._serversock.close() @@ -152,39 +151,39 @@ class NetFH(Thread): def __set_systimeout(self, value: int) -> None: """ - Systemfunktion fuer Timeoutberechnung. + System function for timeout calculation. - :param value: Timeout in Millisekunden 100 - 60000 + :param value: Timeout in milliseconds 100 - 60000 """ if isinstance(value, int) and (100 <= value <= 60000): self.__timeout = value / 1000 - # Timeouts in Socket setzen + # Set timeouts in socket if self._serversock is not None: self._serversock.settimeout(self.__timeout) - # 45 Prozent vom Timeout für Synctimer verwenden + # Use 45 percent of timeout for sync timer self.__waitsync = self.__timeout / 100 * 45 else: raise ValueError("value must between 10 and 60000 milliseconds") def _connect(self) -> None: - """Stellt die Verbindung zu einem RevPiPlcServer her.""" - # Neuen Socket aufbauen + """Establishes the connection to a RevPiPlcServer.""" + # Build new socket so = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: so.connect(self._address) so.settimeout(self.__timeout) - # Hashwerte anfordern + # Request hash values recv_len = 16 so.sendall(_syspictoryh) if self.__check_replace_ios: so.sendall(_sysreplaceioh) recv_len += 16 - # Hashwerte empfangen mit eigenen Puffern, da nicht gelocked + # Receive hash values with own buffers, as not locked buff_recv = bytearray(recv_len) while recv_len > 0: block = so.recv(recv_len) @@ -193,7 +192,7 @@ class NetFH(Thread): buff_recv += block recv_len -= len(block) - # Änderung an piCtory prüfen + # Check for changes to piCtory if self.__pictory_h and buff_recv[:16] != self.__pictory_h: self.__config_changed = True self.close() @@ -201,7 +200,7 @@ class NetFH(Thread): else: self.__pictory_h = buff_recv[:16] - # Änderung an replace_ios prüfen + # Check for changes to replace_ios if ( self.__check_replace_ios and self.__replace_ios_h @@ -218,7 +217,7 @@ class NetFH(Thread): except Exception: so.close() else: - # Alten Socket trennen + # Disconnect old socket with self.__socklock: if self._serversock is not None: self._serversock.close() @@ -226,10 +225,10 @@ class NetFH(Thread): self._serversock = so self.__sockerr.clear() - # Timeout setzen + # Set timeout self.set_timeout(int(self.__timeout * 1000)) - # DirtyBytes übertragen + # Transfer dirty bytes for pos in self.__dictdirty: self.set_dirtybytes(pos, self.__dictdirty[pos]) @@ -285,19 +284,19 @@ class NetFH(Thread): def clear_dirtybytes(self, position=None) -> None: """ - Entfernt die konfigurierten Dirtybytes vom RevPi Server. + Removes the configured dirty bytes from the RevPi server. - Diese Funktion wirft keine Exception bei einem uebertragungsfehler, - veranlasst aber eine Neuverbindung. + This function does not throw an exception on transmission error, + but triggers a reconnection. - :param position: Startposition der Dirtybytes + :param position: Start position of the dirty bytes """ if self.__config_changed: raise ConfigChanged("configuration on revolution pi was changed") if self.__sockend.is_set(): raise ValueError("I/O operation on closed file") - # Daten immer übernehmen + # Always accept data if position is None: self.__dictdirty.clear() elif position in self.__dictdirty: @@ -305,10 +304,10 @@ class NetFH(Thread): try: if position is None: - # Alle Dirtybytes löschen + # Clear all dirty bytes buff = self._direct_sr(_sysdeldirty, 1) else: - # Nur bestimmte Dirtybytes löschen + # Only clear specific dirty bytes # b CM ii xx c0000000 b = 16 buff = self._direct_sr( pack( @@ -322,7 +321,7 @@ class NetFH(Thread): 1, ) if buff != b"\x1e": - # ACL prüfen und ggf Fehler werfen + # Check ACL and throw error if necessary self.__check_acl(buff) raise IOError("clear dirtybytes error on network") @@ -333,14 +332,14 @@ class NetFH(Thread): self.__sockerr.set() def close(self) -> None: - """Verbindung trennen.""" + """Disconnect connection.""" if self.__sockend.is_set(): return self.__sockend.set() self.__sockerr.set() - # Vom Socket sauber trennen + # Cleanly disconnect from socket if self._serversock is not None: try: self.__socklock.acquire() @@ -354,7 +353,7 @@ class NetFH(Thread): self._serversock.close() def flush(self) -> None: - """Schreibpuffer senden.""" + """Send write buffer.""" if self.__config_changed: raise ConfigChanged("configuration on revolution pi was changed") if self.__sockend.is_set(): @@ -380,12 +379,12 @@ class NetFH(Thread): except Exception: raise finally: - # Puffer immer leeren + # Always clear buffer self.__int_buff = 0 self.__by_buff.clear() if buff != b"\x1e": - # ACL prüfen und ggf Fehler werfen + # Check ACL and throw error if necessary self.__check_acl(buff) self.__sockerr.set() @@ -393,23 +392,23 @@ class NetFH(Thread): def get_closed(self) -> bool: """ - Pruefen ob Verbindung geschlossen ist. + Check if connection is closed. - :return: True, wenn Verbindung geschlossen ist + :return: True if connection is closed """ return self.__sockend.is_set() def get_config_changed(self) -> bool: """ - Pruefen ob RevPi Konfiguration geaendert wurde. + Check if RevPi configuration was changed. - :return: True, wenn RevPi Konfiguration geaendert ist + :return: True if RevPi configuration was changed """ return self.__config_changed def get_name(self) -> str: """ - Verbindugnsnamen zurueckgeben. + Return connection name. :return: IP:PORT """ @@ -417,23 +416,23 @@ class NetFH(Thread): def get_reconnecting(self) -> bool: """ - Interner reconnect aktiv wegen Netzwerkfehlern. + Internal reconnect active due to network errors. - :return: True, wenn reconnect aktiv + :return: True if reconnect is active """ return self.__sockerr.is_set() def get_timeout(self) -> int: """ - Gibt aktuellen Timeout zurueck. + Returns current timeout. - :return: in Millisekunden + :return: in milliseconds """ return int(self.__timeout * 1000) def ioctl(self, request: int, arg=b"") -> None: """ - IOCTL Befehle ueber das Netzwerk senden. + Send IOCTL commands via the network. :param request: Request as :param arg: Argument as @@ -451,7 +450,7 @@ class NetFH(Thread): pack("=c2s2xHI4xc", HEADER_START, b"IC", len(arg), request, HEADER_STOP) + arg, 1 ) if buff != b"\x1e": - # ACL prüfen und ggf Fehler werfen + # Check ACL and throw error if necessary self.__check_acl(buff) self.__sockerr.set() @@ -459,10 +458,10 @@ class NetFH(Thread): def read(self, length: int) -> bytes: """ - Daten ueber das Netzwerk lesen. + Read data via the network. - :param length: Anzahl der Bytes - :return: Gelesene + :param length: Number of bytes + :return: Read """ if self.__config_changed: raise ConfigChanged("configuration on revolution pi was changed") @@ -501,9 +500,9 @@ class NetFH(Thread): def readpictory(self) -> bytes: """ - Ruft die piCtory Konfiguration ab. + Retrieves the piCtory configuration. - :return: piCtory Datei + :return: piCtory file """ if self.__sockend.is_set(): raise ValueError("read of closed file") @@ -517,7 +516,7 @@ class NetFH(Thread): def readreplaceio(self) -> bytes: """ - Ruft die replace_io Konfiguration ab. + Retrieves the replace_io configuration. :return: replace_io_file """ @@ -532,24 +531,24 @@ class NetFH(Thread): return self._direct_sr(b"", recv_length) def run(self) -> None: - """Handler fuer Synchronisierung.""" + """Handler for synchronization.""" state_reconnect = False while not self.__sockend.is_set(): - # Bei Fehlermeldung neu verbinden + # Reconnect on error message if self.__sockerr.is_set(): if not state_reconnect: state_reconnect = True warnings.warn("got a network error and try to reconnect", RuntimeWarning) self._connect() if self.__sockerr.is_set(): - # Verhindert beim Scheitern 100% CPU last + # Prevents 100% CPU load on failure self.__sockend.wait(self.__waitsync) continue else: state_reconnect = False warnings.warn("successfully reconnected after network error", RuntimeWarning) - # Kein Fehler aufgetreten, sync durchführen wenn socket frei + # No error occurred, perform sync if socket is free if self.__socklock.acquire(blocking=False): try: self._serversock.sendall(_syssync) @@ -573,12 +572,12 @@ class NetFH(Thread): finally: self.__socklock.release() - # Warten nach Sync damit Instantiierung funktioniert + # Wait after sync so instantiation works self.__sockerr.wait(self.__waitsync) def seek(self, position: int) -> None: - """Springt an angegebene Position. - @param position An diese Position springen""" + """Jump to specified position. + @param position Jump to this position""" if self.__config_changed: raise ConfigChanged("configuration on revolution pi was changed") if self.__sockend.is_set(): @@ -587,20 +586,20 @@ class NetFH(Thread): def set_dirtybytes(self, position: int, dirtybytes: bytes) -> None: """ - Konfiguriert Dirtybytes fuer Prozessabbild bei Verbindungsfehler. + Configures dirty bytes for process image on connection error. - Diese Funktion wirft keine Exception bei einem uebertragungsfehler, - veranlasst aber eine Neuverbindung. + This function does not throw an exception on transmission error, + but triggers a reconnection. - :param position: Startposition zum Schreiben - :param dirtybytes: die geschrieben werden sollen + :param position: Start position for writing + :param dirtybytes: to be written """ if self.__config_changed: raise ConfigChanged("configuration on revolution pi was changed") if self.__sockend.is_set(): raise ValueError("I/O operation on closed file") - # Daten immer übernehmen + # Always accept data self.__dictdirty[position] = dirtybytes try: @@ -612,7 +611,7 @@ class NetFH(Thread): ) if buff != b"\x1e": - # ACL prüfen und ggf Fehler werfen + # Check ACL and throw error if necessary self.__check_acl(buff) raise IOError("set dirtybytes error on network") @@ -625,14 +624,14 @@ class NetFH(Thread): def set_timeout(self, value: int) -> None: """ - Setzt Timeoutwert fuer Verbindung. + Sets timeout value for connection. - :param value: Timeout in Millisekunden + :param value: Timeout in milliseconds """ if self.__sockend.is_set(): raise ValueError("I/O operation on closed file") - # Timeoutwert verarbeiten (könnte Exception auslösen) + # Process timeout value (could throw exception) self.__set_systimeout(value) try: @@ -645,7 +644,7 @@ class NetFH(Thread): def tell(self) -> int: """ - Gibt aktuelle Position zurueck. + Returns aktuelle Position. :return: Aktuelle Position """ @@ -657,10 +656,10 @@ class NetFH(Thread): def write(self, bytebuff: bytes) -> int: """ - Daten ueber das Netzwerk schreiben. + Write data via the network. - :param bytebuff: Bytes zum schreiben - :return: Anzahl geschriebener bytes + :param bytebuff: Bytes to write + :return: Number of written bytes """ if self.__config_changed: raise ConfigChanged("configuration on revolution pi was changed") @@ -672,14 +671,14 @@ class NetFH(Thread): with self.__socklock: self.__int_buff += 1 - # Datenblock mit Position und Länge in Puffer ablegen + # Store data block with position and length in buffer self.__by_buff += ( self.__position.to_bytes(length=2, byteorder="little") + len(bytebuff).to_bytes(length=2, byteorder="little") + bytebuff ) - # TODO: Bufferlänge und dann flushen? + # TODO: Bufferlänge and dann flushen? return len(bytebuff) @@ -692,14 +691,13 @@ class NetFH(Thread): class RevPiNetIO(_RevPiModIO): """ - Klasse fuer die Verwaltung der piCtory Konfiguration ueber das Netzwerk. + Class for managing the piCtory configuration via the network. - Diese Klasse uebernimmt die gesamte Konfiguration aus piCtory und bilded - die Devices und IOs ab. Sie uebernimmt die exklusive Verwaltung des - Prozessabbilds und stellt sicher, dass die Daten synchron sind. - Sollten nur einzelne Devices gesteuert werden, verwendet man - RevPiModIOSelected() und uebergibt bei Instantiierung eine Liste mit - Device Positionen oder Device Namen. + This class takes over the entire configuration from piCtory and maps + the devices and IOs. It takes over exclusive management of the + process image and ensures that the data is synchronized. + If only individual devices should be controlled, use + RevPiNetIOSelected() and pass a list with device positions or device names during instantiation. """ __slots__ = "_address" @@ -716,26 +714,26 @@ class RevPiNetIO(_RevPiModIO): shared_procimg=False, ): """ - Instantiiert die Grundfunktionen. + Instantiates the basic functions. - :param address: IP-Adresse / (IP, Port) - :param autorefresh: Wenn True, alle Devices zu autorefresh hinzufuegen - :param monitoring: In- und Outputs werden gelesen, niemals geschrieben - :param syncoutputs: Aktuell gesetzte Outputs vom Prozessabbild einlesen - :param simulator: Laedt das Modul als Simulator und vertauscht IOs - :param debug: Gibt bei allen Fehlern komplette Meldungen aus - :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 address: IP address / (IP, Port) + :param autorefresh: If True, add all devices to autorefresh + :param monitoring: Inputs and outputs are read, never written + :param syncoutputs: Read currently set outputs from process image + :param simulator: Loads the module as simulator and swaps IOs + :param debug: Output complete messages for all errors + :param replace_io_file: Load replace IO configuration from file + :param shared_procimg: Share process image with other processes, this + could be insecure for automation """ check_ip = compile(r"^(25[0-5]|(2[0-4]|[01]?\d|)\d)(\.(25[0-5]|(2[0-4]|[01]?\d|)\d)){3}$") - # Adresse verarbeiten + # Process address if isinstance(address, str): self._address = (address, 55234) elif isinstance(address, tuple): if len(address) == 2 and isinstance(address[0], str) and isinstance(address[1], int): - # Werte prüfen + # Check values if not 0 < address[1] <= 65535: raise ValueError("port number out of range 1 - 65535") @@ -748,7 +746,7 @@ class RevPiNetIO(_RevPiModIO): "like (, )" ) - # IP-Adresse prüfen und ggf. auflösen + # Check IP address and resolve if necessary if check_ip.match(self._address[0]) is None: try: ipv4 = socket.gethostbyname(self._address[0]) @@ -772,16 +770,16 @@ class RevPiNetIO(_RevPiModIO): ) self._set_device_based_cycle_time = False - # Netzwerkfilehandler anlegen + # Create network file handler self._myfh = self._create_myfh() - # Nur Konfigurieren, wenn nicht vererbt + # Only configure if not inherited if type(self) == RevPiNetIO: self._configure(self.get_jconfigrsc()) def _create_myfh(self): """ - Erstellt NetworkFileObject. + Creates NetworkFileObject. :return: FileObject """ @@ -790,15 +788,15 @@ class RevPiNetIO(_RevPiModIO): def _get_cpreplaceio(self) -> ConfigParser: """ - Laed die replace_io Konfiguration ueber das Netzwerk. + Loads the replace_io configuration via the network. - :return: der replace io daten + :return: of the replace io data """ - # Normale Verwendung über Elternklasse erledigen + # Handle normal usage via parent class if self._replace_io_file != ":network:": return super()._get_cpreplaceio() - # Replace IO Daten über das Netzwerk beziehen + # Obtain replace IO data via the network byte_buff = self._myfh.readreplaceio() cp = ConfigParser() @@ -809,12 +807,12 @@ class RevPiNetIO(_RevPiModIO): return cp def disconnect(self) -> None: - """Trennt Verbindungen und beendet autorefresh inkl. alle Threads.""" + """Disconnects connections and terminates autorefresh including all threads.""" self.cleanup() def exit(self, full=True) -> None: """ - Beendet mainloop() und optional autorefresh. + Terminates mainloop() and optionally autorefresh. :ref: :func:`RevPiModIO.exit()` """ @@ -825,20 +823,20 @@ class RevPiNetIO(_RevPiModIO): def get_config_changed(self) -> bool: """ - Pruefen ob RevPi Konfiguration geaendert wurde. + Check if RevPi configuration was changed. - In diesem Fall ist die Verbindung geschlossen und RevPiNetIO muss - neu instanziert werden. + In this case, the connection is closed and RevPiNetIO must be + reinstantiated. - :return: True, wenn RevPi Konfiguration geaendert ist + :return: True if RevPi configuration was changed """ return self._myfh.config_changed def get_jconfigrsc(self) -> dict: """ - Laedt die piCotry Konfiguration und erstellt ein . + Loads the piCtory configuration and creates a . - :return: der piCtory Konfiguration + :return: of the piCtory configuration """ mynh = NetFH(self._address, False) byte_buff = mynh.readpictory() @@ -847,20 +845,20 @@ class RevPiNetIO(_RevPiModIO): def get_reconnecting(self) -> bool: """ - Interner reconnect aktiv wegen Netzwerkfehlern. + Internal reconnect active due to network errors. - Das Modul versucht intern die Verbindung neu herzustellen. Es ist - kein weiteres Zutun noetig. + The module tries internally to reestablish the connection. No + further action is needed. - :return: True, wenn reconnect aktiv + :return: True if reconnect is active """ return self._myfh.reconnecting def net_cleardefaultvalues(self, device=None) -> None: """ - Loescht Defaultwerte vom PLC Server. + Clears default values from the PLC server. - :param device: nur auf einzelnes Device anwenden, sonst auf Alle + :param device: Only apply to single device, otherwise to all """ if self.monitoring: raise RuntimeError("can not send default values, while system is in monitoring mode") @@ -876,12 +874,12 @@ class RevPiNetIO(_RevPiModIO): def net_setdefaultvalues(self, device=None) -> None: """ - Konfiguriert den PLC Server mit den piCtory Defaultwerten. + Configures the PLC server with the piCtory default values. - Diese Werte werden auf dem RevPi gesetzt, wenn die Verbindung - unerwartet (Netzwerkfehler) unterbrochen wird. + These values are set on the RevPi if the connection is + unexpectedly interrupted (network error). - :param device: nur auf einzelnes Device anwenden, sonst auf Alle + :param device: Only apply to single device, otherwise to all """ if self.monitoring: raise RuntimeError("can not send default values, while system is in monitoring mode") @@ -898,25 +896,25 @@ class RevPiNetIO(_RevPiModIO): listlen = len(lst_io) if listlen == 1: - # Byteorientierte Outputs direkt übernehmen + # Take byte-oriented outputs directly dirtybytes += lst_io[0]._defaultvalue elif listlen > 1: - # Bitorientierte Outputs in ein Byte zusammenfassen + # Combine bit-oriented outputs into one byte int_byte = 0 lstbyte = lst_io.copy() lstbyte.reverse() for bitio in lstbyte: - # Von hinten die bits nach vorne schieben + # Shift the bits from back to front int_byte <<= 1 if bitio is not None: int_byte += 1 if bitio._defaultvalue else 0 - # Errechneten Int-Wert in ein Byte umwandeln + # Convert calculated int value to a byte dirtybytes += int_byte.to_bytes(length=1, byteorder="little") - # Dirtybytes an PLC Server senden + # Send dirtybytes to PLC server self._myfh.set_dirtybytes(dev._offset + dev._slc_out.start, dirtybytes) config_changed = property(get_config_changed) @@ -925,12 +923,12 @@ class RevPiNetIO(_RevPiModIO): class RevPiNetIOSelected(RevPiNetIO): """ - Klasse fuer die Verwaltung einzelner Devices aus piCtory. + Class for managing individual devices from piCtory. - Diese Klasse uebernimmt nur angegebene Devices der piCtory Konfiguration - und bildet sie inkl. IOs ab. Sie uebernimmt die exklusive Verwaltung des - Adressbereichs im Prozessabbild an dem sich die angegebenen Devices - befinden und stellt sicher, dass die Daten synchron sind. + This class only takes over specified devices from the piCtory configuration + and maps them including IOs. It takes over exclusive management of the + address range in the process image where the specified devices are located + and ensures that the data is synchronized. """ __slots__ = () @@ -948,15 +946,15 @@ class RevPiNetIOSelected(RevPiNetIO): shared_procimg=False, ): """ - Instantiiert nur fuer angegebene Devices die Grundfunktionen. + Instantiates the basic functions only for specified devices. - Der Parameter deviceselection kann eine einzelne - Device Position / einzelner Device Name sein oder eine Liste mit - mehreren Positionen / Namen + The parameter deviceselection can be a single + device position / single device name or a list with + multiple positions / names - :param address: IP-Adresse / (IP, Port) - :param deviceselection: Positionsnummer oder Devicename - :ref: :func:`RevPiNetIO.__init__()` + :param address: IP address / (IP, Port) + :param deviceselection: Position number or device name + :ref: :func:`RevPiNetIO.__init__()` """ super().__init__( address, @@ -1002,12 +1000,11 @@ class RevPiNetIOSelected(RevPiNetIO): class RevPiNetIODriver(RevPiNetIOSelected): """ - Klasse um eigene Treiber fuer die virtuellen Devices zu erstellen. + Class to create custom drivers for virtual devices. - Mit dieser Klasse werden nur angegebene Virtuelle Devices mit RevPiModIO - verwaltet. Bei Instantiierung werden automatisch die Inputs und Outputs - verdreht, um das Schreiben der Inputs zu ermoeglichen. Die Daten koennen - dann ueber logiCAD an den Devices abgerufen werden. + With this class, only specified virtual devices are managed with RevPiModIO. + During instantiation, inputs and outputs are automatically swapped to allow + writing of inputs. The data can then be retrieved from the devices via logiCAD. """ __slots__ = () @@ -1023,16 +1020,16 @@ class RevPiNetIODriver(RevPiNetIOSelected): shared_procimg=False, ): """ - Instantiiert die Grundfunktionen. + Instantiates the basic functions. - Parameter 'monitoring' und 'simulator' stehen hier nicht zur - Verfuegung, da diese automatisch gesetzt werden. + Parameters 'monitoring' and 'simulator' are not available here, + as these are set automatically. - :param address: IP-Adresse / (IP, Port) - :param virtdev: Virtuelles Device oder mehrere als - :ref: :func:`RevPiModIO.__init__()` + :param address: IP address / (IP, Port) + :param virtdev: Virtual device or multiple as + :ref: :func:`RevPiModIO.__init__()` """ - # Parent mit monitoring=False und simulator=True laden + # Load parent with monitoring=False and simulator=True if type(virtdev) not in (list, tuple): virtdev = (virtdev,) dev_select = DevSelect(DeviceType.VIRTUAL, "", virtdev) @@ -1062,7 +1059,7 @@ def run_net_plc(address, func, cycletime=50, replace_io_file=None, debug=True): rpi.handlesignalend() return rpi.cycleloop(func, cycletime) - :param address: IP-Adresse / (IP, Port) + :param address: IP address / (IP, Port) :param func: Function to run every set milliseconds :param cycletime: Cycle time in milliseconds :param replace_io_file: Load replace IO configuration from file diff --git a/src/revpimodio2/summary.py b/src/revpimodio2/summary.py index 7e7fafa..bf9361b 100644 --- a/src/revpimodio2/summary.py +++ b/src/revpimodio2/summary.py @@ -1,18 +1,18 @@ # -*- coding: utf-8 -*- -"""Bildet die Summary-Sektion von piCtory ab.""" +"""Maps the Summary section from piCtory.""" __author__ = "Sven Sager" __copyright__ = "Copyright (C) 2023 Sven Sager" __license__ = "LGPLv2" class Summary: - """Bildet die Summary-Sektion der config.rsc ab.""" + """Maps the Summary section of config.rsc.""" __slots__ = "inptotal", "outtotal" def __init__(self, summary: dict): """ - Instantiiert die RevPiSummary-Klasse. + Instantiates the RevPiSummary class. :param summary: piCtory Summaryinformationen """ diff --git a/tests/common/test_devices.py b/tests/common/test_devices.py index 166351a..be9a2fa 100644 --- a/tests/common/test_devices.py +++ b/tests/common/test_devices.py @@ -96,7 +96,7 @@ class TestDevicesModule(TestRevPiModIO): self.assertEqual(33 in rpi.device.virt01, False) self.assertEqual(552 in rpi.device.virt01, True) - # Löschen + # Delete del rpi.device.virt01 with self.assertRaises(AttributeError): rpi.device.virt01 diff --git a/tests/common/test_init_modio.py b/tests/common/test_init_modio.py index 0dffa5d..e5b7b19 100644 --- a/tests/common/test_init_modio.py +++ b/tests/common/test_init_modio.py @@ -93,10 +93,10 @@ class TestInitModio(TestRevPiModIO): self.assertEqual(2, len(rpi.device)) del rpi with self.assertRaises(revpimodio2.errors.DeviceNotFoundError): - # Liste mit einem ungültigen Device als + # List with an invalid device as rpi = revpimodio2.RevPiModIOSelected([32, 10], **defaultkwargs) with self.assertRaises(revpimodio2.errors.DeviceNotFoundError): - # Ungültiges Device als + # Invalid device as rpi = revpimodio2.RevPiModIOSelected(100, **defaultkwargs) with self.assertRaises(ValueError): # Ungültiger Devicetype @@ -116,10 +116,10 @@ class TestInitModio(TestRevPiModIO): # RevPiModIODriver with self.assertRaises(revpimodio2.errors.DeviceNotFoundError): - # Liste mit einem ungültigen Device als + # List with an invalid device as rpi = revpimodio2.RevPiModIODriver([64, 100], **defaultkwargs) with self.assertRaises(revpimodio2.errors.DeviceNotFoundError): - # Ungültiges Device als + # Invalid device as rpi = revpimodio2.RevPiModIODriver([100], **defaultkwargs) with self.assertRaises(ValueError): # Ungültiger Devicetype @@ -132,7 +132,7 @@ class TestInitModio(TestRevPiModIO): self.assertEqual(1, len(rpi.device)) del rpi - # Core ios als bits + # Core ios as bits rpi = self.modio(configrsc="config_core_bits.json") del rpi diff --git a/tests/common/test_modio_class_basics.py b/tests/common/test_modio_class_basics.py index 606d2fa..01189f9 100644 --- a/tests/common/test_modio_class_basics.py +++ b/tests/common/test_modio_class_basics.py @@ -27,7 +27,7 @@ class TestModioClassBasics(TestRevPiModIO): self.assertEqual(rpi.app.savets.tm_hour, 12) del rpi - # Alte config ohne saveTS + # Old config without saveTS with self.assertWarnsRegex(Warning, r"equal device name '.*' in pictory configuration."): rpi = self.modio(configrsc="config_old.rsc") self.assertIsNone(rpi.app.savets) @@ -79,7 +79,7 @@ class TestModioClassBasics(TestRevPiModIO): """Test interaction with process image.""" rpi = self.modio() - # Procimg IO alle + # Procimg IO all self.assertIsNone(rpi.setdefaultvalues()) self.assertEqual(rpi.writeprocimg(), True) self.assertEqual(rpi.syncoutputs(), True) diff --git a/tests/compact/test_compact.py b/tests/compact/test_compact.py index c2d71cf..a877557 100644 --- a/tests/compact/test_compact.py +++ b/tests/compact/test_compact.py @@ -19,7 +19,7 @@ class TestCompact(TestRevPiModIO): self.assertIsInstance(rpi.core, revpimodio2.device.Compact) - # COMPACT LEDs prüfen + # Check COMPACT LEDs self.assertEqual(rpi.io.RevPiLED.get_value(), b"\x00") rpi.core.A1 = revpimodio2.OFF self.assertEqual(rpi.core.A1, 0) @@ -44,12 +44,12 @@ class TestCompact(TestRevPiModIO): with self.assertRaises(ValueError): rpi.core.A2 = 5 - # Spezielle Werte aufrufen + # Call special values self.assertIsInstance(rpi.core.temperature, int) self.assertIsInstance(rpi.core.frequency, int) rpi.core.wd_toggle() - # Directzuweisung nicht erlaubt + # Direct assignment not allowed with self.assertRaisesRegex(AttributeError, r"direct assignment is not supported"): rpi.core.a1green = True diff --git a/tests/flat/test_flat.py b/tests/flat/test_flat.py index b8c938b..32cc926 100644 --- a/tests/flat/test_flat.py +++ b/tests/flat/test_flat.py @@ -20,7 +20,7 @@ class TestFlat(TestRevPiModIO): self.assertIsInstance(rpi.core, revpimodio2.device.Flat) - # FLAT LEDs prüfen + # Check FLAT LEDs rpi.core.A1 = revpimodio2.OFF self.assertEqual(rpi.io.RevPiLED.get_value(), b"\x00\x00") self.assertEqual(rpi.core.A1, 0) @@ -77,12 +77,12 @@ class TestFlat(TestRevPiModIO): with self.assertRaises(ValueError): rpi.core.A5 = 5 - # Spezielle Werte aufrufen + # Call special values self.assertIsInstance(rpi.core.temperature, int) self.assertIsInstance(rpi.core.frequency, int) rpi.core.wd_toggle() - # Directzuweisung nicht erlaubt + # Direct assignment not allowed with self.assertRaisesRegex(AttributeError, r"direct assignment is not supported"): rpi.core.a1green = True diff --git a/tests/io_tests/test_ios.py b/tests/io_tests/test_ios.py index e4a0666..c8488b1 100644 --- a/tests/io_tests/test_ios.py +++ b/tests/io_tests/test_ios.py @@ -84,7 +84,7 @@ class TestIos(TestRevPiModIO): """Testet mehrbittige IOs.""" rpi = self.modio(configrsc="config_supervirt.rsc") - # Adressen und Längen prüfen + # Check addresses and lengths self.assertEqual(rpi.device[65]._offset, 75) self.assertEqual(rpi.io.InBit_1.length, 1) diff --git a/tests/revpi3/test_connect.py b/tests/revpi3/test_connect.py index 23beeec..6c381f5 100644 --- a/tests/revpi3/test_connect.py +++ b/tests/revpi3/test_connect.py @@ -48,7 +48,7 @@ class TestRevPiConnect(TestRevPiModIO): with self.assertRaises(ValueError): rpi.core.A3 = BLUE - # Direkte Zuweisung darf nicht funktionieren + # Direct assignment must not work with self.assertRaises(AttributeError): rpi.core.a3green = True with self.assertRaises(AttributeError): diff --git a/tests/revpi3/test_core.py b/tests/revpi3/test_core.py index fbda827..22360e4 100644 --- a/tests/revpi3/test_core.py +++ b/tests/revpi3/test_core.py @@ -97,7 +97,7 @@ class TestRevPiCore(TestRevPiModIO): with self.assertWarnsRegex(Warning, r"equal device name '.*' in pictory configuration."): rpi = self.modio(configrsc="config_old.rsc") - # Errorlimits testen, die es nicht gibt (damals None, jetzt -1) + # Test error limits that don't exist (formerly None, now -1) self.assertEqual(rpi.core.errorlimit1, -1) self.assertEqual(rpi.core.errorlimit2, -1)