全部產品
Search
文件中心

:使用樣本

更新時間:Jun 30, 2024

./demos/task_posix_demo.c展示裝置任務功能。

背景資訊

  • 裝置任務功能的更多資訊,請參見概述

  • 裝置任務功能基於MQTT接入,開發過程中涉及MQTT接入的代碼說明,請參見MQTT接入

步驟一:初始化

  1. 添加標頭檔。
    ……
    ……
    
    #include "aiot_task_api.h"

  2. 配置底層依賴和日誌輸出。

        aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
        aiot_state_set_logcb(demo_state_logcb);
  3. 調用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(),配置以下功能。

  1. 關聯MQTT串連的控制代碼。
    重要 配置裝置任務功能參數前,請確保已配置裝置認證資訊等相關參數。具體操作,請參見MQTT配置串連參數
    •     aiot_task_setopt(task_handle, AIOT_TASKOPT_MQTT_HANDLE, mqtt_handle);
    • 配置項樣本值說明
      AIOT_TASKOPT_MQTT_HANDLEmqtt_handle

  2. 配置訊息回調
    •     aiot_task_setopt(task_handle, AIOT_TASKOPT_RECV_HANDLER, demo_task_recv_handler);
    • 配置項樣本值說明
      AIOT_TASKOPT_RECV_HANDLERdemo_task_recv_handler接收裝置任務相關訊息時,調用該函數。

步驟三:裝置接收物聯網平台推送的裝置任務通知

當裝置處於線上狀態,如果物聯網平台下發了任務通知,您可以參照以下步驟處理該通知。

  1. 登入物聯網平台控制台,建立裝置任務。
  2. 裝置任務添加後,物聯網平台向裝置下發裝置任務通知,裝置接收通知後,觸發回呼函數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;
              }
      ……
      …… 
      }

步驟四:裝置主動請求裝置任務訊息

裝置上線後,您可以主動擷取裝置任務資訊。

  1. 調用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;
        }
                            
  2. 物聯網平台接收到裝置的請求後,將已建立的裝置任務資訊,返回至裝置。
  3. 裝置接收物聯網平台返回的任務訊息後,觸發回呼函數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;
              }
      ……
      …… 
      }

步驟五:更新任務下作業狀態

裝置擷取任務資訊後,執行任務下的作業操作後,需向物聯網平台返回作業狀態。

  1. 調用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);
  2. 裝置作業狀態上報後,物聯網平台返回應答報文,觸發回呼函數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;
    }

後續步驟