全屋边缘网关SDK开发指南
更新时间:2020-03-17 16:45:04
一 VDA SDK接口
VDA(Vendor Device Access) SDK提供给厂商设备接入阿里人居云端服务的能力,主要有设备上云、数据流转等功能,厂商基于VDA SDK开发出厂商设备对应的driver程序,此driver程序负责与子设备通信。例如,当子设备状态变化时,调用vda_sdk_post_properties或vda_sdk_trigger_event接口上报数据。SDK功能接口都定义在linkedge/include/vda_sdk.h里面,具体说明如下。
1 VDA接口函数
1.1 系统函数
1.1.1 获取系统默认参数
vda_sdk_params_t * vda_sdk_get_default_params (void)
函数功能:获得默认的系统参数,获得的返回值(vda_sdk_params_t类型指针)不需要手动释放。
函数返回值:默认参数值
typedef struct vda_sdk_params_s vda_sdk_params_t;
获得的系统默认参数由vda_sdk_params_s结构体变量表示。具体内容如下:
struct vda_sdk_params_s {
int maxMsgSize; //最大消息长度 默认 20480
int threadStackSize; //线程栈大小
int log_to_file; //log 输出文件
int (event_cb) (vda_sdk_event_t ev, void *ctx); //属性事件处理回调函数集合,ev:传递事件+事件参数
void *ctx; //传递给回调函数的参数
}
1.1.2 修改系统参数
int vda_sdk_set_option (vda_sdk_params_t params, int option, void value, int value_len)
函数功能:对VDA SDK的默认系统参数进行修改
函数描述:
option 选项 | Default | minimum | maximum | |
---|---|---|---|---|
VDA_OPT_MAX_MSG_SIZE | 最大消息长度 | 20480 | 512 | 51200 |
VDA_OPT_THREAD_STACK_SIZE | 线程栈大小 | 8192 | 1024 | 8388608 |
VDA_OPT_LOG_TO_FILE | SDK的log打印方式,0为标准输出,1为输出到文件 | 1 | 0 | 1 |
函数返回值:设置成功返回0,失败返回 -1
1.1.3 设置系统事件回调函数
int vda_sdk_set_event_callback (vda_sdk_params_t* params, event_cb, void *ctx)
函数功能:通过注册事件回调函数来监听系统的内部状态变化,通过下面的接口设置回调函数。
函数描述:
@param params 系统参数,vda_sdk_get_default_params(.html)函数获取的返回值
@param event_cb 事件发生时的回调函数
@param ctx 传递给回调函数的数据
函数返回值:设置成功返回0,失败返回 -1
注意:vda_sdk_set_event_callback在调用vda初始化函数前设置好回调函数,否则回调无法生效。
int (event_cb)(vda_sdk_event_t ev, void *ctx)回调函数参数如下:
其中event_type为事件类型,event_data为对应的数据。
event_data 为union类型,SDK会根据不同的事件类型给event_data的对应成员赋值
typedef struct {
int event_type; //对应事件类型
union {
struct {
char *productKey;
char *deviceName;
} dev_deleted; //VDA_EVENT_DEVICE_DELETED
...
...
...
} event_data; //传递事件相关的数据
} vda_sdk_event_t
事件类型:
enum {
VDA_EVENT_CLOUD_DISCONNECTED = 0, / cloud disconnected /
VDA_EVENT_CLOUD_CONNECTED = 1, / cloud connected /
VDA_EVENT_DEVICE_DELETED = 2, / device deleted /
VDA_EVENT_DEVICE_PERMITED = 3, / device permit join /
VDA_EVENT_DEIVCE_SETUP = 4, / device install /
VDA_EVENT_DEVICE_DISCOVER = 5, / device discover /
VDA_EVENT_DEVICE_RESET = 6, / device reset /
};
1.1.4 VDA初始化
int vda_sdk_init(const char *module_name, vda_sdk_params_t *params)
函数功能:VDA接口初始化
函数描述:
函数返回值:设置成功返回0,失败返回 -1
1.1.5 VDA退出
int vda_sdk_exit (void)
函数功能:退出VDA实例
函数返回值:成功返回0,失败返回 -1
1.1.6 注册设备配置信息变化(可选)
int vda_sdk_register_config_changed_callback(const char *module_name, cb)
函数功能:注册驱动配置变化回调(可选),可以通过这个函数修改注册设备配置变化回调函数。
函数描述:
int (cb)(const char module_name, const char *config)
module_name可以不是当前驱动的名字。但与module_name相关的驱动配置推送到边缘网关时,回调函数cb将被调用。
函数返回值:成功返回0,失败返回 其他
1.2 设备创建与管理
1.2.1 设备注册
int vda_sdk_device_register(const char *productKey, const char *deviceName,vda_sdk_cbs_t *cbs , void *ctx)
函数功能:设备注册,并设定设备对应功能的回调函数。调用此接口完成子设备注册并上线。
函数描述:
@param productKey 设备的产品ID,即pk
@param deviceName 设备的设备名称,即dn
@param cbs 要设置的回调函数, 回调函数的集合,用于处理下行的数据
@param ctx 传递给回调函数的参数
函数返回值:成功返回0,失败返回 -1
typedef struct {
int (get_property)(const char in, char out, int out_len, void ctx);
int (set_property)(const char in, void *ctx);
int (call_service)(const char identifier, const char in, char out, int out_len, void *ctx);
ssize_t (down_rawdata)(const void in, int in_len, void out, int out_len, void ctx);
} vda_sdk_cbs_t
注意: 如果在云端创建的产品是ICA协议的,上述的结构体只需要设置get_property,set_property和call_service三个函数指针;如果创建的产品是透传的,上述结构体只需要设置down_rawdata这个函数指针。目前不支持四个函数指针都设置。
1.2.2 设备上线
int vda_sdk_device_online(const char *productKey, const char *name)
函数功能:设置设备为上线状态
函数描述:
函数返回值:成功返回0,失败返回 -1
1.2.3 设备下线
int vda_sdk_device_offline(const char *productKey, const char *name)
函数功能:设置设备为下线状态
函数描述:
函数返回值:成功返回0,失败返回 -1
1.2.4 上报设备属性
int vda_sdk_post_properties(const char *productKey, const char *name, const char *properties)
函数功能:将设备的属性变更发布到云端
函数描述:
函数返回值:成功返回0,失败返回 -1
1.2.5 上报设备事件
int vda_sdk_trigger_event(const char *productKey, const char *name, const char *identifier, const char *events)
函数功能:将设备的事件发布到云端
函数描述:
@param productKey 设备的产品ID,即pk
@param name 设备的设备名称,即dn
@param identifier 事件的标识符,有关事件标识符的详细信息,请参见TSL
@param properties 属性数据,json格式
函数返回值:成功返回0,失败返回 -1
1.2.6 上报设备的原始数据
int vda_sdk_post_rawdata(const char *productKey, const char *name, const char *data, int len)
函数功能:将设备的原始数据发布到云端。
函数描述:
函数返回值:成功返回0,失败返回 -1
1.2.7 获取网关子设备列表大小
int vda_sdk_get_devicelist_size()
函数功能:从设备管理模块获取子设备列表的大小
函数返回值:返回设备列表大小,大于0即为成功
1.2.8 获取网关子设备列表
int vda_sdk_get_devicelist(char *buff, int size)
函数功能:从设备管理模块获取子设备列表
函数描述:
函数返回值: 成功返回 0,-1 internal error,-2 size of buffer too samll
1.2.9 重置网关或子设备数据
int vda_sdk_device_reset(const char *productKey, const char *deviceName)
函数功能:重置网关,解除包括网关与全部子设备的拓扑关系,网关与用户的关联关系;重置网关子设备,解除包括子设备与网关的拓扑关系,子设备与用户的关联关系。
函数描述:
函数返回值:成功返回0,失败返回 -1
2 使用流程
2.1 接口初始化
SDK的初始化包括以下步骤
1、获取默认参数
调用系统接口vda_sdk_get_default_params获得默认的系统参数,获得的默认参数不需要释放。
sample:
vda_sdk_params_t *params;
...
params = vda_sdk_get_default_params();
2、修改默认参数
调用vda_sdk_set_option接口,对SDK的默认参数进行修改。
sample:
int opt_val;
...
...
opt_val = 8192;
if (vda_sdk_set_option(params, VDA_OPT_MAX_MSG_SIZE, &opt_val, sizeof(opt_val)) < 0) {
log_e(TAG`, "set VDA_OPT_MAX_MSG_SIZE failed!\n");
return -1;
}//设置最大消息队列大小为8192
opt_val = 0;
if (vda_sdk_set_option(params, VDA_OPT_LOG_TO_FILE, &opt_val, sizeof(opt_val)) < 0) {
log_e(TAG, "set VDA_OPT_LOG_LEVEL failed!\n");
return -1;
}//设置系统输出路径为标准输出
3、设置事件回调
SDK通过注册事件回调来通知用户SDK的内部状态变化,通过下面的接口设置SDK的回调函数。 vda_sdk_set_event_callback(params, event_cb, NULL)
回调函数的示例代码
static int event_cb(vda_sdk_event_t ev, void ctx)
{
log_i(DRIVER_NAME, "event %d", ev->event_type);
switch (ev->event_type) {
case VDA_EVENT_CLOUD_CONNECTED:
/*在此处处理成功连接云连接的消息*/
break;
case VDA_EVENT_CLOUD_DISCONNECTED:
/*在此处处理断开云连接的消息,消息通知存在几分钟延时*/
break;
case VDA_EVENT_DEVICE_DELETED:
/*处理删除子设备,此时使用的是event_data的dev_deleted成员结构体*/
devmgr_destroy_device(ev->event_data.dev_deleted.productKey, ev->event_data.dev_deleted.deviceName);
break;
case VDA_EVENT_DEVICE_PERMITED:
/*云端下发添加网关子设备的消息通知*/
break;
}
return 0;
}
注意: vda_sdk_set_event_callback需要在调用vda_sdk_init前设置好回调函数,否则回调无法生效。
4、初始化VDA接口
调用vda_sdk_init对SDK进行初始化。
在设置好初始化参数以后,可以将初始化参数作为vda_sdk_init的输入参数,对SDK进行初始化。
vda_sdk_init (DRIVER_NAME, params);
5、注册驱动配置变化回调(可选)
通过vda_sdk_register_config_changed_callback这个函数注册驱动配置变化回调。
2.2 功能调用
2.2.1 设备的创建和管理
VDA初始化时设定好相应的系统事件回调处理函数后,在事件VDA_EVENT_DEVICE_PERMITED消息中获取待绑定的子设备的pk信息,再调用设备注册函数将获取到子设备进行注册上线。
sample:
vda_sdk_device_register("productKey", "deviceName", cbs, Interface);
//cbs为子设备操作回调函数,Interface为传递给回调函数的参数
2.2.2 设备属性和事件上报
ICA设备属性和事件上报
事件上报:
事件的构成有2部分:
(1)用于标识事件类型的identifier
(2)事件对应的数据
SDK事件上报的接口和调用示例如下:
int vda_sdk_trigger_event(const char productKey, const char name, const char identifier, const char events);
sample:
vda_sdk_trigger_event("productKey", "deviceName", "ErrorCode", "{"ErrorCode": 0}")
属性上报:
当设备的属性发生了变化,可以调用如下的接口上报变更的属性
int vda_sdk_post_properties(const char productKey, const char name, const char *properties);
例如子设备为单键开关类型,子设备属性上报示例如下:
sample:
vda_sdk_post_properties("productKey", "deviceName", ""PowerSwitch_1":0")
透传设备属性与事件上报
设备的原始数据可以通过以下接口上报给云端,需要在云端添加数据解析脚本,来转换原始数据格式到ICA Alink格式。
int vda_sdk_post_rawdata(const char productKey, const char name, const char *data, int len);
sample:
memset(hex_buffer, 0, sizeof(hex_buffer));
rawdata_len = str_to_hex(arg_properties, strlen(arg_properties), hex_buffer, sizeof(hex_buffer));
if (vda_sdk_post_rawdata(productKey, deviceName, hex_buffer, rawdata_len) < 0) {
printf("post rawdata failed!\n");
continue;
}
二 HAL接口
HAL接口需要设备厂商实现,接口定义在linkedge/include/hal.h里面,用来提供设备上云需要的网关设备证书信息(ProductKey\DeviceName\DeviceSecret),用于网关系统整体升级的网关固件版本信息和固件升级相关的接口。
厂商实现HAL的功能函数后,编译生成libhal.so文件,替换掉linkedge/lib/libhal.so文件,重启linkedge即可生效。
1.1 函数定义
HAL_GetProductKey 获取网关的ProductKey
HAL_GetDeviceName 获取网关的DeviceName
HAL_GetDeviceSecret 获取网关的DeviceSecret
HAL_GetFirmwareVersion 获取网关系统版本号
HAL_UpdateFirmware 由厂商接收固件文件并进行升级
HAL_RebootSystem 网关系统重启
int HAL_GetProductKey(char productKey[PRODUCT_KEY_MAXLEN]);
int HAL_GetDeviceName(char device_name[DEVICE_NAME_MAXLEN]);
int HAL_GetDeviceSecret(char device_secret[DEVICE_SECRET_MAXLEN]);
int HAL_GetFirmwareVersion(char version[FIRMWARE_VERSION_MAXLEN]);
int HAL_UpdateFirmware(const char *filename);
int HAL_RebootSystem(void);
1.2 示例代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include "hal.h"
#ifndef TAG
#define TAG "HAL"
#endif
static int get_project_root(char *dir)
{
static char abs_proj_root[FILENAME_MAX + 1] = "../";
char rel_proj_root[FILENAME_MAX + 1];
int len = readlink("/proc/self/exe", rel_proj_root, FILENAME_MAX);
if (len <= 0) {
return 1;
}
rel_proj_root[len] = '\0';
char *path_end = strrchr(rel_proj_root, '/');
if (path_end) {
*path_end = '\0';
}
strcat(rel_proj_root, "/../");
char *real_path = realpath(rel_proj_root, abs_proj_root);
if (NULL == real_path) {
strcpy(dir, rel_proj_root);
return 1;
}
strcpy(dir, abs_proj_root);
return 0;
}
static int get_file_content(char *file_path, char *content, int size)
{
FILE *fp = NULL;
fp = fopen(file_path, "r");
if (fp != NULL)
{
char *line = NULL;
if (size > 0) line = malloc(size);
if (line && fgets(line, size, fp) != NULL)
{
char *find = strchr(line, '\n');
if(find) *find = '\0';
strncpy(content, line, size - 1);
}
fclose(fp);
fp = NULL;
free(line);
line = NULL;
}
return 0;
}
int HAL_GetProductKey(char productKey[PRODUCT_KEY_MAXLEN])
{
char file_path[FILENAME_MAX + 1] = { 0 };
get_project_root(file_path);
strcat(file_path, "/config/gateway_pk");
printf("gateway product key file path is %s\n", file_path);
memset(productKey, 0, PRODUCT_KEY_MAXLEN);
get_file_content(file_path, productKey, PRODUCT_KEY_MAXLEN);
return strlen(productKey);
}
int HAL_GetDeviceName(char deviceName[DEVICE_NAME_MAXLEN])
{
char file_path[FILENAME_MAX + 1] = { 0 };
get_project_root(file_path);
strcat(file_path, "/config/gateway_dn");
printf("gateway product key file path is %s\n", file_path);
memset(deviceName, 0, DEVICE_NAME_MAXLEN);
get_file_content(file_path, deviceName, DEVICE_NAME_MAXLEN);
return strlen(deviceName);
}
int HAL_GetDeviceSecret(char deviceSecret[DEVICE_SECRET_MAXLEN])
{
char file_path[FILENAME_MAX + 1] = { 0 };
get_project_root(file_path);
strcat(file_path, "/config/gateway_ds");
printf("gateway product key file path is %s\n", file_path);
memset(deviceSecret, 0, DEVICE_SECRET_MAXLEN);
get_file_content(file_path, deviceSecret, DEVICE_SECRET_MAXLEN);
return strlen(deviceSecret);
}
int HAL_GetFirmwareVersion(char version[FIRMWARE_VERSION_MAXLEN])
{
strncpy(version, "1.0", FIRMWARE_VERSION_MAXLEN - 1);
return strlen(version);
}
int HAL_UpdateFirmware(const char *filename)
{
int fd = open(filename, O_RDONLY);
if (fd < 0) {
printf("Open firmware file %s failed %s\n", filename, strerror(errno));
return -1;
}
printf("Updating firmware...\n");
sleep(3);
close(fd);
printf("Upgrade firmware success\n");
return 0;
}
int HAL_RebootSystem(void)
{
printf("Rebooting system...\n");
return 0;
}
1.3 编译命令参考
gcc -fPIC -shared hal.c -o libhal.so
三 日志接口
全屋边缘网关的日志采用集中式管理,日志接口定义在linkedge/include/logger.h里面,例如log_d接口用来打印debug级别的日志,log_e接口用来打印错误级别的日志。厂商可选用此方式来输出driver程序日志信息。日志存储在linkedge/run/logger目录下,以程序模块名称分类。默认日志大小为10M,写满后自动循环滚动。