本文以C Link SDK中的Demo文件./demos/shadow_basic_demo.c为例,介绍如何调用Link SDK的API,展示设备影子功能。
               
               
                  
                     
背景信息
                  
                  
                     - 
                        设备影子功能的更多信息,请参见概述。
                         
- 设备影子功能基于MQTT接入,开发过程中涉及MQTT接入的代码说明,请参见MQTT接入。
步骤一:初始化
                  
                     - 添加头文件。……
……
#include "aiot_shadow_api.h"
 
- 配置底层依赖和日志输出。    aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
    aiot_state_set_logcb(demo_state_logcb);
 
- 调用aiot_shadow_init,创建Shadow客户端实例,并初始化默认参数。    shadow_handle = aiot_shadow_init();
    if (shadow_handle == NULL) {
        printf("aiot_shadow_init failed\n");
        return -1;
    }
 
步骤三:设备主动上报状态
                  设备在线时,主动上报设备状态到影子,应用程序主动获取设备影子状态。
                  
                     - 设备调用aiot_shadow_send,向物联网平台上报最新状态到影子。上报状态时,需注意:
                              - 上报状态消息的数据结构类型为aiot_shadow_msg_t,是aiot_shadow_send()的入参。
- 上报状态的消息类型为AIOT_SHADOWMSG_UPDATE。
                              
 int32_t demo_update_shadow(void *shadow_handle, char *reported_data, int64_t version)
{
    aiot_shadow_msg_t message;
    memset(&message, 0, sizeof(aiot_shadow_msg_t));
    message.type = AIOT_SHADOWMSG_UPDATE;
    message.data.update.reported = reported_data;
    message.data.update.version = version;
    return aiot_shadow_send(shadow_handle, &message);
}
 
 
- 设置上报状态消息的内容。
                           
                              - 示例代码:        res = demo_update_shadow(shadow_handle, "{\"LightSwitch\":1}", 0);
        if (res < 0) {
            printf("demo_delete_shadow_report failed, res = -0x%04x\r\n", -res);
        }
 
- 示例消息内容说明:
                                    
                                    
                                       
                                       
                                       
                                    
                                    
                                       
                                          | 示例 | Alink格式 | 说明 |  
                                          | {
    "LightSwitch": 1
}
 | {
  "method": "update", 
  "state": {
    "reported": {
      "LightSwitch": 1
    }
  }, 
  "version": 0
}
 | 上报的消息的内容为JSON格式,是Alink格式数据中state的值。详细说明,请参见设备主动上报状态。 示例代码的消息内容为: 
                                                将属性LightSwitch设置为1。将版本号设置为0。
                                                      
 说明 
                                                         
                                                            后续操作的版本号递增, 否则物联网平台将返回错误。如果将版本号设置为-1, 物联网平台则清空设备影子数据,并将版本号更新为0。 |  
 
 
 
- 物联网平台接收到设备影子的消息后,更新影子文件。然后,向设备返回应答报文。
- 设备接收应答报文后,触发回调函数demo_shadow_recv_handler。
                           您可以参考以下内容,编写回调函数的处理逻辑: 
                              - 应答报文的数据结构类型为aiot_shadow_recv_t,是回调函数的入参。
- 应答报文消息的类型为AIOT_SHADOWRECV_GENERIC_REPLY。
- 以下是应答报文示例及其Alink数据格式:
                                 
                                    
                                    
                                       
                                       
                                       
                                    
                                    
                                       
                                          | 示例 | Alink格式 | 说明 |  
                                          | {
    "status":"success",
    "version":0
}
 | {
  "method": "reply", 
  "payload": {
    "status": "success", 
    "version": 0
  }, 
  "timestamp": 1626317187
}
 | 应答报文的内容是Alink数据中payload的值。 示例消息表示上报状态成功。 |  
 
- 示例代码仅做打印处理。
 void demo_shadow_recv_handler(void *handle, const aiot_shadow_recv_t *recv, void *userdata)
{
    printf("demo_shadow_recv_handler, type = %d, productKey = %s, deviceName = %s\r\n",
            recv->type, recv->product_key, recv->device_name);
    switch (recv->type) {
        case AIOT_SHADOWRECV_GENERIC_REPLY: {
            const aiot_shadow_recv_generic_reply_t *generic_reply = &recv->data.generic_reply;
            printf("payload = \"%.*s\", status = %s, timestamp = %ld\r\n",
                   generic_reply->payload_len,
                   generic_reply->payload,
                   generic_reply->status,
                   (unsigned long)generic_reply->timestamp);
        }
……
……
        default:
            break;
    }
}
 
 
步骤四:应用程序改变设备状态
                  您可以通过应用程序,或登录物联网平台,向设备影子发送期望属性,最终实现改变设备状态。
                  
                     - 下发期望状态给设备影子。
                     
- 物联网平台根据下发的期望状态消息,更新影子内容。然后,下发该影子内容至设备。
- 设备接收影子文件后,触发回调函数demo_shadow_recv_handler。
                           您可以参考以下内容,编写回调函数的处理逻辑:
                              - 下发的期望状态消息的数据结构类型为aiot_shadow_recv_t,是回调函数的入参。
- 期望状态的消息类型为AIOT_SHADOWRECV_CONTROL。
                              
- 以下是期望状态消息示例及其Alink数据格式:
                                 
                                    
                                    
                                       
                                       
                                       
                                    
                                    
                                       
                                          | 示例 | Alink格式 | 说明 |  
                                          | {
    "state": {
        "desired": {
            "LightSwitch": 0
        }
    },
    "metadata": {
        "desired": {
            "LightSwitch": {
                "timestamp": 1626319658
            }
        }
    }
}
 | {
  "method": "control", 
  "payload": { 
   "state": {
        "desired": {
            "LightSwitch": 0
        }
    },
    "metadata": {
        "desired": {
            "LightSwitch": {
                "timestamp": 1626319658 
       }
      }
    }
  }, 
  "version": 2, 
  "timestamp": 1469564576
}
 | 期望状态消息的的内容是Alink数据中payload的值。 示例消息为设置LightSwitch的期望属性值为0。 |  
 
- 示例代码仅做打印处理。
 void demo_shadow_recv_handler(void *handle, const aiot_shadow_recv_t *recv, void *userdata)
{
    printf("demo_shadow_recv_handler, type = %d, productKey = %s, deviceName = %s\r\n",
            recv->type, recv->product_key, recv->device_name);
    switch (recv->type) {
……
……
        case AIOT_SHADOWRECV_CONTROL: {
            const aiot_shadow_recv_control_t *control = &recv->data.control;
            printf("payload = \"%.*s\", version = %ld\r\n",
                   control->payload_len,
                   control->payload,
                   (unsigned long)control->version);
        }
……
……
        default:
            break;
    }
}
 
 
- 设备更新状态后,上报最新的状态至设备影子,并处理应答报文。
                     
- 上报最新的状态后,调用aiot_shadow_send,删除期望属性。请求删除期望属性时,需注意:
                              - 删除期望属性消息的数据结构类型为aiot_shadow_msg_t,是aiot_shadow_send()的入参。
- 消息的类型为AIOT_SHADOWMSG_CLEAN_DESIRED。
                              
- 示例代码删除了期望属性的全部内容,并设置版本号为1。
 int32_t demo_clean_shadow_desired(void *shadow_handle, int64_t version)
{
    aiot_shadow_msg_t message;
    memset(&message, 0, sizeof(aiot_shadow_msg_t));
    message.type = AIOT_SHADOWMSG_CLEAN_DESIRED;
    message.data.clean_desired.version = version;
    return aiot_shadow_send(shadow_handle, &message);
}
……
……
        res = demo_clean_shadow_desired(shadow_handle, 1);
        if (res < 0) {
            printf("demo_clean_shadow_desired failed, res = -0x%04x\r\n", -res);
        }
 
 
- 删除属性的请求发送后,物联网平台返回应答报文,触发回调函数demo_shadow_recv_handler。
步骤五:设备主动获取设备影子内容
                  若应用程序发送指令时,设备离线。设备再次上线后,将主动获取设备影子内容。
                  
                     - 设备调用aiot_shadow_send,向物联网平台发送查询指令,获取设备影子内容。查询指令的消息类型为AIOT_SHADOWMSG_GET。int32_t demo_get_shadow(void *shadow_handle)
{
    aiot_shadow_msg_t message;
    memset(&message, 0, sizeof(aiot_shadow_msg_t));
    message.type = AIOT_SHADOWMSG_GET;
    return aiot_shadow_send(shadow_handle, &message);
}
……
……
        res = demo_get_shadow(shadow_handle);
        if (res < 0) {
            printf("demo_get_shadow failed, res = -0x%04x\r\n", -res);
        }
 
 
- 物联网平台收到查询指令后,返回查询结果。设备接收返回结果后,触发回调函数demo_shadow_recv_handler。您可以参考以下内容,编写回调函数的处理逻辑:
                              - 回调函数处理消息的数据结构类型为aiot_shadow_recv_t,是回调函数的入参。
- 消息的类型为AIOT_SHADOWRECV_GET_REPLY。
                              
- 以下是查询后返回的消息示例及其Alink数据格式:
                                 
                                    
                                    
                                       
                                       
                                       
                                    
                                    
                                       
                                          | 示例 | Alink格式 | 说明 |  
                                          | {
    "status": "success",
    "state": {
        "reported": {
        }
    },
    "metadata": {
        "reported": {
        }
    }
}
 | {
  "method": "reply", 
  "payload": {
    "status": "success",
    "state": {
        "reported": {
        }
    },
    "metadata": {
        "reported": {
        }
    }
}, 
  "version": 5, 
  "timestamp": 1626320690
}
 | 获取属性消息的内容是Alink数据中payload的值。 示例消息表示请求发送成功,获取的消息值未空,未上报过消息。 |  
 
- 示例代码仅做打印处理。
 void demo_shadow_recv_handler(void *handle, const aiot_shadow_recv_t *recv, void *userdata)
{
    printf("demo_shadow_recv_handler, type = %d, productKey = %s, deviceName = %s\r\n",
            recv->type, recv->product_key, recv->device_name);
    switch (recv->type) {
……
……
        case AIOT_SHADOWRECV_GET_REPLY: {
            const aiot_shadow_recv_get_reply_t *get_reply = &recv->data.get_reply;
            printf("payload = \"%.*s\", version = %ld\r\n",
                   get_reply->payload_len,
                   get_reply->payload,
                   (unsigned long)get_reply->version);
        }
        default:
            break;
    }
}
 
 
步骤六:设备主动删除影子属性
                  若设备端已经是最新状态,设备端可以主动发送指令,删除设备影子中保存的某条属性状态。
                  
                     - 设备调用aiot_shadow_send,向物联网平台发送删除指令,删除设备影子中的指定属性。发送删除指令时,需注意:
                              - 删除指令的数据结构类型为aiot_shadow_msg_t,是aiot_shadow_send()的入参。
- 删除指令的消息类型为AIOT_SHADOWMSG_DELETE_REPORTED。
                              
 int32_t demo_delete_shadow_report(void *shadow_handle, char *reported, int64_t version)
{
    aiot_shadow_msg_t message;
    memset(&message, 0, sizeof(aiot_shadow_msg_t));
    message.type = AIOT_SHADOWMSG_DELETE_REPORTED;
    message.data.delete_reporte.reported = reported;
    message.data.delete_reporte.version = version;
    return aiot_shadow_send(shadow_handle, &message);
}
 
 
- 设置删除指令的内容。
                           
                              - 示例代码:        res = demo_delete_shadow_report(shadow_handle, "{\"LightSwitch\":\"null\"}", 2);
        if (res < 0) {
            printf("demo_delete_shadow_report failed, res = -0x%04x\r\n", -res);
        }
 
- 示例消息内容说明:
                                    
                                    
                                       
                                       
                                       
                                    
                                    
                                       
                                          | 示例 | Alink格式 | 说明 |  
                                          | "{\"LightSwitch\":\"null\"}", 2
 | {
  "method": "delete", 
  "state": {
    "reported": {
      "LightSwitch": "null", 
    }
  }, 
  "version": 2
}
 | 删除指令消息的内容为JSON格式,是Alink格式数据中state的值。详细说明,请参见设备主动删除影子属性。 示例代码的消息内容为: 
                                                将属性LightSwitch设置为null,表示清除设备影子所有数据。将版本号设置为2。 |  
 
 
 
- 物联网平台收到删除指令后,返回应答报文。设备接收报文后,触发回调函数demo_shadow_recv_handler。
步骤七:退出程序
                  调用aiot_shadow_deinit,销毁Shadow客户端实例,释放资源。
    res = aiot_shadow_deinit(&shadow_handle);
    if (res < STATE_SUCCESS) {
        printf("aiot_shadow_deinit failed: -0x%04X\n", -res);
        return -1;
    }
               
                  
                     
后续步骤
                  
                  
                     - 例程文件配置完成后,需进行编译,生成可执行文件./output/shadow-basic-demo。
                        更多信息,请参见编译与运行。
                         
                           
 注意 
                              
                                 - 配置Demo文件时,请根据测试场景,取消上报状态、获取属性、删除期望或上报属性的相关代码两边的注释符号(/*和*/),并根据场景更换版本号。
- 更新版本号时,需注意:
                                    
                                       - 后续操作的版本号递增, 否则物联网平台将返回错误。
- 如果将版本号设置为-1, 物联网平台则清空设备影子数据,并将版本号更新为0。
 
 
 
 
- 
                        关于运行结果的详细说明,请参见运行日志。