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