This article describes how to call the API operations of Link SDK for C to implement device jobs. In this example, the ./demos/task_posix_demo.c sample code file is used.

Background information

  • For more information about device jobs, see Overview.

  • You must establish an MQTT connection. For more information, see Overview.

Step 1: Initialize a client

  1. Add header files.
    ...
    ...
    
    #include "aiot_task_api.h"
  2. Add the underlying dependency and configure the log output feature.
        aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
        aiot_state_set_logcb(demo_state_logcb);
  3. Call the aiot_task_init() operation to create a task client instance and initialize the default parameters.
        task_handle = aiot_task_init();
        if (task_handle == NULL) {
            demo_mqtt_stop(&mqtt_handle);
            printf("aiot_task_init failed\n");
            return -1;
        }

Step 2: Configure required features

Call the aiot_task_setopt() operation to configure the following items:

  1. Associate with an MQTT connection handle.
    Notice Before you set device job-specific parameters, make sure that device authentication information is specified. For more information, see Example.
    • Sample code
          aiot_task_setopt(task_handle, AIOT_TASKOPT_MQTT_HANDLE, mqtt_handle);
    • Parameters:
      Parameter Example Description
      AIOT_TASKOPT_MQTT_HANDLE mqtt_handle You must establish an MQTT connection. This parameter is used to associate with the MQTT connection handle.
  2. Configure a message callback.
    • Sample code
          aiot_task_setopt(task_handle, AIOT_TASKOPT_RECV_HANDLER, demo_task_recv_handler);
    • Parameters:
      Parameter Example Description
      AIOT_TASKOPT_RECV_HANDLER demo_task_recv_handler When a device job-specific message is received, this function is called.

Step 3: Receive a downstream job notification

If IoT Platform sends a job notification when the device is online, perform the following steps to process the notification:

  1. Log on to the IoT Platform console and create a device job.
  2. After you create the device job, IoT Platform sends a notification to the device. After the device receives the notification, the demo_task_recv_handler callback is called.
    When you specify the processing logic of the callback, take note of the following items:
    • aiot_task_recv_t is an input parameter of the callback. This parameter indicates the data format.
    • AIOT_TASKRECV_NOTIFY indicates the message type.
    • The following table describes a notification example and the Alink data format of the notification.
      Example Alink data format Description
      {
          "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"
            }
              }
          }
      }
      The message content is in the JSON format. The content is the value of the params parameter in the Alink data.
    • The device implements tasks under the job, and then submits the statuses of the tasks to IoT Platform. For more information about how to update tasks, see Step 5: Update the statuses of tasks under the job.
    • In this example, the logic to implement tasks is not provided. You must specify the processing logic based on your business needs.
      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.If no job records exist in the handle, save the job that is sent by IoT Platform to the default_task_desc field in the handle. */
                  if (NULL == g_local_task_desc) {
                      demo_copy_task_to_local_task(&g_local_task_desc, in_desc);
                      /* Start the job. In this example, the job information is printed. You can specify the logic based on your business needs. */
                      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 {
                          /* Set the type of the downloading thread to detach. After the firmware is obtained, the downloading thread automatically exits. */
                          pthread_detach(g_task_thread);
                      }
                      /* Change the job status.TODO: The following code is for reference only. After the job is completed, you must set the status to 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 the job status is set to the final status by IoT Platform, the local job is cleared. */
                  if (in_desc->status == AIOT_TASK_STATUS_CANCELLED || in_desc->status == AIOT_TASK_STATUS_REMOVED
                      || in_desc->status == AIOT_TASK_STATUS_TIMED_OUT) {
                      /* TODO: Clear the local job and stop the thread. */
                      /* If the job is the default job recorded in the handle, clear the memory of the job. Otherwise, you must maintain the memory of the job. */
                      if (NULL != g_local_task_desc && 0 == strcmp(in_desc->task_id, g_local_task_desc->task_id)) {
                          /* Release the memory of the local job. */
                          demo_free_local_task(&g_local_task_desc);
                      }
                      break;
                  }
      
                  /* 3.If a local job already exists, IoT Platform updates the description of the job. You must check the updated description. */
                  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: Update the description of the local job. You can suspend the current job based on your business needs. * /
                          break;
                      }
                  }
      
                  /* 4.In other cases, the received job is determined as a new job. If a job is running and a new job is received, you can create a list in the main() function. */
                  /* Pass in the list as the userdata parameter and record all jobs in this list for maintenance. */
                  break;
              }
      ……
      ... 
      }

Step 4: Request a device job

After the device goes online, you can request a device job.

  1. Call the aiot_task_get_task_detail operation to send a request to IoT Platform. If you set the parameter to NULL, you can obtain the information about the first job that is not implemented.
    Note If IoT Platform pushes multiple device jobs to the device, you can call the aiot_task_get_task_list operation to query the device jobs before you receive the job notifications. Then, you can query the information about each device job.
        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. After IoT Platform receives the request, IoT Platform returns the created device job information to the device.
  3. After the device receives the job notification, the demo_task_recv_handler callback is called.
    When you specify the processing logic of the callback, take note of the following items:
    • aiot_task_recv_t is an input parameter of the callback. This parameter indicates the data format.
    • AIOT_TASKRECV_GET_DETAIL_REPLY indicates the message type.
    • The following table describes a notification example and the Alink data format of the notification.
      Example Alink data format Description
      {
                "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"
            }
           }
          }
      }
      The message content is in the JSON format. The content is the value of the params parameter in the Alink data.
    • The device implements tasks under the job, and then submits the statuses of the tasks to IoT Platform. For more information about how to update tasks, see Step 5: Update the statuses of tasks under the job.
    • In this example, the logic to implement tasks is not provided. You must specify the processing logic based on your business needs.
      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: Implement the job by starting a thread. */
      
                          /* Change the job status. TODO: The following code is for reference only. You can specify the logic based on your business needs. After the task is completed, set the status to AIOT_TASK_STATUS_SUCCEEDED. */
                          task.status = AIOT_TASK_STATUS_IN_PROGRESS;
                          task.progress = 88;
                          aiot_task_update(handle, &task);
                      }
                  }
                  break;
              }
      ……
      ... 
      }

Step 5: Update the statuses of tasks under the job

After the device obtains the job information and implement tasks under the job, the device must submit task statuses to IoT Platform.

  1. Call the aiot_task_update operation to submit task statuses to IoT Platform.
    When you submit task statuses, take note of the following items:
    • task_desc_t indicates the data format.
    • aiot_task_update indicates the API operation.
    • The following example shows a message in the Alink format.
      Note The message content is in the JSON format. The content is the value of the params parameter in the Alink data.
      {
          "id": "123",
          "version": "1.0",
          "params": {
              "taskId": "i5Ks***F010101",
              "status": "IN_PROGRESS",
              "statusDetails": {
                  "key": "value"
              },
              "progress": 50
          }
      }
      The message content is in the JSON format. The content is the value of the params parameter in the Alink data.The message content is in the JSON format. The content is the value of the params parameter in the Alink data.Note The message content is in the JSON format. The content is the value of the params parameter in the Alink data.Themessage content is in the JSON format. The content is the value ofthe params parameter in the Alink data.
    • In this example, the AIOT_TASK_STATUS_IN_PROGRESS status is submitted. You must obtain task statuses in actual business scenarios and submit the statuses.
      • Sample code for Step 3
                        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);
      • Sample code for Step 4
                            task.status = AIOT_TASK_STATUS_IN_PROGRESS;
                            task.progress = 88;
                            aiot_task_update(handle, &task);
  2. After task statuses are submitted, IoT Platform returns a response message. In this case, the demo_task_recv_handler callback is called.
    When you specify the processing logic of the callback, take note of the following items:
    • aiot_task_recv_t is an input parameter of the callback. This parameter indicates the data format.
    • AIOT_TASKRECV_UPDATE_REPLY indicates the message type.
    • In this example, the logic to implement tasks is not provided. You must specify the processing logic based on your business needs.
      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;
              }
      ……
      ... 
      }

Step 6: Exit the program

Call the aiot_task_deinit() operation to destroy the task client instance and release resources.

    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;
    }

Step 7: End the connection

Note

MQTT connections are applied to devices that remain persistently connected. You can manually disconnect devices from IoT Platform.

In this example, the main thread is used to set parameters and establish a connection. After the connection is established, you can put the main thread to sleep.

Call the aiot_mqtt_disconnect operation to disconnect the device from IoT Platform.

    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;
    }

What to do next

  • After you configure the sample code file, compile the file to generate an executable file In this example, the ./output/task_posix_demo executable file is generated.

    For more information, see Compilation and running.

  • For more information about running results, see View logs.