mirror of
https://github.com/naruxde/revpimodio2.git
synced 2025-11-08 22:03:53 +01:00
Mit slots zusammenführen
This commit is contained in:
@@ -1,12 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# python3-RevPiModIO
|
||||
#
|
||||
# Webpage: https://revpimodio.org/
|
||||
# (c) Sven Sager, License: LGPLv3
|
||||
#
|
||||
"""Stellt alle Klassen fuer den RevolutionPi zur Verfuegung.
|
||||
|
||||
Webpage: https://revpimodio.org/
|
||||
|
||||
Stellt Klassen fuer die einfache Verwendung des Revolution Pis der
|
||||
Kunbus GmbH (https://revolution.kunbus.de/) zur Verfuegung. Alle I/Os werden
|
||||
aus der piCtory Konfiguration eingelesen und mit deren Namen direkt zugreifbar
|
||||
@@ -23,9 +19,10 @@ __all__ = [
|
||||
"RevPiNetIO", "RevPiNetIOSelected", "RevPiNetIODriver"
|
||||
]
|
||||
__author__ = "Sven Sager <akira@revpimodio.org>"
|
||||
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||
__license__ = "LGPLv3"
|
||||
__name__ = "revpimodio2"
|
||||
__package__ = "revpimodio2"
|
||||
__version__ = "2.1.6"
|
||||
__version__ = "2.2.3"
|
||||
|
||||
# Global package values
|
||||
OFF = 0
|
||||
@@ -71,6 +68,7 @@ def consttostr(value):
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
# Benötigte Klassen importieren
|
||||
from .modio import RevPiModIO, RevPiModIOSelected, RevPiModIODriver
|
||||
from .netio import RevPiNetIO, RevPiNetIOSelected, RevPiNetIODriver
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# python3-RevPiModIO
|
||||
#
|
||||
# Webpage: https://revpimodio.org/
|
||||
# (c) Sven Sager, License: LGPLv3
|
||||
#
|
||||
"""Bildet die App Sektion von piCtory ab."""
|
||||
__author__ = "Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||
__license__ = "LGPLv3"
|
||||
|
||||
|
||||
class App(object):
|
||||
|
||||
"""Bildet die App Sektion der config.rsc ab."""
|
||||
|
||||
__slots__ = "name", "version", "language", "layout"
|
||||
|
||||
def __init__(self, app):
|
||||
"""Instantiiert die App-Klasse.
|
||||
@param app piCtory Appinformationen"""
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# python3-RevPiModIO
|
||||
#
|
||||
# Webpage: https://revpimodio.org/
|
||||
# (c) Sven Sager, License: LGPLv3
|
||||
#
|
||||
"""Modul fuer die Verwaltung der Devices."""
|
||||
from threading import Lock
|
||||
__author__ = "Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||
__license__ = "LGPLv3"
|
||||
|
||||
from threading import Thread, Event, Lock
|
||||
from .helper import ProcimgWriter
|
||||
|
||||
|
||||
@@ -55,7 +53,7 @@ class DeviceList(object):
|
||||
def __delitem__(self, key):
|
||||
"""Entfernt Device an angegebener Position.
|
||||
@param key Deviceposition zum entfernen"""
|
||||
if issubclass(type(key), Device):
|
||||
if isinstance(key, Device):
|
||||
key = key._position
|
||||
self.__delattr__(key)
|
||||
|
||||
@@ -65,7 +63,7 @@ class DeviceList(object):
|
||||
@return Gefundenes <class 'Device'>-Objekt"""
|
||||
if type(key) == int:
|
||||
if key not in self.__dict_position:
|
||||
raise KeyError("no device on position {}".format(key))
|
||||
raise KeyError("no device on position {0}".format(key))
|
||||
return self.__dict_position[key]
|
||||
else:
|
||||
return getattr(self, key)
|
||||
@@ -91,7 +89,7 @@ class DeviceList(object):
|
||||
"""Setzt Attribute nur wenn Device.
|
||||
@param key Attributname
|
||||
@param value Attributobjekt"""
|
||||
if issubclass(type(value), Device):
|
||||
if isinstance(value, Device):
|
||||
object.__setattr__(self, key, value)
|
||||
self.__dict_position[value._position] = value
|
||||
elif key == "_DeviceList__dict_position":
|
||||
@@ -108,6 +106,13 @@ class Device(object):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "_ba_devdata", "_ba_datacp", \
|
||||
"_dict_events", "_filelock", "_length", "_modio", "_name", "_offset", \
|
||||
"_position", "_producttype", "_selfupdate", "_slc_devoff", \
|
||||
"_slc_inp", "_slc_inpoff", "_slc_mem", "_slc_memoff", \
|
||||
"_slc_out", "_slc_outoff", "bmk", "catalognr", "comment", "extend", \
|
||||
"guid", "id", "inpvariant", "outvariant", "type"
|
||||
|
||||
def __init__(self, parentmodio, dict_device, simulator=False):
|
||||
"""Instantiierung der Device-Klasse.
|
||||
|
||||
@@ -124,24 +129,24 @@ class Device(object):
|
||||
self._selfupdate = False
|
||||
|
||||
# Wertzuweisung aus dict_device
|
||||
self._name = dict_device.pop("name")
|
||||
self._offset = int(dict_device.pop("offset"))
|
||||
self._position = int(dict_device.pop("position"))
|
||||
self._producttype = int(dict_device.pop("productType"))
|
||||
self._name = dict_device.get("name")
|
||||
self._offset = int(dict_device.get("offset"))
|
||||
self._position = int(dict_device.get("position"))
|
||||
self._producttype = int(dict_device.get("productType"))
|
||||
|
||||
# IOM-Objekte erstellen und Adressen in SLCs speichern
|
||||
if simulator:
|
||||
self._slc_inp = self._buildio(
|
||||
dict_device.pop("out"), INP)
|
||||
dict_device.get("out"), INP)
|
||||
self._slc_out = self._buildio(
|
||||
dict_device.pop("inp"), OUT)
|
||||
dict_device.get("inp"), OUT)
|
||||
else:
|
||||
self._slc_inp = self._buildio(
|
||||
dict_device.pop("inp"), INP)
|
||||
dict_device.get("inp"), INP)
|
||||
self._slc_out = self._buildio(
|
||||
dict_device.pop("out"), OUT)
|
||||
dict_device.get("out"), OUT)
|
||||
self._slc_mem = self._buildio(
|
||||
dict_device.pop("mem"), MEM
|
||||
dict_device.get("mem"), MEM
|
||||
)
|
||||
|
||||
# SLCs mit offset berechnen
|
||||
@@ -164,7 +169,15 @@ class Device(object):
|
||||
self._ba_datacp = bytearray()
|
||||
|
||||
# Alle restlichen attribute an Klasse anhängen
|
||||
self.__dict__.update(dict_device)
|
||||
self.bmk = dict_device.get("bmk", "")
|
||||
self.catalognr = dict_device.get("catalogNr", "")
|
||||
self.comment = dict_device.get("comment", "")
|
||||
self.extend = dict_device.get("extend", {})
|
||||
self.guid = dict_device.get("GUID", "")
|
||||
self.id = dict_device.get("id", "")
|
||||
self.inpvariant = dict_device.get("inpVariant", 0)
|
||||
self.outvariant = dict_device.get("outVariant", 0)
|
||||
self.type = dict_device.get("type", "")
|
||||
|
||||
# Spezielle Konfiguration von abgeleiteten Klassen durchführen
|
||||
self._devconfigure()
|
||||
@@ -178,7 +191,7 @@ class Device(object):
|
||||
"""Prueft ob IO auf diesem Device liegt.
|
||||
@param key IO-Name <class 'str'> / IO-Bytenummer <class 'int'>
|
||||
@return True, wenn IO auf Device vorhanden"""
|
||||
if issubclass(type(key), IOBase):
|
||||
if isinstance(key, IOBase):
|
||||
# Umwandlung für key
|
||||
key = key._name
|
||||
|
||||
@@ -200,7 +213,7 @@ class Device(object):
|
||||
def __iter__(self):
|
||||
"""Gibt Iterator aller IOs zurueck.
|
||||
@return <class 'iter'> aller IOs"""
|
||||
return self.__getioiter(self._slc_devoff)
|
||||
return self.__getioiter(self._slc_devoff, None)
|
||||
|
||||
def __len__(self):
|
||||
"""Gibt Anzahl der Bytes zurueck, die dieses Device belegt.
|
||||
@@ -212,13 +225,17 @@ class Device(object):
|
||||
@return Devicename"""
|
||||
return self._name
|
||||
|
||||
def __getioiter(self, ioslc):
|
||||
def __getioiter(self, ioslc, export):
|
||||
"""Gibt <class 'iter'> mit allen IOs zurueck.
|
||||
|
||||
@param ioslc IO Abschnitt <class 'slice'>
|
||||
@return IOs als Iterator"""
|
||||
@param export Filter fuer 'Export' Flag in piCtory
|
||||
@return IOs als Iterator
|
||||
|
||||
"""
|
||||
for lst_io in self._modio.io[ioslc]:
|
||||
for io in lst_io:
|
||||
if io is not None:
|
||||
if io is not None and (export is None or io.export == export):
|
||||
yield io
|
||||
|
||||
def _buildio(self, dict_io, iotype):
|
||||
@@ -236,7 +253,7 @@ class Device(object):
|
||||
for key in sorted(dict_io, key=lambda x: int(x)):
|
||||
|
||||
# Neuen IO anlegen
|
||||
if bool(dict_io[key][7]) or self._producttype == 95:
|
||||
if bool(dict_io[key][7]) or isinstance(self, Core):
|
||||
# Bei Bitwerten oder Core RevPiIOBase verwenden
|
||||
io_new = IOBase(
|
||||
self, dict_io[key], iotype, "little", False
|
||||
@@ -246,6 +263,7 @@ class Device(object):
|
||||
self, dict_io[key],
|
||||
iotype,
|
||||
"little",
|
||||
# Bei AIO (103) signed auf True setzen
|
||||
self._producttype == 103
|
||||
)
|
||||
|
||||
@@ -285,9 +303,8 @@ class Device(object):
|
||||
self._modio.readprocimg(self)
|
||||
|
||||
# Datenkopie anlegen
|
||||
self._filelock.acquire()
|
||||
self._ba_datacp = self._ba_devdata[:]
|
||||
self._filelock.release()
|
||||
with self._filelock:
|
||||
self._ba_datacp = self._ba_devdata[:]
|
||||
|
||||
self._selfupdate = True
|
||||
|
||||
@@ -322,27 +339,60 @@ class Device(object):
|
||||
if not self._modio._monitoring:
|
||||
self._modio.writeprocimg(self)
|
||||
|
||||
def get_allios(self):
|
||||
def get_allios(self, export=None):
|
||||
"""Gibt eine Liste aller Inputs und Outputs zurueck, keine MEMs.
|
||||
@return <class 'list'> Input und Output, keine MEMs"""
|
||||
|
||||
Bleibt Parameter 'export' auf None werden alle Inputs und Outputs
|
||||
zurueckgegeben. Wird 'export' auf True/False gesetzt, werden nur Inputs
|
||||
und Outputs zurueckgegeben, bei denen der Wert 'Export' in piCtory
|
||||
uebereinstimmt.
|
||||
|
||||
@param export Nur In-/Outputs mit angegebenen 'Export' Wert in piCtory
|
||||
@return <class 'list'> Input und Output, keine MEMs
|
||||
|
||||
"""
|
||||
return list(self.__getioiter(
|
||||
slice(self._slc_inpoff.start, self._slc_outoff.stop)
|
||||
slice(self._slc_inpoff.start, self._slc_outoff.stop), export
|
||||
))
|
||||
|
||||
def get_inputs(self):
|
||||
def get_inputs(self, export=None):
|
||||
"""Gibt eine Liste aller Inputs zurueck.
|
||||
@return <class 'list'> Inputs"""
|
||||
return list(self.__getioiter(self._slc_inpoff))
|
||||
|
||||
def get_outputs(self):
|
||||
Bleibt Parameter 'export' auf None werden alle Inputs zurueckgegeben.
|
||||
Wird 'export' auf True/False gesetzt, werden nur Inputs zurueckgegeben,
|
||||
bei denen der Wert 'Export' in piCtory uebereinstimmt.
|
||||
|
||||
@param export Nur Inputs mit angegebenen 'Export' Wert in piCtory
|
||||
@return <class 'list'> Inputs
|
||||
|
||||
"""
|
||||
return list(self.__getioiter(self._slc_inpoff, export))
|
||||
|
||||
def get_outputs(self, export=None):
|
||||
"""Gibt eine Liste aller Outputs zurueck.
|
||||
@return <class 'list'> Outputs"""
|
||||
return list(self.__getioiter(self._slc_outoff))
|
||||
|
||||
def get_memories(self):
|
||||
"""Gibt eine Liste aller mems zurueck.
|
||||
@return <class 'list'> Mems"""
|
||||
return list(self.__getioiter(self._slc_memoff))
|
||||
Bleibt Parameter 'export' auf None werden alle Outputs zurueckgegeben.
|
||||
Wird 'export' auf True/False gesetzt, werden nur Outputs
|
||||
zurueckgegeben, bei denen der Wert 'Export' in piCtory uebereinstimmt.
|
||||
|
||||
@param export Nur Outputs mit angegebenen 'Export' Wert in piCtory
|
||||
@return <class 'list'> Outputs
|
||||
|
||||
"""
|
||||
return list(self.__getioiter(self._slc_outoff, export))
|
||||
|
||||
def get_memories(self, export=None):
|
||||
"""Gibt eine Liste aller Memoryobjekte zurueck.
|
||||
|
||||
Bleibt Parameter 'export' auf None werden alle Mems zurueckgegeben.
|
||||
Wird 'export' auf True/False gesetzt, werden nur Mems zurueckgegeben,
|
||||
bei denen der Wert 'Export' in piCtory uebereinstimmt.
|
||||
|
||||
@param export Nur Mems mit angegebenen 'Export' Wert in piCtory
|
||||
@return <class 'list'> Mems
|
||||
|
||||
"""
|
||||
return list(self.__getioiter(self._slc_memoff, export))
|
||||
|
||||
def readprocimg(self):
|
||||
"""Alle Inputs fuer dieses Device vom Prozessabbild einlesen.
|
||||
@@ -399,64 +449,73 @@ class Core(Device):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "_iocycle", "_ioerrorcnt", "_iostatusbyte", "_iotemperature", \
|
||||
"_ioerrorlimit1", "_ioerrorlimit2", "_iofrequency", "_ioled", \
|
||||
"a1green", "a1red", "a2green", "a2red"
|
||||
|
||||
def _devconfigure(self):
|
||||
"""Core-Klasse vorbereiten."""
|
||||
# Eigene IO-Liste aufbauen
|
||||
lst_io = [x for x in self.__iter__()]
|
||||
|
||||
self._iostatusbyte = lst_io[0]
|
||||
self._iocycle = None
|
||||
self._iotemperature = None
|
||||
self._iofrequency = None
|
||||
self._ioerrorcnt = None
|
||||
self._ioled = 1
|
||||
self._ioled = lst_io[1]
|
||||
self._ioerrorlimit1 = None
|
||||
self._ioerrorlimit2 = None
|
||||
|
||||
# Eigene IO-Liste aufbauen
|
||||
self.__lst_io = [x for x in self.__iter__()]
|
||||
|
||||
int_lenio = len(self.__lst_io)
|
||||
int_lenio = len(lst_io)
|
||||
if int_lenio == 6:
|
||||
# Core 1.1
|
||||
self._iocycle = 1
|
||||
self._ioerrorcnt = 2
|
||||
self._ioled = 3
|
||||
self._ioerrorlimit1 = 4
|
||||
self._ioerrorlimit2 = 5
|
||||
self._iocycle = lst_io[1]
|
||||
self._ioerrorcnt = lst_io[2]
|
||||
self._ioled = lst_io[3]
|
||||
self._ioerrorlimit1 = lst_io[4]
|
||||
self._ioerrorlimit2 = lst_io[5]
|
||||
elif int_lenio == 8:
|
||||
# core 1.2
|
||||
self._iocycle = 1
|
||||
self._ioerrorcnt = 2
|
||||
self._iotemperature = 3
|
||||
self._iofrequency = 4
|
||||
self._ioled = 5
|
||||
self._ioerrorlimit1 = 6
|
||||
self._ioerrorlimit2 = 7
|
||||
# Core 1.2
|
||||
self._iocycle = lst_io[1]
|
||||
self._ioerrorcnt = lst_io[2]
|
||||
self._iotemperature = lst_io[3]
|
||||
self._iofrequency = lst_io[4]
|
||||
self._ioled = lst_io[5]
|
||||
self._ioerrorlimit1 = lst_io[6]
|
||||
self._ioerrorlimit2 = lst_io[7]
|
||||
|
||||
if not (self._modio._monitoring or self._modio._simulator):
|
||||
# Für RS485 errors defaults laden sollte procimg NULL sein
|
||||
if self._ioerrorlimit1 is not None:
|
||||
self.__lst_io[self._ioerrorlimit1].set_value(
|
||||
self.__lst_io[self._ioerrorlimit1]._defaultvalue
|
||||
)
|
||||
if self._ioerrorlimit2 is not None:
|
||||
self.__lst_io[self._ioerrorlimit2].set_value(
|
||||
self.__lst_io[self._ioerrorlimit2]._defaultvalue
|
||||
)
|
||||
# RS485 errors schreiben
|
||||
self._modio.writeprocimg(self)
|
||||
# Echte IOs erzeugen
|
||||
self.a1green = IOBase(self, [
|
||||
"a1green", 0, 1, self._ioled._slc_address.start,
|
||||
False, None, "LED_A1_GREEN", "0"
|
||||
], OUT, "little", False)
|
||||
self.a1red = IOBase(self, [
|
||||
"a1red", 0, 1, self._ioled._slc_address.start,
|
||||
False, None, "LED_A1_RED", "1"
|
||||
], OUT, "little", False)
|
||||
self.a2green = IOBase(self, [
|
||||
"a2green", 0, 1, self._ioled._slc_address.start,
|
||||
False, None, "LED_A2_GREEN", "2"
|
||||
], OUT, "little", False)
|
||||
self.a2red = IOBase(self, [
|
||||
"a2red", 0, 1, self._ioled._slc_address.start,
|
||||
False, None, "LED_A2_RED", "3"
|
||||
], OUT, "little", False)
|
||||
|
||||
def __errorlimit(self, io_id, errorlimit):
|
||||
def __errorlimit(self, io, errorlimit):
|
||||
"""Verwaltet das Lesen und Schreiben der ErrorLimits.
|
||||
@param io_id Index des IOs fuer ErrorLimit
|
||||
@param io IOs Objekt fuer ErrorLimit
|
||||
@return Aktuellen ErrorLimit oder None wenn nicht verfuegbar"""
|
||||
if errorlimit is None:
|
||||
return None if io_id is None else int.from_bytes(
|
||||
self.__lst_io[io_id].get_value(),
|
||||
byteorder=self.__lst_io[io_id]._byteorder
|
||||
return None if io is None else int.from_bytes(
|
||||
io.get_value(), byteorder="little"
|
||||
)
|
||||
else:
|
||||
if 0 <= errorlimit <= 65535:
|
||||
self.__lst_io[io_id].set_value(errorlimit.to_bytes(
|
||||
2, byteorder=self.__lst_io[io_id]._byteorder
|
||||
))
|
||||
io.set_value(
|
||||
errorlimit.to_bytes(2, byteorder="little")
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
"errorlimit value must be between 0 and 65535"
|
||||
@@ -466,50 +525,63 @@ class Core(Device):
|
||||
"""Gibt den RevPi Core Status zurueck.
|
||||
@return Status als <class 'int'>"""
|
||||
return int.from_bytes(
|
||||
self.__lst_io[0].get_value(), byteorder=self.__lst_io[0]._byteorder
|
||||
self._iostatusbyte.get_value(), byteorder="little"
|
||||
)
|
||||
|
||||
def _get_leda1(self):
|
||||
"""Gibt den Zustand der LED A1 vom core zurueck.
|
||||
"""Gibt den Zustand der LED A1 vom Core zurueck.
|
||||
@return 0=aus, 1=gruen, 2=rot"""
|
||||
int_led = int.from_bytes(
|
||||
self.__lst_io[self._ioled].get_value(),
|
||||
byteorder=self.__lst_io[self._ioled]._byteorder
|
||||
self._ioled.get_value(), byteorder="little"
|
||||
)
|
||||
led = int_led & 1
|
||||
led += int_led & 2
|
||||
return led
|
||||
|
||||
def _get_leda2(self):
|
||||
"""Gibt den Zustand der LED A2 vom core zurueck.
|
||||
"""Gibt den Zustand der LED A2 vom Core zurueck.
|
||||
@return 0=aus, 1=gruen, 2=rot"""
|
||||
int_led = int.from_bytes(
|
||||
self.__lst_io[self._ioled].get_value(),
|
||||
byteorder=self.__lst_io[self._ioled]._byteorder
|
||||
self._ioled.get_value(), byteorder="little"
|
||||
) >> 2
|
||||
led = int_led & 1
|
||||
led += int_led & 2
|
||||
return led
|
||||
|
||||
def _set_calculatedled(self, addresslist, shifted_value):
|
||||
"""Berechnet und setzt neuen Bytewert fuer LED byte.
|
||||
@param addresslist Liste der Vergleicher
|
||||
@param shifed_value Bits vergleichen"""
|
||||
# Byte als int holen
|
||||
int_led = int.from_bytes(
|
||||
self._ioled.get_value(), byteorder="little"
|
||||
)
|
||||
|
||||
for int_bit in addresslist:
|
||||
value = bool(shifted_value & int_bit)
|
||||
if bool(int_led & int_bit) != value:
|
||||
# Berechnen, wenn verändert
|
||||
if value:
|
||||
int_led += int_bit
|
||||
else:
|
||||
int_led -= int_bit
|
||||
|
||||
# Zurückschreiben wenn verändert
|
||||
self._ioled.set_value(int_led.to_bytes(length=1, byteorder="little"))
|
||||
|
||||
def _set_leda1(self, value):
|
||||
"""Setzt den Zustand der LED A1 vom core.
|
||||
"""Setzt den Zustand der LED A1 vom Core.
|
||||
@param value 0=aus, 1=gruen, 2=rot"""
|
||||
if 0 <= value <= 3:
|
||||
int_led = (self._get_leda2() << 2) + value
|
||||
self.__lst_io[self._ioled].set_value(int_led.to_bytes(
|
||||
length=1, byteorder=self.__lst_io[self._ioled]._byteorder
|
||||
))
|
||||
self._set_calculatedled([1, 2], value)
|
||||
else:
|
||||
raise ValueError("led status must be between 0 and 3")
|
||||
|
||||
def _set_leda2(self, value):
|
||||
"""Setzt den Zustand der LED A2 vom core.
|
||||
"""Setzt den Zustand der LED A2 vom Core.
|
||||
@param value 0=aus, 1=gruen, 2=rot"""
|
||||
if 0 <= value <= 3:
|
||||
int_led = (value << 2) + self._get_leda1()
|
||||
self.__lst_io[self._ioled].set_value(int_led.to_bytes(
|
||||
length=1, byteorder=self.__lst_io[self._ioled]._byteorder
|
||||
))
|
||||
self._set_calculatedled([4, 8], value << 2)
|
||||
else:
|
||||
raise ValueError("led status must be between 0 and 3")
|
||||
|
||||
@@ -522,8 +594,7 @@ class Core(Device):
|
||||
"""Statusbit fuer piControl-Treiber laeuft.
|
||||
@return True, wenn Treiber laeuft"""
|
||||
return bool(int.from_bytes(
|
||||
self.__lst_io[0].get_value(),
|
||||
byteorder=self.__lst_io[0]._byteorder
|
||||
self._iostatusbyte.get_value(), byteorder="little"
|
||||
) & 1)
|
||||
|
||||
@property
|
||||
@@ -531,8 +602,7 @@ class Core(Device):
|
||||
"""Statusbit fuer ein IO-Modul nicht mit PiCtory konfiguriert.
|
||||
@return True, wenn IO Modul nicht konfiguriert"""
|
||||
return bool(int.from_bytes(
|
||||
self.__lst_io[0].get_value(),
|
||||
byteorder=self.__lst_io[0]._byteorder
|
||||
self._iostatusbyte.get_value(), byteorder="little"
|
||||
) & 2)
|
||||
|
||||
@property
|
||||
@@ -540,8 +610,7 @@ class Core(Device):
|
||||
"""Statusbit fuer ein IO-Modul fehlt oder piGate konfiguriert.
|
||||
@return True, wenn IO-Modul fehlt oder piGate konfiguriert"""
|
||||
return bool(int.from_bytes(
|
||||
self.__lst_io[0].get_value(),
|
||||
byteorder=self.__lst_io[0]._byteorder
|
||||
self._iostatusbyte.get_value(), byteorder="little"
|
||||
) & 4)
|
||||
|
||||
@property
|
||||
@@ -549,8 +618,7 @@ class Core(Device):
|
||||
"""Statusbit Modul belegt mehr oder weniger Speicher als konfiguriert.
|
||||
@return True, wenn falscher Speicher belegt ist"""
|
||||
return bool(int.from_bytes(
|
||||
self.__lst_io[0].get_value(),
|
||||
byteorder=self.__lst_io[0]._byteorder
|
||||
self._iostatusbyte.get_value(), byteorder="little"
|
||||
) & 8)
|
||||
|
||||
@property
|
||||
@@ -558,8 +626,7 @@ class Core(Device):
|
||||
"""Statusbit links vom RevPi ist ein piGate Modul angeschlossen.
|
||||
@return True, wenn piGate links existiert"""
|
||||
return bool(int.from_bytes(
|
||||
self.__lst_io[0].get_value(),
|
||||
byteorder=self.__lst_io[0]._byteorder
|
||||
self._iostatusbyte.get_value(), byteorder="little"
|
||||
) & 16)
|
||||
|
||||
@property
|
||||
@@ -567,8 +634,7 @@ class Core(Device):
|
||||
"""Statusbit rechts vom RevPi ist ein piGate Modul angeschlossen.
|
||||
@return True, wenn piGate rechts existiert"""
|
||||
return bool(int.from_bytes(
|
||||
self.__lst_io[0].get_value(),
|
||||
byteorder=self.__lst_io[0]._byteorder
|
||||
self._iostatusbyte.get_value(), byteorder="little"
|
||||
) & 32)
|
||||
|
||||
@property
|
||||
@@ -576,8 +642,7 @@ class Core(Device):
|
||||
"""Gibt Zykluszeit der Prozessabbildsynchronisierung zurueck.
|
||||
@return Zykluszeit in ms"""
|
||||
return None if self._iocycle is None else int.from_bytes(
|
||||
self.__lst_io[self._iocycle].get_value(),
|
||||
byteorder=self.__lst_io[self._iocycle]._byteorder
|
||||
self._iocycle.get_value(), byteorder="little"
|
||||
)
|
||||
|
||||
@property
|
||||
@@ -585,8 +650,7 @@ class Core(Device):
|
||||
"""Gibt CPU-Temperatur zurueck.
|
||||
@return CPU-Temperatur in Celsius"""
|
||||
return None if self._iotemperature is None else int.from_bytes(
|
||||
self.__lst_io[self._iotemperature].get_value(),
|
||||
byteorder=self.__lst_io[self._iotemperature]._byteorder
|
||||
self._iotemperature.get_value(), byteorder="little"
|
||||
)
|
||||
|
||||
@property
|
||||
@@ -594,8 +658,7 @@ class Core(Device):
|
||||
"""Gibt CPU Taktfrequenz zurueck.
|
||||
@return CPU Taktfrequenz in MHz"""
|
||||
return None if self._iofrequency is None else int.from_bytes(
|
||||
self.__lst_io[self._iofrequency].get_value(),
|
||||
byteorder=self.__lst_io[self._iofrequency]._byteorder
|
||||
self._iofrequency.get_value(), byteorder="little"
|
||||
) * 10
|
||||
|
||||
@property
|
||||
@@ -603,8 +666,7 @@ class Core(Device):
|
||||
"""Gibt Fehleranzahl auf RS485 piBridge Bus zurueck.
|
||||
@return Fehleranzahl der piBridge"""
|
||||
return None if self._ioerrorcnt is None else int.from_bytes(
|
||||
self.__lst_io[self._ioerrorcnt].get_value(),
|
||||
byteorder=self.__lst_io[self._ioerrorcnt]._byteorder
|
||||
self._ioerrorcnt.get_value(), byteorder="little"
|
||||
)
|
||||
|
||||
@property
|
||||
@@ -632,6 +694,111 @@ class Core(Device):
|
||||
self.__errorlimit(self._ioerrorlimit2, value)
|
||||
|
||||
|
||||
class Connect(Core):
|
||||
|
||||
"""Klasse fuer den RevPi Connect.
|
||||
|
||||
Stellt Funktionen fuer die LEDs, Watchdog und den Status zur Verfuegung.
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "__evt_wdtoggle", "__th_wdtoggle", "a3green", "a3red", "wd", \
|
||||
"x2in", "x2out"
|
||||
|
||||
def __wdtoggle(self):
|
||||
"""WD Ausgang alle 10 Sekunden automatisch toggeln."""
|
||||
while not self.__evt_wdtoggle.wait(10):
|
||||
self.wd.value = not self.wd.value
|
||||
|
||||
def _devconfigure(self):
|
||||
"""Connect-Klasse vorbereiten."""
|
||||
super()._devconfigure()
|
||||
self.__evt_wdtoggle = Event()
|
||||
self.__th_wdtoggle = None
|
||||
|
||||
# Echte IOs erzeugen
|
||||
self.a3green = IOBase(self, [
|
||||
"a3green", 0, 1, self._ioled._slc_address.start,
|
||||
False, None, "LED_A3_GREEN", "4"
|
||||
], OUT, "little", False)
|
||||
self.a3red = IOBase(self, [
|
||||
"a3red", 0, 1, self._ioled._slc_address.start,
|
||||
False, None, "LED_A3_RED", "5"
|
||||
], OUT, "little", False)
|
||||
|
||||
# IO Objekte für WD und X2 in/out erzeugen
|
||||
self.wd = IOBase(self, [
|
||||
"wd", 0, 1, self._ioled._slc_address.start,
|
||||
False, None, "Connect_WatchDog", "7"
|
||||
], OUT, "little", False)
|
||||
self.x2in = IOBase(self, [
|
||||
"x2in", 0, 1, self._iostatusbyte._slc_address.start,
|
||||
False, None, "Connect_X2_IN", "6"
|
||||
], INP, "little", False)
|
||||
self.x2out = IOBase(self, [
|
||||
"x2out", 0, 1, self._ioled._slc_address.start,
|
||||
False, None, "Connect_X2_OUT", "6"
|
||||
], OUT, "little", False)
|
||||
|
||||
def _get_leda3(self):
|
||||
"""Gibt den Zustand der LED A3 vom Connect zurueck.
|
||||
@return 0=aus, 1=gruen, 2=rot"""
|
||||
int_led = int.from_bytes(
|
||||
self._ioled.get_value(), byteorder="little"
|
||||
) >> 4
|
||||
led = int_led & 1
|
||||
led += int_led & 2
|
||||
return led
|
||||
|
||||
def _get_wdtoggle(self):
|
||||
"""Ruft den Wert fuer Autowatchdog ab.
|
||||
@return True, wenn Autowatchdog aktiv ist"""
|
||||
return self.__th_wdtoggle is not None \
|
||||
and self.__th_wdtoggle.is_alive()
|
||||
|
||||
def _set_leda3(self, value):
|
||||
"""Setzt den Zustand der LED A3 vom Connect.
|
||||
@param value 0=aus, 1=gruen, 2=rot"""
|
||||
if 0 <= value <= 3:
|
||||
self._set_calculatedled([16, 32], value << 4)
|
||||
else:
|
||||
raise ValueError("led status must be between 0 and 3")
|
||||
|
||||
def _set_wdtoggle(self, value):
|
||||
"""Setzt den Wert fuer Autowatchdog.
|
||||
|
||||
Wird dieser Wert auf True gesetzt, wechselt im Hintergrund das noetige
|
||||
Bit zum toggeln des Watchdogs alle 10 Sekunden zwichen True und False.
|
||||
Dieses Bit wird bei autorefresh=True natuerlich automatisch in das
|
||||
Prozessabbild geschrieben.
|
||||
|
||||
WICHTIG: Sollte autorefresh=False sein, muss zyklisch
|
||||
.writeprocimg() aufgerufen werden, um den Wert in das
|
||||
Prozessabbild zu schreiben!!!
|
||||
|
||||
@param value True zum aktivieren, Fals zum beenden"""
|
||||
if self._modio._monitoring:
|
||||
raise RuntimeError(
|
||||
"can not toggle watchdog, while system is in monitoring mode"
|
||||
)
|
||||
if self._modio._simulator:
|
||||
raise RuntimeError(
|
||||
"can not toggle watchdog, while system is in simulator mode"
|
||||
)
|
||||
|
||||
if not value:
|
||||
self.__evt_wdtoggle.set()
|
||||
|
||||
elif not self._get_wdtoggle():
|
||||
# Watchdogtoggler erstellen
|
||||
self.__evt_wdtoggle.clear()
|
||||
self.__th_wdtoggle = Thread(target=self.__wdtoggle, daemon=True)
|
||||
self.__th_wdtoggle.start()
|
||||
|
||||
A3 = property(_get_leda3, _set_leda3)
|
||||
wdautotoggle = property(_get_wdtoggle, _set_wdtoggle)
|
||||
|
||||
|
||||
class Gateway(Device):
|
||||
|
||||
"""Klasse fuer die RevPi Gateway-Devices.
|
||||
@@ -645,6 +812,8 @@ class Gateway(Device):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "_dict_slc"
|
||||
|
||||
def __init__(self, parent, dict_device, simulator=False):
|
||||
"""Erweitert Device-Klasse um get_rawbytes-Funktionen.
|
||||
@see #Device.__init__ Device.__init__(...)"""
|
||||
@@ -674,6 +843,8 @@ class Virtual(Gateway):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def writeinputdefaults(self):
|
||||
"""Schreibt fuer ein virtuelles Device piCtory Defaultinputwerte.
|
||||
|
||||
@@ -698,7 +869,7 @@ class Virtual(Gateway):
|
||||
for io in self.get_inputs():
|
||||
self._ba_devdata[io._slc_address] = io._defaultvalue
|
||||
|
||||
# Outpus auf Bus schreiben
|
||||
# Outputs auf Bus schreiben
|
||||
try:
|
||||
self._modio._myfh.seek(self._slc_inpoff.start)
|
||||
self._modio._myfh.write(self._ba_devdata[self._slc_inp])
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# python3-RevPiModIO
|
||||
#
|
||||
# Webpage: https://revpimodio.org/
|
||||
# (c) Sven Sager, License: LGPLv3
|
||||
#
|
||||
"""RevPiModIO Helperklassen und Tools."""
|
||||
__author__ = "Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||
__license__ = "LGPLv3"
|
||||
|
||||
import queue
|
||||
import warnings
|
||||
from math import ceil
|
||||
@@ -39,6 +37,8 @@ class EventCallback(Thread):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "daemon", "exit", "func", "ioname", "iovalue"
|
||||
|
||||
def __init__(self, func, name, value):
|
||||
"""Init EventCallback class.
|
||||
|
||||
@@ -87,6 +87,11 @@ class Cycletools():
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "__cycle", "__cycletime", "__ucycle", \
|
||||
"__dict_ton", "__dict_tof", "__dict_tp", "first", \
|
||||
"flag1c", "flag5c", "flag10c", "flag15c", "flag20c", \
|
||||
"flank5c", "flank10c", "flank15c", "flank20c", "var"
|
||||
|
||||
def __init__(self, cycletime):
|
||||
"""Init Cycletools class."""
|
||||
self.__cycle = 0
|
||||
@@ -110,6 +115,11 @@ class Cycletools():
|
||||
self.flank15c = True
|
||||
self.flank20c = True
|
||||
|
||||
# Benutzerdaten
|
||||
class Var:
|
||||
pass
|
||||
self.var = Var()
|
||||
|
||||
def _docycle(self):
|
||||
"""Zyklusarbeiten."""
|
||||
# Einschaltverzoegerung
|
||||
@@ -279,6 +289,10 @@ class ProcimgWriter(Thread):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "__dict_delay", "__eventth", "__eventqth", "__eventwork", \
|
||||
"_adjwait", "_eventq", "_ioerror", "_maxioerrors", "_modio", \
|
||||
"_refresh", "_work", "daemon", "lck_refresh", "newdata"
|
||||
|
||||
def __init__(self, parentmodio):
|
||||
"""Init ProcimgWriter class.
|
||||
@param parentmodio Parent Object"""
|
||||
@@ -386,10 +400,16 @@ class ProcimgWriter(Thread):
|
||||
|
||||
def _collect_events(self, value):
|
||||
"""Aktiviert oder Deaktiviert die Eventueberwachung.
|
||||
@param value True aktiviert / False deaktiviert"""
|
||||
@param value True aktiviert / False deaktiviert
|
||||
@return True, wenn Anforderung erfolgreich war"""
|
||||
if type(value) != bool:
|
||||
raise ValueError("value must be <class 'bool'>")
|
||||
|
||||
# Nur starten, wenn System läuft
|
||||
if not self.is_alive():
|
||||
self.__eventwork = False
|
||||
return False
|
||||
|
||||
if self.__eventwork != value:
|
||||
with self.lck_refresh:
|
||||
self.__eventwork = value
|
||||
@@ -403,6 +423,8 @@ class ProcimgWriter(Thread):
|
||||
self.__eventth.daemon = True
|
||||
self.__eventth.start()
|
||||
|
||||
return True
|
||||
|
||||
def _get_ioerrors(self):
|
||||
"""Ruft aktuelle Anzahl der Fehler ab.
|
||||
@return Aktuelle Fehleranzahl"""
|
||||
@@ -413,12 +435,12 @@ class ProcimgWriter(Thread):
|
||||
self._ioerror += 1
|
||||
if self._maxioerrors != 0 and self._ioerror >= self._maxioerrors:
|
||||
raise RuntimeError(
|
||||
"reach max io error count {} on process image".format(
|
||||
"reach max io error count {0} on process image".format(
|
||||
self._maxioerrors
|
||||
)
|
||||
)
|
||||
warnings.warn(
|
||||
"count {} io errors on process image".format(self._ioerror),
|
||||
"count {0} io errors on process image".format(self._ioerror),
|
||||
RuntimeWarning
|
||||
)
|
||||
|
||||
@@ -443,7 +465,7 @@ class ProcimgWriter(Thread):
|
||||
# Lockobjekt holen und Fehler werfen, wenn nicht schnell genug
|
||||
if not self.lck_refresh.acquire(timeout=self._adjwait):
|
||||
warnings.warn(
|
||||
"cycle time of {} ms exceeded on lock".format(
|
||||
"cycle time of {0} ms exceeded on lock".format(
|
||||
int(self._refresh * 1000)
|
||||
),
|
||||
RuntimeWarning
|
||||
@@ -458,13 +480,13 @@ class ProcimgWriter(Thread):
|
||||
if self._modio._monitoring:
|
||||
# Inputs und Outputs in Puffer
|
||||
for dev in self._modio._lst_refresh:
|
||||
dev._filelock.acquire()
|
||||
dev._ba_devdata[:] = bytesbuff[dev._slc_devoff]
|
||||
if self.__eventwork \
|
||||
and len(dev._dict_events) > 0 \
|
||||
and dev._ba_datacp != dev._ba_devdata:
|
||||
self.__check_change(dev)
|
||||
dev._filelock.release()
|
||||
with dev._filelock:
|
||||
dev._ba_devdata[:] = bytesbuff[dev._slc_devoff]
|
||||
if self.__eventwork \
|
||||
and len(dev._dict_events) > 0 \
|
||||
and dev._ba_datacp != dev._ba_devdata:
|
||||
self.__check_change(dev)
|
||||
|
||||
else:
|
||||
# Inputs in Puffer, Outputs in Prozessabbild
|
||||
for dev in self._modio._lst_refresh:
|
||||
@@ -495,9 +517,9 @@ class ProcimgWriter(Thread):
|
||||
finally:
|
||||
# Verzögerte Events prüfen
|
||||
if self.__eventwork:
|
||||
for tup_fire in list(self.__dict_delay.keys()):
|
||||
if tup_fire[0].overwrite \
|
||||
and getattr(self._modio.io, tup_fire[1]).value != \
|
||||
for tup_fire in tuple(self.__dict_delay.keys()):
|
||||
if tup_fire[0].overwrite and \
|
||||
getattr(self._modio.io, tup_fire[1]).value != \
|
||||
tup_fire[2]:
|
||||
del self.__dict_delay[tup_fire]
|
||||
else:
|
||||
@@ -518,7 +540,7 @@ class ProcimgWriter(Thread):
|
||||
self._adjwait -= 0.001
|
||||
if self._adjwait < 0:
|
||||
warnings.warn(
|
||||
"cycle time of {} ms exceeded".format(
|
||||
"cycle time of {0} ms exceeded".format(
|
||||
int(self._refresh * 1000)
|
||||
),
|
||||
RuntimeWarning
|
||||
@@ -534,7 +556,6 @@ class ProcimgWriter(Thread):
|
||||
|
||||
def stop(self):
|
||||
"""Beendet die automatische Prozessabbildsynchronisierung."""
|
||||
self._collect_events(False)
|
||||
self._work.set()
|
||||
|
||||
def set_maxioerrors(self, value):
|
||||
@@ -548,13 +569,13 @@ class ProcimgWriter(Thread):
|
||||
def set_refresh(self, value):
|
||||
"""Setzt die Zykluszeit in Millisekunden.
|
||||
@param value <class 'int'> Millisekunden"""
|
||||
if type(value) == int and 10 <= value <= 2000:
|
||||
if type(value) == int and 5 <= value <= 2000:
|
||||
waitdiff = self._refresh - self._adjwait
|
||||
self._refresh = value / 1000
|
||||
self._adjwait = self._refresh - waitdiff
|
||||
self._adjwait = 0 if waitdiff < 0 else self._refresh - waitdiff
|
||||
else:
|
||||
raise ValueError(
|
||||
"refresh time must be 10 to 2000 milliseconds"
|
||||
"refresh time must be 5 to 2000 milliseconds"
|
||||
)
|
||||
|
||||
ioerrors = property(_get_ioerrors)
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# python3-RevPiModIO
|
||||
#
|
||||
# Webpage: https://revpimodio.org/
|
||||
# (c) Sven Sager, License: LGPLv3
|
||||
#
|
||||
"""RevPiModIO Modul fuer die Verwaltung der IOs."""
|
||||
__author__ = "Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||
__license__ = "LGPLv3"
|
||||
|
||||
import struct
|
||||
from re import match as rematch
|
||||
from threading import Event
|
||||
@@ -16,6 +14,8 @@ class IOEvent(object):
|
||||
|
||||
"""Basisklasse fuer IO-Events."""
|
||||
|
||||
__slots__ = "as_thread", "delay", "edge", "func", "overwrite"
|
||||
|
||||
def __init__(self, func, edge, as_thread, delay, overwrite):
|
||||
"""Init IOEvent class."""
|
||||
self.as_thread = as_thread
|
||||
@@ -69,7 +69,7 @@ class IOList(object):
|
||||
if key in self.__dict_iorefname:
|
||||
return self.__dict_iorefname[key]
|
||||
else:
|
||||
raise AttributeError("can not find io '{}'".format(key))
|
||||
raise AttributeError("can not find io '{0}'".format(key))
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Ruft angegebenen IO ab.
|
||||
@@ -86,7 +86,7 @@ class IOList(object):
|
||||
"""
|
||||
if type(key) == int:
|
||||
if key not in self.__dict_iobyte:
|
||||
raise KeyError("byte '{}' does not exist".format(key))
|
||||
raise KeyError("byte '{0}' does not exist".format(key))
|
||||
return self.__dict_iobyte[key]
|
||||
elif type(key) == slice:
|
||||
return [
|
||||
@@ -151,14 +151,14 @@ class IOList(object):
|
||||
if oldio._bitaddress >= 0:
|
||||
if io._bitaddress == oldio._bitaddress:
|
||||
raise MemoryError(
|
||||
"bit {} already assigned to '{}'".format(
|
||||
"bit {0} already assigned to '{1}'".format(
|
||||
io._bitaddress, oldio._name
|
||||
)
|
||||
)
|
||||
else:
|
||||
# Bereits überschriebene bytes sind ungültig
|
||||
raise MemoryError(
|
||||
"new io '{}' overlaps memory of '{}'".format(
|
||||
"new io '{0}' overlaps memory of '{1}'".format(
|
||||
io._name, oldio._name
|
||||
)
|
||||
)
|
||||
@@ -191,10 +191,10 @@ class IOList(object):
|
||||
def _private_register_new_io_object(self, new_io):
|
||||
"""Registriert neues IO Objekt unabhaenging von __setattr__.
|
||||
@param new_io Neues IO Objekt"""
|
||||
if issubclass(type(new_io), IOBase):
|
||||
if isinstance(new_io, IOBase):
|
||||
if hasattr(self, new_io._name):
|
||||
raise AttributeError(
|
||||
"attribute {} already exists - can not set io".format(
|
||||
"attribute {0} already exists - can not set io".format(
|
||||
new_io._name
|
||||
)
|
||||
)
|
||||
@@ -222,6 +222,8 @@ class DeadIO(object):
|
||||
|
||||
"""Klasse, mit der ersetzte IOs verwaltet werden."""
|
||||
|
||||
__slots__ = "__deadio"
|
||||
|
||||
def __init__(self, deadio):
|
||||
"""Instantiierung der DeadIO-Klasse.
|
||||
@param deadio IO, der ersetzt wurde"""
|
||||
@@ -250,19 +252,23 @@ class IOBase(object):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "_bitaddress", "_bitlength", "_byteorder", "_defaultvalue", \
|
||||
"_iotype", "_length", "_name", "_parentdevice", \
|
||||
"_signed", "_slc_address", "bmk", "export"
|
||||
|
||||
def __init__(self, parentdevice, valuelist, iotype, byteorder, signed):
|
||||
"""Instantiierung der IOBase-Klasse.
|
||||
|
||||
@param parentdevice Parentdevice auf dem der IO liegt
|
||||
@param valuelist Datenliste fuer Instantiierung
|
||||
["name","defval","bitlen","startaddr",exp,"idx","bmk","bitaddr"]
|
||||
["name","defval","bitlen","startaddrdev",exp,"idx","bmk","bitaddr"]
|
||||
@param iotype <class 'int'> Wert
|
||||
@param byteorder Byteorder 'little'/'big' fuer <class 'int'> Berechnung
|
||||
@param sigend Intberechnung mit Vorzeichen durchfuehren
|
||||
|
||||
"""
|
||||
# ["name","defval","bitlen","startaddr",exp,"idx","bmk","bitaddr"]
|
||||
# [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ]
|
||||
# ["name","defval","bitlen","startaddrdev",exp,"idx","bmk","bitaddr"]
|
||||
# [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ]
|
||||
self._parentdevice = parentdevice
|
||||
|
||||
# Bitadressen auf Bytes aufbrechen und umrechnen
|
||||
@@ -277,6 +283,7 @@ class IOBase(object):
|
||||
self._name = valuelist[0]
|
||||
self._signed = signed
|
||||
self.bmk = valuelist[6]
|
||||
self.export = bool(valuelist[4])
|
||||
|
||||
int_startaddress = int(valuelist[3])
|
||||
if self._bitaddress == -1:
|
||||
@@ -298,7 +305,7 @@ class IOBase(object):
|
||||
else:
|
||||
raise ValueError(
|
||||
"given bytes for default value must have a length "
|
||||
"of {} but {} was given"
|
||||
"of {0} but {1} was given"
|
||||
"".format(self._length, len(valuelist[1]))
|
||||
)
|
||||
else:
|
||||
@@ -312,12 +319,12 @@ class IOBase(object):
|
||||
if len(buff) <= self._length:
|
||||
self._defaultvalue = \
|
||||
buff + bytes(self._length - len(buff))
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
else:
|
||||
# Höhere Bits als 7 auf nächste Bytes umbrechen
|
||||
int_startaddress += int((int(valuelist[7]) % 16) / 8)
|
||||
int_startaddress += int(int(valuelist[7]) / 8)
|
||||
self._slc_address = slice(
|
||||
int_startaddress, int_startaddress + 1
|
||||
)
|
||||
@@ -328,7 +335,7 @@ class IOBase(object):
|
||||
else:
|
||||
try:
|
||||
self._defaultvalue = bool(int(valuelist[1]))
|
||||
except:
|
||||
except Exception:
|
||||
self._defaultvalue = False
|
||||
|
||||
def __bool__(self):
|
||||
@@ -367,7 +374,7 @@ class IOBase(object):
|
||||
# Prüfen ob Funktion callable ist
|
||||
if not callable(func):
|
||||
raise AttributeError(
|
||||
"registered function '{}' is not callable".format(func)
|
||||
"registered function '{0}' is not callable".format(func)
|
||||
)
|
||||
if type(delay) != int or delay < 0:
|
||||
raise AttributeError(
|
||||
@@ -379,8 +386,9 @@ class IOBase(object):
|
||||
)
|
||||
|
||||
if self not in self._parentdevice._dict_events:
|
||||
self._parentdevice._dict_events[self] = \
|
||||
[IOEvent(func, edge, as_thread, delay, overwrite)]
|
||||
with self._parentdevice._filelock:
|
||||
self._parentdevice._dict_events[self] = \
|
||||
[IOEvent(func, edge, as_thread, delay, overwrite)]
|
||||
else:
|
||||
# Prüfen ob Funktion schon registriert ist
|
||||
for regfunc in self._parentdevice._dict_events[self]:
|
||||
@@ -391,29 +399,31 @@ class IOBase(object):
|
||||
if edge == BOTH or regfunc.edge == BOTH:
|
||||
if self._bitaddress < 0:
|
||||
raise AttributeError(
|
||||
"io '{}' with function '{}' already in list."
|
||||
"io '{0}' with function '{1}' already in list."
|
||||
"".format(self._name, func)
|
||||
)
|
||||
else:
|
||||
raise AttributeError(
|
||||
"io '{}' with function '{}' already in list with "
|
||||
"edge '{}' - edge '{}' not allowed anymore".format(
|
||||
"io '{0}' with function '{1}' already in list "
|
||||
"with edge '{2}' - edge '{3}' not allowed anymore"
|
||||
"".format(
|
||||
self._name, func,
|
||||
consttostr(regfunc.edge), consttostr(edge)
|
||||
)
|
||||
)
|
||||
elif regfunc.edge == edge:
|
||||
raise AttributeError(
|
||||
"io '{}' with function '{}' for given edge '{}' "
|
||||
"io '{0}' with function '{1}' for given edge '{2}' "
|
||||
"already in list".format(
|
||||
self._name, func, consttostr(edge)
|
||||
)
|
||||
)
|
||||
|
||||
# Eventfunktion einfügen
|
||||
self._parentdevice._dict_events[self].append(
|
||||
IOEvent(func, edge, as_thread, delay, overwrite)
|
||||
)
|
||||
with self._parentdevice._filelock:
|
||||
self._parentdevice._dict_events[self].append(
|
||||
IOEvent(func, edge, as_thread, delay, overwrite)
|
||||
)
|
||||
|
||||
def _get_address(self):
|
||||
"""Gibt die absolute Byteadresse im Prozessabbild zurueck.
|
||||
@@ -519,7 +529,7 @@ class IOBase(object):
|
||||
>Python3 struct</a>
|
||||
|
||||
"""
|
||||
if not issubclass(type(self._parentdevice), Gateway):
|
||||
if not isinstance(self._parentdevice, Gateway):
|
||||
raise RuntimeError(
|
||||
"this function can be used for ios on gatway or virtual "
|
||||
"devices only"
|
||||
@@ -584,31 +594,31 @@ class IOBase(object):
|
||||
value
|
||||
else:
|
||||
raise ValueError(
|
||||
"'{}' requires a <class 'bytes'> object of length "
|
||||
"{}, but {} was given".format(
|
||||
"'{0}' requires a <class 'bytes'> object of "
|
||||
"length {1}, but {2} was given".format(
|
||||
self._name, self._length, len(value)
|
||||
)
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
"'{}' requires a <class 'bytes'> object, not {}"
|
||||
"'{0}' requires a <class 'bytes'> object, not {1}"
|
||||
"".format(self._name, type(value))
|
||||
)
|
||||
|
||||
elif self._iotype == INP:
|
||||
if self._parentdevice._modio._simulator:
|
||||
raise AttributeError(
|
||||
"can not write to output '{}' in simulator mode"
|
||||
"can not write to output '{0}' in simulator mode"
|
||||
"".format(self._name)
|
||||
)
|
||||
else:
|
||||
raise AttributeError(
|
||||
"can not write to input '{}'".format(self._name)
|
||||
"can not write to input '{0}'".format(self._name)
|
||||
)
|
||||
|
||||
elif self._iotype == MEM:
|
||||
raise AttributeError(
|
||||
"can not write to memory '{}'".format(self._name)
|
||||
"can not write to memory '{0}'".format(self._name)
|
||||
)
|
||||
|
||||
def unreg_event(self, func=None, edge=None):
|
||||
@@ -620,7 +630,8 @@ class IOBase(object):
|
||||
"""
|
||||
if self in self._parentdevice._dict_events:
|
||||
if func is None:
|
||||
del self._parentdevice._dict_events[self]
|
||||
with self._parentdevice._filelock:
|
||||
del self._parentdevice._dict_events[self]
|
||||
else:
|
||||
newlist = []
|
||||
for regfunc in self._parentdevice._dict_events[self]:
|
||||
@@ -630,10 +641,11 @@ class IOBase(object):
|
||||
newlist.append(regfunc)
|
||||
|
||||
# Wenn Funktionen übrig bleiben, diese übernehmen
|
||||
if len(newlist) > 0:
|
||||
self._parentdevice._dict_events[self] = newlist
|
||||
else:
|
||||
del self._parentdevice._dict_events[self]
|
||||
with self._parentdevice._filelock:
|
||||
if len(newlist) > 0:
|
||||
self._parentdevice._dict_events[self] = newlist
|
||||
else:
|
||||
del self._parentdevice._dict_events[self]
|
||||
|
||||
def wait(self, edge=BOTH, exitevent=None, okvalue=None, timeout=0):
|
||||
"""Wartet auf Wertaenderung eines IOs.
|
||||
@@ -679,7 +691,7 @@ class IOBase(object):
|
||||
# Prüfen ob Device in autorefresh ist
|
||||
if not self._parentdevice._selfupdate:
|
||||
raise RuntimeError(
|
||||
"autorefresh is not activated for device '{}|{}' - there "
|
||||
"autorefresh is not activated for device '{0}|{1}' - there "
|
||||
"will never be new data".format(
|
||||
self._parentdevice._position, self._parentdevice._name
|
||||
)
|
||||
@@ -768,6 +780,8 @@ class IntIO(IOBase):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __int__(self):
|
||||
"""Gibt IO-Wert zurueck mit Beachtung byteorder/signed.
|
||||
@return IO-Wert als <class 'int'>"""
|
||||
@@ -825,7 +839,7 @@ class IntIO(IOBase):
|
||||
))
|
||||
else:
|
||||
raise ValueError(
|
||||
"'{}' need a <class 'int'> value, but {} was given"
|
||||
"'{0}' need a <class 'int'> value, but {1} was given"
|
||||
"".format(self._name, type(value))
|
||||
)
|
||||
|
||||
@@ -845,6 +859,9 @@ class StructIO(IOBase):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "__frm", "_parentio_address", "_parentio_defaultvalue", \
|
||||
"_parentio_length"
|
||||
|
||||
def __init__(self, parentio, name, frm, **kwargs):
|
||||
"""Erstellt einen IO mit struct-Formatierung.
|
||||
|
||||
@@ -873,7 +890,7 @@ class StructIO(IOBase):
|
||||
max_bits = parentio._length * 8
|
||||
if not (0 <= bitaddress < max_bits):
|
||||
raise AttributeError(
|
||||
"bitaddress must be a value between 0 and {}"
|
||||
"bitaddress must be a value between 0 and {0}"
|
||||
"".format(max_bits - 1)
|
||||
)
|
||||
bitlength = 1
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# python3-RevPiModIO
|
||||
#
|
||||
# Webpage: https://revpimodio.org/
|
||||
# (c) Sven Sager, License: LGPLv3
|
||||
#
|
||||
"""RevPiModIO Hauptklasse fuer piControl0 Zugriff."""
|
||||
__author__ = "Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||
__license__ = "LGPLv3"
|
||||
|
||||
import warnings
|
||||
from json import load as jload
|
||||
from multiprocessing import cpu_count
|
||||
from os import access, F_OK, R_OK
|
||||
from queue import Empty
|
||||
from signal import signal, SIG_DFL, SIGINT, SIGTERM
|
||||
from threading import Thread, Event
|
||||
from timeit import default_timer
|
||||
|
||||
from . import app as appmodule
|
||||
from . import device as devicemodule
|
||||
@@ -33,6 +33,13 @@ class RevPiModIO(object):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "__cleanupfunc", "_autorefresh", "_buffedwrite", \
|
||||
"_configrsc", "_exit", "_imgwriter", "_ioerror", "_length", \
|
||||
"_looprunning", "_lst_devselect", "_lst_refresh", "_maxioerrors", \
|
||||
"_myfh", "_monitoring", "_procimg", "_simulator", "_syncoutputs", \
|
||||
"_th_mainloop", "_waitexit", \
|
||||
"core", "app", "device", "exitsignal", "io", "summary"
|
||||
|
||||
def __init__(
|
||||
self, autorefresh=False, monitoring=False, syncoutputs=True,
|
||||
procimg=None, configrsc=None, simulator=False):
|
||||
@@ -79,6 +86,9 @@ class RevPiModIO(object):
|
||||
self.io = None
|
||||
self.summary = None
|
||||
|
||||
# Event für Benutzeraktionen
|
||||
self.exitsignal = Event()
|
||||
|
||||
# Nur Konfigurieren, wenn nicht vererbt
|
||||
if type(self) == RevPiModIO:
|
||||
self._configure(self.get_jconfigrsc())
|
||||
@@ -100,7 +110,8 @@ class RevPiModIO(object):
|
||||
if self.__cleanupfunc is not None:
|
||||
self.readprocimg()
|
||||
self.__cleanupfunc()
|
||||
self.writeprocimg()
|
||||
if not self._monitoring:
|
||||
self.writeprocimg()
|
||||
|
||||
def _configure(self, jconfigrsc):
|
||||
"""Verarbeitet die piCtory Konfigurationsdatei."""
|
||||
@@ -150,10 +161,17 @@ class RevPiModIO(object):
|
||||
device["position"] += 1
|
||||
|
||||
if device["type"] == "BASE":
|
||||
# Core
|
||||
dev_new = devicemodule.Core(
|
||||
self, device, simulator=self._simulator
|
||||
)
|
||||
pt = int(device["productType"])
|
||||
if pt == 105:
|
||||
# RevPi Connect
|
||||
dev_new = devicemodule.Connect(
|
||||
self, device, simulator=self._simulator
|
||||
)
|
||||
else:
|
||||
# RevPi Core immer als Fallback verwenden
|
||||
dev_new = devicemodule.Core(
|
||||
self, device, simulator=self._simulator
|
||||
)
|
||||
self.core = dev_new
|
||||
elif device["type"] == "LEFT_RIGHT":
|
||||
# IOs
|
||||
@@ -170,10 +188,13 @@ class RevPiModIO(object):
|
||||
dev_new = devicemodule.Gateway(
|
||||
self, device, simulator=self._simulator
|
||||
)
|
||||
elif device["type"] == "RIGHT":
|
||||
# Connectdevice
|
||||
dev_new = None
|
||||
else:
|
||||
# Device-Type nicht gefunden
|
||||
warnings.warn(
|
||||
"device type '{}' unknown".format(device["type"]),
|
||||
"device type '{0}' unknown".format(device["type"]),
|
||||
Warning
|
||||
)
|
||||
dev_new = None
|
||||
@@ -205,10 +226,28 @@ class RevPiModIO(object):
|
||||
# ImgWriter erstellen
|
||||
self._imgwriter = helpermodule.ProcimgWriter(self)
|
||||
|
||||
# Refreshzeit CM1 25 Hz / CM3 50 Hz
|
||||
if not isinstance(self, RevPiNetIO):
|
||||
self._imgwriter.refresh = 20 if cpu_count() > 1 else 40
|
||||
|
||||
# Aktuellen Outputstatus von procimg einlesen
|
||||
if self._syncoutputs:
|
||||
self.syncoutputs()
|
||||
|
||||
# Für RS485 errors am core defaults laden sollte procimg NULL sein
|
||||
if not (self.core is None or self._monitoring or self._simulator):
|
||||
if self.core._ioerrorlimit1 is not None:
|
||||
self.core._ioerrorlimit1.set_value(
|
||||
self.core._ioerrorlimit1._defaultvalue
|
||||
)
|
||||
if self.core._ioerrorlimit2 is not None:
|
||||
self.core._ioerrorlimit2.set_value(
|
||||
self.core._ioerrorlimit2._defaultvalue
|
||||
)
|
||||
|
||||
# RS485 errors schreiben
|
||||
self.writeprocimg(self.core)
|
||||
|
||||
# Optional ins autorefresh aufnehmen
|
||||
if self._autorefresh:
|
||||
self.autorefresh_all()
|
||||
@@ -270,12 +309,12 @@ class RevPiModIO(object):
|
||||
self._ioerror += 1
|
||||
if self._maxioerrors != 0 and self._ioerror >= self._maxioerrors:
|
||||
raise RuntimeError(
|
||||
"reach max io error count {} on process image".format(
|
||||
"reach max io error count {0} on process image".format(
|
||||
self._maxioerrors
|
||||
)
|
||||
)
|
||||
warnings.warn(
|
||||
"got io error during {} and count {} errors now".format(
|
||||
"got io error during {0} and count {1} errors now".format(
|
||||
action, self._ioerror
|
||||
),
|
||||
RuntimeWarning
|
||||
@@ -286,7 +325,7 @@ class RevPiModIO(object):
|
||||
@param milliseconds <class 'int'> in Millisekunden"""
|
||||
if self._looprunning:
|
||||
raise RuntimeError(
|
||||
"can not change cycletime when cycleloop or mainloop are "
|
||||
"can not change cycletime when cycleloop or mainloop is "
|
||||
"running"
|
||||
)
|
||||
else:
|
||||
@@ -316,7 +355,7 @@ class RevPiModIO(object):
|
||||
self.io = None
|
||||
self.summary = None
|
||||
|
||||
def cycleloop(self, func, cycletime=None):
|
||||
def cycleloop(self, func, cycletime=50):
|
||||
"""Startet den Cycleloop.
|
||||
|
||||
Der aktuelle Programmthread wird hier bis Aufruf von
|
||||
@@ -328,19 +367,20 @@ class RevPiModIO(object):
|
||||
Prozessabbild geschrieben.
|
||||
|
||||
Verlassen wird der Cycleloop, wenn die aufgerufene Funktion einen
|
||||
Rueckgabewert nicht gleich None liefert, oder durch Aufruf von
|
||||
revpimodio.exit().
|
||||
Rueckgabewert nicht gleich None liefert (z.B. return True), oder durch
|
||||
Aufruf von .exit().
|
||||
|
||||
HINWEIS: Die Aktualisierungszeit und die Laufzeit der Funktion duerfen
|
||||
die eingestellte autorefresh Zeit, bzw. uebergebene cycletime nicht
|
||||
ueberschreiten!
|
||||
|
||||
Ueber das Attribut cycletime kann die Aktualisierungsrate fuer das
|
||||
Prozessabbild gesetzt werden.
|
||||
Ueber den Parameter cycletime wird die gewuenschte Zukluszeit der
|
||||
uebergebenen Funktion gesetzt. Der Standardwert betraegt
|
||||
50 Millisekunden, in denen das Prozessabild eingelesen, die uebergebene
|
||||
Funktion ausgefuert und das Prozessabbild geschrieben wird.
|
||||
|
||||
@param func Funktion, die ausgefuehrt werden soll
|
||||
@param cycletime Zykluszeit in Millisekunden, bei Nichtangabe wird
|
||||
aktuelle .cycletime Zeit verwendet - Standardwert 50 ms
|
||||
@param cycletime Zykluszeit in Millisekunden - Standardwert 50 ms
|
||||
@return None
|
||||
|
||||
"""
|
||||
@@ -352,21 +392,28 @@ class RevPiModIO(object):
|
||||
|
||||
# Prüfen ob Devices in autorefresh sind
|
||||
if len(self._lst_refresh) == 0:
|
||||
raise RuntimeError("no device with autorefresh activated")
|
||||
raise RuntimeError(
|
||||
"no device with autorefresh activated - use autorefresh=True "
|
||||
"or call .autorefresh_all() before entering cycleloop"
|
||||
)
|
||||
|
||||
# Prüfen ob Funktion callable ist
|
||||
if not callable(func):
|
||||
raise RuntimeError(
|
||||
"registered function '{}' ist not callable".format(func)
|
||||
"registered function '{0}' ist not callable".format(func)
|
||||
)
|
||||
|
||||
# Zykluszeit übernehmen
|
||||
if not (cycletime is None or cycletime == self._imgwriter.refresh):
|
||||
old_cycletime = self._imgwriter.refresh
|
||||
if not cycletime == self._imgwriter.refresh:
|
||||
self._imgwriter.refresh = cycletime
|
||||
|
||||
# Zeitänderung in _imgwriter neuladen
|
||||
self._imgwriter.newdata.clear()
|
||||
|
||||
# Benutzerevent
|
||||
self.exitsignal.clear()
|
||||
|
||||
# Cycleloop starten
|
||||
self._exit.clear()
|
||||
self._looprunning = True
|
||||
@@ -403,6 +450,9 @@ class RevPiModIO(object):
|
||||
# Cycleloop beenden
|
||||
self._looprunning = False
|
||||
|
||||
# Alte autorefresh Zeit setzen
|
||||
self._imgwriter.refresh = old_cycletime
|
||||
|
||||
return ec
|
||||
|
||||
def exit(self, full=True):
|
||||
@@ -416,6 +466,10 @@ class RevPiModIO(object):
|
||||
wird dann gestoppt und das Programm kann sauber beendet werden.
|
||||
|
||||
@param full Entfernt auch alle Devices aus autorefresh"""
|
||||
|
||||
# Benutzerevent
|
||||
self.exitsignal.set()
|
||||
|
||||
self._exit.set()
|
||||
self._waitexit.set()
|
||||
|
||||
@@ -425,7 +479,7 @@ class RevPiModIO(object):
|
||||
self._imgwriter.stop()
|
||||
self._imgwriter.join(self._imgwriter._refresh)
|
||||
|
||||
# Mainloop beenden und darauf waretn
|
||||
# Mainloop beenden und darauf 1 Sekunde warten
|
||||
if self._th_mainloop is not None and self._th_mainloop.is_alive():
|
||||
self._th_mainloop.join(1)
|
||||
|
||||
@@ -443,7 +497,7 @@ class RevPiModIO(object):
|
||||
if self._configrsc is not None:
|
||||
if not access(self._configrsc, F_OK | R_OK):
|
||||
raise RuntimeError(
|
||||
"can not access pictory configuration at {}".format(
|
||||
"can not access pictory configuration at {0}".format(
|
||||
self._configrsc))
|
||||
else:
|
||||
# piCtory Konfiguration an bekannten Stellen prüfen
|
||||
@@ -454,7 +508,7 @@ class RevPiModIO(object):
|
||||
break
|
||||
if self._configrsc is None:
|
||||
raise RuntimeError(
|
||||
"can not access known pictory configurations at {} - "
|
||||
"can not access known pictory configurations at {0} - "
|
||||
"use 'configrsc' parameter so specify location"
|
||||
"".format(", ".join(lst_rsc))
|
||||
)
|
||||
@@ -462,7 +516,7 @@ class RevPiModIO(object):
|
||||
with open(self._configrsc, "r") as fhconfigrsc:
|
||||
try:
|
||||
jdata = jload(fhconfigrsc)
|
||||
except:
|
||||
except Exception:
|
||||
raise RuntimeError(
|
||||
"can not read piCtory configuration - check your hardware "
|
||||
"configuration http://revpi_ip/"
|
||||
@@ -494,7 +548,8 @@ class RevPiModIO(object):
|
||||
# Prüfen ob Funktion callable ist
|
||||
if not (cleanupfunc is None or callable(cleanupfunc)):
|
||||
raise RuntimeError(
|
||||
"registered function '{}' ist not callable".format(cleanupfunc)
|
||||
"registered function '{0}' ist not callable"
|
||||
"".format(cleanupfunc)
|
||||
)
|
||||
self.__cleanupfunc = cleanupfunc
|
||||
signal(SIGINT, self.__evt_exit)
|
||||
@@ -527,7 +582,10 @@ class RevPiModIO(object):
|
||||
|
||||
# Prüfen ob Devices in autorefresh sind
|
||||
if len(self._lst_refresh) == 0:
|
||||
raise RuntimeError("no device with autorefresh activated")
|
||||
raise RuntimeError(
|
||||
"no device with autorefresh activated - use autorefresh=True "
|
||||
"or call .autorefresh_all() before entering mainloop"
|
||||
)
|
||||
|
||||
# Thread erstellen, wenn nicht blockieren soll
|
||||
if not blocking:
|
||||
@@ -537,25 +595,48 @@ class RevPiModIO(object):
|
||||
self._th_mainloop.start()
|
||||
return
|
||||
|
||||
# Benutzerevent
|
||||
self.exitsignal.clear()
|
||||
|
||||
# Event säubern vor Eintritt in Mainloop
|
||||
self._exit.clear()
|
||||
self._looprunning = True
|
||||
|
||||
# Beim Eintritt in mainloop Bytecopy erstellen
|
||||
for dev in self._lst_refresh:
|
||||
dev._filelock.acquire()
|
||||
dev._ba_datacp = dev._ba_devdata[:]
|
||||
dev._filelock.release()
|
||||
with dev._filelock:
|
||||
dev._ba_datacp = dev._ba_devdata[:]
|
||||
|
||||
# ImgWriter mit Eventüberwachung aktivieren
|
||||
self._imgwriter._collect_events(True)
|
||||
e = None
|
||||
runtime = 0
|
||||
|
||||
while not self._exit.is_set():
|
||||
|
||||
# Laufzeit der Eventqueue auf 0 setzen
|
||||
if self._imgwriter._eventq.qsize() == 0:
|
||||
runtime = 0
|
||||
|
||||
try:
|
||||
tup_fire = self._imgwriter._eventq.get(timeout=1)
|
||||
|
||||
# Messung Laufzeit der Queue starten
|
||||
if runtime == 0:
|
||||
runtime = default_timer()
|
||||
|
||||
# Direct callen da Prüfung in io.IOBase.reg_event ist
|
||||
tup_fire[0].func(tup_fire[1], tup_fire[2])
|
||||
|
||||
# Laufzeitprüfung
|
||||
if runtime != -1 and \
|
||||
default_timer() - runtime > self._imgwriter._refresh:
|
||||
runtime = -1
|
||||
warnings.warn(
|
||||
"can not execute all event functions in one cycle - "
|
||||
"rise .cycletime or optimize your event functions",
|
||||
RuntimeWarning
|
||||
)
|
||||
except Empty:
|
||||
if not self._exit.is_set() and not self._imgwriter.is_alive():
|
||||
self.exit(full=False)
|
||||
@@ -585,12 +666,12 @@ class RevPiModIO(object):
|
||||
if device is None:
|
||||
mylist = self.device
|
||||
else:
|
||||
dev = device if issubclass(type(device), devicemodule.Device) \
|
||||
dev = device if isinstance(device, devicemodule.Device) \
|
||||
else self.device.__getitem__(device)
|
||||
|
||||
if dev._selfupdate:
|
||||
raise RuntimeError(
|
||||
"can not read process image, while device '{}|{}'"
|
||||
"can not read process image, while device '{0}|{1}'"
|
||||
"is in autorefresh mode".format(dev._position, dev._name)
|
||||
)
|
||||
mylist = [dev]
|
||||
@@ -640,7 +721,7 @@ class RevPiModIO(object):
|
||||
if device is None:
|
||||
mylist = self.device
|
||||
else:
|
||||
dev = device if issubclass(type(device), devicemodule.Device) \
|
||||
dev = device if isinstance(device, devicemodule.Device) \
|
||||
else self.device.__getitem__(device)
|
||||
mylist = [dev]
|
||||
|
||||
@@ -660,12 +741,12 @@ class RevPiModIO(object):
|
||||
if device is None:
|
||||
mylist = self.device
|
||||
else:
|
||||
dev = device if issubclass(type(device), devicemodule.Device) \
|
||||
dev = device if isinstance(device, devicemodule.Device) \
|
||||
else self.device.__getitem__(device)
|
||||
|
||||
if dev._selfupdate:
|
||||
raise RuntimeError(
|
||||
"can not sync process image, while device '{}|{}'"
|
||||
"can not sync outputs, while device '{0}|{1}'"
|
||||
"is in autorefresh mode".format(dev._position, dev._name)
|
||||
)
|
||||
mylist = [dev]
|
||||
@@ -703,12 +784,12 @@ class RevPiModIO(object):
|
||||
if device is None:
|
||||
mylist = self.device
|
||||
else:
|
||||
dev = device if issubclass(type(device), devicemodule.Device) \
|
||||
dev = device if isinstance(device, devicemodule.Device) \
|
||||
else self.device.__getitem__(device)
|
||||
|
||||
if dev._selfupdate:
|
||||
raise RuntimeError(
|
||||
"can not write process image, while device '{}|{}'"
|
||||
"can not write process image, while device '{0}|{1}'"
|
||||
"is in autorefresh mode".format(dev._position, dev._name)
|
||||
)
|
||||
mylist = [dev]
|
||||
@@ -759,6 +840,8 @@ class RevPiModIOSelected(RevPiModIO):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(
|
||||
self, deviceselection, autorefresh=False, monitoring=False,
|
||||
syncoutputs=True, procimg=None, configrsc=None, simulator=False):
|
||||
@@ -823,6 +906,8 @@ class RevPiModIODriver(RevPiModIOSelected):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(
|
||||
self, virtdev, autorefresh=False, monitoring=False,
|
||||
syncoutputs=True, procimg=None, configrsc=None):
|
||||
@@ -842,4 +927,4 @@ class RevPiModIODriver(RevPiModIOSelected):
|
||||
|
||||
|
||||
# Nachträglicher Import
|
||||
from .netio import RevPiNetIODriver
|
||||
from .netio import RevPiNetIODriver, RevPiNetIO
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# python3-RevPiModIO
|
||||
#
|
||||
# Webpage: https://revpimodio.org/
|
||||
# (c) Sven Sager, License: LGPLv3
|
||||
#
|
||||
"""RevPiModIO Hauptklasse fuer Netzwerkzugriff."""
|
||||
__author__ = "Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||
__license__ = "LGPLv3"
|
||||
|
||||
import socket
|
||||
import warnings
|
||||
from json import loads as jloads
|
||||
@@ -37,10 +35,16 @@ class NetFH(Thread):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "__by_buff", "__int_buff", "__dictdirty", "__flusherr", \
|
||||
"__position", "__sockact", "__sockerr", "__sockend", "__socklock", \
|
||||
"__timeout", "__trigger", "__waitsync", \
|
||||
"_address", "_slavesock", \
|
||||
"daemon"
|
||||
|
||||
def __init__(self, address, timeout=500):
|
||||
"""Init NetFH-class.
|
||||
@param address IP Adresse des RevPis
|
||||
@param timeout Verbindungstimeout in Millisekunden"""
|
||||
@param address IP Adresse des RevPi
|
||||
@param timeout Timeout in Millisekunden der Verbindung"""
|
||||
super().__init__()
|
||||
self.daemon = True
|
||||
|
||||
@@ -94,7 +98,7 @@ class NetFH(Thread):
|
||||
so = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
so.connect(self._address)
|
||||
except:
|
||||
except Exception:
|
||||
so.close()
|
||||
else:
|
||||
# Alten Socket trennen
|
||||
@@ -156,7 +160,7 @@ class NetFH(Thread):
|
||||
self._slavesock.send(_sysexit)
|
||||
else:
|
||||
self._slavesock.shutdown(socket.SHUT_RDWR)
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
self._slavesock.close()
|
||||
|
||||
@@ -193,7 +197,7 @@ class NetFH(Thread):
|
||||
def get_name(self):
|
||||
"""Verbindugnsnamen zurueckgeben.
|
||||
@return <class 'str'> IP:PORT"""
|
||||
return "{}:{}".format(*self._address)
|
||||
return "{0}:{1}".format(*self._address)
|
||||
|
||||
def get_timeout(self):
|
||||
"""Gibt aktuellen Timeout zurueck.
|
||||
@@ -391,6 +395,8 @@ class RevPiNetIO(_RevPiModIO):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = "_address"
|
||||
|
||||
def __init__(
|
||||
self, address, autorefresh=False, monitoring=False,
|
||||
syncoutputs=True, simulator=False):
|
||||
@@ -445,7 +451,7 @@ class RevPiNetIO(_RevPiModIO):
|
||||
autorefresh,
|
||||
monitoring,
|
||||
syncoutputs,
|
||||
"{}:{}".format(*self._address),
|
||||
"{0}:{1}".format(*self._address),
|
||||
None,
|
||||
simulator
|
||||
)
|
||||
@@ -488,7 +494,7 @@ class RevPiNetIO(_RevPiModIO):
|
||||
if device is None:
|
||||
self._myfh.clear_dirtybytes()
|
||||
else:
|
||||
dev = device if issubclass(type(device), Device) \
|
||||
dev = device if isinstance(device, Device) \
|
||||
else self.device.__getitem__(device)
|
||||
mylist = [dev]
|
||||
|
||||
@@ -508,7 +514,7 @@ class RevPiNetIO(_RevPiModIO):
|
||||
if device is None:
|
||||
mylist = self.device
|
||||
else:
|
||||
dev = device if issubclass(type(device), Device) \
|
||||
dev = device if isinstance(device, Device) \
|
||||
else self.device.__getitem__(device)
|
||||
mylist = [dev]
|
||||
|
||||
@@ -554,6 +560,8 @@ class RevPiNetIOSelected(RevPiNetIO):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(
|
||||
self, address, deviceselection, autorefresh=False,
|
||||
monitoring=False, syncoutputs=True, simulator=False):
|
||||
@@ -619,6 +627,8 @@ class RevPiNetIODriver(RevPiNetIOSelected):
|
||||
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(
|
||||
self, address, virtdev, autorefresh=False, monitoring=False,
|
||||
syncoutputs=True):
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# python3-RevPiModIO
|
||||
#
|
||||
# Webpage: https://revpimodio.org/
|
||||
# (c) Sven Sager, License: LGPLv3
|
||||
#
|
||||
"""Bildet die Summary-Sektion von piCtory ab."""
|
||||
__author__ = "Sven Sager"
|
||||
__copyright__ = "Copyright (C) 2018 Sven Sager"
|
||||
__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"""
|
||||
|
||||
Reference in New Issue
Block a user