delay Parameter für reg_event eingebaut

reg_event schneller und Prüfung auf doppelte Events verbessert
cycletime kann nicht mehr verändert werden, wenn ein Loop läuft
io.IntIO.get_int und .set_int in get_intvalue und set_intvalue geändert
docstring
This commit is contained in:
2017-08-29 18:22:14 +02:00
parent dcc8c22428
commit 5c7a540d29
11 changed files with 220 additions and 124 deletions

View File

@@ -35,3 +35,28 @@ FALLING = 32
BOTH = 33
warnings.simplefilter(action="always")
def consttostr(value):
"""Gibt <class 'str'> fuer Konstanten zurueck.
Diese Funktion ist erforderlich, da enum in Python 3.2 nicht existiert.
@param value Konstantenwert
@return <class 'str'> Name der Konstanten
"""
if value == 0:
return "OFF"
elif value == 1:
return "GREEN"
elif value == 2:
return "RED"
elif value == 31:
return "RISING"
elif value == 32:
return "FALLING"
elif value == 33:
return "BOTH"
else:
return ""

View File

@@ -317,7 +317,7 @@ class Device(object):
lst_return += lst_io
return lst_return
def get_memmories(self):
def get_memories(self):
"""Gibt eine Liste aller mems zurueck.
@return <class 'list'> Mems"""
lst_return = []

View File

@@ -8,7 +8,7 @@
"""RevPiModIO Modul fuer die Verwaltung der IOs."""
import struct
from threading import Event
from .__init__ import RISING, FALLING, BOTH
from .__init__ import RISING, FALLING, BOTH, consttostr
class Type(object):
@@ -356,32 +356,44 @@ class IOBase(object):
else:
return bytes(self._parentdevice._ba_devdata[self._slc_address])
def reg_event(self, func, edge=BOTH, as_thread=False):
"""Registriert ein Event bei der Eventueberwachung.
def reg_event(self, func, delay=0, edge=BOTH, as_thread=False):
"""Registriert fuer IO ein Event bei der Eventueberwachung.
Die uebergebene Funktion wird ausgefuehrt, wenn sich der IO Wert
aendert. Mit Angabe von optionalen Parametern kann das
Ausloeseverhalten gesteuert werden.
@param func Funktion die bei Aenderung aufgerufen werden soll
@param delay Verzoegerung in ms zum Ausloesen wenn Wert gleich bleibt
@param edge Ausfuehren bei RISING, FALLING or BOTH Wertaenderung
@param as_thread Bei True, Funktion als EventCallback-Thread ausfuehren
"""
# Prüfen ob Funktion callable ist
if not callable(func):
raise RuntimeError(
"registered function '{}' ist not callable".format(func)
raise AttributeError(
"registered function '{}' is not callable".format(func)
)
if type(delay) != int or delay < 0:
raise AttributeError(
"parameter 'delay' must be greater or equal 0"
)
if edge != BOTH and self._bitaddress < 0:
raise AttributeError(
"parameter 'edge' can be used with bit io objects only"
)
if self not in self._parentdevice._dict_events:
self._parentdevice._dict_events[self] = [(func, edge, as_thread)]
self._parentdevice._dict_events[self] = \
[(func, edge, as_thread, delay)]
else:
# Prüfen ob Funktion schon registriert ist
for regfunc in self._parentdevice._dict_events[self]:
if regfunc[0] != func:
# Nächsten Eintrag testen
continue
if regfunc[0] == func and edge == BOTH:
if edge == BOTH or regfunc[1] == BOTH:
if self._bitaddress < 0:
raise AttributeError(
"io '{}' with function '{}' already in list."
@@ -389,35 +401,42 @@ class IOBase(object):
)
else:
raise AttributeError(
"io '{}' with function '{}' already in list. "
"edge 'BOTH' not allowed anymore".format(
self._name, func
"io '{}' with function '{}' already in list with "
"edge '{}' - edge '{}' not allowed anymore".format(
self._name, func,
consttostr(regfunc[1]), consttostr(edge)
)
)
elif regfunc[0] == func and regfunc[1] == edge:
elif regfunc[1] == edge:
raise AttributeError(
"io '{}' with function '{}' for given edge "
"already in list".format(self._name, func)
"io '{}' with function '{}' for given edge '{}' "
"already in list".format(
self._name, func, consttostr(edge)
)
)
else:
self._parentdevice._dict_events[self].append(
(func, edge, as_thread)
)
break
# Eventfunktion einfügen
self._parentdevice._dict_events[self].append(
(func, edge, as_thread, delay)
)
def replace_io(self, name, frm, **kwargs):
"""Ersetzt bestehenden IO mit Neuem.
Wenn die kwargs fuer byteorder und defaultvalue nicht angegeben werden,
uebernimmt das System die Daten aus dem ersetzten IO.
@param name Name des neuen Inputs
@param frm struct formatierung (1 Zeichen)
@param frm struct Formatierung (1 Zeichen)
@param kwargs Weitere Parameter:
- bmk: Bezeichnung fuer Input
- bmk: interne Bezeichnung fuer IO
- bit: Registriert IO als <class 'bool'> am angegebenen Bit im Byte
- byteorder: Byteorder fuer den Input, Standardwert=little
- defaultvalue: Standardwert fuer Input, Standard ist 0
- byteorder: Byteorder fuer den IO, Standardwert=little
- defaultvalue: Standardwert fuer IO
- event: Funktion fuer Eventhandling registrieren
- delay: Verzoegerung in ms zum Ausloesen wenn Wert gleich bleibt
- edge: Event ausfuehren bei RISING, FALLING or BOTH Wertaenderung
- as_thread: Fuehrt die event-Funktion als RevPiCallback-Thread aus
- edge: event-Ausfuehren bei RISING, FALLING or BOTH Wertaenderung
@see <a target="_blank"
href="https://docs.python.org/3/library/struct.html#format-characters"
>Python3 struct</a>
@@ -449,8 +468,9 @@ class IOBase(object):
if reg_event is not None:
io_new.reg_event(
reg_event,
as_thread=kwargs.get("as_thread", False),
edge=kwargs.get("edge", BOTH)
kwargs.get("delay", 0),
kwargs.get("edge", BOTH),
kwargs.get("as_thread", False)
)
def set_value(self, value):
@@ -548,7 +568,7 @@ class IOBase(object):
HINWEIS: Wenn <class 'ProcimgWriter'> keine neuen Daten liefert, wird
bis in die Ewigkeit gewartet (nicht bei Angabe von "timeout").
Wenn edge mit RISING oder FALLING angegeben wird muss diese Flanke
Wenn edge mit RISING oder FALLING angegeben wird, muss diese Flanke
ausgeloest werden. Sollte der Wert 1 sein beim Eintritt mit Flanke
RISING, wird das Warten erst bei Aenderung von 0 auf 1 beendet.
@@ -561,13 +581,13 @@ class IOBase(object):
Der Timeoutwert bricht beim Erreichen das Warten sofort mit
Wert 2 Rueckgabewert ab. (Das Timeout wird ueber die Zykluszeit
der autorefresh Funktion berechnet, entspricht also nicht exact den
der autorefresh Funktion berechnet, entspricht also nicht exakt den
angegeben Millisekunden! Es wird immer nach oben gerundet!)
@param edge Flanke RISING, FALLING, BOTH bei der mit True beendet wird
@param edge Flanke RISING, FALLING, BOTH die eintreten muss
@param exitevent <class 'thrading.Event'> fuer vorzeitiges Beenden
@param okvalue IO-Wert, bei dem das Warten sofort mit True beendet wird
@param timeout Zeit in ms nach der mit False abgebrochen wird
@param okvalue IO-Wert, bei dem das Warten sofort beendet wird
@param timeout Zeit in ms nach der abgebrochen wird
@return <class 'int'> erfolgreich Werte <= 0
- Erfolgreich gewartet
Wert 0: IO hat den Wert gewechselt
@@ -695,7 +715,7 @@ class IntIO(IOBase):
self._defaultvalue, byteorder=self._byteorder, signed=self._signed
)
def get_int(self):
def get_intvalue(self):
"""Gibt IO-Wert zurueck mit Beachtung byteorder/signed.
@return IO-Wert als <class 'int'>"""
return int.from_bytes(
@@ -704,7 +724,7 @@ class IntIO(IOBase):
signed=self._signed
)
def set_int(self, value):
def set_intvalue(self, value):
"""Setzt IO mit Beachtung byteorder/signed.
@param value <class 'int'> Wert"""
if type(value) == int:
@@ -722,7 +742,7 @@ class IntIO(IOBase):
byteorder = property(IOBase._get_byteorder, _set_byteorder)
defaultvalue = property(get_intdefaultvalue)
signed = property(_get_signed, _set_signed)
value = property(get_int, set_int)
value = property(get_intvalue, set_intvalue)
class StructIO(IOBase):

View File

@@ -281,7 +281,13 @@ class RevPiModIO(object):
def _set_cycletime(self, milliseconds):
"""Setzt Aktualisierungsrate der Prozessabbild-Synchronisierung.
@param milliseconds <class 'int'> in Millisekunden"""
self._imgwriter.refresh = milliseconds
if self._looprunning:
raise RuntimeError(
"can not change cycletime when cycleloop or mainloop are "
"running"
)
else:
self._imgwriter.refresh = milliseconds
def _set_maxioerrors(self, value):
"""Setzt Anzahl der maximal erlaubten Fehler bei Prozessabbildzugriff.
@@ -311,7 +317,7 @@ class RevPiModIO(object):
"""Startet den Cycleloop.
Der aktuelle Programmthread wird hier bis Aufruf von
RevPiDevicelist.exit() "gefangen". Er fuehrt nach jeder Aktualisierung
.exit() "gefangen". Er fuehrt nach jeder Aktualisierung
des Prozessabbilds die uebergebene Funktion "func" aus und arbeitet sie
ab. Waehrend der Ausfuehrung der Funktion wird das Prozessabbild nicht
weiter aktualisiert. Die Inputs behalten bis zum Ende den aktuellen
@@ -518,6 +524,7 @@ class RevPiModIO(object):
dev._filelock.release()
lst_fire = []
dict_delay = {}
while not self._exit.is_set():
# Auf neue Daten warten und nur ausführen wenn set()
@@ -560,15 +567,28 @@ class RevPiModIO(object):
if regfunc[1] == BOTH \
or regfunc[1] == RISING and boolor \
or regfunc[1] == FALLING and not boolor:
if regfunc[3] == 0:
lst_fire.append((
regfunc, io_event._name, io_event.value
))
else:
# Verzögertes Event in dict einfügen
dict_delay[(
regfunc, io_event._name, io_event.value
)] = int(
regfunc[3] / self._imgwriter.refresh
)
else:
for regfunc in dev._dict_events[io_event]:
if regfunc[3] == 0:
lst_fire.append(
(regfunc, io_event._name, io_event.value)
)
else:
for regfunc in dev._dict_events[io_event]:
lst_fire.append(
(regfunc, io_event._name, io_event.value)
)
else:
# Verzögertes Event in dict einfügen
dict_delay[(
regfunc, io_event._name, io_event.value
)] = int(regfunc[3] / self._imgwriter.refresh)
# Nach Verarbeitung aller IOs die Bytes kopieren
dev._filelock.acquire()
@@ -579,20 +599,29 @@ class RevPiModIO(object):
if not freeze:
self._imgwriter.lck_refresh.release()
# Verzögerte Events prüfen
for tup_fire in list(dict_delay.keys()):
if getattr(self.io, tup_fire[1]).value != tup_fire[2]:
del dict_delay[tup_fire]
else:
dict_delay[tup_fire] -= 1
if dict_delay[tup_fire] <= 0:
# Verzögertes Event übernehmen und löschen
lst_fire.append(tup_fire)
del dict_delay[tup_fire]
# Erst nach Datenübernahme alle Events feuern
while len(lst_fire) > 0:
# EventTuple ((func, edge, as_thread, delay), ioname, iovalue)
tup_fire = lst_fire.pop()
event_func = tup_fire[0][0]
passname = tup_fire[1]
passvalue = tup_fire[2]
if tup_fire[0][2]:
th = helpermodule.EventCallback(
event_func, passname, passvalue
tup_fire[0][0], tup_fire[1], tup_fire[2]
)
th.start()
else:
# Direct callen da Prüfung in RevPiDevice.reg_event ist
event_func(passname, passvalue)
# Direct callen da Prüfung in io.IOBase.reg_event ist
tup_fire[0][0](tup_fire[1], tup_fire[2])
# Refreshsperre aufheben wenn freeze
if freeze: