tble:任务(task)

  • tble2是app实现的、从tble派生的类。
  • 特征写导致出的反馈,不管这次写的数据有多少,都是一次写对应一个反馈吗?
  • 须要增加执行任务的时间溢出机制。

 

任务由一组有序的操作组成,操作可能是notify特征、写特征或读特征。一个tble::ttask对象表示一个任务,一个tble::tstep对象表示一个操作。因为任务中操作是有序的,操作有时称为步骤。

只有在连接着状态下,才能执行任务。

任一时刻,最多只能运行着一个任务。

数个操作组成一个任务,一个操作指的是对一个特征进行读、写,或设置notify。

要执行一个操作了,tble会以参数start=true,调用虚函数app_task_callback,这时tble会忽略该函数的返回值。完成一操作了,tble也会调用app_task_callback,只是此时start=false,函数的返回值决定是不是要继续执行此任务。

 

一、app编程

必须要做两件事:创建任务和执行任务,如果需要的话,重载app_task_callback。

1.1 创建任务

若没意外,在tble2的构造函数创建任务。

enum {taskid_postready, ...};
tble2::tble2()
{
  ttask& task = insert_task(taskid_postready);
  task.insert(nposm, "1809", "2A1C", operator_notify);
  int interval = 10;
  task.insert(nposm, "CCC0", "CCC1", operator_write, (uint8_t*)&interval, 4);
  int now = (int)time(NULL);
  task.insert(nposm, "CCC0", "CCC2", operator_write, (uint8_t*)&now, 4);
}

设想这么个场景;一个能定时向center报告体温的蓝牙体温计(peripheral),初始化阶段需执行三个操作,1)向2A1C这个特征设置notify。2)向CCC1这个特征设置体温间隔5秒。3)向CCC2这个特征设置center端时间,以便体温计上报的时间有一个初始时刻。以上代码把这三个操作组成一个任务,任务标识是taskid_postready,然后计划在每次连接成功(peripheral就绪)后就自动执行。

ttask& insert_task(int id);

insert_task用于创建一个任务。参数是一个int类型的标识,为易记,建议定义成enum。

void ttask::insert(int at, const std::string& service, const std::string& characteristic, 
    int op, const uint8_t* data = NULL, const int len = 0);

insert用于向任务添加步骤。

  • at。计划是用于告知ttask把这步骤添加到哪位置,目前只能填nposm(-1),即尾部追加。
  • service。特征归属的service的uuid。
  • characteristic。特征的uuid。
  • op。操作类型。只能是:operator_notify(notify)、operator_write(write)或operator_read(read)。
  • data、len。写特征时,须提供要发送的数据,data是数据的内存地址,len是数据长度。notify、read时,这两参数填nullptr、0。

1.2 执行任务

void tble2::app_connect_peripheral(SDL_BlePeripheral& peripheral, const int error)
{
  if (!error) {
    tble::ttask& task = get_task(taskid_postready);
    task.execute(*this);
  }
}

先以任务标识为参数调用tble::get_tack,得到task对象,然后调用task.execute方法,任务便开始执行了。蓝牙连接成功后,会以error=0调用app_connect_peripheral,意味着每次连接成功(peripheral就绪)后就自动执行taskid_postready任务。

1.3 重载tble::app_task_callback

要执行一个操作了,或执行完了一个操作,tble会调用虚函数app_task_callback。

  • bool tble2::app_task_callback(ttask& task, int step_at, bool start)
  • task:要执行的哪个任务。
  • step_at:从0始。start=true时,要执行哪一步了。start=false时,执行完了哪一步。
  • start;指示是要开始执行操作调用的(true),还是执行完操作调用的(false)。

通过重载tble::app_task_callback,app可自定义执行任务过程中的一些逻辑。

  1. 写特征时,有时发送的数据是要到此时才能确定,在这里可以修改data、len。
  2. 如果是执行完成调用(start=false),而且该任务还有步骤未执行,但发现没必要继续了,可让返回false,提前结束任务。 
  3. 根据当前状态,调用task.execute开始执行另一个任务。
bool tble2::app_task_callback(ttask& task, int step_at, bool start)
{
  if (start) {
    if (task.id() == taskid_postready) {
      if (step_at == 2) {
        tstep& step = task.get_step(step_at);
          int now = (int)time(NULL);
          step.set_data((uint8_t*)&now, 4);
      }
    }
  }
}

步骤3的目的是要校对温度计时钟,这时距离创建taskid_postready已过去一段时间了,于是须要改为“此刻时间”发送到温度计。

 

二、tble如何实现任务

图1 tble处理任务
  • ble.current_task_。指向当前正在执行的任务。
  • ble.current_step_。指示当前任务正在执行第几步,0是第一步。

执行任务第二步或更后面,依赖蓝牙模块操作特征(write、read、notify)后都有反馈,执行这些步骤的tble::task_next_step都是在这三种回调函数内完成。

任一时刻,最多只能运行着一个任务。所以在ttask::exeucte,首先要求current_task_必须是nullptr。

在tble::task_next_step。因为app有可能在ble2::app_task_callback启动新任务,而current_task_指向正执行任务,为让那个ttask::exeucte能通过“current_task_ == nullptr”诊断,调用app_task_callback前,先把current_task_置为nullptr。如果app_task_callback没有启动新任务,再恢复回current_task_。如果有启动,那ttask::exeucte会调置正确的current_task_、current_step_。

全部评论: 0

    写评论: