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