From b90e858fefb038bdc68b0b9eda00b6471bb78ad4 Mon Sep 17 00:00:00 2001 From: Sven Sager Date: Sun, 17 Oct 2021 19:11:04 +0200 Subject: [PATCH 1/3] Make the cycle more precise Measure each cycle of process image synchronisation and do not build a mean value for that. The cycle function is now exactly timed, even if the individual runtimes are different. --- revpimodio2/__init__.py | 2 +- revpimodio2/helper.py | 39 +++++++++++++++------------------------ setup.py | 2 +- 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/revpimodio2/__init__.py b/revpimodio2/__init__.py index 5963bbf..488ff35 100644 --- a/revpimodio2/__init__.py +++ b/revpimodio2/__init__.py @@ -22,7 +22,7 @@ __author__ = "Sven Sager " __copyright__ = "Copyright (C) 2020 Sven Sager" __license__ = "LGPLv3" __name__ = "revpimodio2" -__version__ = "2.5.8" +__version__ = "2.5.8b" # Global package values OFF = 0 diff --git a/revpimodio2/helper.py b/revpimodio2/helper.py index 1a72e1c..4ca4056 100644 --- a/revpimodio2/helper.py +++ b/revpimodio2/helper.py @@ -365,7 +365,7 @@ class ProcimgWriter(Thread): """ __slots__ = "__dict_delay", "__eventth", "_eventqth", "__eventwork", \ - "_adjwait", "_eventq", "_modio", \ + "_eventq", "_modio", \ "_refresh", "_work", "daemon", "lck_refresh", "newdata" def __init__(self, parentmodio): @@ -375,7 +375,6 @@ class ProcimgWriter(Thread): self.__eventth = Thread(target=self.__exec_th) self._eventqth = queue.Queue() self.__eventwork = False - self._adjwait = 0 self._eventq = queue.Queue() self._modio = parentmodio self._refresh = 0.05 @@ -512,17 +511,18 @@ class ProcimgWriter(Thread): def run(self): """Startet die automatische Prozessabbildsynchronisierung.""" fh = self._modio._create_myfh() - self._adjwait = self._refresh mrk_warn = True - mrk_dt = default_timer() - bytesbuff = bytearray(self._modio._length) + + # First sync of cycle time to start the main loop and match exact time + sleep(self._refresh - (default_timer() % self._refresh)) + while not self._work.is_set(): - ot = mrk_dt + ot = default_timer() # Lockobjekt holen und Fehler werfen, wenn nicht schnell genug - if not self.lck_refresh.acquire(timeout=self._adjwait): + if not self.lck_refresh.acquire(timeout=self._refresh): warnings.warn( "cycle time of {0} ms exceeded during executing function" "".format(int(self._refresh * 1000)), @@ -609,22 +609,15 @@ class ProcimgWriter(Thread): self._eventq.put(tup_fire, False) del self.__dict_delay[tup_fire] - # Sleep and not .wait (.wait uses system clock) - sleep(self._adjwait) - - # Wartezeit anpassen um echte self._refresh zu erreichen - mrk_dt = default_timer() - if mrk_dt - ot >= self._refresh: - self._adjwait -= 0.001 - if self._adjwait < 0: - warnings.warn( - "cycle time of {0} ms exceeded several times - can not" - " hold cycle time!".format(int(self._refresh * 1000)), - RuntimeWarning - ) - self._adjwait = 0 + if default_timer() - ot > self._refresh: + warnings.warn( + "cycle time of {0} ms exceeded - can not hold cycle time!" + "".format(int(self._refresh * 1000)), + RuntimeWarning + ) else: - self._adjwait += 0.001 + # Sleep and not .wait (.wait uses system clock) + sleep(self._refresh - (default_timer() % self._refresh)) # Alle am Ende erneut aufwecken self._collect_events(False) @@ -639,9 +632,7 @@ class ProcimgWriter(Thread): """Setzt die Zykluszeit in Millisekunden. @param value Millisekunden""" if type(value) == int and 5 <= value <= 2000: - waitdiff = self._refresh - self._adjwait self._refresh = value / 1000 - self._adjwait = 0 if waitdiff < 0 else self._refresh - waitdiff else: raise ValueError( "refresh time must be 5 to 2000 milliseconds" diff --git a/setup.py b/setup.py index 618a5d3..5e9f6ff 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ setup( license="LGPLv3", name="revpimodio2", - version="2.5.8", + version="2.5.8b", packages=["revpimodio2"], python_requires="~=3.2", From bc8a13da1badb5329d81328da107b55dbc3eb9bf Mon Sep 17 00:00:00 2001 From: Sven Sager Date: Sun, 24 Oct 2021 10:50:42 +0200 Subject: [PATCH 2/3] Warn the user faster, if the cycle time is exhausted --- revpimodio2/__init__.py | 2 +- revpimodio2/helper.py | 13 +++++++++---- setup.py | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/revpimodio2/__init__.py b/revpimodio2/__init__.py index 488ff35..6773474 100644 --- a/revpimodio2/__init__.py +++ b/revpimodio2/__init__.py @@ -22,7 +22,7 @@ __author__ = "Sven Sager " __copyright__ = "Copyright (C) 2020 Sven Sager" __license__ = "LGPLv3" __name__ = "revpimodio2" -__version__ = "2.5.8b" +__version__ = "2.5.8c" # Global package values OFF = 0 diff --git a/revpimodio2/helper.py b/revpimodio2/helper.py index 4ca4056..880e51e 100644 --- a/revpimodio2/helper.py +++ b/revpimodio2/helper.py @@ -512,6 +512,7 @@ class ProcimgWriter(Thread): """Startet die automatische Prozessabbildsynchronisierung.""" fh = self._modio._create_myfh() + mrk_delay = self._refresh mrk_warn = True bytesbuff = bytearray(self._modio._length) @@ -521,13 +522,14 @@ class ProcimgWriter(Thread): while not self._work.is_set(): ot = default_timer() - # Lockobjekt holen und Fehler werfen, wenn nicht schnell genug - if not self.lck_refresh.acquire(timeout=self._refresh): + # At this point, we slept and have the rest of delay from last cycle + if not self.lck_refresh.acquire(timeout=mrk_delay): warnings.warn( - "cycle time of {0} ms exceeded during executing function" + "cycle time of {0} ms exceeded in your cycle function" "".format(int(self._refresh * 1000)), RuntimeWarning ) + mrk_delay = self._refresh # Nur durch cycleloop erreichbar - keine verzögerten Events continue @@ -609,15 +611,18 @@ class ProcimgWriter(Thread): self._eventq.put(tup_fire, False) del self.__dict_delay[tup_fire] + mrk_delay = default_timer() % self._refresh + # Second default_timer call include calculation time from above if default_timer() - ot > self._refresh: warnings.warn( "cycle time of {0} ms exceeded - can not hold cycle time!" "".format(int(self._refresh * 1000)), RuntimeWarning ) + mrk_delay = 0.0 else: # Sleep and not .wait (.wait uses system clock) - sleep(self._refresh - (default_timer() % self._refresh)) + sleep(self._refresh - mrk_delay) # Alle am Ende erneut aufwecken self._collect_events(False) diff --git a/setup.py b/setup.py index 5e9f6ff..c8c704c 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ setup( license="LGPLv3", name="revpimodio2", - version="2.5.8b", + version="2.5.8c", packages=["revpimodio2"], python_requires="~=3.2", From 36ff69aaa650f2503353cf1fbcf708d801dcac61 Mon Sep 17 00:00:00 2001 From: Sven Sager Date: Sun, 24 Oct 2021 14:28:33 +0200 Subject: [PATCH 3/3] Sync the first cycle with imgwriter after calling `.cycleloop` --- revpimodio2/helper.py | 3 --- revpimodio2/modio.py | 5 +++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/revpimodio2/helper.py b/revpimodio2/helper.py index 880e51e..a202382 100644 --- a/revpimodio2/helper.py +++ b/revpimodio2/helper.py @@ -516,9 +516,6 @@ class ProcimgWriter(Thread): mrk_warn = True bytesbuff = bytearray(self._modio._length) - # First sync of cycle time to start the main loop and match exact time - sleep(self._refresh - (default_timer() % self._refresh)) - while not self._work.is_set(): ot = default_timer() diff --git a/revpimodio2/modio.py b/revpimodio2/modio.py index b81e3be..ea673ca 100644 --- a/revpimodio2/modio.py +++ b/revpimodio2/modio.py @@ -767,10 +767,10 @@ class RevPiModIO(object): # Zykluszeit übernehmen old_cycletime = self._imgwriter.refresh if not cycletime == self._imgwriter.refresh: + # Set new cycle time and wait one imgwriter cycle to sync fist cycle self._imgwriter.refresh = cycletime - - # Zeitänderung in _imgwriter neuladen self._imgwriter.newdata.clear() + self._imgwriter.newdata.wait(self._imgwriter._refresh) # Benutzerevent self.exitsignal.clear() @@ -781,6 +781,7 @@ class RevPiModIO(object): cycleinfo = helpermodule.Cycletools(self._imgwriter.refresh, self) e = None # Exception ec = None # Return value of cycle_function + self._imgwriter.newdata.clear() try: while ec is None and not cycleinfo.last: # Auf neue Daten warten und nur ausführen wenn set()