Embedded Template Library  1.0
message_timer.h
1 /******************************************************************************
2 The MIT License(MIT)
3 
4 Embedded Template Library.
5 https://github.com/ETLCPP/etl
6 https://www.etlcpp.com
7 
8 Copyright(c) 2017 jwellbelove
9 
10 Permission is hereby granted, free of charge, to any person obtaining a copy
11 of this software and associated documentation files(the "Software"), to deal
12 in the Software without restriction, including without limitation the rights
13 to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
14 copies of the Software, and to permit persons to whom the Software is
15 furnished to do so, subject to the following conditions :
16 
17 The above copyright notice and this permission notice shall be included in all
18 copies or substantial portions of the Software.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
23 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 SOFTWARE.
27 ******************************************************************************/
28 
29 #ifndef ETL_MESSAGE_TIMER_INCLUDED
30 #define ETL_MESSAGE_TIMER_INCLUDED
31 
32 #include <stdint.h>
33 #include "algorithm.h"
34 
35 #include "platform.h"
36 #include "nullptr.h"
37 #include "message_types.h"
38 #include "message.h"
39 #include "message_router.h"
40 #include "message_bus.h"
41 #include "static_assert.h"
42 #include "timer.h"
43 #include "atomic.h"
44 
45 #undef ETL_FILE
46 #define ETL_FILE "44"
47 
48 #if defined(ETL_IN_UNIT_TEST) && ETL_NOT_USING_STL
49  #define ETL_DISABLE_TIMER_UPDATES
50  #define ETL_ENABLE_TIMER_UPDATES
51  #define ETL_TIMER_UPDATES_ENABLED true
52 
53  #undef ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK
54  #undef ETL_MESSAGE_TIMER_USE_INTERRUPT_LOCK
55 #else
56  #if !defined(ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK) && !defined(ETL_MESSAGE_TIMER_USE_INTERRUPT_LOCK)
57  #error ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK or ETL_MESSAGE_TIMER_USE_INTERRUPT_LOCK not defined
58  #endif
59 
60  #if defined(ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK) && defined(ETL_MESSAGE_TIMER_USE_INTERRUPT_LOCK)
61  #error Only define one of ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK or ETL_MESSAGE_TIMER_USE_INTERRUPT_LOCK
62  #endif
63 
64  #if defined(ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK)
65  #define ETL_DISABLE_TIMER_UPDATES (++process_semaphore)
66  #define ETL_ENABLE_TIMER_UPDATES (--process_semaphore)
67  #define ETL_TIMER_UPDATES_ENABLED (process_semaphore.load() == 0)
68  #endif
69 
70  #if defined(ETL_MESSAGE_TIMER_USE_INTERRUPT_LOCK)
71  #if !defined(ETL_MESSAGE_TIMER_DISABLE_INTERRUPTS) || !defined(ETL_MESSAGE_TIMER_ENABLE_INTERRUPTS)
72  #error ETL_MESSAGE_TIMER_DISABLE_INTERRUPTS and/or ETL_MESSAGE_TIMER_ENABLE_INTERRUPTS not defined
73  #endif
74 
75  #define ETL_DISABLE_TIMER_UPDATES (ETL_MESSAGE_TIMER_DISABLE_INTERRUPTS)
76  #define ETL_ENABLE_TIMER_UPDATES (ETL_MESSAGE_TIMER_ENABLE_INTERRUPTS)
77  #define ETL_TIMER_UPDATES_ENABLED true
78  #endif
79 #endif
80 
81 namespace etl
82 {
83  //*************************************************************************
86  {
87  //*******************************************
89  : p_message(ETL_NULLPTR),
90  p_router(ETL_NULLPTR),
91  period(0),
92  delta(etl::timer::state::INACTIVE),
93  destination_router_id(etl::imessage_bus::ALL_MESSAGE_ROUTERS),
94  id(etl::timer::id::NO_TIMER),
95  previous(etl::timer::id::NO_TIMER),
96  next(etl::timer::id::NO_TIMER),
97  repeating(true)
98  {
99  }
100 
101  //*******************************************
102  message_timer_data(etl::timer::id::type id_,
103  const etl::imessage& message_,
104  etl::imessage_router& irouter_,
105  uint32_t period_,
106  bool repeating_,
107  etl::message_router_id_t destination_router_id_ = etl::imessage_bus::ALL_MESSAGE_ROUTERS)
108  : p_message(&message_),
109  p_router(&irouter_),
110  period(period_),
111  delta(etl::timer::state::INACTIVE),
112  destination_router_id(destination_router_id_),
113  id(id_),
114  previous(etl::timer::id::NO_TIMER),
115  next(etl::timer::id::NO_TIMER),
116  repeating(repeating_)
117  {
118  }
119 
120  //*******************************************
122  //*******************************************
123  bool is_active() const
124  {
125  return delta != etl::timer::state::INACTIVE;
126  }
127 
128  //*******************************************
130  //*******************************************
132  {
133  delta = etl::timer::state::INACTIVE;
134  }
135 
136  const etl::imessage* p_message;
137  etl::imessage_router* p_router;
138  uint32_t period;
139  uint32_t delta;
140  etl::message_router_id_t destination_router_id;
141  etl::timer::id::type id;
142  uint_least8_t previous;
143  uint_least8_t next;
144  bool repeating;
145 
146  private:
147 
148  // Disabled.
150  message_timer_data& operator =(const message_timer_data& other);
151  };
152 
153  namespace private_message_timer
154  {
155  //*************************************************************************
157  //*************************************************************************
158  class list
159  {
160  public:
161 
162  //*******************************
163  list(etl::message_timer_data* ptimers_)
164  : head(etl::timer::id::NO_TIMER),
165  tail(etl::timer::id::NO_TIMER),
166  current(etl::timer::id::NO_TIMER),
167  ptimers(ptimers_)
168  {
169  }
170 
171  //*******************************
172  bool empty() const
173  {
174  return head == etl::timer::id::NO_TIMER;
175  }
176 
177  //*******************************
178  // Inserts the timer at the correct delta position
179  //*******************************
180  void insert(etl::timer::id::type id_)
181  {
182  etl::message_timer_data& timer = ptimers[id_];
183 
184  if (head == etl::timer::id::NO_TIMER)
185  {
186  // No entries yet.
187  head = id_;
188  tail = id_;
189  timer.previous = etl::timer::id::NO_TIMER;
190  timer.next = etl::timer::id::NO_TIMER;
191  }
192  else
193  {
194  // We already have entries.
195  etl::timer::id::type test_id = begin();
196 
197  while (test_id != etl::timer::id::NO_TIMER)
198  {
199  etl::message_timer_data& test = ptimers[test_id];
200 
201  // Find the correct place to insert.
202  if (timer.delta <= test.delta)
203  {
204  if (test.id == head)
205  {
206  head = timer.id;
207  }
208 
209  // Insert before test.
210  timer.previous = test.previous;
211  test.previous = timer.id;
212  timer.next = test.id;
213 
214  // Adjust the next delta to compensate.
215  test.delta -= timer.delta;
216 
217  if (timer.previous != etl::timer::id::NO_TIMER)
218  {
219  ptimers[timer.previous].next = timer.id;
220  }
221  break;
222  }
223  else
224  {
225  timer.delta -= test.delta;
226  }
227 
228  test_id = next(test_id);
229  }
230 
231  // Reached the end?
232  if (test_id == etl::timer::id::NO_TIMER)
233  {
234  // Tag on to the tail.
235  ptimers[tail].next = timer.id;
236  timer.previous = tail;
237  timer.next = etl::timer::id::NO_TIMER;
238  tail = timer.id;
239  }
240  }
241  }
242 
243  //*******************************
244  void remove(etl::timer::id::type id_, bool has_expired)
245  {
246  etl::message_timer_data& timer = ptimers[id_];
247 
248  if (head == id_)
249  {
250  head = timer.next;
251  }
252  else
253  {
254  ptimers[timer.previous].next = timer.next;
255  }
256 
257  if (tail == id_)
258  {
259  tail = timer.previous;
260  }
261  else
262  {
263  ptimers[timer.next].previous = timer.previous;
264  }
265 
266  if (!has_expired)
267  {
268  // Adjust the next delta.
269  if (timer.next != etl::timer::id::NO_TIMER)
270  {
271  ptimers[timer.next].delta += timer.delta;
272  }
273  }
274 
275  timer.previous = etl::timer::id::NO_TIMER;
276  timer.next = etl::timer::id::NO_TIMER;
277  timer.delta = etl::timer::state::INACTIVE;
278  }
279 
280  //*******************************
281  etl::message_timer_data& front()
282  {
283  return ptimers[head];
284  }
285 
286  //*******************************
287  etl::timer::id::type begin()
288  {
289  current = head;
290  return current;
291  }
292 
293  //*******************************
294  etl::timer::id::type previous(etl::timer::id::type last)
295  {
296  current = ptimers[last].previous;
297  return current;
298  }
299 
300  //*******************************
301  etl::timer::id::type next(etl::timer::id::type last)
302  {
303  current = ptimers[last].next;
304  return current;
305  }
306 
307  //*******************************
308  void clear()
309  {
310  etl::timer::id::type id = begin();
311 
312  while (id != etl::timer::id::NO_TIMER)
313  {
314  etl::message_timer_data& timer = ptimers[id];
315  id = next(id);
316  timer.next = etl::timer::id::NO_TIMER;
317  }
318 
319  head = etl::timer::id::NO_TIMER;
320  tail = etl::timer::id::NO_TIMER;
321  current = etl::timer::id::NO_TIMER;
322  }
323 
324  private:
325 
326  etl::timer::id::type head;
327  etl::timer::id::type tail;
328  etl::timer::id::type current;
329 
330  etl::message_timer_data* const ptimers;
331  };
332  }
333 
334  //***************************************************************************
336  //***************************************************************************
338  {
339  public:
340 
341  //*******************************************
343  //*******************************************
344  etl::timer::id::type register_timer(const etl::imessage& message_,
345  etl::imessage_router& router_,
346  uint32_t period_,
347  bool repeating_,
348  etl::message_router_id_t destination_router_id_ = etl::imessage_router::ALL_MESSAGE_ROUTERS)
349  {
350  etl::timer::id::type id = etl::timer::id::NO_TIMER;
351 
352  bool is_space = (registered_timers < MAX_TIMERS);
353 
354  if (is_space)
355  {
356  // There's no point adding null message routers.
357  if (!router_.is_null_router())
358  {
359  // Search for the free space.
360  for (uint_least8_t i = 0; i < MAX_TIMERS; ++i)
361  {
362  etl::message_timer_data& timer = timer_array[i];
363 
364  if (timer.id == etl::timer::id::NO_TIMER)
365  {
366  // Create in-place.
367  new (&timer) message_timer_data(i, message_, router_, period_, repeating_, destination_router_id_);
368  ++registered_timers;
369  id = i;
370  break;
371  }
372  }
373  }
374  }
375 
376  return id;
377  }
378 
379  //*******************************************
381  //*******************************************
382  bool unregister_timer(etl::timer::id::type id_)
383  {
384  bool result = false;
385 
386  if (id_ != etl::timer::id::NO_TIMER)
387  {
388  etl::message_timer_data& timer = timer_array[id_];
389 
390  if (timer.id != etl::timer::id::NO_TIMER)
391  {
392  if (timer.is_active())
393  {
394  ETL_DISABLE_TIMER_UPDATES;
395  active_list.remove(timer.id, true);
396  ETL_ENABLE_TIMER_UPDATES;
397  }
398 
399  // Reset in-place.
400  new (&timer) message_timer_data();
401  --registered_timers;
402 
403  result = true;
404  }
405  }
406 
407  return result;
408  }
409 
410  //*******************************************
412  //*******************************************
413  void enable(bool state_)
414  {
415  enabled = state_;
416  }
417 
418  //*******************************************
420  //*******************************************
421  bool is_running() const
422  {
423  return enabled;
424  }
425 
426  //*******************************************
428  //*******************************************
429  void clear()
430  {
431  ETL_DISABLE_TIMER_UPDATES;
432  active_list.clear();
433  ETL_ENABLE_TIMER_UPDATES;
434 
435  for (int i = 0; i < MAX_TIMERS; ++i)
436  {
437  new (&timer_array[i]) message_timer_data();
438  }
439 
440  registered_timers = 0;
441  }
442 
443  //*******************************************
444  // Called by the timer service to indicate the
445  // amount of time that has elapsed since the last successful call to 'tick'.
446  // Returns true if the tick was processed,
447  // false if not.
448  //*******************************************
449  bool tick(uint32_t count)
450  {
451  if (enabled)
452  {
453  if (ETL_TIMER_UPDATES_ENABLED)
454  {
455  // We have something to do?
456  bool has_active = !active_list.empty();
457 
458  if (has_active)
459  {
460  while (has_active && (count >= active_list.front().delta))
461  {
462  etl::message_timer_data& timer = active_list.front();
463 
464  count -= timer.delta;
465 
466  active_list.remove(timer.id, true);
467 
468  if (timer.repeating)
469  {
470  timer.delta = timer.period;
471  active_list.insert(timer.id);
472  }
473 
474  if (timer.p_router != ETL_NULLPTR)
475  {
476  static etl::null_message_router nmr;
477  timer.p_router->receive(nmr, timer.destination_router_id, *(timer.p_message));
478  }
479 
480  has_active = !active_list.empty();
481  }
482 
483  if (has_active)
484  {
485  // Subtract any remainder from the next due timeout.
486  active_list.front().delta -= count;
487  }
488  }
489 
490  return true;
491  }
492  }
493 
494  return false;
495  }
496 
497  //*******************************************
499  //*******************************************
500  bool start(etl::timer::id::type id_, bool immediate_ = false)
501  {
502  bool result = false;
503 
504  // Valid timer id?
505  if (id_ != etl::timer::id::NO_TIMER)
506  {
507  etl::message_timer_data& timer = timer_array[id_];
508 
509  // Registered timer?
510  if (timer.id != etl::timer::id::NO_TIMER)
511  {
512  // Has a valid period.
513  if (timer.period != etl::timer::state::INACTIVE)
514  {
515  ETL_DISABLE_TIMER_UPDATES;
516  if (timer.is_active())
517  {
518  active_list.remove(timer.id, false);
519  }
520 
521  timer.delta = immediate_ ? 0 : timer.period;
522  active_list.insert(timer.id);
523  ETL_ENABLE_TIMER_UPDATES;
524 
525  result = true;
526  }
527  }
528  }
529 
530  return result;
531  }
532 
533  //*******************************************
535  //*******************************************
536  bool stop(etl::timer::id::type id_)
537  {
538  bool result = false;
539 
540  // Valid timer id?
541  if (id_ != etl::timer::id::NO_TIMER)
542  {
543  etl::message_timer_data& timer = timer_array[id_];
544 
545  // Registered timer?
546  if (timer.id != etl::timer::id::NO_TIMER)
547  {
548  if (timer.is_active())
549  {
550  ETL_DISABLE_TIMER_UPDATES;
551  active_list.remove(timer.id, false);
552  ETL_ENABLE_TIMER_UPDATES;
553  }
554 
555  result = true;
556  }
557  }
558 
559  return result;
560  }
561 
562  //*******************************************
564  //*******************************************
565  bool set_period(etl::timer::id::type id_, uint32_t period_)
566  {
567  if (stop(id_))
568  {
569  timer_array[id_].period = period_;
570  return true;
571  }
572 
573  return false;
574  }
575 
576  //*******************************************
578  //*******************************************
579  bool set_mode(etl::timer::id::type id_, bool repeating_)
580  {
581  if (stop(id_))
582  {
583  timer_array[id_].repeating = repeating_;
584  return true;
585  }
586 
587  return false;
588  }
589 
590  protected:
591 
592  //*******************************************
594  //*******************************************
595  imessage_timer(message_timer_data* const timer_array_, const uint_least8_t MAX_TIMERS_)
596  : timer_array(timer_array_),
597  active_list(timer_array_),
598  enabled(false),
599 #if defined(ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK)
600  process_semaphore(0),
601 #endif
602  registered_timers(0),
603  MAX_TIMERS(MAX_TIMERS_)
604  {
605  }
606 
607  //*******************************************
609  //*******************************************
611  {
612  }
613 
614  private:
615 
616  // The array of timer data structures.
617  message_timer_data* const timer_array;
618 
619  // The list of active timers.
620  private_message_timer::list active_list;
621 
622  volatile bool enabled;
623 
624 #if defined(ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK)
625  volatile etl::timer_semaphore_t process_semaphore;
626 #endif
627  volatile uint_least8_t registered_timers;
628 
629  public:
630 
631  const uint_least8_t MAX_TIMERS;
632  };
633 
634  //***************************************************************************
636  //***************************************************************************
637  template <uint_least8_t MAX_TIMERS_>
639  {
640  public:
641 
642  ETL_STATIC_ASSERT(MAX_TIMERS_ <= 254, "No more than 254 timers are allowed");
643 
644  //*******************************************
646  //*******************************************
648  : imessage_timer(timer_array, MAX_TIMERS_)
649  {
650  }
651 
652  private:
653 
654  message_timer_data timer_array[MAX_TIMERS_];
655  };
656 }
657 
658 #undef ETL_DISABLE_TIMER_UPDATES
659 #undef ETL_ENABLE_TIMER_UPDATES
660 #undef ETL_TIMER_UPDATES_ENABLED
661 
662 #undef ETL_FILE
663 
664 #endif
For all types except bool and pointers.
Definition: atomic_gcc_sync.h:69
This is the base of all message routers.
Definition: message_router_generator.h:114
Interface for message timer.
Definition: message_timer.h:338
void enable(bool state_)
Enable/disable the timer.
Definition: message_timer.h:413
bool start(etl::timer::id::type id_, bool immediate_=false)
Starts a timer.
Definition: message_timer.h:500
bool unregister_timer(etl::timer::id::type id_)
Unregister a timer.
Definition: message_timer.h:382
etl::timer::id::type register_timer(const etl::imessage &message_, etl::imessage_router &router_, uint32_t period_, bool repeating_, etl::message_router_id_t destination_router_id_=etl::imessage_router::ALL_MESSAGE_ROUTERS)
Register a timer.
Definition: message_timer.h:344
bool is_running() const
Get the enable/disable state.
Definition: message_timer.h:421
bool set_mode(etl::timer::id::type id_, bool repeating_)
Sets a timer's mode.
Definition: message_timer.h:579
imessage_timer(message_timer_data *const timer_array_, const uint_least8_t MAX_TIMERS_)
Constructor.
Definition: message_timer.h:595
void clear()
Clears the timer of data.
Definition: message_timer.h:429
bool stop(etl::timer::id::type id_)
Stops a timer.
Definition: message_timer.h:536
~imessage_timer()
Destructor.
Definition: message_timer.h:610
bool set_period(etl::timer::id::type id_, uint32_t period_)
Sets a timer's period.
Definition: message_timer.h:565
Definition: message.h:68
The message timer.
Definition: message_timer.h:639
message_timer()
Constructor.
Definition: message_timer.h:647
This router can be used as a sink for messages or a 'null source' router.
Definition: message_router_generator.h:194
A specialised intrusive linked list for timer data.
Definition: message_timer.h:159
Definition: absolute.h:37
The configuration of a timer.
Definition: message_timer.h:86
bool is_active() const
Returns true if the timer is active.
Definition: message_timer.h:123
void set_inactive()
Sets the timer to the inactive state.
Definition: message_timer.h:131
Definition: timer.h:81
Common definitions for the timer framework.
Definition: timer.h:54