-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtask.c
1253 lines (1157 loc) · 47.3 KB
/
task.c
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
/* Edited by Rob Capo on Apr 9, 2014
* Added implementations of task manager functions with function inputs
*/
#include "task.h"
#ifdef PERMANENT_TASKS
#include "permanent_tasks.h"
#endif
//#define DEBUGGING
// set DEBUGGING in a project specific file or as a compiler macro
#ifndef DEBUGGING
// used for debugging, set to return when not debugging
#define RETURN_OR_STOP return
// used for debugging, set to Reset() when not debugging
#define RESET_OR_STOP Reset()
#else
// used for debugging, set to return when not debugging
#define RETURN_OR_STOP while(1)
// used for debugging, set to Reset() when not debugging
#define RESET_OR_STOP while(1)
#endif
// allow the user to define a default task that will fill the function pointers
// that are not in use. This allows for more advanced error handling or debugging
// if not defined then make it a while(1) loop if debugging, nothing otherwise
#ifndef DEFAULT_TASK
void DefaultTask(void) {
#ifdef DEBUGGING
while(1);
#endif
}
void DefaultInputTask(void *input) {
#ifdef DEBUGGING
while(1);
#endif
}
#define DEFAULT_TASK DefaultTask
#define DEFAULT_INPUT_TASK DefaultInputTask
#endif
// next value to indicate that a task is waiting to be linked in the queue
#define TASK_LINK_QUEUE -2
// count value to indicate that a task is waiting to be removed from the queue
#define TASK_UNLINK_QUEUE -6
// next value to indicate that a task is waiting to be linked in the schedule
#define TASK_LINK_SCHEDULE -3
// count value to indicate that a task is waiting to be removed from the schedule
#define TASK_UNLINK_SCHEDULE -5
// task structure to hold function pointer, priority, time, and period as well
// as next and previous to implement a double linked list
typedef struct {
void (*void_fn)(void); // void function to run
void (*fn)(void *); // function with input to run
void *input; // Pointer to argument to input to fn
int16_t next; // the next task to execute (-1 if this is the last task)
int16_t previous; // the previous task to execute (-1 if the first task)
int16_t priority; // priority of the task (higher value is higher priority
// 0xFFFF is reserved to indicate that the task is done
tint_t time; // the time the task was added to the queue
// or the time the task is scheduled to run
tint_t period; // the period for repeating the task if it is to be repeated
int16_t count; // the number of times the taks should be repeated
// (-1) for an infinitely repeated task
} task_t;
typedef struct {
task_t task[MAX_TASK_LENGTH]; // the task array
int16_t first; // the first task to be executed (-1 is none)
int16_t last; // the last task to be executed (-1 is none)
int16_t available; // next available slot where a task can be loaded
int16_t length; // current length of the queue
int16_t to_link; // index of task that needs to be linked into the queue*
int16_t max_length; // max length is only used to store the max length of
// the queue, it has no function besides diagnostics
int16_t sch_first; // the first task in the schedule
int16_t sch_last; // the last task in the schedule
int16_t sch_length; // current length of the schedule
int16_t sch_to_link; // index of task to be linked into the schedule*
} task_queue_t;
// * to_link indecies are TAsK_DONE if nothing needs to be linked and
// TASK_LINK_QUEUE or TASK_LINK_SCHEDULE if more than two tasks need to be
// linked
task_queue_t tasks;
// QueueTask will take a task and link it into the double linked task queue
// according to the priority of the task such that the first in the list will
// have the highest priority and the last in the list will have the lowest
// priority
static void QueueTask(int16_t index);
// UnQueueTask will unlink a task from the double linked task queue
static void UnQueueTask(int16_t this_task);
// ScheduleTask will take a task and link it into the double linked schedule
// according to the next time the task is scheduled to run such that the first
// in the list will have the soonest time and the last in the list will have
// the furthest time
void ScheduleTask(int16_t index);
// UnScheduleTask will unlink a task from the double linked task schedule
void UnScheduleTask(int16_t this_task);
// GetAvailableTask sets tasks.available to an available task slot in the task
// array. If no slot is available it will remove the last task in the task queue
// if its priority is lower than priority. Otherwise it will set available = -1
// and whatever function is seeking to add the task will have to abort.
void GetAvailableTask(int16_t priority);
// a service routine to make sure no tasks need to be linked into the queue
// or schedule and to increase the priority of any tasks that have been in the
// queue too long
void ServiceTasks(void);
// make sure perminantly scheduled tasks exist
void CheckPermSchedule(void);
// initialize the double linked lists for the task queue and the schedule and
// register the task module with the logging module
void TaskInit(void) {
// terminate all tasks and initialize double linked lists to 0 length
TerminateAllTasks();
// register the TASK module with the logging module and set the log level
#ifdef TASK_LOGGING_ENABLE
LogInitSubsystem(TASK, EVERYTHING, "task", TASKS_VERSION);
#endif
// if permanent tasks are enabled then call CheckPermSchedule to add the
// permanent tasks to the schedule
#ifdef PERMANENT_TASKS
CheckPermSchedule();
#endif
}
// wait up to 1 second for the task queue to clear and then reset the task queue
// and schedule (either after 1 second or when the task queue is empty)
void TaskShutdown(void) {
tint_t time;
// get the current time
time = TimeNow();
// execute tasks for up to 1 second
while(TimeSince(time)<1000) {
// call SystemTick()
SystemTick();
// if there are no tasks left then break
if(tasks.length == 0) break;
}
// terminate all tasks and initialize double linked lists to 0 length
TerminateAllTasks();
}
// reset the task queue and schedule double linked lists and set all tasks to
// inactive (TASK_DONE)
void TerminateAllTasks(void) {
int16_t i;
// set first and last for both lists to -1
tasks.first = -1;
tasks.last = -1;
tasks.sch_first = -1;
tasks.sch_last = -1;
// initialize available to the first task slot
tasks.available = 0;
// set the lengths to 0
tasks.length = 0;
tasks.sch_length = 0;
// set max_length to 0
// note: currently max_length is only used for diagnostics
tasks.max_length = 0;
// clear all tasks (set priority = TASK_DONE)
for (i = 0; i < MAX_TASK_LENGTH; i++) {
tasks.task[i].priority = TASK_DONE;
tasks.task[i].void_fn = DEFAULT_TASK;
tasks.task[i].fn = DEFAULT_INPUT_TASK;
tasks.task[i].input = 0;
}
}
void TaskQueueAdd(void(*fn)(void), int16_t priority) {
int16_t i;
task_t * task_ptr;
// make sure next task is set, if not find the next available slot
if (tasks.available < 0) {
// get the next available task
GetAvailableTask(priority);
}
// needs to be un-interruptable to avoid an interrupt firing at this spot
// and adding a task to the same index
unsigned int intStatus;
intStatus = DisableInterrupts();
i = tasks.available;
// if no task is available then abort
if(i < 0 || i >= MAX_TASK_LENGTH) {
EnableInterrupts(intStatus);
RETURN_OR_STOP;
}
task_ptr = &tasks.task[i];
// tasks.available is no longer available so set it to -1
tasks.available = -1;
// load the function, timestamp, and priority and set period to 0
task_ptr->priority = priority;
EnableInterrupts(intStatus);
task_ptr->void_fn = fn;
task_ptr->time = TimeNow();
task_ptr->period = 0;
// If there is no task waiting to be linked into the queue then set to_link
// to this task, otherwise set to_link to TASK_LINK_QUEUE
// at the next SystemTick() the to_link task will be linked or if to_link
// is TASK_LINK_QUEUE then all tasks will be checked if they are waiting to
// be linked
if(tasks.to_link == TASK_DONE) tasks.to_link = i;
else tasks.to_link = TASK_LINK_QUEUE;
task_ptr->next = TASK_LINK_QUEUE;
}
void TaskInputQueueAdd(void(*fn)(void *), void *input, int16_t priority) {
int16_t i;
task_t * task_ptr;
// make sure next task is set, if not find the next available slot
if (tasks.available < 0) {
// get the next available task
GetAvailableTask(priority);
}
// needs to be un-interruptable to avoid an interrupt firing at this spot
// and adding a task to the same index
unsigned int intStatus;
intStatus = DisableInterrupts();
i = tasks.available;
// if no task is available then abort
if(i < 0 || i >= MAX_TASK_LENGTH) {
EnableInterrupts(intStatus);
RETURN_OR_STOP;
}
task_ptr = &tasks.task[i];
// tasks.available is no longer available so set it to -1
tasks.available = -1;
// load the function, timestamp, and priority and set period to 0
task_ptr->priority = priority;
EnableInterrupts(intStatus);
task_ptr->fn = fn;
task_ptr->input = input;
task_ptr->time = TimeNow();
task_ptr->period = 0;
// If there is no task waiting to be linked into the queue then set to_link
// to this task, otherwise set to_link to TASK_LINK_QUEUE
// at the next SystemTick() the to_link task will be linked or if to_link
// is TASK_LINK_QUEUE then all tasks will be checked if they are waiting to
// be linked
if(tasks.to_link == TASK_DONE) tasks.to_link = i;
else tasks.to_link = TASK_LINK_QUEUE;
task_ptr->next = TASK_LINK_QUEUE;
}
// QueueTask will take a task and link it into the double linked task queue
// according to the priority of the task such that the first in the list will
// have the highest priority and the last in the list will have the lowest
// priority
void QueueTask(int16_t index) {
int16_t i;
task_t * this_task;
task_t * current_task;
if(index < 0 || index >= MAX_TASK_LENGTH) RESET_OR_STOP;
this_task = &tasks.task[index];
if(this_task->priority == TASK_DONE) return;
if(this_task->next < -1) this_task->next = -1;
// check if there are no tasks in the queue
if (tasks.length == 0 || tasks.first < 0 || tasks.last < 0) {
tasks.length = 0;
// set first and last to this task and set next and previous to -1
tasks.first = index;
tasks.last = index;
this_task->next = -1;
this_task->previous = -1;
}
// otherwise find where to put this task and re-link the list
else {
// scan the task queue to find where to insert this task
// start at the last task in the list and work forward
i = tasks.last;
// infinite loop (we will break out of it though)
while (1) {
if(i < 0 || i >= MAX_TASK_LENGTH) RESET_OR_STOP;
// if this task is of greater priority than the current one (i)
current_task = &tasks.task[i];
if (this_task->priority > current_task->priority) {
// if there is no previous task then this task becomes the first
// otherwise set the current task to the current task's previous
// and continue
if (current_task->previous < 0) {
// set first task to this one
tasks.first = index;
// set the current task's previous to this task
current_task->previous = index;
// clear this task's previous (set to -1)
this_task->previous = -1;
// set this task's next to the current one (i)
this_task->next = i;
break;
}
// otherwise go to the previous task (e.g. traverse the list)
else {
// set the current task (i) to the current task's previous
i = current_task->previous;
// continue the scan
continue;
}
// if the priority of this task is not greater than the current task
// then insert this task after the current task
} else {
// set this task's next to the current task's next
this_task->next = current_task->next;
// set this task's previous to the current task (i)
this_task->previous = i;
// set the current task's next to this task
current_task->next = index;
// if the current task was the last task then update last to this task
if (this_task->next < 0) tasks.last = index;
else if(this_task->next < MAX_TASK_LENGTH){
// set the next tasks previous to this task
tasks.task[this_task->next].previous = index;
}
break;
}
}
}
// increment the number of tasks in the task queue
tasks.length++;
}
void TaskScheduleAddCount(void(*fn)(void), int16_t priority, tint_t delay, tint_t period, int16_t count) {
task_t * task_ptr;
// make sure next task is set, if not find the next available slot
if (tasks.available < 0) {
// get the next available task
GetAvailableTask(priority);
}
// needs to be un-interruptable to avoid an interrupt firing at this spot
// and adding a task to the same index
unsigned int intStatus;
intStatus = DisableInterrupts();
// if no task is available then abort
if(tasks.available < 0 || tasks.available >= MAX_TASK_LENGTH) {
EnableInterrupts(intStatus);
RESET_OR_STOP;
}
task_ptr = &tasks.task[tasks.available];
// schedule the task
if(tasks.sch_to_link == TASK_DONE) tasks.sch_to_link = tasks.available;
else tasks.sch_to_link = TASK_LINK_SCHEDULE;
// tasks.available is no longer available so set it to -1
tasks.available = -1;
// load the function, priority, next time to run, and period
task_ptr->priority = priority;
EnableInterrupts(intStatus);
task_ptr->void_fn = fn;
task_ptr->time = TimeNow() + delay;
task_ptr->period = period;
task_ptr->count = count;
task_ptr->next = TASK_LINK_SCHEDULE;
// get the next available task slot so its ready (this is not required but
// it will allow TaskQueueAdd() to run faster)
// use TASK_NO_PRIORITY as the priority to ensure that we don't bump a task
// off the queue if there are no available task slots
GetAvailableTask(TASK_NO_PRIORITY);
}
void TaskInputScheduleAddCount(void(*fn)(void *), void *input, int16_t priority, tint_t delay, tint_t period, int16_t count) {
task_t * task_ptr;
// make sure next task is set, if not find the next available slot
if (tasks.available < 0) {
// get the next available task
GetAvailableTask(priority);
}
// needs to be un-interruptable to avoid an interrupt firing at this spot
// and adding a task to the same index
unsigned int intStatus;
intStatus = DisableInterrupts();
// if no task is available then abort
if(tasks.available < 0 || tasks.available >= MAX_TASK_LENGTH) {
EnableInterrupts(intStatus);
RESET_OR_STOP;
}
task_ptr = &tasks.task[tasks.available];
// schedule the task
if(tasks.sch_to_link == TASK_DONE) tasks.sch_to_link = tasks.available;
else tasks.sch_to_link = TASK_LINK_SCHEDULE;
// tasks.available is no longer available so set it to -1
tasks.available = -1;
// load the function, priority, next time to run, and period
task_ptr->priority = priority;
EnableInterrupts(intStatus);
task_ptr->fn = fn;
task_ptr->input = input;
task_ptr->time = TimeNow() + delay;
task_ptr->period = period;
task_ptr->count = count;
task_ptr->next = TASK_LINK_SCHEDULE;
// get the next available task slot so its ready (this is not required but
// it will allow TaskQueueAdd() to run faster)
// use TASK_NO_PRIORITY as the priority to ensure that we don't bump a task
// off the queue if there are no available task slots
GetAvailableTask(TASK_NO_PRIORITY);
}
void TaskScheduleAdd(void(*fn)(void), int16_t priority, tint_t delay, tint_t period) {
TaskScheduleAddCount(fn,priority,delay,period,-1);
}
void TaskInputScheduleAdd(void(*fn)(void *), void *input, int16_t priority, tint_t delay, tint_t period) {
TaskInputScheduleAddCount(fn, input, priority, delay, period, -1);
}
// NOT INTERRUPT SAFE
void TaskReSchedule(void(*fn)(void), int16_t priority, tint_t delay, tint_t period) {
int16_t i;
uint8_t found = 0;
task_t * task_ptr;
// RemoveTask(fn);
// TaskScheduleAdd(fn, priority, delay, period);
// if(found == 0) return;
// check if the task is in the queue, if so make sure it is set not to be scheduled
i = tasks.first;
while(i >= 0) {
if(i > MAX_TASK_LENGTH) break;
task_ptr = &tasks.task[i];
if(task_ptr->void_fn == fn) {
task_ptr->period = 0;
if(i != tasks.first) UnQueueTask(i);
}
i = task_ptr->next;
}
// check if the task is in the schedule and change the time to run
i = tasks.sch_first;
while(i >= 0) {
if(i > MAX_TASK_LENGTH) break;
task_ptr = &tasks.task[i];
if(task_ptr->void_fn == fn) {
if(found == 0) {
found = 1;
// remove the task from the schedule:
UnScheduleTask(i);
task_ptr->priority = priority;
task_ptr->time = TimeNow() + delay;
task_ptr->period = period;
task_ptr->count = -1;
ScheduleTask(i);
}else {
UnScheduleTask(i);
}
}
i = task_ptr->next;
}
// otherwise add the task to the schedule
if(found == 0) TaskScheduleAdd(fn, priority, delay, period);
}
// NOT INTERRUPT SAFE
void TaskInputReSchedule(void(*fn)(void *), void *input, int16_t priority, tint_t delay, tint_t period) {
int16_t i;
uint8_t found = 0;
task_t * task_ptr;
// RemoveTask(fn);
// TaskScheduleAdd(fn, priority, delay, period);
// if(found == 0) return;
// check if the task is in the queue, if so make sure it is set not to be scheduled
i = tasks.first;
while(i >= 0) {
if(i > MAX_TASK_LENGTH) break;
task_ptr = &tasks.task[i];
if(task_ptr->fn == fn && task_ptr->input == input) {
task_ptr->period = 0;
if(i != tasks.first) UnQueueTask(i);
}
i = task_ptr->next;
}
// check if the task is in the schedule and change the time to run
i = tasks.sch_first;
while(i >= 0) {
if(i > MAX_TASK_LENGTH) break;
task_ptr = &tasks.task[i];
if(task_ptr->fn == fn && task_ptr->input == input) {
if(found == 0) {
found = 1;
// remove the task from the schedule:
UnScheduleTask(i);
task_ptr->priority = priority;
task_ptr->time = TimeNow() + delay;
task_ptr->period = period;
task_ptr->count = -1;
ScheduleTask(i);
}else {
UnScheduleTask(i);
}
}
i = task_ptr->next;
}
// otherwise add the task to the schedule
if(found == 0) TaskInputScheduleAdd(fn, input, priority, delay, period);
}
void ScheduleTask(int16_t index) {
int16_t i;
task_t * this_task;
task_t * current_task;
if(index < 0 || index >= MAX_TASK_LENGTH) RESET_OR_STOP;
this_task = &tasks.task[index];
if(this_task->priority == TASK_DONE) RETURN_OR_STOP;
if(this_task->next < -1) this_task->next = -1;
// check if task is scheduled after a rollover by checking if the task is
// scheduled before TIME_MAX/2 and if the current time is a after TIME_MAX/2
if(this_task->time < TIME_MAX/2 && TimeNow() > TIME_MAX/2){
// add the time til TIME_MAX to the time then roll the timer
this_task->time = this_task->time + (TIME_MAX - TimeNow());
RollTimer();
// if we do not roll the system timer then this task will be scheduled
// for a time long before the current time and it will run right away
// instead of running in the future. For this reason it is not
// recommended to schedule any tasks more than TIME_MAX/2 in the future
// (if tint_t is a uint32_t and the timer ticks every ms then this
// equates to 24 days 20 hours 31 minutes and 23.648 seconds)
}
// check if there are no tasks in the schedule
if (tasks.sch_length == 0) {
// set first and last to this task and set next and previous to -1
tasks.sch_first = index;
tasks.sch_last = index;
this_task->next = -1;
this_task->previous = -1;
} else {
// scan the task queue to find where to insert this task
// start at the last task in the list and work forward
i = tasks.sch_last;
// infinite loop (we will break out of it though)
while (1) {
if(i < 0 || i >= MAX_TASK_LENGTH) RESET_OR_STOP;
// if this task is scheduled before the current task (i)
current_task = &tasks.task[i];
if (this_task->time < current_task->time) {
// if there is no previous task then this task becomes the first
// otherwise set the current task to the current task's previous
// and continue
if (current_task->previous < 0) {
// set first task to this one
tasks.sch_first = index;
// set the current task's previous to this one
current_task->previous = index;
// clear this task's previous (set to -1)
this_task->previous = -1;
// set this task's next to the current one
this_task->next = i;
break;
}
// otherwise go to the previous task (e.g. traverse the list)
else {
// set the current task (i) to the current task's previous
i = current_task->previous;
// continue the scan
continue;
}
// if the priority of this task is not greater than the current task
// then insert this task after the current task
} else {
// set this task's next to the current task's next
this_task->next = current_task->next;
// set this task's previous to the current task
this_task->previous = i;
// set the current task's next to this task
current_task->next = index;
// if the current task was the last task then update last to this task
if (this_task->next < 0) tasks.sch_last = index;
else if(this_task->next < MAX_TASK_LENGTH) {
// set the next tasks previous to this task
tasks.task[this_task->next].previous = index;
}
break;
}
}
}
// increment the number of tasks in the schedule
tasks.sch_length++;
}
// remove a task from both the task queue and the schedule
// also check for any tasks that are pending
void RemoveTask(void(*fn)(void)) {
int16_t i;
task_t * task_ptr;
RemoveTaskFromQueue(fn);
RemoveTaskFromSchedule(fn);
for(i = 0; i < MAX_TASK_LENGTH; i++) {
task_ptr = &tasks.task[i];
if(task_ptr->void_fn == fn) {
task_ptr->void_fn = DEFAULT_TASK;
if(task_ptr->priority >= 0) {
if(task_ptr->next == TASK_LINK_QUEUE) {
task_ptr->count = TASK_UNLINK_QUEUE;
//task_ptr->next = TASK_DONE;
}
if(task_ptr->next == TASK_LINK_SCHEDULE) {
task_ptr->count = TASK_UNLINK_SCHEDULE;
//task_ptr->next = TASK_DONE;
}
}
}
}
}
// remove a task from both the task queue and the schedule
// also check for any tasks that are pending
void RemoveTaskInput(void(*fn)(void *), void *input) {
int16_t i;
task_t * task_ptr;
RemoveTaskInputFromQueue(fn, input);
RemoveTaskInputFromSchedule(fn, input);
for(i = 0; i < MAX_TASK_LENGTH; i++) {
task_ptr = &tasks.task[i];
if(task_ptr->fn == fn && task_ptr->input == input) {
task_ptr->fn = DEFAULT_INPUT_TASK;
task_ptr->input = 0;
if(task_ptr->priority >= 0) {
if(task_ptr->next == TASK_LINK_QUEUE) {
task_ptr->count = TASK_UNLINK_QUEUE;
//task_ptr->next = TASK_DONE;
}
if(task_ptr->next == TASK_LINK_SCHEDULE) {
task_ptr->count = TASK_UNLINK_SCHEDULE;
//task_ptr->next = TASK_DONE;
}
}
}
}
}
void RemoveTaskFromQueue(void(*fn)(void)) {
int16_t this_task_i, next_task_i;
task_t * this_task;
// loop through the task queue
this_task_i = tasks.first;
while(this_task_i >= 0 && this_task_i < MAX_TASK_LENGTH) {
this_task = &tasks.task[this_task_i];
// set the index of the next task
next_task_i = this_task->next;
// if the task's function pointer = the task to remove
if(this_task->void_fn == fn) {
// set the task to unlink
this_task->count = TASK_UNLINK_QUEUE;
if(tasks.to_link == TASK_DONE) tasks.to_link = this_task_i;
else tasks.to_link = TASK_LINK_QUEUE;
}
// set this_task to the index of the next_task
this_task_i = next_task_i;
}
}
void RemoveTaskInputFromQueue(void(*fn)(void *), void *input) {
int16_t this_task_i, next_task_i;
task_t * this_task;
// loop through the task queue
this_task_i = tasks.first;
while(this_task_i >= 0 && this_task_i < MAX_TASK_LENGTH) {
this_task = &tasks.task[this_task_i];
// set the index of the next task
next_task_i = this_task->next;
// if the task's function pointer = the task to remove
if(this_task->fn == fn && this_task->input == input) {
// set the task to unlink
this_task->count = TASK_UNLINK_QUEUE;
if(tasks.to_link == TASK_DONE) tasks.to_link = this_task_i;
else tasks.to_link = TASK_LINK_QUEUE;
}
// set this_task to the index of the next_task
this_task_i = next_task_i;
}
}
void UnQueueTask(int16_t this_task_i) {
int16_t previous_task_i, next_task_i;
task_t * this_task;
if(this_task_i < 0 || this_task_i > MAX_TASK_LENGTH) RETURN_OR_STOP;
this_task = &tasks.task[this_task_i];
if(this_task->priority == TASK_DONE) return;
// set the index of the previous task
previous_task_i = this_task->previous;
// set the index of the next task
next_task_i = this_task->next;
// // if this is the only task in the queue
// if(tasks.length <= 1) {
// tasks.length = 0;
// tasks.first = TASK_DONE;
// tasks.last = TASK_DONE;
// tasks.task[this_task].priority = TASK_DONE;
// tasks.task[this_task].count = TASK_DONE;
// return;
// }
// if((tasks.length <= 1 && next_task >= 0) || tasks.length == 0) {
// while(1);
// }
// set the task to done
this_task->priority = TASK_DONE;
this_task->count = TASK_DONE;
this_task->void_fn = DEFAULT_TASK;
this_task->fn = DEFAULT_INPUT_TASK;
this_task->input = 0;
tasks.length--;
// if there is a previous task
if(previous_task_i >= 0 && previous_task_i < MAX_TASK_LENGTH) {
// set the previous task's next to this task's next
tasks.task[previous_task_i].next = next_task_i;
// if this task's next = -1 then set last to point to the
// previous task
if(next_task_i < 0) tasks.last = previous_task_i;
}// if there is no previous task then set first to the next task
else tasks.first = next_task_i;
// if there is a next task
if(next_task_i >= 0 && next_task_i < MAX_TASK_LENGTH) {
// set the next task's previous to this task's previous
tasks.task[next_task_i].previous = previous_task_i;
// if this task's previous = -1 then set first to point to the
// next task
if(previous_task_i < 0) tasks.first = next_task_i;
}// if there is no next task then set last to the previous task
else tasks.last = previous_task_i;
// if(tasks.length <= 0 && tasks.first >= 0) while(1);
}
void RemoveTaskFromSchedule(void(*fn)(void)) {
int16_t this_task_i, next_task_i;
task_t * this_task;
// loop through the schedule
this_task_i = tasks.sch_first;
while(this_task_i >= 0 && this_task_i < MAX_TASK_LENGTH) {
this_task = &tasks.task[this_task_i];
// set the index of the next task
next_task_i = this_task->next;
// if the task's function pointer = the task to remove
if(this_task->void_fn == fn) {
// set the task to unlink
this_task->count = TASK_UNLINK_SCHEDULE;
if(tasks.sch_to_link == TASK_DONE) tasks.sch_to_link = this_task_i;
else tasks.sch_to_link = TASK_LINK_SCHEDULE;
}
// set this_task to the index of the next task
this_task_i = next_task_i;
}
}
void RemoveTaskInputFromSchedule(void(*fn)(void *), void *input) {
int16_t this_task_i, next_task_i;
task_t * this_task;
// loop through the schedule
this_task_i = tasks.sch_first;
while(this_task_i >= 0 && this_task_i < MAX_TASK_LENGTH) {
this_task = &tasks.task[this_task_i];
// set the index of the next task
next_task_i = this_task->next;
// if the task's function pointer = the task to remove
if(this_task->fn == fn && this_task->input == input) {
// set the task to unlink
this_task->count = TASK_UNLINK_SCHEDULE;
if(tasks.sch_to_link == TASK_DONE) tasks.sch_to_link = this_task_i;
else tasks.sch_to_link = TASK_LINK_SCHEDULE;
}
// set this_task to the index of the next task
this_task_i = next_task_i;
}
}
void UnScheduleTask(int16_t this_task_i) {
int16_t previous_task_i, next_task_i;
task_t * this_task;
if(this_task_i < 0 || this_task_i >= MAX_TASK_LENGTH) RETURN_OR_STOP;
this_task = &tasks.task[this_task_i];
if(this_task->priority == TASK_DONE) return;
// set the index of the previous task
previous_task_i = this_task->previous;
// set the index of the next task
next_task_i = this_task->next;
// set the task to done
this_task->priority = TASK_DONE;
this_task->count = TASK_DONE;
this_task->void_fn = DEFAULT_TASK;
this_task->fn = DEFAULT_INPUT_TASK;
this_task->input = 0;
tasks.sch_length--;
// if there is a previous task
if(previous_task_i >= 0 && previous_task_i < MAX_TASK_LENGTH) {
// set the previous task's next to this task's next
tasks.task[previous_task_i].next = next_task_i;
// if this task's next = -1 then set sch_last to point to the
// previous task
if(next_task_i < 0) tasks.sch_last = previous_task_i;
}// if there is no previous task then set sch_first to the next task
else tasks.sch_first = next_task_i;
// if there is a next task
if(next_task_i >= 0 && next_task_i < MAX_TASK_LENGTH) {
// set the next task's previous to this task's previous
tasks.task[next_task_i].previous = previous_task_i;
// if this task's previous = -1 then set sch_first to point to the
// next task
if(previous_task_i < 0) tasks.sch_first = next_task_i;
}// if there is no next task then set sch_last to the previous task
else tasks.sch_last = previous_task_i;
}
void ChangePriority(void(*fn)(void), int16_t priority) {
int16_t i;
task_t * task_ptr;
// loop through all tasks and set any with the function pointer fn to have
// a priority of priority
for (i = 0; i < MAX_TASK_LENGTH; i++) {
task_ptr = &tasks.task[i];
if (task_ptr->void_fn == fn) task_ptr->priority = priority;
}
}
void ChangePriorityInput(void(*fn)(void *), void *input, int16_t priority) {
int16_t i;
task_t * task_ptr;
// loop through all tasks and set any with the function pointer fn to have
// a priority of priority
for (i = 0; i < MAX_TASK_LENGTH; i++) {
task_ptr = &tasks.task[i];
if (task_ptr->fn == fn && task_ptr->input == input) task_ptr->priority = priority;
}
}
// must be called in order for the task management system to work
// currently implemented to execute one task on each call and load any
// scheduled tasks as needed
void SystemTick(void) {
int16_t i;
task_t * task_ptr;
#ifdef PERMANENT_TASKS
static tint_t check_perm_time = 0;
#endif
static tint_t last_time = 0;
static tint_t service_tasks_time = 0;
// check if any tasks need to be linked into the queue
if (tasks.to_link != TASK_DONE) {
// temp save and clear to_link so it can be set properly if QueueTask
// is interrupted
i = tasks.to_link;
tasks.to_link = TASK_DONE;
if (i >= 0 && i < MAX_TASK_LENGTH) {
task_ptr = &tasks.task[i];
// double check that the task needs to be linked and link the task
if (task_ptr->next == TASK_LINK_QUEUE) QueueTask(i);
if (task_ptr->count == TASK_UNLINK_QUEUE) UnQueueTask(i);
} else {
// go through the whole task array and look for any tasks needing to
// be linked into the task queue
for (i = 0; i < MAX_TASK_LENGTH; i++) {
if (tasks.task[i].next == TASK_LINK_QUEUE) QueueTask(i);
if (tasks.task[i].count == TASK_UNLINK_QUEUE) UnQueueTask(i);
}
}
}
// set i to the first task
i = tasks.first;
// if there is a task in queue then run it
if (i >= 0 && i < MAX_TASK_LENGTH) {
task_ptr = &tasks.task[i];
// run the task
task_ptr->fn(task_ptr->input);
task_ptr->void_fn();
// set the next task to run
tasks.first = task_ptr->next;
if (tasks.first >= 0 && tasks.first < MAX_TASK_LENGTH) {
tasks.task[tasks.first].previous = TASK_DONE;
}
// check if the task needs to be added to the schedule (period > 0)
if (task_ptr->period > 0 && task_ptr->count != 0) {
if (task_ptr->count > 0) {
task_ptr->count--;
}
// if remove task was called after the above if but before count--
// then count could be corrupted, if so don't schedule the task
if (task_ptr->count < TASK_DONE) {
// clear this task
task_ptr->priority = TASK_DONE;
task_ptr->void_fn = DEFAULT_TASK;
task_ptr->fn = DEFAULT_INPUT_TASK;
task_ptr->input = 0;
// set available to the task that just ran
tasks.available = i;
task_ptr->count = TASK_DONE;
} else {
// increment the task's time by period
task_ptr->time += task_ptr->period;
// schedule the task
ScheduleTask(i);
}
}// otherwise set this task to inactive (priority = TASK_DONE) and set
// tasks.available to this task
else {
// clear this task
task_ptr->priority = TASK_DONE;
task_ptr->void_fn = DEFAULT_TASK;
task_ptr->fn = DEFAULT_INPUT_TASK;
task_ptr->input = 0;
// set available to the task that just ran
tasks.available = i;
}
// if length is greater than max length then update max length
if ((tasks.length + tasks.sch_length) > tasks.max_length)
tasks.max_length = tasks.length + tasks.sch_length;
// decrement the length of the queue since it is one shorter now
tasks.length--;
if (tasks.length < 1) tasks.last = TASK_DONE;
}
// check if any tasks need to be linked into the schedule
if (tasks.sch_to_link != TASK_DONE) {
// temp save and clear to_link so it can be set properly if ScheduleTask
// is interrupted
i = tasks.sch_to_link;
tasks.sch_to_link = TASK_DONE;
if (i >= 0 && i < MAX_TASK_LENGTH) {
task_ptr = &tasks.task[i];
// double check that the task needs to be linked and link the task
if (task_ptr->next == TASK_LINK_SCHEDULE) ScheduleTask(i);
if (task_ptr->count == TASK_UNLINK_SCHEDULE) UnScheduleTask(i);
} else {
// go through the whole task array and look for any tasks needing to
// be linked into the task queue
for (i = 0; i < MAX_TASK_LENGTH; i++) {
if (tasks.task[i].next == TASK_LINK_SCHEDULE) ScheduleTask(i);
if (tasks.task[i].count == TASK_UNLINK_SCHEDULE) UnScheduleTask(i);
}
}
}
if (TimeNow() != last_time) {
last_time = TimeNow();
// if the next task time is scheduled <= the current time
// add the task to the queue and update the next task scheduled
// repeat until the next schedule task is in the future
while (tasks.sch_first >= 0 && tasks.sch_first < MAX_TASK_LENGTH) {
// if the task is scheduled in the future then break
if (tasks.task[tasks.sch_first].time > TimeNow()) break;
// set i to the first scheduled task (soonest to run)
i = tasks.sch_first;
// update the first scheduled task to the current first's next task
tasks.sch_first = tasks.task[i].next;
if (tasks.sch_first >= 0 && tasks.sch_first < MAX_TASK_LENGTH) {
tasks.task[tasks.sch_first].previous = TASK_DONE;
}
// decrement the schedule length
tasks.sch_length--;
if (tasks.sch_length < 1) tasks.sch_last = TASK_DONE;
// queue the task
QueueTask(i);
}
if (TimeSince(service_tasks_time) > SERVICE_TASKS_PERIOD) {
service_tasks_time = TimeNow();
ServiceTasks();
}