Mit mainloop zusammenführen

This commit is contained in:
2017-12-02 17:24:07 +01:00
7 changed files with 371 additions and 278 deletions

View File

@@ -440,6 +440,15 @@ Methods</h3>
<td><a style="color:#0000FF" href="#ProcimgWriter.__init__">ProcimgWriter</a></td>
<td>Init ProcimgWriter class.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#ProcimgWriter.__check_change">__check_change</a></td>
<td>Findet Aenderungen fuer die Eventueberwachung.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#ProcimgWriter.__exec_th">__exec_th</a></td>
<td>Fuehrt Events aus, die als Thread registriert wurden.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#ProcimgWriter._collect_events">_collect_events</a></td>
<td>Aktiviert oder Deaktiviert die Eventueberwachung.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#ProcimgWriter._get_ioerrors">_get_ioerrors</a></td>
<td>Ruft aktuelle Anzahl der Fehler ab.</td>
</tr><tr>
@@ -481,6 +490,29 @@ Init ProcimgWriter class.
<dd>
Parent Object
</dd>
</dl><a NAME="ProcimgWriter.__check_change" ID="ProcimgWriter.__check_change"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
ProcimgWriter.__check_change</h3>
<b>__check_change</b>(<i>dev</i>)
<p>
Findet Aenderungen fuer die Eventueberwachung.
</p><a NAME="ProcimgWriter.__exec_th" ID="ProcimgWriter.__exec_th"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
ProcimgWriter.__exec_th</h3>
<b>__exec_th</b>(<i></i>)
<p>
Fuehrt Events aus, die als Thread registriert wurden.
</p><a NAME="ProcimgWriter._collect_events" ID="ProcimgWriter._collect_events"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
ProcimgWriter._collect_events</h3>
<b>_collect_events</b>(<i>value</i>)
<p>
Aktiviert oder Deaktiviert die Eventueberwachung.
</p><dl>
<dt><i>value</i></dt>
<dd>
True aktiviert / False deaktiviert
</dd>
</dl><a NAME="ProcimgWriter._get_ioerrors" ID="ProcimgWriter._get_ioerrors"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
ProcimgWriter._get_ioerrors</h3>

View File

@@ -24,6 +24,9 @@ Classes</h3>
<td><a style="color:#0000FF" href="#IOBase">IOBase</a></td>
<td>Basisklasse fuer alle IO-Objekte.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#IOEvent">IOEvent</a></td>
<td>Basisklasse fuer IO-Events.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#IOList">IOList</a></td>
<td>Basisklasse fuer direkten Zugriff auf IO Objekte.</td>
</tr><tr>
@@ -141,6 +144,9 @@ Methods</h3>
<td><a style="color:#0000FF" href="#IOBase.__len__">__len__</a></td>
<td>Gibt die Bytelaenge des IO zurueck.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#IOBase.__reg_xevent">__reg_xevent</a></td>
<td>Verwaltet reg_event und reg_timerevent.</td>
</tr><tr>
<td><a style="color:#0000FF" href="#IOBase.__str__">__str__</a></td>
<td><class 'str'>-Wert der Klasse.</td>
</tr><tr>
@@ -228,6 +234,29 @@ Gibt die Bytelaenge des IO zurueck.
<dd>
Bytelaenge des IO - 0 bei BITs
</dd>
</dl><a NAME="IOBase.__reg_xevent" ID="IOBase.__reg_xevent"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
IOBase.__reg_xevent</h3>
<b>__reg_xevent</b>(<i>func, delay, edge, as_thread, overwrite</i>)
<p>
Verwaltet reg_event und reg_timerevent.
</p><dl>
<dt><i>func</i></dt>
<dd>
Funktion die bei Aenderung aufgerufen werden soll
</dd><dt><i>delay</i></dt>
<dd>
Verzoegerung in ms zum Ausloesen - auch bei Wertaenderung
</dd><dt><i>edge</i></dt>
<dd>
Ausfuehren bei RISING, FALLING or BOTH Wertaenderung
</dd><dt><i>as_thread</i></dt>
<dd>
Bei True, Funktion als EventCallback-Thread ausfuehren
</dd><dt><i>overwrite</i></dt>
<dd>
Wenn True, wird Event bei ueberschrieben
</dd>
</dl><a NAME="IOBase.__str__" ID="IOBase.__str__"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
IOBase.__str__</h3>
@@ -468,6 +497,46 @@ Zeit in ms nach der abgebrochen wird
</dl>
<div align="right"><a style="color:#0000FF" href="#top">Up</a></div>
<hr /><hr />
<a NAME="IOEvent" ID="IOEvent"></a>
<h2 style="background-color:#FFFFFF;color:#0000FF">IOEvent</h2>
<p>
Basisklasse fuer IO-Events.
</p>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Derived from</h3>
object
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Attributes</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Class Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Methods</h3>
<table>
<tr>
<td><a style="color:#0000FF" href="#IOEvent.__init__">IOEvent</a></td>
<td>Init IOEvent class.</td>
</tr>
</table>
<h3 style="background-color:#FFFFFF;color:#FF0000">
Static Methods</h3>
<table>
<tr><td>None</td></tr>
</table>
<a NAME="IOEvent.__init__" ID="IOEvent.__init__"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
IOEvent (Constructor)</h3>
<b>IOEvent</b>(<i>func, edge, as_thread, delay, overwrite</i>)
<p>
Init IOEvent class.
</p>
<div align="right"><a style="color:#0000FF" href="#top">Up</a></div>
<hr /><hr />
<a NAME="IOList" ID="IOList"></a>
<h2 style="background-color:#FFFFFF;color:#0000FF">IOList</h2>
<p>

View File

@@ -440,7 +440,7 @@ Funktion wird nach dem letzten Lesen der Inputs
</dl><a NAME="RevPiModIO.mainloop" ID="RevPiModIO.mainloop"></a>
<h3 style="background-color:#FFFFFF;color:#FF0000">
RevPiModIO.mainloop</h3>
<b>mainloop</b>(<i>freeze=False, blocking=True</i>)
<b>mainloop</b>(<i>blocking=True</i>)
<p>
Startet den Mainloop mit Eventueberwachung.
</p><p>
@@ -449,12 +449,6 @@ Startet den Mainloop mit Eventueberwachung.
durchlaeuft die Eventueberwachung und prueft Aenderungen der, mit
einem Event registrierten, IOs. Wird eine Veraenderung erkannt,
fuert das Programm die dazugehoerigen Funktionen der Reihe nach aus.
</p><p>
Wenn der Parameter "freeze" mit True angegeben ist, wird die
Prozessabbildsynchronisierung angehalten bis alle Eventfunktionen
ausgefuehrt wurden. Inputs behalten fuer die gesamte Dauer ihren
aktuellen Wert und Outputs werden erst nach Durchlauf aller Funktionen
in das Prozessabbild geschrieben.
</p><p>
Wenn der Parameter "blocking" mit False angegeben wird, aktiviert
dies die Eventueberwachung und blockiert das Programm NICHT an der
@@ -462,10 +456,7 @@ Startet den Mainloop mit Eventueberwachung.
Events vom RevPi benoetigt werden, aber das Programm weiter ausgefuehrt
werden soll.
</p><dl>
<dt><i>freeze</i></dt>
<dd>
Wenn True, Prozessabbildsynchronisierung anhalten
</dd><dt><i>blocking</i></dt>
<dt><i>blocking</i></dt>
<dd>
Wenn False, blockiert das Programm NICHT
</dd>

View File

@@ -70,6 +70,7 @@ revpimodio2.helper.Cycletools?1(cycletime)
revpimodio2.helper.EventCallback.run?4()
revpimodio2.helper.EventCallback.stop?4()
revpimodio2.helper.EventCallback?1(func, name, value)
revpimodio2.helper.ProcimgWriter._collect_events?5(value)
revpimodio2.helper.ProcimgWriter._get_ioerrors?5()
revpimodio2.helper.ProcimgWriter._gotioerror?5()
revpimodio2.helper.ProcimgWriter.get_maxioerrors?4()
@@ -104,6 +105,7 @@ revpimodio2.io.IOBase.unreg_event?4(func=None, edge=None)
revpimodio2.io.IOBase.value?7
revpimodio2.io.IOBase.wait?4(edge=BOTH, exitevent=None, okvalue=None, timeout=0)
revpimodio2.io.IOBase?1(parentdevice, valuelist, iotype, byteorder, signed)
revpimodio2.io.IOEvent?1(func, edge, as_thread, delay, overwrite)
revpimodio2.io.IOList._private_register_new_io_object?5(new_io)
revpimodio2.io.IOList?1()
revpimodio2.io.IntIO._get_signed?5()
@@ -149,7 +151,7 @@ revpimodio2.modio.RevPiModIO.get_jconfigrsc?4()
revpimodio2.modio.RevPiModIO.handlesignalend?4(cleanupfunc=None)
revpimodio2.modio.RevPiModIO.ioerrors?7
revpimodio2.modio.RevPiModIO.length?7
revpimodio2.modio.RevPiModIO.mainloop?4(freeze=False, blocking=True)
revpimodio2.modio.RevPiModIO.mainloop?4(blocking=True)
revpimodio2.modio.RevPiModIO.maxioerrors?7
revpimodio2.modio.RevPiModIO.monitoring?7
revpimodio2.modio.RevPiModIO.procimg?7

View File

@@ -6,10 +6,12 @@
# (c) Sven Sager, License: LGPLv3
#
"""RevPiModIO Helperklassen und Tools."""
import queue
import warnings
from math import ceil
from threading import Event, Lock, Thread
from timeit import default_timer
from revpimodio2 import RISING, FALLING, BOTH
class EventCallback(Thread):
@@ -281,7 +283,12 @@ class ProcimgWriter(Thread):
"""Init ProcimgWriter class.
@param parentmodio Parent Object"""
super().__init__()
self.__dict_delay = {}
self.__eventth = Thread(target=self.__exec_th)
self.__eventqth = queue.Queue()
self.__eventwork = False
self._adjwait = 0
self._eventq = queue.Queue()
self._ioerror = 0
self._maxioerrors = 0
self._modio = parentmodio
@@ -292,6 +299,109 @@ class ProcimgWriter(Thread):
self.lck_refresh = Lock()
self.newdata = Event()
def __check_change(self, dev):
"""Findet Aenderungen fuer die Eventueberwachung."""
for io_event in dev._dict_events:
if dev._ba_datacp[io_event._slc_address] == \
dev._ba_devdata[io_event._slc_address]:
continue
if io_event._bitaddress >= 0:
boolcp = bool(int.from_bytes(
dev._ba_datacp[io_event._slc_address],
byteorder=io_event._byteorder
) & 1 << io_event._bitaddress)
boolor = bool(int.from_bytes(
dev._ba_devdata[io_event._slc_address],
byteorder=io_event._byteorder
) & 1 << io_event._bitaddress)
if boolor == boolcp:
continue
for regfunc in dev._dict_events[io_event]:
if regfunc.edge == BOTH \
or regfunc.edge == RISING and boolor \
or regfunc.edge == FALLING and not boolor:
if regfunc.delay == 0:
if regfunc.as_thread:
self.__eventqth.put(
(regfunc, io_event._name, io_event.value),
False
)
else:
self._eventq.put(
(regfunc, io_event._name, io_event.value),
False
)
else:
# Verzögertes Event in dict einfügen
tupfire = (
regfunc, io_event._name, io_event.value
)
if regfunc.overwrite \
or tupfire not in self.__dict_delay:
self.__dict_delay[tupfire] = ceil(
regfunc.delay / 1000 / self._refresh
)
else:
for regfunc in dev._dict_events[io_event]:
if regfunc.delay == 0:
if regfunc.as_thread:
self.__eventqth.put(
(regfunc, io_event._name, io_event.value),
False
)
else:
self._eventq.put(
(regfunc, io_event._name, io_event.value),
False
)
else:
# Verzögertes Event in dict einfügen
tupfire = (
regfunc, io_event._name, io_event.value
)
if regfunc.overwrite \
or tupfire not in self.__dict_delay:
self.__dict_delay[tupfire] = ceil(
regfunc.delay / 1000 / self._refresh
)
# Nach Verarbeitung aller IOs die Bytes kopieren (Lock ist noch drauf)
dev._ba_datacp = dev._ba_devdata[:]
def __exec_th(self):
"""Fuehrt Events aus, die als Thread registriert wurden."""
while self.__eventwork:
try:
tup_fireth = self.__eventqth.get(timeout=1)
th = EventCallback(
tup_fireth[0].func, tup_fireth[1], tup_fireth[2]
)
th.start()
except queue.Empty:
pass
def _collect_events(self, value):
"""Aktiviert oder Deaktiviert die Eventueberwachung.
@param value True aktiviert / False deaktiviert"""
if type(value) != bool:
raise ValueError("value must be <class 'bool'>")
if self.__eventwork != value:
with self.lck_refresh:
self.__eventwork = value
self.__eventqth = queue.Queue()
self._eventq = queue.Queue()
self.__dict_delay = {}
# Threadmanagement
if value and not self.__eventth.is_alive():
self.__eventth = Thread(target=self.__exec_th)
self.__eventth.start()
def _get_ioerrors(self):
"""Ruft aktuelle Anzahl der Fehler ab.
@return Aktuelle Fehleranzahl"""
@@ -325,6 +435,7 @@ class ProcimgWriter(Thread):
"""Startet die automatische Prozessabbildsynchronisierung."""
fh = self._modio._create_myfh()
self._adjwait = self._refresh
while not self._work.is_set():
ot = default_timer()
@@ -336,54 +447,70 @@ class ProcimgWriter(Thread):
),
RuntimeWarning
)
# Verzögerte Events pausieren an dieser Stelle
continue
try:
fh.seek(0)
bytesbuff = bytearray(fh.read(self._modio._length))
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()
else:
# Inputs in Puffer, Outputs in Prozessabbild
for dev in self._modio._lst_refresh:
with dev._filelock:
dev._ba_devdata[dev._slc_inp] = \
bytesbuff[dev._slc_inpoff]
if self.__eventwork\
and len(dev._dict_events) > 0 \
and dev._ba_datacp != dev._ba_devdata:
self.__check_change(dev)
fh.seek(dev._slc_outoff.start)
fh.write(dev._ba_devdata[dev._slc_out])
if self._modio._buffedwrite:
fh.flush()
except IOError:
self._gotioerror()
self.lck_refresh.release()
self._work.wait(self._adjwait)
continue
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]
dev._filelock.release()
else:
# Inputs in Puffer, Outputs in Prozessabbild
ioerr = False
for dev in self._modio._lst_refresh:
dev._filelock.acquire()
dev._ba_devdata[dev._slc_inp] = bytesbuff[dev._slc_inpoff]
try:
fh.seek(dev._slc_outoff.start)
fh.write(dev._ba_devdata[dev._slc_out])
except IOError:
ioerr = True
finally:
dev._filelock.release()
# Alle aufwecken
self.lck_refresh.release()
self.newdata.set()
if self._modio._buffedwrite:
try:
fh.flush()
except IOError:
ioerr = True
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 != \
tup_fire[2]:
del self.__dict_delay[tup_fire]
else:
self.__dict_delay[tup_fire] -= 1
if self.__dict_delay[tup_fire] <= 0:
# Verzögertes Event übernehmen und löschen
if tup_fire[0].as_thread:
self.__eventqth.put(tup_fire, False)
else:
self._eventq.put(tup_fire, False)
del self.__dict_delay[tup_fire]
if ioerr:
self._gotioerror()
self.lck_refresh.release()
self._work.wait(self._adjwait)
continue
self.lck_refresh.release()
# Alle aufwecken
self.newdata.set()
self._work.wait(self._adjwait)
# Refresh abwarten
self._work.wait(self._adjwait)
# Wartezeit anpassen um echte self._refresh zu erreichen
if default_timer() - ot >= self._refresh:
@@ -400,11 +527,13 @@ class ProcimgWriter(Thread):
self._adjwait += 0.001
# Alle am Ende erneut aufwecken
self._collect_events(False)
self.newdata.set()
fh.close()
def stop(self):
"""Beendet die automatische Prozessabbildsynchronisierung."""
self._collect_events(False)
self._work.set()
def set_maxioerrors(self, value):

View File

@@ -11,6 +11,19 @@ from threading import Event
from revpimodio2 import RISING, FALLING, BOTH, INP, OUT, MEM, consttostr
class IOEvent(object):
"""Basisklasse fuer IO-Events."""
def __init__(self, func, edge, as_thread, delay, overwrite):
"""Init IOEvent class."""
self.as_thread = as_thread
self.delay = delay
self.edge = edge
self.func = func
self.overwrite = overwrite
class IOList(object):
"""Basisklasse fuer direkten Zugriff auf IO Objekte."""
@@ -317,6 +330,67 @@ class IOBase(object):
@return Namen des IOs"""
return self._name
def __reg_xevent(self, func, delay, edge, as_thread, overwrite):
"""Verwaltet reg_event und reg_timerevent.
@param func Funktion die bei Aenderung aufgerufen werden soll
@param delay Verzoegerung in ms zum Ausloesen - auch bei Wertaenderung
@param edge Ausfuehren bei RISING, FALLING or BOTH Wertaenderung
@param as_thread Bei True, Funktion als EventCallback-Thread ausfuehren
@param overwrite Wenn True, wird Event bei ueberschrieben
"""
# Prüfen ob Funktion callable ist
if not callable(func):
raise AttributeError(
"registered function '{}' is not callable".format(func)
)
if type(delay) != int or delay < 0:
raise AttributeError(
"'delay' must be <class 'int'> and 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] = \
[IOEvent(func, edge, as_thread, delay, overwrite)]
else:
# Prüfen ob Funktion schon registriert ist
for regfunc in self._parentdevice._dict_events[self]:
if regfunc.func != func:
# Nächsten Eintrag testen
continue
if edge == BOTH or regfunc.edge == BOTH:
if self._bitaddress < 0:
raise AttributeError(
"io '{}' with function '{}' already in list."
"".format(self._name, func)
)
else:
raise AttributeError(
"io '{}' with function '{}' already in list with "
"edge '{}' - edge '{}' not allowed anymore".format(
self._name, func,
consttostr(regfunc.edge), consttostr(edge)
)
)
elif regfunc.edge == edge:
raise AttributeError(
"io '{}' with function '{}' for given edge '{}' "
"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)
)
def _get_address(self):
"""Gibt die absolute Byteadresse im Prozessabbild zurueck.
@return Absolute Byteadresse"""
@@ -365,56 +439,7 @@ class IOBase(object):
@param as_thread Bei True, Funktion als EventCallback-Thread ausfuehren
"""
# Prüfen ob Funktion callable ist
if not callable(func):
raise AttributeError(
"registered function '{}' is not callable".format(func)
)
if type(delay) != int or delay < 0:
raise AttributeError(
"'delay' must be <class 'int'> and 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, delay, True)]
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 edge == BOTH or regfunc[1] == BOTH:
if self._bitaddress < 0:
raise AttributeError(
"io '{}' with function '{}' already in list."
"".format(self._name, func)
)
else:
raise AttributeError(
"io '{}' with function '{}' already in list with "
"edge '{}' - edge '{}' not allowed anymore".format(
self._name, func,
consttostr(regfunc[1]), consttostr(edge)
)
)
elif regfunc[1] == edge:
raise AttributeError(
"io '{}' with function '{}' for given edge '{}' "
"already in list".format(
self._name, func, consttostr(edge)
)
)
# Eventfunktion einfügen
self._parentdevice._dict_events[self].append(
(func, edge, as_thread, delay, True)
)
self.__reg_xevent(func, delay, edge, as_thread, True)
def reg_timerevent(self, func, delay, edge=BOTH, as_thread=False):
"""Registriert fuer IO einen Timer, welcher nach delay func ausfuehrt.
@@ -435,56 +460,7 @@ class IOBase(object):
@param as_thread Bei True, Funktion als EventCallback-Thread ausfuehren
"""
# Prüfen ob Funktion callable ist
if not callable(func):
raise AttributeError(
"registered function '{}' is not callable".format(func)
)
if type(delay) != int or delay < 0:
raise AttributeError(
"'delay' must be <class 'int'> and 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, delay, False)]
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 edge == BOTH or regfunc[1] == BOTH:
if self._bitaddress < 0:
raise AttributeError(
"io '{}' with function '{}' already in list."
"".format(self._name, func)
)
else:
raise AttributeError(
"io '{}' with function '{}' already in list with "
"edge '{}' - edge '{}' not allowed anymore".format(
self._name, func,
consttostr(regfunc[1]), consttostr(edge)
)
)
elif regfunc[1] == edge:
raise AttributeError(
"io '{}' with function '{}' for given edge '{}' "
"already in list".format(
self._name, func, consttostr(edge)
)
)
# Eventfunktion einfügen
self._parentdevice._dict_events[self].append(
(func, edge, as_thread, delay, False)
)
self.__reg_xevent(func, delay, edge, as_thread, False)
def replace_io(self, name, frm, **kwargs):
"""Ersetzt bestehenden IO mit Neuem.
@@ -613,8 +589,8 @@ class IOBase(object):
else:
newlist = []
for regfunc in self._parentdevice._dict_events[self]:
if regfunc[0] != func or edge is not None \
and regfunc[1] != edge:
if regfunc.func != func or edge is not None \
and regfunc.edge != edge:
newlist.append(regfunc)

View File

@@ -8,8 +8,8 @@
"""RevPiModIO Hauptklasse fuer piControl0 Zugriff."""
import warnings
from json import load as jload
from math import ceil
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
@@ -18,7 +18,6 @@ from . import device as devicemodule
from . import helper as helpermodule
from . import summary as summarymodule
from .io import IOList
from revpimodio2 import RISING, FALLING, BOTH
class RevPiModIO(object):
@@ -419,12 +418,13 @@ class RevPiModIO(object):
if self._imgwriter is not None and self._imgwriter.is_alive():
self._imgwriter.stop()
self._imgwriter.join(self._imgwriter._refresh)
if self._th_mainloop is not None and self._th_mainloop.is_alive():
self._th_mainloop.join(1)
while len(self._lst_refresh) > 0:
dev = self._lst_refresh.pop()
dev._selfupdate = False
if not self._monitoring:
self.writeprocimg(dev)
self._looprunning = False
def get_jconfigrsc(self):
"""Laed die piCotry Konfiguration und erstellt ein <class 'dict'>.
@@ -483,7 +483,7 @@ class RevPiModIO(object):
signal(SIGINT, self.__evt_exit)
signal(SIGTERM, self.__evt_exit)
def mainloop(self, freeze=False, blocking=True):
def mainloop(self, blocking=True):
"""Startet den Mainloop mit Eventueberwachung.
Der aktuelle Programmthread wird hier bis Aufruf von
@@ -492,19 +492,12 @@ class RevPiModIO(object):
einem Event registrierten, IOs. Wird eine Veraenderung erkannt,
fuert das Programm die dazugehoerigen Funktionen der Reihe nach aus.
Wenn der Parameter "freeze" mit True angegeben ist, wird die
Prozessabbildsynchronisierung angehalten bis alle Eventfunktionen
ausgefuehrt wurden. Inputs behalten fuer die gesamte Dauer ihren
aktuellen Wert und Outputs werden erst nach Durchlauf aller Funktionen
in das Prozessabbild geschrieben.
Wenn der Parameter "blocking" mit False angegeben wird, aktiviert
dies die Eventueberwachung und blockiert das Programm NICHT an der
Stelle des Aufrufs. Eignet sich gut fuer die GUI Programmierung, wenn
Events vom RevPi benoetigt werden, aber das Programm weiter ausgefuehrt
werden soll.
@param freeze Wenn True, Prozessabbildsynchronisierung anhalten
@param blocking Wenn False, blockiert das Programm NICHT
@return None
@@ -522,8 +515,7 @@ class RevPiModIO(object):
# Thread erstellen, wenn nicht blockieren soll
if not blocking:
self._th_mainloop = Thread(
target=self.mainloop,
kwargs={"freeze": freeze, "blocking": True}
target=self.mainloop, kwargs={"blocking": True}
)
self._th_mainloop.start()
return
@@ -538,129 +530,31 @@ class RevPiModIO(object):
dev._ba_datacp = dev._ba_devdata[:]
dev._filelock.release()
lst_fire = []
dict_delay = {}
# ImgWriter mit Eventüberwachung aktivieren
self._imgwriter._collect_events(True)
e = None
while not self._exit.is_set():
# Auf neue Daten warten und nur ausführen wenn set()
if not self._imgwriter.newdata.wait(2.5):
try:
tup_fire = self._imgwriter._eventq.get(timeout=1)
# Direct callen da Prüfung in io.IOBase.reg_event ist
tup_fire[0].func(tup_fire[1], tup_fire[2])
except Empty:
if not self._exit.is_set() and not self._imgwriter.is_alive():
self.exit(full=False)
self._looprunning = False
raise RuntimeError("autorefresh thread not running")
continue
self._imgwriter.newdata.clear()
# Während Auswertung refresh sperren
self._imgwriter.lck_refresh.acquire()
for dev in self._lst_refresh:
if len(dev._dict_events) == 0 \
or dev._ba_datacp == dev._ba_devdata:
continue
for io_event in dev._dict_events:
if dev._ba_datacp[io_event._slc_address] == \
dev._ba_devdata[io_event._slc_address]:
continue
if io_event._bitaddress >= 0:
boolcp = bool(int.from_bytes(
dev._ba_datacp[io_event._slc_address],
byteorder=io_event._byteorder
) & 1 << io_event._bitaddress)
boolor = bool(int.from_bytes(
dev._ba_devdata[io_event._slc_address],
byteorder=io_event._byteorder
) & 1 << io_event._bitaddress)
if boolor == boolcp:
continue
for regfunc in dev._dict_events[io_event]:
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
tupfire = (
regfunc, io_event._name, io_event.value
)
if regfunc[4] or tupfire not in dict_delay:
dict_delay[tupfire] = ceil(
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:
# Verzögertes Event in dict einfügen
if regfunc[4] or regfunc not in dict_delay:
dict_delay[(
regfunc, io_event._name, io_event.value
)] = ceil(
regfunc[3] / self._imgwriter.refresh
)
# Nach Verarbeitung aller IOs die Bytes kopieren
dev._filelock.acquire()
dev._ba_datacp = dev._ba_devdata[:]
dev._filelock.release()
# Refreshsperre aufheben wenn nicht freeze
if not freeze:
self._imgwriter.lck_refresh.release()
# EventTuple:
# ((func, edge, as_thread, delay, löschen), ioname, iovalue)
# Verzögerte Events prüfen
for tup_fire in list(dict_delay.keys()):
if tup_fire[0][4] \
and 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
try:
while len(lst_fire) > 0:
tup_fire = lst_fire.pop()
if tup_fire[0][2]:
th = helpermodule.EventCallback(
tup_fire[0][0], tup_fire[1], tup_fire[2]
)
th.start()
else:
# Direct callen da Prüfung in io.IOBase.reg_event ist
tup_fire[0][0](tup_fire[1], tup_fire[2])
except Exception as e:
if self._imgwriter.lck_refresh.locked():
self._imgwriter.lck_refresh.release()
e = RuntimeError("autorefresh thread not running")
except Exception as ex:
self.exit(full=False)
self._looprunning = False
raise e
# Refreshsperre aufheben wenn freeze
if freeze:
self._imgwriter.lck_refresh.release()
e = ex
# Mainloop verlassen
self._imgwriter._collect_events(False)
self._looprunning = False
self._th_mainloop = None
# Fehler prüfen
if e is not None:
raise e
def readprocimg(self, device=None):
"""Einlesen aller Inputs aller/eines Devices vom Prozessabbild.