mirror of
https://github.com/naruxde/revpimodio2.git
synced 2025-11-08 13:53:53 +01:00
Docstrings optimieren 2 - Code-Cleanup
This commit is contained in:
@@ -12,7 +12,7 @@ class App(object):
|
||||
|
||||
__slots__ = "name", "version", "language", "layout", "savets"
|
||||
|
||||
def __init__(self, app):
|
||||
def __init__(self, app: dict):
|
||||
"""
|
||||
Instantiiert die App-Klasse.
|
||||
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""RevPiModIO Hauptklasse fuer piControl0 Zugriff."""
|
||||
__author__ = "Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||
__license__ = "LGPLv3"
|
||||
|
||||
import warnings
|
||||
from configparser import ConfigParser
|
||||
from json import load as jload
|
||||
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 queue import Empty
|
||||
from revpimodio2 import acheck, DeviceNotFoundError, BOTH, RISING, FALLING
|
||||
from signal import signal, SIG_DFL, SIGINT, SIGTERM
|
||||
from signal import SIGINT, SIGTERM, SIG_DFL, signal
|
||||
from stat import S_ISCHR
|
||||
from threading import Thread, Event, Lock
|
||||
from threading import Event, Lock, Thread
|
||||
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):
|
||||
|
||||
"""Klasse fuer die Verwaltung der piCtory Konfiguration.
|
||||
"""
|
||||
Klasse fuer die Verwaltung der piCtory Konfiguration.
|
||||
|
||||
Diese Klasse uebernimmt die gesamte Konfiguration aus piCtory und bilded
|
||||
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
|
||||
RevPiModIOSelected() und uebergibt bei Instantiierung eine Liste mit
|
||||
Device Positionen oder Device Namen.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "__cleanupfunc", "_autorefresh", "_buffedwrite", \
|
||||
"_configrsc", "_direct_output", "_exit", "_imgwriter", "_ioerror", \
|
||||
"_length", "_looprunning", "_lst_devselect", "_lst_refresh", \
|
||||
"_maxioerrors", "_myfh", "_myfh_lck", "_monitoring", "_procimg", \
|
||||
"_simulator", "_syncoutputs", "_th_mainloop", "_waitexit", \
|
||||
"core", "app", "device", "exitsignal", "io", "summary", "_debug", \
|
||||
"_replace_io_file", "_run_on_pi"
|
||||
"_configrsc", "_direct_output", "_exit", "_imgwriter", "_ioerror", \
|
||||
"_length", "_looprunning", "_lst_devselect", "_lst_refresh", \
|
||||
"_maxioerrors", "_myfh", "_myfh_lck", "_monitoring", "_procimg", \
|
||||
"_simulator", "_syncoutputs", "_th_mainloop", "_waitexit", \
|
||||
"core", "app", "device", "exitsignal", "io", "summary", "_debug", \
|
||||
"_replace_io_file", "_run_on_pi"
|
||||
|
||||
def __init__(
|
||||
self, autorefresh=False, monitoring=False, syncoutputs=True,
|
||||
procimg=None, configrsc=None, simulator=False, debug=True,
|
||||
replace_io_file=None, direct_output=False):
|
||||
"""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)
|
||||
"""
|
||||
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)
|
||||
"""
|
||||
# Parameterprüfung
|
||||
acheck(
|
||||
@@ -127,10 +127,13 @@ class RevPiModIO(object):
|
||||
if self._myfh is not None:
|
||||
self._myfh.close()
|
||||
|
||||
def __evt_exit(self, signum, sigframe):
|
||||
"""Eventhandler fuer Programmende.
|
||||
@param signum Signalnummer
|
||||
@param sigframe Signalframe"""
|
||||
def __evt_exit(self, signum, sigframe) -> None:
|
||||
"""
|
||||
Eventhandler fuer Programmende.
|
||||
|
||||
:param signum: Signalnummer
|
||||
:param sigframe: Signalframe
|
||||
"""
|
||||
signal(SIGINT, SIG_DFL)
|
||||
signal(SIGTERM, SIG_DFL)
|
||||
self.exit(full=True)
|
||||
@@ -140,10 +143,12 @@ class RevPiModIO(object):
|
||||
if not self._monitoring:
|
||||
self.writeprocimg()
|
||||
|
||||
def _configure(self, jconfigrsc):
|
||||
"""Verarbeitet die piCtory Konfigurationsdatei.
|
||||
@param jconfigrsc: Data to build IOs as <class 'dict'> of JSON"""
|
||||
def _configure(self, jconfigrsc: dict) -> None:
|
||||
"""
|
||||
Verarbeitet die piCtory Konfigurationsdatei.
|
||||
|
||||
:param jconfigrsc: Data to build IOs as <class 'dict'> of JSON
|
||||
"""
|
||||
# Filehandler konfigurieren, wenn er noch nicht existiert
|
||||
if self._myfh is None:
|
||||
self._myfh = self._create_myfh()
|
||||
@@ -283,12 +288,12 @@ class RevPiModIO(object):
|
||||
if self.core._slc_errorlimit1 is not None:
|
||||
io = self.io[
|
||||
self.core.offset + self.core._slc_errorlimit1.start
|
||||
][0]
|
||||
][0]
|
||||
io.set_value(io._defaultvalue)
|
||||
if self.core._slc_errorlimit2 is not None:
|
||||
io = self.io[
|
||||
self.core.offset + self.core._slc_errorlimit2.start
|
||||
][0]
|
||||
][0]
|
||||
io.set_value(io._defaultvalue)
|
||||
|
||||
# RS485 errors schreiben
|
||||
@@ -301,15 +306,15 @@ class RevPiModIO(object):
|
||||
# Summary Klasse instantiieren
|
||||
self.summary = summarymodule.Summary(jconfigrsc["Summary"])
|
||||
|
||||
def _configure_replace_io(self, creplaceio):
|
||||
"""Importiert ersetzte IOs in diese Instanz.
|
||||
def _configure_replace_io(self, creplaceio: ConfigParser) -> None:
|
||||
"""
|
||||
Importiert ersetzte IOs in diese Instanz.
|
||||
|
||||
Importiert ersetzte IOs, welche vorher mit .export_replaced_ios(...)
|
||||
in eine Datei exportiert worden sind. Diese IOs werden in dieser
|
||||
Instanz wiederhergestellt.
|
||||
|
||||
@param ireplaceio: Data to replace ios as <class 'ConfigParser'>
|
||||
|
||||
:param creplaceio: Data to replace ios as <class 'ConfigParser'>
|
||||
"""
|
||||
for io in creplaceio:
|
||||
if io == "DEFAULT":
|
||||
@@ -371,19 +376,28 @@ class RevPiModIO(object):
|
||||
)
|
||||
|
||||
def _create_myfh(self):
|
||||
"""Erstellt FileObject mit Pfad zum procimg.
|
||||
return FileObject"""
|
||||
"""
|
||||
Erstellt FileObject mit Pfad zum procimg.
|
||||
|
||||
:return: FileObject
|
||||
"""
|
||||
self._buffedwrite = False
|
||||
return open(self._procimg, "r+b", 0)
|
||||
|
||||
def _get_configrsc(self):
|
||||
"""Getter function.
|
||||
@return Pfad der verwendeten piCtory Konfiguration"""
|
||||
def _get_configrsc(self) -> str:
|
||||
"""
|
||||
Getter function.
|
||||
|
||||
:return: Pfad der verwendeten piCtory Konfiguration
|
||||
"""
|
||||
return self._configrsc
|
||||
|
||||
def _get_cpreplaceio(self):
|
||||
"""Laed die replace_io_file Konfiguration und verarbeitet sie.
|
||||
@return <class 'ConfigParser'> der replace io daten"""
|
||||
def _get_cpreplaceio(self) -> ConfigParser:
|
||||
"""
|
||||
Laed die replace_io_file Konfiguration und verarbeitet sie.
|
||||
|
||||
:return: <class 'ConfigParser'> der replace io daten
|
||||
"""
|
||||
cp = ConfigParser()
|
||||
|
||||
# TODO: verfeinern!
|
||||
@@ -400,58 +414,85 @@ class RevPiModIO(object):
|
||||
|
||||
return cp
|
||||
|
||||
def _get_cycletime(self):
|
||||
"""Gibt Aktualisierungsrate in ms der Prozessabbildsynchronisierung aus.
|
||||
@return Millisekunden"""
|
||||
def _get_cycletime(self) -> int:
|
||||
"""
|
||||
Gibt Aktualisierungsrate in ms der Prozessabbildsynchronisierung aus.
|
||||
|
||||
:return: Millisekunden
|
||||
"""
|
||||
return self._imgwriter.refresh
|
||||
|
||||
def _get_debug(self):
|
||||
"""Gibt Status des Debugflags zurueck.
|
||||
@return Status des Debugflags"""
|
||||
def _get_debug(self) -> bool:
|
||||
"""
|
||||
Gibt Status des Debugflags zurueck.
|
||||
|
||||
:return: Status des Debugflags
|
||||
"""
|
||||
return self._debug == 1
|
||||
|
||||
def _get_ioerrors(self):
|
||||
"""Getter function.
|
||||
@return Aktuelle Anzahl gezaehlter Fehler"""
|
||||
def _get_ioerrors(self) -> int:
|
||||
"""
|
||||
Getter function.
|
||||
|
||||
:return: Aktuelle Anzahl gezaehlter Fehler
|
||||
"""
|
||||
return self._ioerror
|
||||
|
||||
def _get_length(self):
|
||||
"""Getter function.
|
||||
@return Laenge in Bytes der Devices"""
|
||||
def _get_length(self) -> int:
|
||||
"""
|
||||
Getter function.
|
||||
|
||||
:return: Laenge in Bytes der Devices
|
||||
"""
|
||||
return self._length
|
||||
|
||||
def _get_maxioerrors(self):
|
||||
"""Getter function.
|
||||
@return Anzahl erlaubte Fehler"""
|
||||
def _get_maxioerrors(self) -> int:
|
||||
"""
|
||||
Getter function.
|
||||
|
||||
:return: Anzahl erlaubte Fehler
|
||||
"""
|
||||
return self._maxioerrors
|
||||
|
||||
def _get_monitoring(self):
|
||||
"""Getter function.
|
||||
@return True, wenn als Monitoring gestartet"""
|
||||
def _get_monitoring(self) -> bool:
|
||||
"""
|
||||
Getter function.
|
||||
|
||||
:return: True, wenn als Monitoring gestartet
|
||||
"""
|
||||
return self._monitoring
|
||||
|
||||
def _get_procimg(self):
|
||||
"""Getter function.
|
||||
@return Pfad des verwendeten Prozessabbilds"""
|
||||
def _get_procimg(self) -> str:
|
||||
"""
|
||||
Getter function.
|
||||
|
||||
:return: Pfad des verwendeten Prozessabbilds
|
||||
"""
|
||||
return self._procimg
|
||||
|
||||
def _get_replace_io_file(self):
|
||||
"""Gibt Pfad zur verwendeten replace IO Datei aus.
|
||||
@return Pfad zur replace IO Datei"""
|
||||
def _get_replace_io_file(self) -> str:
|
||||
"""
|
||||
Gibt Pfad zur verwendeten replace IO Datei aus.
|
||||
|
||||
:return: Pfad zur replace IO Datei
|
||||
"""
|
||||
return self._replace_io_file
|
||||
|
||||
def _get_simulator(self):
|
||||
"""Getter function.
|
||||
@return True, wenn als Simulator gestartet"""
|
||||
def _get_simulator(self) -> bool:
|
||||
"""
|
||||
Getter function.
|
||||
|
||||
:return: True, wenn als Simulator gestartet
|
||||
"""
|
||||
return self._simulator
|
||||
|
||||
def _gotioerror(self, action, e=None, show_warn=True):
|
||||
"""IOError Verwaltung fuer Prozessabbildzugriff.
|
||||
|
||||
@param action Zusatzinformationen zum loggen
|
||||
@param e Exception to log if debug is enabled
|
||||
@param show_warn Warnung anzeigen
|
||||
def _gotioerror(self, action: str, e=None, show_warn=True) -> None:
|
||||
"""
|
||||
IOError Verwaltung fuer Prozessabbildzugriff.
|
||||
|
||||
:param action: Zusatzinformationen zum loggen
|
||||
:param e: Exception to log if debug is enabled
|
||||
:param show_warn: Warnung anzeigen
|
||||
"""
|
||||
self._ioerror += 1
|
||||
if self._maxioerrors != 0 and self._ioerror >= self._maxioerrors:
|
||||
@@ -475,9 +516,12 @@ class RevPiModIO(object):
|
||||
RuntimeWarning
|
||||
)
|
||||
|
||||
def _set_cycletime(self, milliseconds):
|
||||
"""Setzt Aktualisierungsrate der Prozessabbild-Synchronisierung.
|
||||
@param milliseconds <class 'int'> in Millisekunden"""
|
||||
def _set_cycletime(self, milliseconds: int) -> None:
|
||||
"""
|
||||
Setzt Aktualisierungsrate der Prozessabbild-Synchronisierung.
|
||||
|
||||
:param milliseconds: <class 'int'> in Millisekunden
|
||||
"""
|
||||
if self._looprunning:
|
||||
raise RuntimeError(
|
||||
"can not change cycletime when cycleloop or mainloop is "
|
||||
@@ -486,9 +530,12 @@ class RevPiModIO(object):
|
||||
else:
|
||||
self._imgwriter.refresh = milliseconds
|
||||
|
||||
def _set_debug(self, value):
|
||||
"""Setzt debugging Status um mehr Meldungen zu erhalten oder nicht.
|
||||
@param value Wenn True, werden umfangreiche Medungen angezeigt"""
|
||||
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
|
||||
"""
|
||||
if type(value) == bool:
|
||||
value = int(value)
|
||||
if not type(value) == int:
|
||||
@@ -506,18 +553,24 @@ class RevPiModIO(object):
|
||||
else:
|
||||
warnings.filterwarnings("always", module="revpimodio2")
|
||||
|
||||
def _set_maxioerrors(self, value):
|
||||
"""Setzt Anzahl der maximal erlaubten Fehler bei Prozessabbildzugriff.
|
||||
@param value Anzahl erlaubte Fehler"""
|
||||
def _set_maxioerrors(self, value: int) -> None:
|
||||
"""
|
||||
Setzt Anzahl der maximal erlaubten Fehler bei Prozessabbildzugriff.
|
||||
|
||||
:param value: Anzahl erlaubte Fehler
|
||||
"""
|
||||
if type(value) == int and value >= 0:
|
||||
self._maxioerrors = value
|
||||
else:
|
||||
raise ValueError("value must be 0 or a positive integer")
|
||||
|
||||
def _simulate_ioctl(self, request, arg=b''):
|
||||
"""Simuliert IOCTL Funktionen auf procimg Datei.
|
||||
@param request IO Request
|
||||
@param arg: Request argument"""
|
||||
def _simulate_ioctl(self, request: int, arg=b'') -> None:
|
||||
"""
|
||||
Simuliert IOCTL Funktionen auf procimg Datei.
|
||||
|
||||
:param request: IO Request
|
||||
:param arg: Request argument
|
||||
"""
|
||||
if request == 19216:
|
||||
# Einzelnes Bit setzen
|
||||
byte_address = int.from_bytes(arg[:2], byteorder="little")
|
||||
@@ -552,7 +605,7 @@ class RevPiModIO(object):
|
||||
for i in range(16):
|
||||
if bool(bit_field & 1 << i):
|
||||
io_byte = self.device[dev_position].offset \
|
||||
+ int(self.device[dev_position]._lst_counter[i])
|
||||
+ int(self.device[dev_position]._lst_counter[i])
|
||||
break
|
||||
|
||||
if io_byte == -1:
|
||||
@@ -564,12 +617,12 @@ class RevPiModIO(object):
|
||||
if self._buffedwrite:
|
||||
self._myfh.flush()
|
||||
|
||||
def autorefresh_all(self):
|
||||
def autorefresh_all(self) -> None:
|
||||
"""Setzt alle Devices in autorefresh Funktion."""
|
||||
for dev in self.device:
|
||||
dev.autorefresh()
|
||||
|
||||
def cleanup(self):
|
||||
def cleanup(self) -> None:
|
||||
"""Beendet autorefresh und alle Threads."""
|
||||
self.exit(full=True)
|
||||
self._myfh.close()
|
||||
@@ -580,7 +633,8 @@ class RevPiModIO(object):
|
||||
self.summary = None
|
||||
|
||||
def cycleloop(self, func, cycletime=50):
|
||||
"""Startet den Cycleloop.
|
||||
"""
|
||||
Startet den Cycleloop.
|
||||
|
||||
Der aktuelle Programmthread wird hier bis Aufruf von
|
||||
.exit() "gefangen". Er fuehrt nach jeder Aktualisierung
|
||||
@@ -603,10 +657,9 @@ class RevPiModIO(object):
|
||||
50 Millisekunden, in denen das Prozessabild eingelesen, die uebergebene
|
||||
Funktion ausgefuert und das Prozessabbild geschrieben wird.
|
||||
|
||||
@param func Funktion, die ausgefuehrt werden soll
|
||||
@param cycletime Zykluszeit in Millisekunden - Standardwert 50 ms
|
||||
@return None or the return value of the cycle function
|
||||
|
||||
:param func: Funktion, die ausgefuehrt werden soll
|
||||
:param cycletime: Zykluszeit in Millisekunden - Standardwert 50 ms
|
||||
:return: None or the return value of the cycle function
|
||||
"""
|
||||
# Prüfen ob ein Loop bereits läuft
|
||||
if self._looprunning:
|
||||
@@ -679,8 +732,9 @@ class RevPiModIO(object):
|
||||
|
||||
return ec
|
||||
|
||||
def exit(self, full=True):
|
||||
"""Beendet mainloop() und optional autorefresh.
|
||||
def exit(self, full=True) -> None:
|
||||
"""
|
||||
Beendet mainloop() und optional autorefresh.
|
||||
|
||||
Wenn sich das Programm im mainloop() befindet, wird durch Aufruf
|
||||
von exit() die Kontrolle wieder an das Hauptprogramm zurueckgegeben.
|
||||
@@ -689,8 +743,8 @@ class RevPiModIO(object):
|
||||
dem autorefresh. Der Thread fuer die Prozessabbildsynchronisierung
|
||||
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
|
||||
self.exitsignal.set()
|
||||
|
||||
@@ -714,15 +768,17 @@ class RevPiModIO(object):
|
||||
if not self._monitoring:
|
||||
self.writeprocimg(dev)
|
||||
|
||||
def export_replaced_ios(self, filename="replace_ios.conf"):
|
||||
"""Exportiert ersetzte IOs dieser Instanz.
|
||||
def export_replaced_ios(self, filename="replace_ios.conf") -> None:
|
||||
"""
|
||||
Exportiert ersetzte IOs dieser Instanz.
|
||||
|
||||
Exportiert alle ersetzten IOs, welche mit .replace_io(...) angelegt
|
||||
wurden. Die Datei kann z.B. fuer RevPiPyLoad verwndet werden um Daten
|
||||
in den neuen Formaten per MQTT zu uebertragen oder mit RevPiPyControl
|
||||
anzusehen.
|
||||
|
||||
@param filename Dateiname fuer Exportdatei"""
|
||||
@param filename Dateiname fuer Exportdatei
|
||||
"""
|
||||
acheck(str, filename=filename)
|
||||
|
||||
cp = ConfigParser()
|
||||
@@ -753,9 +809,12 @@ class RevPiModIO(object):
|
||||
"".format(filename, e)
|
||||
)
|
||||
|
||||
def get_jconfigrsc(self):
|
||||
"""Laedt die piCtory Konfiguration und erstellt ein <class 'dict'>.
|
||||
@return <class 'dict'> der piCtory Konfiguration"""
|
||||
def get_jconfigrsc(self) -> dict:
|
||||
"""
|
||||
Laedt die piCtory Konfiguration und erstellt ein <class 'dict'>.
|
||||
|
||||
:return: <class 'dict'> der piCtory Konfiguration
|
||||
"""
|
||||
# piCtory Konfiguration prüfen
|
||||
if self._configrsc is not None:
|
||||
if not access(self._configrsc, F_OK | R_OK):
|
||||
@@ -786,8 +845,9 @@ class RevPiModIO(object):
|
||||
)
|
||||
return jdata
|
||||
|
||||
def handlesignalend(self, cleanupfunc=None):
|
||||
"""Signalhandler fuer Programmende verwalten.
|
||||
def handlesignalend(self, cleanupfunc=None) -> None:
|
||||
"""
|
||||
Signalhandler fuer Programmende verwalten.
|
||||
|
||||
Wird diese Funktion aufgerufen, uebernimmt RevPiModIO die SignalHandler
|
||||
fuer SIGINT und SIGTERM. Diese werden Empfangen, wenn das
|
||||
@@ -804,9 +864,7 @@ class RevPiModIO(object):
|
||||
RevPiModIO Thrads / Funktionen werden die SignalHandler wieder
|
||||
freigegeben.
|
||||
|
||||
@param cleanupfunc Funktion wird nach dem letzten Lesen der Inputs
|
||||
ausgefuehrt, gefolgt vom letzten Schreiben der Outputs
|
||||
|
||||
:param cleanupfunc: Funktion wird nach dem Beenden ausgefuehrt
|
||||
"""
|
||||
# Prüfen ob Funktion callable ist
|
||||
if not (cleanupfunc is None or callable(cleanupfunc)):
|
||||
@@ -818,8 +876,9 @@ class RevPiModIO(object):
|
||||
signal(SIGINT, self.__evt_exit)
|
||||
signal(SIGTERM, self.__evt_exit)
|
||||
|
||||
def mainloop(self, blocking=True):
|
||||
"""Startet den Mainloop mit Eventueberwachung.
|
||||
def mainloop(self, blocking=True) -> None:
|
||||
"""
|
||||
Startet den Mainloop mit Eventueberwachung.
|
||||
|
||||
Der aktuelle Programmthread wird hier bis Aufruf von
|
||||
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
|
||||
werden soll.
|
||||
|
||||
@param blocking Wenn False, blockiert das Programm hier NICHT
|
||||
@return None
|
||||
|
||||
:param blocking: Wenn False, blockiert das Programm hier NICHT
|
||||
"""
|
||||
# Prüfen ob ein Loop bereits läuft
|
||||
if self._looprunning:
|
||||
@@ -935,14 +992,14 @@ class RevPiModIO(object):
|
||||
if e is not None:
|
||||
raise e
|
||||
|
||||
def readprocimg(self, device=None):
|
||||
"""Einlesen aller Inputs aller/eines Devices vom Prozessabbild.
|
||||
def readprocimg(self, device=None) -> bool:
|
||||
"""
|
||||
Einlesen aller Inputs aller/eines Devices vom Prozessabbild.
|
||||
|
||||
Devices mit aktiverem autorefresh werden ausgenommen!
|
||||
|
||||
@param device nur auf einzelnes Device anwenden
|
||||
@return True, wenn Arbeiten an allen Devices erfolgreich waren
|
||||
|
||||
:param device: nur auf einzelnes Device anwenden
|
||||
:return: True, wenn Arbeiten an allen Devices erfolgreich waren
|
||||
"""
|
||||
if device is None:
|
||||
mylist = self.device
|
||||
@@ -988,13 +1045,16 @@ class RevPiModIO(object):
|
||||
|
||||
return True
|
||||
|
||||
def resetioerrors(self):
|
||||
def resetioerrors(self) -> None:
|
||||
"""Setzt aktuellen IOError-Zaehler auf 0 zurueck."""
|
||||
self._ioerror = 0
|
||||
|
||||
def setdefaultvalues(self, device=None):
|
||||
"""Alle Outputbuffer werden auf die piCtory default Werte gesetzt.
|
||||
@param device nur auf einzelnes Device anwenden"""
|
||||
def setdefaultvalues(self, device=None) -> None:
|
||||
"""
|
||||
Alle Outputbuffer werden auf die piCtory default Werte gesetzt.
|
||||
|
||||
:param device: nur auf einzelnes Device anwenden
|
||||
"""
|
||||
if self._monitoring:
|
||||
raise RuntimeError(
|
||||
"can not set default values, while system is in monitoring "
|
||||
@@ -1012,14 +1072,14 @@ class RevPiModIO(object):
|
||||
for io in dev.get_outputs():
|
||||
io.set_value(io._defaultvalue)
|
||||
|
||||
def syncoutputs(self, device=None):
|
||||
"""Lesen aller aktuell gesetzten Outputs im Prozessabbild.
|
||||
def syncoutputs(self, device=None) -> bool:
|
||||
"""
|
||||
Lesen aller aktuell gesetzten Outputs im Prozessabbild.
|
||||
|
||||
Devices mit aktiverem autorefresh werden ausgenommen!
|
||||
|
||||
@param device nur auf einzelnes Device anwenden
|
||||
@return True, wenn Arbeiten an allen Devices erfolgreich waren
|
||||
|
||||
:param device: nur auf einzelnes Device anwenden
|
||||
:return: True, wenn Arbeiten an allen Devices erfolgreich waren
|
||||
"""
|
||||
if device is None:
|
||||
mylist = self.device
|
||||
@@ -1052,14 +1112,14 @@ class RevPiModIO(object):
|
||||
|
||||
return True
|
||||
|
||||
def writeprocimg(self, device=None):
|
||||
"""Schreiben aller Outputs aller Devices ins Prozessabbild.
|
||||
def writeprocimg(self, device=None) -> bool:
|
||||
"""
|
||||
Schreiben aller Outputs aller Devices ins Prozessabbild.
|
||||
|
||||
Devices mit aktiverem autorefresh werden ausgenommen!
|
||||
|
||||
@param device nur auf einzelnes Device anwenden
|
||||
@return True, wenn Arbeiten an allen Devices erfolgreich waren
|
||||
|
||||
:param device: nur auf einzelnes Device anwenden
|
||||
:return: True, wenn Arbeiten an allen Devices erfolgreich waren
|
||||
"""
|
||||
if self._direct_output:
|
||||
return True
|
||||
@@ -1127,14 +1187,13 @@ class RevPiModIO(object):
|
||||
|
||||
|
||||
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
|
||||
und bilded 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.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
@@ -1144,15 +1203,15 @@ class RevPiModIOSelected(RevPiModIO):
|
||||
syncoutputs=True, procimg=None, configrsc=None,
|
||||
simulator=False, debug=True, 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
|
||||
Device Position / einzelner Device Name sein oder eine Liste mit
|
||||
mehreren Positionen / Namen
|
||||
|
||||
@param deviceselection Positionsnummer oder Devicename
|
||||
@see #RevPiModIO.__init__ RevPiModIO.__init__(...)
|
||||
|
||||
:param deviceselection: Positionsnummer oder Devicename
|
||||
:ref: :func:`RevPiModIO.__init__(...)`
|
||||
"""
|
||||
super().__init__(
|
||||
autorefresh, monitoring, syncoutputs, procimg, configrsc,
|
||||
@@ -1197,29 +1256,29 @@ class RevPiModIOSelected(RevPiModIO):
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(
|
||||
self, virtdev, autorefresh=False, monitoring=False,
|
||||
self, virtdev, autorefresh=False,
|
||||
syncoutputs=True, procimg=None, configrsc=None, debug=True,
|
||||
replace_io_file=None, direct_output=False):
|
||||
"""Instantiiert die Grundfunktionen.
|
||||
"""
|
||||
Instantiiert die Grundfunktionen.
|
||||
|
||||
Parameter 'monitoring' und 'simulator' stehen hier nicht zur
|
||||
Verfuegung, da diese automatisch gesetzt werden.
|
||||
|
||||
@param virtdev Virtuelles Device oder mehrere als <class 'list'>
|
||||
@see #RevPiModIO.__init__ RevPiModIO.__init__(...)
|
||||
:param virtdev: Virtuelles Device oder mehrere als <class 'list'>
|
||||
:ref: :func:`RevPiModIO.__init__()`
|
||||
|
||||
"""
|
||||
# Parent mit monitoring=False und simulator=True laden
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""RevPiModIO Hauptklasse fuer Netzwerkzugriff."""
|
||||
__author__ = "Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||
__license__ = "LGPLv3"
|
||||
import socket
|
||||
import warnings
|
||||
from configparser import ConfigParser
|
||||
from json import loads as jloads
|
||||
from re import compile
|
||||
from revpimodio2 import DeviceNotFoundError
|
||||
from threading import Thread, Event, Lock
|
||||
from threading import Event, Lock, Thread
|
||||
|
||||
from revpimodio2 import DeviceNotFoundError
|
||||
from .device import Device
|
||||
from .modio import RevPiModIO as _RevPiModIO
|
||||
|
||||
__author__ = "Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||
__license__ = "LGPLv3"
|
||||
|
||||
# Synchronisierungsbefehl
|
||||
_syssync = b'\x01\x06\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17'
|
||||
# Disconnectbefehl
|
||||
@@ -33,42 +34,39 @@ HASH_FAIL = b'\xff' * 16
|
||||
|
||||
|
||||
class AclException(Exception):
|
||||
|
||||
"""Probleme mit Berechtigungen."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ConfigChanged(Exception):
|
||||
|
||||
"""Aenderung der piCtory oder replace_ios Datei."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
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
|
||||
Prozessabbilds ueber das Netzwerk. Ein entfernter Revolution Pi kann
|
||||
so gesteuert werden.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "__by_buff", "__check_replace_ios", "__config_changed", \
|
||||
"__int_buff", "__dictdirty", "__flusherr", "__replace_ios_h", \
|
||||
"__pictory_h", "__position", "__sockact", "__sockerr", "__sockend", \
|
||||
"__socklock", "__timeout", "__trigger", "__waitsync", "_address", \
|
||||
"_slavesock", "daemon"
|
||||
"__int_buff", "__dictdirty", "__flusherr", "__replace_ios_h", \
|
||||
"__pictory_h", "__position", "__sockact", "__sockerr", "__sockend", \
|
||||
"__socklock", "__timeout", "__trigger", "__waitsync", "_address", \
|
||||
"_slavesock", "daemon"
|
||||
|
||||
def __init__(self, address, check_replace_ios, timeout=500):
|
||||
"""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
|
||||
def __init__(self, address: tuple, check_replace_ios: bool, timeout=500):
|
||||
"""
|
||||
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
|
||||
"""
|
||||
super().__init__()
|
||||
self.daemon = True
|
||||
@@ -114,10 +112,13 @@ class NetFH(Thread):
|
||||
"""NetworkFileHandler beenden."""
|
||||
self.close()
|
||||
|
||||
def __check_acl(self, bytecode):
|
||||
"""Pueft ob ACL auf RevPi den Vorgang erlaubt oder wirft exception."""
|
||||
if bytecode == b'\x18':
|
||||
def __check_acl(self, bytecode: bytes) -> None:
|
||||
"""
|
||||
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
|
||||
self.__sockend.set()
|
||||
self.__sockerr.set()
|
||||
@@ -128,10 +129,12 @@ class NetFH(Thread):
|
||||
"reload revpipyload!"
|
||||
)
|
||||
|
||||
def __set_systimeout(self, value):
|
||||
"""Systemfunktion fuer Timeoutberechnung.
|
||||
@param value Timeout in Millisekunden 100 - 60000"""
|
||||
def __set_systimeout(self, value: int) -> None:
|
||||
"""
|
||||
Systemfunktion fuer Timeoutberechnung.
|
||||
|
||||
:param value: Timeout in Millisekunden 100 - 60000
|
||||
"""
|
||||
if isinstance(value, int) and (100 <= value <= 60000):
|
||||
self.__timeout = value / 1000
|
||||
|
||||
@@ -147,7 +150,7 @@ class NetFH(Thread):
|
||||
else:
|
||||
raise ValueError("value must between 10 and 60000 milliseconds")
|
||||
|
||||
def _connect(self):
|
||||
def _connect(self) -> None:
|
||||
"""Stellt die Verbindung zu einem RevPiSlave her."""
|
||||
# Neuen Socket aufbauen
|
||||
so = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
@@ -212,13 +215,13 @@ class NetFH(Thread):
|
||||
for pos in self.__dictdirty:
|
||||
self.set_dirtybytes(pos, self.__dictdirty[pos])
|
||||
|
||||
def _direct_send(self, send_bytes, recv_count):
|
||||
"""Fuer debugging direktes Senden von Daten.
|
||||
|
||||
@param send_bytes Bytes, die gesendet werden sollen
|
||||
@param recv_count Anzahl der Empfangsbytes
|
||||
@returns Empfangende Bytes
|
||||
def _direct_send(self, send_bytes: bytes, recv_count: int) -> bytes:
|
||||
"""
|
||||
Fuer debugging direktes Senden von Daten.
|
||||
|
||||
:param send_bytes: Bytes, die gesendet werden sollen
|
||||
:param recv_count: Anzahl der Empfangsbytes
|
||||
:return: Empfangende Bytes
|
||||
"""
|
||||
if self.__sockend.is_set():
|
||||
raise ValueError("I/O operation on closed file")
|
||||
@@ -230,9 +233,12 @@ class NetFH(Thread):
|
||||
self.__trigger = True
|
||||
return recv
|
||||
|
||||
def clear_dirtybytes(self, position=None):
|
||||
"""Entfernt die konfigurierten Dirtybytes vom RevPi Slave.
|
||||
@param position Startposition der Dirtybytes"""
|
||||
def clear_dirtybytes(self, position=None) -> None:
|
||||
"""
|
||||
Entfernt die konfigurierten Dirtybytes vom RevPi Slave.
|
||||
|
||||
:param position: Startposition der Dirtybytes
|
||||
"""
|
||||
if self.__config_changed:
|
||||
raise ConfigChanged("configuration on revolution pi was changed")
|
||||
if self.__sockend.is_set():
|
||||
@@ -255,7 +261,6 @@ class NetFH(Thread):
|
||||
|
||||
check = self._slavesock.recv(1)
|
||||
if check != b'\x1e':
|
||||
|
||||
# ACL prüfen und ggf Fehler werfen
|
||||
self.__check_acl(check)
|
||||
|
||||
@@ -279,7 +284,7 @@ class NetFH(Thread):
|
||||
|
||||
self.__trigger = True
|
||||
|
||||
def close(self):
|
||||
def close(self) -> None:
|
||||
"""Verbindung trennen."""
|
||||
if self.__sockend.is_set():
|
||||
return
|
||||
@@ -302,7 +307,7 @@ class NetFH(Thread):
|
||||
|
||||
self._slavesock.close()
|
||||
|
||||
def flush(self):
|
||||
def flush(self) -> None:
|
||||
"""Schreibpuffer senden."""
|
||||
if self.__config_changed:
|
||||
raise ConfigChanged("configuration on revolution pi was changed")
|
||||
@@ -334,35 +339,53 @@ class NetFH(Thread):
|
||||
|
||||
self.__trigger = True
|
||||
|
||||
def get_closed(self):
|
||||
"""Pruefen ob Verbindung geschlossen ist.
|
||||
@return True, wenn Verbindung geschlossen ist"""
|
||||
def get_closed(self) -> bool:
|
||||
"""
|
||||
Pruefen ob Verbindung geschlossen ist.
|
||||
|
||||
:return: True, wenn Verbindung geschlossen ist
|
||||
"""
|
||||
return self.__sockend.is_set()
|
||||
|
||||
def get_config_changed(self):
|
||||
"""Pruefen ob RevPi Konfiguration geaendert wurde.
|
||||
@return True, wenn RevPi Konfiguration geaendert ist"""
|
||||
def get_config_changed(self) -> bool:
|
||||
"""
|
||||
Pruefen ob RevPi Konfiguration geaendert wurde.
|
||||
|
||||
:return: True, wenn RevPi Konfiguration geaendert ist
|
||||
"""
|
||||
return self.__config_changed
|
||||
|
||||
def get_name(self):
|
||||
"""Verbindugnsnamen zurueckgeben.
|
||||
@return <class 'str'> IP:PORT"""
|
||||
def get_name(self) -> str:
|
||||
"""
|
||||
Verbindugnsnamen zurueckgeben.
|
||||
|
||||
:return: <class 'str'> IP:PORT
|
||||
"""
|
||||
return "{0}:{1}".format(*self._address)
|
||||
|
||||
def get_reconnecting(self):
|
||||
"""Interner reconnect aktiv wegen Netzwerkfehlern.
|
||||
@return True, wenn reconnect aktiv"""
|
||||
def get_reconnecting(self) -> bool:
|
||||
"""
|
||||
Interner reconnect aktiv wegen Netzwerkfehlern.
|
||||
|
||||
:return: True, wenn reconnect aktiv
|
||||
"""
|
||||
return self.__sockerr.is_set()
|
||||
|
||||
def get_timeout(self):
|
||||
"""Gibt aktuellen Timeout zurueck.
|
||||
@return <class 'int'> in Millisekunden"""
|
||||
def get_timeout(self) -> int:
|
||||
"""
|
||||
Gibt aktuellen Timeout zurueck.
|
||||
|
||||
:return: <class 'int'> in Millisekunden
|
||||
"""
|
||||
return int(self.__timeout * 1000)
|
||||
|
||||
def ioctl(self, request, arg=b''):
|
||||
"""IOCTL Befehle ueber das Netzwerk senden.
|
||||
@param request Request as <class 'int'>
|
||||
@param arg Argument as <class 'byte'>"""
|
||||
def ioctl(self, request: int, arg=b'') -> None:
|
||||
"""
|
||||
IOCTL Befehle ueber das Netzwerk senden.
|
||||
|
||||
:param request: Request as <class 'int'>
|
||||
:param arg: Argument as <class 'byte'>
|
||||
"""
|
||||
if self.__config_changed:
|
||||
raise ConfigChanged("configuration on revolution pi was changed")
|
||||
if self.__sockend.is_set():
|
||||
@@ -383,7 +406,6 @@ class NetFH(Thread):
|
||||
# Rückmeldebyte auswerten
|
||||
check = self._slavesock.recv(1)
|
||||
if check != b'\x1e':
|
||||
|
||||
# ACL prüfen und ggf Fehler werfen
|
||||
self.__check_acl(check)
|
||||
|
||||
@@ -392,10 +414,13 @@ class NetFH(Thread):
|
||||
|
||||
self.__trigger = True
|
||||
|
||||
def read(self, length):
|
||||
"""Daten ueber das Netzwerk lesen.
|
||||
@param length Anzahl der Bytes
|
||||
@return Gelesene <class 'bytes'>"""
|
||||
def read(self, length: int) -> bytes:
|
||||
"""
|
||||
Daten ueber das Netzwerk lesen.
|
||||
|
||||
:param length: Anzahl der Bytes
|
||||
:return: Gelesene <class 'bytes'>
|
||||
"""
|
||||
if self.__config_changed:
|
||||
raise ConfigChanged("configuration on revolution pi was changed")
|
||||
if self.__sockend.is_set():
|
||||
@@ -423,9 +448,12 @@ class NetFH(Thread):
|
||||
|
||||
return bytes(bytesbuff)
|
||||
|
||||
def readpictory(self):
|
||||
"""Ruft die piCtory Konfiguration ab.
|
||||
@return <class 'bytes'> piCtory Datei"""
|
||||
def readpictory(self) -> bytes:
|
||||
"""
|
||||
Ruft die piCtory Konfiguration ab.
|
||||
|
||||
:return: <class 'bytes'> piCtory Datei
|
||||
"""
|
||||
if self.__sockend.is_set():
|
||||
raise ValueError("read of closed file")
|
||||
|
||||
@@ -454,9 +482,12 @@ class NetFH(Thread):
|
||||
self.__sockerr.set()
|
||||
raise IOError("readpictory error on network")
|
||||
|
||||
def readreplaceio(self):
|
||||
"""Ruft die replace_io Konfiguration ab.
|
||||
@return <class 'bytes'> replace_io_file"""
|
||||
def readreplaceio(self) -> bytes:
|
||||
"""
|
||||
Ruft die replace_io Konfiguration ab.
|
||||
|
||||
:return: <class 'bytes'> replace_io_file
|
||||
"""
|
||||
if self.__sockend.is_set():
|
||||
raise ValueError("read of closed file")
|
||||
|
||||
@@ -485,7 +516,7 @@ class NetFH(Thread):
|
||||
self.__sockerr.set()
|
||||
raise IOError("readreplaceio error on network")
|
||||
|
||||
def run(self):
|
||||
def run(self) -> None:
|
||||
"""Handler fuer Synchronisierung."""
|
||||
state_reconnect = False
|
||||
while not self.__sockend.is_set():
|
||||
@@ -532,7 +563,7 @@ class NetFH(Thread):
|
||||
# Warten nach Sync damit Instantiierung funktioniert
|
||||
self.__sockerr.wait(self.__waitsync)
|
||||
|
||||
def seek(self, position):
|
||||
def seek(self, position: int) -> None:
|
||||
"""Springt an angegebene Position.
|
||||
@param position An diese Position springen"""
|
||||
if self.__config_changed:
|
||||
@@ -541,10 +572,13 @@ class NetFH(Thread):
|
||||
raise ValueError("seek of closed file")
|
||||
self.__position = int(position)
|
||||
|
||||
def set_dirtybytes(self, position, dirtybytes):
|
||||
"""Konfiguriert Dirtybytes fuer Prozessabbild bei Verbindungsfehler.
|
||||
@param positon Startposition zum Schreiben
|
||||
@param dirtybytes <class 'bytes'> die geschrieben werden sollen"""
|
||||
def set_dirtybytes(self, position: int, dirtybytes: bytes) -> None:
|
||||
"""
|
||||
Konfiguriert Dirtybytes fuer Prozessabbild bei Verbindungsfehler.
|
||||
|
||||
:param position: Startposition zum Schreiben
|
||||
:param dirtybytes: <class 'bytes'> die geschrieben werden sollen
|
||||
"""
|
||||
if self.__config_changed:
|
||||
raise ConfigChanged("configuration on revolution pi was changed")
|
||||
if self.__sockend.is_set():
|
||||
@@ -564,7 +598,6 @@ class NetFH(Thread):
|
||||
|
||||
check = self._slavesock.recv(1)
|
||||
if check != b'\x1e':
|
||||
|
||||
# ACL prüfen und ggf Fehler werfen
|
||||
self.__check_acl(check)
|
||||
|
||||
@@ -585,9 +618,12 @@ class NetFH(Thread):
|
||||
|
||||
self.__trigger = True
|
||||
|
||||
def set_timeout(self, value):
|
||||
"""Setzt Timeoutwert fuer Verbindung.
|
||||
@param value Timeout in Millisekunden"""
|
||||
def set_timeout(self, value: int) -> None:
|
||||
"""
|
||||
Setzt Timeoutwert fuer Verbindung.
|
||||
|
||||
:param value: Timeout in Millisekunden
|
||||
"""
|
||||
if self.__sockend.is_set():
|
||||
raise ValueError("I/O operation on closed file")
|
||||
|
||||
@@ -612,19 +648,25 @@ class NetFH(Thread):
|
||||
|
||||
self.__trigger = True
|
||||
|
||||
def tell(self):
|
||||
"""Gibt aktuelle Position zurueck.
|
||||
@return int aktuelle Position"""
|
||||
def tell(self) -> int:
|
||||
"""
|
||||
Gibt aktuelle Position zurueck.
|
||||
|
||||
:return: Aktuelle Position
|
||||
"""
|
||||
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")
|
||||
return self.__position
|
||||
|
||||
def write(self, bytebuff):
|
||||
"""Daten ueber das Netzwerk schreiben.
|
||||
@param bytebuff Bytes zum schreiben
|
||||
@return <class 'int'> Anzahl geschriebener bytes"""
|
||||
def write(self, bytebuff: bytes) -> int:
|
||||
"""
|
||||
Daten ueber das Netzwerk schreiben.
|
||||
|
||||
:param bytebuff: Bytes zum schreiben
|
||||
:return: <class 'int'> Anzahl geschriebener bytes
|
||||
"""
|
||||
if self.__config_changed:
|
||||
raise ConfigChanged("configuration on revolution pi was changed")
|
||||
if self.__sockend.is_set():
|
||||
@@ -638,10 +680,10 @@ class NetFH(Thread):
|
||||
|
||||
# Datenblöcke mit Group Seperator in Puffer ablegen
|
||||
self.__by_buff += b'\x01SD' + \
|
||||
self.__position.to_bytes(length=2, byteorder="little") + \
|
||||
len(bytebuff).to_bytes(length=2, byteorder="little") + \
|
||||
b'\x1d\x00\x00\x00\x00\x00\x00\x00\x17' + \
|
||||
bytebuff
|
||||
self.__position.to_bytes(length=2, byteorder="little") + \
|
||||
len(bytebuff).to_bytes(length=2, byteorder="little") + \
|
||||
b'\x1d\x00\x00\x00\x00\x00\x00\x00\x17' + \
|
||||
bytebuff
|
||||
|
||||
# TODO: Bufferlänge und dann flushen?
|
||||
|
||||
@@ -655,8 +697,8 @@ class NetFH(Thread):
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
RevPiModIOSelected() und uebergibt bei Instantiierung eine Liste mit
|
||||
Device Positionen oder Device Namen.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "_address"
|
||||
@@ -673,17 +714,17 @@ class RevPiNetIO(_RevPiModIO):
|
||||
self, address, autorefresh=False, monitoring=False,
|
||||
syncoutputs=True, simulator=False, debug=True,
|
||||
replace_io_file=None, direct_output=False):
|
||||
"""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)
|
||||
"""
|
||||
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)
|
||||
"""
|
||||
check_ip = compile(
|
||||
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())
|
||||
|
||||
def _create_myfh(self):
|
||||
"""Erstellt NetworkFileObject.
|
||||
return FileObject"""
|
||||
"""
|
||||
Erstellt NetworkFileObject.
|
||||
|
||||
:return: FileObject
|
||||
"""
|
||||
self._buffedwrite = True
|
||||
return NetFH(self._address, self._replace_io_file == ":network:")
|
||||
|
||||
def _get_cpreplaceio(self):
|
||||
"""Laed die replace_io Konfiguration ueber das Netzwerk.
|
||||
@return <class 'ConfigParser'> der replace io daten"""
|
||||
def _get_cpreplaceio(self) -> ConfigParser:
|
||||
"""
|
||||
Laed die replace_io Konfiguration ueber das Netzwerk.
|
||||
|
||||
:return: <class 'ConfigParser'> der replace io daten
|
||||
"""
|
||||
# Normale Verwendung über Elternklasse erledigen
|
||||
if self._replace_io_file != ":network:":
|
||||
return super()._get_cpreplaceio()
|
||||
@@ -771,48 +817,60 @@ class RevPiNetIO(_RevPiModIO):
|
||||
)
|
||||
return cp
|
||||
|
||||
def disconnect(self):
|
||||
def disconnect(self) -> None:
|
||||
"""Trennt Verbindungen und beendet autorefresh inkl. alle Threads."""
|
||||
self.cleanup()
|
||||
|
||||
def exit(self, full=True):
|
||||
"""Beendet mainloop() und optional autorefresh.
|
||||
@see #RevPiModIO.exit(...)"""
|
||||
def exit(self, full=True) -> None:
|
||||
"""
|
||||
Beendet mainloop() und optional autorefresh.
|
||||
|
||||
:ref: :func:`RevPiModIO.exit()`
|
||||
"""
|
||||
try:
|
||||
super().exit(full)
|
||||
except ConfigChanged:
|
||||
pass
|
||||
|
||||
def get_config_changed(self):
|
||||
"""Pruefen ob RevPi Konfiguration geaendert wurde.
|
||||
def get_config_changed(self) -> bool:
|
||||
"""
|
||||
Pruefen ob RevPi Konfiguration geaendert wurde.
|
||||
|
||||
In diesem Fall ist die Verbindung geschlossen und RevPiNetIO muss
|
||||
neu instanziert werden.
|
||||
|
||||
@return True, wenn RevPi Konfiguration geaendert ist"""
|
||||
:return: True, wenn RevPi Konfiguration geaendert ist
|
||||
"""
|
||||
return self._myfh.config_changed
|
||||
|
||||
def get_jconfigrsc(self):
|
||||
"""Laedt die piCotry Konfiguration und erstellt ein <class 'dict'>.
|
||||
@return <class 'dict'> der piCtory Konfiguration"""
|
||||
def get_jconfigrsc(self) -> dict:
|
||||
"""
|
||||
Laedt die piCotry Konfiguration und erstellt ein <class 'dict'>.
|
||||
|
||||
:return: <class 'dict'> der piCtory Konfiguration
|
||||
"""
|
||||
mynh = NetFH(self._address, False)
|
||||
byte_buff = mynh.readpictory()
|
||||
mynh.close()
|
||||
return jloads(byte_buff.decode("utf-8"))
|
||||
|
||||
def get_reconnecting(self):
|
||||
"""Interner reconnect aktiv wegen Netzwerkfehlern.
|
||||
def get_reconnecting(self) -> bool:
|
||||
"""
|
||||
Interner reconnect aktiv wegen Netzwerkfehlern.
|
||||
|
||||
Das Modul versucht intern die Verbindung neu herzustellen. Es ist
|
||||
kein weiteres Zutun noetig.
|
||||
|
||||
@return True, wenn reconnect aktiv"""
|
||||
:return: True, wenn reconnect aktiv
|
||||
"""
|
||||
return self._myfh.reconnecting
|
||||
|
||||
def net_cleardefaultvalues(self, device=None):
|
||||
"""Loescht Defaultwerte vom PLC Slave.
|
||||
@param device nur auf einzelnes Device anwenden, sonst auf Alle"""
|
||||
def net_cleardefaultvalues(self, device=None) -> None:
|
||||
"""
|
||||
Loescht Defaultwerte vom PLC Slave.
|
||||
|
||||
:param device: nur auf einzelnes Device anwenden, sonst auf Alle
|
||||
"""
|
||||
if self.monitoring:
|
||||
raise RuntimeError(
|
||||
"can not send default values, while system is in "
|
||||
@@ -829,10 +887,12 @@ class RevPiNetIO(_RevPiModIO):
|
||||
for dev in mylist:
|
||||
self._myfh.clear_dirtybytes(dev._offset + dev._slc_out.start)
|
||||
|
||||
def net_setdefaultvalues(self, device=None):
|
||||
"""Konfiguriert den PLC Slave mit den piCtory Defaultwerten.
|
||||
@param device nur auf einzelnes Device anwenden, sonst auf Alle"""
|
||||
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
|
||||
"""
|
||||
if self.monitoring:
|
||||
raise RuntimeError(
|
||||
"can not send default values, while system is in "
|
||||
@@ -881,14 +941,13 @@ class RevPiNetIO(_RevPiModIO):
|
||||
|
||||
|
||||
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
|
||||
und bilded 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.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
@@ -897,16 +956,16 @@ class RevPiNetIOSelected(RevPiNetIO):
|
||||
self, address, deviceselection, autorefresh=False,
|
||||
monitoring=False, syncoutputs=True, simulator=False, debug=True,
|
||||
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
|
||||
Device Position / einzelner Device Name sein oder eine Liste mit
|
||||
mehreren Positionen / Namen
|
||||
|
||||
@param address: IP-Adresse <class 'str'> / (IP, Port) <class 'tuple'>
|
||||
@param deviceselection Positionsnummer oder Devicename
|
||||
@see #RevPiNetIO.__init__ RevPiNetIO.__init__(...)
|
||||
|
||||
:param address: IP-Adresse <class 'str'> / (IP, Port) <class 'tuple'>
|
||||
:param deviceselection: Positionsnummer oder Devicename
|
||||
:ref: :func:`RevPiNetIO.__init__()`
|
||||
"""
|
||||
super().__init__(
|
||||
address, autorefresh, monitoring, syncoutputs, simulator, debug,
|
||||
@@ -951,31 +1010,30 @@ class RevPiNetIOSelected(RevPiNetIO):
|
||||
|
||||
|
||||
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
|
||||
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.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(
|
||||
self, address, virtdev, autorefresh=False, monitoring=False,
|
||||
self, address, virtdev, autorefresh=False,
|
||||
syncoutputs=True, debug=True, replace_io_file=None,
|
||||
direct_output=False):
|
||||
"""Instantiiert die Grundfunktionen.
|
||||
"""
|
||||
Instantiiert die Grundfunktionen.
|
||||
|
||||
Parameter 'monitoring' und 'simulator' stehen hier nicht zur
|
||||
Verfuegung, da diese automatisch gesetzt werden.
|
||||
|
||||
@param address: IP-Adresse <class 'str'> / (IP, Port) <class 'tuple'>
|
||||
@param virtdev Virtuelles Device oder mehrere als <class 'list'>
|
||||
@see #RevPiModIO.__init__ RevPiModIO.__init__(...)
|
||||
|
||||
:param address: IP-Adresse <class 'str'> / (IP, Port) <class 'tuple'>
|
||||
:param virtdev: Virtuelles Device oder mehrere als <class 'list'>
|
||||
:ref: :func:`RevPiModIO.__init__()`
|
||||
"""
|
||||
# Parent mit monitoring=False und simulator=True laden
|
||||
super().__init__(
|
||||
|
||||
@@ -6,13 +6,15 @@ __license__ = "LGPLv3"
|
||||
|
||||
|
||||
class Summary(object):
|
||||
|
||||
"""Bildet die Summary-Sektion der config.rsc ab."""
|
||||
|
||||
__slots__ = "inptotal", "outtotal"
|
||||
|
||||
def __init__(self, summary):
|
||||
"""Instantiiert die RevPiSummary-Klasse.
|
||||
@param summary piCtory Summaryinformationen"""
|
||||
def __init__(self, summary: dict):
|
||||
"""
|
||||
Instantiiert die RevPiSummary-Klasse.
|
||||
|
||||
:param summary: piCtory Summaryinformationen
|
||||
"""
|
||||
self.inptotal = summary["inpTotal"]
|
||||
self.outtotal = summary["outTotal"]
|
||||
|
||||
Reference in New Issue
Block a user