diff --git a/src/revpimodio2/__about__.py b/src/revpimodio2/__about__.py index 1ac724e..d093900 100644 --- a/src/revpimodio2/__about__.py +++ b/src/revpimodio2/__about__.py @@ -3,4 +3,4 @@ __author__ = "Sven Sager " __copyright__ = "Copyright (C) 2023 Sven Sager" __license__ = "LGPLv2" -__version__ = "2.7.0rc2" +__version__ = "2.7.0rc3" diff --git a/src/revpimodio2/io.py b/src/revpimodio2/io.py index 4dae8bd..847339b 100644 --- a/src/revpimodio2/io.py +++ b/src/revpimodio2/io.py @@ -88,16 +88,42 @@ class IOList(object): io_del._parentdevice._update_my_io_list() def __enter__(self): - if self.__modio._looprunning: - raise RuntimeError("can not start multiple mainloop/cycleloop/with sessions") - self.__modio._looprunning = True + """ + Read inputs on entering context manager and write outputs on leaving. + + All entries are read when entering the context manager. Within the + context manager, further .readprocimg() or .writeprocimg() calls can + be made and the process image can be read or written. When exiting, + all outputs are always written into the process image. + + When 'autorefresh=True' is used, all read or write actions in the + background are performed automatically. + """ + if not self.__modio._context_manager: + # If ModIO itself is in a context manager, it sets the _looprunning=True flag itself + if self.__modio._looprunning: + raise RuntimeError("can not enter context manager inside mainloop or cycleloop") + self.__modio._looprunning = True self.__modio.readprocimg() return self def __exit__(self, exc_type, exc_val, exc_tb): + """Write outputs to process image before leaving the context manager.""" + if self.__modio._imgwriter.is_alive(): + # Reset new data flat to sync with imgwriter + self.__modio._imgwriter.newdata.clear() + + # Write outputs on devices without autorefresh self.__modio.writeprocimg() - self.__modio._looprunning = False + + if self.__modio._imgwriter.is_alive(): + # Wait until imgwriter has written outputs + self.__modio._imgwriter.newdata.wait(2.5) + + if self.__modio._context_manager: + # Do not reset if ModIO is in a context manager itself, it will handle that flag + self.__modio._looprunning = False def __getattr__(self, key): """ diff --git a/src/revpimodio2/modio.py b/src/revpimodio2/modio.py index 38bceb1..ff37f3a 100644 --- a/src/revpimodio2/modio.py +++ b/src/revpimodio2/modio.py @@ -70,6 +70,7 @@ class RevPiModIO(object): "_autorefresh", "_buffedwrite", "_configrsc", + "_context_manager", "_debug", "_devselect", "_exit", @@ -146,6 +147,7 @@ class RevPiModIO(object): self._autorefresh = autorefresh self._configrsc = configrsc + self._context_manager = False self._monitoring = monitoring self._procimg = "/dev/piControl0" if procimg is None else procimg self._set_device_based_cycle_time = True @@ -205,6 +207,22 @@ class RevPiModIO(object): if self._myfh is not None: self._myfh.close() + def __enter__(self): + if self._context_manager: + raise RuntimeError("can not use multiple context managers of same instance") + if self._looprunning: + raise RuntimeError("can not enter context manager with running mainloop or cycleloop") + self._context_manager = True + self._looprunning = True + + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.writeprocimg() + self.exit(full=True) + self._looprunning = False + self._context_manager = False + def __evt_exit(self, signum, sigframe) -> None: """ Eventhandler fuer Programmende. @@ -805,6 +823,9 @@ class RevPiModIO(object): :param blocking: Wenn False, blockiert das Programm hier NICHT :return: None or the return value of the cycle function """ + # Check for context manager + if self._context_manager: + raise RuntimeError("Can not start cycleloop inside a context manager (with statement)") # Prüfen ob ein Loop bereits läuft if self._looprunning: raise RuntimeError("can not start multiple loops mainloop/cycleloop") @@ -1060,6 +1081,9 @@ class RevPiModIO(object): :param blocking: Wenn False, blockiert das Programm hier NICHT """ + # Check for context manager + if self._context_manager: + raise RuntimeError("Can not start mainloop inside a context manager (with statement)") # Prüfen ob ein Loop bereits läuft if self._looprunning: raise RuntimeError("can not start multiple loops mainloop/cycleloop")