forked from xvedra/TaskScheduler-custom-sleep-method
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTaskScheduler.h
More file actions
1225 lines (1021 loc) · 40.7 KB
/
TaskScheduler.h
File metadata and controls
1225 lines (1021 loc) · 40.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Cooperative multitasking library for Arduino
// Copyright (c) 2015-2019 Anatoli Arkhipenko
//
// Changelog:
// v1.0.0:
// 2015-02-24 - Initial release
// 2015-02-28 - added delay() and disableOnLastIteration() methods
// 2015-03-25 - changed scheduler execute() method for a more precise delay calculation:
// 1. Do not delay if any of the tasks ran (making request for immediate execution redundant)
// 2. Delay is invoked only if none of the tasks ran
// 3. Delay is based on the min anticipated wait until next task _AND_ the runtime of execute method itself.
// 2015-05-11 - added restart() and restartDelayed() methods to restart tasks which are on hold after running all iterations
// 2015-05-19 - completely removed delay from the scheduler since there are no power saving there. using 1 ms sleep instead
//
// v1.4.1:
// 2015-09-15 - more careful placement of AVR-specific includes for sleep method (compatibility with DUE)
// sleep on idle run is no longer a default and should be explicitly compiled with
// _TASK_SLEEP_ON_IDLE_RUN defined
//
// v1.5.0:
// 2015-09-20 - access to currently executing task (for callback methods)
// 2015-09-20 - pass scheduler as a parameter to the task constructor to append the task to the end of the chain
// 2015-09-20 - option to create a task already enabled
//
// v1.5.1:
// 2015-09-21 - bug fix: incorrect handling of active tasks via set() and setIterations().
// Thanks to Hannes Morgenstern for catching this one
//
// v1.6.0:
// 2015-09-22 - revert back to having all tasks disable on last iteration.
// 2015-09-22 - deprecated disableOnLastIteration method as a result
// 2015-09-22 - created a separate branch 'disable-on-last-iteration' for this
// 2015-10-01 - made version numbers semver compliant (documentation only)
//
// v1.7.0:
// 2015-10-08 - introduced callback run counter - callback methods can branch on the iteration number.
// 2015-10-11 - enableIfNot() - enable a task only if it is not already enabled. Returns true if was already enabled,
// false if was disabled.
// 2015-10-11 - disable() returns previous enable state (true if was enabled, false if was already disabled)
// 2015-10-11 - introduced callback methods "on enable" and "on disable". On enable runs every time enable is called,
// on disable runs only if task was enabled
// 2015-10-12 - new Task method: forceNextIteration() - makes next iteration happen immediately during the next pass
// regardless how much time is left
//
// v1.8.0:
// 2015-10-13 - support for status request objects allowing tasks waiting on requests
// 2015-10-13 - moved to a single header file to allow compilation control via #defines from the main sketch
//
// v1.8.1:
// 2015-10-22 - implement Task id and control points to support identification of failure points for watchdog timer logging
//
// v1.8.2:
// 2015-10-27 - implement Local Task Storage Pointer (allow use of same callback code for different tasks)
// 2015-10-27 - bug: currentTask() method returns incorrect Task reference if called within OnEnable and OnDisable methods
// 2015-10-27 - protection against infinite loop in OnEnable (if enable() methods are called within OnEnable)
// 2015-10-29 - new currentLts() method in the scheduler class returns current task's LTS pointer in one call
//
// v1.8.3:
// 2015-11-05 - support for task activation on a status request with arbitrary interval and number of iterations
// (0 and 1 are still default values)
// 2015-11-05 - implement waitForDelayed() method to allow task activation on the status request completion
// delayed for one current interval
// 2015-11-09 - added callback methods prototypes to all examples for Arduino IDE 1.6.6 compatibility
// 2015-11-14 - added several constants to be used as task parameters for readability (e.g, TASK_FOREVER, TASK_SECOND, etc.)
// 2015-11-14 - significant optimization of the scheduler's execute loop, including millis() rollover fix option
//
// v1.8.4:
// 2015-11-15 - bug fix: Task alignment with millis() for scheduling purposes should be done after OnEnable, not before.
// Especially since OnEnable method can change the interval
// 2015-11-16 - further optimizations of the task scheduler execute loop
//
// v1.8.5:
// 2015-11-23 - bug fix: incorrect calculation of next task invocation in case callback changed the interval
// 2015-11-23 - bug fix: Task::set() method calls setInterval() explicitly, therefore delaying the task in the same manner
//
// v1.9.0:
// 2015-11-24 - packed three byte-long status variables into bit array structure data type - saving 2 bytes per each task instance
//
// v1.9.2:
// 2015-11-28 - _TASK_ROLLOVER_FIX is deprecated (not necessary)
// 2015-12-16 - bug fixes: automatic millis rollover support for delay methods
// 2015-12-17 - new method for _TASK_TIMECRITICAL option: getStartDelay()
//
// v2.0.0:
// 2015-12-22 - _TASK_PRIORITY - support for layered task prioritization
//
// v2.0.1:
// 2016-01-02 - bug fix: issue#11 Xtensa compiler (esp8266): Declaration of constructor does not match implementation
//
// v2.0.2:
// 2016-01-05 - bug fix: time constants wrapped inside compile option
// 2016-01-05 - support for ESP8266 wifi power saving mode for _TASK_SLEEP_ON_IDLE_RUN compile option
//
// v2.1.0:
// 2016-02-01 - support for microsecond resolution
// 2016-02-02 - added Scheduler baseline start time reset method: startNow()
//
// v2.2.0:
// 2016-11-17 - all methods made 'inline' to support inclusion of TaskSchedule.h file into other header files
//
// v2.2.1:
// 2016-11-30 - inlined constructors. Added "yield()" and "yieldOnce()" functions to easily break down and chain
// back together long running callback methods
// 2016-12-16 - added "getCount()" to StatusRequest objects, made every task StatusRequest enabled.
// Internal StatusRequest objects are accessible via "getInternalStatusRequest()" method.
//
// v2.3.0:
// 2017-02-24 - new timeUntilNextIteration() method within Scheduler class - inquire when a particlar task is
// scheduled to run next time
//
// v2.4.0:
// 2017-04-27 - added destructor to the Task class to ensure tasks are disables and taken off the execution chain
// upon destruction. (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder)
//
// v2.5.0:
// 2017-04-27 - ESP8266 ONLY: added optional support for std::functions via _TASK_STD_FUNCTION compilation option
// (Contributed by Edwin van Leeuwen [BlackEdder - https://github.com/BlackEdder)
// 2017-08-30 - add _TASK_DEBUG making all methods and variables public FOR DEBUGGING PURPOSES ONLY!
// Use at your own risk!
// 2017-08-30 - bug fix: Scheduler::addTask() checks if task is already part of an execution chain (github issue #37)
// 2017-08-30 - support for multi-tab sketches (Contributed by Adam Ryczkowski - https://github.com/adamryczkowski)
//
// v2.5.1:
// 2018-01-06 - support for IDLE sleep on Teensy boards (tested on Teensy 3.5)
//
// v2.5.2:
// 2018-01-09 - _TASK_INLINE compilation directive making all methods declared "inline" (issue #42)
//
// v2.6.0:
// 2018-01-30 - _TASK_TIMEOUT compilation directive: Task overall timeout functionality
// 2018-01-30 - ESP32 support (experimental)
// (Contributed by Marco Tombesi: https://github.com/baggior)
//
// v2.6.1:
// 2018-02-13 - Bug: support for task self-destruction in the OnDisable method
// Example 19: dynamic tasks creation and destruction
// 2018-03-14 - Bug: high level scheduler ignored if lower level chain is empty
// Example 20: use of local task storage to work with task-specific class objects
//
// v3.0.0:
// 2018-03-15 - Major Release: Support for dynamic callback methods binding via compilation parameter _TASK_OO_CALLBACKS
//
// v3.0.1:
// 2018-11-09 - bug: task deleted from the execution chain cannot be added back (github issue #67)
//
// v3.0.2:
// 2018-11-11 - bug: default constructor is ambiguous when Status Request objects are enabled (github issue #65 & #68)
//
// v3.0.3:
// 2019-06-13 - feature: custom sleep callback method: setSleepMethod() - ability to dynamically control idle sleep for various microcontrollers
// - feature: support for MSP430 and MSP432 boards (pull request #75: big thanks to Guillaume Pirou, https://github.com/elominp)
// - officially discontinued support for offile documentation in favor of updating the Wiki pages
//
// v3.1.0:
// 2020-01-07 - feature: added 4 cpu load monitoring methods for _TASK_TIMECRITICAL compilation option
//
// v3.1.1:
// 2020-01-09 - update: more precise CPU load measuring. Ability to define idle sleep threshold for ESP chips
//
// v3.1.2:
// 2020-01-17 - bug fix: corrected external forward definitions of millis() and micros
//
// v3.1.3:
// 2020-01-30 - bug fix: _TASK_DEFINE_MILLIS to force forward definition of millis and micros. Not defined by default.
// 2020-02-16 - bug fix: add 'virtual' to the Task destructor definition (issue #86)
//
// v3.1.4:
// 2020-02-22 - bug: get rid of unnecessary compiler warnings
// 2020-02-22 - feature: access to the task chain with _TASK_EXPOSE_CHAIN compile option
//
// v3.1.5:
// 2020-05-08 - feature: implemented light sleep for esp32
//
// v3.1.6:
// 2020-05-12 - bug fix: deleteTask and addTask should check task ownership first (Issue #97)
//
// v3.1.7:
// 2020-07-07 - warning fix: unused parameter 'aRecursive' (Issue #99)
//
// v3.2.0:
// 2020-08-16 - feature: scheduling options
//
// v3.2.1:
// 2020-10-04 - feature: Task.abort method. Stop task execution without calling OnDisable().
//
// v3.2.2:
// 2020-12-14 - feature: enable and restart methods return true if task enabled
// feature: Task.cancel() method - disable task with a cancel flag (could be used for alt. path
// processing in the onDisable method.
// feature: Task.cancelled() method - indicates that task was disabled with a cancel() method.
//
// v3.2.3:
// 2021-01-01 - feature: discontinued use of 'register' keyword. Depricated in C++ 11
// feature: add STM32 as a platform supporting _TASK_STD_FUNCTION. (PR #105)
//
// v3.3.0:
// 2021-05-11 - feature: Timeout() methods for StatusRequest objects
//
// v3.4.0:
// 2021-07-14 - feature: ability to Enable/Disable and Pause/Resume scheduling
// - feature: optional use of external millis/micros methods
#include <Arduino.h>
#ifdef _TASK_DEFINE_MILLIS
extern "C" {
unsigned long micros(void);
unsigned long millis(void);
}
#endif
#include "TaskSchedulerDeclarations.h"
#ifndef _TASKSCHEDULER_H_
#define _TASKSCHEDULER_H_
// ----------------------------------------
// The following "defines" control library functionality at compile time,
// and should be used in the main sketch depending on the functionality required
//
// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns
// #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between runs if no callback methods were invoked during the pass
// #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer
// #define _TASK_PRIORITY // Support for layered scheduling priority
// #define _TASK_MICRO_RES // Support for microsecond resolution
// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY)
// #define _TASK_DEBUG // Make all methods and variables public for debug purposes
// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations
// #define _TASK_TIMEOUT // Support for overall task timeout
// #define _TASK_OO_CALLBACKS // Support for callbacks via inheritance
// #define _TASK_EXPOSE_CHAIN // Methods to access tasks in the task chain
// #define _TASK_SCHEDULING_OPTIONS // Support for multiple scheduling options
// #define _TASK_DEFINE_MILLIS // Force forward declaration of millis() and micros() "C" style
// #define _TASK_EXTERNAL_TIME // Custom millis() and micros() methods
#ifdef _TASK_MICRO_RES
#undef _TASK_SLEEP_ON_IDLE_RUN // SLEEP_ON_IDLE has only millisecond resolution
#define _TASK_TIME_FUNCTION() _task_micros()
#else
#define _TASK_TIME_FUNCTION() _task_millis()
#endif // _TASK_MICRO_RES
#ifdef _TASK_SLEEP_ON_IDLE_RUN
#include "TaskSchedulerSleepMethods.h"
Scheduler* iSleepScheduler;
SleepCallback iSleepMethod;
#endif // _TASK_SLEEP_ON_IDLE_RUN
#if !defined (ARDUINO_ARCH_ESP8266) && !defined (ARDUINO_ARCH_ESP32) && !defined (ARDUINO_ARCH_STM32)
#ifdef _TASK_STD_FUNCTION
#error Support for std::function only for ESP8266 or ESP32 architecture
#undef _TASK_STD_FUNCTION
#endif // _TASK_STD_FUNCTION
#endif // ARDUINO_ARCH_ESP8266
#ifdef _TASK_WDT_IDS
static unsigned int __task_id_counter = 0; // global task ID counter for assiging task IDs automatically.
#endif // _TASK_WDT_IDS
#ifdef _TASK_PRIORITY
Scheduler* iCurrentScheduler;
#endif // _TASK_PRIORITY
// ------------------ TaskScheduler implementation --------------------
#ifndef _TASK_EXTERNAL_TIME
static uint32_t _task_millis() {return millis();}
static uint32_t _task_micros() {return micros();}
#endif // _TASK_EXTERNAL_TIME
/** Constructor, uses default values for the parameters
* so could be called with no parameters.
*/
#ifdef _TASK_OO_CALLBACKS
Task::Task( unsigned long aInterval, long aIterations, Scheduler* aScheduler, bool aEnable ) {
reset();
set(aInterval, aIterations);
#else
Task::Task( unsigned long aInterval, long aIterations, TaskCallback aCallback, Scheduler* aScheduler, bool aEnable, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable ) {
reset();
set(aInterval, aIterations, aCallback, aOnEnable, aOnDisable);
#endif
if (aScheduler) aScheduler->addTask(*this);
#ifdef _TASK_WDT_IDS
iTaskID = ++__task_id_counter;
#endif // _TASK_WDT_IDS
if (aEnable) enable();
}
/** Destructor.
* Makes sure the task disabled and deleted out of the chain
* prior to being deleted.
*/
Task::~Task() {
disable();
if (iScheduler)
iScheduler->deleteTask(*this);
}
#ifdef _TASK_STATUS_REQUEST
/** Constructor with reduced parameter list for tasks created for
* StatusRequest only triggering (always immediate and only 1 iteration)
*/
#ifdef _TASK_OO_CALLBACKS
Task::Task( Scheduler* aScheduler ) {
reset();
set(TASK_IMMEDIATE, TASK_ONCE);
#else
Task::Task( TaskCallback aCallback, Scheduler* aScheduler, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable ) {
reset();
set(TASK_IMMEDIATE, TASK_ONCE, aCallback, aOnEnable, aOnDisable);
#endif // _TASK_OO_CALLBACKS
if (aScheduler) aScheduler->addTask(*this);
#ifdef _TASK_WDT_IDS
iTaskID = ++__task_id_counter;
#endif // _TASK_WDT_IDS
}
StatusRequest::StatusRequest()
{
iCount = 0;
iStatus = 0;
}
void StatusRequest::setWaiting(unsigned int aCount) {
iCount = aCount;
iStatus = 0;
#ifdef _TASK_TIMEOUT
iStarttime = _TASK_TIME_FUNCTION();
#endif // #ifdef _TASK_TIMEOUT
}
bool StatusRequest::pending() { return (iCount != 0); }
bool StatusRequest::completed() { return (iCount == 0); }
int StatusRequest::getStatus() { return iStatus; }
int StatusRequest::getCount() { return iCount; }
StatusRequest* Task::getStatusRequest() { return iStatusRequest; }
StatusRequest* Task::getInternalStatusRequest() { return &iMyStatusRequest; }
/** Signals completion of the StatusRequest by one of the participating events
* @param: aStatus - if provided, sets the return code of the StatusRequest: negative = error, 0 (default) = OK, positive = OK with a specific status code
* Negative status will complete Status Request fully (since an error occured).
* @return: true, if StatusRequest is complete, false otherwise (still waiting for other events)
*/
bool StatusRequest::signal(int aStatus) {
if ( iCount) { // do not update the status request if it was already completed
if (iCount > 0) --iCount;
if ( (iStatus = aStatus) < 0 ) iCount = 0; // if an error is reported, the status is requested to be completed immediately
}
return (iCount == 0);
}
void StatusRequest::signalComplete(int aStatus) {
if (iCount) { // do not update the status request if it was already completed
iCount = 0;
iStatus = aStatus;
}
}
/** Sets a Task to wait until a particular event completes
* @param: aStatusRequest - a pointer for the StatusRequest to wait for.
* If aStatusRequest is NULL, request for waiting is ignored, and the waiting task is not enabled.
*/
bool Task::waitFor(StatusRequest* aStatusRequest, unsigned long aInterval, long aIterations) {
iStatusRequest = aStatusRequest;
if ( iStatusRequest != NULL ) { // assign internal StatusRequest var and check if it is not NULL
setIterations(aIterations);
setInterval(aInterval);
iStatus.waiting = _TASK_SR_NODELAY; // no delay
return enable();
}
return false;
}
bool Task::waitForDelayed(StatusRequest* aStatusRequest, unsigned long aInterval, long aIterations) {
iStatusRequest = aStatusRequest;
if ( iStatusRequest != NULL ) { // assign internal StatusRequest var and check if it is not NULL
setIterations(aIterations);
if ( aInterval ) setInterval(aInterval); // For the dealyed version only set the interval if it was not a zero
iStatus.waiting = _TASK_SR_DELAY; // with delay equal to the current interval
return enable();
}
return false;
}
#ifdef _TASK_TIMEOUT
void StatusRequest::resetTimeout() {
iStarttime = _TASK_TIME_FUNCTION();
}
long StatusRequest::untilTimeout() {
if ( iTimeout ) {
return ( (long) (iStarttime + iTimeout) - (long) _TASK_TIME_FUNCTION() );
}
return -1;
}
#endif // _TASK_TIMEOUT
#endif // _TASK_STATUS_REQUEST
bool Task::isEnabled() { return iStatus.enabled; }
unsigned long Task::getInterval() { return iInterval; }
long Task::getIterations() { return iIterations; }
unsigned long Task::getRunCounter() { return iRunCounter; }
#ifdef _TASK_OO_CALLBACKS
// bool Task::Callback() { return true; }
bool Task::OnEnable() { return true; }
void Task::OnDisable() { }
#else
void Task::setCallback(TaskCallback aCallback) { iCallback = aCallback; }
void Task::setOnEnable(TaskOnEnable aCallback) { iOnEnable = aCallback; }
void Task::setOnDisable(TaskOnDisable aCallback) { iOnDisable = aCallback; }
#endif // _TASK_OO_CALLBACKS
/** Resets (initializes) the task/
* Task is not enabled and is taken out
* out of the execution chain as a result
*/
void Task::reset() {
iStatus.enabled = false;
iStatus.inonenable = false;
iStatus.canceled = false;
iPreviousMillis = 0;
iInterval = iDelay = 0;
iPrev = NULL;
iNext = NULL;
iScheduler = NULL;
iRunCounter = 0;
#ifdef _TASK_SCHEDULING_OPTIONS
iOption = TASK_SCHEDULE;
#endif // _TASK_SCHEDULING_OPTIONS
#ifdef _TASK_TIMECRITICAL
iOverrun = 0;
iStartDelay = 0;
#endif // _TASK_TIMECRITICAL
#ifdef _TASK_WDT_IDS
iControlPoint = 0;
#endif // _TASK_WDT_IDS
#ifdef _TASK_LTS_POINTER
iLTS = NULL;
#endif // _TASK_LTS_POINTER
#ifdef _TASK_STATUS_REQUEST
iStatusRequest = NULL;
iStatus.waiting = 0;
iMyStatusRequest.signalComplete();
#endif // _TASK_STATUS_REQUEST
#ifdef _TASK_TIMEOUT
iTimeout = 0;
iStarttime = 0;
iStatus.timeout = false;
#endif // _TASK_TIMEOUT
}
/** Explicitly set Task execution parameters
* @param aInterval - execution interval in ms
* @param aIterations - number of iterations, use -1 for no limit
* @param aCallback - pointer to the callback method which executes the task actions
* @param aOnEnable - pointer to the callback method which is called on enable()
* @param aOnDisable - pointer to the callback method which is called on disable()
*/
#ifdef _TASK_OO_CALLBACKS
void Task::set(unsigned long aInterval, long aIterations) {
#else
void Task::set(unsigned long aInterval, long aIterations, TaskCallback aCallback, TaskOnEnable aOnEnable, TaskOnDisable aOnDisable) {
iCallback = aCallback;
iOnEnable = aOnEnable;
iOnDisable = aOnDisable;
#endif // _TASK_OO_CALLBACKS
setInterval(aInterval);
iSetIterations = iIterations = aIterations;
}
/** Sets number of iterations for the task
* if task is enabled, schedule for immediate execution
* @param aIterations - number of iterations, use -1 for no limit
*/
void Task::setIterations(long aIterations) {
iSetIterations = iIterations = aIterations;
}
#ifndef _TASK_OO_CALLBACKS
/** Prepare task for next step iteration following yielding of control to the scheduler
* @param aCallback - pointer to the callback method for the next step
*/
void Task::yield (TaskCallback aCallback) {
iCallback = aCallback;
forceNextIteration();
// The next 2 lines adjust runcounter and number of iterations
// as if it is the same run of the callback, just split between
// a series of callback methods
iRunCounter--;
if ( iIterations >= 0 ) iIterations++;
}
/** Prepare task for next step iteration following yielding of control to the scheduler
* @param aCallback - pointer to the callback method for the next step
*/
void Task::yieldOnce (TaskCallback aCallback) {
yield(aCallback);
iIterations = 1;
}
#endif // _TASK_OO_CALLBACKS
/** Enables the task
* schedules it for execution as soon as possible,
* and resets the RunCounter back to zero
*/
bool Task::enable() {
if (iScheduler) { // activation without active scheduler does not make sense
iRunCounter = 0;
iStatus.canceled = false;
#ifdef _TASK_OO_CALLBACKS
if ( !iStatus.inonenable ) {
Task *current = iScheduler->iCurrent;
iScheduler->iCurrent = this;
iStatus.inonenable = true; // Protection against potential infinite loop
iStatus.enabled = OnEnable();
iStatus.inonenable = false; // Protection against potential infinite loop
iScheduler->iCurrent = current;
}
#else
if ( iOnEnable && !iStatus.inonenable ) {
Task *current = iScheduler->iCurrent;
iScheduler->iCurrent = this;
iStatus.inonenable = true; // Protection against potential infinite loop
iStatus.enabled = iOnEnable();
iStatus.inonenable = false; // Protection against potential infinite loop
iScheduler->iCurrent = current;
}
else {
iStatus.enabled = true;
}
#endif // _TASK_OO_CALLBACKS
iPreviousMillis = _TASK_TIME_FUNCTION() - (iDelay = iInterval);
#ifdef _TASK_TIMEOUT
resetTimeout();
#endif // _TASK_TIMEOUT
if ( iStatus.enabled ) {
#ifdef _TASK_STATUS_REQUEST
iMyStatusRequest.setWaiting();
#endif // _TASK_STATUS_REQUEST
}
return iStatus.enabled;
}
return false;
}
/** Enables the task only if it was not enabled already
* Returns previous state (true if was already enabled, false if was not)
*/
bool Task::enableIfNot() {
bool previousEnabled = iStatus.enabled;
if ( !previousEnabled ) enable();
return (previousEnabled);
}
/** Enables the task
* and schedules it for execution after a delay = aInterval
*/
bool Task::enableDelayed(unsigned long aDelay) {
enable();
delay(aDelay);
return iStatus.enabled;
}
#ifdef _TASK_TIMEOUT
void Task::setTimeout(unsigned long aTimeout, bool aReset) {
iTimeout = aTimeout;
if (aReset) resetTimeout();
}
void Task::resetTimeout() {
iStarttime = _TASK_TIME_FUNCTION();
iStatus.timeout = false;
}
unsigned long Task::getTimeout() {
return iTimeout;
}
long Task::untilTimeout() {
if ( iTimeout ) {
return ( (long) (iStarttime + iTimeout) - (long) _TASK_TIME_FUNCTION() );
}
return -1;
}
bool Task::timedOut() {
return iStatus.timeout;
}
#endif // _TASK_TIMEOUT
/** Delays Task for execution after a delay = aInterval (if task is enabled).
* leaves task enabled or disabled
* if aDelay is zero, delays for the original scheduling interval from now
*/
void Task::delay(unsigned long aDelay) {
// if (!aDelay) aDelay = iInterval;
iDelay = aDelay ? aDelay : iInterval;
iPreviousMillis = _TASK_TIME_FUNCTION(); // - iInterval + aDelay;
}
/** Schedules next iteration of Task for execution immediately (if enabled)
* leaves task enabled or disabled
* Task's original schedule is shifted, and all subsequent iterations will continue from this point in time
*/
void Task::forceNextIteration() {
iPreviousMillis = _TASK_TIME_FUNCTION() - (iDelay = iInterval);
}
/** Sets the execution interval.
* Task execution is delayed for aInterval
* Use enable() to schedule execution ASAP
* @param aInterval - new execution interval
*/
void Task::setInterval (unsigned long aInterval) {
iInterval = aInterval;
delay(); // iDelay will be updated by the delay() function
}
/** Disables task
* Task will no longer be executed by the scheduler
* Returns status of the task before disable was called (i.e., if the task was already disabled)
*/
bool Task::disable() {
bool previousEnabled = iStatus.enabled;
iStatus.enabled = false;
iStatus.inonenable = false;
#ifdef _TASK_OO_CALLBACKS
if (previousEnabled) {
#else
if (previousEnabled && iOnDisable) {
#endif // _TASK_OO_CALLBACKS
Task *current = iScheduler->iCurrent;
iScheduler->iCurrent = this;
#ifdef _TASK_OO_CALLBACKS
OnDisable();
#else
iOnDisable();
#endif // _TASK_OO_CALLBACKS
iScheduler->iCurrent = current;
}
#ifdef _TASK_STATUS_REQUEST
iMyStatusRequest.signalComplete();
#endif
return (previousEnabled);
}
/** Aborts task execution
* Task will no longer be executed by the scheduler AND ondisable method will not be called
*/
void Task::abort() {
iStatus.enabled = false;
iStatus.inonenable = false;
iStatus.canceled = true;
}
/** Cancels task execution
* Task will no longer be executed by the scheduler. Ondisable method will be called after 'canceled' flag is set
*/
void Task::cancel() {
iStatus.canceled = true;
disable();
}
bool Task::canceled() {
return iStatus.canceled;
}
/** Restarts task
* Task will run number of iterations again
*/
bool Task::restart() {
iIterations = iSetIterations;
return enable();
}
/** Restarts task delayed
* Task will run number of iterations again
*/
bool Task::restartDelayed(unsigned long aDelay) {
iIterations = iSetIterations;
return enableDelayed(aDelay);
}
bool Task::isFirstIteration() { return (iRunCounter <= 1); }
bool Task::isLastIteration() { return (iIterations == 0); }
#ifdef _TASK_TIMECRITICAL
long Task::getOverrun() { return iOverrun; }
long Task::getStartDelay() { return iStartDelay; }
#endif // _TASK_TIMECRITICAL
#ifdef _TASK_WDT_IDS
void Task::setId(unsigned int aID) { iTaskID = aID; }
unsigned int Task::getId() { return iTaskID; }
void Task::setControlPoint(unsigned int aPoint) { iControlPoint = aPoint; }
unsigned int Task::getControlPoint() { return iControlPoint; }
#endif // _TASK_WDT_IDS
#ifdef _TASK_LTS_POINTER
void Task::setLtsPointer(void *aPtr) { iLTS = aPtr; }
void* Task::getLtsPointer() { return iLTS; }
#endif // _TASK_LTS_POINTER
// ------------------ Scheduler implementation --------------------
/** Default constructor.
* Creates a scheduler with an empty execution chain.
*/
Scheduler::Scheduler() {
init();
#ifdef _TASK_SLEEP_ON_IDLE_RUN
setSleepMethod(&SleepMethod);
#endif // _TASK_SLEEP_ON_IDLE_RUN
}
/*
Scheduler::~Scheduler() {
#ifdef _TASK_SLEEP_ON_IDLE_RUN
#endif // _TASK_SLEEP_ON_IDLE_RUN
}
*/
/** Initializes all internal varaibles
*/
void Scheduler::init() {
iFirst = NULL;
iLast = NULL;
iCurrent = NULL;
iPaused = false;
iEnabled = true;
#ifdef _TASK_PRIORITY
iHighPriority = NULL;
#endif // _TASK_PRIORITY
#ifdef _TASK_SLEEP_ON_IDLE_RUN
allowSleep(true);
#endif // _TASK_SLEEP_ON_IDLE_RUN
#ifdef _TASK_TIMECRITICAL
cpuLoadReset();
#endif // _TASK_TIMECRITICAL
}
/** Appends task aTask to the tail of the execution chain.
* @param &aTask - reference to the Task to be appended.
* @note Task can only be part of the chain once.
*/
void Scheduler::addTask(Task& aTask) {
// If task already belongs to a scheduler, we should not be adding
// it to this scheduler. It should be deleted from the other scheduler first.
if (aTask.iScheduler != NULL)
return;
aTask.iScheduler = this;
// First task situation:
if (iFirst == NULL) {
iFirst = &aTask;
aTask.iPrev = NULL;
}
else {
// This task gets linked back to the previous last one
aTask.iPrev = iLast;
iLast->iNext = &aTask;
}
// "Previous" last task gets linked to this one - as this one becomes the last one
aTask.iNext = NULL;
iLast = &aTask;
}
/** Deletes specific Task from the execution chain
* @param &aTask - reference to the task to be deleted from the chain
*/
void Scheduler::deleteTask(Task& aTask) {
// Can only delete own tasks
if (aTask.iScheduler != this)
return;
aTask.iScheduler = NULL;
if (aTask.iPrev == NULL) {
if (aTask.iNext == NULL) {
iFirst = NULL;
iLast = NULL;
return;
}
else {
aTask.iNext->iPrev = NULL;
iFirst = aTask.iNext;
aTask.iNext = NULL;
return;
}
}
if (aTask.iNext == NULL) {
aTask.iPrev->iNext = NULL;
iLast = aTask.iPrev;
aTask.iPrev = NULL;
return;
}
aTask.iPrev->iNext = aTask.iNext;
aTask.iNext->iPrev = aTask.iPrev;
aTask.iPrev = NULL;
aTask.iNext = NULL;
}
/** Disables all tasks in the execution chain
* Convenient for error situations, when the only
* task remaining active is an error processing task
* @param aRecursive - if true, tasks of the higher priority chains are disabled as well recursively
*/
#ifdef _TASK_PRIORITY
void Scheduler::disableAll(bool aRecursive) {
#else
void Scheduler::disableAll() {
#endif
Task *current = iFirst;
while (current) {
current->disable();
current = current->iNext;
}
#ifdef _TASK_PRIORITY
if (aRecursive && iHighPriority) iHighPriority->disableAll(true);
#endif // _TASK_PRIORITY
}
/** Enables all the tasks in the execution chain
* @param aRecursive - if true, tasks of the higher priority chains are enabled as well recursively
*/
#ifdef _TASK_PRIORITY
void Scheduler::enableAll(bool aRecursive) {
#else
void Scheduler::enableAll() {
#endif
Task *current = iFirst;
while (current) {
current->enable();
current = current->iNext;
}
#ifdef _TASK_PRIORITY
if (aRecursive && iHighPriority) iHighPriority->enableAll(true);
#endif // _TASK_PRIORITY
}
/** Sets scheduler for the higher priority tasks (support for layered task priority)
* @param aScheduler - pointer to a scheduler for the higher priority tasks
*/
#ifdef _TASK_PRIORITY
void Scheduler::setHighPriorityScheduler(Scheduler* aScheduler) {
if (aScheduler != this) iHighPriority = aScheduler; // Setting yourself as a higher priority one will create infinite recursive call
#ifdef _TASK_SLEEP_ON_IDLE_RUN
if (iHighPriority) {
iHighPriority->allowSleep(false); // Higher priority schedulers should not do power management
}
#endif // _TASK_SLEEP_ON_IDLE_RUN
};
#endif // _TASK_PRIORITY
#ifdef _TASK_SLEEP_ON_IDLE_RUN
void Scheduler::allowSleep(bool aState) {
iAllowSleep = aState;
}
#endif // _TASK_SLEEP_ON_IDLE_RUN
#ifdef _TASK_PRIORITY
void Scheduler::startNow( bool aRecursive ) {
#else
void Scheduler::startNow() {
#endif
unsigned long t = _TASK_TIME_FUNCTION();
iCurrent = iFirst;
while (iCurrent) {
if ( iCurrent->iStatus.enabled ) iCurrent->iPreviousMillis = t - iCurrent->iDelay;
iCurrent = iCurrent->iNext;
}
#ifdef _TASK_PRIORITY
if (aRecursive && iHighPriority) iHighPriority->startNow( true );
#endif // _TASK_PRIORITY
}
/** Returns number millis or micros until next scheduled iteration of a given task
*
* @param aTask - reference to task which next iteration is in question
*/
long Scheduler::timeUntilNextIteration(Task& aTask) {
#ifdef _TASK_STATUS_REQUEST
StatusRequest *s = aTask.getStatusRequest();
if ( s != NULL && s->pending() )
return (-1); // cannot be determined
#endif
if ( !aTask.isEnabled() )
return (-1); // cannot be determined
long d = (long) aTask.iDelay - ( (long) ((_TASK_TIME_FUNCTION() - aTask.iPreviousMillis)) );
if ( d < 0 )
return (0); // Task will run as soon as possible
return ( d );
}
Task& Scheduler::currentTask() { return *iCurrent; } // DEPRICATED. Use the next one instead
Task* Scheduler::getCurrentTask() { return iCurrent; }
#ifdef _TASK_LTS_POINTER
void* Scheduler::currentLts() { return iCurrent->iLTS; }
#endif // _TASK_LTS_POINTER
#ifdef _TASK_TIMECRITICAL
bool Scheduler::isOverrun() { return (iCurrent->iOverrun < 0); }
void Scheduler::cpuLoadReset() {
iCPUStart = _task_micros();
iCPUCycle = 0;
iCPUIdle = 0;
}
unsigned long Scheduler::getCpuLoadTotal() {
return (_task_micros() - iCPUStart);
}
#endif // _TASK_TIMECRITICAL