Source code for ska_mid_dish_manager.utils.schedulers

"""This module provides functionality related to scheduling and managing of tasks."""

import threading
from typing import Callable

DEFAULT_WATCHDOG_TIMEOUT = 10.0  # seconds


[docs]class WatchdogTimerInactiveError(RuntimeError): """Exception raised when the watchdog timer is not enabled."""
class WatchdogTimer: def __init__( self, callback_on_timeout: Callable = None, timeout: float = DEFAULT_WATCHDOG_TIMEOUT ): """This class implements a watchdog timer that will make a callback when the timer expires. :param callback_on_timeout: The callback function to be invoked when the timer expires. :param timeout: The default time in seconds after which the callback will be invoked if not reset, defaults to DEFAULT_WATCHDOG_TIMEOUT :raises ValueError: If timeout specified is less than zero. """ if timeout <= 0: raise ValueError("Timeout must be greater than 0.") self._timeout = timeout self._external_callback = callback_on_timeout self._timer = None self._lock = threading.RLock() self._enabled = False def enable(self, timeout: float = None): """Enable the watchdog timer. :param timeout: Time in seconds, defaults to None :raises ValueError: If the timeout is less than or equal to zero. """ with self._lock: # override the default timeout if provided if timeout is not None: if timeout <= 0: raise ValueError("Watchdog timer is disabled. Timeout must be greater than 0.") self._timeout = timeout self._enabled = True self._start_timer() def is_enabled(self) -> bool: """Check if the watchdog timer is enabled.""" with self._lock: return self._enabled def disable(self): """Disable the watchdog timer.""" with self._lock: self._enabled = False if self._timer is not None: self._timer.cancel() self._timer = None def reset(self): """Reset the watchdog timer. This method will cancel the current timer and start a new one with the same timeout. If the timer is not active, it raises an exception. :raises WatchdogTimerInactiveError: If called when the timer is disabled / not active. """ with self._lock: if not self._enabled: raise WatchdogTimerInactiveError("Watchdog timer is disabled. Call enable first.") if self._timer is not None: self._timer.cancel() self._timer = None self._start_timer() def _start_timer(self): """Start the timer. If the timer is already running, there is no effect. This method should be called with the lock held. """ if self._timer is None and self._enabled: self._timer = threading.Timer(self._timeout, self._callback_on_timeout_and_disable) self._timer.start() def _callback_on_timeout_and_disable(self): """Wrapper for the callback to disable the timer after timeout.""" # Clear the timer reference and disable the watchdog with self._lock: self._timer = None self._enabled = False if self._external_callback: self._external_callback()