Embedded Template Library 1.0
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 //*******************************************
99 message_timer_data(etl::timer::id::type id_,
100 const etl::imessage& message_,
101 etl::imessage_router& irouter_,
102 uint32_t period_,
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;
138 etl::timer::id::type id;
139 uint_least8_t previous;
140 uint_least8_t next;
141 bool repeating;
142
143 private:
144
145 // Disabled.
147 message_timer_data& operator =(const message_timer_data& other);
148 };
149
150 namespace private_message_timer
151 {
152 //*************************************************************************
154 //*************************************************************************
155 class list
156 {
157 public:
158
159 //*******************************
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 etl::timer::id::type begin()
285 {
286 current = head;
287 return current;
288 }
289
290 //*******************************
291 etl::timer::id::type previous(etl::timer::id::type last)
292 {
293 current = ptimers[last].previous;
294 return current;
295 }
296
297 //*******************************
298 etl::timer::id::type next(etl::timer::id::type last)
299 {
300 current = ptimers[last].next;
301 return current;
302 }
303
304 //*******************************
305 void clear()
306 {
307 etl::timer::id::type id = begin();
308
309 while (id != etl::timer::id::NO_TIMER)
310 {
311 etl::message_timer_data& timer = ptimers[id];
312 id = next(id);
313 timer.next = etl::timer::id::NO_TIMER;
314 }
315
316 head = etl::timer::id::NO_TIMER;
317 tail = etl::timer::id::NO_TIMER;
318 current = etl::timer::id::NO_TIMER;
319 }
320
321 private:
322
323 etl::timer::id::type head;
324 etl::timer::id::type tail;
325 etl::timer::id::type current;
326
327 etl::message_timer_data* const ptimers;
328 };
329 }
330
331 //***************************************************************************
333 //***************************************************************************
335 {
336 public:
337
338 //*******************************************
340 //*******************************************
341 etl::timer::id::type register_timer(const etl::imessage& message_,
342 etl::imessage_router& router_,
343 uint32_t period_,
344 bool repeating_,
345 etl::message_router_id_t destination_router_id_ = etl::imessage_router::ALL_MESSAGE_ROUTERS)
346 {
347 etl::timer::id::type id = etl::timer::id::NO_TIMER;
348
349 bool is_space = (registered_timers < MAX_TIMERS);
350
351 if (is_space)
352 {
353 // There's no point adding null message routers.
354 if (!router_.is_null_router())
355 {
356 // Search for the free space.
357 for (uint_least8_t i = 0U; i < MAX_TIMERS; ++i)
358 {
359 etl::message_timer_data& timer = timer_array[i];
360
361 if (timer.id == etl::timer::id::NO_TIMER)
362 {
363 // Create in-place.
364 new (&timer) message_timer_data(i, message_, router_, period_, repeating_, destination_router_id_);
365 ++registered_timers;
366 id = i;
367 break;
368 }
369 }
370 }
371 }
372
373 return id;
374 }
375
376 //*******************************************
378 //*******************************************
379 bool unregister_timer(etl::timer::id::type id_)
380 {
381 bool result = false;
382
383 if (id_ != etl::timer::id::NO_TIMER)
384 {
385 etl::message_timer_data& timer = timer_array[id_];
386
387 if (timer.id != etl::timer::id::NO_TIMER)
388 {
389 if (timer.is_active())
390 {
391 ETL_DISABLE_TIMER_UPDATES;
392 active_list.remove(timer.id, true);
393 ETL_ENABLE_TIMER_UPDATES;
394 }
395
396 // Reset in-place.
397 new (&timer) message_timer_data();
398 --registered_timers;
399
400 result = true;
401 }
402 }
403
404 return result;
405 }
406
407 //*******************************************
409 //*******************************************
410 void enable(bool state_)
411 {
412 enabled = state_;
413 }
414
415 //*******************************************
417 //*******************************************
418 bool is_running() const
419 {
420 return enabled;
421 }
422
423 //*******************************************
425 //*******************************************
426 void clear()
427 {
428 ETL_DISABLE_TIMER_UPDATES;
429 active_list.clear();
430 ETL_ENABLE_TIMER_UPDATES;
431
432 for (int i = 0; i < MAX_TIMERS; ++i)
433 {
434 new (&timer_array[i]) message_timer_data();
435 }
436
437 registered_timers = 0;
438 }
439
440 //*******************************************
441 // Called by the timer service to indicate the
442 // amount of time that has elapsed since the last successful call to 'tick'.
443 // Returns true if the tick was processed,
444 // false if not.
445 //*******************************************
446 bool tick(uint32_t count)
447 {
448 if (enabled)
449 {
450 if (ETL_TIMER_UPDATES_ENABLED)
451 {
452 // We have something to do?
453 bool has_active = !active_list.empty();
454
455 if (has_active)
456 {
457 while (has_active && (count >= active_list.front().delta))
458 {
459 etl::message_timer_data& timer = active_list.front();
460
461 count -= timer.delta;
462
463 active_list.remove(timer.id, true);
464
465 if (timer.repeating)
466 {
467 timer.delta = timer.period;
468 active_list.insert(timer.id);
469 }
470
471 if (timer.p_router != ETL_NULLPTR)
472 {
473 timer.p_router->receive(timer.destination_router_id, *(timer.p_message));
474 }
475
476 has_active = !active_list.empty();
477 }
478
479 if (has_active)
480 {
481 // Subtract any remainder from the next due timeout.
482 active_list.front().delta -= count;
483 }
484 }
485
486 return true;
487 }
488 }
489
490 return false;
491 }
492
493 //*******************************************
495 //*******************************************
496 bool start(etl::timer::id::type id_, bool immediate_ = false)
497 {
498 bool result = false;
499
500 // Valid timer id?
501 if (id_ != etl::timer::id::NO_TIMER)
502 {
503 etl::message_timer_data& timer = timer_array[id_];
504
505 // Registered timer?
506 if (timer.id != etl::timer::id::NO_TIMER)
507 {
508 // Has a valid period.
509 if (timer.period != etl::timer::state::INACTIVE)
510 {
511 ETL_DISABLE_TIMER_UPDATES;
512 if (timer.is_active())
513 {
514 active_list.remove(timer.id, false);
515 }
516
517 timer.delta = immediate_ ? 0 : timer.period;
518 active_list.insert(timer.id);
519 ETL_ENABLE_TIMER_UPDATES;
520
521 result = true;
522 }
523 }
524 }
525
526 return result;
527 }
528
529 //*******************************************
531 //*******************************************
532 bool stop(etl::timer::id::type id_)
533 {
534 bool result = false;
535
536 // Valid timer id?
537 if (id_ != etl::timer::id::NO_TIMER)
538 {
539 etl::message_timer_data& timer = timer_array[id_];
540
541 // Registered timer?
542 if (timer.id != etl::timer::id::NO_TIMER)
543 {
544 if (timer.is_active())
545 {
546 ETL_DISABLE_TIMER_UPDATES;
547 active_list.remove(timer.id, false);
548 ETL_ENABLE_TIMER_UPDATES;
549 }
550
551 result = true;
552 }
553 }
554
555 return result;
556 }
557
558 //*******************************************
560 //*******************************************
561 bool set_period(etl::timer::id::type id_, uint32_t period_)
562 {
563 if (stop(id_))
564 {
565 timer_array[id_].period = period_;
566 return true;
567 }
568
569 return false;
570 }
571
572 //*******************************************
574 //*******************************************
575 bool set_mode(etl::timer::id::type id_, bool repeating_)
576 {
577 if (stop(id_))
578 {
579 timer_array[id_].repeating = repeating_;
580 return true;
581 }
582
583 return false;
584 }
585
586 protected:
587
588 //*******************************************
590 //*******************************************
591 imessage_timer(message_timer_data* const timer_array_, const uint_least8_t MAX_TIMERS_)
592 : timer_array(timer_array_),
593 active_list(timer_array_),
594 enabled(false),
595#if defined(ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK)
596 process_semaphore(0),
597#endif
598 registered_timers(0),
599 MAX_TIMERS(MAX_TIMERS_)
600 {
601 }
602
603 //*******************************************
605 //*******************************************
607 {
608 }
609
610 private:
611
612 // The array of timer data structures.
613 message_timer_data* const timer_array;
614
615 // The list of active timers.
616 private_message_timer::list active_list;
617
618 bool enabled;
619
620#if defined(ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK)
621
622#if defined(ETL_TIMER_SEMAPHORE_TYPE)
623 typedef ETL_TIMER_SEMAPHORE_TYPE timer_semaphore_t;
624#else
625 #if ETL_HAS_ATOMIC
626 typedef etl::atomic_uint16_t timer_semaphore_t;
627 #else
628 #error No atomic type available
629 #endif
630#endif
631
632 etl::timer_semaphore_t process_semaphore;
633#endif
634 uint_least8_t registered_timers;
635
636 public:
637
638 const uint_least8_t MAX_TIMERS;
639 };
640
641 //***************************************************************************
643 //***************************************************************************
644 template <uint_least8_t MAX_TIMERS_>
646 {
647 public:
648
649 ETL_STATIC_ASSERT(MAX_TIMERS_ <= 254, "No more than 254 timers are allowed");
650
651 //*******************************************
653 //*******************************************
655 : imessage_timer(timer_array, MAX_TIMERS_)
656 {
657 }
658
659 private:
660
661 message_timer_data timer_array[MAX_TIMERS_];
662 };
663}
664
665#undef ETL_DISABLE_TIMER_UPDATES
666#undef ETL_ENABLE_TIMER_UPDATES
667#undef ETL_TIMER_UPDATES_ENABLED
668
669#endif
This is the base of all message routers.
Definition: message_router_generator.h:121
Interface for message timer.
Definition: message_timer.h:335
void enable(bool state_)
Enable/disable the timer.
Definition: message_timer.h:410
bool start(etl::timer::id::type id_, bool immediate_=false)
Starts a timer.
Definition: message_timer.h:496
bool unregister_timer(etl::timer::id::type id_)
Unregister a timer.
Definition: message_timer.h:379
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:341
bool is_running() const
Get the enable/disable state.
Definition: message_timer.h:418
bool set_mode(etl::timer::id::type id_, bool repeating_)
Sets a timer's mode.
Definition: message_timer.h:575
imessage_timer(message_timer_data *const timer_array_, const uint_least8_t MAX_TIMERS_)
Constructor.
Definition: message_timer.h:591
void clear()
Clears the timer of data.
Definition: message_timer.h:426
bool stop(etl::timer::id::type id_)
Stops a timer.
Definition: message_timer.h:532
~imessage_timer()
Destructor.
Definition: message_timer.h:606
bool set_period(etl::timer::id::type id_, uint32_t period_)
Sets a timer's period.
Definition: message_timer.h:561
Definition: message.h:69
The message timer.
Definition: message_timer.h:646
message_timer()
Constructor.
Definition: message_timer.h:654
A specialised intrusive linked list for timer data.
Definition: message_timer.h:156
bitset_ext
Definition: absolute.h:38
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
Definition: timer.h:82
Common definitions for the timer framework.
Definition: timer.h:55