From 1c1e6b45b6d749d39deed3f446ed29b9ec523bae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gina=20H=C3=A4u=C3=9Fge?= Date: Wed, 25 Mar 2015 16:48:30 +0100 Subject: [PATCH] Added RepeatedTime class to use for repeated tasks Utilizing it for temperature and sd status polling in comm.py --- src/octoprint/util/__init__.py | 63 ++++++++++++++++++++++++++++++++++ src/octoprint/util/comm.py | 19 ++++------ 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/src/octoprint/util/__init__.py b/src/octoprint/util/__init__.py index e00245dd..3c337c00 100644 --- a/src/octoprint/util/__init__.py +++ b/src/octoprint/util/__init__.py @@ -534,6 +534,69 @@ def address_for_client(host, port): except: continue +class RepeatedTimer(threading.Thread): + """ + This class represents an action that should be run repeatedly in a predefined interval. It is similar to python's + own :class:`threading.Timer` class, but instead of only running once the ``function`` will be run again and again, + sleeping the stated ``interval`` in between. + + RepeatedTimers are started, as with threads, by calling their ``start()`` method. The timer can be stopped (in + between runs) by calling the :func:`cancel` method. The interval the time waited before execution of a loop may + not be exactly the same as the interval specified by the user. + + For example: + + .. code-block:: python + + def hello(): + print("Hello World!") + + t = Timer(1.0, hello) + t.start() # prints "Hello World!" every second + + Arguments: + interval (float): The interval between each ``function`` call, in seconds. + function (callable): The function to call. + args (list or tuple): The arguments for the ``function`` call. + kwargs (dict): The keyword arguments for the ``function`` call. + run_first (boolean): If set to True, the function will be run for the first time *before* the first wait period. + If set to False (the default), the function will be run for the first time *after* the first wait period. + """ + + def __init__(self, interval, function, args=None, kwargs=None, run_first=False): + threading.Thread.__init__(self) + + if args is None: + args = [] + if kwargs is None: + kwargs = dict() + + self.interval = interval + self.function = function + self.finished = threading.Event() + self.args = args + self.kwargs = kwargs + self.run_first = run_first + + def cancel(self): + self.finished.set() + + def run(self): + while True: + if self.run_first: + # if we are to run the function BEFORE waiting for the first time + self.function(*self.args, **self.kwargs) + + # wait, but break if we are cancelled + self.finished.wait(self.interval) + if self.finished.is_set(): + return + + if not self.run_first: + # if we are to run the function AFTER waiting for the first time + self.function(*self.args, **self.kwargs) + + class CountedEvent(object): def __init__(self, value=0, max=None, name=None): diff --git a/src/octoprint/util/comm.py b/src/octoprint/util/comm.py index 7a970f30..2f317cfe 100644 --- a/src/octoprint/util/comm.py +++ b/src/octoprint/util/comm.py @@ -24,7 +24,7 @@ from octoprint.settings import settings, default_settings from octoprint.events import eventManager, Events from octoprint.filemanager import valid_file_type from octoprint.filemanager.destinations import FileDestinations -from octoprint.util import get_exception_string, sanitize_ascii, filter_non_ascii, CountedEvent +from octoprint.util import get_exception_string, sanitize_ascii, filter_non_ascii, CountedEvent, RepeatedTimer from octoprint.util.virtual import VirtualPrinter try: @@ -165,8 +165,8 @@ class MachineCom(object): self._clear_to_send = CountedEvent(max=10, name="comm.clear_to_send") self._send_queue = TypedQueue() - self._sd_status_timer = None self._temperature_timer = None + self._sd_status_timer = None # hooks self._pluginManager = octoprint.plugin.plugin_manager() @@ -510,7 +510,9 @@ class MachineCom(object): self._currentFile.setFilepos(0) self.sendCommand("M24") - self._poll_sd_status() + + self._sd_status_timer = RepeatedTimer(get_interval("sdStatus"), self._poll_sd_status, run_first=True) + self._sd_status_timer.start() else: line = self._getNext() if line is not None: @@ -1130,10 +1132,6 @@ class MachineCom(object): if self.isOperational() and not self.isStreaming() and not self._blocking_command and not self._heating: self.sendCommand("M105", cmd_type="temperature_poll") - interval = get_interval("temperature") - self._temperature_timer = threading.Timer(interval, self._poll_temperature) - self._temperature_timer.start() - def _poll_sd_status(self): """ Polls the sd printing status after the sd status timeout, re-enqueues itself. @@ -1145,12 +1143,9 @@ class MachineCom(object): if self.isOperational() and self.isSdPrinting() and not self._blocking_command and not self._heating: self.sendCommand("M27", cmd_type="sd_status_poll") - interval = get_interval("sdStatus") - self._sd_status_timer = threading.Timer(interval, self._poll_sd_status) - self._sd_status_timer.start() - def _onConnected(self): - self._poll_temperature() + self._temperature_timer = RepeatedTimer(get_interval("temperature"), self._poll_temperature, run_first=True) + self._temperature_timer.start() self._changeState(self.STATE_OPERATIONAL)