./demos/task_posix_demo.c展示裝置任務功能。
背景資訊
步驟一:初始化
- 添加標頭檔。
…… …… #include "aiot_task_api.h"
配置底層依賴和日誌輸出。
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile); aiot_state_set_logcb(demo_state_logcb);
- 調用aiot_task_init(),建立
Task
task_handle = aiot_task_init(); if (task_handle == NULL) { demo_mqtt_stop(&mqtt_handle); printf("aiot_task_init failed\n"); return -1; }
步驟二:配置功能
調用aiot_task_setopt(),配置以下功能。
- 關聯MQTT串連的控制代碼。重要 配置裝置任務功能參數前,請確保已配置裝置認證資訊等相關參數。具體操作,請參見MQTT配置串連參數。
aiot_task_setopt(task_handle, AIOT_TASKOPT_MQTT_HANDLE, mqtt_handle);
配置項 樣本值 說明 AIOT_TASKOPT_MQTT_HANDLE mqtt_handle
- 配置訊息回調。
aiot_task_setopt(task_handle, AIOT_TASKOPT_RECV_HANDLER, demo_task_recv_handler);
配置項 樣本值 說明 AIOT_TASKOPT_RECV_HANDLER demo_task_recv_handler 接收裝置任務相關訊息時,調用該函數。
步驟三:裝置接收物聯網平台推送的裝置任務通知
當裝置處於線上狀態,如果物聯網平台下發了任務通知,您可以參照以下步驟處理該通知。
- 登入物聯網平台控制台,建立裝置任務。
- 裝置任務添加後,物聯網平台向裝置下發裝置任務通知,裝置接收通知後,觸發回呼函數
demo_task_recv_handler
。您可以參考以下內容,編寫回呼函數的處理邏輯:- 任務通知訊息的資料結構類型為aiot_task_recv_t,是回呼函數的入參。
- 任務通知訊息的類型為AIOT_TASKRECV_NOTIFY。
- 以下是通知訊息的樣本及其Alink資料格式:
樣本 Alink資料格式 說明 { "task": { "taskId": "i5Ks6***pF010101", "status": "SENT", "jobDocument": { }, "jobFile": { "signMethod": "Md5", "sign": "wssxff56dhdsd***", "fileUrl": "https://iotx-***.aliyuncs.com/***.zip" } } }
{ "id": "7542940", "version": "1.0", "params": { "task": { "taskId": "i5Ks6***pF010101", "status": "SENT", "jobDocument": { }, "jobFile":{ "signMethod":"Md5", "sign":"wssxff56dhdsd***", "fileUrl": "https://iotx-***.aliyuncs.com/***.zip" } } } }
任務通知的訊息的內容為JSON格式,是Alink格式資料中。 - 根據通知訊息內容,執行裝置任務下的作業,然後向物聯網平台上報作業的狀態。
- 範例程式碼沒有執行任務的邏輯,您需根據業務需要,編寫處理邏輯。
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: 以下代碼僅供參考,任務執行完畢時, 您需將狀態設定為AIOT_TASK_STATUS_SUCCEEDED*/ 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; } …… …… }
步驟四:裝置主動請求裝置任務訊息
裝置上線後,您可以主動擷取裝置任務資訊。
- 調用aiot_task_get_task_detail,向物聯網平台發送請求,當參數為NULL的時候,擷取未執行的第一個任務的資訊。說明 如果物聯網平台向裝置下推了多個裝置任務,在擷取裝置任務訊息前,您也可以調用aiot_task_get_task_list,擷取裝置工作清單,然後逐一擷取裝置任務的資訊。
res = aiot_task_get_task_detail(task_handle, NULL); if (res < STATE_SUCCESS) { aiot_task_deinit(&task_handle); demo_mqtt_stop(&mqtt_handle); return -1; }
- 物聯網平台接收到裝置的請求後,將已建立的裝置任務資訊,返回至裝置。
- 裝置接收物聯網平台返回的任務訊息後,觸發回呼函數
demo_task_recv_handler
。您可以參考以下內容,編寫回呼函數的處理邏輯:- 任務訊息的資料結構類型為aiot_task_recv_t,是回呼函數的入參。
- 任務訊息的類型為AIOT_TASKRECV_GET_DETAIL_REPLY。
- 以下是任務訊息的樣本及其Alink資料格式:
樣本 Alink資料格式 說明 { "taskId": "i5Ks***F010101", "status": "IN_PROGRESS", "jobDocument": { }, "jobFile":{ "signMethod":"Md5", "sign":"wssxff56dhdsd***", "fileUrl": "https://iotx-***.aliyuncs.com/***.zip" } }
{ "id": "1234", "code": 200, "data": { "taskId": "$next", "task":{ "taskId": "i5Ks***F010101", "status": "IN_PROGRESS", "jobDocument": { }, "jobFile":{ "signMethod":"Md5", "sign":"wssxff56dhdsd***", "fileUrl": "https://iotx-***.aliyuncs.com/***.zip" } } } }
任務的訊息內容為JSON格式,是Alink格式資料中params的值。 - 根據任務訊息內容,執行裝置任務下的作業,然後向物聯網平台上報作業的狀態。
- 範例程式碼沒有執行任務的邏輯,您需根據業務需要,編寫處理邏輯。
void demo_task_recv_handler(void *handle, const aiot_task_recv_t *packet, void *userdata) { switch (packet->type) { …… …… 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: 這裡僅為參考實現, 使用者可以根據實際情況來適配. 任務執行完畢時, 要將狀態設定為AIOT_TASK_STATUS_SUCCEEDED */ task.status = AIOT_TASK_STATUS_IN_PROGRESS; task.progress = 88; aiot_task_update(handle, &task); } } break; } …… …… }
步驟五:更新任務下作業狀態
裝置擷取任務資訊後,執行任務下的作業操作後,需向物聯網平台返回作業狀態。
- 調用aiot_task_update,向物聯網平台發起上報作業狀態的請求。上報任務狀態時,您需注意:
- 上報作業狀態訊息的資料結構是task_desc_t。
- aiot_task_update介面的調用方法。
- 對應的Alink報文格式如下:
{ "id": "123", "version": "1.0", "params": { "taskId": "i5Ks***F010101", "status": "IN_PROGRESS", "statusDetails": { "key": "value" }, "progress": 50 } }
- 範例程式碼上報的裝置狀態為AIOT_TASK_STATUS_IN_PROGRESS,您需根據業務實際情境擷取裝置的實際作業情況,上報作業狀態。
- 步驟三情境的範例程式碼:
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);
- 步驟四情境的範例程式碼:
task.status = AIOT_TASK_STATUS_IN_PROGRESS; task.progress = 88; aiot_task_update(handle, &task);
- 步驟三情境的範例程式碼:
- 裝置作業狀態上報後,物聯網平台返回應答報文,觸發回呼函數
demo_task_recv_handler
。您可以參考以下內容,編寫回呼函數的處理邏輯:- 應答報文的資料結構類型為aiot_task_recv_t,是回呼函數的入參。
- 應答報文訊息的類型為AIOT_TASKRECV_UPDATE_REPLY。
- 範例程式碼沒有執行任務的邏輯,您需根據業務需要,編寫處理邏輯。
void demo_task_recv_handler(void *handle, const aiot_task_recv_t *packet, void *userdata) { switch (packet->type) { …… …… case AIOT_TASKRECV_UPDATE_REPLY: { const task_update_reply_t *update_reply = &(packet->data.update_reply); printf("revice task update reply, code:[%d]\r\n", update_reply->code); if (200 == update_reply->code) { printf("revice task update reply, task_id:[%s]\r\n", update_reply->task_id); } if (71012 == update_reply->code) { printf("aiot_task_update task's status_details value must be json format\r\n"); } /* TODO */ break; } …… …… }
步驟六:退出裝置任務程式
調用aiot_task_deinit(),銷毀Task
res = aiot_task_deinit(&task_handle);
if (res < STATE_SUCCESS) {
demo_mqtt_stop(&mqtt_handle);
printf("aiot_task_deinit failed: -0x%04X\n", -res);
return -1;
}
步驟七:中斷連線
說明
MQTT接入常應用於長串連的裝置,程式通常不會運行至此。
常式的主線程任務為配置參數並成功建立串連。串連建立後,主線程可進入休眠。
調用aiot_mqtt_disconnect,向物聯網平台發送中斷連線的報文,然後斷開網路連接。
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);
return -1;
}