/********************************************************************* * Software License Agreement (BSD License) * * Copyright (c) 2024 * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. *********************************************************************/ #ifndef ROBOT_WALL_TIMER_H #define ROBOT_WALL_TIMER_H #include #include "robot_time_decl.h" #include #include #include #include #include #include namespace robot { /** * @brief Structure containing information about a wall timer event * * Similar to ros::WallTimerEvent, this structure is passed to wall timer callbacks * and contains timing information about the current and previous timer events. * Uses wall-clock time (not affected by simulated time). */ struct ROBOT_TIME_DECL WallTimerEvent { /** * @brief The time when the current callback was actually called (wall-clock time) */ WallTime current_real; /** * @brief The time when the current callback was expected to be called (wall-clock time) */ WallTime current_expected; /** * @brief The time when the previous callback was actually called (wall-clock time) */ WallTime last_real; /** * @brief The time when the previous callback was expected to be called (wall-clock time) */ WallTime last_expected; /** * @brief The time between the last two callbacks (wall-clock duration) */ WallDuration last_duration; /** * @brief Default constructor */ WallTimerEvent() : current_real(WallTime::now()) , current_expected(WallTime::now()) , last_real(WallTime()) , last_expected(WallTime()) , last_duration(WallDuration()) {} }; /** * @class WallTimer * @brief A class to call a callback function at a specified rate using wall-clock time * * This class is similar to ros::WallTimer. It creates a separate thread that * calls a callback function at a specified period. The callback receives * a WallTimerEvent structure containing timing information. * * Unlike Timer, WallTimer always uses wall-clock time and is not affected * by simulated time. This makes it ideal for: * - Performance monitoring * - Real-time deadlines * - Hardware interfaces * - Profiling and benchmarking * * Example usage: * @code * void myCallback(const robot::WallTimerEvent& event) { * // Do something periodically * } * * robot::WallTimer timer(robot::WallDuration(1.0), myCallback); * timer.start(); * // ... later ... * timer.stop(); * @endcode */ class ROBOT_TIME_DECL WallTimer { public: /** * @brief Callback function type for wall timer events */ using Callback = std::function; /** * @brief Default constructor - creates an uninitialized timer * Timer must be assigned or constructed with parameters before use */ WallTimer(); /** * @brief Constructor * @param period The period between timer callbacks (wall-clock duration) * @param callback The callback function to call * @param oneshot If true, the timer will only fire once. If false, it will fire repeatedly. * @param autostart If true, the timer will start automatically. If false, you must call start(). */ WallTimer(const WallDuration& period, const Callback& callback, bool oneshot = false, bool autostart = true); /** * @brief Constructor with member function pointer * @param period The period between timer callbacks (wall-clock duration) * @param callback Member function pointer to call * @param obj Object instance to call the member function on * @param oneshot If true, the timer will only fire once. If false, it will fire repeatedly. * @param autostart If true, the timer will start automatically. If false, you must call start(). * * Example: * @code * class MyClass { * void callback(const robot::WallTimerEvent& event) { } * }; * MyClass obj; * robot::WallTimer timer(robot::WallDuration(1.0), &MyClass::callback, &obj); * @endcode */ template WallTimer(const WallDuration& period, void (T::*callback)(const WallTimerEvent&), T* obj, bool oneshot = false, bool autostart = true) : WallTimer(period, [obj, callback](const WallTimerEvent& event) { (obj->*callback)(event); }, oneshot, autostart) {} /** * @brief Copy constructor */ WallTimer(const WallTimer& rhs); /** * @brief Destructor - stops the timer and joins the thread */ ~WallTimer(); /** * @brief Start the timer. Does nothing if the timer is already started. */ void start(); /** * @brief Stop the timer. Once this call returns, no more callbacks will be called. * Does nothing if the timer is already stopped. */ void stop(); /** * @brief Set the period of this timer * @param period The new period between timer callbacks * @param reset Whether to reset the timer. If true, timer ignores elapsed time and next callback occurs at now()+period */ void setPeriod(const WallDuration& period, bool reset = true); /** * @brief Check if the timer has been started * @return True if the timer is running, false otherwise */ bool hasStarted() const; /** * @brief Check if the timer is valid (has a callback) * @return True if the timer is valid, false otherwise */ bool isValid() const; /** * @brief Check if the timer has any pending events to call * @return True if there are pending events, false otherwise */ bool hasPending() const; /** * @brief Check if the timer is one-shot * @return True if the timer is one-shot, false if it repeats */ bool isOneShot() const; /** * @brief Set whether the timer is one-shot * @param oneshot If true, the timer will only fire once */ void setOneShot(bool oneshot); /** * @brief Get the timer period * @return The period between timer callbacks */ WallDuration getPeriod() const; /** * @brief Conversion to bool (for checking validity) */ operator void*() const; /** * @brief Equality operator */ bool operator==(const WallTimer& rhs) const; /** * @brief Inequality operator */ bool operator!=(const WallTimer& rhs) const; /** * @brief Less-than operator (for ordering in containers) */ bool operator<(const WallTimer& rhs) const; // Non-copyable assignment WallTimer& operator=(const WallTimer&) = delete; // Movable WallTimer(WallTimer&& other) noexcept; WallTimer& operator=(WallTimer&& other) noexcept; private: /** * @brief The timer thread function */ void timerThread(); WallDuration period_; ///< Period between callbacks Callback callback_; ///< Callback function bool oneshot_; ///< Whether timer is one-shot std::atomic running_; ///< Whether timer is running std::atomic should_stop_; ///< Flag to stop the timer thread std::thread thread_; ///< Timer thread mutable std::mutex mutex_; ///< Mutex for thread safety std::condition_variable cv_; ///< Condition variable for timing WallTime last_real_; ///< Last actual callback time WallTime last_expected_; ///< Last expected callback time }; } // namespace robot #endif // ROBOT_WALL_TIMER_H