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, false);
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 , ptimers(ptimers_)
484 {
485 }
486
487 //*******************************
488 bool empty() const
489 {
490 return head == etl::timer::id::NO_TIMER;
491 }
492
493 //*******************************
494 // Inserts the timer at the correct delta position
495 //*******************************
496 void insert(etl::timer::id::type id_)
497 {
498 timer_data& timer = ptimers[id_];
499
500 if (head == etl::timer::id::NO_TIMER)
501 {
502 // No entries yet.
503 head = id_;
504 tail = id_;
505 timer.previous = etl::timer::id::NO_TIMER;
506 timer.next = etl::timer::id::NO_TIMER;
507 }
508 else
509 {
510 // We already have entries.
511 etl::timer::id::type test_id = begin();
512
513 while (test_id != etl::timer::id::NO_TIMER)
514 {
515 timer_data& test = ptimers[test_id];
516
517 // Find the correct place to insert.
518 if (timer.delta <= test.delta)
519 {
520 if (test.id == head)
521 {
522 head = timer.id;
523 }
524
525 // Insert before test.
526 timer.previous = test.previous;
527 test.previous = timer.id;
528 timer.next = test.id;
529
530 // Adjust the next delta to compensate.
531 test.delta -= timer.delta;
532
533 if (timer.previous != etl::timer::id::NO_TIMER)
534 {
535 ptimers[timer.previous].next = timer.id;
536 }
537 break;
538 }
539 else
540 {
541 timer.delta -= test.delta;
542 }
543
544 test_id = next(test_id);
545 }
546
547 // Reached the end?
548 if (test_id == etl::timer::id::NO_TIMER)
549 {
550 // Tag on to the tail.
551 ptimers[tail].next = timer.id;
552 timer.previous = tail;
553 timer.next = etl::timer::id::NO_TIMER;
554 tail = timer.id;
555 }
556 }
557 }
558
559 //*******************************
560 void remove(etl::timer::id::type id_, bool has_expired)
561 {
562 timer_data& timer = ptimers[id_];
563
564 if (head == id_)
565 {
566 head = timer.next;
567 }
568 else
569 {
570 ptimers[timer.previous].next = timer.next;
571 }
572
573 if (tail == id_)
574 {
575 tail = timer.previous;
576 }
577 else
578 {
579 ptimers[timer.next].previous = timer.previous;
580 }
581
582 if (!has_expired)
583 {
584 // Adjust the next delta.
585 if (timer.next != etl::timer::id::NO_TIMER)
586 {
587 ptimers[timer.next].delta += timer.delta;
588 }
589 }
590
591 timer.previous = etl::timer::id::NO_TIMER;
592 timer.next = etl::timer::id::NO_TIMER;
593 timer.delta = etl::timer::state::Inactive;
594 }
595
596 //*******************************
597 timer_data& front()
598 {
599 return ptimers[head];
600 }
601
602 //*******************************
603 const timer_data& front() const
604 {
605 return ptimers[head];
606 }
607
608 //*******************************
609 etl::timer::id::type begin()
610 {
611 return head;
612 }
613
614 //*******************************
615 etl::timer::id::type next(etl::timer::id::type last)
616 {
617 return ptimers[last].next;
618 }
619
620 //*******************************
621 void clear()
622 {
623 etl::timer::id::type id = begin();
624
625 while (id != etl::timer::id::NO_TIMER)
626 {
627 timer_data& timer = ptimers[id];
628 id = next(id);
629 timer.next = etl::timer::id::NO_TIMER;
630 }
631
632 head = etl::timer::id::NO_TIMER;
633 tail = etl::timer::id::NO_TIMER;
634 }
635
636 private:
637
638 etl::timer::id::type head;
639 etl::timer::id::type tail;
640
641 timer_data* const ptimers;
642 };
643
644 // The array of timer data structures.
645 timer_data* const timer_array;
646
647 // The list of active timers.
648 timer_list active_list;
649
650 bool enabled;
651 uint_least8_t number_of_registered_timers;
652
653 try_lock_type try_lock;
654 lock_type lock;
655 unlock_type unlock;
656
657 event_callback_type insert_callback;
658 event_callback_type remove_callback;
659
660 public:
661
662 const uint_least8_t Max_Timers;
663 };
664
665 //***************************************************************************
667 //***************************************************************************
668 template <uint_least8_t Max_Timers_>
670 {
671 public:
672
673 ETL_STATIC_ASSERT(Max_Timers_ <= 254, "No more than 254 timers are allowed");
674
675 typedef imessage_timer_locked::callback_type callback_type;
676 typedef imessage_timer_locked::try_lock_type try_lock_type;
677 typedef imessage_timer_locked::lock_type lock_type;
678 typedef imessage_timer_locked::unlock_type unlock_type;
679
680 //*******************************************
682 //*******************************************
684 : imessage_timer_locked(timer_array, Max_Timers_)
685 {
686 }
687
688 //*******************************************
690 //*******************************************
691 message_timer_locked(try_lock_type try_lock_, lock_type lock_, unlock_type unlock_)
692 : imessage_timer_locked(timer_array, Max_Timers_)
693 {
694 this->set_locks(try_lock_, lock_, unlock_);
695 }
696
697 private:
698
699 timer_data timer_array[Max_Timers_];
700 };
701} // namespace etl
702
703#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_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:73
message_timer_locked(try_lock_type try_lock_, lock_type lock_, unlock_type unlock_)
Constructor.
Definition message_timer_locked.h:691
message_timer_locked()
Constructor.
Definition message_timer_locked.h:683
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