Stefan Scholz / ETL
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers message_timer.h Source File

message_timer.h

00001 /******************************************************************************
00002 The MIT License(MIT)
00003 
00004 Embedded Template Library.
00005 https://github.com/ETLCPP/etl
00006 https://www.etlcpp.com
00007 
00008 Copyright(c) 2017 jwellbelove
00009 
00010 Permission is hereby granted, free of charge, to any person obtaining a copy
00011 of this software and associated documentation files(the "Software"), to deal
00012 in the Software without restriction, including without limitation the rights
00013 to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
00014 copies of the Software, and to permit persons to whom the Software is
00015 furnished to do so, subject to the following conditions :
00016 
00017 The above copyright notice and this permission notice shall be included in all
00018 copies or substantial portions of the Software.
00019 
00020 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00021 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00022 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
00023 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00024 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00025 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00026 SOFTWARE.
00027 ******************************************************************************/
00028 
00029 #ifndef __ETL_MESSAGE_TIMER__
00030 #define __ETL_MESSAGE_TIMER__
00031 
00032 #include <stdint.h>
00033 #include <algorithm>
00034 
00035 #include "platform.h "
00036 #include "nullptr.h "
00037 #include "message_types.h"
00038 #include "message.h"
00039 #include "message_router.h"
00040 #include "message_bus.h"
00041 #include "static_assert.h"
00042 #include "timer.h"
00043 #include "atomic.h"
00044 
00045 #undef ETL_FILE
00046 #define ETL_FILE "41"
00047 
00048 namespace etl
00049 {
00050   //*************************************************************************
00051   /// The configuration of a timer.
00052   struct message_timer_data
00053   {
00054     //*******************************************
00055     message_timer_data()
00056       : p_message(std::nullptr),
00057         p_router(std::nullptr),
00058         period(0),
00059         delta(etl::timer::state::INACTIVE),
00060         destination_router_id(etl::imessage_bus::ALL_MESSAGE_ROUTERS),
00061         id(etl::timer::id::NO_TIMER),
00062         previous(etl::timer::id::NO_TIMER),
00063         next(etl::timer::id::NO_TIMER),
00064         repeating(true)
00065     {
00066     }
00067 
00068     //*******************************************
00069     message_timer_data(etl::timer::id::type     id_,
00070                        const etl::imessage&     message_,
00071                        etl::imessage_router&    irouter_,
00072                        uint32_t                 period_,
00073                        bool                     repeating_,
00074                        etl::message_router_id_t destination_router_id_)
00075       : p_message(&message_),
00076         p_router(&irouter_),
00077         period(period_),
00078         delta(etl::timer::state::INACTIVE),
00079         id(id_),
00080         previous(etl::timer::id::NO_TIMER),
00081         next(etl::timer::id::NO_TIMER),
00082         repeating(repeating_)
00083     {
00084       if (irouter_.is_bus())
00085       {
00086         destination_router_id = destination_router_id_;
00087       }
00088       else
00089       {
00090         destination_router_id = etl::imessage_bus::ALL_MESSAGE_ROUTERS;
00091       }
00092     }
00093 
00094     //*******************************************
00095     /// Returns true if the timer is active.
00096     //*******************************************
00097     bool is_active() const
00098     {
00099       return delta != etl::timer::state::INACTIVE;
00100     }
00101 
00102     //*******************************************
00103     /// Sets the timer to the inactive state.
00104     //*******************************************
00105     void set_inactive()
00106     {
00107       delta = etl::timer::state::INACTIVE;
00108     }
00109 
00110     const etl::imessage*     p_message;
00111     etl::imessage_router*    p_router;
00112     uint32_t                 period;
00113     uint32_t                 delta;
00114     etl::message_router_id_t destination_router_id;
00115     etl::timer::id::type     id;
00116     uint_least8_t            previous;
00117     uint_least8_t            next;
00118     bool                     repeating;
00119 
00120   private:
00121 
00122     // Disabled.
00123     message_timer_data(const message_timer_data& other);
00124     message_timer_data& operator =(const message_timer_data& other);
00125   };
00126 
00127   namespace __private_message_timer__
00128   {
00129     //*************************************************************************
00130     /// A specialised intrusive linked list for timer data.
00131     //*************************************************************************
00132     class list
00133     {
00134     public:
00135 
00136       //*******************************
00137       list(etl::message_timer_data* ptimers_)
00138         : head(etl::timer::id::NO_TIMER),
00139           tail(etl::timer::id::NO_TIMER),
00140           current(etl::timer::id::NO_TIMER),
00141           ptimers(ptimers_)
00142       {
00143       }
00144 
00145       //*******************************
00146       bool empty() const
00147       {
00148         return head == etl::timer::id::NO_TIMER;
00149       }
00150 
00151       //*******************************
00152       // Inserts the timer at the correct delta position
00153       //*******************************
00154       void insert(etl::timer::id::type id_)
00155       {
00156         etl::message_timer_data& timer = ptimers[id_];
00157 
00158         if (head == etl::timer::id::NO_TIMER)
00159         {
00160           // No entries yet.
00161           head = id_;
00162           tail = id_;
00163           timer.previous = etl::timer::id::NO_TIMER;
00164           timer.next     = etl::timer::id::NO_TIMER;
00165         }
00166         else
00167         {
00168           // We already have entries.
00169           etl::timer::id::type test_id = begin();
00170 
00171           while (test_id != etl::timer::id::NO_TIMER)
00172           {
00173             etl::message_timer_data& test = ptimers[test_id];
00174 
00175             // Find the correct place to insert.
00176             if (timer.delta <= test.delta)
00177             {
00178               if (test.id == head)
00179               {
00180                 head = timer.id;
00181               }
00182 
00183               // Insert before test.
00184               timer.previous = test.previous;
00185               test.previous  = timer.id;
00186               timer.next     = test.id;
00187 
00188               // Adjust the next delta to compensate.
00189               test.delta -= timer.delta;
00190 
00191               if (timer.previous != etl::timer::id::NO_TIMER)
00192               {
00193                 ptimers[timer.previous].next = timer.id;
00194               }
00195               break;
00196             }
00197             else
00198             {
00199               timer.delta -= test.delta;
00200             }
00201 
00202             test_id = next(test_id);
00203           }
00204 
00205           // Reached the end?
00206           if (test_id == etl::timer::id::NO_TIMER)
00207           {
00208             // Tag on to the tail.
00209             ptimers[tail].next = timer.id;
00210             timer.previous     = tail;
00211             timer.next         = etl::timer::id::NO_TIMER;
00212             tail               = timer.id;
00213           }
00214         }
00215       }
00216 
00217       //*******************************
00218       void remove(etl::timer::id::type id_, bool has_expired)
00219       {
00220         etl::message_timer_data& timer = ptimers[id_];
00221 
00222         if (head == id_)
00223         {
00224           head = timer.next;
00225         }
00226         else
00227         {
00228           ptimers[timer.previous].next = timer.next;
00229         }
00230 
00231         if (tail == id_)
00232         {
00233           tail = timer.previous;
00234         }
00235         else
00236         {
00237           ptimers[timer.next].previous = timer.previous;
00238         }
00239 
00240         if (!has_expired)
00241         {
00242           // Adjust the next delta.
00243           if (timer.next != etl::timer::id::NO_TIMER)
00244           {
00245             ptimers[timer.next].delta += timer.delta;
00246           }
00247         }
00248 
00249         timer.previous = etl::timer::id::NO_TIMER;
00250         timer.next     = etl::timer::id::NO_TIMER;
00251         timer.delta    = etl::timer::state::INACTIVE;
00252       }
00253 
00254       //*******************************
00255       etl::message_timer_data& front()
00256       {
00257         return ptimers[head];
00258       }
00259 
00260       //*******************************
00261       etl::timer::id::type begin()
00262       {
00263         current = head;
00264         return current;
00265       }
00266 
00267       //*******************************
00268       etl::timer::id::type previous(etl::timer::id::type last)
00269       {
00270         current = ptimers[last].previous;
00271         return current;
00272       }
00273 
00274       //*******************************
00275       etl::timer::id::type next(etl::timer::id::type last)
00276       {
00277         current = ptimers[last].next;
00278         return current;
00279       }
00280 
00281       //*******************************
00282       void clear()
00283       {
00284         etl::timer::id::type id = begin();
00285 
00286         while (id != etl::timer::id::NO_TIMER)
00287         {
00288           etl::message_timer_data& timer = ptimers[id];
00289           id = next(id);
00290           timer.next = etl::timer::id::NO_TIMER;
00291         }
00292 
00293         head    = etl::timer::id::NO_TIMER;
00294         tail    = etl::timer::id::NO_TIMER;
00295         current = etl::timer::id::NO_TIMER;
00296       }
00297 
00298     private:
00299 
00300       etl::timer::id::type head;
00301       etl::timer::id::type tail;
00302       etl::timer::id::type current;
00303 
00304       etl::message_timer_data* const ptimers;
00305     };
00306   }
00307 
00308   //***************************************************************************
00309   /// Interface for message timer
00310   //***************************************************************************
00311   class imessage_timer
00312   {
00313   public:
00314 
00315     //*******************************************
00316     /// Register a timer.
00317     //*******************************************
00318     etl::timer::id::type register_timer(const etl::imessage&     message_,
00319                                         etl::imessage_router&    router_,
00320                                         uint32_t                 period_,
00321                                         bool                     repeating_,
00322                                         etl::message_router_id_t destination_router_id_ = etl::imessage_router::ALL_MESSAGE_ROUTERS)
00323     {
00324       etl::timer::id::type id = etl::timer::id::NO_TIMER;
00325 
00326       disable_timer_updates();
00327 
00328       bool is_space = (registered_timers < MAX_TIMERS);
00329 
00330       if (is_space)
00331       {
00332         // There's no point adding null message routers.
00333         if (!router_.is_null_router())
00334         {
00335           // Search for the free space.
00336           for (uint_least8_t i = 0; i < MAX_TIMERS; ++i)
00337           {
00338             etl::message_timer_data& timer = timer_array[i];
00339 
00340             if (timer.id == etl::timer::id::NO_TIMER)
00341             {
00342               // Create in-place.
00343               new (&timer) message_timer_data(i, message_, router_, period_, repeating_, destination_router_id_);
00344               ++registered_timers;
00345               id = i;
00346               break;
00347             }
00348           }
00349         }
00350       }
00351 
00352       enable_timer_updates();
00353 
00354       return id;
00355     }
00356 
00357     //*******************************************
00358     /// Unregister a timer.
00359     //*******************************************
00360     bool unregister_timer(etl::timer::id::type id_)
00361     {
00362       bool result = false;
00363 
00364       if (id_ != etl::timer::id::NO_TIMER)
00365       {
00366         disable_timer_updates();
00367 
00368         etl::message_timer_data& timer = timer_array[id_];
00369 
00370         if (timer.id != etl::timer::id::NO_TIMER)
00371         {
00372           if (timer.is_active())
00373           {
00374             active_list.remove(timer.id, true);
00375 
00376             // Reset in-place.
00377             new (&timer) message_timer_data();
00378             --registered_timers;
00379 
00380             result = true;
00381           }
00382         }
00383 
00384         enable_timer_updates();
00385       }
00386 
00387       return result;
00388     }
00389 
00390     //*******************************************
00391     /// Enable/disable the timer.
00392     //*******************************************
00393     void enable(bool state_)
00394     {
00395       enabled = state_;
00396     }
00397 
00398     //*******************************************
00399     /// Get the enable/disable state.
00400     //*******************************************
00401     bool is_running() const
00402     {
00403       return enabled;
00404     }
00405 
00406     //*******************************************
00407     /// Clears the timer of data.
00408     //*******************************************
00409     void clear()
00410     {
00411       disable_timer_updates();
00412 
00413       active_list.clear();
00414 
00415       for (int i = 0; i < MAX_TIMERS; ++i)
00416       {
00417         new (&timer_array[i]) message_timer_data();
00418       }
00419 
00420       registered_timers = 0;
00421 
00422       enable_timer_updates();
00423     }
00424 
00425     //*******************************************
00426     // Called by the timer service to indicate the
00427     // amount of time that has elapsed since the last successful call to 'tick'.
00428     // Returns true if the tick was processed,
00429     // false if not.
00430     //*******************************************
00431     bool tick(uint32_t count)
00432     {
00433       if (enabled)
00434       {
00435         if (process_semaphore.load() == 0)
00436         {
00437           // We have something to do?
00438           bool has_active = !active_list.empty();
00439 
00440           if (has_active)
00441           {
00442             while (has_active && (count >= active_list.front().delta))
00443             {
00444               etl::message_timer_data& timer = active_list.front();
00445 
00446               count -= timer.delta;
00447 
00448               active_list.remove(timer.id, true);
00449 
00450               if (timer.repeating)
00451               {
00452                 timer.delta = timer.period;
00453                 active_list.insert(timer.id);
00454               }
00455 
00456               if (timer.p_router != std::nullptr)
00457               {
00458                 if (timer.p_router->is_bus())
00459                 {
00460                   // Send to a message bus.
00461                   etl::imessage_bus& bus = static_cast<etl::imessage_bus&>(*(timer.p_router));
00462                   bus.receive(timer.destination_router_id, *(timer.p_message));
00463                 }
00464                 else
00465                 {
00466                   // Send to a router.
00467                   timer.p_router->receive(*(timer.p_message));
00468                 }
00469               }
00470 
00471               has_active = !active_list.empty();
00472             }
00473 
00474             if (has_active)
00475             {
00476               // Subtract any remainder from the next due timeout.
00477               active_list.front().delta -= count;
00478             }
00479           }
00480 
00481           return true;
00482         }
00483       }
00484 
00485       return false;
00486     }
00487 
00488     //*******************************************
00489     /// Starts a timer.
00490     //*******************************************
00491     bool start(etl::timer::id::type id_, bool immediate_ = false)
00492     {
00493       bool result = false;
00494 
00495       disable_timer_updates();
00496 
00497       // Valid timer id?
00498       if (id_ != etl::timer::id::NO_TIMER)
00499       {
00500         etl::message_timer_data& timer = timer_array[id_];
00501 
00502         // Registered timer?
00503         if (timer.id != etl::timer::id::NO_TIMER)
00504         {
00505           // Has a valid period.
00506           if (timer.period != etl::timer::state::INACTIVE)
00507           {
00508             if (timer.is_active())
00509             {
00510               active_list.remove(timer.id, false);
00511             }
00512 
00513             timer.delta = immediate_ ? 0 : timer.period;
00514             active_list.insert(timer.id);
00515 
00516             result = true;
00517           }
00518         }
00519       }
00520 
00521       enable_timer_updates();
00522 
00523       return result;
00524     }
00525 
00526     //*******************************************
00527     /// Stops a timer.
00528     //*******************************************
00529     bool stop(etl::timer::id::type id_)
00530     {
00531       bool result = false;
00532 
00533       disable_timer_updates();
00534 
00535       // Valid timer id?
00536       if (id_ != etl::timer::id::NO_TIMER)
00537       {
00538         etl::message_timer_data& timer = timer_array[id_];
00539 
00540         // Registered timer?
00541         if (timer.id != etl::timer::id::NO_TIMER)
00542         {
00543           if (timer.is_active())
00544           {
00545             active_list.remove(timer.id, false);
00546             result = true;
00547           }
00548         }
00549       }
00550 
00551       enable_timer_updates();
00552 
00553       return result;
00554     }
00555 
00556     //*******************************************
00557     /// Sets a timer's period.
00558     //*******************************************
00559     bool set_period(etl::timer::id::type id_, uint32_t period_)
00560     {
00561       if (stop(id_))
00562       {
00563         timer_array[id_].period = period_;
00564         return start(id_);
00565       }
00566       
00567       return false;
00568     }
00569 
00570     //*******************************************
00571     /// Sets a timer's mode.
00572     //*******************************************
00573     bool set_mode(etl::timer::id::type id_, bool repeating_)
00574     {
00575       if (stop(id_))
00576       {
00577         timer_array[id_].repeating = repeating_;
00578         return start(id_);
00579       }
00580 
00581       return false;
00582     }
00583 
00584   protected:
00585 
00586     //*******************************************
00587     /// Constructor.
00588     //*******************************************
00589     imessage_timer(message_timer_data* const timer_array_, const uint_least8_t  MAX_TIMERS_)
00590       : timer_array(timer_array_),
00591         active_list(timer_array_),
00592         enabled(false),
00593         process_semaphore(0),
00594         registered_timers(0),
00595         MAX_TIMERS(MAX_TIMERS_)
00596     {
00597     }
00598 
00599   private:
00600 
00601     //*******************************************
00602     /// Enable timer callback events.
00603     //*******************************************
00604     void enable_timer_updates()
00605     {
00606       --process_semaphore;
00607     }
00608 
00609     //*******************************************
00610     /// Disable timer callback events.
00611     //*******************************************
00612     void disable_timer_updates()
00613     {
00614       ++process_semaphore;
00615     }
00616 
00617     // The array of timer data structures.
00618     message_timer_data* const timer_array;
00619 
00620     // The list of active timers.
00621     __private_message_timer__::list active_list;
00622 
00623     volatile bool enabled;
00624     volatile etl::timer_semaphore_t process_semaphore;
00625     volatile uint_least8_t registered_timers;
00626 
00627   public:
00628 
00629     const uint_least8_t MAX_TIMERS;
00630   };
00631 
00632   //***************************************************************************
00633   /// The message timer
00634   //***************************************************************************
00635   template <uint_least8_t MAX_TIMERS_>
00636   class message_timer : public etl::imessage_timer
00637   {
00638   public:
00639 
00640     STATIC_ASSERT(MAX_TIMERS_ <= 254, "No more than 254 timers are allowed");
00641 
00642     //*******************************************
00643     /// Constructor.
00644     //*******************************************
00645     message_timer()
00646       : imessage_timer(timer_array, MAX_TIMERS_)
00647     {
00648     }
00649 
00650   private:
00651 
00652     message_timer_data timer_array[MAX_TIMERS_];
00653   };
00654 }
00655 
00656 #undef ETL_FILE
00657 
00658 #endif
00659