Docstrings optimieren 2 - Code-Cleanup

This commit is contained in:
2019-10-20 23:51:57 +02:00
parent 10af9a01d5
commit 9d85c7bdc0
4 changed files with 424 additions and 305 deletions

View File

@@ -12,7 +12,7 @@ class App(object):
__slots__ = "name", "version", "language", "layout", "savets" __slots__ = "name", "version", "language", "layout", "savets"
def __init__(self, app): def __init__(self, app: dict):
""" """
Instantiiert die App-Klasse. Instantiiert die App-Klasse.

View File

@@ -1,26 +1,27 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""RevPiModIO Hauptklasse fuer piControl0 Zugriff.""" """RevPiModIO Hauptklasse fuer piControl0 Zugriff."""
__author__ = "Sven Sager"
__copyright__ = "Copyright (C) 2018 Sven Sager"
__license__ = "LGPLv3"
import warnings import warnings
from configparser import ConfigParser from configparser import ConfigParser
from json import load as jload from json import load as jload
from multiprocessing import cpu_count from multiprocessing import cpu_count
from os import access, F_OK, R_OK from os import F_OK, R_OK, access
from os import stat as osstat from os import stat as osstat
from queue import Empty from queue import Empty
from revpimodio2 import acheck, DeviceNotFoundError, BOTH, RISING, FALLING from signal import SIGINT, SIGTERM, SIG_DFL, signal
from signal import signal, SIG_DFL, SIGINT, SIGTERM
from stat import S_ISCHR from stat import S_ISCHR
from threading import Thread, Event, Lock from threading import Event, Lock, Thread
from timeit import default_timer from timeit import default_timer
from revpimodio2 import BOTH, DeviceNotFoundError, FALLING, RISING, acheck
__author__ = "Sven Sager"
__copyright__ = "Copyright (C) 2018 Sven Sager"
__license__ = "LGPLv3"
class RevPiModIO(object): class RevPiModIO(object):
"""
"""Klasse fuer die Verwaltung der piCtory Konfiguration. Klasse fuer die Verwaltung der piCtory Konfiguration.
Diese Klasse uebernimmt die gesamte Konfiguration aus piCtory und bilded Diese Klasse uebernimmt die gesamte Konfiguration aus piCtory und bilded
die Devices und IOs ab. Sie uebernimmt die exklusive Verwaltung des die Devices und IOs ab. Sie uebernimmt die exklusive Verwaltung des
@@ -28,33 +29,32 @@ class RevPiModIO(object):
Sollten nur einzelne Devices gesteuert werden, verwendet man Sollten nur einzelne Devices gesteuert werden, verwendet man
RevPiModIOSelected() und uebergibt bei Instantiierung eine Liste mit RevPiModIOSelected() und uebergibt bei Instantiierung eine Liste mit
Device Positionen oder Device Namen. Device Positionen oder Device Namen.
""" """
__slots__ = "__cleanupfunc", "_autorefresh", "_buffedwrite", \ __slots__ = "__cleanupfunc", "_autorefresh", "_buffedwrite", \
"_configrsc", "_direct_output", "_exit", "_imgwriter", "_ioerror", \ "_configrsc", "_direct_output", "_exit", "_imgwriter", "_ioerror", \
"_length", "_looprunning", "_lst_devselect", "_lst_refresh", \ "_length", "_looprunning", "_lst_devselect", "_lst_refresh", \
"_maxioerrors", "_myfh", "_myfh_lck", "_monitoring", "_procimg", \ "_maxioerrors", "_myfh", "_myfh_lck", "_monitoring", "_procimg", \
"_simulator", "_syncoutputs", "_th_mainloop", "_waitexit", \ "_simulator", "_syncoutputs", "_th_mainloop", "_waitexit", \
"core", "app", "device", "exitsignal", "io", "summary", "_debug", \ "core", "app", "device", "exitsignal", "io", "summary", "_debug", \
"_replace_io_file", "_run_on_pi" "_replace_io_file", "_run_on_pi"
def __init__( def __init__(
self, autorefresh=False, monitoring=False, syncoutputs=True, self, autorefresh=False, monitoring=False, syncoutputs=True,
procimg=None, configrsc=None, simulator=False, debug=True, procimg=None, configrsc=None, simulator=False, debug=True,
replace_io_file=None, direct_output=False): replace_io_file=None, direct_output=False):
"""Instantiiert die Grundfunktionen. """
Instantiiert die Grundfunktionen.
@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 direct_output Write outputs immediately to process image (slow)
: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 direct_output: Write outputs immediately to process image (slow)
""" """
# Parameterprüfung # Parameterprüfung
acheck( acheck(
@@ -127,10 +127,13 @@ class RevPiModIO(object):
if self._myfh is not None: if self._myfh is not None:
self._myfh.close() self._myfh.close()
def __evt_exit(self, signum, sigframe): def __evt_exit(self, signum, sigframe) -> None:
"""Eventhandler fuer Programmende. """
@param signum Signalnummer Eventhandler fuer Programmende.
@param sigframe Signalframe"""
:param signum: Signalnummer
:param sigframe: Signalframe
"""
signal(SIGINT, SIG_DFL) signal(SIGINT, SIG_DFL)
signal(SIGTERM, SIG_DFL) signal(SIGTERM, SIG_DFL)
self.exit(full=True) self.exit(full=True)
@@ -140,10 +143,12 @@ class RevPiModIO(object):
if not self._monitoring: if not self._monitoring:
self.writeprocimg() self.writeprocimg()
def _configure(self, jconfigrsc): def _configure(self, jconfigrsc: dict) -> None:
"""Verarbeitet die piCtory Konfigurationsdatei. """
@param jconfigrsc: Data to build IOs as <class 'dict'> of JSON""" Verarbeitet die piCtory Konfigurationsdatei.
:param jconfigrsc: Data to build IOs as <class 'dict'> of JSON
"""
# Filehandler konfigurieren, wenn er noch nicht existiert # Filehandler konfigurieren, wenn er noch nicht existiert
if self._myfh is None: if self._myfh is None:
self._myfh = self._create_myfh() self._myfh = self._create_myfh()
@@ -283,12 +288,12 @@ class RevPiModIO(object):
if self.core._slc_errorlimit1 is not None: if self.core._slc_errorlimit1 is not None:
io = self.io[ io = self.io[
self.core.offset + self.core._slc_errorlimit1.start self.core.offset + self.core._slc_errorlimit1.start
][0] ][0]
io.set_value(io._defaultvalue) io.set_value(io._defaultvalue)
if self.core._slc_errorlimit2 is not None: if self.core._slc_errorlimit2 is not None:
io = self.io[ io = self.io[
self.core.offset + self.core._slc_errorlimit2.start self.core.offset + self.core._slc_errorlimit2.start
][0] ][0]
io.set_value(io._defaultvalue) io.set_value(io._defaultvalue)
# RS485 errors schreiben # RS485 errors schreiben
@@ -301,15 +306,15 @@ class RevPiModIO(object):
# Summary Klasse instantiieren # Summary Klasse instantiieren
self.summary = summarymodule.Summary(jconfigrsc["Summary"]) self.summary = summarymodule.Summary(jconfigrsc["Summary"])
def _configure_replace_io(self, creplaceio): def _configure_replace_io(self, creplaceio: ConfigParser) -> None:
"""Importiert ersetzte IOs in diese Instanz. """
Importiert ersetzte IOs in diese Instanz.
Importiert ersetzte IOs, welche vorher mit .export_replaced_ios(...) Importiert ersetzte IOs, welche vorher mit .export_replaced_ios(...)
in eine Datei exportiert worden sind. Diese IOs werden in dieser in eine Datei exportiert worden sind. Diese IOs werden in dieser
Instanz wiederhergestellt. Instanz wiederhergestellt.
@param ireplaceio: Data to replace ios as <class 'ConfigParser'> :param creplaceio: Data to replace ios as <class 'ConfigParser'>
""" """
for io in creplaceio: for io in creplaceio:
if io == "DEFAULT": if io == "DEFAULT":
@@ -371,19 +376,28 @@ class RevPiModIO(object):
) )
def _create_myfh(self): def _create_myfh(self):
"""Erstellt FileObject mit Pfad zum procimg. """
return FileObject""" Erstellt FileObject mit Pfad zum procimg.
:return: FileObject
"""
self._buffedwrite = False self._buffedwrite = False
return open(self._procimg, "r+b", 0) return open(self._procimg, "r+b", 0)
def _get_configrsc(self): def _get_configrsc(self) -> str:
"""Getter function. """
@return Pfad der verwendeten piCtory Konfiguration""" Getter function.
:return: Pfad der verwendeten piCtory Konfiguration
"""
return self._configrsc return self._configrsc
def _get_cpreplaceio(self): def _get_cpreplaceio(self) -> ConfigParser:
"""Laed die replace_io_file Konfiguration und verarbeitet sie. """
@return <class 'ConfigParser'> der replace io daten""" Laed die replace_io_file Konfiguration und verarbeitet sie.
:return: <class 'ConfigParser'> der replace io daten
"""
cp = ConfigParser() cp = ConfigParser()
# TODO: verfeinern! # TODO: verfeinern!
@@ -400,58 +414,85 @@ class RevPiModIO(object):
return cp return cp
def _get_cycletime(self): def _get_cycletime(self) -> int:
"""Gibt Aktualisierungsrate in ms der Prozessabbildsynchronisierung aus. """
@return Millisekunden""" Gibt Aktualisierungsrate in ms der Prozessabbildsynchronisierung aus.
:return: Millisekunden
"""
return self._imgwriter.refresh return self._imgwriter.refresh
def _get_debug(self): def _get_debug(self) -> bool:
"""Gibt Status des Debugflags zurueck. """
@return Status des Debugflags""" Gibt Status des Debugflags zurueck.
:return: Status des Debugflags
"""
return self._debug == 1 return self._debug == 1
def _get_ioerrors(self): def _get_ioerrors(self) -> int:
"""Getter function. """
@return Aktuelle Anzahl gezaehlter Fehler""" Getter function.
:return: Aktuelle Anzahl gezaehlter Fehler
"""
return self._ioerror return self._ioerror
def _get_length(self): def _get_length(self) -> int:
"""Getter function. """
@return Laenge in Bytes der Devices""" Getter function.
:return: Laenge in Bytes der Devices
"""
return self._length return self._length
def _get_maxioerrors(self): def _get_maxioerrors(self) -> int:
"""Getter function. """
@return Anzahl erlaubte Fehler""" Getter function.
:return: Anzahl erlaubte Fehler
"""
return self._maxioerrors return self._maxioerrors
def _get_monitoring(self): def _get_monitoring(self) -> bool:
"""Getter function. """
@return True, wenn als Monitoring gestartet""" Getter function.
:return: True, wenn als Monitoring gestartet
"""
return self._monitoring return self._monitoring
def _get_procimg(self): def _get_procimg(self) -> str:
"""Getter function. """
@return Pfad des verwendeten Prozessabbilds""" Getter function.
:return: Pfad des verwendeten Prozessabbilds
"""
return self._procimg return self._procimg
def _get_replace_io_file(self): def _get_replace_io_file(self) -> str:
"""Gibt Pfad zur verwendeten replace IO Datei aus. """
@return Pfad zur replace IO Datei""" Gibt Pfad zur verwendeten replace IO Datei aus.
:return: Pfad zur replace IO Datei
"""
return self._replace_io_file return self._replace_io_file
def _get_simulator(self): def _get_simulator(self) -> bool:
"""Getter function. """
@return True, wenn als Simulator gestartet""" Getter function.
:return: True, wenn als Simulator gestartet
"""
return self._simulator return self._simulator
def _gotioerror(self, action, e=None, show_warn=True): def _gotioerror(self, action: str, e=None, show_warn=True) -> None:
"""IOError Verwaltung fuer Prozessabbildzugriff. """
IOError Verwaltung fuer Prozessabbildzugriff.
@param action Zusatzinformationen zum loggen
@param e Exception to log if debug is enabled
@param show_warn Warnung anzeigen
:param action: Zusatzinformationen zum loggen
:param e: Exception to log if debug is enabled
:param show_warn: Warnung anzeigen
""" """
self._ioerror += 1 self._ioerror += 1
if self._maxioerrors != 0 and self._ioerror >= self._maxioerrors: if self._maxioerrors != 0 and self._ioerror >= self._maxioerrors:
@@ -475,9 +516,12 @@ class RevPiModIO(object):
RuntimeWarning RuntimeWarning
) )
def _set_cycletime(self, milliseconds): def _set_cycletime(self, milliseconds: int) -> None:
"""Setzt Aktualisierungsrate der Prozessabbild-Synchronisierung. """
@param milliseconds <class 'int'> in Millisekunden""" Setzt Aktualisierungsrate der Prozessabbild-Synchronisierung.
:param milliseconds: <class 'int'> in Millisekunden
"""
if self._looprunning: if self._looprunning:
raise RuntimeError( raise RuntimeError(
"can not change cycletime when cycleloop or mainloop is " "can not change cycletime when cycleloop or mainloop is "
@@ -486,9 +530,12 @@ class RevPiModIO(object):
else: else:
self._imgwriter.refresh = milliseconds self._imgwriter.refresh = milliseconds
def _set_debug(self, value): def _set_debug(self, value: bool) -> None:
"""Setzt debugging Status um mehr Meldungen zu erhalten oder nicht. """
@param value Wenn True, werden umfangreiche Medungen angezeigt""" Setzt debugging Status um mehr Meldungen zu erhalten oder nicht.
:param value: Wenn True, werden umfangreiche Medungen angezeigt
"""
if type(value) == bool: if type(value) == bool:
value = int(value) value = int(value)
if not type(value) == int: if not type(value) == int:
@@ -506,18 +553,24 @@ class RevPiModIO(object):
else: else:
warnings.filterwarnings("always", module="revpimodio2") warnings.filterwarnings("always", module="revpimodio2")
def _set_maxioerrors(self, value): def _set_maxioerrors(self, value: int) -> None:
"""Setzt Anzahl der maximal erlaubten Fehler bei Prozessabbildzugriff. """
@param value Anzahl erlaubte Fehler""" Setzt Anzahl der maximal erlaubten Fehler bei Prozessabbildzugriff.
:param value: Anzahl erlaubte Fehler
"""
if type(value) == int and value >= 0: if type(value) == int and value >= 0:
self._maxioerrors = value self._maxioerrors = value
else: else:
raise ValueError("value must be 0 or a positive integer") raise ValueError("value must be 0 or a positive integer")
def _simulate_ioctl(self, request, arg=b''): def _simulate_ioctl(self, request: int, arg=b'') -> None:
"""Simuliert IOCTL Funktionen auf procimg Datei. """
@param request IO Request Simuliert IOCTL Funktionen auf procimg Datei.
@param arg: Request argument"""
:param request: IO Request
:param arg: Request argument
"""
if request == 19216: if request == 19216:
# Einzelnes Bit setzen # Einzelnes Bit setzen
byte_address = int.from_bytes(arg[:2], byteorder="little") byte_address = int.from_bytes(arg[:2], byteorder="little")
@@ -552,7 +605,7 @@ class RevPiModIO(object):
for i in range(16): for i in range(16):
if bool(bit_field & 1 << i): if bool(bit_field & 1 << i):
io_byte = self.device[dev_position].offset \ io_byte = self.device[dev_position].offset \
+ int(self.device[dev_position]._lst_counter[i]) + int(self.device[dev_position]._lst_counter[i])
break break
if io_byte == -1: if io_byte == -1:
@@ -564,12 +617,12 @@ class RevPiModIO(object):
if self._buffedwrite: if self._buffedwrite:
self._myfh.flush() self._myfh.flush()
def autorefresh_all(self): def autorefresh_all(self) -> None:
"""Setzt alle Devices in autorefresh Funktion.""" """Setzt alle Devices in autorefresh Funktion."""
for dev in self.device: for dev in self.device:
dev.autorefresh() dev.autorefresh()
def cleanup(self): def cleanup(self) -> None:
"""Beendet autorefresh und alle Threads.""" """Beendet autorefresh und alle Threads."""
self.exit(full=True) self.exit(full=True)
self._myfh.close() self._myfh.close()
@@ -580,7 +633,8 @@ class RevPiModIO(object):
self.summary = None self.summary = None
def cycleloop(self, func, cycletime=50): def cycleloop(self, func, cycletime=50):
"""Startet den Cycleloop. """
Startet den Cycleloop.
Der aktuelle Programmthread wird hier bis Aufruf von Der aktuelle Programmthread wird hier bis Aufruf von
.exit() "gefangen". Er fuehrt nach jeder Aktualisierung .exit() "gefangen". Er fuehrt nach jeder Aktualisierung
@@ -603,10 +657,9 @@ class RevPiModIO(object):
50 Millisekunden, in denen das Prozessabild eingelesen, die uebergebene 50 Millisekunden, in denen das Prozessabild eingelesen, die uebergebene
Funktion ausgefuert und das Prozessabbild geschrieben wird. Funktion ausgefuert und das Prozessabbild geschrieben wird.
@param func Funktion, die ausgefuehrt werden soll :param func: Funktion, die ausgefuehrt werden soll
@param cycletime Zykluszeit in Millisekunden - Standardwert 50 ms :param cycletime: Zykluszeit in Millisekunden - Standardwert 50 ms
@return None or the return value of the cycle function :return: None or the return value of the cycle function
""" """
# Prüfen ob ein Loop bereits läuft # Prüfen ob ein Loop bereits läuft
if self._looprunning: if self._looprunning:
@@ -679,8 +732,9 @@ class RevPiModIO(object):
return ec return ec
def exit(self, full=True): def exit(self, full=True) -> None:
"""Beendet mainloop() und optional autorefresh. """
Beendet mainloop() und optional autorefresh.
Wenn sich das Programm im mainloop() befindet, wird durch Aufruf Wenn sich das Programm im mainloop() befindet, wird durch Aufruf
von exit() die Kontrolle wieder an das Hauptprogramm zurueckgegeben. von exit() die Kontrolle wieder an das Hauptprogramm zurueckgegeben.
@@ -689,8 +743,8 @@ class RevPiModIO(object):
dem autorefresh. Der Thread fuer die Prozessabbildsynchronisierung dem autorefresh. Der Thread fuer die Prozessabbildsynchronisierung
wird dann gestoppt und das Programm kann sauber beendet werden. wird dann gestoppt und das Programm kann sauber beendet werden.
@param full Entfernt auch alle Devices aus autorefresh""" :param full: Entfernt auch alle Devices aus autorefresh
"""
# Benutzerevent # Benutzerevent
self.exitsignal.set() self.exitsignal.set()
@@ -714,15 +768,17 @@ class RevPiModIO(object):
if not self._monitoring: if not self._monitoring:
self.writeprocimg(dev) self.writeprocimg(dev)
def export_replaced_ios(self, filename="replace_ios.conf"): def export_replaced_ios(self, filename="replace_ios.conf") -> None:
"""Exportiert ersetzte IOs dieser Instanz. """
Exportiert ersetzte IOs dieser Instanz.
Exportiert alle ersetzten IOs, welche mit .replace_io(...) angelegt Exportiert alle ersetzten IOs, welche mit .replace_io(...) angelegt
wurden. Die Datei kann z.B. fuer RevPiPyLoad verwndet werden um Daten wurden. Die Datei kann z.B. fuer RevPiPyLoad verwndet werden um Daten
in den neuen Formaten per MQTT zu uebertragen oder mit RevPiPyControl in den neuen Formaten per MQTT zu uebertragen oder mit RevPiPyControl
anzusehen. anzusehen.
@param filename Dateiname fuer Exportdatei""" @param filename Dateiname fuer Exportdatei
"""
acheck(str, filename=filename) acheck(str, filename=filename)
cp = ConfigParser() cp = ConfigParser()
@@ -753,9 +809,12 @@ class RevPiModIO(object):
"".format(filename, e) "".format(filename, e)
) )
def get_jconfigrsc(self): def get_jconfigrsc(self) -> dict:
"""Laedt die piCtory Konfiguration und erstellt ein <class 'dict'>. """
@return <class 'dict'> der piCtory Konfiguration""" Laedt die piCtory Konfiguration und erstellt ein <class 'dict'>.
:return: <class 'dict'> der piCtory Konfiguration
"""
# piCtory Konfiguration prüfen # piCtory Konfiguration prüfen
if self._configrsc is not None: if self._configrsc is not None:
if not access(self._configrsc, F_OK | R_OK): if not access(self._configrsc, F_OK | R_OK):
@@ -786,8 +845,9 @@ class RevPiModIO(object):
) )
return jdata return jdata
def handlesignalend(self, cleanupfunc=None): def handlesignalend(self, cleanupfunc=None) -> None:
"""Signalhandler fuer Programmende verwalten. """
Signalhandler fuer Programmende verwalten.
Wird diese Funktion aufgerufen, uebernimmt RevPiModIO die SignalHandler Wird diese Funktion aufgerufen, uebernimmt RevPiModIO die SignalHandler
fuer SIGINT und SIGTERM. Diese werden Empfangen, wenn das fuer SIGINT und SIGTERM. Diese werden Empfangen, wenn das
@@ -804,9 +864,7 @@ class RevPiModIO(object):
RevPiModIO Thrads / Funktionen werden die SignalHandler wieder RevPiModIO Thrads / Funktionen werden die SignalHandler wieder
freigegeben. freigegeben.
@param cleanupfunc Funktion wird nach dem letzten Lesen der Inputs :param cleanupfunc: Funktion wird nach dem Beenden ausgefuehrt
ausgefuehrt, gefolgt vom letzten Schreiben der Outputs
""" """
# Prüfen ob Funktion callable ist # Prüfen ob Funktion callable ist
if not (cleanupfunc is None or callable(cleanupfunc)): if not (cleanupfunc is None or callable(cleanupfunc)):
@@ -818,8 +876,9 @@ class RevPiModIO(object):
signal(SIGINT, self.__evt_exit) signal(SIGINT, self.__evt_exit)
signal(SIGTERM, self.__evt_exit) signal(SIGTERM, self.__evt_exit)
def mainloop(self, blocking=True): def mainloop(self, blocking=True) -> None:
"""Startet den Mainloop mit Eventueberwachung. """
Startet den Mainloop mit Eventueberwachung.
Der aktuelle Programmthread wird hier bis Aufruf von Der aktuelle Programmthread wird hier bis Aufruf von
RevPiDevicelist.exit() "gefangen" (es sei denn blocking=False). Er RevPiDevicelist.exit() "gefangen" (es sei denn blocking=False). Er
@@ -833,9 +892,7 @@ class RevPiModIO(object):
Events vom RevPi benoetigt werden, aber das Programm weiter ausgefuehrt Events vom RevPi benoetigt werden, aber das Programm weiter ausgefuehrt
werden soll. werden soll.
@param blocking Wenn False, blockiert das Programm hier NICHT :param blocking: Wenn False, blockiert das Programm hier NICHT
@return None
""" """
# Prüfen ob ein Loop bereits läuft # Prüfen ob ein Loop bereits läuft
if self._looprunning: if self._looprunning:
@@ -935,14 +992,14 @@ class RevPiModIO(object):
if e is not None: if e is not None:
raise e raise e
def readprocimg(self, device=None): def readprocimg(self, device=None) -> bool:
"""Einlesen aller Inputs aller/eines Devices vom Prozessabbild. """
Einlesen aller Inputs aller/eines Devices vom Prozessabbild.
Devices mit aktiverem autorefresh werden ausgenommen! Devices mit aktiverem autorefresh werden ausgenommen!
@param device nur auf einzelnes Device anwenden :param device: nur auf einzelnes Device anwenden
@return True, wenn Arbeiten an allen Devices erfolgreich waren :return: True, wenn Arbeiten an allen Devices erfolgreich waren
""" """
if device is None: if device is None:
mylist = self.device mylist = self.device
@@ -988,13 +1045,16 @@ class RevPiModIO(object):
return True return True
def resetioerrors(self): def resetioerrors(self) -> None:
"""Setzt aktuellen IOError-Zaehler auf 0 zurueck.""" """Setzt aktuellen IOError-Zaehler auf 0 zurueck."""
self._ioerror = 0 self._ioerror = 0
def setdefaultvalues(self, device=None): def setdefaultvalues(self, device=None) -> None:
"""Alle Outputbuffer werden auf die piCtory default Werte gesetzt. """
@param device nur auf einzelnes Device anwenden""" Alle Outputbuffer werden auf die piCtory default Werte gesetzt.
:param device: nur auf einzelnes Device anwenden
"""
if self._monitoring: if self._monitoring:
raise RuntimeError( raise RuntimeError(
"can not set default values, while system is in monitoring " "can not set default values, while system is in monitoring "
@@ -1012,14 +1072,14 @@ class RevPiModIO(object):
for io in dev.get_outputs(): for io in dev.get_outputs():
io.set_value(io._defaultvalue) io.set_value(io._defaultvalue)
def syncoutputs(self, device=None): def syncoutputs(self, device=None) -> bool:
"""Lesen aller aktuell gesetzten Outputs im Prozessabbild. """
Lesen aller aktuell gesetzten Outputs im Prozessabbild.
Devices mit aktiverem autorefresh werden ausgenommen! Devices mit aktiverem autorefresh werden ausgenommen!
@param device nur auf einzelnes Device anwenden :param device: nur auf einzelnes Device anwenden
@return True, wenn Arbeiten an allen Devices erfolgreich waren :return: True, wenn Arbeiten an allen Devices erfolgreich waren
""" """
if device is None: if device is None:
mylist = self.device mylist = self.device
@@ -1052,14 +1112,14 @@ class RevPiModIO(object):
return True return True
def writeprocimg(self, device=None): def writeprocimg(self, device=None) -> bool:
"""Schreiben aller Outputs aller Devices ins Prozessabbild. """
Schreiben aller Outputs aller Devices ins Prozessabbild.
Devices mit aktiverem autorefresh werden ausgenommen! Devices mit aktiverem autorefresh werden ausgenommen!
@param device nur auf einzelnes Device anwenden :param device: nur auf einzelnes Device anwenden
@return True, wenn Arbeiten an allen Devices erfolgreich waren :return: True, wenn Arbeiten an allen Devices erfolgreich waren
""" """
if self._direct_output: if self._direct_output:
return True return True
@@ -1127,14 +1187,13 @@ class RevPiModIO(object):
class RevPiModIOSelected(RevPiModIO): class RevPiModIOSelected(RevPiModIO):
"""
"""Klasse fuer die Verwaltung einzelner Devices aus piCtory. Klasse fuer die Verwaltung einzelner Devices aus piCtory.
Diese Klasse uebernimmt nur angegebene Devices der piCtory Konfiguration Diese Klasse uebernimmt nur angegebene Devices der piCtory Konfiguration
und bilded sie inkl. IOs ab. Sie uebernimmt die exklusive Verwaltung des und bilded sie inkl. IOs ab. Sie uebernimmt die exklusive Verwaltung des
Adressbereichs im Prozessabbild an dem sich die angegebenen Devices Adressbereichs im Prozessabbild an dem sich die angegebenen Devices
befinden und stellt sicher, dass die Daten synchron sind. befinden und stellt sicher, dass die Daten synchron sind.
""" """
__slots__ = () __slots__ = ()
@@ -1144,15 +1203,15 @@ class RevPiModIOSelected(RevPiModIO):
syncoutputs=True, procimg=None, configrsc=None, syncoutputs=True, procimg=None, configrsc=None,
simulator=False, debug=True, replace_io_file=None, simulator=False, debug=True, replace_io_file=None,
direct_output=False): direct_output=False):
"""Instantiiert nur fuer angegebene Devices die Grundfunktionen. """
Instantiiert nur fuer angegebene Devices die Grundfunktionen.
Der Parameter deviceselection kann eine einzelne Der Parameter deviceselection kann eine einzelne
Device Position / einzelner Device Name sein oder eine Liste mit Device Position / einzelner Device Name sein oder eine Liste mit
mehreren Positionen / Namen mehreren Positionen / Namen
@param deviceselection Positionsnummer oder Devicename :param deviceselection: Positionsnummer oder Devicename
@see #RevPiModIO.__init__ RevPiModIO.__init__(...) :ref: :func:`RevPiModIO.__init__(...)`
""" """
super().__init__( super().__init__(
autorefresh, monitoring, syncoutputs, procimg, configrsc, autorefresh, monitoring, syncoutputs, procimg, configrsc,
@@ -1197,29 +1256,29 @@ class RevPiModIOSelected(RevPiModIO):
class RevPiModIODriver(RevPiModIOSelected): class RevPiModIODriver(RevPiModIOSelected):
"""
"""Klasse um eigene Treiber fuer die virtuellen Devices zu erstellen. Klasse um eigene Treiber fuer die virtuellen Devices zu erstellen.
Mit dieser Klasse werden nur angegebene Virtuelle Devices mit RevPiModIO Mit dieser Klasse werden nur angegebene Virtuelle Devices mit RevPiModIO
verwaltet. Bei Instantiierung werden automatisch die Inputs und Outputs verwaltet. Bei Instantiierung werden automatisch die Inputs und Outputs
verdreht, um das Schreiben der Inputs zu ermoeglichen. Die Daten koennen verdreht, um das Schreiben der Inputs zu ermoeglichen. Die Daten koennen
dann ueber logiCAD an den Devices abgerufen werden. dann ueber logiCAD an den Devices abgerufen werden.
""" """
__slots__ = () __slots__ = ()
def __init__( def __init__(
self, virtdev, autorefresh=False, monitoring=False, self, virtdev, autorefresh=False,
syncoutputs=True, procimg=None, configrsc=None, debug=True, syncoutputs=True, procimg=None, configrsc=None, debug=True,
replace_io_file=None, direct_output=False): replace_io_file=None, direct_output=False):
"""Instantiiert die Grundfunktionen. """
Instantiiert die Grundfunktionen.
Parameter 'monitoring' und 'simulator' stehen hier nicht zur Parameter 'monitoring' und 'simulator' stehen hier nicht zur
Verfuegung, da diese automatisch gesetzt werden. Verfuegung, da diese automatisch gesetzt werden.
@param virtdev Virtuelles Device oder mehrere als <class 'list'> :param virtdev: Virtuelles Device oder mehrere als <class 'list'>
@see #RevPiModIO.__init__ RevPiModIO.__init__(...) :ref: :func:`RevPiModIO.__init__()`
""" """
# Parent mit monitoring=False und simulator=True laden # Parent mit monitoring=False und simulator=True laden

View File

@@ -1,19 +1,20 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""RevPiModIO Hauptklasse fuer Netzwerkzugriff.""" """RevPiModIO Hauptklasse fuer Netzwerkzugriff."""
__author__ = "Sven Sager"
__copyright__ = "Copyright (C) 2018 Sven Sager"
__license__ = "LGPLv3"
import socket import socket
import warnings import warnings
from configparser import ConfigParser from configparser import ConfigParser
from json import loads as jloads from json import loads as jloads
from re import compile from re import compile
from revpimodio2 import DeviceNotFoundError from threading import Event, Lock, Thread
from threading import Thread, Event, Lock
from revpimodio2 import DeviceNotFoundError
from .device import Device from .device import Device
from .modio import RevPiModIO as _RevPiModIO from .modio import RevPiModIO as _RevPiModIO
__author__ = "Sven Sager"
__copyright__ = "Copyright (C) 2018 Sven Sager"
__license__ = "LGPLv3"
# Synchronisierungsbefehl # Synchronisierungsbefehl
_syssync = b'\x01\x06\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17' _syssync = b'\x01\x06\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17'
# Disconnectbefehl # Disconnectbefehl
@@ -33,42 +34,39 @@ HASH_FAIL = b'\xff' * 16
class AclException(Exception): class AclException(Exception):
"""Probleme mit Berechtigungen.""" """Probleme mit Berechtigungen."""
pass pass
class ConfigChanged(Exception): class ConfigChanged(Exception):
"""Aenderung der piCtory oder replace_ios Datei.""" """Aenderung der piCtory oder replace_ios Datei."""
pass pass
class NetFH(Thread): class NetFH(Thread):
"""
"""Netzwerk File Handler fuer das Prozessabbild. Netzwerk File Handler fuer das Prozessabbild.
Dieses FileObject-like Object verwaltet das Lesen und Schriben des Dieses FileObject-like Object verwaltet das Lesen und Schriben des
Prozessabbilds ueber das Netzwerk. Ein entfernter Revolution Pi kann Prozessabbilds ueber das Netzwerk. Ein entfernter Revolution Pi kann
so gesteuert werden. so gesteuert werden.
""" """
__slots__ = "__by_buff", "__check_replace_ios", "__config_changed", \ __slots__ = "__by_buff", "__check_replace_ios", "__config_changed", \
"__int_buff", "__dictdirty", "__flusherr", "__replace_ios_h", \ "__int_buff", "__dictdirty", "__flusherr", "__replace_ios_h", \
"__pictory_h", "__position", "__sockact", "__sockerr", "__sockend", \ "__pictory_h", "__position", "__sockact", "__sockerr", "__sockend", \
"__socklock", "__timeout", "__trigger", "__waitsync", "_address", \ "__socklock", "__timeout", "__trigger", "__waitsync", "_address", \
"_slavesock", "daemon" "_slavesock", "daemon"
def __init__(self, address, check_replace_ios, timeout=500): def __init__(self, address: tuple, check_replace_ios: bool, timeout=500):
"""Init NetFH-class. """
Init NetFH-class.
@param address IP Adresse, Port des RevPi als <class 'tuple'>
@param check_replace_ios Prueft auf Veraenderungen der Datei
@param timeout Timeout in Millisekunden der Verbindung
:param address: IP Adresse, Port des RevPi als <class 'tuple'>
:param check_replace_ios: Prueft auf Veraenderungen der Datei
:param timeout: Timeout in Millisekunden der Verbindung
""" """
super().__init__() super().__init__()
self.daemon = True self.daemon = True
@@ -114,10 +112,13 @@ class NetFH(Thread):
"""NetworkFileHandler beenden.""" """NetworkFileHandler beenden."""
self.close() self.close()
def __check_acl(self, bytecode): def __check_acl(self, bytecode: bytes) -> None:
"""Pueft ob ACL auf RevPi den Vorgang erlaubt oder wirft exception.""" """
if bytecode == b'\x18': Pueft ob ACL auf RevPi den Vorgang erlaubt oder wirft exception.
:param bytecode: Antwort, die geprueft werden solll
"""
if bytecode == b'\x18':
# Alles beenden, wenn nicht erlaubt # Alles beenden, wenn nicht erlaubt
self.__sockend.set() self.__sockend.set()
self.__sockerr.set() self.__sockerr.set()
@@ -128,10 +129,12 @@ class NetFH(Thread):
"reload revpipyload!" "reload revpipyload!"
) )
def __set_systimeout(self, value): def __set_systimeout(self, value: int) -> None:
"""Systemfunktion fuer Timeoutberechnung. """
@param value Timeout in Millisekunden 100 - 60000""" Systemfunktion fuer Timeoutberechnung.
:param value: Timeout in Millisekunden 100 - 60000
"""
if isinstance(value, int) and (100 <= value <= 60000): if isinstance(value, int) and (100 <= value <= 60000):
self.__timeout = value / 1000 self.__timeout = value / 1000
@@ -147,7 +150,7 @@ class NetFH(Thread):
else: else:
raise ValueError("value must between 10 and 60000 milliseconds") raise ValueError("value must between 10 and 60000 milliseconds")
def _connect(self): def _connect(self) -> None:
"""Stellt die Verbindung zu einem RevPiSlave her.""" """Stellt die Verbindung zu einem RevPiSlave her."""
# Neuen Socket aufbauen # Neuen Socket aufbauen
so = socket.socket(socket.AF_INET, socket.SOCK_STREAM) so = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -212,13 +215,13 @@ class NetFH(Thread):
for pos in self.__dictdirty: for pos in self.__dictdirty:
self.set_dirtybytes(pos, self.__dictdirty[pos]) self.set_dirtybytes(pos, self.__dictdirty[pos])
def _direct_send(self, send_bytes, recv_count): def _direct_send(self, send_bytes: bytes, recv_count: int) -> bytes:
"""Fuer debugging direktes Senden von Daten. """
Fuer debugging direktes Senden von Daten.
@param send_bytes Bytes, die gesendet werden sollen
@param recv_count Anzahl der Empfangsbytes
@returns Empfangende Bytes
:param send_bytes: Bytes, die gesendet werden sollen
:param recv_count: Anzahl der Empfangsbytes
:return: Empfangende Bytes
""" """
if self.__sockend.is_set(): if self.__sockend.is_set():
raise ValueError("I/O operation on closed file") raise ValueError("I/O operation on closed file")
@@ -230,9 +233,12 @@ class NetFH(Thread):
self.__trigger = True self.__trigger = True
return recv return recv
def clear_dirtybytes(self, position=None): def clear_dirtybytes(self, position=None) -> None:
"""Entfernt die konfigurierten Dirtybytes vom RevPi Slave. """
@param position Startposition der Dirtybytes""" Entfernt die konfigurierten Dirtybytes vom RevPi Slave.
:param position: Startposition der Dirtybytes
"""
if self.__config_changed: if self.__config_changed:
raise ConfigChanged("configuration on revolution pi was changed") raise ConfigChanged("configuration on revolution pi was changed")
if self.__sockend.is_set(): if self.__sockend.is_set():
@@ -255,7 +261,6 @@ class NetFH(Thread):
check = self._slavesock.recv(1) check = self._slavesock.recv(1)
if check != b'\x1e': if check != b'\x1e':
# ACL prüfen und ggf Fehler werfen # ACL prüfen und ggf Fehler werfen
self.__check_acl(check) self.__check_acl(check)
@@ -279,7 +284,7 @@ class NetFH(Thread):
self.__trigger = True self.__trigger = True
def close(self): def close(self) -> None:
"""Verbindung trennen.""" """Verbindung trennen."""
if self.__sockend.is_set(): if self.__sockend.is_set():
return return
@@ -302,7 +307,7 @@ class NetFH(Thread):
self._slavesock.close() self._slavesock.close()
def flush(self): def flush(self) -> None:
"""Schreibpuffer senden.""" """Schreibpuffer senden."""
if self.__config_changed: if self.__config_changed:
raise ConfigChanged("configuration on revolution pi was changed") raise ConfigChanged("configuration on revolution pi was changed")
@@ -334,35 +339,53 @@ class NetFH(Thread):
self.__trigger = True self.__trigger = True
def get_closed(self): def get_closed(self) -> bool:
"""Pruefen ob Verbindung geschlossen ist. """
@return True, wenn Verbindung geschlossen ist""" Pruefen ob Verbindung geschlossen ist.
:return: True, wenn Verbindung geschlossen ist
"""
return self.__sockend.is_set() return self.__sockend.is_set()
def get_config_changed(self): def get_config_changed(self) -> bool:
"""Pruefen ob RevPi Konfiguration geaendert wurde. """
@return True, wenn RevPi Konfiguration geaendert ist""" Pruefen ob RevPi Konfiguration geaendert wurde.
:return: True, wenn RevPi Konfiguration geaendert ist
"""
return self.__config_changed return self.__config_changed
def get_name(self): def get_name(self) -> str:
"""Verbindugnsnamen zurueckgeben. """
@return <class 'str'> IP:PORT""" Verbindugnsnamen zurueckgeben.
:return: <class 'str'> IP:PORT
"""
return "{0}:{1}".format(*self._address) return "{0}:{1}".format(*self._address)
def get_reconnecting(self): def get_reconnecting(self) -> bool:
"""Interner reconnect aktiv wegen Netzwerkfehlern. """
@return True, wenn reconnect aktiv""" Interner reconnect aktiv wegen Netzwerkfehlern.
:return: True, wenn reconnect aktiv
"""
return self.__sockerr.is_set() return self.__sockerr.is_set()
def get_timeout(self): def get_timeout(self) -> int:
"""Gibt aktuellen Timeout zurueck. """
@return <class 'int'> in Millisekunden""" Gibt aktuellen Timeout zurueck.
:return: <class 'int'> in Millisekunden
"""
return int(self.__timeout * 1000) return int(self.__timeout * 1000)
def ioctl(self, request, arg=b''): def ioctl(self, request: int, arg=b'') -> None:
"""IOCTL Befehle ueber das Netzwerk senden. """
@param request Request as <class 'int'> IOCTL Befehle ueber das Netzwerk senden.
@param arg Argument as <class 'byte'>"""
:param request: Request as <class 'int'>
:param arg: Argument as <class 'byte'>
"""
if self.__config_changed: if self.__config_changed:
raise ConfigChanged("configuration on revolution pi was changed") raise ConfigChanged("configuration on revolution pi was changed")
if self.__sockend.is_set(): if self.__sockend.is_set():
@@ -383,7 +406,6 @@ class NetFH(Thread):
# Rückmeldebyte auswerten # Rückmeldebyte auswerten
check = self._slavesock.recv(1) check = self._slavesock.recv(1)
if check != b'\x1e': if check != b'\x1e':
# ACL prüfen und ggf Fehler werfen # ACL prüfen und ggf Fehler werfen
self.__check_acl(check) self.__check_acl(check)
@@ -392,10 +414,13 @@ class NetFH(Thread):
self.__trigger = True self.__trigger = True
def read(self, length): def read(self, length: int) -> bytes:
"""Daten ueber das Netzwerk lesen. """
@param length Anzahl der Bytes Daten ueber das Netzwerk lesen.
@return Gelesene <class 'bytes'>"""
:param length: Anzahl der Bytes
:return: Gelesene <class 'bytes'>
"""
if self.__config_changed: if self.__config_changed:
raise ConfigChanged("configuration on revolution pi was changed") raise ConfigChanged("configuration on revolution pi was changed")
if self.__sockend.is_set(): if self.__sockend.is_set():
@@ -423,9 +448,12 @@ class NetFH(Thread):
return bytes(bytesbuff) return bytes(bytesbuff)
def readpictory(self): def readpictory(self) -> bytes:
"""Ruft die piCtory Konfiguration ab. """
@return <class 'bytes'> piCtory Datei""" Ruft die piCtory Konfiguration ab.
:return: <class 'bytes'> piCtory Datei
"""
if self.__sockend.is_set(): if self.__sockend.is_set():
raise ValueError("read of closed file") raise ValueError("read of closed file")
@@ -454,9 +482,12 @@ class NetFH(Thread):
self.__sockerr.set() self.__sockerr.set()
raise IOError("readpictory error on network") raise IOError("readpictory error on network")
def readreplaceio(self): def readreplaceio(self) -> bytes:
"""Ruft die replace_io Konfiguration ab. """
@return <class 'bytes'> replace_io_file""" Ruft die replace_io Konfiguration ab.
:return: <class 'bytes'> replace_io_file
"""
if self.__sockend.is_set(): if self.__sockend.is_set():
raise ValueError("read of closed file") raise ValueError("read of closed file")
@@ -485,7 +516,7 @@ class NetFH(Thread):
self.__sockerr.set() self.__sockerr.set()
raise IOError("readreplaceio error on network") raise IOError("readreplaceio error on network")
def run(self): def run(self) -> None:
"""Handler fuer Synchronisierung.""" """Handler fuer Synchronisierung."""
state_reconnect = False state_reconnect = False
while not self.__sockend.is_set(): while not self.__sockend.is_set():
@@ -532,7 +563,7 @@ class NetFH(Thread):
# Warten nach Sync damit Instantiierung funktioniert # Warten nach Sync damit Instantiierung funktioniert
self.__sockerr.wait(self.__waitsync) self.__sockerr.wait(self.__waitsync)
def seek(self, position): def seek(self, position: int) -> None:
"""Springt an angegebene Position. """Springt an angegebene Position.
@param position An diese Position springen""" @param position An diese Position springen"""
if self.__config_changed: if self.__config_changed:
@@ -541,10 +572,13 @@ class NetFH(Thread):
raise ValueError("seek of closed file") raise ValueError("seek of closed file")
self.__position = int(position) self.__position = int(position)
def set_dirtybytes(self, position, dirtybytes): def set_dirtybytes(self, position: int, dirtybytes: bytes) -> None:
"""Konfiguriert Dirtybytes fuer Prozessabbild bei Verbindungsfehler. """
@param positon Startposition zum Schreiben Konfiguriert Dirtybytes fuer Prozessabbild bei Verbindungsfehler.
@param dirtybytes <class 'bytes'> die geschrieben werden sollen"""
:param position: Startposition zum Schreiben
:param dirtybytes: <class 'bytes'> die geschrieben werden sollen
"""
if self.__config_changed: if self.__config_changed:
raise ConfigChanged("configuration on revolution pi was changed") raise ConfigChanged("configuration on revolution pi was changed")
if self.__sockend.is_set(): if self.__sockend.is_set():
@@ -564,7 +598,6 @@ class NetFH(Thread):
check = self._slavesock.recv(1) check = self._slavesock.recv(1)
if check != b'\x1e': if check != b'\x1e':
# ACL prüfen und ggf Fehler werfen # ACL prüfen und ggf Fehler werfen
self.__check_acl(check) self.__check_acl(check)
@@ -585,9 +618,12 @@ class NetFH(Thread):
self.__trigger = True self.__trigger = True
def set_timeout(self, value): def set_timeout(self, value: int) -> None:
"""Setzt Timeoutwert fuer Verbindung. """
@param value Timeout in Millisekunden""" Setzt Timeoutwert fuer Verbindung.
:param value: Timeout in Millisekunden
"""
if self.__sockend.is_set(): if self.__sockend.is_set():
raise ValueError("I/O operation on closed file") raise ValueError("I/O operation on closed file")
@@ -612,19 +648,25 @@ class NetFH(Thread):
self.__trigger = True self.__trigger = True
def tell(self): def tell(self) -> int:
"""Gibt aktuelle Position zurueck. """
@return int aktuelle Position""" Gibt aktuelle Position zurueck.
:return: Aktuelle Position
"""
if self.__config_changed: if self.__config_changed:
raise ConfigChanged("configuration on revolution pi was changed") raise ConfigChanged("configuration on revolution pi was changed")
if self.__sockend.is_set(): if self.__sockend.is_set():
raise ValueError("I/O operation on closed file") raise ValueError("I/O operation on closed file")
return self.__position return self.__position
def write(self, bytebuff): def write(self, bytebuff: bytes) -> int:
"""Daten ueber das Netzwerk schreiben. """
@param bytebuff Bytes zum schreiben Daten ueber das Netzwerk schreiben.
@return <class 'int'> Anzahl geschriebener bytes"""
:param bytebuff: Bytes zum schreiben
:return: <class 'int'> Anzahl geschriebener bytes
"""
if self.__config_changed: if self.__config_changed:
raise ConfigChanged("configuration on revolution pi was changed") raise ConfigChanged("configuration on revolution pi was changed")
if self.__sockend.is_set(): if self.__sockend.is_set():
@@ -638,10 +680,10 @@ class NetFH(Thread):
# Datenblöcke mit Group Seperator in Puffer ablegen # Datenblöcke mit Group Seperator in Puffer ablegen
self.__by_buff += b'\x01SD' + \ self.__by_buff += b'\x01SD' + \
self.__position.to_bytes(length=2, byteorder="little") + \ self.__position.to_bytes(length=2, byteorder="little") + \
len(bytebuff).to_bytes(length=2, byteorder="little") + \ len(bytebuff).to_bytes(length=2, byteorder="little") + \
b'\x1d\x00\x00\x00\x00\x00\x00\x00\x17' + \ b'\x1d\x00\x00\x00\x00\x00\x00\x00\x17' + \
bytebuff bytebuff
# TODO: Bufferlänge und dann flushen? # TODO: Bufferlänge und dann flushen?
@@ -655,8 +697,8 @@ class NetFH(Thread):
class RevPiNetIO(_RevPiModIO): class RevPiNetIO(_RevPiModIO):
"""
"""Klasse fuer die Verwaltung der piCtory Konfiguration ueber das Netzwerk. Klasse fuer die Verwaltung der piCtory Konfiguration ueber das Netzwerk.
Diese Klasse uebernimmt die gesamte Konfiguration aus piCtory und bilded Diese Klasse uebernimmt die gesamte Konfiguration aus piCtory und bilded
die Devices und IOs ab. Sie uebernimmt die exklusive Verwaltung des die Devices und IOs ab. Sie uebernimmt die exklusive Verwaltung des
@@ -664,7 +706,6 @@ class RevPiNetIO(_RevPiModIO):
Sollten nur einzelne Devices gesteuert werden, verwendet man Sollten nur einzelne Devices gesteuert werden, verwendet man
RevPiModIOSelected() und uebergibt bei Instantiierung eine Liste mit RevPiModIOSelected() und uebergibt bei Instantiierung eine Liste mit
Device Positionen oder Device Namen. Device Positionen oder Device Namen.
""" """
__slots__ = "_address" __slots__ = "_address"
@@ -673,17 +714,17 @@ class RevPiNetIO(_RevPiModIO):
self, address, autorefresh=False, monitoring=False, self, address, autorefresh=False, monitoring=False,
syncoutputs=True, simulator=False, debug=True, syncoutputs=True, simulator=False, debug=True,
replace_io_file=None, direct_output=False): replace_io_file=None, direct_output=False):
"""Instantiiert die Grundfunktionen. """
Instantiiert die Grundfunktionen.
@param address: IP-Adresse <class 'str'> / (IP, Port) <class 'tuple'>
@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 direct_output Write outputs immediately to process image (slow)
:param address: IP-Adresse <class 'str'> / (IP, Port) <class 'tuple'>
: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 direct_output: Write outputs immediately to process image (slow)
""" """
check_ip = compile( check_ip = compile(
r"^(?P<ipn>(25[0-5]|(2[0-4]|[01]?\d|)\d))(\.(?P=ipn)){3}$" r"^(?P<ipn>(25[0-5]|(2[0-4]|[01]?\d|)\d))(\.(?P=ipn)){3}$"
@@ -745,15 +786,20 @@ class RevPiNetIO(_RevPiModIO):
self._configure_replace_io(self._get_cpreplaceio()) self._configure_replace_io(self._get_cpreplaceio())
def _create_myfh(self): def _create_myfh(self):
"""Erstellt NetworkFileObject. """
return FileObject""" Erstellt NetworkFileObject.
:return: FileObject
"""
self._buffedwrite = True self._buffedwrite = True
return NetFH(self._address, self._replace_io_file == ":network:") return NetFH(self._address, self._replace_io_file == ":network:")
def _get_cpreplaceio(self): def _get_cpreplaceio(self) -> ConfigParser:
"""Laed die replace_io Konfiguration ueber das Netzwerk. """
@return <class 'ConfigParser'> der replace io daten""" Laed die replace_io Konfiguration ueber das Netzwerk.
:return: <class 'ConfigParser'> der replace io daten
"""
# Normale Verwendung über Elternklasse erledigen # Normale Verwendung über Elternklasse erledigen
if self._replace_io_file != ":network:": if self._replace_io_file != ":network:":
return super()._get_cpreplaceio() return super()._get_cpreplaceio()
@@ -771,48 +817,60 @@ class RevPiNetIO(_RevPiModIO):
) )
return cp return cp
def disconnect(self): def disconnect(self) -> None:
"""Trennt Verbindungen und beendet autorefresh inkl. alle Threads.""" """Trennt Verbindungen und beendet autorefresh inkl. alle Threads."""
self.cleanup() self.cleanup()
def exit(self, full=True): def exit(self, full=True) -> None:
"""Beendet mainloop() und optional autorefresh. """
@see #RevPiModIO.exit(...)""" Beendet mainloop() und optional autorefresh.
:ref: :func:`RevPiModIO.exit()`
"""
try: try:
super().exit(full) super().exit(full)
except ConfigChanged: except ConfigChanged:
pass pass
def get_config_changed(self): def get_config_changed(self) -> bool:
"""Pruefen ob RevPi Konfiguration geaendert wurde. """
Pruefen ob RevPi Konfiguration geaendert wurde.
In diesem Fall ist die Verbindung geschlossen und RevPiNetIO muss In diesem Fall ist die Verbindung geschlossen und RevPiNetIO muss
neu instanziert werden. neu instanziert werden.
@return True, wenn RevPi Konfiguration geaendert ist""" :return: True, wenn RevPi Konfiguration geaendert ist
"""
return self._myfh.config_changed return self._myfh.config_changed
def get_jconfigrsc(self): def get_jconfigrsc(self) -> dict:
"""Laedt die piCotry Konfiguration und erstellt ein <class 'dict'>. """
@return <class 'dict'> der piCtory Konfiguration""" Laedt die piCotry Konfiguration und erstellt ein <class 'dict'>.
:return: <class 'dict'> der piCtory Konfiguration
"""
mynh = NetFH(self._address, False) mynh = NetFH(self._address, False)
byte_buff = mynh.readpictory() byte_buff = mynh.readpictory()
mynh.close() mynh.close()
return jloads(byte_buff.decode("utf-8")) return jloads(byte_buff.decode("utf-8"))
def get_reconnecting(self): def get_reconnecting(self) -> bool:
"""Interner reconnect aktiv wegen Netzwerkfehlern. """
Interner reconnect aktiv wegen Netzwerkfehlern.
Das Modul versucht intern die Verbindung neu herzustellen. Es ist Das Modul versucht intern die Verbindung neu herzustellen. Es ist
kein weiteres Zutun noetig. kein weiteres Zutun noetig.
@return True, wenn reconnect aktiv""" :return: True, wenn reconnect aktiv
"""
return self._myfh.reconnecting return self._myfh.reconnecting
def net_cleardefaultvalues(self, device=None): def net_cleardefaultvalues(self, device=None) -> None:
"""Loescht Defaultwerte vom PLC Slave. """
@param device nur auf einzelnes Device anwenden, sonst auf Alle""" Loescht Defaultwerte vom PLC Slave.
:param device: nur auf einzelnes Device anwenden, sonst auf Alle
"""
if self.monitoring: if self.monitoring:
raise RuntimeError( raise RuntimeError(
"can not send default values, while system is in " "can not send default values, while system is in "
@@ -829,10 +887,12 @@ class RevPiNetIO(_RevPiModIO):
for dev in mylist: for dev in mylist:
self._myfh.clear_dirtybytes(dev._offset + dev._slc_out.start) self._myfh.clear_dirtybytes(dev._offset + dev._slc_out.start)
def net_setdefaultvalues(self, device=None): def net_setdefaultvalues(self, device=None) -> None:
"""Konfiguriert den PLC Slave mit den piCtory Defaultwerten. """
@param device nur auf einzelnes Device anwenden, sonst auf Alle""" Konfiguriert den PLC Slave mit den piCtory Defaultwerten.
:param device: nur auf einzelnes Device anwenden, sonst auf Alle
"""
if self.monitoring: if self.monitoring:
raise RuntimeError( raise RuntimeError(
"can not send default values, while system is in " "can not send default values, while system is in "
@@ -881,14 +941,13 @@ class RevPiNetIO(_RevPiModIO):
class RevPiNetIOSelected(RevPiNetIO): class RevPiNetIOSelected(RevPiNetIO):
"""
"""Klasse fuer die Verwaltung einzelner Devices aus piCtory. Klasse fuer die Verwaltung einzelner Devices aus piCtory.
Diese Klasse uebernimmt nur angegebene Devices der piCtory Konfiguration Diese Klasse uebernimmt nur angegebene Devices der piCtory Konfiguration
und bilded sie inkl. IOs ab. Sie uebernimmt die exklusive Verwaltung des und bilded sie inkl. IOs ab. Sie uebernimmt die exklusive Verwaltung des
Adressbereichs im Prozessabbild an dem sich die angegebenen Devices Adressbereichs im Prozessabbild an dem sich die angegebenen Devices
befinden und stellt sicher, dass die Daten synchron sind. befinden und stellt sicher, dass die Daten synchron sind.
""" """
__slots__ = () __slots__ = ()
@@ -897,16 +956,16 @@ class RevPiNetIOSelected(RevPiNetIO):
self, address, deviceselection, autorefresh=False, self, address, deviceselection, autorefresh=False,
monitoring=False, syncoutputs=True, simulator=False, debug=True, monitoring=False, syncoutputs=True, simulator=False, debug=True,
replace_io_file=None, direct_output=False): replace_io_file=None, direct_output=False):
"""Instantiiert nur fuer angegebene Devices die Grundfunktionen. """
Instantiiert nur fuer angegebene Devices die Grundfunktionen.
Der Parameter deviceselection kann eine einzelne Der Parameter deviceselection kann eine einzelne
Device Position / einzelner Device Name sein oder eine Liste mit Device Position / einzelner Device Name sein oder eine Liste mit
mehreren Positionen / Namen mehreren Positionen / Namen
@param address: IP-Adresse <class 'str'> / (IP, Port) <class 'tuple'> :param address: IP-Adresse <class 'str'> / (IP, Port) <class 'tuple'>
@param deviceselection Positionsnummer oder Devicename :param deviceselection: Positionsnummer oder Devicename
@see #RevPiNetIO.__init__ RevPiNetIO.__init__(...) :ref: :func:`RevPiNetIO.__init__()`
""" """
super().__init__( super().__init__(
address, autorefresh, monitoring, syncoutputs, simulator, debug, address, autorefresh, monitoring, syncoutputs, simulator, debug,
@@ -951,31 +1010,30 @@ class RevPiNetIOSelected(RevPiNetIO):
class RevPiNetIODriver(RevPiNetIOSelected): class RevPiNetIODriver(RevPiNetIOSelected):
"""
"""Klasse um eigene Treiber fuer die virtuellen Devices zu erstellen. Klasse um eigene Treiber fuer die virtuellen Devices zu erstellen.
Mit dieser Klasse werden nur angegebene Virtuelle Devices mit RevPiModIO Mit dieser Klasse werden nur angegebene Virtuelle Devices mit RevPiModIO
verwaltet. Bei Instantiierung werden automatisch die Inputs und Outputs verwaltet. Bei Instantiierung werden automatisch die Inputs und Outputs
verdreht, um das Schreiben der Inputs zu ermoeglichen. Die Daten koennen verdreht, um das Schreiben der Inputs zu ermoeglichen. Die Daten koennen
dann ueber logiCAD an den Devices abgerufen werden. dann ueber logiCAD an den Devices abgerufen werden.
""" """
__slots__ = () __slots__ = ()
def __init__( def __init__(
self, address, virtdev, autorefresh=False, monitoring=False, self, address, virtdev, autorefresh=False,
syncoutputs=True, debug=True, replace_io_file=None, syncoutputs=True, debug=True, replace_io_file=None,
direct_output=False): direct_output=False):
"""Instantiiert die Grundfunktionen. """
Instantiiert die Grundfunktionen.
Parameter 'monitoring' und 'simulator' stehen hier nicht zur Parameter 'monitoring' und 'simulator' stehen hier nicht zur
Verfuegung, da diese automatisch gesetzt werden. Verfuegung, da diese automatisch gesetzt werden.
@param address: IP-Adresse <class 'str'> / (IP, Port) <class 'tuple'> :param address: IP-Adresse <class 'str'> / (IP, Port) <class 'tuple'>
@param virtdev Virtuelles Device oder mehrere als <class 'list'> :param virtdev: Virtuelles Device oder mehrere als <class 'list'>
@see #RevPiModIO.__init__ RevPiModIO.__init__(...) :ref: :func:`RevPiModIO.__init__()`
""" """
# Parent mit monitoring=False und simulator=True laden # Parent mit monitoring=False und simulator=True laden
super().__init__( super().__init__(

View File

@@ -6,13 +6,15 @@ __license__ = "LGPLv3"
class Summary(object): class Summary(object):
"""Bildet die Summary-Sektion der config.rsc ab.""" """Bildet die Summary-Sektion der config.rsc ab."""
__slots__ = "inptotal", "outtotal" __slots__ = "inptotal", "outtotal"
def __init__(self, summary): def __init__(self, summary: dict):
"""Instantiiert die RevPiSummary-Klasse. """
@param summary piCtory Summaryinformationen""" Instantiiert die RevPiSummary-Klasse.
:param summary: piCtory Summaryinformationen
"""
self.inptotal = summary["inpTotal"] self.inptotal = summary["inpTotal"]
self.outtotal = summary["outTotal"] self.outtotal = summary["outTotal"]