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