/** 
 *  TaskScheduler测试程序 - 使用任务的本地任务存储指针
 *  测试用例：
 *    整体测试运行5秒
 *    多个计算器任务每秒运行一次，使用本地任务存储指针更新各自的变量
 *    所有计算器任务使用相同的回调代码，通过LTS指针获取适当变量的引用
 *    计算器任务执行简单计算（作为示例）：
 *      将任务ID号加到自身
 *      将任务ID号乘以10
 *      
 *  在整体测试完成后，打印所有结果。
 *  可以使用不同数量的计算器任务重复测试。
 *  需要更改的只是数据定义 - 代码完全不知道任务的数量
 */
 
#define _TASK_SLEEP_ON_IDLE_RUN  // 编译时支持在没有任务调度时进入空闲睡眠状态1毫秒
#define _TASK_WDT_IDS           // 编译时支持任务ID和看门狗定时器
#define _TASK_LTS_POINTER       // 编译时支持本地任务存储指针
#include <TaskScheduler.h>  // 包含任务调度器库

// 计算器任务的总数：
#define NO_TASKS  3

Scheduler ts;  // 创建调度器对象

// 回调方法原型
void Calculate(); bool CalcOn();
bool WrapperOn(); void WrapperOff(); 

// 任务
// 计算器任务。
// 注意所有三个任务使用相同的回调方法
// 它们将基于本地任务存储指针更新特定变量
Task t1(TASK_SECOND, TASK_FOREVER, &Calculate, &ts, false, &CalcOn);  // 任务1：每秒运行一次，无限期运行，初始禁用，有使能回调
Task t2(TASK_SECOND, TASK_FOREVER, &Calculate, &ts, false, &CalcOn);  // 任务2：每秒运行一次，无限期运行，初始禁用，有使能回调
Task t3(TASK_SECOND, TASK_FOREVER, &Calculate, &ts, false, &CalcOn);  // 任务3：每秒运行一次，无限期运行，初始禁用，有使能回调
// 如果需要，可以在这里添加更多计算任务

Task tWrapper(5*TASK_SECOND, TASK_ONCE, NULL, &ts, false, &WrapperOn, &WrapperOff);  // 包装任务：5秒运行一次，运行一次，初始禁用，有使能和禁用回调 

// 下面是由LTS指针引用的结构
typedef struct {
  unsigned int id;      // 任务ID
  long         sum;       // 求和结果
  long         product;   // 乘积结果
} task_var;

// 这些是保存任务特定值的实际结构
task_var v1;  // 任务1的变量
 task_var v2;  // 任务2的变量
 task_var v3;  // 任务3的变量

// 下面的数组允许索引访问特定任务和任务变量
Task     *tasks[] = { &t1, &t2, &t3 };  // 任务指针数组
task_var *vars[]  = { &v1, &v2, &v3 };  // 变量指针数组


/**
 * 当包装任务被启用时调用此方法
 * 目的是为所有任务提供LTS指针
 */
bool WrapperOn() {

  for (int i=0; i < NO_TASKS; i++) {
    Task& T = *tasks[i];  // 获取任务引用
    T.setLtsPointer( vars[i] );  // 设置LTS指针
    T.enableDelayed();  // 延迟启用任务
  }
  
  return true;  // 信号表示任务可以被启用
}

/**
 * 当包装任务被禁用（执行第一次也是唯一一次迭代后）时调用此方法
 * 打印每个计算器任务的结果
 */
void WrapperOff() {
  Serial.println("Finished processing");  // 打印处理完成信息
  
  ts.disableAll();  // 禁用所有任务
  
  for (int i=0; i < NO_TASKS; i++) {
    Serial.print("ID: "); Serial.println(vars[i]->id);  // 打印任务ID
    Serial.print("Sum: "); Serial.println(vars[i]->sum);  // 打印求和结果
    Serial.print("Product: "); Serial.println(vars[i]->product);  // 打印乘积结果
    Serial.println();  // 打印空行
  }
}


/**
 * 当每个计算器任务被启用时执行此方法
 * 目的是初始化所有本地变量
 */
bool CalcOn() {
  Task& T = ts.currentTask();  // 获取当前任务
  task_var& var = *((task_var*) T.getLtsPointer());  // 获取LTS指针并转换为task_var引用
  
// 初始化本地变量
  var.id = T.getId();  // 设置任务ID
  var.sum = 0;  // 初始化求和为0
  var.product = var.id;  // 初始化乘积为任务ID
  
  return true;  // 返回true使任务启用
}


/**
 * 此方法对任务的本地变量执行简单计算
 */
void Calculate() {
  Task& T = ts.currentTask();  // 获取当前任务
// 获取LTS指针的另一种方式：  
  task_var& var = *((task_var*) ts.currentLts());


  Serial.print("Calculating for task: ");  // 打印计算任务信息
  Serial.print(T.getId());  // 打印任务ID
  Serial.print("; Task id per LTS is: ");  // 打印LTS中的任务ID
  Serial.println( var.id );  // 打印变量ID
  
  var.sum += T.getId();  // 将任务ID加到求和变量
  var.product = var.product * 10;  // 将乘积变量乘以10
  
}


/**
 * Standard Arduino setup and loop methods
 */
void setup() {
  Serial.begin(115200);  // 初始化串口通信，波特率115200

  randomSeed(analogRead(0)+analogRead(5));  // 使用模拟引脚0和5的值初始化随机数种子
  
  pinMode(13, OUTPUT);  // 设置引脚13为输出模式
  digitalWrite(13, LOW);  // 设置引脚13为低电平
 
  Serial.println("Local Task Storage pointer test");  // 打印测试信息

  tWrapper.enableDelayed();  // 延迟启用包装任务
}

void loop() {
  ts.execute();  // 执行调度器，运行所有已安排的任务
}