全部产品

任务管理

功能描述

任务管理用于对设备中将要执行的任务进行部署和管理:

如上图流程说明, 设备通过调用api往阿里云发起获取任务的请求, 从MQTT长连接通道收到并执行阿里云推送的新任务, 然后将任务状态同步到阿里云。

API列表

接口名

说明

aiot_task_init

创建task客户端实例并设置默认参数

aiot_task_deinit

销毁task客户端实例, 回收资源

aiot_task_get_task_detail

查询当前设备的某一个任务

aiot_task_get_task_list

查询当前设备的任务列表

aiot_task_update

更新当前任务的状态

aiot_task_setopt

设置任务实例的参数

任务状态:

状态

说明

AIOT_TASK_STATUS_QUEUED

服务端设置的状态: 任务处于队列中, 还没有推送

AIOT_TASK_STATUS_SENT

服务端设置的状态: 任务已推送

AIOT_TASK_STATUS_IN_PROGRESS

设备端设置的状态: 任务进行中.

AIOT_TASK_STATUS_SUCCEEDED

设备端设置的状态: 任务完成

AIOT_TASK_STATUS_FAILED

设备端设置的状态: 任务执行失败

AIOT_TASK_STATUS_REJECTED

设备端设置的状态: 设备端拒绝执行任务

AIOT_TASK_STATUS_CANCELLED

服务端设置的状态: 任务被服务端取消

AIOT_TASK_STATUS_REMOVED

服务端设置的状态: 任务从服务端删除

AIOT_TASK_STATUS_TIMED_OUT

服务端设置的状态: 任务执行超时

AIOT_TASK_STATUS_NOT_FOUND

服务端设置的状态: 没有找到此任务相关信息

API使用概述

参考任务管理协议页面, 了解设备端进行任务管理时的网络交互流程:

  • 设备调用aiot_task_get_task_detail 向云端请求一个可执行的任务, 或则请求一个任务的详细信息

  • 设备要使用任务管理服务, 可以调用`aiot_task_get_task_list` 向云端请求任务的列表, 进而从中选一个, 调用`aiot_task_get_task_detail`去获取它的详情

  • 任务执行的过程中和结束的时候, 要调用`aiot_task_update` 向云端更新任务的状态

例程讲解

现对照`components/task/demos/task_posix_demo.c`例程, 分步骤讲解如何使用API:

这个例程适用于`Linux`这类支持pthread的POSIX设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建2个线程:

  • 一个线程用于保活长连接

  • 一个线程用于接收消息, 并在有消息到达时进入默认的数据回调, 在连接状态变化时进入事件回调

需要用户关注或者修改的部分, 已用 `TODO` 在注释中标明

设置设备的三元组

例程使用的三元组是公用的, 所以应用于实际业务时, 请替换如下的`TODO`部分, 传入用户自己真实的三元组

* TODO: 替换为自己设备的三元组 */
    char *product_key       = "a13FN5TplKq";
    char *device_name       = "linksdk_task_demo";
    char *device_secret     = "5c7714848639a32274ea36fee99f39ef";

建立MQTT连接

任务管理需要从MQTT通道接收设备的任务信息, 所以程序的主体中, 建立MQTT连接仍是必须的

    int32_t     res = STATE_SUCCESS;
    void       *mqtt_handle = NULL, *task_handle = NULL;

    /* 建立MQTT连接, 并开启保活线程和接收线程 */
    res = demo_mqtt_start(&mqtt_handle);
    if (res < 0) {
        printf("demo_mqtt_start failed\n");
        return -1;
    }

创建task会话实例

与单纯MQTT连云场景不同的是, 还应为TASK会话创建单独的实例, 并将它与MQTT会话实例关联起来, 再发起MQTT连接。

    task_handle = aiot_task_init();
    /* 用以下语句, 把TASK会话和MQTT会话关联起来 */
    aiot_task_setopt(task_handle, AIOT_TASKOPT_MQTT_HANDLE, mqtt_handle);
    /* task会话设置了数据接收回调, 为demo_task_recv_handler函数 */
    res = aiot_task_setopt(task_handle, AIOT_TASKOPT_RECV_HANDLER, demo_task_recv_handler);

向云端获取任务

aiot_task_get_detail的第二个入参为NULL, 云端会返回该设备名下处于QUEUED, SENT, IN_PROGRESS三种中的状态的任务列表中的头一个

    aiot_task_get_detail(task_handle, NULL);

任务管理消息到达时, 开始任务处理

以上程序主体已讲解完毕, 此后, 如果SDK从MQTT通道收到了任务管理的消息, 开始做处理当控制台任务部署完毕后再启动task_posix_demo程序时, 任务管理的回调函数会从AIOT_TASKRECV_GET_DETAIL_REPLY这里出来当先启动task_posix_demo程序再部署任务, 任务管理的回调函数会从AIOT_TASKRECV_NOTIFY这里出来

void demo_task_recv_handler(void *handle, const aiot_task_recv_t *packet, void *userdata)
{
    switch (packet->type) {
        case AIOT_TASKRECV_NOTIFY: {
            const task_desc_t *in_desc = &(packet->data.notify);

            printf("revice task notify, task_id:[%s],status:[%d],job_document[%s],document_file_url:[%s],\
                sign_method:[%s],sign[%s]\r\n",
                   in_desc->task_id, in_desc->status, in_desc->job_document,
                   in_desc->document_file_url, in_desc->sign_method, in_desc->sign);

            /* 1.在handle里面没有任务记录的情况下, 把云端下行的task保存到handle里面的default_task_desc字段 */
            if (NULL == g_local_task_desc) {
                demo_copy_task_to_local_task(&g_local_task_desc, in_desc);
                /* 启动任务. 这里仅为参考实现, 用户可以根据实际情况来适配 */
                int res = pthread_create(&g_task_thread, NULL, demo_task_thread, g_local_task_desc);
                if (res != 0) {
                    printf("pthread_create demo_task_thread failed: %d\r\n", res);
                } else {
                    /* 下载线程被设置为 detach 类型, 固件内容获取完毕后可自主退出 */
                    pthread_detach(g_task_thread);
                }
                /* 变更任务状态.TODO: 这里仅为参考实现, 用户可以根据实际情况来适配 */
                g_local_task_desc->status = AIOT_TASK_STATUS_IN_PROGRESS;
                aiot_task_update(handle, g_local_task_desc);
                demo_free_local_task(&g_local_task_desc);
                break;
            }

            /* 2.如果状态被云端设置为终态, 则在这里将本地的任务清理掉 */
            if (in_desc->status == AIOT_TASK_STATUS_CANCELLED || in_desc->status == AIOT_TASK_STATUS_REMOVED
                || in_desc->status == AIOT_TASK_STATUS_TIMED_OUT) {
                /* TODO: 清理本地任务, 停下线程 */
                /* 如果该任务是记录在handle里面的默认任务, 则要把它的内存清理掉; 如果是记录在handle外的, 需要用户自己维护内存 */
                if (NULL != g_local_task_desc && 0 == strcmp(in_desc->task_id, g_local_task_desc->task_id)) {
                    /* 释放本地任务内存 */
                    demo_free_local_task(&g_local_task_desc);
                }
                break;
            }

            /* 3.在本地已有任务记录的情况,云端可能更新了当前这个任务的描述, 需要检查一下更新的内容 */
            if (in_desc->status == AIOT_TASK_STATUS_IN_PROGRESS) {
                if (NULL != g_local_task_desc && 0 == strcmp(in_desc->task_id, g_local_task_desc->task_id)) {
                    /* TODO: 更新本地的任务描述. 用户可能要暂停当前的任务再更新, 这一点取决于用户 */
                    break;
                }
            }

            /* 4.如果不是上述情况, 说明来了一个新任务. 此时用户已经有一个任务在执行了, 又来了一个任务, 用户可以在main中自己创建一个列表 */
            /* 将这个列表作为userdata传进来, 并把任务记录在这个列表里面, 自己维护*/
            break;
        }
        case AIOT_TASKRECV_GET_DETAIL_REPLY: {
            const task_get_detail_reply_t *in_reply = &(packet->data.get_detail_reply);
            printf("revice task get detail, code:[%d]\r\n", in_reply->code);
            if (200 == in_reply->code) {
                printf("revice task get detail reply, task_id:[%s],status:[%d]\r\n",
                       in_reply->task.task_id, in_reply->task.status);
                if (in_reply->task.status != AIOT_TASK_STATUS_NOT_FOUND) {
                    printf("job_document[%s],document_file_url:[%s], sign_method:[%s],sign[%s]\r\n",
                           in_reply->task.job_document, in_reply->task.document_file_url,
                           in_reply->task.sign_method, in_reply->task.sign);
                    task_desc_t task;
                    memset(&task, 0, sizeof(task));
                    memcpy(&task, &(in_reply->task), sizeof(task));
                    /* TODO: 执行任务, 可以通过起线程的方式 */
                   
                    /* 变更任务状态. TODO: 这里仅为参考实现, 用户可以根据实际情况来适配 */
                    task.status = AIOT_TASK_STATUS_IN_PROGRESS;
                    task.progress = 88;
                    aiot_task_update(handle, &task);
                }
            }
            break;
        }
        ...
        default:
            /* TODO */
            break;
    }
}

观察运行日志

编译 `output/task-posix-demo` 后, 先在控制台网页操作部署一个任务, 如下图所示:

然后再运行demo程序, 得到的一次完整运行日志,如下图所示:

$ ./output/task-posix-demo
MacBook-Pro-3:united-sdk xicai.cxc$ ./output/task-posix-demo
[1610544010.746][LK-0313] MQTT user calls aiot_mqtt_connect api, connect
[1610544010.746][LK-0317] linksdk_task_demo&a13FN5TplKq
[1610544010.746][LK-0318] 16A0AC6AF73F792F1E68EE92D106E1235BD1178489CF14C9A44C9C3595D6468B
establish mbedtls connection with server(host='a13FN5TplKq.iot-as-mqtt.cn-shanghai.aliyuncs.com', port=[1883])
success to establish tcp, fd=5
success to establish mbedtls connection, fd = 5(cost 45134 bytes in total, max used 47870 bytes)
[1610544010.823][LK-0313] MQTT connect success in 77 ms
AIOT_MQTTEVT_CONNECT
[1610544010.823][LK-0309] pub: /sys/a13FN5TplKq/linksdk_task_demo/thing/job/get

[LK-030A] > 7B 22 69 64 22 3A 22 31  22 2C 22 76 65 72 73 69 | {"id":"1","versi
[LK-030A] > 6F 6E 22 3A 22 31 2E 30  2E 30 22 2C 22 70 61 72 | on":"1.0.0","par
[LK-030A] > 61 6D 73 22 3A 7B 22 74  61 73 6B 49 64 22 3A 22 | ams":{"taskId":"
[LK-030A] > 24 6E 65 78 74 22 7D 7D                          | $next"}}

heartbeat response
[1610544010.900][LK-0309] pub: /sys/a13FN5TplKq/linksdk_task_demo/thing/job/get_reply

[LK-030A] < 7B 22 63 6F 64 65 22 3A  32 30 30 2C 22 64 61 74 | {"code":200,"dat
[LK-030A] < 61 22 3A 7B 22 74 61 73  6B 22 3A 7B 22 6A 6F 62 | a":{"task":{"job
[LK-030A] < 44 6F 63 75 6D 65 6E 74  22 3A 7B 22 68 65 6C 6C | Document":{"hell
[LK-030A] < 6F 22 3A 22 68 69 22 7D  2C 22 74 61 73 6B 49 64 | o":"hi"},"taskId
[LK-030A] < 22 3A 22 64 66 61 62 66  30 31 33 33 30 63 37 34 | ":"dfabf01330c74
[LK-030A] < 33 32 65 61 65 66 37 61  38 34 30 65 37 39 39 66 | 32eaef7a840e799f
[LK-030A] < 66 33 33 22 2C 22 73 74  61 74 75 73 22 3A 22 51 | f33","status":"Q
[LK-030A] < 55 45 55 45 44 22 7D 2C  22 74 61 73 6B 49 64 22 | UEUED"},"taskId"
[LK-030A] < 3A 22 24 6E 65 78 74 22  7D 2C 22 69 64 22 3A 22 | :"$next"},"id":"
[LK-030A] < 31 22 2C 22 6D 65 74 68  6F 64 22 3A 22 74 68 69 | 1","method":"thi
[LK-030A] < 6E 67 2E 6A 6F 62 2E 67  65 74 22 2C 22 76 65 72 | ng.job.get","ver
[LK-030A] < 73 69 6F 6E 22 3A 22 31  2E 30 22 7D             | sion":"1.0"}

revice task get detail, code:[200]
revice task get detail reply, task_id:[dfabf01330c7432eaef7a840e799ff33],status:[0]
job_document[{"hello":"hi"}],document_file_url:[(null)], sign_method:[(null)],sign[(null)]
[1610544010.900][LK-0309] pub: /sys/a13FN5TplKq/linksdk_task_demo/thing/job/update

[LK-030A] > 7B 22 69 64 22 3A 22 32  22 2C 22 76 65 72 73 69 | {"id":"2","versi
[LK-030A] > 6F 6E 22 3A 22 31 2E 30  2E 30 22 2C 22 70 61 72 | on":"1.0.0","par
[LK-030A] > 61 6D 73 22 3A 7B 22 74  61 73 6B 49 64 22 3A 22 | ams":{"taskId":"
[LK-030A] > 64 66 61 62 66 30 31 33  33 30 63 37 34 33 32 65 | dfabf01330c7432e
[LK-030A] > 61 65 66 37 61 38 34 30  65 37 39 39 66 66 33 33 | aef7a840e799ff33
[LK-030A] > 22 2C 22 73 74 61 74 75  73 22 3A 22 49 4E 5F 50 | ","status":"IN_P
[LK-030A] > 52 4F 47 52 45 53 53 22  2C 22 70 72 6F 67 72 65 | ROGRESS","progre
[LK-030A] > 73 73 22 3A 38 38 2C 22  73 74 61 74 75 73 44 65 | ss":88,"statusDe
[LK-030A] > 74 61 69 6C 73 22 3A 7B  22 6B 65 79 22 3A 20 22 | tails":{"key": "
[LK-030A] > 76 61 6C 75 65 22 7D 7D  7D                      | value"}}}

[1610544010.937][LK-0309] pub: /sys/a13FN5TplKq/linksdk_task_demo/thing/job/update_reply

[LK-030A] < 7B 22 63 6F 64 65 22 3A  32 30 30 2C 22 64 61 74 | {"code":200,"dat
[LK-030A] < 61 22 3A 7B 22 74 61 73  6B 49 64 22 3A 22 64 66 | a":{"taskId":"df
[LK-030A] < 61 62 66 30 31 33 33 30  63 37 34 33 32 65 61 65 | abf01330c7432eae
[LK-030A] < 66 37 61 38 34 30 65 37  39 39 66 66 33 33 22 7D | f7a840e799ff33"}
[LK-030A] < 2C 22 69 64 22 3A 22 32  22 2C 22 6D 65 74 68 6F | ,"id":"2","metho
[LK-030A] < 64 22 3A 22 74 68 69 6E  67 2E 6A 6F 62 2E 75 70 | d":"thing.job.up
[LK-030A] < 64 61 74 65 22 2C 22 76  65 72 73 69 6F 6E 22 3A | date","version":
[LK-030A] < 22 31 2E 30 22 7D                                | "1.0"}

revice task update reply, code:[200]
revice task update reply, task_id:[dfabf01330c7432eaef7a840e799ff33]