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 "algorithm.h"
34#include "atomic.h"
35#include "delegate.h"
36#include "message.h"
37#include "message_bus.h"
38#include "message_router.h"
39#include "message_types.h"
40#include "nullptr.h"
41#include "static_assert.h"
42#include "timer.h"
43
44#include <stdint.h>
45
46#if defined(ETL_IN_UNIT_TEST) && ETL_NOT_USING_STL
47 #define ETL_DISABLE_TIMER_UPDATES ((void)0)
48 #define ETL_ENABLE_TIMER_UPDATES ((void)0)
49 #define ETL_TIMER_UPDATES_ENABLED true
50
51 #undef ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK
52 #undef ETL_MESSAGE_TIMER_USE_INTERRUPT_LOCK
53#else
54 #if !defined(ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK) && !defined(ETL_MESSAGE_TIMER_USE_INTERRUPT_LOCK)
55 #error ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK or ETL_MESSAGE_TIMER_USE_INTERRUPT_LOCK not defined
56 #endif
57
58 #if defined(ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK) && defined(ETL_MESSAGE_TIMER_USE_INTERRUPT_LOCK)
59 #error Only define one of ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK or ETL_MESSAGE_TIMER_USE_INTERRUPT_LOCK
60 #endif
61
62 #if defined(ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK)
63 #define ETL_DISABLE_TIMER_UPDATES (++process_semaphore)
64 #define ETL_ENABLE_TIMER_UPDATES (--process_semaphore)
65 #define ETL_TIMER_UPDATES_ENABLED (process_semaphore.load() == 0)
66 #endif
67
68 #if defined(ETL_MESSAGE_TIMER_USE_INTERRUPT_LOCK)
69 #if !defined(ETL_MESSAGE_TIMER_DISABLE_INTERRUPTS) || !defined(ETL_MESSAGE_TIMER_ENABLE_INTERRUPTS)
70 #error ETL_MESSAGE_TIMER_DISABLE_INTERRUPTS and/or ETL_MESSAGE_TIMER_ENABLE_INTERRUPTS not defined
71 #endif
72
73 #define ETL_DISABLE_TIMER_UPDATES ETL_MESSAGE_TIMER_DISABLE_INTERRUPTS
74 #define ETL_ENABLE_TIMER_UPDATES ETL_MESSAGE_TIMER_ENABLE_INTERRUPTS
75 #define ETL_TIMER_UPDATES_ENABLED true
76 #endif
77#endif
78
79namespace etl
80{
81 //*************************************************************************
83 struct message_timer_data
84 {
85 //*******************************************
86 message_timer_data()
87 : p_message(ETL_NULLPTR)
88 , p_router(ETL_NULLPTR)
89 , period(0)
90 , delta(etl::timer::state::Inactive)
91 , destination_router_id(etl::imessage_bus::ALL_MESSAGE_ROUTERS)
92 , id(etl::timer::id::NO_TIMER)
93 , previous(etl::timer::id::NO_TIMER)
94 , next(etl::timer::id::NO_TIMER)
95 , repeating(true)
96 {
97 }
98
99 //*******************************************
100 message_timer_data(etl::timer::id::type id_, const etl::imessage& message_, etl::imessage_router& irouter_, uint32_t period_, bool repeating_,
101 etl::message_router_id_t destination_router_id_ = etl::imessage_bus::ALL_MESSAGE_ROUTERS)
102 : p_message(&message_)
103 , p_router(&irouter_)
104 , period(period_)
105 , delta(etl::timer::state::Inactive)
106 , destination_router_id(destination_router_id_)
107 , id(id_)
108 , previous(etl::timer::id::NO_TIMER)
109 , next(etl::timer::id::NO_TIMER)
110 , repeating(repeating_)
111 {
112 }
113
114 //*******************************************
116 //*******************************************
117 bool is_active() const
118 {
119 return delta != etl::timer::state::Inactive;
120 }
121
122 //*******************************************
124 //*******************************************
126 {
127 delta = etl::timer::state::Inactive;
128 }
129
130 const etl::imessage* p_message;
131 etl::imessage_router* p_router;
132 uint32_t period;
133 uint32_t delta;
134 etl::message_router_id_t destination_router_id;
135 etl::timer::id::type id;
136 uint_least8_t previous;
137 uint_least8_t next;
138 bool repeating;
139
140 private:
141
142 // Disabled.
144 message_timer_data& operator=(const message_timer_data& other);
145 };
146
147 namespace private_message_timer
148 {
149 //*************************************************************************
151 //*************************************************************************
152 class list
153 {
154 public:
155
156 //*******************************
157 list(etl::message_timer_data* ptimers_)
158 : head(etl::timer::id::NO_TIMER)
159 , tail(etl::timer::id::NO_TIMER)
160 , ptimers(ptimers_)
161 {
162 }
163
164 //*******************************
165 bool empty() const
166 {
167 return head == etl::timer::id::NO_TIMER;
168 }
169
170 //*******************************
171 // Inserts the timer at the correct delta position
172 //*******************************
173 void insert(etl::timer::id::type id_)
174 {
175 etl::message_timer_data& timer = ptimers[id_];
176
177 if (head == etl::timer::id::NO_TIMER)
178 {
179 // No entries yet.
180 head = id_;
181 tail = id_;
182 timer.previous = etl::timer::id::NO_TIMER;
183 timer.next = etl::timer::id::NO_TIMER;
184 }
185 else
186 {
187 // We already have entries.
188 etl::timer::id::type test_id = begin();
189
190 while (test_id != etl::timer::id::NO_TIMER)
191 {
192 etl::message_timer_data& test = ptimers[test_id];
193
194 // Find the correct place to insert.
195 if (timer.delta <= test.delta)
196 {
197 if (test.id == head)
198 {
199 head = timer.id;
200 }
201
202 // Insert before test.
203 timer.previous = test.previous;
204 test.previous = timer.id;
205 timer.next = test.id;
206
207 // Adjust the next delta to compensate.
208 test.delta -= timer.delta;
209
210 if (timer.previous != etl::timer::id::NO_TIMER)
211 {
212 ptimers[timer.previous].next = timer.id;
213 }
214 break;
215 }
216 else
217 {
218 timer.delta -= test.delta;
219 }
220
221 test_id = next(test_id);
222 }
223
224 // Reached the end?
225 if (test_id == etl::timer::id::NO_TIMER)
226 {
227 // Tag on to the tail.
228 ptimers[tail].next = timer.id;
229 timer.previous = tail;
230 timer.next = etl::timer::id::NO_TIMER;
231 tail = timer.id;
232 }
233 }
234 }
235
236 //*******************************
237 void remove(etl::timer::id::type id_, bool has_expired)
238 {
239 etl::message_timer_data& timer = ptimers[id_];
240
241 if (head == id_)
242 {
243 head = timer.next;
244 }
245 else
246 {
247 ptimers[timer.previous].next = timer.next;
248 }
249
250 if (tail == id_)
251 {
252 tail = timer.previous;
253 }
254 else
255 {
256 ptimers[timer.next].previous = timer.previous;
257 }
258
259 if (!has_expired)
260 {
261 // Adjust the next delta.
262 if (timer.next != etl::timer::id::NO_TIMER)
263 {
264 ptimers[timer.next].delta += timer.delta;
265 }
266 }
267
268 timer.previous = etl::timer::id::NO_TIMER;
269 timer.next = etl::timer::id::NO_TIMER;
270 timer.delta = etl::timer::state::Inactive;
271 }
272
273 //*******************************
275 {
276 return ptimers[head];
277 }
278
279 //*******************************
280 const etl::message_timer_data& front() const
281 {
282 return ptimers[head];
283 }
284
285 //*******************************
286 etl::timer::id::type begin()
287 {
288 return head;
289 }
290
291 //*******************************
292 etl::timer::id::type next(etl::timer::id::type last)
293 {
294 return ptimers[last].next;
295 }
296
297 //*******************************
298 void clear()
299 {
300 etl::timer::id::type id = begin();
301
302 while (id != etl::timer::id::NO_TIMER)
303 {
304 etl::message_timer_data& timer = ptimers[id];
305 id = next(id);
306 timer.next = etl::timer::id::NO_TIMER;
307 }
308
309 head = etl::timer::id::NO_TIMER;
310 tail = etl::timer::id::NO_TIMER;
311 }
312
313 private:
314
315 etl::timer::id::type head;
316 etl::timer::id::type tail;
317
318 etl::message_timer_data* const ptimers;
319 };
320 } // namespace private_message_timer
321
322 //***************************************************************************
324 //***************************************************************************
326 {
327 public:
328
329 typedef etl::delegate<void(etl::timer::id::type)> event_callback_type;
330
331 //*******************************************
333 //*******************************************
334 etl::timer::id::type register_timer(const etl::imessage& message_, etl::imessage_router& router_, uint32_t period_, bool repeating_,
335 etl::message_router_id_t destination_router_id_ = etl::imessage_router::ALL_MESSAGE_ROUTERS)
336 {
337 etl::timer::id::type id = etl::timer::id::NO_TIMER;
338
339 bool is_space = (registered_timers < Max_Timers);
340
341 if (is_space)
342 {
343 // There's no point adding null message routers.
344 if (!router_.is_null_router())
345 {
346 // Search for the free space.
347 for (uint_least8_t i = 0U; i < Max_Timers; ++i)
348 {
349 etl::message_timer_data& timer = timer_array[i];
350
351 if (timer.id == etl::timer::id::NO_TIMER)
352 {
353 // Create in-place.
354 new (&timer) message_timer_data(i, message_, router_, period_, repeating_, destination_router_id_);
355 ++registered_timers;
356 id = i;
357 break;
358 }
359 }
360 }
361 }
362
363 return id;
364 }
365
366 //*******************************************
368 //*******************************************
369 bool unregister_timer(etl::timer::id::type id_)
370 {
371 bool result = false;
372
373 if (id_ != etl::timer::id::NO_TIMER)
374 {
375 etl::message_timer_data& timer = timer_array[id_];
376
377 if (timer.id != etl::timer::id::NO_TIMER)
378 {
379 if (timer.is_active())
380 {
381 ETL_DISABLE_TIMER_UPDATES;
382 active_list.remove(timer.id, false);
383 remove_callback.call_if(timer.id);
384 ETL_ENABLE_TIMER_UPDATES;
385 }
386
387 // Reset in-place.
388 new (&timer) message_timer_data();
389 --registered_timers;
390
391 result = true;
392 }
393 }
394
395 return result;
396 }
397
398 //*******************************************
400 //*******************************************
401 void enable(bool state_)
402 {
403 enabled = state_;
404 }
405
406 //*******************************************
408 //*******************************************
409 bool is_running() const
410 {
411 return enabled;
412 }
413
414 //*******************************************
416 //*******************************************
417 void clear()
418 {
419 ETL_DISABLE_TIMER_UPDATES;
420 active_list.clear();
421 ETL_ENABLE_TIMER_UPDATES;
422
423 for (int i = 0; i < Max_Timers; ++i)
424 {
425 new (&timer_array[i]) message_timer_data();
426 }
427
428 registered_timers = 0;
429 }
430
431 //*******************************************
432 // Called by the timer service to indicate the
433 // amount of time that has elapsed since the last successful call to 'tick'.
434 // Returns true if the tick was processed,
435 // false if not.
436 //*******************************************
437 bool tick(uint32_t count)
438 {
439 if (enabled)
440 {
441 if (ETL_TIMER_UPDATES_ENABLED)
442 {
443 // We have something to do?
444 bool has_active = !active_list.empty();
445
446 if (has_active)
447 {
448 while (has_active && (count >= active_list.front().delta))
449 {
450 etl::message_timer_data& timer = active_list.front();
451
452 count -= timer.delta;
453
454 active_list.remove(timer.id, true);
455 remove_callback.call_if(timer.id);
456
457 if (timer.repeating)
458 {
459 timer.delta = timer.period;
460 active_list.insert(timer.id);
461 insert_callback.call_if(timer.id);
462 }
463
464 if (timer.p_router != ETL_NULLPTR)
465 {
466 timer.p_router->receive(timer.destination_router_id, *(timer.p_message));
467 }
468
469 has_active = !active_list.empty();
470 }
471
472 if (has_active)
473 {
474 // Subtract any remainder from the next due timeout.
475 active_list.front().delta -= count;
476 }
477 }
478
479 return true;
480 }
481 }
482
483 return false;
484 }
485
486 //*******************************************
488 //*******************************************
489 bool start(etl::timer::id::type id_, bool immediate_ = false)
490 {
491 bool result = false;
492
493 // Valid timer id?
494 if (id_ != etl::timer::id::NO_TIMER)
495 {
496 etl::message_timer_data& timer = timer_array[id_];
497
498 // Registered timer?
499 if (timer.id != etl::timer::id::NO_TIMER)
500 {
501 // Has a valid period.
502 if (timer.period != etl::timer::state::Inactive)
503 {
504 ETL_DISABLE_TIMER_UPDATES;
505 if (timer.is_active())
506 {
507 active_list.remove(timer.id, false);
508 remove_callback.call_if(timer.id);
509 }
510
511 timer.delta = immediate_ ? 0 : timer.period;
512 active_list.insert(timer.id);
513 insert_callback.call_if(timer.id);
514 ETL_ENABLE_TIMER_UPDATES;
515
516 result = true;
517 }
518 }
519 }
520
521 return result;
522 }
523
524 //*******************************************
526 //*******************************************
527 bool stop(etl::timer::id::type id_)
528 {
529 bool result = false;
530
531 // Valid timer id?
532 if (id_ != etl::timer::id::NO_TIMER)
533 {
534 etl::message_timer_data& timer = timer_array[id_];
535
536 // Registered timer?
537 if (timer.id != etl::timer::id::NO_TIMER)
538 {
539 if (timer.is_active())
540 {
541 ETL_DISABLE_TIMER_UPDATES;
542 active_list.remove(timer.id, false);
543 remove_callback.call_if(timer.id);
544 ETL_ENABLE_TIMER_UPDATES;
545 }
546
547 result = true;
548 }
549 }
550
551 return result;
552 }
553
554 //*******************************************
556 //*******************************************
557 bool set_period(etl::timer::id::type id_, uint32_t period_)
558 {
559 if (stop(id_))
560 {
561 timer_array[id_].period = period_;
562 return true;
563 }
564
565 return false;
566 }
567
568 //*******************************************
570 //*******************************************
571 bool set_mode(etl::timer::id::type id_, bool repeating_)
572 {
573 if (stop(id_))
574 {
575 timer_array[id_].repeating = repeating_;
576 return true;
577 }
578
579 return false;
580 }
581
582 //*******************************************
584 //*******************************************
585 bool has_active_timer() const
586 {
587 ETL_DISABLE_TIMER_UPDATES;
588 bool result = !active_list.empty();
589 ETL_ENABLE_TIMER_UPDATES;
590
591 return result;
592 }
593
594 //*******************************************
598 //*******************************************
599 uint32_t time_to_next() const
600 {
601 uint32_t delta = static_cast<uint32_t>(etl::timer::interval::No_Active_Interval);
602
603 ETL_DISABLE_TIMER_UPDATES;
604 if (!active_list.empty())
605 {
606 delta = active_list.front().delta;
607 }
608 ETL_ENABLE_TIMER_UPDATES;
609
610 return delta;
611 }
612
613 //*******************************************
615 //*******************************************
616 void set_insert_callback(event_callback_type insert_)
617 {
618 insert_callback = insert_;
619 }
620
621 //*******************************************
623 //*******************************************
624 void set_remove_callback(event_callback_type remove_)
625 {
626 remove_callback = remove_;
627 }
628
629 //*******************************************
630 void clear_insert_callback()
631 {
632 insert_callback.clear();
633 }
634
635 //*******************************************
636 void clear_remove_callback()
637 {
638 remove_callback.clear();
639 }
640
641 protected:
642
643 //*******************************************
645 //*******************************************
646 imessage_timer(message_timer_data* const timer_array_, const uint_least8_t Max_Timers_)
647 : timer_array(timer_array_)
648 , active_list(timer_array_)
649 , enabled(false)
650 ,
651#if defined(ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK)
652 process_semaphore(0)
653 ,
654#endif
655 registered_timers(0)
656 , Max_Timers(Max_Timers_)
657 {
658 }
659
660 //*******************************************
662 //*******************************************
664
665 private:
666
667 // The array of timer data structures.
668 message_timer_data* const timer_array;
669
670 // The list of active timers.
671 private_message_timer::list active_list;
672
673 bool enabled;
674
675#if defined(ETL_MESSAGE_TIMER_USE_ATOMIC_LOCK)
676
677 #if defined(ETL_TIMER_SEMAPHORE_TYPE)
678 typedef ETL_TIMER_SEMAPHORE_TYPE timer_semaphore_t;
679 #else
680 #if ETL_HAS_ATOMIC
681 typedef etl::atomic_uint16_t timer_semaphore_t;
682 #else
683 #error No atomic type available
684 #endif
685 #endif
686
687 mutable etl::timer_semaphore_t process_semaphore;
688#endif
689 uint_least8_t registered_timers;
690
691 event_callback_type insert_callback;
692 event_callback_type remove_callback;
693
694 public:
695
696 const uint_least8_t Max_Timers;
697 };
698
699 //***************************************************************************
701 //***************************************************************************
702 template <uint_least8_t Max_Timers_>
704 {
705 public:
706
707 ETL_STATIC_ASSERT(Max_Timers_ <= 254, "No more than 254 timers are allowed");
708
709 //*******************************************
711 //*******************************************
713 : imessage_timer(timer_array, Max_Timers_)
714 {
715 }
716
717 private:
718
719 message_timer_data timer_array[Max_Timers_];
720 };
721} // namespace etl
722
723#undef ETL_DISABLE_TIMER_UPDATES
724#undef ETL_ENABLE_TIMER_UPDATES
725#undef ETL_TIMER_UPDATES_ENABLED
726
727#endif
Declaration.
Definition delegate_cpp03.h:191
This is the base of all message routers.
Definition message_router.h:141
Interface for message timer.
Definition message_timer.h:326
uint32_t time_to_next() const
Definition message_timer.h:599
bool has_active_timer() const
Check if there is an active timer.
Definition message_timer.h:585
void set_remove_callback(event_callback_type remove_)
Set a callback when a timer is removed from list.
Definition message_timer.h:624
void enable(bool state_)
Enable/disable the timer.
Definition message_timer.h:401
bool start(etl::timer::id::type id_, bool immediate_=false)
Starts a timer.
Definition message_timer.h:489
bool unregister_timer(etl::timer::id::type id_)
Unregister a timer.
Definition message_timer.h:369
imessage_timer(message_timer_data *const timer_array_, const uint_least8_t Max_Timers_)
Constructor.
Definition message_timer.h:646
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:334
bool is_running() const
Get the enable/disable state.
Definition message_timer.h:409
bool set_mode(etl::timer::id::type id_, bool repeating_)
Sets a timer's mode.
Definition message_timer.h:571
void set_insert_callback(event_callback_type insert_)
Set a callback when a timer is inserted on list.
Definition message_timer.h:616
void clear()
Clears the timer of data.
Definition message_timer.h:417
bool stop(etl::timer::id::type id_)
Stops a timer.
Definition message_timer.h:527
~imessage_timer()
Destructor.
Definition message_timer.h:663
bool set_period(etl::timer::id::type id_, uint32_t period_)
Sets a timer's period.
Definition message_timer.h:557
Definition message.h:73
message_timer()
Constructor.
Definition message_timer.h:712
A specialised intrusive linked list for timer data.
Definition message_timer.h:153
Definition absolute.h:40
The configuration of a timer.
Definition message_timer.h:84
bool is_active() const
Returns true if the timer is active.
Definition message_timer.h:117
void set_inactive()
Sets the timer to the inactive state.
Definition message_timer.h:125
Definition timer.h:88
Common definitions for the timer framework.
Definition timer.h:55