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