更新硬件SDK

This commit is contained in:
kerwincui
2023-03-04 03:44:56 +08:00
parent dcdf6e1b7c
commit e39d3d2f03
1900 changed files with 663153 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
Name: 物模型模块
Data-Model Component for Link SDK V4.0.0

View File

@@ -0,0 +1,834 @@
/**
* @file aiot_dm_api.c
* @brief 数据模型模块接口实现文件, 包含了支持物模型数据格式通信的所有接口实现
* @date 2020-01-20
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#include "dm_private.h"
static int32_t _dm_send_property_post(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg);
static int32_t _dm_send_event_post(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg);
static int32_t _dm_send_property_set_reply(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg);
static int32_t _dm_send_async_service_reply(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg);
static int32_t _dm_send_sync_service_reply(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg);
static int32_t _dm_send_raw_data(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg);
static int32_t _dm_send_raw_service_reply(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg);
static int32_t _dm_send_desired_get(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg);
static int32_t _dm_send_desired_delete(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg);
static int32_t _dm_send_property_batch_post(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg);
static void _dm_recv_generic_reply_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata);
static void _dm_recv_property_set_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata);
static void _dm_recv_async_service_invoke_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata);
static void _dm_recv_sync_service_invoke_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata);
static void _dm_recv_raw_data_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata);
static void _dm_recv_raw_sync_service_invoke_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata);
static void _dm_recv_up_raw_reply_data_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata);
static const dm_send_topic_map_t g_dm_send_topic_mapping[AIOT_DMMSG_MAX] = {
{
"/sys/%s/%s/thing/event/property/post",
_dm_send_property_post
},
{
"/sys/%s/%s/thing/event/%s/post",
_dm_send_event_post
},
{
"/sys/%s/%s/thing/service/property/set_reply",
_dm_send_property_set_reply
},
{
"/sys/%s/%s/thing/service/%s_reply",
_dm_send_async_service_reply
},
{
"/ext/rrpc/%s/sys/%s/%s/thing/service/%s",
_dm_send_sync_service_reply
},
{
"/sys/%s/%s/thing/model/up_raw",
_dm_send_raw_data
},
{
"/ext/rrpc/%s/sys/%s/%s/thing/model/down_raw",
_dm_send_raw_service_reply
},
{
"/sys/%s/%s/thing/property/desired/get",
_dm_send_desired_get
},
{
"/sys/%s/%s/thing/property/desired/delete",
_dm_send_desired_delete
},
{
"/sys/%s/%s/thing/event/property/batch/post",
_dm_send_property_batch_post
},
};
static const dm_recv_topic_map_t g_dm_recv_topic_mapping[] = {
{
"/sys/+/+/thing/event/+/post_reply",
_dm_recv_generic_reply_handler,
},
{
"/sys/+/+/thing/service/property/set",
_dm_recv_property_set_handler,
},
{
"/sys/+/+/thing/service/+",
_dm_recv_async_service_invoke_handler,
},
{
"/ext/rrpc/+/sys/+/+/thing/service/+",
_dm_recv_sync_service_invoke_handler,
},
{
"/sys/+/+/thing/model/down_raw",
_dm_recv_raw_data_handler,
},
{
"/sys/+/+/thing/model/up_raw_reply",
_dm_recv_up_raw_reply_data_handler,
},
{
"/ext/rrpc/+/sys/+/+/thing/model/down_raw",
_dm_recv_raw_sync_service_invoke_handler,
},
{
"/sys/+/+/thing/property/desired/get_reply",
_dm_recv_generic_reply_handler,
},
{
"/sys/+/+/thing/property/desired/delete_reply",
_dm_recv_generic_reply_handler,
},
{
"/sys/+/+/thing/event/property/batch/post_reply",
_dm_recv_generic_reply_handler,
},
};
static void _append_diag_data(dm_handle_t *dm_handle, uint8_t msg_type, int32_t msg_id)
{
/* append diagnose data */
uint8_t diag_data[] = { 0x00, 0x30, 0x01, 0x00, 0x00, 0x31, 0x04, 0x00, 0x00, 0x00, 0x00 };
diag_data[3] = msg_type;
diag_data[7] = (msg_id >> 24) & 0xFF;
diag_data[8] = (msg_id >> 16) & 0xFF;
diag_data[9] = (msg_id >> 8) & 0xFF;
diag_data[10] = msg_id & 0xFF;
core_diag(dm_handle->sysdep, STATE_DM_BASE, diag_data, sizeof(diag_data));
}
static int32_t _dm_setup_topic_mapping(void *mqtt_handle, void *dm_handle)
{
uint32_t i = 0;
int32_t res = STATE_SUCCESS;
for (i = 0; i < sizeof(g_dm_recv_topic_mapping) / sizeof(dm_recv_topic_map_t); i++) {
aiot_mqtt_topic_map_t topic_mapping;
topic_mapping.topic = g_dm_recv_topic_mapping[i].topic;
topic_mapping.handler = g_dm_recv_topic_mapping[i].func;
topic_mapping.userdata = dm_handle;
res = aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_APPEND_TOPIC_MAP, &topic_mapping);
if (res < 0) {
break;
}
}
return res;
}
static int32_t _dm_prepare_send_topic(dm_handle_t *dm_handle, const aiot_dm_msg_t *msg, char **topic)
{
char *src[4];
uint8_t src_count = 0;
char *pk = NULL;
char *dn = NULL;
if (NULL == msg->product_key && NULL == core_mqtt_get_product_key(dm_handle->mqtt_handle)) {
return STATE_USER_INPUT_MISSING_PRODUCT_KEY;
}
if (NULL == msg->device_name && NULL == core_mqtt_get_device_name(dm_handle->mqtt_handle)) {
return STATE_USER_INPUT_MISSING_DEVICE_NAME;
}
pk = (msg->product_key != NULL) ? msg->product_key : core_mqtt_get_product_key(dm_handle->mqtt_handle);
dn = (msg->device_name != NULL) ? msg->device_name : core_mqtt_get_device_name(dm_handle->mqtt_handle);
switch (msg->type) {
case AIOT_DMMSG_PROPERTY_POST:
case AIOT_DMMSG_PROPERTY_BATCH_POST:
case AIOT_DMMSG_PROPERTY_SET_REPLY:
case AIOT_DMMSG_GET_DESIRED:
case AIOT_DMMSG_DELETE_DESIRED:
case AIOT_DMMSG_RAW_DATA: {
src[0] = pk;
src[1] = dn;
src_count = 2;
}
break;
case AIOT_DMMSG_EVENT_POST: {
if (msg->data.event_post.event_id == NULL) {
return STATE_DM_EVENT_ID_IS_NULL;
}
src[0] = pk;
src[1] = dn;
src[2] = msg->data.event_post.event_id;
src_count = 3;
}
break;
case AIOT_DMMSG_ASYNC_SERVICE_REPLY: {
if (msg->data.async_service_reply.service_id == NULL) {
return STATE_DM_SERVICE_ID_IS_NULL;
}
src[0] = pk;
src[1] = dn;
src[2] = msg->data.async_service_reply.service_id;
src_count = 3;
}
break;
case AIOT_DMMSG_SYNC_SERVICE_REPLY: {
if (msg->data.sync_service_reply.rrpc_id == NULL) {
return STATE_DM_RRPC_ID_IS_NULL;
}
if (msg->data.sync_service_reply.service_id == NULL) {
return STATE_DM_SERVICE_ID_IS_NULL;
}
src[0] = msg->data.sync_service_reply.rrpc_id;
src[1] = pk;
src[2] = dn;
src[3] = msg->data.sync_service_reply.service_id;
src_count = 4;
}
break;
case AIOT_DMMSG_RAW_SERVICE_REPLY: {
if (msg->data.raw_service_reply.rrpc_id == NULL) {
return STATE_DM_RRPC_ID_IS_NULL;
}
src[0] = msg->data.raw_service_reply.rrpc_id;
src[1] = pk;
src[2] = dn;
src_count = 3;
}
break;
default:
return STATE_USER_INPUT_OUT_RANGE;
}
return core_sprintf(dm_handle->sysdep, topic, g_dm_send_topic_mapping[msg->type].topic, src, src_count,
DATA_MODEL_MODULE_NAME);
}
static int32_t _dm_send_alink_req(dm_handle_t *handle, const char *topic, char *params)
{
char *payload = NULL;
int32_t id = 0;
char id_string[11] = { 0 };
char *src[3] = { NULL };
int32_t res = STATE_SUCCESS;
if (NULL == params) {
return STATE_DM_MSG_PARAMS_IS_NULL;
}
core_global_alink_id_next(handle->sysdep, &id);
core_int2str(id, id_string, NULL);
_append_diag_data(handle, DM_DIAG_MSG_TYPE_REQ, id);
src[0] = id_string;
src[1] = params;
src[2] = (0 == handle->post_reply) ? "0" : "1";
res = core_sprintf(handle->sysdep, &payload, ALINK_REQUEST_FMT, src, sizeof(src) / sizeof(char *),
DATA_MODEL_MODULE_NAME);
if (res < 0) {
return res;
}
res = aiot_mqtt_pub(handle->mqtt_handle, (char *)topic, (uint8_t *)payload, strlen(payload), 0);
handle->sysdep->core_sysdep_free(payload);
if (STATE_SUCCESS == res) {
return id;
}
return res;
}
static int32_t _dm_send_alink_rsp(dm_handle_t *handle, const char *topic, uint64_t msg_id, uint32_t code,
char *data)
{
char *payload = NULL;
char id_string[21] = { 0 };
char code_string[11] = { 0 };
char *src[3] = { NULL };
int32_t res = STATE_SUCCESS;
if (NULL == data) {
return STATE_DM_MSG_DATA_IS_NULL;
}
core_uint642str(msg_id, id_string, NULL);
core_uint2str(code, code_string, NULL);
src[0] = id_string;
src[1] = code_string;
src[2] = data;
res = core_sprintf(handle->sysdep, &payload, ALINK_RESPONSE_FMT, src, sizeof(src) / sizeof(char *),
DATA_MODEL_MODULE_NAME);
if (res < 0) {
return res;
}
res = aiot_mqtt_pub(handle->mqtt_handle, (char *)topic, (uint8_t *)payload, strlen(payload), 0);
handle->sysdep->core_sysdep_free(payload);
return res;
}
/*** dm send function start ***/
static int32_t _dm_send_property_post(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg)
{
return _dm_send_alink_req(handle, topic, msg->data.property_post.params);
}
static int32_t _dm_send_event_post(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg)
{
return _dm_send_alink_req(handle, topic, msg->data.event_post.params);
}
static int32_t _dm_send_property_set_reply(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg)
{
return _dm_send_alink_rsp(handle, topic, msg->data.property_set_reply.msg_id,
msg->data.property_set_reply.code,
msg->data.property_set_reply.data);
}
static int32_t _dm_send_async_service_reply(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg)
{
return _dm_send_alink_rsp(handle, topic, msg->data.async_service_reply.msg_id,
msg->data.async_service_reply.code,
msg->data.async_service_reply.data);
}
static int32_t _dm_send_sync_service_reply(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg)
{
return _dm_send_alink_rsp(handle, topic, msg->data.sync_service_reply.msg_id,
msg->data.sync_service_reply.code,
msg->data.sync_service_reply.data);
}
static int32_t _dm_send_raw_data(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg)
{
return aiot_mqtt_pub(handle->mqtt_handle, (char *)topic, msg->data.raw_data.data, msg->data.raw_data.data_len, 0);
}
static int32_t _dm_send_raw_service_reply(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg)
{
return aiot_mqtt_pub(handle->mqtt_handle, (char *)topic, msg->data.raw_service_reply.data,
msg->data.raw_service_reply.data_len, 0);
}
static int32_t _dm_send_desired_get(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg)
{
return _dm_send_alink_req(handle, topic, msg->data.get_desired.params);
}
static int32_t _dm_send_desired_delete(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg)
{
return _dm_send_alink_req(handle, topic, msg->data.delete_desired.params);
}
static int32_t _dm_send_property_batch_post(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg)
{
return _dm_send_alink_req(handle, topic, msg->data.property_post.params);
}
/*** dm send function end ***/
/*** dm recv handler functions start ***/
static int32_t _dm_get_topic_level(aiot_sysdep_portfile_t *sysdep, char *topic, uint32_t topic_len, uint8_t level,
char **level_name)
{
uint32_t i = 0;
uint16_t level_curr = 0;
char *p_open = NULL;
char *p_close = NULL;
char *p_name = NULL;
uint16_t name_len = 0;
for (i = 0; i < (topic_len - 1); i++) {
if (topic[i] == '/') {
level_curr++;
if (level_curr == level && p_open == NULL) {
p_open = topic + i + 1;
}
if (level_curr == (level + 1) && p_close == NULL) {
p_close = topic + i;
}
}
}
if (p_open == NULL) {
return STATE_DM_INTERNAL_TOPIC_ERROR;
}
if (p_close == NULL) {
p_close = topic + topic_len;
}
name_len = p_close - p_open;
p_name = sysdep->core_sysdep_malloc(name_len + 1, DATA_MODEL_MODULE_NAME);
if (p_name == NULL) {
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
memset(p_name, 0, name_len + 1);
memcpy(p_name, p_open, name_len);
*level_name = p_name;
return STATE_SUCCESS;
}
static int32_t _dm_parse_alink_request(const char *payload, uint32_t payload_len, uint64_t *msg_id, char **params,
uint32_t *params_len)
{
char *value = NULL;
uint32_t value_len = 0;
int32_t res = STATE_SUCCESS;
if ((res = core_json_value((char *)payload, payload_len, ALINK_JSON_KEY_ID, strlen(ALINK_JSON_KEY_ID),
&value, &value_len)) < 0 ||
((res = core_str2uint64(value, value_len, msg_id)) < 0)) {
return res;
}
if ((res = core_json_value((char *)payload, payload_len, ALINK_JSON_KEY_PARAMS, strlen(ALINK_JSON_KEY_PARAMS),
&value, &value_len)) < 0) {
return res;
}
*params = value;
*params_len = value_len;
return res;
}
static void _dm_recv_generic_reply_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata)
{
dm_handle_t *dm_handle = (dm_handle_t *)userdata;
aiot_dm_recv_t recv;
char *value = NULL;
uint32_t value_len = 0;
if (NULL == dm_handle->recv_handler) {
return;
}
/* construct recv message */
memset(&recv, 0, sizeof(aiot_dm_recv_t));
recv.type = AIOT_DMRECV_GENERIC_REPLY;
core_log(dm_handle->sysdep, STATE_DM_LOG_RECV, "DM recv generic reply\r\n");
do {
if (_dm_get_topic_level(dm_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, 2, &recv.product_key) < 0 ||
_dm_get_topic_level(dm_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, 3, &recv.device_name) < 0) {
break; /* must be malloc failed */
}
if ((core_json_value((char *)msg->data.pub.payload, msg->data.pub.payload_len,
ALINK_JSON_KEY_ID, strlen(ALINK_JSON_KEY_ID), &value, &value_len)) < 0 ||
(core_str2uint(value, value_len, &recv.data.generic_reply.msg_id)) < 0 ||
(core_json_value((char *)msg->data.pub.payload, msg->data.pub.payload_len,
ALINK_JSON_KEY_CODE, strlen(ALINK_JSON_KEY_CODE), &value, &value_len)) < 0 ||
(core_str2uint(value, value_len, &recv.data.generic_reply.code)) < 0 ||
(core_json_value((char *)msg->data.pub.payload, msg->data.pub.payload_len,
ALINK_JSON_KEY_DATA, strlen(ALINK_JSON_KEY_DATA),
&recv.data.generic_reply.data,
&recv.data.generic_reply.data_len)) < 0) {
core_log(dm_handle->sysdep, SATAE_DM_LOG_PARSE_RECV_MSG_FAILED, "DM parse generic reply failed\r\n");
break;
}
core_json_value((char *)msg->data.pub.payload, msg->data.pub.payload_len,
ALINK_JSON_KEY_MESSAGE, strlen(ALINK_JSON_KEY_MESSAGE),
&recv.data.generic_reply.message,
&recv.data.generic_reply.message_len);
_append_diag_data(dm_handle, DM_DIAG_MSG_TYPE_RSP, recv.data.generic_reply.msg_id);
dm_handle->recv_handler(dm_handle, &recv, dm_handle->userdata);
} while (0);
DM_FREE(recv.product_key);
DM_FREE(recv.device_name);
}
static void _dm_recv_property_set_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata)
{
dm_handle_t *dm_handle = (dm_handle_t *)userdata;
aiot_dm_recv_t recv;
if (NULL == dm_handle->recv_handler) {
return;
}
/* construct recv message */
memset(&recv, 0, sizeof(aiot_dm_recv_t));
recv.type = AIOT_DMRECV_PROPERTY_SET;
core_log(dm_handle->sysdep, STATE_DM_LOG_RECV, "DM recv property set\r\n");
do {
if (_dm_get_topic_level(dm_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, 2, &recv.product_key) < 0 ||
_dm_get_topic_level(dm_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, 3, &recv.device_name) < 0) {
break; /* must be malloc failed */
}
if ((_dm_parse_alink_request((char *)msg->data.pub.payload, msg->data.pub.payload_len,
&recv.data.property_set.msg_id,
&recv.data.property_set.params,
&recv.data.property_set.params_len)) < 0) {
core_log(dm_handle->sysdep, SATAE_DM_LOG_PARSE_RECV_MSG_FAILED, "DM parse property set failed\r\n");
break;
}
dm_handle->recv_handler(dm_handle, &recv, dm_handle->userdata);
} while (0);
DM_FREE(recv.product_key);
DM_FREE(recv.device_name);
}
static void _dm_recv_async_service_invoke_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata)
{
dm_handle_t *dm_handle = (dm_handle_t *)userdata;
aiot_dm_recv_t recv;
if (NULL == dm_handle->recv_handler) {
return;
}
memset(&recv, 0, sizeof(aiot_dm_recv_t));
recv.type = AIOT_DMRECV_ASYNC_SERVICE_INVOKE;
core_log(dm_handle->sysdep, STATE_DM_LOG_RECV, "DM recv async service invoke\r\n");
do {
if (_dm_get_topic_level(dm_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, 2, &recv.product_key) < 0 ||
_dm_get_topic_level(dm_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, 3, &recv.device_name) < 0 ||
_dm_get_topic_level(dm_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, 6,
&recv.data.async_service_invoke.service_id) < 0) {
break;
}
if ((_dm_parse_alink_request((char *)msg->data.pub.payload, msg->data.pub.payload_len,
&recv.data.async_service_invoke.msg_id,
&recv.data.async_service_invoke.params,
&recv.data.async_service_invoke.params_len)) < 0) {
/* core_log(dm_handle->sysdep, SATAE_DM_LOG_PARSE_RECV_MSG_FAILED, "DM parse async servicey failed\r\n"); */
break;
}
dm_handle->recv_handler(dm_handle, &recv, dm_handle->userdata);
} while (0);
DM_FREE(recv.product_key);
DM_FREE(recv.device_name);
DM_FREE(recv.data.async_service_invoke.service_id);
}
static void _dm_recv_sync_service_invoke_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata)
{
dm_handle_t *dm_handle = (dm_handle_t *)userdata;
aiot_dm_recv_t recv;
if (NULL == dm_handle->recv_handler) {
return;
}
memset(&recv, 0, sizeof(aiot_dm_recv_t));
recv.type = AIOT_DMRECV_SYNC_SERVICE_INVOKE;
core_log(dm_handle->sysdep, STATE_DM_LOG_RECV, "DM recv sync service invoke\r\n");
do {
if (_dm_get_topic_level(dm_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, 5, &recv.product_key) < 0 ||
_dm_get_topic_level(dm_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, 6, &recv.device_name) < 0 ||
_dm_get_topic_level(dm_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, 3,
&recv.data.sync_service_invoke.rrpc_id) < 0 ||
_dm_get_topic_level(dm_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, 9,
&recv.data.sync_service_invoke.service_id) < 0) {
break;
}
if ((_dm_parse_alink_request((char *)msg->data.pub.payload, msg->data.pub.payload_len,
&recv.data.sync_service_invoke.msg_id,
&recv.data.sync_service_invoke.params,
&recv.data.sync_service_invoke.params_len)) < 0) {
core_log(dm_handle->sysdep, SATAE_DM_LOG_PARSE_RECV_MSG_FAILED, "DM parse sync service failed\r\n");
break;
}
dm_handle->recv_handler(dm_handle, &recv, dm_handle->userdata);
} while (0);
DM_FREE(recv.data.sync_service_invoke.rrpc_id);
DM_FREE(recv.product_key);
DM_FREE(recv.device_name);
DM_FREE(recv.data.sync_service_invoke.service_id);
}
static void _dm_recv_raw_data_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata)
{
dm_handle_t *dm_handle = (dm_handle_t *)userdata;
aiot_dm_recv_t recv;
if (NULL == dm_handle->recv_handler) {
return;
}
memset(&recv, 0, sizeof(aiot_dm_recv_t));
recv.type = AIOT_DMRECV_RAW_DATA;
core_log(dm_handle->sysdep, STATE_DM_LOG_RECV, "DM recv raw data\r\n");
do {
if (_dm_get_topic_level(dm_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, 2, &recv.product_key) < 0 ||
_dm_get_topic_level(dm_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, 3, &recv.device_name) < 0) {
break;
}
recv.data.raw_data.data = msg->data.pub.payload;
recv.data.raw_data.data_len = msg->data.pub.payload_len;
dm_handle->recv_handler(dm_handle, &recv, dm_handle->userdata);
} while (0);
DM_FREE(recv.product_key);
DM_FREE(recv.device_name);
}
static void _dm_recv_up_raw_reply_data_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata)
{
dm_handle_t *dm_handle = (dm_handle_t *)userdata;
aiot_dm_recv_t recv;
if (NULL == dm_handle->recv_handler) {
return;
}
memset(&recv, 0, sizeof(aiot_dm_recv_t));
recv.type = AIOT_DMRECV_RAW_DATA_REPLY;
core_log(dm_handle->sysdep, STATE_DM_LOG_RECV, "DM recv raw data\r\n");
do {
if (_dm_get_topic_level(dm_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, 2, &recv.product_key) < 0 ||
_dm_get_topic_level(dm_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, 3, &recv.device_name) < 0) {
break;
}
recv.data.raw_data.data = msg->data.pub.payload;
recv.data.raw_data.data_len = msg->data.pub.payload_len;
dm_handle->recv_handler(dm_handle, &recv, dm_handle->userdata);
} while (0);
DM_FREE(recv.product_key);
DM_FREE(recv.device_name);
}
static void _dm_recv_raw_sync_service_invoke_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata)
{
dm_handle_t *dm_handle = (dm_handle_t *)userdata;
aiot_dm_recv_t recv;
if (NULL == dm_handle->recv_handler) {
return;
}
memset(&recv, 0, sizeof(aiot_dm_recv_t));
recv.type = AIOT_DMRECV_RAW_SYNC_SERVICE_INVOKE;
core_log(dm_handle->sysdep, STATE_DM_LOG_RECV, "DM recv raw sync service invoke\r\n");
do {
if (_dm_get_topic_level(dm_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, 3,
&recv.data.raw_service_invoke.rrpc_id) < 0 ||
_dm_get_topic_level(dm_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, 5, &recv.product_key) < 0 ||
_dm_get_topic_level(dm_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, 6, &recv.device_name) < 0) {
break;
}
recv.data.raw_service_invoke.data = msg->data.pub.payload;
recv.data.raw_service_invoke.data_len = msg->data.pub.payload_len;
dm_handle->recv_handler(dm_handle, &recv, dm_handle->userdata);
} while (0);
DM_FREE(recv.data.raw_service_invoke.rrpc_id);
DM_FREE(recv.product_key);
DM_FREE(recv.device_name);
}
static void _dm_core_mqtt_process_handler(void *context, aiot_mqtt_event_t *event, core_mqtt_event_t *core_event)
{
dm_handle_t *dm_handle = (dm_handle_t *)context;
if (core_event != NULL) {
switch (core_event->type) {
case CORE_MQTTEVT_DEINIT: {
dm_handle->mqtt_handle = NULL;
return;
}
default: {
}
break;
}
}
}
static int32_t _dm_core_mqtt_operate_process_handler(dm_handle_t *dm_handle, core_mqtt_option_t option)
{
core_mqtt_process_data_t process_data;
memset(&process_data, 0, sizeof(core_mqtt_process_data_t));
process_data.handler = _dm_core_mqtt_process_handler;
process_data.context = dm_handle;
return core_mqtt_setopt(dm_handle->mqtt_handle, option, &process_data);
}
void *aiot_dm_init(void)
{
aiot_sysdep_portfile_t *sysdep = aiot_sysdep_get_portfile();
dm_handle_t *dm_handle = NULL;
if (NULL == sysdep) {
return NULL;
}
dm_handle = sysdep->core_sysdep_malloc(sizeof(dm_handle_t), DATA_MODEL_MODULE_NAME);
if (NULL == dm_handle) {
return NULL;
}
memset(dm_handle, 0, sizeof(dm_handle_t));
dm_handle->sysdep = sysdep;
dm_handle->post_reply = 1;
core_global_init(sysdep);
return dm_handle;
}
int32_t aiot_dm_setopt(void *handle, aiot_dm_option_t option, void *data)
{
dm_handle_t *dm_handle;
int32_t res = STATE_SUCCESS;
if (NULL == handle || NULL == data) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (option >= AIOT_DMOPT_MAX) {
return STATE_USER_INPUT_OUT_RANGE;
}
dm_handle = (dm_handle_t *)handle;
switch (option) {
case AIOT_DMOPT_MQTT_HANDLE: {
dm_handle->mqtt_handle = data;
/* setup mqtt topic mapping */
res = _dm_setup_topic_mapping(data, dm_handle);
if (res >= STATE_SUCCESS) {
res = _dm_core_mqtt_operate_process_handler(dm_handle, CORE_MQTTOPT_APPEND_PROCESS_HANDLER);
}
}
break;
case AIOT_DMOPT_RECV_HANDLER: {
dm_handle->recv_handler = (aiot_dm_recv_handler_t)data;
}
break;
case AIOT_DMOPT_USERDATA: {
dm_handle->userdata = data;
}
break;
case AIOT_DMOPT_POST_REPLY: {
dm_handle->post_reply = *(uint8_t *)data;
}
break;
default:
break;
}
return res;
}
int32_t aiot_dm_send(void *handle, const aiot_dm_msg_t *msg)
{
dm_handle_t *dm_handle = NULL;
char *topic = NULL;
int32_t res = STATE_SUCCESS;
if (NULL == handle || NULL == msg) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (msg->type >= AIOT_DMMSG_MAX) {
return STATE_USER_INPUT_OUT_RANGE;
}
dm_handle = (dm_handle_t *)handle;
if (NULL == dm_handle->mqtt_handle) {
return STATE_DM_MQTT_HANDLE_IS_NULL;
}
res = _dm_prepare_send_topic(dm_handle, msg, &topic);
if (res < 0) {
return res;
}
res = g_dm_send_topic_mapping[msg->type].func(dm_handle, topic, msg);
dm_handle->sysdep->core_sysdep_free(topic);
return res;
}
int32_t aiot_dm_deinit(void **p_handle)
{
dm_handle_t *dm_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
uint8_t i = 0;
if (NULL == p_handle || NULL == *p_handle) {
return STATE_USER_INPUT_NULL_POINTER;
}
dm_handle = *p_handle;
sysdep = dm_handle->sysdep;
*p_handle = NULL;
_dm_core_mqtt_operate_process_handler(dm_handle, CORE_MQTTOPT_REMOVE_PROCESS_HANDLER);
/* remove mqtt topic mapping */
for (i = 0; i < sizeof(g_dm_recv_topic_mapping) / sizeof(dm_recv_topic_map_t); i++) {
aiot_mqtt_topic_map_t topic_mapping;
memset(&topic_mapping, 0, sizeof(aiot_mqtt_topic_map_t));
topic_mapping.topic = g_dm_recv_topic_mapping[i].topic;
topic_mapping.handler = g_dm_recv_topic_mapping[i].func;
aiot_mqtt_setopt(dm_handle->mqtt_handle, AIOT_MQTTOPT_REMOVE_TOPIC_MAP, &topic_mapping);
}
sysdep->core_sysdep_free(dm_handle);
core_global_deinit(sysdep);
return STATE_SUCCESS;
}

View File

@@ -0,0 +1,681 @@
/**
* @file aiot_dm_api.h
* @brief 数据模型模块头文件, 提供了物模型数据格式的上云能力, 包括属性, 事件, 服务和物模型二进制格式的数据上下行能力
* @date 2020-01-20
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
* @details
*
* 请按照以下流程使用API
*
* 1. 在使用物模型模块前, 用户应首先创建好一个MQTT实例
*
* 2. 调用`aiot_dm_init`创建一个物模型实例, 保存实例句柄
*
* 3. 调用`aiot_dm_setopt`配置`AIOT_DMOPT_MQTT_HANDLE`选项以设置MQTT句柄, 此选项为强制配置选项
*
* 4. 调用`aiot_dm_setopt`配置`AIOT_DMOPT_RECV_HANDLER`和`AIOT_DMOPT_USERDATA`选项以注册数据接受回调函数和用户上下文数据指针
*
* 5. 在使用`aiot_dm_send`发送消息前, 应先完成MQTT实例的建连
*
* 6. 调动`aiot_dm_send`发送不同类型的消息到云平台, 在注册的回调函数中处理各种类型的云平台下行消息
*
*/
#ifndef __AIOT_DM_API_H__
#define __AIOT_DM_API_H__
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdint.h>
/**
* @brief -0x0A00~-0x0AFF表达SDK在data-model模块内的状态码
*/
#define STATE_DM_BASE (-0x0A00)
/**
* @brief 用户发送@ref AIOT_DMMSG_EVENT_POST 消息时, 消息数据中的event_id为NULL
*/
#define STATE_DM_EVENT_ID_IS_NULL (-0x0A01)
/**
* @brief 用户发送@ref AIOT_DMMSG_ASYNC_SERVICE_REPLY 或@ref AIOT_DMMSG_SYNC_SERVICE_REPLY 消息时, 消息数据中的event_id为NULL
*/
#define STATE_DM_SERVICE_ID_IS_NULL (-0x0A02)
/**
* @brief 用户发送@ref AIOT_DMMSG_SYNC_SERVICE_REPLY 消息时, 消息数据中的rrpc_id为NULL
*/
#define STATE_DM_RRPC_ID_IS_NULL (-0x0A03)
/**
* @brief 用户发送请求类消息时, 消息数据中的param为NULL
*/
#define STATE_DM_MSG_PARAMS_IS_NULL (-0X0A04)
/**
* @brief 用户发送应答类消息时, 消息数据中的data为NULL
*/
#define STATE_DM_MSG_DATA_IS_NULL (-0X0A05)
/**
* @brief 解析下行数据对应的topic时发生错误
*/
#define STATE_DM_INTERNAL_TOPIC_ERROR (-0x0A06)
/**
* @brief 用户未调用@ref aiot_dm_setopt 配置MQTT句柄
*/
#define STATE_DM_MQTT_HANDLE_IS_NULL (-0x0A07)
/**
* @brief 接收到服务器下行消息时的日志状态码
*/
#define STATE_DM_LOG_RECV (-0x0A08)
/**
* @brief 解析服务器下行消息失败时的日志状态码
*/
#define SATAE_DM_LOG_PARSE_RECV_MSG_FAILED (-0x0A09)
/**
* @brief data-model模块的配置选项枚举类型定义. @ref aiot_dm_setopt 函数入数data的数据类型根据不同的选项而不同
*
*/
typedef enum {
/**
* @brief 模块依赖的MQTT句柄
*
* @details
*
* data-model模块依赖底层的MQTT模块, 用户必需配置正确的MQTT句柄, 否则无法正常工作
*
* 数据类型: (void *)
*/
AIOT_DMOPT_MQTT_HANDLE,
/**
* @brief 数据接收回调函数, data-model接收物联网平台的下行消息后调用此回调函数
*
* @details
*
* 数据类型: (aiot_dm_recv_handler_t), 详细查看@ref aiot_dm_recv_handler_t 回调函数原型
*/
AIOT_DMOPT_RECV_HANDLER,
/**
* @brief 指向用户上下文数据的指针
*
* @details
*
* 在用户注册的@ref aiot_dm_recv_handler_t 数据接收回调函数中会同过userdata参数将此指针返回给用户
*
* 数据类型: (void *)
*/
AIOT_DMOPT_USERDATA,
/**
* @brief 用户是否希望接收post消息后的reply
*
* @details
*
* 是否要接收云端的reply消息. 1表示要接收, 0表示不要.
*
* 数据类型: (uint8_t) *)
*/
AIOT_DMOPT_POST_REPLY,
/**
* @brief 配置选项数量最大值, 不可用作配置参数
*/
AIOT_DMOPT_MAX,
} aiot_dm_option_t;
/**
* @brief data-model模块发送消息类型
*
* @details
*
* 这个枚举类型包括了dm模块支持发送的所有数据类型, 不同的消息类型将对于不同的消息结构体
* 用户可查看网页文档<a href="https://help.aliyun.com/document_detail/89301.html">设备属性/事件/服务</a>进一步了解各种数据类型
*
*/
typedef enum {
/**
* @brief 属性上报, 消息结构体参考@ref aiot_dm_msg_property_post_t \n
* 成功发送此消息后, 将会收到@ref AIOT_DMRECV_GENERIC_REPLY 类型的应答消息
*/
AIOT_DMMSG_PROPERTY_POST,
/**
* @brief 事件上报, 消息结构体参考@ref aiot_dm_msg_event_post_t \n
* 成功发送此消息后, 将会收到@ref AIOT_DMRECV_GENERIC_REPLY 类型的应答消息
*/
AIOT_DMMSG_EVENT_POST,
/**
* @brief 属性设置应答, 消息结构体参考@ref aiot_dm_msg_property_set_reply_t
*/
AIOT_DMMSG_PROPERTY_SET_REPLY,
/**
* @brief 异步服务应答, 消息结构体参考@ref aiot_dm_msg_async_service_reply_t
*/
AIOT_DMMSG_ASYNC_SERVICE_REPLY,
/**
* @brief 同步服务应答, 消息结构体参考@ref aiot_dm_msg_sync_service_reply_t
*/
AIOT_DMMSG_SYNC_SERVICE_REPLY,
/**
* @brief 二进制格式的物模型上行数据, 消息结构体参考@ref aiot_dm_msg_raw_data_t
*/
AIOT_DMMSG_RAW_DATA,
/**
* @brief 二进制格式的同步服务应答, 消息结构体参考@ref aiot_dm_msg_raw_service_reply_t
*/
AIOT_DMMSG_RAW_SERVICE_REPLY,
/**
* @brief 获取期望属性值, 消息结构体请参考@ref aiot_dm_msg_get_desired_t, \n
* 成功发送此消息后, 将会收到@ref AIOT_DMRECV_GENERIC_REPLY 类型的应答消息
*/
AIOT_DMMSG_GET_DESIRED,
/**
* @brief 清除指定的期望值, 消息结构体请参考@ref aiot_dm_msg_delete_desired_t \n
* 成功发送此消息后, 将会收到@ref AIOT_DMRECV_GENERIC_REPLY 类型的应答消息
*/
AIOT_DMMSG_DELETE_DESIRED,
/**
* @brief 清除指定的期望值, 消息结构体请参考@ref aiot_dm_msg_delete_desired_t \n
* 成功发送此消息后, 将会收到@ref AIOT_DMRECV_GENERIC_REPLY 类型的应答消息
*/
AIOT_DMMSG_PROPERTY_BATCH_POST,
/**
* @brief 消息数量最大值, 不可用作消息类型
*/
AIOT_DMMSG_MAX,
} aiot_dm_msg_type_t;
/**
* @brief <b>物模型属性上报</b>消息结构体
*/
typedef struct {
/**
* @brief 字符串形式的JSON结构体, <b>必须以结束符'\0'结尾</b>. 包含用户要上报的属性数据, 如<i>"{\"LightSwitch\":0}"</i>
*/
char *params;
} aiot_dm_msg_property_post_t;
/**
* @brief <b>物模型事件上报</b>消息结构体
*/
typedef struct {
/**
* @brief 事件标示符, <b>必须为以结束符'\0'结尾的字符串</b>
*/
char *event_id;
/**
* @brief 字符串形式的JSON结构体, <b>必须以结束符'\0'结尾</b>. 包含用户要上报的事件数据, 如<i>"{\"ErrorNum\":0}"</i>
*/
char *params;
} aiot_dm_msg_event_post_t;
/**
* @brief <b>属性设置应答</b>消息结构体, 用户在收到@ref AIOT_DMRECV_PROPERTY_SET 类型的属性设置后, 可发送此消息进行回复
*/
typedef struct {
/**
* @brief 消息标识符, uint64_t类型的整数, <b>必须与属性设置的消息标示符一致</b>
*/
uint64_t msg_id;
/**
* @brief 设备端状态码, 200-请求成功, 更多状态码查看<a href="https://help.aliyun.com/document_detail/89309.html">设备端通用code</a>
*/
uint32_t code;
/**
* @brief 设备端应答数据, 字符串形式的JSON结构体, <b>必须以结束符'\0'结尾</b>, 如<i>"{}"</i>表示应答数据为空
*/
char *data;
} aiot_dm_msg_property_set_reply_t;
/**
* @brief <b>异步服务应答</b>消息结构体, 用户在收到@ref AIOT_DMRECV_ASYNC_SERVICE_INVOKE 类型的异步服务调用消息后, 应发送此消息进行应答
*/
typedef struct {
/**
* @brief 消息标识符, uint64_t类型的整数, <b>必须与异步服务调用的消息标示符一致</b>
*/
uint64_t msg_id;
/**
* @brief 服务标示符, 标识了要响应服务
*/
char *service_id;
/**
* @brief 设备端状态码, 200-请求成功, 更多状态码查看<a href="https://help.aliyun.com/document_detail/89309.html">设备端通用code</a>
*/
uint32_t code;
/**
* @brief 设备端应答数据, 字符串形式的JSON结构体, <b>必须以结束符'\0'结尾</b>, 如<i>"{}"</i>表示应答数据为空
*/
char *data;
} aiot_dm_msg_async_service_reply_t;
/**
* @brief <b>同步服务应答</b>消息结构体, 用户在收到@ref AIOT_DMRECV_SYNC_SERVICE_INVOKE 类型的同步服务调用消息后, 应在超时时间(默认7s)内进行应答
*/
typedef struct {
/**
* @brief 消息标识符, uint64_t类型的整数, <b>必须与同步服务调用的消息标示符一致</b>
*/
uint64_t msg_id;
/**
* @brief RRPC标示符, 用于唯一标识每一个同步服务的字符串, <b>必须与同步服务调用消息的RRPC标示符一致</b>
*/
char *rrpc_id;
/**
* @brief 服务标示符, 标识了要响应服务
*/
char *service_id;
/**
* @brief 设备端状态码, 200-请求成功, 更多状态码查看<a href="https://help.aliyun.com/document_detail/89309.html">设备端通用code</a>
*/
uint32_t code;
/**
* @brief 设备端应答数据, 字符串形式的JSON结构体, <b>必须以结束符'\0'结尾</b>, 如<i>"{}"</i>表示应答数据为空
*/
char *data;
} aiot_dm_msg_sync_service_reply_t;
/**
* @brief <b>物模型二进制数据</b>消息结构体, 发送的二进制数据将通过物联网平台的JavaScript脚本转化为JSON格式数据, 用户发送此消息前应确保已正确启用云端解析脚本
*/
typedef struct {
/**
* @brief 指向待发送二进制数据的指针
*/
uint8_t *data;
/**
* @brief 待发送数据的长度
*/
uint32_t data_len;
} aiot_dm_msg_raw_data_t;
/**
* @brief <b>二进制格式的同步服务应答</b>消息结构体, 用户在收到@ref AIOT_DMRECV_RAW_SYNC_SERVICE_INVOKE 类型消息后, 应在超时时间(默认7s)内进行应答\n
* 用户在使用此消息前应确保已启用云端解析脚本, 并且脚本工作正常
*/
typedef struct {
/**
* @brief RRPC标示符, 特殊字符串, <b>必须与同步服务调用消息的RRPC标示符一致</b>
*/
char *rrpc_id;
/**
* @brief 指向待发送二进制数据的指针
*/
uint8_t *data;
/**
* @brief 待发送数据的长度
*/
uint32_t data_len;
} aiot_dm_msg_raw_service_reply_t;
/**
* @brief <b>获取期望属性值</b>消息结构体, 发送
*/
typedef struct {
/**
* @brief 字符串形式的JSON<b>数组</b>, <b>必须以结束符'\0'结尾</b>. 应包含用户要获取的期望属性的ID, 如<i>"[\"LightSwitch\"]"</i>
*/
char *params;
} aiot_dm_msg_get_desired_t;
/**
* @brief <b>删除指定期望值</b>消息结构体
*/
typedef struct {
/**
* @brief 字符串形式的JSON结构体, <b>必须以结束符'\0'结尾</b>. 应包含用户要删除的期望属性的ID和期望值版本号, 如<i>"{\"LightSwitch\":{\"version\":1},\"Color\":{}}"</i>
*/
char *params;
} aiot_dm_msg_delete_desired_t;
/**
* @brief <b>物模型属性上报</b>消息结构体
*/
typedef struct {
/**
* @brief 字符串形式的JSON结构体, <b>必须以结束符'\0'结尾</b>. 包含用户要批量上报的属性和事件数据, 如 {"properties":{"Power": [ { "value": "on", "time": 1524448722000 },
* { "value": "off", "time": 1524448722001 } ], "WF": [ { "value": 3, "time": 1524448722000 }]}, "events": {"alarmEvent": [{ "value": { "Power": "on", "WF": "2"},
* "time": 1524448722000}]}}
*/
char *params;
} aiot_dm_msg_property_batch_post_t;
/**
* @brief data-model模块发送消息的消息结构体
*/
typedef struct {
/**
* @brief 消息所属设备的product_key, 若为NULL则使用通过aiot_dm_setopt配置的product_key\n
* 在网关子设备场景下, 可通过指定为子设备的product_key来发送子设备的消息到云端
*/
char *product_key;
/**
* @brief 消息所属设备的device_name, 若为NULL则使用通过aiot_dm_setopt配置的device_name\n
* 在网关子设备场景下, 可通过指定为子设备的product_key来发送子设备的消息到云端
*/
char *device_name;
/**
* @brief 消息类型, 可参考@ref aiot_dm_msg_type_t
*/
aiot_dm_msg_type_t type;
/**
* @brief 消息数据联合体, 不同的消息类型将使用不同的消息结构体
*/
union {
aiot_dm_msg_property_post_t property_post;
aiot_dm_msg_event_post_t event_post;
aiot_dm_msg_property_set_reply_t property_set_reply;
aiot_dm_msg_sync_service_reply_t sync_service_reply;
aiot_dm_msg_async_service_reply_t async_service_reply;
aiot_dm_msg_raw_data_t raw_data;
aiot_dm_msg_raw_service_reply_t raw_service_reply;
aiot_dm_msg_get_desired_t get_desired;
aiot_dm_msg_delete_desired_t delete_desired;
} data;
} aiot_dm_msg_t;
/**
* @brief data-model模块接受消息类型枚举
*
* @details
*
* 这个枚举类型包括了dm模块支持接收的所有数据类型, 不同的消息类型将对于不同的消息结构体
* 用户可查看网页文档<a href="https://help.aliyun.com/document_detail/89301.html">设备属性/事件/服务</a>进一步了解各种数据类型
*
*/
typedef enum {
/**
* @brief 上报属性/实践后服务器返回的应答消息, 消息数据结构体参考@ref aiot_dm_recv_generic_reply_t
*/
AIOT_DMRECV_GENERIC_REPLY,
/**
* @brief 服务器下发的属性设置消息, 消息数据结构体参考@ref aiot_dm_recv_property_set_t
*/
AIOT_DMRECV_PROPERTY_SET,
/**
* @brief 服务器下发的异步服务调用消息, 消息数据结构体参考@ref aiot_dm_recv_async_service_invoke_t
*/
AIOT_DMRECV_ASYNC_SERVICE_INVOKE,
/**
* @brief 服务器下发的同步服务调用消息, 消息数据结构体参考@ref aiot_dm_recv_sync_service_invoke_t
*/
AIOT_DMRECV_SYNC_SERVICE_INVOKE,
/**
* @brief 服务器对设备上报的二进制数据应答, 消息数据结构体参考@ref aiot_dm_recv_raw_data_t
*/
AIOT_DMRECV_RAW_DATA_REPLY,
/**
* @brief 服务器下发的物模型二进制数据, 消息数据结构体参考@ref aiot_dm_recv_raw_data_t
*/
AIOT_DMRECV_RAW_DATA,
/**
* @brief 服务器下发的二进制格式的同步服务调用消息, 消息数据结构体参考@ref aiot_dm_recv_raw_service_invoke_t
*/
AIOT_DMRECV_RAW_SYNC_SERVICE_INVOKE,
/**
* @brief 消息数量最大值, 不可用作消息类型
*/
AIOT_DMRECV_MAX,
} aiot_dm_recv_type_t;
/**
* @brief <b>云端通用应答</b>消息结构体, 设备端上报@ref AIOT_DMMSG_PROPERTY_POST, @ref AIOT_DMMSG_EVENT_POST 或者@ref AIOT_DMMSG_GET_DESIRED 等消息后, 服务器会应答此消息
*/
typedef struct {
/**
* @brief 消息标识符, uint64_t类型的整数, 与属性上报或事件上报的消息标示符一致
*/
uint32_t msg_id;
/**
* @brief 设备端错误码, 200-请求成功, 更多错误码码查看<a href="https://help.aliyun.com/document_detail/120329.html">设备端错误码</a>
*/
uint32_t code;
/**
* @brief 指向云端应答数据的指针
*/
char *data;
/**
* @brief 云端应答数据的长度
*/
uint32_t data_len;
/**
* @brief 指向状态消息字符串的指针, 当设备端上报请求成功时对应的应答消息为"success", 若请求失败则应答消息中包含错误信息
*/
char *message;
/**
* @brief 消息字符串的长度
*/
uint32_t message_len;
} aiot_dm_recv_generic_reply_t;
/**
* @brief <b>属性设置</b>消息结构体
*/
typedef struct {
/**
* @brief 消息标识符, uint64_t类型的整数
*/
uint64_t msg_id;
/**
* @brief 服务器下发的属性数据, 为字符串形式的JSON结构体, 此字符串<b>不</b>以结束符'\0'结尾, 如<i>"{\"LightSwitch\":0}"</i>
*/
char *params;
/**
* @brief 属性数据的字符串长度
*/
uint32_t params_len;
} aiot_dm_recv_property_set_t;
/**
* @brief <b>同步服务调用</b>消息结构体, 用户收到同步服务后, 必须在超时时间(默认7s)内进行应答
*/
typedef struct {
/**
* @brief 消息标识符, uint64_t类型的整数
*/
uint64_t msg_id;
/**
* @brief RRPC标识符, 用于唯一标识每一个同步服务的特殊字符串
*/
char *rrpc_id;
/**
* @brief 服务标示符, 字符串内容由用户定义的物模型决定
*/
char *service_id;
/**
* @brief 服务调用的输入参数数据, 为字符串形式的JSON结构体, 此字符串<b>不</b>以结束符'\0'结尾, 如<i>"{\"LightSwitch\":0}"</i>
*/
char *params;
/**
* @brief 输入参数的字符串长度
*/
uint32_t params_len;
} aiot_dm_recv_sync_service_invoke_t;
/**
* @brief <b>同步服务调用</b>消息结构体
*/
typedef struct {
/**
* @brief 消息标识符, uint64_t类型的整数
*/
uint64_t msg_id;
/**
* @brief 服务标示符, 字符串内容由用户定义的物模型决定
*/
char *service_id;
/**
* @brief 服务调用的输入参数数据, 为字符串形式的JSON结构体, 此字符串<b>不</b>以结束符'\0'结尾, 如<i>"{\"LightSwitch\":0}"</i>
*/
char *params;
/**
* @brief 输入参数的字符串长度
*/
uint32_t params_len;
} aiot_dm_recv_async_service_invoke_t;
/**
* @brief <b>物模型二进制数据</b>消息结构体, 服务器的JSON格式物模型数据将通过物联网平台的JavaScript脚本转化为二进制数据, 用户在接收此消息前应确保已正确启用云端解析脚本
*/
typedef struct {
/**
* @brief 指向接受数据缓冲区的指针
*/
uint8_t *data;
/**
* @brief 二进制数据的长度
*/
uint32_t data_len;
} aiot_dm_recv_raw_data_t;
/**
* @brief <b>二进制数据的同步服务调用</b>消息结构体, 服务器的JSON格式物模型数据将通过物联网平台的JavaScript脚本转化为二进制数据, 用户在接收此消息前应确保已正确启用云端解析脚本
*/
typedef struct {
/**
* @brief RRPC标识符, 用于唯一标识每一个同步服务的特殊字符串
*/
char *rrpc_id;
/**
* @brief 指向接受数据缓冲区的指针
*/
uint8_t *data;
/**
* @brief 二进制数据的长度
*/
uint32_t data_len;
} aiot_dm_recv_raw_service_invoke_t;
/**
* @brief data-model模块接收消息的结构体
*/
typedef struct {
/**
* @brief 消息所属设备的product_key, 不配置则默认使用MQTT模块配置的product_key
*/
char *product_key;
/**
* @brief 消息所属设备的device_name, 不配置则默认使用MQTT模块配置的device_name
*/
char *device_name;
/**
* @brief 接收消息的类型, 可参考@ref aiot_dm_recv_type_t
*/
aiot_dm_recv_type_t type;
/**
* @brief 消息数据联合体, 不同的消息类型将使用不同的消息结构体
*/
union {
aiot_dm_recv_generic_reply_t generic_reply;
aiot_dm_recv_property_set_t property_set;
aiot_dm_recv_async_service_invoke_t async_service_invoke;
aiot_dm_recv_sync_service_invoke_t sync_service_invoke;
aiot_dm_recv_raw_data_t raw_data;
aiot_dm_recv_raw_service_invoke_t raw_service_invoke;
} data;
} aiot_dm_recv_t;
/**
* @brief data-model模块消息接收回调函数的函数原型定义, 当模块接收到服务器下行数据后将调用此回调函数, 并将消息数据通过<i>recv</i>参数输入给用户, \n
* 同时将用户上下文数据指针通过<i>userdata</i>参数返回给用户
*
* @param[in] handle data-model实例句柄
* @param[in] recv 服务下发的消息数据, <b>消息结构体中的所有数据指针在离开回调函数后将失效, 保存消息数据必须使用内存复制的方式</b>
* @param[in] userdata 指向用户上下文数据的指针, 这个指针由用户通过调用@ref aiot_dm_setopt 配置@ref AIOT_DMOPT_USERDATA 选项设置
*
* @return void
*/
typedef void (*aiot_dm_recv_handler_t)(void *handle, const aiot_dm_recv_t *recv, void *userdata);
/**
* @brief 初始化data-model实例
*
* @return void*
* @retval 非NULL data-model实例句柄
* @retval NULL 初始化失败, 一般是内存分配失败导致
*/
void *aiot_dm_init(void);
/**
* @brief 设置data-model参数
*
* @param[in] handle data-model实例句柄
* @param[in] option 配置选项, 更多信息请查看@ref aiot_dm_option_t
* @param[in] data 配置数据, 更多信息请查看@ref aiot_dm_option_t
*
* @return int32_t
* @retval STATE_SUCCESS 参数配置成功
* @retval STATE_USER_INPUT_NULL_POINTER 入参handle或data为NULL
* @retval STATE_USER_INPUT_OUT_RANGE 入参optioin的枚举值>=AIOT_DMOPT_MAX
* @retval others 参考@ref aiot_state_api.h
*
*/
int32_t aiot_dm_setopt(void *handle, aiot_dm_option_t option, void *data);
/**
* @brief 发送一条data-model消息到物联网平台, 消息类型和消息数据由msg入参决定
*
* @param[in] handle data-model实例句柄
* @param[in] msg 消息结构体, 可指定发送消息的设备<i>productKey</i>, <i>deviceName</i>; 消息类型, 消息数据等, 更多信息请参考@ref aiot_dm_msg_t
*
* @return int32_t
* @retval >=STATE_SUCCESS 消息发送成功, 对于@ref AIOT_DMMSG_PROPERTY_POST, @ref AIOT_DMMSG_EVENT_POST, @ref AIOT_DMMSG_GET_DESIRED 和@ref AIOT_DMMSG_DELETE_DESIRED 消息, \n
* 发送成功返回值为>STATE_SUCCESS的消息标示符<i>msg_id</i>值
* @retval STATE_USER_INPUT_NULL_POINTER 入参<i>handle</i>或<i>msg</i>为NULL
* @retval STATE_USER_INPUT_OUT_RANGE 入参<i>msg</i>的结构体成员<i>type</i> >= AIOT_DMMSG_MAX
* @retval STATE_SYS_DEPEND_MALLOC_FAILED 内存分配失败
* @retval STATE_DM_MQTT_HANDLE_IS_NULL 用户未调用@ref aiot_dm_setopt 配置MQTT句柄
* @retval others 参考@ref aiot_state_api.h 或@ref STATE_DM_BASE 中对应的错误码说明
*
*/
int32_t aiot_dm_send(void *handle, const aiot_dm_msg_t *msg);
/**
* @brief 销毁data-model实例, 释放资源
*
* @param[in] p_handle 指向data-model实例句柄的指针
* @return int32_t
* @retval STATE_SUCCESS 执行成功
* @retval <STATE_SUCCESS 执行失败
*
*/
int32_t aiot_dm_deinit(void **p_handle);
#if defined(__cplusplus)
}
#endif
#endif /* #ifndef __AIOT_DM_API_H__ */

View File

@@ -0,0 +1,78 @@
/**
* @file dm_private.h
* @brief 数据模型模块内部头文件
* @date 2020-01-20
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#ifndef __DM_PRIVATE_H__
#define __DM_PRIVATE_H__
#if defined(__cplusplus)
extern "C" {
#endif
#include "core_stdinc.h"
#include "core_string.h"
#include "core_log.h"
#include "core_diag.h"
#include "core_global.h"
#include "core_mqtt.h"
#include "aiot_sysdep_api.h"
#include "aiot_state_api.h"
#include "aiot_mqtt_api.h"
#include "aiot_dm_api.h"
/* 模块内部名 */
#define DATA_MODEL_MODULE_NAME "dm"
/* ALINK请求的JSON格式 */
#define ALINK_REQUEST_FMT "{\"id\":\"%s\",\"version\":\"1.0\",\"params\":%s,\"sys\":{\"ack\":%s}}"
/* ALINK应答的JSON格式 */
#define ALINK_RESPONSE_FMT "{\"id\":\"%s\",\"code\":%s,\"data\":%s}"
#define ALINK_JSON_KEY_ID "id"
#define ALINK_JSON_KEY_CODE "code"
#define ALINK_JSON_KEY_PARAMS "params"
#define ALINK_JSON_KEY_DATA "data"
#define ALINK_JSON_KEY_MESSAGE "message"
/* 诊断消息类型 */
#define DM_DIAG_MSG_TYPE_REQ (0x00)
#define DM_DIAG_MSG_TYPE_RSP (0x01)
#define DM_FREE(ptr) do {if (ptr) {dm_handle->sysdep->core_sysdep_free(ptr); ptr = NULL;}} while (0)
/* data-model模块的上下文结构体定义 */
typedef struct {
aiot_sysdep_portfile_t *sysdep;
void *mqtt_handle;
aiot_dm_recv_handler_t recv_handler;
void *userdata;
uint8_t post_reply;
} dm_handle_t;
/* data-model内部发送函数原型定义 */
typedef int32_t (*dm_msg_send_func_t)(dm_handle_t *handle, const char *topic, const aiot_dm_msg_t *msg);
/* 包含上行topic和对应处理函数的结构体定义 */
typedef struct {
char *topic;
dm_msg_send_func_t func;
} dm_send_topic_map_t;
/* 包含下行topic和对应处理函数的结构体定义 */
typedef struct {
char *topic;
aiot_mqtt_recv_handler_t func;
} dm_recv_topic_map_t;
#if defined(__cplusplus)
}
#endif
#endif /* #ifndef __DM_PRIVATE_H__ */

View File

@@ -0,0 +1,3 @@
Name: 设备标签模块
DEVINFO Component for Link SDK V4.0.0

View File

@@ -0,0 +1,424 @@
/**
* @file aiot_devinfo_api.c
* @brief devinfo模块的API接口实现, 提供更新和删除设备标签的能力
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#include "devinfo_private.h"
#include "core_string.h"
#include "core_log.h"
#include "core_global.h"
#include "core_mqtt.h"
static void _core_devinfo_exec_inc(devinfo_handle_t *devinfo_handle)
{
devinfo_handle->sysdep->core_sysdep_mutex_lock(devinfo_handle->data_mutex);
devinfo_handle->exec_count++;
devinfo_handle->sysdep->core_sysdep_mutex_unlock(devinfo_handle->data_mutex);
}
static void _core_devinfo_exec_dec(devinfo_handle_t *devinfo_handle)
{
devinfo_handle->sysdep->core_sysdep_mutex_lock(devinfo_handle->data_mutex);
devinfo_handle->exec_count--;
devinfo_handle->sysdep->core_sysdep_mutex_unlock(devinfo_handle->data_mutex);
}
static void _devinfo_find_pk_dn(devinfo_handle_t *devinfo_handle, char *topic, uint16_t topic_len, char **product_key,
char **device_name)
{
uint16_t idx = 0, prev_slash = 0, slash = 0, pk_len = 0, dn_len = 0;
char *pk_pos = NULL, *dn_pos = NULL, *tmp_pk = NULL, *tmp_dn = NULL;
for (idx = 0; idx < topic_len; idx++) {
if (topic[idx] == '/') {
slash++;
if (slash == 2) {
pk_pos = &topic[idx + 1];
prev_slash = idx;
} else if (slash == 3) {
dn_pos = &topic[idx + 1];
pk_len = idx - prev_slash - 1;
prev_slash = idx;
} else if (slash == 4) {
dn_len = idx - prev_slash - 1;
break;
}
}
}
if (pk_len == 0 || dn_len == 0) {
return;
}
tmp_pk = devinfo_handle->sysdep->core_sysdep_malloc(pk_len + 1, DEVINFO_MODULE_NAME);
if (tmp_pk == NULL) {
return;
}
memset(tmp_pk, 0, pk_len + 1);
memcpy(tmp_pk, pk_pos, pk_len);
tmp_dn = devinfo_handle->sysdep->core_sysdep_malloc(dn_len + 1, DEVINFO_MODULE_NAME);
if (tmp_dn == NULL) {
devinfo_handle->sysdep->core_sysdep_free(tmp_pk);
return;
}
memset(tmp_dn, 0, dn_len + 1);
memcpy(tmp_dn, dn_pos, dn_len);
*product_key = tmp_pk;
*device_name = tmp_dn;
}
static void _devinfo_mqtt_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata)
{
devinfo_handle_t *devinfo_handle = (devinfo_handle_t *)userdata;
aiot_devinfo_event_t event;
char *product_key = NULL, *device_name = NULL;
char *code_key = "code", *id_key = "id", *data_key = "data", *message_key = "message";
char *code_value = NULL, *id_value = NULL, *data_value = NULL, *message_value = NULL;
uint32_t code_value_len = 0, id_value_len = 0, data_value_len = 0, message_value_len = 0;
if (devinfo_handle->recv_handler == NULL) {
return;
}
_devinfo_find_pk_dn(devinfo_handle, packet->data.pub.topic, packet->data.pub.topic_len, &product_key, &device_name);
if (product_key == NULL || device_name == NULL) {
if (devinfo_handle->event_handler != NULL) {
memset(&event, 0, sizeof(aiot_devinfo_event_t));
event.type = AIOT_DEVINFOEVT_INVALID_DEVINFO;
devinfo_handle->event_handler(devinfo_handle, &event, devinfo_handle->userdata);
}
return;
}
if (core_json_value((char *)packet->data.pub.payload, packet->data.pub.payload_len,
code_key, (uint32_t)strlen(code_key), &code_value, &code_value_len) == STATE_SUCCESS &&
core_json_value((char *)packet->data.pub.payload, packet->data.pub.payload_len,
id_key, (uint32_t)strlen(id_key), &id_value, &id_value_len) == STATE_SUCCESS &&
core_json_value((char *)packet->data.pub.payload, packet->data.pub.payload_len,
data_key, (uint32_t)strlen(data_key), &data_value, &data_value_len) == STATE_SUCCESS &&
core_json_value((char *)packet->data.pub.payload, packet->data.pub.payload_len,
message_key, (uint32_t)strlen(message_key), &message_value, &message_value_len) == STATE_SUCCESS) {
uint32_t code = 0, id = 0;
if (core_str2uint(code_value, code_value_len, &code) == STATE_SUCCESS &&
core_str2uint(id_value, id_value_len, &id) == STATE_SUCCESS) {
aiot_devinfo_recv_t recv;
memset(&recv, 0, sizeof(aiot_devinfo_recv_t));
recv.product_key = product_key;
recv.device_name = device_name;
recv.type = AIOT_DEVINFORECV_GENERIC_REPLY;
recv.data.generic_reply.code = code;
recv.data.generic_reply.msg_id = id;
recv.data.generic_reply.data = data_value;
recv.data.generic_reply.data_len = data_value_len;
recv.data.generic_reply.message = message_value;
recv.data.generic_reply.message_len = message_value_len;
devinfo_handle->recv_handler(devinfo_handle, &recv, devinfo_handle->userdata);
} else {
if (devinfo_handle->event_handler != NULL) {
memset(&event, 0, sizeof(aiot_devinfo_event_t));
event.type = AIOT_DEVINFOEVT_INVALID_RESPONSE_FORMAT;
devinfo_handle->event_handler(devinfo_handle, &event, devinfo_handle->userdata);
}
}
} else {
if (devinfo_handle->event_handler != NULL) {
memset(&event, 0, sizeof(aiot_devinfo_event_t));
event.type = AIOT_DEVINFOEVT_INVALID_RESPONSE;
devinfo_handle->event_handler(devinfo_handle, &event, devinfo_handle->userdata);
}
}
devinfo_handle->sysdep->core_sysdep_free(product_key);
devinfo_handle->sysdep->core_sysdep_free(device_name);
}
static int32_t _devinfo_operate_topic_map(devinfo_handle_t *devinfo_handle, aiot_mqtt_option_t option)
{
int32_t res = STATE_SUCCESS;
aiot_mqtt_topic_map_t map;
if (option != AIOT_MQTTOPT_APPEND_TOPIC_MAP && option != AIOT_MQTTOPT_REMOVE_TOPIC_MAP) {
return STATE_USER_INPUT_OUT_RANGE;
}
memset(&map, 0, sizeof(aiot_mqtt_topic_map_t));
map.topic = DEVINFO_UPDATE_REPLY_TOPIC;
map.handler = _devinfo_mqtt_recv_handler;
map.userdata = devinfo_handle;
res = aiot_mqtt_setopt(devinfo_handle->mqtt_handle, option, &map);
if (res < STATE_SUCCESS) {
return res;
}
map.topic = DEVINFO_DELETE_REPLY_TOPIC;
map.handler = _devinfo_mqtt_recv_handler;
map.userdata = devinfo_handle;
res = aiot_mqtt_setopt(devinfo_handle->mqtt_handle, option, &map);
if (res < STATE_SUCCESS) {
return res;
}
return STATE_SUCCESS;
}
static int32_t _devinfo_send(devinfo_handle_t *devinfo_handle, char *product_key, char *device_name,
aiot_devinfo_msg_data_t *data, char *topic_fmt)
{
int32_t res = STATE_SUCCESS, alink_id = 0;
char *topic = NULL, *payload = NULL;
char *topic_src[] = { product_key, device_name };
char alink_id_str[11] = {0}, *payload_src[] = { alink_id_str, data->params };
char *payload_fmt = "{\"id\":\"%s\",\"version\":\"1.0\",\"params\":%s}";
res = core_global_alink_id_next(devinfo_handle->sysdep, &alink_id);
if (res < STATE_SUCCESS) {
return res;
}
res = core_int2str(alink_id, alink_id_str, NULL);
if (res < STATE_SUCCESS) {
return res;
}
res = core_sprintf(devinfo_handle->sysdep, &topic, topic_fmt, topic_src, sizeof(topic_src) / sizeof(char *),
DEVINFO_MODULE_NAME);
if (res < STATE_SUCCESS) {
return res;
}
res = core_sprintf(devinfo_handle->sysdep, &payload, payload_fmt, payload_src, sizeof(payload_src) / sizeof(char *),
DEVINFO_MODULE_NAME);
if (res < STATE_SUCCESS) {
devinfo_handle->sysdep->core_sysdep_free(topic);
return res;
}
res = aiot_mqtt_pub(devinfo_handle->mqtt_handle, topic, (uint8_t *)payload, (uint32_t)strlen(payload), 0);
devinfo_handle->sysdep->core_sysdep_free(topic);
devinfo_handle->sysdep->core_sysdep_free(payload);
if (res >= STATE_SUCCESS) {
res = alink_id;
}
return res;
}
static void _devinfo_core_mqtt_process_handler(void *context, aiot_mqtt_event_t *event, core_mqtt_event_t *core_event)
{
devinfo_handle_t *devinfo_handle = (devinfo_handle_t *)context;
if (core_event != NULL) {
switch (core_event->type) {
case CORE_MQTTEVT_DEINIT: {
devinfo_handle->mqtt_handle = NULL;
return;
}
break;
default: {
}
break;
}
}
}
static int32_t _devinfo_core_mqtt_operate_process_handler(devinfo_handle_t *devinfo_handle, core_mqtt_option_t option)
{
core_mqtt_process_data_t process_data;
memset(&process_data, 0, sizeof(core_mqtt_process_data_t));
process_data.handler = _devinfo_core_mqtt_process_handler;
process_data.context = devinfo_handle;
return core_mqtt_setopt(devinfo_handle->mqtt_handle, option, &process_data);
}
void *aiot_devinfo_init(void)
{
int32_t res = STATE_SUCCESS;
devinfo_handle_t *devinfo_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
sysdep = aiot_sysdep_get_portfile();
if (sysdep == NULL) {
return NULL;
}
res = core_global_init(sysdep);
if (res < STATE_SUCCESS) {
return NULL;
}
devinfo_handle = sysdep->core_sysdep_malloc(sizeof(devinfo_handle_t), DEVINFO_MODULE_NAME);
if (devinfo_handle == NULL) {
core_global_deinit(sysdep);
return NULL;
}
memset(devinfo_handle, 0, sizeof(devinfo_handle_t));
devinfo_handle->sysdep = sysdep;
devinfo_handle->deinit_timeout_ms = DEVINFO_DEFAULT_DEINIT_TIMEOUT_MS;
devinfo_handle->data_mutex = sysdep->core_sysdep_mutex_init();
devinfo_handle->exec_enabled = 1;
return devinfo_handle;
}
int32_t aiot_devinfo_setopt(void *handle, aiot_devinfo_option_t option, void *data)
{
int32_t res = STATE_SUCCESS;
devinfo_handle_t *devinfo_handle = (devinfo_handle_t *)handle;
if (handle == NULL || data == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (option >= AIOT_DEVINFOOPT_MAX) {
return STATE_USER_INPUT_OUT_RANGE;
}
if (devinfo_handle->exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
_core_devinfo_exec_inc(devinfo_handle);
devinfo_handle->sysdep->core_sysdep_mutex_lock(devinfo_handle->data_mutex);
switch (option) {
case AIOT_DEVINFOOPT_MQTT_HANDLE: {
devinfo_handle->mqtt_handle = data;
devinfo_handle->sysdep->core_sysdep_mutex_unlock(devinfo_handle->data_mutex);
res = _devinfo_operate_topic_map(devinfo_handle, AIOT_MQTTOPT_APPEND_TOPIC_MAP);
if (res >= STATE_SUCCESS) {
res = _devinfo_core_mqtt_operate_process_handler(devinfo_handle, CORE_MQTTOPT_APPEND_PROCESS_HANDLER);
}
devinfo_handle->sysdep->core_sysdep_mutex_lock(devinfo_handle->data_mutex);
}
break;
case AIOT_DEVINFOOPT_RECV_HANDLER: {
devinfo_handle->recv_handler = (aiot_devinfo_recv_handler_t)data;
}
break;
case AIOT_DEVINFOOPT_EVENT_HANDLER: {
devinfo_handle->event_handler = (aiot_devinfo_event_handler_t)data;
}
break;
case AIOT_DEVINFOOPT_USERDATA: {
devinfo_handle->userdata = data;
}
break;
case AIOT_DEVINFOOPT_DEINIT_TIMEOUT_MS: {
devinfo_handle->deinit_timeout_ms = *(uint32_t *)data;
}
break;
default: {
res = STATE_USER_INPUT_UNKNOWN_OPTION;
}
}
devinfo_handle->sysdep->core_sysdep_mutex_unlock(devinfo_handle->data_mutex);
_core_devinfo_exec_dec(devinfo_handle);
return res;
}
int32_t aiot_devinfo_deinit(void **handle)
{
uint64_t deinit_timestart = 0;
devinfo_handle_t *devinfo_handle = NULL;
if (handle == NULL || *handle == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
devinfo_handle = *(devinfo_handle_t **)handle;
if (devinfo_handle->exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
devinfo_handle->exec_enabled = 0;
_devinfo_core_mqtt_operate_process_handler(devinfo_handle, CORE_MQTTOPT_REMOVE_PROCESS_HANDLER);
_devinfo_operate_topic_map(devinfo_handle, AIOT_MQTTOPT_REMOVE_TOPIC_MAP);
deinit_timestart = devinfo_handle->sysdep->core_sysdep_time();
do {
if (devinfo_handle->exec_count == 0) {
break;
}
devinfo_handle->sysdep->core_sysdep_sleep(DEVINFO_DEINIT_INTERVAL_MS);
} while ((devinfo_handle->sysdep->core_sysdep_time() - deinit_timestart) < devinfo_handle->deinit_timeout_ms);
if (devinfo_handle->exec_count != 0) {
return STATE_MQTT_DEINIT_TIMEOUT;
}
*handle = NULL;
devinfo_handle->sysdep->core_sysdep_mutex_deinit(&devinfo_handle->data_mutex);
core_global_deinit(devinfo_handle->sysdep);
devinfo_handle->sysdep->core_sysdep_free(devinfo_handle);
return STATE_SUCCESS;
}
int32_t aiot_devinfo_send(void *handle, aiot_devinfo_msg_t *msg)
{
int32_t res = STATE_SUCCESS;
devinfo_handle_t *devinfo_handle = (devinfo_handle_t *)handle;
if (handle == NULL || msg == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (devinfo_handle->mqtt_handle == NULL) {
return STATE_DEVINFO_MISSING_MQTT_HANDLE;
}
if (msg->product_key == NULL) {
return STATE_USER_INPUT_MISSING_PRODUCT_KEY;
}
if (msg->device_name == NULL) {
return STATE_USER_INPUT_MISSING_DEVICE_NAME;
}
switch (msg->type) {
case AIOT_DEVINFO_MSG_UPDATE: {
if (msg->data.update.params == NULL) {
res = STATE_USER_INPUT_NULL_POINTER;
} else {
res = _devinfo_send(devinfo_handle, msg->product_key, msg->device_name, &msg->data.update, DEVINFO_UPDATE_TOPIC_FMT);
}
}
break;
case AIOT_DEVINFO_MSG_DELETE: {
if (msg->data.delete.params == NULL) {
res = STATE_USER_INPUT_NULL_POINTER;
} else {
res = _devinfo_send(devinfo_handle, msg->product_key, msg->device_name, &msg->data.delete, DEVINFO_DELETE_TOPIC_FMT);
}
}
break;
default: {
res = STATE_USER_INPUT_UNKNOWN_OPTION;
}
}
return res;
}

View File

@@ -0,0 +1,315 @@
/**
* @file aiot_devinfo_api.h
* @brief devinfo模块头文件, 提供更新和删除设备标签的能力
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
* @details
*
* Devinfo模块用于向阿里云物联网平台更新或删除设备的标签, API的使用流程如下:
*
* 1. 首先参考 @ref aiot_mqtt_api.h 的说明, 保证成功建立与物联网平台的`MQTT`连接
*
* 2. 调用 @ref aiot_devinfo_init 初始化devinfo会话, 获取会话句柄
*
* 3. 调用 @ref aiot_devinfo_setopt 配置devinfo会话的参数, 常用配置项见 @ref aiot_devinfo_setopt 的说明
*
* 4. 调用 @ref aiot_devinfo_send 发送标签变更的请求, 比如更新或删除
*
* 5. 收到的应答经SDK处理后会调用由 @ref aiot_devinfo_setopt 配置的 @ref AIOT_DEVINFOOPT_RECV_HANDLER 回调函数, 通知用户云端的应答
*
*/
#ifndef __AIOT_DEVINFO_API_H__
#define __AIOT_DEVINFO_API_H__
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdint.h>
/**
* @brief -0x1200~-0x12FF表达SDK在devinfo模块内的状态码
*/
#define STATE_DEVINFO_BASE (-0x1200)
/**
* @brief MQTT会话句柄未设置, 请通过 @ref aiot_devinfo_setopt 设置MQTT会话句柄
*/
#define STATE_DEVINFO_MISSING_MQTT_HANDLE (-0x1201)
/**
* @brief devinfo模块收到从网络上来的报文时, 通知用户的报文类型
*/
typedef enum {
AIOT_DEVINFORECV_GENERIC_REPLY,
} aiot_devinfo_recv_type_t;
typedef struct {
/**
* @brief 消息标识符, uint64_t类型的整数, 与属性上报或事件上报的消息标示符一致
*/
uint32_t msg_id;
/**
* @brief 设备端错误码, 200-请求成功, 更多错误码码查看<a href="https://help.aliyun.com/document_detail/120329.html">设备端错误码</a>
*/
uint32_t code;
/**
* @brief 指向云端应答数据的指针
*/
char *data;
/**
* @brief 云端应答数据的长度
*/
uint32_t data_len;
/**
* @brief 指向状态消息字符串的指针, 当设备端上报请求成功时对应的应答消息为"success", 若请求失败则应答消息中包含错误信息
*/
char *message;
/**
* @brief 消息字符串的长度
*/
uint32_t message_len;
} aiot_devinfo_recv_generic_reply_t;
/**
* @brief devinfo模块收到从网络上来的报文时, 通知用户的报文内容
*/
typedef struct {
char *product_key;
char *device_name;
/**
* @brief 报文内容所对应的报文类型, 更多信息请参考@ref aiot_devinfo_recv_type_t
*/
aiot_devinfo_recv_type_t type;
union {
/**
* @brief 从云端收到的更新或删除设备标签的应答
*/
aiot_devinfo_recv_generic_reply_t generic_reply;
} data;
} aiot_devinfo_recv_t;
/**
* @brief devinfo模块收到从网络上来的报文时, 通知用户所调用的数据回调函数
*
* @param[in] handle devinfo会话句柄
* @param[in] packet devinfo消息结构体, 存放收到的devinfo报文内容
* @param[in] userdata 用户上下文
*
* @return void
*/
typedef void (* aiot_devinfo_recv_handler_t)(void *handle, const aiot_devinfo_recv_t *packet, void *userdata);
/**
* @brief devinfo模块内部发生值得用户关注的状态变化时, 通知用户的事件类型
*/
typedef enum {
/**
* @brief 收到的应答中设备信息不合法, 无法获取product key和device name
*/
AIOT_DEVINFOEVT_INVALID_DEVINFO,
/**
* @brief 收到的应答中字段不合法
*/
AIOT_DEVINFOEVT_INVALID_RESPONSE,
/**
* @brief 收到的应答中字段格式错误
*/
AIOT_DEVINFOEVT_INVALID_RESPONSE_FORMAT,
} aiot_devinfo_event_type_t;
/**
* @brief devinfo模块内部发生值得用户关注的状态变化时, 通知用户的事件内容
*/
typedef struct {
/**
* @brief 事件内容所对应的事件类型, 更多信息请参考@ref aiot_devinfo_event_type_t
*/
aiot_devinfo_event_type_t type;
} aiot_devinfo_event_t;
/**
* @brief devinfo模块内部发生值得用户关注的状态变化时, 通知用户所调用的事件回调函数
*
* @param[in] handle, devinfo会话句柄
* @param[in] event, devinfo模块中发生的事件的内容
* @param[in] userdata, 用户上下文
*
* @return void
*/
typedef void (*aiot_devinfo_event_handler_t)(void *handle, const aiot_devinfo_event_t *event, void *userdata);
/**
* @brief @ref aiot_devinfo_msg_t 中的发送消息类型
*
* @details
*
* 消息类型有两个, 分别是更新设备标签和删除设备标签
*/
typedef enum {
/**
* @brief 更新设备标签
*/
AIOT_DEVINFO_MSG_UPDATE,
/**
* @brief 删除设备标签
*/
AIOT_DEVINFO_MSG_DELETE
} aiot_devinfo_msg_type_t;
/**
* @brief 更新或删除设备标签的params内容
*/
typedef struct {
char *params;
} aiot_devinfo_msg_data_t;
typedef struct {
/**
* @brief 设备的product key
*/
char *product_key;
/**
* @brief 设备的device name
*/
char *device_name;
/**
* @brief 消息类型, 更多信息请参考@ref aiot_devinfo_msg_type_t
*/
aiot_devinfo_msg_type_t type;
union {
/**
* @brief 更新设备标签, 格式:"[{\"attrKey\":\"xxx\",\"attrValue\":\"yyy\"}]"
*
* @details
*
* 从上述格式可以看出更新设备标签的格式是一个JSON数组一次可按attrKey和attrValue上报多组设备标签
*/
aiot_devinfo_msg_data_t update;
/**
* @brief 删除设备标签, 格式:"[{\"attrKey\":\"xxx\"}]"
*
* @details
*
* 从上述格式可以看出删除设备标签的格式是一个JSON数组一次可按attrKey删除多组设备标签
*/
aiot_devinfo_msg_data_t delete;
} data;
} aiot_devinfo_msg_t;
/**
* @brief @ref aiot_devinfo_setopt 接口的option参数可选值.
*
* @details 下文每个选项中的数据类型, 指的是@ref aiot_devinfo_setopt 中, data参数的数据类型
*
* 1. data的数据类型是void *时, 以配置@ref AIOT_DEVINFOOPT_MQTT_HANDLE 为例:
*
* void *mqtt_handle = aiot_mqtt_init();
* aiot_devinfo_setopt(devinfo_handle, AIOT_DEVINFOOPT_MQTT_HANDLE, mqtt_handle);
*
* 2. data的数据类型是其他数据类型时, 以配置@ref AIOT_DEVINFOOPT_DEINIT_TIMEOUT_MS 为例:
*
* uint32_t deinit_timeout_ms = 443;
* aiot_devinfo_setopt(devinfo_handle, AIOT_DEVINFOOPT_DEINIT_TIMEOUT_MS, (void *)&deinit_timeout_ms);
*/
typedef enum {
/**
* @brief devinfo会话 需要的MQTT句柄, 需要先建立MQTT连接, 再设置MQTT句柄
*/
AIOT_DEVINFOOPT_MQTT_HANDLE,
/**
* @brief 设置回调, 它在SDK收到网络报文的时候被调用, 告知用户
*
* @details
*
* 数据类型: ( @ref aiot_devinfo_recv_handler_t)
*/
AIOT_DEVINFOOPT_RECV_HANDLER,
/**
* @brief 设置回调, 它在SDK发生内部状态变更时被调用, 告知用户
*
* @details
*
* 数据类型: ( @ref aiot_devinfo_event_handler_t)
*/
AIOT_DEVINFOOPT_EVENT_HANDLER,
/**
* @brief 用户需要SDK暂存的上下文, 数据类型为(void *)
*
* @details 这个上下文指针会在 AIOT_DEVINFOOPT_RECV_HANDLER 和 AIOT_DEVINFOOPT_EVENT_HANDLER 设置的回调被调用时, 由SDK传给用户
*/
AIOT_DEVINFOOPT_USERDATA,
/**
* @brief 销毁devinfo实例时, 等待其他api执行完毕的时间
*
* @details
*
* 当调用@ref aiot_devinfo_deinit 销毁devinfo实例时, 若继续调用其他aiot_devinfo_xxx API, API会返回@ref STATE_USER_INPUT_EXEC_DISABLED 错误
*
* 此时, 用户应该停止调用其他aiot_devinfo_xxx API
*
* 数据类型: (uint32_t *) 默认值: (2 * 1000) ms
*/
AIOT_DEVINFOOPT_DEINIT_TIMEOUT_MS,
AIOT_DEVINFOOPT_MAX
} aiot_devinfo_option_t;
/**
* @brief 创建devinfo会话实例, 并以默认值配置会话参数
*
* @return void *
* @retval 非NULL devinfo实例的句柄
* @retval NULL 初始化失败, 一般是内存分配失败导致
*
*/
void *aiot_devinfo_init(void);
/**
* @brief 配置devinfo会话
*
* @param[in] handle devinfo会话句柄
* @param[in] option 配置选项, 更多信息请参考@ref aiot_devinfo_option_t
* @param[in] data 配置选项数据, 更多信息请参考@ref aiot_devinfo_option_t
*
* @return int32_t
* @retval <STATE_SUCCESS 参数配置失败
* @retval >=STATE_SUCCESS 参数配置成功
*
*/
int32_t aiot_devinfo_setopt(void *handle, aiot_devinfo_option_t option, void *data);
/**
* @brief 结束devinfo会话, 销毁实例并回收资源
*
* @param[in] handle 指向devinfo会话句柄的指针
*
* @return int32_t
* @retval <STATE_SUCCESS 执行失败
* @retval >=STATE_SUCCESS 执行成功
*
*/
int32_t aiot_devinfo_deinit(void **handle);
/**
* @brief 向devinfo服务器发送devinfo消息请求
*
* @param handle devinfo会话句柄
* @param msg devinfo发送给云端的删除/更新设备标签信息的报文
*
* @return int32_t
* @retval <STATE_SUCCESS 请求发送失败
* @retval >=STATE_SUCCESS 请求发送成功
*/
int32_t aiot_devinfo_send(void *handle, aiot_devinfo_msg_t *msg);
#if defined(__cplusplus)
}
#endif
#endif /* __AIOT_DEVINFO_API_H__ */

View File

@@ -0,0 +1,57 @@
/**
* @file devinfo_private.h
* @brief devinfo模块内部的宏定义和数据结构声明, 不面向其它模块, 更不面向用户
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#ifndef __DEVINFO_PRIVATE_H__
#define __DEVINFO_PRIVATE_H__
#if defined(__cplusplus)
extern "C" {
#endif
#include "core_stdinc.h"
/* TODO: 这一段列出需要包含SDK其它模块头文件, 与上一段落以1个空行隔开 */
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
#include "aiot_devinfo_api.h" /* 内部头文件是用户可见头文件的超集 */
typedef struct {
aiot_sysdep_portfile_t *sysdep; /* 底层依赖回调合集的引用指针 */
void *mqtt_handle;
aiot_devinfo_event_handler_t event_handler; /* 组件内部运行状态变更时, 通知用户的回调 */
aiot_devinfo_recv_handler_t recv_handler; /* 组件从协议栈读到内容时, 通知用户的回调 */
void *userdata; /* 组件调用以上2个 devinfo_handler 时的入参之一 */
uint32_t deinit_timeout_ms;
/*---- 以上都是用户在API可配 ----*/
/*---- 以下都是DEVINFO在内部使用, 用户无感知 ----*/
void *data_mutex; /* 保护本地的数据结构 */
uint8_t exec_enabled;
uint32_t exec_count;
} devinfo_handle_t;
#define DEVINFO_MODULE_NAME "devinfo" /* 用于内存统计的模块名字符串 */
#define DEVINFO_DEFAULT_DEINIT_TIMEOUT_MS (2 * 1000)
#define DEVINFO_UPDATE_TOPIC_FMT "/sys/%s/%s/thing/deviceinfo/update"
#define DEVINFO_UPDATE_REPLY_TOPIC "/sys/+/+/thing/deviceinfo/update_reply"
#define DEVINFO_DELETE_TOPIC_FMT "/sys/%s/%s/thing/deviceinfo/delete"
#define DEVINFO_DELETE_REPLY_TOPIC "/sys/+/+/thing/deviceinfo/delete_reply"
#define DEVINFO_DEINIT_INTERVAL_MS (100)
#if defined(__cplusplus)
}
#endif
#endif /* __DEVINFO_PRIVATE_H__ */

View File

@@ -0,0 +1,3 @@
Name: 设备诊断模块
DIAG Component for Link SDK V4.0.0

View File

@@ -0,0 +1,763 @@
/**
* @file aiot_diag_api.c
* @brief diag模块的API接口实现, 提供诊断SDK的能力
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#include "diag_private.h"
#include "core_string.h"
#include "core_log.h"
#include "core_global.h"
#include "core_mqtt.h"
#include "core_diag.h"
static void _diag_mqtt_conn_hb_extra_clean(void *handle, void *extra_data);
static int32_t _diag_mqtt_conn_hb_extra_stop(void *handle, diag_running_state_node_t *node, uint32_t stat_idx,
uint32_t stat_number, void *extra_data);
static int32_t _diag_mqtt_conn_report_desc_append(void *handle, diag_running_state_t *running_state,
diag_running_state_node_t *node, char **desc);
static int32_t _diag_mqtt_hb_report_desc_append(void *handle, diag_running_state_t *running_state,
diag_running_state_node_t *node, char **desc);
static void _diag_alink_uplink_extra_clean(void *handle, void *extra_data);
static int32_t _diag_alink_uplink_extra_stop(void *handle, diag_running_state_node_t *node, uint32_t stat_idx,
uint32_t stat_number, void *extra_data);
static int32_t _diag_alink_uplink_report_desc_append(void *handle, diag_running_state_t *running_state,
diag_running_state_node_t *node, char **desc);
static diag_config_t g_diag_config[] = {
{
DIAG_MQTT_CONNECTION_STAT_INDEX,
DIAG_MQTT_CONNECTION_NAME_STR,
DIAG_TLV_MQTT_CONNECTION,
{
DIAG_DEFAULT_MQTT_CONN_ENABLED,
DIAG_DEFAULT_MQTT_CONN_INTERVAL_MS,
DIAG_DEFAULT_MQTT_CONN_WARNING_THRESHOLD,
DIAG_DEFAULT_MQTT_CONN_FATAL_THRESHOLD,
},
DIAG_DEFAULT_MQTT_CONN_MAX_STAT_NUMBER,
{
_diag_mqtt_conn_hb_extra_clean,
_diag_mqtt_conn_hb_extra_stop,
_diag_mqtt_conn_report_desc_append
},
1
},
{
DIAG_MQTT_HEARTBEAT_STAT_INDEX,
DIAG_MQTT_HEARTBEAT_NAME_STR,
DIAG_TLV_MQTT_HEARTBEAT,
{
DIAG_DEFAULT_MQTT_HB_ENABLED,
DIAG_DEFAULT_MQTT_HB_INTERVAL_MS,
DIAG_DEFAULT_MQTT_HB_WARNING_THRESHOLD,
DIAG_DEFAULT_MQTT_HB_FATAL_THRESHOLD
},
DIAG_DEFAULT_MQTT_HB_MAX_STAT_NUMBER,
{
_diag_mqtt_conn_hb_extra_clean,
_diag_mqtt_conn_hb_extra_stop,
_diag_mqtt_hb_report_desc_append,
},
0
},
{
DIAG_ALINK_UPLINK_STAT_INDEX,
DIAG_ALINK_UPLINK_NAME_STR,
DIAG_TLV_ALINK_UPLINK,
{
DIAG_DEFAULT_ALINK_UPLINK_ENABLED,
DIAG_DEFAULT_ALINK_UPLINK_INTERVAL_MS,
DIAG_DEFAULT_ALINK_UPLINK_WARNING_THRESHOLD,
DIAG_DEFAULT_ALINK_UPLINK_FATAL_THRESHOLD
},
DIAG_DEFAULT_ALINK_UPLINK_MAX_STAT_NUMBER,
{
_diag_alink_uplink_extra_clean,
_diag_alink_uplink_extra_stop,
_diag_alink_uplink_report_desc_append
},
0
}
};
static void _core_diag_exec_inc(diag_handle_t *diag_handle)
{
diag_handle->sysdep->core_sysdep_mutex_lock(diag_handle->data_mutex);
diag_handle->exec_count++;
diag_handle->sysdep->core_sysdep_mutex_unlock(diag_handle->data_mutex);
}
static void _core_diag_exec_dec(diag_handle_t *diag_handle)
{
diag_handle->sysdep->core_sysdep_mutex_lock(diag_handle->data_mutex);
diag_handle->exec_count--;
diag_handle->sysdep->core_sysdep_mutex_unlock(diag_handle->data_mutex);
}
static void _diag_desc_list_append(diag_handle_t *diag_handle, diag_stat_t *stat,
diag_running_state_node_t *running_state_node, struct core_list_head *desc_list)
{
int32_t res = STATE_SUCCESS;
char *desc = NULL;
diag_desc_node_t *node = NULL;
res = stat->stat_cb.desc_append_cb(diag_handle, &stat->running_state, running_state_node, &desc);
if (res < STATE_SUCCESS) {
return;
}
node = diag_handle->sysdep->core_sysdep_malloc(sizeof(diag_desc_node_t), DIAG_MODULE_NAME);
if (node == NULL) {
diag_handle->sysdep->core_sysdep_free(desc);
return;
}
memset(node, 0, sizeof(diag_desc_node_t));
node->timestamp = core_log_get_timestamp(diag_handle->sysdep);
node->code = stat->running_state.code;
node->module_name = stat->running_state.name;
node->level = (stat->running_state.is_reported == 0) ? (running_state_node->level) : (DIAG_REPORT_LEVEL_WARNING_STR);
node->desc = desc;
node->qos = stat->running_state.qos;
CORE_INIT_LIST_HEAD(&node->linked_node);
core_list_add_tail(&node->linked_node, desc_list);
}
static void _diag_desc_list_send(diag_handle_t *diag_handle, struct core_list_head *desc_list)
{
diag_desc_node_t *node = NULL;
core_list_for_each_entry(node, desc_list, linked_node, diag_desc_node_t) {
/* local event notify */
if ((diag_handle->event_handler != NULL) && (diag_handle->local_report_enabled == 1)) {
aiot_diag_event_t event;
memset(&event, 0, sizeof(aiot_diag_event_t));
event.type = AIOT_DIAGEVT_ALERT;
event.data.alert.module_name = node->module_name;
event.data.alert.level = node->level;
event.data.alert.desc = node->desc;
diag_handle->event_handler(diag_handle, &event, diag_handle->userdata);
}
/* cloud event report */
if (diag_handle->cloud_report_enabled == 1) {
int32_t res = STATE_SUCCESS;
char *topic = NULL, *topic_fmt = DIAG_REPORT_TOPIC_FMT;
char *topic_src[] = { core_mqtt_get_product_key(diag_handle->mqtt_handle), core_mqtt_get_device_name(diag_handle->mqtt_handle) };
int32_t alink_id = 0;
char alink_id_str[11] = {0}, utc_time_str[21] = {0}, code_str[11] = {0};
char *payload = NULL, *payload_fmt = DIAG_REPORT_PAYLOAD_FMT;
char *payload_src[] = { alink_id_str, utc_time_str, node->level, node->module_name, code_str, alink_id_str, node->desc };
res = core_sprintf(diag_handle->sysdep, &topic, topic_fmt, topic_src, sizeof(topic_src) / sizeof(char *),
DIAG_MODULE_NAME);
if (res < STATE_SUCCESS) {
continue;
}
core_global_alink_id_next(diag_handle->sysdep, &alink_id);
core_int2str(alink_id, alink_id_str, NULL);
core_uint642str(node->timestamp, utc_time_str, NULL);
core_uint2str(node->code, code_str, NULL);
res = core_sprintf(diag_handle->sysdep, &payload, payload_fmt, payload_src, sizeof(payload_src) / sizeof(char *),
DIAG_MODULE_NAME);
if (res < STATE_SUCCESS) {
diag_handle->sysdep->core_sysdep_free(topic);
continue;
}
res = aiot_mqtt_pub(diag_handle->mqtt_handle, topic, (uint8_t *)payload, (uint32_t)strlen(payload), node->qos);
if (res < STATE_SUCCESS) {
core_log(diag_handle->sysdep, STATE_DIAG_PUB_FAILED, "pub failed\r\n");
}
diag_handle->sysdep->core_sysdep_free(topic);
diag_handle->sysdep->core_sysdep_free(payload);
}
}
}
static void _diag_desc_list_destroy(diag_handle_t *diag_handle, struct core_list_head *desc_list)
{
diag_desc_node_t *node = NULL, *next = NULL;
core_list_for_each_entry_safe(node, next, desc_list, linked_node, diag_desc_node_t) {
core_list_del(&node->linked_node);
diag_handle->sysdep->core_sysdep_free(node->desc);
diag_handle->sysdep->core_sysdep_free(node);
}
}
static void _diag_core_mqtt_process_handler(void *context, aiot_mqtt_event_t *event, core_mqtt_event_t *core_event)
{
uint32_t stat_idx = 0;
diag_handle_t *diag_handle = (diag_handle_t *)context;
uint64_t timenow_ms = core_log_get_timestamp(diag_handle->sysdep);
if (core_event != NULL) {
switch (core_event->type) {
case CORE_MQTTEVT_DEINIT: {
diag_handle->mqtt_handle = NULL;
return;
}
break;
default: {
}
break;
}
}
if (diag_handle->mqtt_process.last_check_time > timenow_ms) {
diag_handle->mqtt_process.last_check_time = timenow_ms;
}
if (timenow_ms - diag_handle->mqtt_process.last_check_time >= DIAG_MQTT_PROCESS_CHECK_INTERVAL_MS) {
diag_running_state_node_t *node = NULL;
struct core_list_head desc_list;
CORE_INIT_LIST_HEAD(&desc_list);
for (stat_idx = 0; stat_idx < DIAG_STAT_ITEM_NUMBER; stat_idx++) {
diag_handle->sysdep->core_sysdep_mutex_lock(diag_handle->diag_stat[stat_idx].running_state.mutex);
diag_handle->diag_stat[stat_idx].running_state.alert_counts = 0;
core_list_for_each_entry(node, &diag_handle->diag_stat[stat_idx].running_state.linked_list,
linked_node, diag_running_state_node_t) {
if ((node->stop_time != 0) && (node->start_time > node->stop_time)) {
node->start_time = node->stop_time;
}
if (node->is_diag == 1) {
if (node->start_time > timenow_ms) {
node->start_time = timenow_ms;
}
if (node->stop_time == 0
&& (timenow_ms - node->start_time >= diag_handle->diag_stat[stat_idx].config.fatal_threshold)) {
node->stop_time = node->start_time + diag_handle->diag_stat[stat_idx].config.fatal_threshold;
}
if (node->stop_time != 0) {
if ((node->stop_time - node->start_time >= diag_handle->diag_stat[stat_idx].config.warning_threashold) &&
(node->stop_time - node->start_time < diag_handle->diag_stat[stat_idx].config.fatal_threshold)) {
node->level = DIAG_REPORT_LEVEL_WARNING_STR;
} else if (node->stop_time - node->start_time >= diag_handle->diag_stat[stat_idx].config.fatal_threshold) {
node->level = DIAG_REPORT_LEVEL_FATAL_STR;
}
node->is_diag = 0;
}
if ((diag_handle->diag_stat[stat_idx].running_state.is_reported == 0) && (node->level != NULL)) {
/* report first in current period*/
_diag_desc_list_append(diag_handle, &diag_handle->diag_stat[stat_idx], node, &desc_list);
diag_handle->diag_stat[stat_idx].running_state.is_reported = 1;
diag_handle->diag_stat[stat_idx].running_state.report_start_time = timenow_ms;
}
}
if ((node->start_time >= diag_handle->diag_stat[stat_idx].running_state.report_start_time) && node->level != NULL) {
diag_handle->diag_stat[stat_idx].running_state.alert_counts++;
}
if (diag_handle->diag_stat[stat_idx].running_state.report_start_time > timenow_ms) {
diag_handle->diag_stat[stat_idx].running_state.report_start_time = timenow_ms;
}
if ((diag_handle->diag_stat[stat_idx].running_state.is_reported == 1) &&
(timenow_ms - diag_handle->diag_stat[stat_idx].running_state.report_start_time >=
diag_handle->diag_stat[stat_idx].config.interval_ms) &&
(node->linked_node.next == &diag_handle->diag_stat[stat_idx].running_state.linked_list)) {
/* report alert counts in this period */
if (diag_handle->diag_stat[stat_idx].running_state.alert_counts > 0) {
_diag_desc_list_append(diag_handle, &diag_handle->diag_stat[stat_idx], node, &desc_list);
}
diag_handle->diag_stat[stat_idx].running_state.is_reported = 0;
diag_handle->diag_stat[stat_idx].running_state.report_start_time = timenow_ms;
}
}
diag_handle->sysdep->core_sysdep_mutex_unlock(diag_handle->diag_stat[stat_idx].running_state.mutex);
}
_diag_desc_list_send(diag_handle, &desc_list);
_diag_desc_list_destroy(diag_handle, &desc_list);
diag_handle->mqtt_process.last_check_time = timenow_ms;
}
}
static int32_t _diag_core_mqtt_operate_process_handler(diag_handle_t *diag_handle, core_mqtt_option_t option)
{
core_mqtt_process_data_t process_data;
memset(&process_data, 0, sizeof(core_mqtt_process_data_t));
process_data.handler = _diag_core_mqtt_process_handler;
process_data.context = diag_handle;
return core_mqtt_setopt(diag_handle->mqtt_handle, option, &process_data);
}
static void _diag_mqtt_conn_hb_extra_clean(void *handle, void *extra_data)
{
}
static int32_t _diag_mqtt_conn_hb_extra_stop(void *handle, diag_running_state_node_t *node, uint32_t stat_idx,
uint32_t stat_number, void *extra_data)
{
if (node->stop_time != 0) {
return STATE_DIAG_STOP_NODE_NOT_MATCH;
}
return STATE_SUCCESS;
}
static int32_t _diag_mqtt_conn_report_desc_append(void *handle, diag_running_state_t *running_state,
diag_running_state_node_t *node, char **desc)
{
char *tmp_desc = NULL;
diag_handle_t *diag_handle = (diag_handle_t *)handle;
uint64_t timenow_ms = core_log_get_timestamp(diag_handle->sysdep);
if (running_state->is_reported == 0) {
/* report first time */
uint64_t time_elapsed = node->stop_time - node->start_time;
char time_elapsed_str[21] = {0};
char *desc_fmt = "MQTT connection establish time %s ms";
char *desc_src[] = { time_elapsed_str };
core_uint642str(time_elapsed, time_elapsed_str, NULL);
core_sprintf(diag_handle->sysdep, &tmp_desc, desc_fmt, desc_src, sizeof(desc_src) / sizeof(char *), DIAG_MODULE_NAME);
} else if (running_state->is_reported == 1) {
/* report period stat data */
uint64_t time_elapsed = timenow_ms - running_state->report_start_time;
char time_elapsed_str[21] = {0};
char alert_counts_str[11] = {0};
char *desc_fmt = "MQTT connection has been alert extra %s times in past %s ms";
char *desc_src[] = { alert_counts_str, time_elapsed_str };
core_uint642str(time_elapsed, time_elapsed_str, NULL);
core_uint2str(running_state->alert_counts, alert_counts_str, NULL);
core_sprintf(diag_handle->sysdep, &tmp_desc, desc_fmt, desc_src, sizeof(desc_src) / sizeof(char *), DIAG_MODULE_NAME);
}
if (tmp_desc == NULL) {
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
*desc = tmp_desc;
return STATE_SUCCESS;
}
static int32_t _diag_mqtt_hb_report_desc_append(void *handle, diag_running_state_t *running_state,
diag_running_state_node_t *node, char **desc)
{
char *tmp_desc = NULL;
diag_handle_t *diag_handle = (diag_handle_t *)handle;
uint64_t timenow_ms = core_log_get_timestamp(diag_handle->sysdep);
if (running_state->is_reported == 0) {
/* report first time */
uint64_t time_elapsed = node->stop_time - node->start_time;
char time_elapsed_str[21] = {0};
char *desc_fmt = "MQTT lost heartbeat 1 times in %s ms";
char *desc_src[] = { time_elapsed_str };
core_uint642str(time_elapsed, time_elapsed_str, NULL);
core_sprintf(diag_handle->sysdep, &tmp_desc, desc_fmt, desc_src, sizeof(desc_src) / sizeof(char *), DIAG_MODULE_NAME);
} else if (running_state->is_reported == 1) {
/* report period stat data */
uint64_t time_elapsed = timenow_ms - running_state->report_start_time;
char time_elapsed_str[21] = {0};
char alert_counts_str[11] = {0};
char *desc_fmt = "MQTT lost heartbeat has been alert extra %s times in past %s ms";
char *desc_src[] = { alert_counts_str, time_elapsed_str };
core_uint642str(time_elapsed, time_elapsed_str, NULL);
core_uint2str(running_state->alert_counts, alert_counts_str, NULL);
core_sprintf(diag_handle->sysdep, &tmp_desc, desc_fmt, desc_src, sizeof(desc_src) / sizeof(char *), DIAG_MODULE_NAME);
}
if (tmp_desc == NULL) {
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
*desc = tmp_desc;
return STATE_SUCCESS;
}
static void _diag_alink_uplink_extra_clean(void *handle, void *extra_data)
{
diag_handle_t *diag_handle = (diag_handle_t *)handle;
diag_handle->sysdep->core_sysdep_free(extra_data);
}
static int32_t _diag_alink_uplink_extra_stop(void *handle, diag_running_state_node_t *node, uint32_t stat_idx,
uint32_t stat_number, void *extra_data)
{
if (*(uint32_t *)node->extra_data != *(uint32_t *)extra_data) {
return STATE_DIAG_STOP_NODE_NOT_MATCH;
}
return STATE_SUCCESS;
}
static int32_t _diag_alink_uplink_report_desc_append(void *handle, diag_running_state_t *running_state,
diag_running_state_node_t *node, char **desc)
{
char *tmp_desc = NULL;
diag_handle_t *diag_handle = (diag_handle_t *)handle;
uint64_t timenow_ms = core_log_get_timestamp(diag_handle->sysdep);
if (running_state->is_reported == 0) {
/* report first time */
char alink_id_str[11] = {0};
uint64_t time_elapsed = node->stop_time - node->start_time;
char time_elapsed_str[21] = {0};
char *desc_fmt = "Alink message %s waiting for reply has already exceed %s ms";
char *desc_src[] = { alink_id_str, time_elapsed_str };
core_uint2str(*(uint32_t *)node->extra_data, alink_id_str, NULL);
core_uint642str(time_elapsed, time_elapsed_str, NULL);
core_sprintf(diag_handle->sysdep, &tmp_desc, desc_fmt, desc_src, sizeof(desc_src) / sizeof(char *), DIAG_MODULE_NAME);
} else if (running_state->is_reported == 1) {
/* report period stat data */
uint64_t time_elapsed = timenow_ms - running_state->report_start_time;
char time_elapsed_str[21] = {0};
char alert_counts_str[11] = {0};
char *desc_fmt = "Alink message reply too slow has been alert extra %s times in past %s ms";
char *desc_src[] = { alert_counts_str, time_elapsed_str };
core_uint642str(time_elapsed, time_elapsed_str, NULL);
core_uint2str(running_state->alert_counts, alert_counts_str, NULL);
core_sprintf(diag_handle->sysdep, &tmp_desc, desc_fmt, desc_src, sizeof(desc_src) / sizeof(char *), DIAG_MODULE_NAME);
}
if (tmp_desc == NULL) {
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
*desc = tmp_desc;
return STATE_SUCCESS;
}
static void _diag_running_state_start(diag_handle_t *diag_handle, diag_stat_t *stat, uint64_t timestamp,
void *extra_data)
{
diag_running_state_node_t *node = NULL;
diag_handle->sysdep->core_sysdep_mutex_lock(stat->running_state.mutex);
if (stat->running_state.stat_number == stat->running_state.max_stat_number) {
node = core_list_entry(stat->running_state.linked_list.next, diag_running_state_node_t, linked_node);
core_list_del(&node->linked_node);
stat->running_state.stat_number--;
stat->stat_cb.extra_clean_cb(diag_handle, node->extra_data);
diag_handle->sysdep->core_sysdep_free(node);
}
node = diag_handle->sysdep->core_sysdep_malloc(sizeof(diag_running_state_node_t), DIAG_MODULE_NAME);
if (node == NULL) {
diag_handle->sysdep->core_sysdep_mutex_unlock(stat->running_state.mutex);
return;
}
memset(node, 0, sizeof(diag_running_state_node_t));
node->is_diag = 1;
node->start_time = timestamp;
node->extra_data = extra_data;
CORE_INIT_LIST_HEAD(&node->linked_node);
core_list_add_tail(&node->linked_node, &stat->running_state.linked_list);
stat->running_state.stat_number++;
diag_handle->sysdep->core_sysdep_mutex_unlock(stat->running_state.mutex);
}
static void _diag_running_state_stop(diag_handle_t *diag_handle, diag_stat_t *stat, uint64_t timestamp,
void *extra_data)
{
uint32_t stat_idx = 0;
diag_running_state_node_t *node = NULL;
diag_handle->sysdep->core_sysdep_mutex_lock(stat->running_state.mutex);
core_list_for_each_entry(node, &stat->running_state.linked_list,
linked_node, diag_running_state_node_t) {
if (stat->stat_cb.extra_stop_cb(diag_handle, node,
stat_idx, stat->running_state.stat_number, extra_data) >= STATE_SUCCESS) {
node->stop_time = timestamp;
break;
}
stat_idx++;
}
stat->stat_cb.extra_clean_cb(diag_handle, extra_data);
diag_handle->sysdep->core_sysdep_mutex_unlock(stat->running_state.mutex);
}
static int32_t _diag_get_extra_data(diag_handle_t *diag_handle, diag_raw_data_t *raw_data, uint32_t code,
void **out_extra_data)
{
if (code == DIAG_TLV_ALINK_UPLINK) {
uint32_t tlv_sub_type1 = (raw_data->data[4] << 8) | (raw_data->data[5]);
if (tlv_sub_type1 == DIAG_TLV_ALINK_MSGID) {
uint32_t *extra_data = NULL;
extra_data = diag_handle->sysdep->core_sysdep_malloc(sizeof(uint32_t), DIAG_MODULE_NAME);
if (extra_data == NULL) {
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
memset(extra_data, 0, sizeof(uint32_t));
*extra_data = ((raw_data->data[7] << 24) | (raw_data->data[8] << 16) | (raw_data->data[9] << 8) | (raw_data->data[10]));
*(uint32_t **)out_extra_data = extra_data;
}
}
return STATE_SUCCESS;
}
static void _diag_raw_data(diag_handle_t *diag_handle, diag_raw_data_t *raw_data)
{
uint32_t code = (raw_data->data[0] << 8) | (raw_data->data[1]);
uint32_t stat_idx = 0;
void *extra_data = NULL;
for (stat_idx = 0; stat_idx < DIAG_STAT_ITEM_NUMBER; stat_idx++) {
if (g_diag_config[stat_idx].code == code) {
if (_diag_get_extra_data(diag_handle, raw_data, code, &extra_data) < STATE_SUCCESS) {
return;
}
if (raw_data->data[3] == 0x00) {
_diag_running_state_start(diag_handle, &diag_handle->diag_stat[stat_idx], raw_data->timestamp, extra_data);
} else if (raw_data->data[3] == 0x01) {
_diag_running_state_stop(diag_handle, &diag_handle->diag_stat[stat_idx], raw_data->timestamp, extra_data);
}
}
}
}
static void _diag_core_diag_callback(void *handle, uint64_t timestamp, int32_t code, uint8_t *data, uint32_t data_len)
{
diag_raw_data_t raw_data;
memset(&raw_data, 0, sizeof(diag_raw_data_t));
raw_data.timestamp = timestamp;
raw_data.code = code;
raw_data.data = data;
raw_data.data_len = data_len;
_diag_raw_data((diag_handle_t *)handle, &raw_data);
}
static void _diag_running_state_clean(diag_handle_t *diag_handle)
{
uint32_t stat_idx = 0;
diag_running_state_node_t *node = NULL, *next = NULL;
for (stat_idx = 0; stat_idx < DIAG_STAT_ITEM_NUMBER; stat_idx++) {
diag_handle->sysdep->core_sysdep_mutex_lock(diag_handle->diag_stat[stat_idx].running_state.mutex);
core_list_for_each_entry_safe(node, next, &diag_handle->diag_stat[stat_idx].running_state.linked_list,
linked_node, diag_running_state_node_t) {
core_list_del(&node->linked_node);
diag_handle->diag_stat[stat_idx].stat_cb.extra_clean_cb(diag_handle, node->extra_data);
diag_handle->sysdep->core_sysdep_free(node);
}
diag_handle->sysdep->core_sysdep_mutex_unlock(diag_handle->diag_stat[stat_idx].running_state.mutex);
diag_handle->sysdep->core_sysdep_mutex_deinit(&diag_handle->diag_stat[stat_idx].running_state.mutex);
}
}
void *aiot_diag_init(void)
{
int32_t res = STATE_SUCCESS;
uint32_t stat_idx = 0;
diag_handle_t *diag_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
sysdep = aiot_sysdep_get_portfile();
if (sysdep == NULL) {
return NULL;
}
res = core_global_init(sysdep);
if (res < STATE_SUCCESS) {
return NULL;
}
diag_handle = sysdep->core_sysdep_malloc(sizeof(diag_handle_t), DIAG_MODULE_NAME);
if (diag_handle == NULL) {
return NULL;
}
memset(diag_handle, 0, sizeof(diag_handle_t));
diag_handle->sysdep = sysdep;
diag_handle->local_report_enabled = DIAG_DAFAULT_LOCAL_REPORT_ENABLED;
diag_handle->cloud_report_enabled = DIAG_DAFAULT_CLOUD_REPORT_ENABLED;
for (stat_idx = 0; stat_idx < DIAG_STAT_ITEM_NUMBER; stat_idx++) {
diag_handle->diag_stat[stat_idx].running_state.code = g_diag_config[stat_idx].code;
diag_handle->diag_stat[stat_idx].running_state.name = g_diag_config[stat_idx].name;
memcpy(&diag_handle->diag_stat[stat_idx].config, &g_diag_config[stat_idx].def_config, sizeof(aiot_diag_config_t));
diag_handle->diag_stat[stat_idx].running_state.max_stat_number = g_diag_config[stat_idx].def_max_stat_number;
memcpy(&diag_handle->diag_stat[stat_idx].stat_cb, &g_diag_config[stat_idx].def_stat_cb, sizeof(diag_stat_callback_t));
diag_handle->diag_stat[stat_idx].running_state.qos = g_diag_config[stat_idx].qos;
CORE_INIT_LIST_HEAD(&diag_handle->diag_stat[stat_idx].running_state.linked_list);
diag_handle->diag_stat[stat_idx].running_state.mutex = diag_handle->sysdep->core_sysdep_mutex_init();
}
diag_handle->deinit_timeout_ms = DIAG_DEFAULT_DEINIT_TIMEOUT_MS;
diag_handle->data_mutex = sysdep->core_sysdep_mutex_init();
diag_handle->exec_enabled = 1;
core_diag_set_cb(diag_handle, _diag_core_diag_callback);
return diag_handle;
}
int32_t aiot_diag_setopt(void *handle, aiot_diag_option_t option, void *data)
{
int32_t res = STATE_SUCCESS;
diag_handle_t *diag_handle = (diag_handle_t *)handle;
if (diag_handle == NULL || data == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (option >= AIOT_DIAGOPT_MAX) {
return STATE_USER_INPUT_OUT_RANGE;
}
if (diag_handle->exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
_core_diag_exec_inc(diag_handle);
diag_handle->sysdep->core_sysdep_mutex_lock(diag_handle->data_mutex);
switch (option) {
case AIOT_DIAGOPT_MQTT_HANDLE: {
diag_handle->mqtt_handle = data;
diag_handle->sysdep->core_sysdep_mutex_unlock(diag_handle->data_mutex);
res = _diag_core_mqtt_operate_process_handler(diag_handle, CORE_MQTTOPT_APPEND_PROCESS_HANDLER);
diag_handle->sysdep->core_sysdep_mutex_lock(diag_handle->data_mutex);
}
break;
case AIOT_DIAGOPT_LOCAL_REPORT_ENABLED: {
diag_handle->local_report_enabled = *(uint8_t *)data;
}
break;
case AIOT_DIAGOPT_CLOUD_REPORT_ENABLED: {
diag_handle->cloud_report_enabled = *(uint8_t *)data;
}
break;
case AIOT_DIAGOPT_MQTT_CONNECTION: {
memcpy(&diag_handle->diag_stat[DIAG_MQTT_CONNECTION_STAT_INDEX].config, (aiot_diag_config_t *)data,
sizeof(aiot_diag_config_t));
}
break;
case AIOT_DIAGOPT_MQTT_HEARTBEAT: {
memcpy(&diag_handle->diag_stat[DIAG_MQTT_HEARTBEAT_STAT_INDEX].config, (aiot_diag_config_t *)data,
sizeof(aiot_diag_config_t));
}
break;
case AIOT_DIAGOPT_ALINK_UPLINK: {
memcpy(&diag_handle->diag_stat[DIAG_ALINK_UPLINK_STAT_INDEX].config, (aiot_diag_config_t *)data,
sizeof(aiot_diag_config_t));
}
break;
case AIOT_DIAGOPT_RECV_HANDLER: {
diag_handle->recv_handler = (aiot_diag_recv_handler_t)data;
}
break;
case AIOT_DIAGOPT_EVENT_HANDLER: {
diag_handle->event_handler = (aiot_diag_event_handler_t)data;
}
break;
case AIOT_DIAGOPT_USERDATA: {
diag_handle->userdata = data;
}
break;
default: {
res = STATE_USER_INPUT_UNKNOWN_OPTION;
}
break;
}
diag_handle->sysdep->core_sysdep_mutex_unlock(diag_handle->data_mutex);
_core_diag_exec_dec(diag_handle);
return res;
}
int32_t aiot_diag_deinit(void **handle)
{
uint64_t deinit_timestart = 0;
diag_handle_t *diag_handle = NULL;
if (handle == NULL || *handle == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
diag_handle = *(diag_handle_t **)handle;
if (diag_handle->exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
core_diag_set_cb(diag_handle, NULL);
_diag_core_mqtt_operate_process_handler(diag_handle, CORE_MQTTOPT_REMOVE_PROCESS_HANDLER);
diag_handle->exec_enabled = 0;
deinit_timestart = diag_handle->sysdep->core_sysdep_time();
do {
if (diag_handle->exec_count == 0) {
break;
}
diag_handle->sysdep->core_sysdep_sleep(DIAG_DEINIT_INTERVAL_MS);
} while ((diag_handle->sysdep->core_sysdep_time() - deinit_timestart) < diag_handle->deinit_timeout_ms);
if (diag_handle->exec_count != 0) {
return STATE_MQTT_DEINIT_TIMEOUT;
}
*handle = NULL;
_diag_running_state_clean(diag_handle);
diag_handle->sysdep->core_sysdep_mutex_deinit(&diag_handle->data_mutex);
core_global_deinit(diag_handle->sysdep);
diag_handle->sysdep->core_sysdep_free(diag_handle);
return STATE_SUCCESS;
}
int32_t aiot_diag_start(void *handle)
{
diag_handle_t *diag_handle = (diag_handle_t *)handle;
if (diag_handle == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
diag_handle->sysdep->core_sysdep_mutex_lock(diag_handle->data_mutex);
diag_handle->diag_status = 1;
diag_handle->sysdep->core_sysdep_mutex_unlock(diag_handle->data_mutex);
return STATE_SUCCESS;
}
int32_t aiot_diag_stop(void *handle)
{
diag_handle_t *diag_handle = (diag_handle_t *)handle;
if (diag_handle == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
diag_handle->sysdep->core_sysdep_mutex_lock(diag_handle->data_mutex);
diag_handle->diag_status = 0;
diag_handle->sysdep->core_sysdep_mutex_unlock(diag_handle->data_mutex);
return STATE_SUCCESS;
}

View File

@@ -0,0 +1,315 @@
/**
* @file aiot_diag_api.h
* @brief diag模块头文件, 提供诊断SDK的能力
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#ifndef __AIOT_DIAG_API_H__
#define __AIOT_DIAG_API_H__
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdint.h>
/**
* @brief -0x0.00~-0x0.FF表达SDK在diag模块内的状态码
*/
#define STATE_DIAG_BASE (-0x1400)
/**
* @brief STATE_DIAG_CODE1的说明
*/
#define STATE_DIAG_LOG_UNKNOWN_STATE_CODE_BASE (-0x1401)
/**
* @brief STATE_DIAG_CODE2的说明
*/
#define STATE_DIAG_CODE2 (-0x1402)
/**
* @brief STATE_DIAG_PUB_FAILED的说明
*/
#define STATE_DIAG_PUB_FAILED (-0x1403)
/**
* @brief diag模块收到从网络上来的报文时, 通知用户的报文类型
*/
typedef enum {
AIOT_DIAGRECV_DIAG_CONTROL
} aiot_diag_recv_type_t;
/**
* @brief diag模块收到从网络上来的报文时, 通知用户的报文内容
*/
typedef struct {
/**
* @brief 报文内容所对应的报文类型, 更多信息请参考@ref aiot_diag_recv_type_t
*/
aiot_diag_recv_type_t type;
union {
/**
* @brief 收到的云端控制指令
*/
struct {
/**
* @brief 0: 关闭诊断功能1: 打开诊断功能
*/
uint32_t data;
} diag_control;
} data;
} aiot_diag_recv_t;
/**
* @brief diag模块收到从网络上来的报文时, 通知用户所调用的数据回调函数
*
* @param[in] handle diag会话句柄
* @param[in] packet diag消息结构体, 存放收到的diag报文内容
* @param[in] userdata 用户上下文
*
* @return void
*/
typedef void (* aiot_diag_recv_handler_t)(void *handle, const aiot_diag_recv_t *packet, void *userdata);
/**
* @brief diag模块内部发生值得用户关注的状态变化时, 通知用户的事件类型
*/
typedef enum {
/**
* @brief 诊断模块产生的告警信息
*/
AIOT_DIAGEVT_ALERT
} aiot_diag_event_type_t;
/**
* @brief diag模块内部发生值得用户关注的状态变化时, 通知用户的事件内容
*/
typedef struct {
/**
* @brief 事件内容所对应的事件类型, 更多信息请参考@ref aiot_diag_event_type_t
*/
aiot_diag_event_type_t type;
union {
struct {
/**
* @brief 告警模块名
*/
char *module_name;
/**
* @brief 告警级别
*/
char *level;
/**
* @brief 告警信息描述字符串
*/
char *desc;
} alert;
} data;
} aiot_diag_event_t;
/**
* @brief diag模块内部发生值得用户关注的状态变化时, 通知用户所调用的事件回调函数
*
* @param[in] handle, diag会话句柄
* @param[in] event, diag模块中发生的事件的内容
* @param[in] userdata, 用户上下文
*
* @return void
*/
typedef void (*aiot_diag_event_handler_t)(void *handle, const aiot_diag_event_t *event, void *userdata);
/**
* @brief 诊断项的配置参数
*/
typedef struct {
/**
* @brief 对当前诊断项是否进行诊断的开关
*
* @details
*
* 0: 关闭对当前诊断项的诊断, 1: 打开对当前诊断项的诊断
*/
uint8_t enabled;
/**
* @brief 对当前诊断项连续两次告警的最小时间间隔
*/
uint32_t interval_ms;
/**
* @brief warning级别告警的阈值
*/
int64_t warning_threashold;
/**
* @brief fatal级别告警的阈值
*/
int64_t fatal_threshold;
} aiot_diag_config_t;
/**
* @brief @ref aiot_diag_setopt 接口的option参数可选值.
*
* @details 下文每个选项中的数据类型, 指的是@ref aiot_diag_setopt 中, data参数的数据类型
*
* 1. data的数据类型是void *时, 以配置@ref AIOT_DIAGOPT_MQTT_HANDLE 为例:
*
* void *mqtt_handle = aiot_mqtt_init();
* aiot_diag_setopt(diag_handle, AIOT_DIAGOPT_MQTT_HANDLE, mqtt_handle);
*
* 2. data的数据类型是其他数据类型时, 以配置@ref AIOT_DIAGOPT_LOCAL_REPORT_ENABLED 为例:
*
* uint8_t local_report_enabled = 1;
* aiot_mqtt_setopt(diag_handle, AIOT_DIAGOPT_LOCAL_REPORT_ENABLED, (void *)&local_report_enabled);
*/
typedef enum {
/**
* @brief diag会话 需要的MQTT句柄, 需要先建立MQTT连接, 再设置MQTT句柄
*
* @details
*
* 数据类型: (void *)
*/
AIOT_DIAGOPT_MQTT_HANDLE,
/**
* @brief 是否需要从事件回调函数中输出告警信息
*
* @details
*
* 0: 不从事件回调函数中输出告警信息, 1: 从事件回调函数中输出告警信息
*
* 数据类型: (uint8_t *)
*/
AIOT_DIAGOPT_LOCAL_REPORT_ENABLED,
/**
* @brief 是否需要上报告警信息至云端
*
* @details
*
* 0: 不上报告警信息至云端, 1: 上报告警信息至云端
*
* 数据类型: (uint8_t *)
*/
AIOT_DIAGOPT_CLOUD_REPORT_ENABLED,
/**
* @brief MQTT建联时长告警配置
*
* @details
*
* 数据类型: ( @ref aiot_diag_config_t )
*/
AIOT_DIAGOPT_MQTT_CONNECTION,
/**
* @brief MQTT心跳丢失告警配置
*
* @details
*
* 数据类型: ( @ref aiot_diag_config_t )
*/
AIOT_DIAGOPT_MQTT_HEARTBEAT,
/**
* @brief Alink协议上行报文的回复速度告警配置
*
* @details
*
* 数据类型: ( @ref aiot_diag_config_t )
*/
AIOT_DIAGOPT_ALINK_UPLINK,
/**
* @brief 设置回调, 它在SDK收到网络报文的时候被调用, 告知用户
*
* @details
*
* 数据类型: ( @ref aiot_diag_recv_handler_t )
*/
AIOT_DIAGOPT_RECV_HANDLER,
/**
* @brief diag内部发生的事件会从此回调函数进行通知
*
* @details
*
* 数据类型: ( @ref aiot_diag_event_handler_t )
*/
AIOT_DIAGOPT_EVENT_HANDLER,
/**
* @brief 用户需要SDK暂存的上下文
*
* @details 这个上下文指针会在 AIOT_DIAGOPT_RECV_HANDLER 和 AIOT_DIAGOPT_EVENT_HANDLER 设置的回调被调用时, 由SDK传给用户
*
* 数据类型: (void *)
*/
AIOT_DIAGOPT_USERDATA,
AIOT_DIAGOPT_MAX
} aiot_diag_option_t;
/**
* @brief 创建diag会话实例, 并以默认值配置会话参数
*
* @return void *
* @retval 非NULL diag实例的句柄
* @retval NULL 初始化失败, 一般是内存分配失败导致
*
*/
void *aiot_diag_init(void);
/**
* @brief 配置diag会话
*
* @param[in] handle diag会话句柄
* @param[in] option 配置选项, 更多信息请参考@ref aiot_diag_option_t
* @param[in] data 配置选项数据, 更多信息请参考@ref aiot_diag_option_t
*
* @return int32_t
* @retval <STATE_SUCCESS 参数配置失败
* @retval >=STATE_SUCCESS 参数配置成功
*
*/
int32_t aiot_diag_setopt(void *handle, aiot_diag_option_t option, void *data);
/**
* @brief 结束diag会话, 销毁实例并回收资源
*
* @param[in] handle 指向diag会话句柄的指针
*
* @return int32_t
* @retval <STATE_SUCCESS 执行失败
* @retval >=STATE_SUCCESS 执行成功
*
*/
int32_t aiot_diag_deinit(void **handle);
/**
* @brief 开始诊断SDK内部信息
*
* @param handle diag会话句柄
*
* @return int32_t
* @retval <STATE_SUCCESS 请求发送失败
* @retval >=STATE_SUCCESS 请求发送成功
*/
int32_t aiot_diag_start(void *handle);
/**
* @brief 停止诊断SDK内部信息
*
* @param handle diag会话句柄
*
* @return int32_t
* @retval <STATE_SUCCESS 数据接收失败
* @retval >=STATE_SUCCESS 数据接收成功
*/
int32_t aiot_diag_stop(void *handle);
#if defined(__cplusplus)
}
#endif
#endif /* __AIOT_DIAG_API_H__ */

View File

@@ -0,0 +1,191 @@
/**
* @file diag_private.h
* @brief diag模块内部的宏定义和数据结构声明, 不面向其它模块, 更不面向用户
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#ifndef __DIAG_PRIVATE_H__
#define __DIAG_PRIVATE_H__
#if defined(__cplusplus)
extern "C" {
#endif
#include "core_stdinc.h"
#include "core_list.h"
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_diag_api.h"
typedef struct {
uint64_t last_check_time;
} diag_mqtt_process_t;
typedef struct {
uint64_t timestamp;
int32_t code;
uint8_t *data;
uint32_t data_len;
} diag_raw_data_t;
typedef struct {
uint64_t timestamp;
uint16_t code;
char *module_name;
char *level;
char *desc;
uint8_t qos;
struct core_list_head linked_node;
} diag_desc_node_t;
typedef struct {
uint8_t is_diag; /* 该诊断项是否处于诊断状态 */
char *level; /* 告警级别 */
uint64_t start_time; /* 统计开始时间 */
uint64_t stop_time; /* 统计结束时间 */
void *extra_data; /* 统计节点附加数据 */
struct core_list_head linked_node;
} diag_running_state_node_t;
typedef struct {
uint32_t code; /* 诊断项代号 */
char *name; /* 诊断项名 */
uint8_t is_reported; /* 当前统计周期内是否上报过 */
uint64_t report_start_time; /* 当前统计周期开始时间 */
uint32_t alert_counts; /* 当前统计周期累计产生告警次数 */
uint32_t stat_number; /* 当前节点数量 */
uint32_t max_stat_number; /* 统计节点最大数量 */
uint8_t qos; /* 使用MQTT发送告警报文至云端时使用的QoS值 */
void *mutex; /* 数据保护锁 */
struct core_list_head linked_list;
} diag_running_state_t;
typedef void (*diag_running_state_node_extra_clean_t)(void *handle, void *extra_data);
typedef int32_t (*diag_running_state_node_extra_stop_t)(void *handle, diag_running_state_node_t *node, uint32_t stat_idx, uint32_t stat_number, void *extra_data);
typedef int32_t (*diag_report_desc_append_t)(void *handle, diag_running_state_t *running_state, diag_running_state_node_t *node, char **desc);
typedef struct {
diag_running_state_node_extra_clean_t extra_clean_cb;
diag_running_state_node_extra_stop_t extra_stop_cb;
diag_report_desc_append_t desc_append_cb;
} diag_stat_callback_t;
typedef struct {
aiot_diag_config_t config;
diag_running_state_t running_state;
diag_stat_callback_t stat_cb;
} diag_stat_t;
typedef struct {
uint32_t msgid;
} diag_alink_uplink_extra_data_t;
#define DIAG_STAT_ITEM_NUMBER (3)
typedef struct {
aiot_sysdep_portfile_t *sysdep; /* 底层依赖回调合集的引用指针 */
void *mqtt_handle;
uint8_t local_report_enabled;
uint8_t cloud_report_enabled;
diag_stat_t diag_stat[DIAG_STAT_ITEM_NUMBER];
uint32_t deinit_timeout_ms;
aiot_diag_event_handler_t event_handler; /* 组件内部运行状态变更时, 通知用户的回调 */
aiot_diag_recv_handler_t recv_handler; /* 组件从协议栈读到内容时, 通知用户的回调 */
void *userdata; /* 组件调用以上2个 diag_handler 时的入参之一 */
/*---- 以上都是用户在API可配 ----*/
/*---- 以下都是DIAG在内部使用, 用户无感知 ----*/
void *data_mutex; /* 保护本地的数据结构 */
uint8_t diag_status; /* 本地诊断模块状态, 0: stop, 1: start */
uint8_t cloud_switch;
diag_mqtt_process_t mqtt_process;
uint8_t exec_enabled;
uint32_t exec_count;
} diag_handle_t;
typedef struct {
uint32_t stat_idx;
char *name;
uint32_t code;
aiot_diag_config_t def_config;
uint32_t def_max_stat_number;
diag_stat_callback_t def_stat_cb;
uint8_t qos;
} diag_config_t;
#define DIAG_MODULE_NAME "diag" /* 用于内存统计的模块名字符串 */
#define DIAG_DAFAULT_LOCAL_REPORT_ENABLED (1)
#define DIAG_DAFAULT_CLOUD_REPORT_ENABLED (1)
#define DIAG_DEFAULT_DEINIT_TIMEOUT_MS (2 * 1000)
/* MQTT connection diag default configuration */
#define DIAG_DEFAULT_MQTT_CONN_ENABLED (1)
#define DIAG_DEFAULT_MQTT_CONN_INTERVAL_MS (30 * 1000)
#define DIAG_DEFAULT_MQTT_CONN_WARNING_THRESHOLD (200)
#define DIAG_DEFAULT_MQTT_CONN_FATAL_THRESHOLD (500)
#define DIAG_DEFAULT_MQTT_CONN_MAX_STAT_NUMBER (20)
/* MQTT heartbeag diag default configuration */
#define DIAG_DEFAULT_MQTT_HB_ENABLED (1)
#define DIAG_DEFAULT_MQTT_HB_INTERVAL_MS (30 * 1000)
#define DIAG_DEFAULT_MQTT_HB_WARNING_THRESHOLD (800)
#define DIAG_DEFAULT_MQTT_HB_FATAL_THRESHOLD (1500)
#define DIAG_DEFAULT_MQTT_HB_MAX_STAT_NUMBER (20)
/* MQTT alink uplink default configuration */
#define DIAG_DEFAULT_ALINK_UPLINK_ENABLED (1)
#define DIAG_DEFAULT_ALINK_UPLINK_INTERVAL_MS (30 * 1000)
#define DIAG_DEFAULT_ALINK_UPLINK_WARNING_THRESHOLD (600)
#define DIAG_DEFAULT_ALINK_UPLINK_FATAL_THRESHOLD (1000)
#define DIAG_DEFAULT_ALINK_UPLINK_MAX_STAT_NUMBER (20)
#define DIAG_REPORT_TOPIC_FMT "/sys/%s/%s/thing/log/post"
#define DIAG_REPORT_PAYLOAD_FMT "{\"id\":\"%s\",\"version\":\"1.0\",\"params\":[{\"utcTime\":\"%s\"," \
"\"logLevel\":\"%s\",\"module\":\"%s\",\"code\":\"%s\",\"traceContext\":\"%s\",\"logContent\":\"%s\"}]}"
#define DIAG_DEINIT_INTERVAL_MS (100)
#define DIAG_MQTT_PROCESS_CHECK_INTERVAL_MS (2000)
#define DIAG_REPORT_LEVEL_WARNING_STR "WARN"
#define DIAG_REPORT_LEVEL_FATAL_STR "FATAL"
#define DIAG_STATE_MQTT_BASE (STATE_MQTT_BASE)
#define DIAG_STATE_DM_BASE (-0x0A00)
/* MQTT connection diag constant */
#define DIAG_MQTT_CONNECTION_STAT_INDEX (0)
#define DIAG_MQTT_CONNECTION_NAME_STR "DiagMqttConnection"
#define DIAG_TLV_MQTT_CONNECTION (0x0010)
/* MQTT heartbeat diag constant */
#define DIAG_MQTT_HEARTBEAT_STAT_INDEX (1)
#define DIAG_MQTT_HEARTBEAT_NAME_STR "DiagMqttHeartbeat"
#define DIAG_TLV_MQTT_HEARTBEAT (0x0020)
/* MQTT alink uplink diag constant */
#define DIAG_ALINK_UPLINK_STAT_INDEX (2)
#define DIAG_ALINK_UPLINK_NAME_STR "DiagAlinkUplink"
#define DIAG_TLV_ALINK_UPLINK (0x0030)
#define DIAG_TLV_ALINK_MSGID (0x0031)
/* internal state code */
#define STATE_DIAG_STOP_NODE_NOT_MATCH (-0x14FF)
#if defined(__cplusplus)
}
#endif
#endif /* __DIAG_PRIVATE_H__ */

View File

@@ -0,0 +1,2 @@
Name: 基于MQTT的动态注册
MQTT-dynreg Component for Link Kit SDK V4.0.0

View File

@@ -0,0 +1,543 @@
/**
* @file aiot_dynregmq_api.c
* @brief dynregmq模块的API接口实现, 提供获取设备信息的能力
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#include "dynregmq_private.h"
#include "core_string.h"
#include "core_log.h"
#include "core_auth.h"
static void _dynregmq_exec_inc(dynregmq_handle_t *dynregmq_handle)
{
dynregmq_handle->sysdep->core_sysdep_mutex_lock(dynregmq_handle->data_mutex);
dynregmq_handle->exec_count++;
dynregmq_handle->sysdep->core_sysdep_mutex_unlock(dynregmq_handle->data_mutex);
}
static void _dynregmq_exec_dec(dynregmq_handle_t *dynregmq_handle)
{
dynregmq_handle->sysdep->core_sysdep_mutex_lock(dynregmq_handle->data_mutex);
dynregmq_handle->exec_count--;
dynregmq_handle->sysdep->core_sysdep_mutex_unlock(dynregmq_handle->data_mutex);
}
static void _dynregmq_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata)
{
dynregmq_handle_t *dynregmq_handle = (dynregmq_handle_t *)userdata;
if (dynregmq_handle->recv_handler == NULL) {
return;
}
switch (packet->type) {
case AIOT_MQTTRECV_PUB: {
char *topic = packet->data.pub.topic;
uint32_t topic_len = packet->data.pub.topic_len;
char *payload = (char *)packet->data.pub.payload;
uint32_t payload_len = packet->data.pub.payload_len;
const char *topic_register = "/ext/register";
const char *topic_regnwl = "/ext/regnwl";
int32_t res = STATE_SUCCESS;
if (strlen(topic_register) == topic_len && !memcmp(topic_register, topic, topic_len)) {
const char *key_ds = "deviceSecret";
char *ds = NULL;
uint32_t ds_len = 0;
res = core_json_value(payload, payload_len, key_ds, strlen(key_ds),
&ds, &ds_len);
if (res != STATE_SUCCESS || ds == NULL || ds_len == 0) {
break;
}
dynregmq_handle->flag_completed = 1;
if (dynregmq_handle->recv_handler) {
aiot_dynregmq_recv_t recv_data;
memset(&recv_data, 0, sizeof(aiot_dynregmq_recv_t));
*(ds + ds_len) = 0;
recv_data.type = AIOT_DYNREGMQRECV_DEVICEINFO_WL;
recv_data.data.deviceinfo_wl.device_secret = ds;
dynregmq_handle->recv_handler(dynregmq_handle, &recv_data, dynregmq_handle->userdata);
}
} else if (strlen(topic_regnwl) == topic_len && !memcmp(topic_regnwl, topic, topic_len)) {
const char *key_clientid = "clientId";
const char *key_devicetoken = "deviceToken";
char *client_id = NULL;
char *device_token = NULL;
uint32_t client_id_len = 0;
uint32_t device_token_len = 0;
int32_t res = STATE_SUCCESS;
if ((res = core_json_value(payload, payload_len, key_clientid, strlen(key_clientid), &client_id,
&client_id_len)) != STATE_SUCCESS ||
(res = core_json_value(payload, payload_len, key_devicetoken, strlen(key_devicetoken), &device_token,
&device_token_len)) != STATE_SUCCESS) {
break;
}
if (client_id == NULL || device_token == NULL || client_id_len == 0 || device_token_len == 0) {
break;
}
dynregmq_handle->flag_completed = 1;
if (dynregmq_handle->recv_handler) {
aiot_dynregmq_recv_t recv_data;
char *conn_clientid = NULL;
char *conn_username = NULL;
char *conn_username_fmt[] = { dynregmq_handle->device_name, dynregmq_handle->product_key };
*(client_id + client_id_len) = 0;
*(device_token + device_token_len) = 0;
core_sprintf(dynregmq_handle->sysdep, &conn_clientid,
"%s|authType=connwl,securemode=-2,_ss=1,ext=3,_v="CORE_AUTH_SDK_VERSION"|",
&client_id, 1, DYNREGMQ_MODULE_NAME);
core_sprintf(dynregmq_handle->sysdep, &conn_username, "%s&%s",
conn_username_fmt, sizeof(conn_username_fmt) / sizeof(char *), DYNREGMQ_MODULE_NAME);
memset(&recv_data, 0, sizeof(aiot_dynregmq_recv_t));
recv_data.type = AIOT_DYNREGMQRECV_DEVICEINFO_NWL;
recv_data.data.deviceinfo_nwl.clientid = conn_clientid;
recv_data.data.deviceinfo_nwl.username = conn_username;
recv_data.data.deviceinfo_nwl.password = device_token;
dynregmq_handle->recv_handler(dynregmq_handle, &recv_data, dynregmq_handle->userdata);
dynregmq_handle->sysdep->core_sysdep_free(conn_clientid);
dynregmq_handle->sysdep->core_sysdep_free(conn_username);
}
}
}
break;
default:
break;
}
}
void *aiot_dynregmq_init(void)
{
dynregmq_handle_t *dynregmq_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
sysdep = aiot_sysdep_get_portfile();
if (sysdep == NULL) {
return NULL;
}
dynregmq_handle = sysdep->core_sysdep_malloc(sizeof(dynregmq_handle_t), DYNREGMQ_MODULE_NAME);
if (dynregmq_handle == NULL) {
return NULL;
}
memset(dynregmq_handle, 0, sizeof(dynregmq_handle_t));
dynregmq_handle->sysdep = sysdep;
dynregmq_handle->timeout_ms = DYNREGMQ_DEFAULT_TIMEOUT_MS;
dynregmq_handle->deinit_timeout_ms = DYNREGMQ_DEFAULT_DEINIT_TIMEOUT_MS;
dynregmq_handle->send_timeout_ms = DYNREGMQ_DEFAULT_SEND_TIMEOUT;
dynregmq_handle->recv_timeout_ms = DYNREGMQ_DEFAULT_RECV_TIMEOUT;
dynregmq_handle->data_mutex = dynregmq_handle->sysdep->core_sysdep_mutex_init();
dynregmq_handle->exec_enabled = 1;
return dynregmq_handle;
}
int32_t aiot_dynregmq_setopt(void *handle, aiot_dynregmq_option_t option, void *data)
{
int32_t res = STATE_SUCCESS;
dynregmq_handle_t *dynregmq_handle = (dynregmq_handle_t *)handle;
if (dynregmq_handle == NULL || data == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (option >= AIOT_DYNREGMQOPT_MAX) {
return STATE_USER_INPUT_OUT_RANGE;
}
if (dynregmq_handle->exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
_dynregmq_exec_inc(dynregmq_handle);
dynregmq_handle->sysdep->core_sysdep_mutex_lock(dynregmq_handle->data_mutex);
switch (option) {
case AIOT_DYNREGMQOPT_NETWORK_CRED: {
if (dynregmq_handle->cred != NULL) {
dynregmq_handle->sysdep->core_sysdep_free(dynregmq_handle->cred);
dynregmq_handle->cred = NULL;
}
dynregmq_handle->cred = dynregmq_handle->sysdep->core_sysdep_malloc(sizeof(aiot_sysdep_network_cred_t),
DYNREGMQ_MODULE_NAME);
if (dynregmq_handle->cred != NULL) {
memset(dynregmq_handle->cred, 0, sizeof(aiot_sysdep_network_cred_t));
memcpy(dynregmq_handle->cred, data, sizeof(aiot_sysdep_network_cred_t));
} else {
res = STATE_SYS_DEPEND_MALLOC_FAILED;
}
}
break;
case AIOT_DYNREGMQOPT_HOST: {
res = core_strdup(dynregmq_handle->sysdep, &dynregmq_handle->host, data, DYNREGMQ_MODULE_NAME);
}
break;
case AIOT_DYNREGMQOPT_PORT: {
dynregmq_handle->port = *(uint16_t *)data;
}
break;
case AIOT_DYNREGMQOPT_PRODUCT_KEY: {
res = core_strdup(dynregmq_handle->sysdep, &dynregmq_handle->product_key, data, DYNREGMQ_MODULE_NAME);
}
break;
case AIOT_DYNREGMQOPT_PRODUCT_SECRET: {
res = core_strdup(dynregmq_handle->sysdep, &dynregmq_handle->product_secret, data, DYNREGMQ_MODULE_NAME);
}
break;
case AIOT_DYNREGMQOPT_DEVICE_NAME: {
res = core_strdup(dynregmq_handle->sysdep, &dynregmq_handle->device_name, data, DYNREGMQ_MODULE_NAME);
}
break;
case AIOT_DYNREGMQOPT_SEND_TIMEOUT_MS: {
dynregmq_handle->send_timeout_ms = *(uint32_t *)data;
}
break;
case AIOT_DYNREGMQOPT_RECV_TIMEOUT_MS: {
dynregmq_handle->recv_timeout_ms = *(uint32_t *)data;
}
break;
case AIOT_DYNREGMQOPT_RECV_HANDLER: {
dynregmq_handle->recv_handler = (aiot_dynregmq_recv_handler_t)data;
}
break;
case AIOT_DYNREGMQOPT_USERDATA: {
dynregmq_handle->userdata = data;
}
break;
case AIOT_DYNREGMQOPT_TIMEOUT_MS: {
dynregmq_handle->timeout_ms = *(uint32_t *)data;
}
break;
case AIOT_DYNREGMQOPT_DEINIT_TIMEOUT_MS: {
dynregmq_handle->deinit_timeout_ms = *(uint32_t *)data;
}
break;
case AIOT_DYNREGMQOPT_NO_WHITELIST: {
dynregmq_handle->flag_nowhitelist = *(uint8_t *)data;
}
break;
case AIOT_DYNREGMQOPT_INSTANCE_ID: {
res = core_strdup(dynregmq_handle->sysdep, &dynregmq_handle->instance_id, data, DYNREGMQ_MODULE_NAME);
}
break;
default: {
res = STATE_USER_INPUT_UNKNOWN_OPTION;
}
break;
}
dynregmq_handle->sysdep->core_sysdep_mutex_unlock(dynregmq_handle->data_mutex);
_dynregmq_exec_dec(dynregmq_handle);
return res;
}
int32_t aiot_dynregmq_deinit(void **handle)
{
uint32_t deinit_timeout_ms = 0;
dynregmq_handle_t *dynregmq_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
if (handle == NULL || *handle == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
dynregmq_handle = *(dynregmq_handle_t **)handle;
sysdep = dynregmq_handle->sysdep;
if (dynregmq_handle->exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
dynregmq_handle->exec_enabled = 0;
deinit_timeout_ms = dynregmq_handle->deinit_timeout_ms;
do {
if (dynregmq_handle->exec_count == 0) {
break;
}
dynregmq_handle->sysdep->core_sysdep_sleep(DYNREGMQ_DEINIT_INTERVAL_MS);
} while ((deinit_timeout_ms > DYNREGMQ_DEINIT_INTERVAL_MS) && (deinit_timeout_ms - DYNREGMQ_DEINIT_INTERVAL_MS > 0));
if (dynregmq_handle->exec_count != 0) {
return STATE_DYNREGMQ_DEINIT_TIMEOUT;
}
*handle = NULL;
if (dynregmq_handle->mqtt_handle != NULL) {
aiot_mqtt_deinit(&dynregmq_handle->mqtt_handle);
}
if (dynregmq_handle->host != NULL) {
sysdep->core_sysdep_free(dynregmq_handle->host);
}
if (dynregmq_handle->product_key != NULL) {
sysdep->core_sysdep_free(dynregmq_handle->product_key);
}
if (dynregmq_handle->product_secret != NULL) {
sysdep->core_sysdep_free(dynregmq_handle->product_secret);
}
if (dynregmq_handle->device_name != NULL) {
sysdep->core_sysdep_free(dynregmq_handle->device_name);
}
if (dynregmq_handle->cred != NULL) {
sysdep->core_sysdep_free(dynregmq_handle->cred);
}
if (dynregmq_handle->instance_id != NULL) {
sysdep->core_sysdep_free(dynregmq_handle->instance_id);
}
sysdep->core_sysdep_mutex_deinit(&dynregmq_handle->data_mutex);
sysdep->core_sysdep_free(dynregmq_handle);
return STATE_SUCCESS;
}
static void _core_append_instance_id(dynregmq_handle_t *dynregmq_handle)
{
const char *INSTANCE_PATTERN = ".mqtt.iothub.aliyuncs.com";
char *host = NULL;
char *ptr = NULL;
int total_len = 0;
int instance_id_len = 0;
void *content = NULL;
if (NULL == dynregmq_handle || NULL == dynregmq_handle->host) {
return;
}
host = dynregmq_handle->host;
if (NULL == host) {
return;
}
ptr = strstr(host, INSTANCE_PATTERN);
if (NULL == ptr) {
return;
}
total_len = ptr - host + 1;
instance_id_len = ptr - host;
if (instance_id_len <= 0) {
return;
}
content = dynregmq_handle->sysdep->core_sysdep_malloc(total_len, DYNREGMQ_MODULE_NAME);
if (content == NULL) {
return;
}
memset(content, 0, total_len);
memcpy(content, host, instance_id_len);
if (NULL == dynregmq_handle->instance_id) {
core_strdup(dynregmq_handle->sysdep, &dynregmq_handle->instance_id, content, DYNREGMQ_MODULE_NAME);
}
dynregmq_handle->sysdep->core_sysdep_free(content);
}
int32_t aiot_dynregmq_send_request(void *handle)
{
int32_t res = STATE_SUCCESS;
dynregmq_handle_t *dynregmq_handle = (dynregmq_handle_t *)handle;
char *auth_clientid = NULL;
char *auth_username = NULL;
char auth_password[65] = {0};
char *sign_input = NULL;
uint32_t random_num = 0;
char random[11] = {0};
char *auth_type = NULL;
uint8_t reconnect = 0;
if (dynregmq_handle == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (dynregmq_handle->host == NULL) {
return STATE_USER_INPUT_MISSING_HOST;
}
if (dynregmq_handle->product_key == NULL) {
return STATE_USER_INPUT_MISSING_PRODUCT_KEY;
}
if (dynregmq_handle->product_secret == NULL) {
return STATE_USER_INPUT_MISSING_PRODUCT_SECRET;
}
if (dynregmq_handle->device_name == NULL) {
return STATE_USER_INPUT_MISSING_DEVICE_NAME;
}
if (dynregmq_handle->exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
_dynregmq_exec_inc(dynregmq_handle);
dynregmq_handle->sysdep->core_sysdep_mutex_lock(dynregmq_handle->data_mutex);
if (dynregmq_handle->mqtt_handle != NULL) {
aiot_mqtt_deinit(&dynregmq_handle->mqtt_handle);
}
dynregmq_handle->mqtt_handle = aiot_mqtt_init();
if (dynregmq_handle->mqtt_handle == NULL) {
dynregmq_handle->sysdep->core_sysdep_mutex_unlock(dynregmq_handle->data_mutex);
_dynregmq_exec_dec(dynregmq_handle);
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
/* setup auth_type */
auth_type = (dynregmq_handle->flag_nowhitelist) ? "regnwl" : "register";
/* generate random string */
dynregmq_handle->sysdep->core_sysdep_rand((uint8_t *)&random_num, 4);
core_uint2str(random_num, random, NULL);
/* assamble clientid, username and password */
{
/* generate client_id automatically */
_core_append_instance_id(dynregmq_handle);
uint8_t has_instance_id = (dynregmq_handle->instance_id != NULL) ? 1 : 0;
char *client_fmt = (has_instance_id) ?
"%s.%s|random=%s,authType=%s,securemode=2,signmethod=hmacsha256,instanceId=%s|" :
"%s.%s|random=%s,authType=%s,securemode=2,signmethod=hmacsha256|";
char *client_src[] = { dynregmq_handle->device_name, dynregmq_handle->product_key,
random, auth_type, dynregmq_handle->instance_id
};
char *username_fmt = "%s&%s";
char *username_src[] = { dynregmq_handle->device_name, dynregmq_handle->product_key };
char *sign_input_fmt = "deviceName%sproductKey%srandom%s";
uint8_t sign_output[32] = {0};
core_sprintf(dynregmq_handle->sysdep, &auth_clientid, client_fmt, client_src,
has_instance_id ? 5 : 4, DYNREGMQ_MODULE_NAME);
core_sprintf(dynregmq_handle->sysdep, &auth_username, username_fmt, username_src,
sizeof(username_src) / sizeof(char *), DYNREGMQ_MODULE_NAME);
core_sprintf(dynregmq_handle->sysdep, &sign_input, sign_input_fmt, client_src,
3, DYNREGMQ_MODULE_NAME);
core_hmac_sha256((const uint8_t *)sign_input, (uint32_t)strlen(sign_input),
(const uint8_t *)dynregmq_handle->product_secret,
(uint32_t)strlen(dynregmq_handle->product_secret), sign_output);
core_hex2str(sign_output, sizeof(sign_output), auth_password, 0);
}
if (((res = aiot_mqtt_setopt(dynregmq_handle->mqtt_handle, AIOT_MQTTOPT_HOST,
(void *)dynregmq_handle->host)) < STATE_SUCCESS) ||
((res = aiot_mqtt_setopt(dynregmq_handle->mqtt_handle, AIOT_MQTTOPT_PORT,
(void *)&dynregmq_handle->port)) < STATE_SUCCESS) ||
((res = aiot_mqtt_setopt(dynregmq_handle->mqtt_handle, AIOT_MQTTOPT_CLIENTID,
(void *)auth_clientid)) < STATE_SUCCESS) ||
((res = aiot_mqtt_setopt(dynregmq_handle->mqtt_handle, AIOT_MQTTOPT_USERNAME,
(void *)auth_username)) < STATE_SUCCESS) ||
((res = aiot_mqtt_setopt(dynregmq_handle->mqtt_handle, AIOT_MQTTOPT_PASSWORD,
(void *)auth_password)) < STATE_SUCCESS) ||
((res = aiot_mqtt_setopt(dynregmq_handle->mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED,
(void *)dynregmq_handle->cred)) < STATE_SUCCESS) ||
((res = aiot_mqtt_setopt(dynregmq_handle->mqtt_handle, AIOT_MQTTOPT_SEND_TIMEOUT_MS,
(void *)&dynregmq_handle->send_timeout_ms)) < STATE_SUCCESS) ||
((res = aiot_mqtt_setopt(dynregmq_handle->mqtt_handle, AIOT_MQTTOPT_RECV_TIMEOUT_MS,
(void *)&dynregmq_handle->recv_timeout_ms)) < STATE_SUCCESS) ||
((res = aiot_mqtt_setopt(dynregmq_handle->mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER,
(void *)_dynregmq_recv_handler)) < STATE_SUCCESS) ||
((res = aiot_mqtt_setopt(dynregmq_handle->mqtt_handle, AIOT_MQTTOPT_USERDATA,
(void *)dynregmq_handle)) < STATE_SUCCESS) ||
((res = aiot_mqtt_setopt(dynregmq_handle->mqtt_handle, AIOT_MQTTOPT_RECONN_ENABLED,
(void *)&reconnect)) < STATE_SUCCESS)) {
aiot_mqtt_deinit(&dynregmq_handle->mqtt_handle);
dynregmq_handle->sysdep->core_sysdep_mutex_unlock(dynregmq_handle->data_mutex);
_dynregmq_exec_dec(dynregmq_handle);
dynregmq_handle->sysdep->core_sysdep_free(auth_clientid);
dynregmq_handle->sysdep->core_sysdep_free(auth_username);
dynregmq_handle->sysdep->core_sysdep_free(sign_input);
return res;
}
res = aiot_mqtt_connect(dynregmq_handle->mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&dynregmq_handle->mqtt_handle);
}
dynregmq_handle->sysdep->core_sysdep_mutex_unlock(dynregmq_handle->data_mutex);
_dynregmq_exec_dec(dynregmq_handle);
dynregmq_handle->sysdep->core_sysdep_free(auth_clientid);
dynregmq_handle->sysdep->core_sysdep_free(auth_username);
dynregmq_handle->sysdep->core_sysdep_free(sign_input);
return res;
}
int32_t aiot_dynregmq_recv(void *handle)
{
int32_t res = STATE_SUCCESS;
uint64_t timenow_ms = 0;
dynregmq_handle_t *dynregmq_handle = (dynregmq_handle_t *)handle;
if (dynregmq_handle == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (dynregmq_handle->mqtt_handle == NULL) {
return STATE_DYNREGMQ_NEED_SEND_REQUEST;
}
if (dynregmq_handle->exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
_dynregmq_exec_inc(dynregmq_handle);
dynregmq_handle->sysdep->core_sysdep_mutex_lock(dynregmq_handle->data_mutex);
timenow_ms = dynregmq_handle->sysdep->core_sysdep_time();
while (1) {
if (timenow_ms >= dynregmq_handle->sysdep->core_sysdep_time()) {
timenow_ms = dynregmq_handle->sysdep->core_sysdep_time();
}
if (dynregmq_handle->sysdep->core_sysdep_time() - timenow_ms >= dynregmq_handle->timeout_ms) {
res = STATE_DYNREGMQ_AUTH_TIMEOUT;
break;
}
res = aiot_mqtt_recv(dynregmq_handle->mqtt_handle);
if (res < 0) {
break;
}
if (dynregmq_handle->flag_completed == 1) {
dynregmq_handle->flag_completed = 0;
res = STATE_SUCCESS;
break;
}
}
dynregmq_handle->sysdep->core_sysdep_mutex_unlock(dynregmq_handle->data_mutex);
_dynregmq_exec_dec(dynregmq_handle);
return res;
}

View File

@@ -0,0 +1,336 @@
/**
* @file aiot_dynregmq_api.h
* @brief dynregmq模块头文件, 提供了基于MQTT的设备信息动态注册能力
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#ifndef __AIOT_DYNREGMQ_API_H__
#define __AIOT_DYNREGMQ_API_H__
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdint.h>
/**
* @brief -0x0500~-0x05FF表达SDK在dynregmq模块内的状态码
*/
#define STATE_DYNREGMQ_BASE (-0x0500)
/**
* @brief 执行@ref aiot_dynregmq_deinit 时, 等待其他API执行结束的超过设定的超时时间, DYNREGMQ实例销毁失败
*/
#define STATE_DYNREGMQ_DEINIT_TIMEOUT (-0x0501)
/**
* @brief 需要首先执行@ref aiot_dynregmq_send_request 发送动态注册请求
*/
#define STATE_DYNREGMQ_NEED_SEND_REQUEST (-0x0502)
/**
* @brief 接收服务器应答超时
*/
#define STATE_DYNREGMQ_AUTH_TIMEOUT (-0x0503)
/**
* @brief @ref aiot_dynregmq_setopt 接口的option参数可选值.
*
* @details 下文每个选项中的数据类型, 指的是@ref aiot_dynregmq_setopt 中, data参数的数据类型
*
* 1. data的数据类型是char *时, 以配置@ref AIOT_DYNREGMQOPT_HOST 为例:
*
* char *host = "xxx";
* aiot_dynregmq_setopt(dynregmq_handle, AIOT_DYNREGMQOPT_HOST, host);
*
* 2. data的数据类型是其他数据类型时, 以配置@ref AIOT_DYNREGMQOPT_PORT 为例:
*
* uint16_t port = 443;
* aiot_mqtt_setopt(dynregmq_handle, AIOT_DYNREGMQOPT_PORT, (void *)&port);
*/
typedef enum {
/**
* @brief mqtt动态注册 服务器建联时, 网络使用的安全凭据, 动态注册必需使用TLS方式建连
*
* @details
*
* 该配置项用于为底层网络配置@ref aiot_sysdep_network_cred_t 安全凭据数据
*
* 应当把 @ref aiot_sysdep_network_cred_t 中option配置为@ref AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA , 以tls方式建联
*
* 数据类型: (aiot_sysdep_network_cred_t *)
*/
AIOT_DYNREGMQOPT_NETWORK_CRED,
/**
* @brief mqtt动态注册 服务器的域名地址或者ip地址
*
* @details
*
* 阿里云物联网平台域名地址列表(必须使用自己的product key替换${pk}):
*
* 使用tls证书方式建联:
*
* | 域名地址 | 区域 | 端口号
* |-------------------------------------------------|---------|---------
* | ${pk}.iot-as-mqtt.cn-shanghai.aliyuncs.com | 上海 | 443
* | ${pk}.iot-as-mqtt.ap-southeast-1.aliyuncs.com | 新加坡 | 443
* | ${pk}.iot-as-mqtt.ap-northeast-1.aliyuncs.com | 日本 | 443
* | ${pk}.iot-as-mqtt.us-west-1.aliyuncs.com | 美西 | 443
* | ${pk}.iot-as-mqtt.eu-central-1.aliyuncs.com | 德国 | 443
*
* 数据类型: (char *)
*/
AIOT_DYNREGMQOPT_HOST,
/**
* @brief mqtt动态注册 服务器的端口号
*
* @details
*
* 连接阿里云物联网平台 mqtt动态注册 服务器时:
*
* 必须使用tls方式建联, 端口号设置为443
*
* 数据类型: (uint16_t *)
*/
AIOT_DYNREGMQOPT_PORT,
/**
* @brief 设备的productKey, 可从<a href="http://iot.console.aliyun.com/">阿里云物联网平台控制台</a>获取
*
* @details
*
* 数据类型: (char *)
*/
AIOT_DYNREGMQOPT_PRODUCT_KEY,
/**
* @brief 设备的productSecret, 可从<a href="http://iot.console.aliyun.com/">阿里云物联网平台控制台</a>获取
*
* @details
*
* 数据类型: (char *)
*/
AIOT_DYNREGMQOPT_PRODUCT_SECRET,
/**
* @brief 设备的deviceName, 可从<a href="http://iot.console.aliyun.com/">阿里云物联网平台控制台</a>获取
*
* @details
*
* 数据类型: (char *)
*/
AIOT_DYNREGMQOPT_DEVICE_NAME,
/**
* @brief dynregmq会话发送消息时可消费的最长时间间隔
*
* @details
*
* 数据类型: (uint32_t) 默认值: (5 * 1000) ms
*/
AIOT_DYNREGMQOPT_SEND_TIMEOUT_MS,
/**
* @brief dynregmq会话接收消息时可消费的最长时间间隔
*
* @details
*
* 数据类型: (uint32_t) 默认值: (5 * 1000) ms
*/
AIOT_DYNREGMQOPT_RECV_TIMEOUT_MS,
/**
* @brief 设置回调, 它在SDK收到网络报文的时候被调用, 告知用户
*
* @details
*
* 数据类型: (aiot_dynregmq_http_recv_handler_t)
*/
AIOT_DYNREGMQOPT_RECV_HANDLER,
/**
* @brief 用户需要SDK暂存的上下文
*
* @details
*
* 这个上下文指针会在 AIOT_DYNREGMQOPT_RECV_HANDLER 和 AIOT_DYNREGMQOPT_EVENT_HANDLER 设置的回调被调用时, 由SDK传给用户
*
* 数据类型: (void *)
*/
AIOT_DYNREGMQOPT_USERDATA,
/**
* @brief dynregmq模块接收消息的超时时间
*
* @details
*
* 数据类型: (uint32_t) 默认值: (5 * 1000) ms
*/
AIOT_DYNREGMQOPT_TIMEOUT_MS,
/**
* @brief 销毁dynregmq实例时, 等待其他api执行完毕的时间
*
* @details
*
* 当调用@ref aiot_dynregmq_deinit 销毁MQTT实例时, 若继续调用其他aiot_dynregmq_xxx API, API会返回@ref STATE_USER_INPUT_EXEC_DISABLED 错误
*
* 此时, 用户应该停止调用其他aiot_dynregmq_xxx API
*
* 数据类型: (uint32_t *) 默认值: (2 * 1000) ms
*/
AIOT_DYNREGMQOPT_DEINIT_TIMEOUT_MS,
/**
* @brief 是否使用免白名单功能
*
* @details
*
* 1. 配置为 0 则为白名单模式, 使用这种模式用户必须提前在控制台录入deviceName, 动态注册完成后服务会返回deviceSecret, 用户可通过
* AIOT_DYNREGMQRECV_DEVICEINFO_WL类型数据回调获取到deviceSecret.
* 2. 配置为 1 则为免白名单模式, 使用这种模式用户无需提前在控制台录入deviceName, 动态注册完成后服务会返回MQTT建连信息, 用户可通过
* AIOT_DYNREGMQRECV_DEVICEINFO_NWL类型数据回调获取到clientid, username, password. 用户需要将这三个参数通过
* aiot_mqtt_setopt接口以AIOT_MQTTOPT_CLIENTID, AIOT_MQTTOPT_USERNAME, AIOT_MQTTOPT_PASSWORD配置选项
* 配置到MQTT句柄中。
*
* 数据类型: (uint8_t *) 默认值: (0)
*
*/
AIOT_DYNREGMQOPT_NO_WHITELIST,
/**
* @brief 用户购买的物联网平台实例ID. 当用户使用自购实例, 且使用免白名单方式时, 必须设置实例ID
*
* @details
*
* 数据类型: (char *)
*
*/
AIOT_DYNREGMQOPT_INSTANCE_ID,
AIOT_DYNREGMQOPT_MAX
} aiot_dynregmq_option_t;
/**
* @brief dynregmq模块收到从网络上来的报文时, 通知用户的报文类型
*/
typedef enum {
/**
* @brief 白名单模式下服务器返回的设备信息
*/
AIOT_DYNREGMQRECV_DEVICEINFO_WL,
/**
* @brief 免白名单模式下服务器返回的设备信息
*/
AIOT_DYNREGMQRECV_DEVICEINFO_NWL,
} aiot_dynregmq_recv_type_t;
/**
* @brief dynregmq模块收到从网络上来的报文时, 通知用户的报文内容
*/
typedef struct {
/**
* @brief 报文内容所对应的报文类型, 更多信息请参考@ref aiot_dynregmq_recv_type_t
*/
aiot_dynregmq_recv_type_t type;
union {
/**
* @brief 白名单模式下服务器返回的设备信息
*/
struct {
char *device_secret;
} deviceinfo_wl;
/**
* @brief 免白名单模式下服务器返回的设备信息
*/
struct {
char *clientid;
char *username;
char *password;
} deviceinfo_nwl;
} data;
} aiot_dynregmq_recv_t;
/**
* @brief dynregmq模块收到从网络上来的报文时, 通知用户所调用的数据回调函数
*
* @param[in] handle dynregmq会话句柄
* @param[in] packet dynregmq消息结构体, 存放收到的dynregmq报文内容
* @param[in] userdata 用户上下文
*
* @return void
*/
typedef void (* aiot_dynregmq_recv_handler_t)(void *handle,
const aiot_dynregmq_recv_t *packet, void *userdata);
/**
* @brief 创建dynregmq会话实例, 并以默认值配置会话参数
*
* @return void *
* @retval 非NULL dynregmq实例的句柄
* @retval NULL 初始化失败, 一般是内存分配失败导致
*
*/
void *aiot_dynregmq_init(void);
/**
* @brief 配置dynregmq会话
*
* @param[in] handle dynregmq会话句柄
* @param[in] option 配置选项, 更多信息请参考@ref aiot_dynregmq_option_t
* @param[in] data 配置选项数据, 更多信息请参考@ref aiot_dynregmq_option_t
*
* @return int32_t
* @retval <STATE_SUCCESS 参数配置失败
* @retval >=STATE_SUCCESS 参数配置成功
*
*/
int32_t aiot_dynregmq_setopt(void *handle, aiot_dynregmq_option_t option, void *data);
/**
* @brief 结束dynregmq会话, 销毁实例并回收资源
*
* @param[in] handle 指向dynregmq会话句柄的指针
*
* @return int32_t
* @retval <STATE_SUCCESS 执行失败
* @retval >=STATE_SUCCESS 执行成功
*
*/
int32_t aiot_dynregmq_deinit(void **handle);
/**
* @brief 向dynregmq服务器发送dynregmq消息请求
*
* @param handle dynregmq会话句柄
*
* @return int32_t
* @retval <STATE_SUCCESS 请求发送失败
* @retval >=STATE_SUCCESS 请求发送成功
*/
int32_t aiot_dynregmq_send_request(void *handle);
/**
* @brief 从网络上收取dynregmq消息
*
* @param handle dynregmq会话句柄
*
* @return int32_t
* @retval <STATE_SUCCESS 数据接收失败
* @retval >=STATE_SUCCESS 数据接收成功
*/
int32_t aiot_dynregmq_recv(void *handle);
#if defined(__cplusplus)
}
#endif
#endif /* __AIOT_DYNREGMQMQ_API_H__ */

View File

@@ -0,0 +1,78 @@
/**
* @file dynregmq_private.h
* @brief dynregmq模块内部的宏定义和数据结构声明, 不面向其它模块, 更不面向用户
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#ifndef __DYNREGMQ_PRIVATE_H__
#define __DYNREGMQ_PRIVATE_H__
#if defined(__cplusplus)
extern "C" {
#endif
/* 用这种方式包含标准C库的头文件 */
#include "core_stdinc.h"
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
#include "aiot_dynregmq_api.h" /* 内部头文件是用户可见头文件的超集 */
typedef struct {
uint32_t code;
uint8_t *content;
uint32_t content_len;
uint32_t content_total_len;
} core_mqtt_response_t;
/* 定义dynregmq模块内部的会话句柄结构体, SDK用户不可见, 只能得到void *handle类型的指针 */
typedef struct {
aiot_sysdep_portfile_t *sysdep; /* 底层依赖回调合集的引用指针 */
aiot_sysdep_network_cred_t *cred; /* 指向当前连接使用的安全凭据 */
char *host; /* 会话目标服务器域名 */
uint16_t port; /* 会话目标服务器端口 */
char *product_key;
char *product_secret;
char *device_name;
uint8_t flag_nowhitelist; /* 是否使用免白名单功能 */
char
*instance_id; /* 实例ID当用户使用自购实例且使用免白名单方式时需设置实例ID */
aiot_dynregmq_recv_handler_t recv_handler; /* 组件从协议栈读到内容时, 通知用户的回调 */
void *userdata; /* 组件调用以上2个 dynregmq_handler 时的入参之一 */
uint32_t recv_timeout_ms; /* 从协议栈收包时最长等待时间 */
uint32_t send_timeout_ms; /* 向协议栈写入时最长花费时间 */
uint32_t timeout_ms;
uint32_t deinit_timeout_ms;
/*---- 以上都是用户在API可配 ----*/
/*---- 以下都是DYNREGMQ在内部使用, 用户无感知 ----*/
void *mqtt_handle;
uint8_t flag_completed;
uint8_t exec_enabled;
uint32_t exec_count;
void *data_mutex; /* 保护本地的数据结构 */
} dynregmq_handle_t;
#define DYNREGMQ_MODULE_NAME "dynregmq" /* 用于内存统计的模块名字符串 */
#define DYNREGMQ_DEFAULT_TIMEOUT_MS (5 * 1000)
#define DYNREGMQ_DEFAULT_DEINIT_TIMEOUT_MS (2 * 1000)
#define DYNREGMQ_DEFAULT_RECV_TIMEOUT (5 * 1000)
#define DYNREGMQ_DEFAULT_SEND_TIMEOUT (5 * 1000)
#define DYNREGMQ_DEINIT_INTERVAL_MS (100)
#if defined(__cplusplus)
}
#endif
#endif /* __DYNREGMQ_PRIVATE_H__ */

View File

@@ -0,0 +1,3 @@
Name: 动态注册
DYNREG Component for Link SDK V4.0.0

View File

@@ -0,0 +1,522 @@
/**
* @file aiot_dynreg_api.c
* @brief dynreg模块的API接口实现, 提供获取设备信息的能力
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#include "dynreg_private.h"
#include "core_string.h"
#include "core_log.h"
#include "core_auth.h"
static void _dynreg_exec_inc(dynreg_handle_t *dynreg_handle)
{
dynreg_handle->sysdep->core_sysdep_mutex_lock(dynreg_handle->data_mutex);
dynreg_handle->exec_count++;
dynreg_handle->sysdep->core_sysdep_mutex_unlock(dynreg_handle->data_mutex);
}
static void _dynreg_exec_dec(dynreg_handle_t *dynreg_handle)
{
dynreg_handle->sysdep->core_sysdep_mutex_lock(dynreg_handle->data_mutex);
dynreg_handle->exec_count--;
dynreg_handle->sysdep->core_sysdep_mutex_unlock(dynreg_handle->data_mutex);
}
static int32_t _dynreg_sign(dynreg_handle_t *dynreg_handle, char *random, char sign_str[65])
{
int32_t res = STATE_SUCCESS;
uint8_t sign_hex[32] = {0};
char *src_fmt = "deviceName%sproductKey%srandom%s";
char *src[] = {dynreg_handle->device_name, dynreg_handle->product_key, random};
char *plain_text = NULL;
res = core_sprintf(dynreg_handle->sysdep, &plain_text, src_fmt, src, sizeof(src) / sizeof(char *), DYNREG_MODULE_NAME);
if (res < STATE_SUCCESS) {
return res;
}
core_hmac_sha256((const uint8_t *)plain_text, (uint32_t)strlen(plain_text),
(const uint8_t *)dynreg_handle->product_secret, (uint32_t)strlen(dynreg_handle->product_secret), sign_hex);
core_hex2str(sign_hex, 32, sign_str, 0);
dynreg_handle->sysdep->core_sysdep_free(plain_text);
return STATE_SUCCESS;
}
static void _dynreg_recv_handler(void *handle, const aiot_http_recv_t *packet, void *userdata)
{
dynreg_handle_t *dynreg_handle = (dynreg_handle_t *)userdata;
if (dynreg_handle->recv_handler == NULL) {
return;
}
switch (packet->type) {
case AIOT_HTTPRECV_STATUS_CODE: {
dynreg_handle->response.code = packet->data.status_code.code;
}
break;
case AIOT_HTTPRECV_HEADER: {
if ((strlen(packet->data.header.key) == strlen("Content-Length")) &&
(memcmp(packet->data.header.key, "Content-Length", strlen(packet->data.header.key)) == 0)) {
core_str2uint(packet->data.header.value, (uint8_t)strlen(packet->data.header.value),
&dynreg_handle->response.content_total_len);
}
}
break;
case AIOT_HTTPRECV_BODY: {
uint8_t *content = dynreg_handle->sysdep->core_sysdep_malloc(dynreg_handle->response.content_len + packet->data.body.len
+ 1,
CORE_HTTP_MODULE_NAME);
if (content == NULL) {
return;
}
memset(content, 0, dynreg_handle->response.content_len + packet->data.body.len + 1);
if (content != NULL) {
memcpy(content, dynreg_handle->response.content, dynreg_handle->response.content_len);
dynreg_handle->sysdep->core_sysdep_free(dynreg_handle->response.content);
}
memcpy(content + dynreg_handle->response.content_len, packet->data.body.buffer, packet->data.body.len);
dynreg_handle->response.content = content;
dynreg_handle->response.content_len = dynreg_handle->response.content_len + packet->data.body.len;
}
break;
default: {
}
break;
}
}
static int32_t _dynreg_device_info(dynreg_handle_t *dynreg_handle, char **device_secret)
{
int32_t res = STATE_SUCCESS;
char *tmp_ds = NULL, *ds_key = "deviceSecret";
char *ds_value = NULL;
uint32_t ds_value_len = 0;
if (dynreg_handle->response.code != 200) {
return STATE_DYNREG_INVALID_STATUS_CODE;
}
if ((res = core_json_value((char *)dynreg_handle->response.content, dynreg_handle->response.content_len, ds_key,
strlen(ds_key), &ds_value, &ds_value_len)) < STATE_SUCCESS) {
return STATE_DYNREG_INVALID_DEVICE_SECRET;
}
tmp_ds = dynreg_handle->sysdep->core_sysdep_malloc(ds_value_len + 1, DYNREG_MODULE_NAME);
if (tmp_ds == NULL) {
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
memset(tmp_ds, 0, ds_value_len + 1);
memcpy(tmp_ds, ds_value, ds_value_len);
*device_secret = tmp_ds;
return STATE_SUCCESS;
}
void *aiot_dynreg_init(void)
{
dynreg_handle_t *dynreg_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
sysdep = aiot_sysdep_get_portfile();
if (sysdep == NULL) {
return NULL;
}
dynreg_handle = sysdep->core_sysdep_malloc(sizeof(dynreg_handle_t), DYNREG_MODULE_NAME);
if (dynreg_handle == NULL) {
return NULL;
}
memset(dynreg_handle, 0, sizeof(dynreg_handle_t));
dynreg_handle->sysdep = sysdep;
dynreg_handle->response_body_len = DYNREG_RESPONSE_BODY_LEN;
dynreg_handle->timeout_ms = DYNREG_DEFAULT_TIMEOUT_MS;
dynreg_handle->deinit_timeout_ms = DYNREG_DEFAULT_DEINIT_TIMEOUT_MS;
dynreg_handle->send_timeout_ms = DYNREG_DEFAULT_SEND_TIMEOUT;
dynreg_handle->recv_timeout_ms = DYNREG_DEFAULT_RECV_TIMEOUT;
dynreg_handle->data_mutex = dynreg_handle->sysdep->core_sysdep_mutex_init();
dynreg_handle->exec_enabled = 1;
return dynreg_handle;
}
int32_t aiot_dynreg_setopt(void *handle, aiot_dynreg_option_t option, void *data)
{
int32_t res = STATE_SUCCESS;
dynreg_handle_t *dynreg_handle = (dynreg_handle_t *)handle;
if (dynreg_handle == NULL || data == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (option >= AIOT_DYNREGOPT_MAX) {
return STATE_USER_INPUT_OUT_RANGE;
}
if (dynreg_handle->exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
_dynreg_exec_inc(dynreg_handle);
dynreg_handle->sysdep->core_sysdep_mutex_lock(dynreg_handle->data_mutex);
switch (option) {
case AIOT_DYNREGOPT_NETWORK_CRED: {
if (dynreg_handle->cred != NULL) {
dynreg_handle->sysdep->core_sysdep_free(dynreg_handle->cred);
dynreg_handle->cred = NULL;
}
dynreg_handle->cred = dynreg_handle->sysdep->core_sysdep_malloc(sizeof(aiot_sysdep_network_cred_t), DYNREG_MODULE_NAME);
if (dynreg_handle->cred != NULL) {
memset(dynreg_handle->cred, 0, sizeof(aiot_sysdep_network_cred_t));
memcpy(dynreg_handle->cred, data, sizeof(aiot_sysdep_network_cred_t));
} else {
res = STATE_SYS_DEPEND_MALLOC_FAILED;
}
}
break;
case AIOT_DYNREGOPT_HOST: {
res = core_strdup(dynreg_handle->sysdep, &dynreg_handle->host, data, DYNREG_MODULE_NAME);
}
break;
case AIOT_DYNREGOPT_PORT: {
dynreg_handle->port = *(uint16_t *)data;
}
break;
case AIOT_DYNREGOPT_PRODUCT_KEY: {
res = core_strdup(dynreg_handle->sysdep, &dynreg_handle->product_key, data, DYNREG_MODULE_NAME);
}
break;
case AIOT_DYNREGOPT_PRODUCT_SECRET: {
res = core_strdup(dynreg_handle->sysdep, &dynreg_handle->product_secret, data, DYNREG_MODULE_NAME);
}
break;
case AIOT_DYNREGOPT_DEVICE_NAME: {
res = core_strdup(dynreg_handle->sysdep, &dynreg_handle->device_name, data, DYNREG_MODULE_NAME);
}
break;
case AIOT_DYNREGOPT_SEND_TIMEOUT_MS: {
dynreg_handle->send_timeout_ms = *(uint32_t *)data;
}
break;
case AIOT_DYNREGOPT_RECV_TIMEOUT_MS: {
dynreg_handle->recv_timeout_ms = *(uint32_t *)data;
}
break;
case AIOT_DYNREGOPT_RECV_HANDLER: {
dynreg_handle->recv_handler = (aiot_dynreg_recv_handler_t)data;
}
break;
case AIOT_DYNREGOPT_USERDATA: {
dynreg_handle->userdata = data;
}
break;
case AIOT_DYNREGOPT_TIMEOUT_MS: {
dynreg_handle->timeout_ms = *(uint32_t *)data;
}
break;
case AIOT_DYNREGOPT_DEINIT_TIMEOUT_MS: {
dynreg_handle->deinit_timeout_ms = *(uint32_t *)data;
}
break;
default: {
res = STATE_USER_INPUT_UNKNOWN_OPTION;
}
break;
}
dynreg_handle->sysdep->core_sysdep_mutex_unlock(dynreg_handle->data_mutex);
_dynreg_exec_dec(dynreg_handle);
return res;
}
int32_t aiot_dynreg_deinit(void **handle)
{
uint64_t deinit_timestart = 0;
dynreg_handle_t *dynreg_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
if (handle == NULL || *handle == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
dynreg_handle = *(dynreg_handle_t **)handle;
sysdep = dynreg_handle->sysdep;
if (dynreg_handle->exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
dynreg_handle->exec_enabled = 0;
deinit_timestart = dynreg_handle->sysdep->core_sysdep_time();
do {
if (dynreg_handle->exec_count == 0) {
break;
}
dynreg_handle->sysdep->core_sysdep_sleep(DYNREG_DEINIT_INTERVAL_MS);
} while ((dynreg_handle->sysdep->core_sysdep_time() - deinit_timestart) < dynreg_handle->deinit_timeout_ms);
if (dynreg_handle->exec_count != 0) {
return STATE_DYNREG_DEINIT_TIMEOUT;
}
*handle = NULL;
if (dynreg_handle->response.content != NULL) {
sysdep->core_sysdep_free(dynreg_handle->response.content);
}
memset(&dynreg_handle->response, 0, sizeof(core_http_response_t));
if (dynreg_handle->http_handle != NULL) {
core_http_deinit(&dynreg_handle->http_handle);
}
if (dynreg_handle->host != NULL) {
sysdep->core_sysdep_free(dynreg_handle->host);
}
if (dynreg_handle->product_key != NULL) {
sysdep->core_sysdep_free(dynreg_handle->product_key);
}
if (dynreg_handle->product_secret != NULL) {
sysdep->core_sysdep_free(dynreg_handle->product_secret);
}
if (dynreg_handle->device_name != NULL) {
sysdep->core_sysdep_free(dynreg_handle->device_name);
}
if (dynreg_handle->cred != NULL) {
sysdep->core_sysdep_free(dynreg_handle->cred);
}
sysdep->core_sysdep_mutex_deinit(&dynreg_handle->data_mutex);
sysdep->core_sysdep_free(dynreg_handle);
return STATE_SUCCESS;
}
int32_t aiot_dynreg_send_request(void *handle)
{
int32_t res = STATE_SUCCESS;
dynreg_handle_t *dynreg_handle = (dynreg_handle_t *)handle;
if (dynreg_handle == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (dynreg_handle->host == NULL) {
return STATE_USER_INPUT_MISSING_HOST;
}
if (dynreg_handle->product_key == NULL) {
return STATE_USER_INPUT_MISSING_PRODUCT_KEY;
}
if (dynreg_handle->product_secret == NULL) {
return STATE_USER_INPUT_MISSING_PRODUCT_SECRET;
}
if (dynreg_handle->device_name == NULL) {
return STATE_USER_INPUT_MISSING_DEVICE_NAME;
}
if (dynreg_handle->exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
_dynreg_exec_inc(dynreg_handle);
dynreg_handle->sysdep->core_sysdep_mutex_lock(dynreg_handle->data_mutex);
if (dynreg_handle->response.content != NULL) {
dynreg_handle->sysdep->core_sysdep_free(dynreg_handle->response.content);
}
memset(&dynreg_handle->response, 0, sizeof(core_http_response_t));
if (dynreg_handle->http_handle != NULL) {
core_http_deinit(&dynreg_handle->http_handle);
}
dynreg_handle->http_handle = core_http_init();
if (dynreg_handle->http_handle == NULL) {
dynreg_handle->sysdep->core_sysdep_mutex_unlock(dynreg_handle->data_mutex);
_dynreg_exec_dec(dynreg_handle);
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
if (((res = core_http_setopt(dynreg_handle->http_handle, CORE_HTTPOPT_HOST,
(void *)dynreg_handle->host)) < STATE_SUCCESS) ||
((res = core_http_setopt(dynreg_handle->http_handle, CORE_HTTPOPT_PORT, (void *)&dynreg_handle->port)) < STATE_SUCCESS)
||
((res = core_http_setopt(dynreg_handle->http_handle, CORE_HTTPOPT_NETWORK_CRED,
(void *)dynreg_handle->cred)) < STATE_SUCCESS) ||
((res = core_http_setopt(dynreg_handle->http_handle, CORE_HTTPOPT_SEND_TIMEOUT_MS,
(void *)&dynreg_handle->send_timeout_ms)) < STATE_SUCCESS) ||
((res = core_http_setopt(dynreg_handle->http_handle, CORE_HTTPOPT_RECV_TIMEOUT_MS,
(void *)&dynreg_handle->recv_timeout_ms)) < STATE_SUCCESS) ||
((res = core_http_setopt(dynreg_handle->http_handle, CORE_HTTPOPT_BODY_BUFFER_MAX_LEN,
(void *)&dynreg_handle->response_body_len)) < STATE_SUCCESS) ||
((res = core_http_setopt(dynreg_handle->http_handle, CORE_HTTPOPT_RECV_HANDLER,
(void *)_dynreg_recv_handler)) < STATE_SUCCESS) ||
((res = core_http_setopt(dynreg_handle->http_handle, CORE_HTTPOPT_USERDATA, (void *)dynreg_handle)) < STATE_SUCCESS)) {
core_http_deinit(&dynreg_handle->http_handle);
dynreg_handle->sysdep->core_sysdep_mutex_unlock(dynreg_handle->data_mutex);
_dynreg_exec_dec(dynreg_handle);
return res;
}
res = core_http_connect(dynreg_handle->http_handle);
if (res < STATE_SUCCESS) {
core_http_deinit(&dynreg_handle->http_handle);
dynreg_handle->sysdep->core_sysdep_mutex_unlock(dynreg_handle->data_mutex);
_dynreg_exec_dec(dynreg_handle);
return res;
}
{
uint32_t random_num = 0;
char random[11] = {0};
char sign_str[65] = {0};
char *content = NULL;
char *content_fmt = "productKey=%s&deviceName=%s&random=%s&sign=%s&signMethod=hmacsha256";
char *content_src[] = { dynreg_handle->product_key, dynreg_handle->device_name, (char *)random, sign_str };
core_http_request_t request;
dynreg_handle->sysdep->core_sysdep_rand((uint8_t *)&random_num, 4);
core_uint2str(random_num, random, NULL);
res = _dynreg_sign(dynreg_handle, (char *)random, sign_str);
if (res < STATE_SUCCESS) {
core_http_deinit(&dynreg_handle->http_handle);
dynreg_handle->sysdep->core_sysdep_mutex_unlock(dynreg_handle->data_mutex);
_dynreg_exec_dec(dynreg_handle);
return res;
}
memset(&request, 0, sizeof(core_http_request_t));
res = core_sprintf(dynreg_handle->sysdep, &content, content_fmt, content_src, sizeof(content_src) / sizeof(char *),
DYNREG_MODULE_NAME);
if (res < STATE_SUCCESS) {
core_http_deinit(&dynreg_handle->http_handle);
dynreg_handle->sysdep->core_sysdep_mutex_unlock(dynreg_handle->data_mutex);
_dynreg_exec_dec(dynreg_handle);
return res;
}
request.method = "POST";
request.path = DYNREG_PATH;
request.header = "Accept: text/xml,text/javascript,text/html,application/json\r\n" \
"Content-Type: application/x-www-form-urlencoded\r\n";
request.content = (uint8_t *)content;
request.content_len = (uint32_t)strlen(content);
res = core_http_send(dynreg_handle->http_handle, &request);
dynreg_handle->sysdep->core_sysdep_free(content);
if (res < STATE_SUCCESS) {
core_http_deinit(&dynreg_handle->http_handle);
dynreg_handle->sysdep->core_sysdep_mutex_unlock(dynreg_handle->data_mutex);
_dynreg_exec_dec(dynreg_handle);
return res;
}
}
dynreg_handle->sysdep->core_sysdep_mutex_unlock(dynreg_handle->data_mutex);
_dynreg_exec_dec(dynreg_handle);
return res;
}
int32_t aiot_dynreg_recv(void *handle)
{
int32_t res = STATE_SUCCESS;
uint64_t timenow_ms = 0;
dynreg_handle_t *dynreg_handle = (dynreg_handle_t *)handle;
char *device_secret = NULL;
aiot_dynreg_recv_t packet;
if (dynreg_handle == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (dynreg_handle->http_handle == NULL) {
return STATE_DYNREG_NEED_SEND_REQUEST;
}
if (dynreg_handle->exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
_dynreg_exec_inc(dynreg_handle);
dynreg_handle->sysdep->core_sysdep_mutex_lock(dynreg_handle->data_mutex);
timenow_ms = dynreg_handle->sysdep->core_sysdep_time();
while (1) {
if (timenow_ms >= dynreg_handle->sysdep->core_sysdep_time()) {
timenow_ms = dynreg_handle->sysdep->core_sysdep_time();
}
if (dynreg_handle->sysdep->core_sysdep_time() - timenow_ms >= dynreg_handle->timeout_ms) {
break;
}
res = core_http_recv(dynreg_handle->http_handle);
if (res < STATE_SUCCESS) {
break;
}
}
if (res < STATE_SUCCESS) {
if (res != STATE_HTTP_READ_BODY_FINISHED) {
if (dynreg_handle->response.content != NULL) {
dynreg_handle->sysdep->core_sysdep_free(dynreg_handle->response.content);
memset(&dynreg_handle->response, 0, sizeof(core_http_response_t));
}
dynreg_handle->sysdep->core_sysdep_mutex_unlock(dynreg_handle->data_mutex);
_dynreg_exec_dec(dynreg_handle);
return res;
} else {
res = STATE_SUCCESS;
}
} else {
dynreg_handle->sysdep->core_sysdep_mutex_unlock(dynreg_handle->data_mutex);
_dynreg_exec_dec(dynreg_handle);
return STATE_HTTP_RECV_NOT_FINISHED;
}
if (dynreg_handle->recv_handler != NULL) {
packet.type = AIOT_DYNREGRECV_STATUS_CODE;
packet.data.status_code.code = dynreg_handle->response.code;
dynreg_handle->recv_handler(dynreg_handle, &packet, dynreg_handle->userdata);
}
res = _dynreg_device_info(dynreg_handle, &device_secret);
dynreg_handle->sysdep->core_sysdep_mutex_unlock(dynreg_handle->data_mutex);
if (res < STATE_SUCCESS) {
_dynreg_exec_dec(dynreg_handle);
return res;
}
memset(&packet, 0, sizeof(aiot_dynreg_recv_t));
if (dynreg_handle->recv_handler != NULL) {
packet.type = AIOT_DYNREGRECV_DEVICE_INFO;
packet.data.device_info.device_secret = device_secret;
dynreg_handle->recv_handler(dynreg_handle, &packet, dynreg_handle->userdata);
}
dynreg_handle->sysdep->core_sysdep_free(device_secret);
_dynreg_exec_dec(dynreg_handle);
return STATE_SUCCESS;
}

View File

@@ -0,0 +1,302 @@
/**
* @file aiot_dynreg_api.h
* @brief dynreg模块头文件, 提供获取设备信息的能力
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#ifndef __AIOT_DYNREG_API_H__
#define __AIOT_DYNREG_API_H__
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdint.h>
/**
* @brief -0x0600~-0x06FF表达SDK在dynreg模块内的状态码
*/
#define STATE_DYNREG_BASE (-0x0600)
/**
* @brief 执行@ref aiot_dynreg_deinit 时, 等待其他API执行结束的超过设定的超时时间, MQTT实例销毁失败
*/
#define STATE_DYNREG_DEINIT_TIMEOUT (-0x0601)
/**
* @brief 需要首先执行@ref aiot_dynreg_send_request 发送dynreg请求
*/
#define STATE_DYNREG_NEED_SEND_REQUEST (-0x0602)
/**
* @brief dynreg模块返回了错误的http status code
*/
#define STATE_DYNREG_INVALID_STATUS_CODE (-0x0603)
/**
* @brief 收到非法的device secret
*/
#define STATE_DYNREG_INVALID_DEVICE_SECRET (-0x0604)
/**
* @brief dynreg模块收到从网络上来的报文时, 通知用户的报文类型
*/
typedef enum {
/**
* @brief dynreg模块返回的http status code
*/
AIOT_DYNREGRECV_STATUS_CODE,
/**
* @brief dynreg模块返回的设备信息
*/
AIOT_DYNREGRECV_DEVICE_INFO,
} aiot_dynreg_recv_type_t;
/**
* @brief dynreg模块收到从网络上来的报文时, 通知用户的报文内容
*/
typedef struct {
/**
* @brief 报文内容所对应的报文类型, 更多信息请参考@ref aiot_dynreg_recv_type_t
*/
aiot_dynreg_recv_type_t type;
union {
/**
* @brief dynreg模块返回的http status code
*/
struct {
uint32_t code;
} status_code;
/**
* @brief dynreg模块返回的设备信息
*/
struct {
char *device_secret;
} device_info;
} data;
} aiot_dynreg_recv_t;
/**
* @brief dynreg模块收到从网络上来的报文时, 通知用户所调用的数据回调函数
*
* @param[in] handle dynreg会话句柄
* @param[in] packet dynreg消息结构体, 存放收到的dynreg报文内容
* @param[in] userdata 用户上下文
*
* @return void
*/
typedef void (* aiot_dynreg_recv_handler_t)(void *handle,
const aiot_dynreg_recv_t *packet, void *userdata);
/**
* @brief @ref aiot_dynreg_setopt 接口的option参数可选值.
*
* @details 下文每个选项中的数据类型, 指的是@ref aiot_dynreg_setopt 中, data参数的数据类型
*
* 1. data的数据类型是char *时, 以配置@ref AIOT_DYNREGOPT_HOST 为例:
*
* char *host = "xxx";
* aiot_dynreg_setopt(dynreg_handle, AIOT_DYNREGOPT_HOST, host);
*
* 2. data的数据类型是其他数据类型时, 以配置@ref AIOT_DYNREGOPT_PORT 为例:
*
* uint16_t port = 443;
* aiot_mqtt_setopt(dynreg_handle, AIOT_DYNREGOPT_PORT, (void *)&port);
*/
typedef enum {
/**
* @brief http动态注册 服务器建联时, 网络使用的安全凭据
*
* @details
*
* 该配置项用于为底层网络配置@ref aiot_sysdep_network_cred_t 安全凭据数据
*
* 应当把 @ref aiot_sysdep_network_cred_t 中option配置为@ref AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA , 以tls方式建联
*
* 数据类型: (aiot_sysdep_network_cred_t *)
*/
AIOT_DYNREGOPT_NETWORK_CRED,
/**
* @brief http动态注册 服务器的域名地址或者ip地址
*
* @details
*
* 阿里云物联网平台 http动态注册 服务器域名地址列表:
*
* | 域名地址 | 区域 | 端口号
* |-------------------------------------------------|---------|---------
* | iot-auth.cn-shanghai.aliyuncs.com | 国内 | 443
* | iot-auth.ap-southeast-1.aliyuncs.com | 海外 | 443
*
* 数据类型: (char *)
*/
AIOT_DYNREGOPT_HOST,
/**
* @brief http动态注册 服务器的端口号
*
* @details
*
* 连接阿里云物联网平台 http动态注册 服务器时:
*
* 必须使用tls方式建联, 端口号设置为443
*
* 数据类型: (uint16_t *)
*/
AIOT_DYNREGOPT_PORT,
/**
* @brief 设备的productKey, 可从<a href="http://iot.console.aliyun.com/">阿里云物联网平台控制台</a>获取
*
* @details
*
* 数据类型: (char *)
*/
AIOT_DYNREGOPT_PRODUCT_KEY,
/**
* @brief 设备的productSecret, 可从<a href="http://iot.console.aliyun.com/">阿里云物联网平台控制台</a>获取
*
* @details
*
* 数据类型: (char *)
*/
AIOT_DYNREGOPT_PRODUCT_SECRET,
/**
* @brief 设备的deviceName, 可从<a href="http://iot.console.aliyun.com/">阿里云物联网平台控制台</a>获取
*
* @details
*
* 数据类型: (char *)
*/
AIOT_DYNREGOPT_DEVICE_NAME,
/**
* @brief dynreg会话发送消息时可消费的最长时间间隔
*
* @details
*
* 数据类型: (uint32_t) 默认值: (5 * 1000) ms
*/
AIOT_DYNREGOPT_SEND_TIMEOUT_MS,
/**
* @brief dynreg会话接收消息时可消费的最长时间间隔
*
* @details
*
* 数据类型: (uint32_t) 默认值: (5 * 1000) ms
*/
AIOT_DYNREGOPT_RECV_TIMEOUT_MS,
/**
* @brief 设置回调, 它在SDK收到网络报文的时候被调用, 告知用户
*
* @details
*
* 数据类型: (aiot_dynreg_http_recv_handler_t)
*/
AIOT_DYNREGOPT_RECV_HANDLER,
/**
* @brief 用户需要SDK暂存的上下文
*
* @details
*
* 这个上下文指针会在 AIOT_DYNREGOPT_RECV_HANDLER 和 AIOT_DYNREGOPT_EVENT_HANDLER 设置的回调被调用时, 由SDK传给用户
*
* 数据类型: (void *)
*/
AIOT_DYNREGOPT_USERDATA,
/**
* @brief dynreg模块接收消息的超时时间
*
* @details
*
* 数据类型: (uint32_t) 默认值: (5 * 1000) ms
*/
AIOT_DYNREGOPT_TIMEOUT_MS,
/**
* @brief 销毁dynreg实例时, 等待其他api执行完毕的时间
*
* @details
*
* 当调用@ref aiot_dynreg_deinit 销毁MQTT实例时, 若继续调用其他aiot_dynreg_xxx API, API会返回@ref STATE_USER_INPUT_EXEC_DISABLED 错误
*
* 此时, 用户应该停止调用其他aiot_dynreg_xxx API
*
* 数据类型: (uint32_t *) 默认值: (2 * 1000) ms
*/
AIOT_DYNREGOPT_DEINIT_TIMEOUT_MS,
AIOT_DYNREGOPT_MAX
} aiot_dynreg_option_t;
/**
* @brief 创建dynreg会话实例, 并以默认值配置会话参数
*
* @return void *
* @retval 非NULL dynreg实例的句柄
* @retval NULL 初始化失败, 一般是内存分配失败导致
*
*/
void *aiot_dynreg_init(void);
/**
* @brief 配置dynreg会话
*
* @param[in] handle dynreg会话句柄
* @param[in] option 配置选项, 更多信息请参考@ref aiot_dynreg_option_t
* @param[in] data 配置选项数据, 更多信息请参考@ref aiot_dynreg_option_t
*
* @return int32_t
* @retval <STATE_SUCCESS 参数配置失败
* @retval >=STATE_SUCCESS 参数配置成功
*
*/
int32_t aiot_dynreg_setopt(void *handle, aiot_dynreg_option_t option, void *data);
/**
* @brief 结束dynreg会话, 销毁实例并回收资源
*
* @param[in] handle 指向dynreg会话句柄的指针
*
* @return int32_t
* @retval <STATE_SUCCESS 执行失败
* @retval >=STATE_SUCCESS 执行成功
*
*/
int32_t aiot_dynreg_deinit(void **handle);
/**
* @brief 向dynreg服务器发送dynreg消息请求
*
* @param handle dynreg会话句柄
*
* @return int32_t
* @retval <STATE_SUCCESS 请求发送失败
* @retval >=STATE_SUCCESS 请求发送成功
*/
int32_t aiot_dynreg_send_request(void *handle);
/**
* @brief 从网络上收取dynreg消息
*
* @param handle dynreg会话句柄
*
* @return int32_t
* @retval <STATE_SUCCESS 数据接收失败
* @retval >=STATE_SUCCESS 数据接收成功
*/
int32_t aiot_dynreg_recv(void *handle);
#if defined(__cplusplus)
}
#endif
#endif /* __AIOT_DYNREG_API_H__ */

View File

@@ -0,0 +1,73 @@
/**
* @file dynreg_private.h
* @brief dynreg模块内部的宏定义和数据结构声明, 不面向其它模块, 更不面向用户
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#ifndef __DYNREG_PRIVATE_H__
#define __DYNREG_PRIVATE_H__
#if defined(__cplusplus)
extern "C" {
#endif
/* 用这种方式包含标准C库的头文件 */
#include "core_stdinc.h"
/* TODO: 这一段列出需要包含SDK其它模块头文件, 与上一段落以1个空行隔开 */
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "core_http.h"
#include "aiot_dynreg_api.h" /* 内部头文件是用户可见头文件的超集 */
/* TODO: 定义dynreg模块内部的会话句柄结构体, SDK用户不可见, 只能得到void *handle类型的指针 */
typedef struct {
aiot_sysdep_portfile_t *sysdep; /* 底层依赖回调合集的引用指针 */
aiot_sysdep_network_cred_t *cred; /* 指向当前连接使用的安全凭据 */
char *host; /* 会话目标服务器域名 */
uint16_t port; /* 会话目标服务器端口 */
char *product_key;
char *product_secret;
char *device_name;
aiot_dynreg_recv_handler_t recv_handler; /* 组件从协议栈读到内容时, 通知用户的回调 */
void *userdata; /* 组件调用以上2个 dynreg_handler 时的入参之一 */
uint32_t recv_timeout_ms; /* 从协议栈收包时最长等待时间 */
uint32_t send_timeout_ms; /* 向协议栈写入时最长花费时间 */
uint32_t timeout_ms;
uint32_t deinit_timeout_ms;
/*---- 以上都是用户在API可配 ----*/
/*---- 以下都是DYNREG在内部使用, 用户无感知 ----*/
void *http_handle;
core_http_response_t response;
uint32_t response_body_len;
uint8_t exec_enabled;
uint32_t exec_count;
void *data_mutex; /* 保护本地的数据结构 */
} dynreg_handle_t;
#define DYNREG_MODULE_NAME "dynreg" /* 用于内存统计的模块名字符串 */
#define DYNREG_DEFAULT_TIMEOUT_MS (5 * 1000)
#define DYNREG_DEFAULT_DEINIT_TIMEOUT_MS (2 * 1000)
#define DYNREG_DEFAULT_RECV_TIMEOUT (5 * 1000)
#define DYNREG_DEFAULT_SEND_TIMEOUT (5 * 1000)
#define DYNREG_PATH "/auth/register/device"
#define DYNREG_DEINIT_INTERVAL_MS (100)
#define DYNREG_RESPONSE_BODY_LEN (192)
#if defined(__cplusplus)
}
#endif
#endif /* __DYNREG_PRIVATE_H__ */

View File

@@ -0,0 +1,65 @@
/**
* @file aiot_fs_api.h
* @brief SDK linkspeech依赖的文件操作所有依赖的文件操作将在此列出
* @date 2019-12-27
*
* @copyright Copyright (C) 2015-2025 Alibaba Group Holding Limited
*
*/
#ifndef _AIOT_FS_API_H_
#define _AIOT_FS_API_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdint.h>
/**
* @brief 用以向SDK描述其运行硬件平台的资源如何使用的方法结构体
*/
typedef struct {
/**
* @brief 查询文件大小
*
* @return int32_t
* @retval -1 文件不存在
* @retval >=0 实际文件大小
*/
int32_t (*file_size)(char *path);
/**
* @brief 删除文件
*
* @return int32_t
* @retval 0 文件删除成功
* @retval -1 文件修改失败
*/
int32_t (*file_delete)(char *path);
/**
* @brief 写文件数据
*
* @details 如果文件不存在,则创建新文件
* @return int32_t
* @retval 成功,返回写入的文件长度
* @retval 失败,返回-1
*/
int32_t (*file_write)(char *path, uint32_t offset, uint8_t *data, uint32_t len);
/**
* @brief 写文件数据
*
* @details open操作需用户执行
* @return int32_t
* @retval 成功,返回读取到的文件长度
* @retval 失败,返回-1
*/
int32_t (*file_read)(char *path, uint32_t offset, uint8_t *data, uint32_t len);
} aiot_fs_t;
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,948 @@
#include "aiot_linkspeech_api.h"
#include "aiot_fs_api.h"
#include "aiot_dm_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_state_api.h"
#include <stdio.h>
#include <cJSON.h>
#include <string.h>
#include "core_string.h"
#include "http_download.h"
#include "core_list.h"
#include "speech_trans.h"
#include "core_log.h"
#define TAG "LINKSPEECH"
/* 文件系统抽象接口 */
static aiot_fs_t *fs = NULL;
/* 操作系统抽象接口*/
static aiot_sysdep_portfile_t *sysdep = NULL;
#define AIOT_LSOPT_SPEECH_BUFFER_SIZE_DEFAULT 10
#define AIOT_SPEECH_DUPLICATE_SIZE_DEFAULT 15
typedef struct {
char *jobcode;
char *json_url;
struct core_list_head linked_node;
} core_download_node_t;
typedef struct {
uint64_t msg_id;
char *rrpc_id;
char *id;
char *format;
char *speechs;
struct core_list_head play_list;
struct core_list_head linked_node;
} core_speech_task_t;
typedef struct {
/* 物模型对象*/
void *dm_handle;
/* 音频播放接口 */
player_cb_t player;
/* 用户设置工作目录 */
char *work_dir;
int32_t https_enable;
int32_t speech_buffer_size;
/* 以上内容为用户配置,以下为运行时状态 */
/* 下载任务管理 */
void *download_mutex;
struct core_list_head download_task_list;
/* 播报任务管理 */
void *speech_task_mutex;
int32_t speech_task_num;
struct core_list_head speech_task_list;
/* 用于判断是否需要退出千里传音 */
int8_t running;
/* 正在播报的任务 */
core_speech_task_t *playing_task;
/* 播放音频回调参数 */
play_param_t play_param;
/* 防止重复播放 */
char *dup_id[AIOT_SPEECH_DUPLICATE_SIZE_DEFAULT];
int8_t write_pos;
} linkspeech_handle_t;
typedef struct {
linkspeech_handle_t *linkspeech;
char *file_name;
} download_userdata_t;
static int32_t file_write(uint32_t offset, uint8_t *data, uint32_t data_len, void *userdata) {
char *file_name = (char *)userdata;
if(userdata == NULL || data == NULL) {
return -1;
}
core_log3(sysdep, 0, "file %s, offset %d, len %d\r\n", file_name, &offset, &data_len);
return fs->file_write(file_name, offset, data, data_len);
}
/* 下载文件到本地文件系统 */
static int32_t core_download_file(const char *remote_url, const char *local_url, int32_t tls) {
http_download_params_t params;
core_log2(sysdep, 0, "[http] remote %s, local %s\r\n", (char *)remote_url, (char *)local_url);
if(tls == 0) {
return core_http_download_request((char *)remote_url, NULL, file_write, (void *)local_url);
} else {
memset(&params, 0, sizeof(http_download_params_t));
params.https_enable = 1;
return core_http_download_request((char *)remote_url, &params, file_write, (void *)local_url);
}
}
/* 事件上报函数演示 */
static int32_t core_send_event_post(void *dm_handle, char *event_id, char *params)
{
aiot_dm_msg_t msg;
memset(&msg, 0, sizeof(aiot_dm_msg_t));
msg.type = AIOT_DMMSG_EVENT_POST;
msg.data.event_post.event_id = event_id;
msg.data.event_post.params = params;
return aiot_dm_send(dm_handle, &msg);
}
static int32_t core_delete_speech_task(core_speech_task_t *speech_task)
{
play_node_t *node = NULL, *next = NULL;
if(speech_task == NULL) {
return -1;
}
if(speech_task->rrpc_id != NULL) {
sysdep->core_sysdep_free(speech_task->rrpc_id);
}
if(speech_task->id != NULL) {
sysdep->core_sysdep_free(speech_task->id);
}
if(speech_task->format != NULL) {
sysdep->core_sysdep_free(speech_task->format);
}
if(speech_task->speechs != NULL) {
sysdep->core_sysdep_free(speech_task->speechs);
}
core_list_for_each_entry_safe(node, next, &speech_task->play_list, linked_node, play_node_t) {
core_list_del(&node->linked_node);
if(node->filename != NULL) {
sysdep->core_sysdep_free(node->filename);
}
sysdep->core_sysdep_free(node);
}
sysdep->core_sysdep_free(speech_task);
return STATE_SUCCESS;
}
/* 将语料信息转换为语料文件路径 */
static int32_t core_speech_task_prepare(core_speech_task_t *speech_task)
{
cJSON *speechs = cJSON_Parse(speech_task->speechs);
char *value = NULL;
/* 如果是否json格式数据 */
if (speechs == NULL) {
return -1;
}
/* 将语料信息加入到播放列表中 */
CORE_INIT_LIST_HEAD(&speech_task->play_list);
for (int i = 0; i < cJSON_GetArraySize(speechs); i++)
{
value = cJSON_GetArrayItem(speechs, i)->valuestring;
if(*value == '{' && *(value + 1)== '$') {
money_to_speech(value + 2, speech_task->format, &speech_task->play_list);
} else {
name_to_speech(value, speech_task->format, &speech_task->play_list);
}
}
cJSON_Delete(speechs);
return STATE_SUCCESS;
}
/* 检查是否重复播放,如果不是重复播放,添加进查重播报列表 */
static int32_t core_speech_duplicate_check(linkspeech_handle_t *linkspeech, core_speech_task_t *speech_task)
{
int i = 0;
/* 查重 */
for(i = 0; i < AIOT_SPEECH_DUPLICATE_SIZE_DEFAULT; i++) {
if(linkspeech->dup_id[i] != NULL && 0 == strcmp(speech_task->id, linkspeech->dup_id[i])) {
return -1;
}
}
/* 添加 */
if(linkspeech->dup_id[linkspeech->write_pos] != NULL) {
sysdep->core_sysdep_free(linkspeech->dup_id[linkspeech->write_pos]);
linkspeech->dup_id[linkspeech->write_pos] = NULL;
}
core_strdup(sysdep, &linkspeech->dup_id[linkspeech->write_pos], speech_task->id, TAG);
linkspeech->write_pos++;
if(linkspeech->write_pos >= AIOT_SPEECH_DUPLICATE_SIZE_DEFAULT) {
linkspeech->write_pos = 0;
}
return STATE_SUCCESS;
}
/* 检查语料文件是否存在 */
static int32_t core_speech_task_file_check(linkspeech_handle_t *linkspeech, core_speech_task_t *speech_task)
{
play_node_t *node = NULL, *next = NULL;
char *path = NULL;
char *src[2] = { NULL, NULL};
int32_t res = STATE_SUCCESS;
res = core_speech_task_prepare(speech_task);
if(res != STATE_SUCCESS ) {
return -1;
}
core_list_for_each_entry_safe(node, next, &speech_task->play_list, linked_node, play_node_t) {
src[0] = linkspeech->work_dir;
src[1] = node->filename;
path = NULL;
core_sprintf(sysdep, &path, "%s/%s", src, sizeof(src)/sizeof(char *), TAG);
sysdep->core_sysdep_free(node->filename);
node->filename = path;
if(fs->file_size(node->filename) <= 0) {
core_log1(sysdep, 0, "loss file %s\r\n", node->filename);
res = -1;
}
}
return res;
}
static int32_t send_speech_task_result(linkspeech_handle_t *linkspeech, core_speech_task_t* speech_task, int32_t code)
{
aiot_dm_msg_t msg;
int32_t res = STATE_SUCCESS;
char *data = NULL;
char *src[] = { NULL, NULL, NULL };
if(linkspeech == NULL || speech_task == NULL) {
return -1;
}
switch(code){
case 1:{
src[0] = "1";
src[1] = "duplicate speechbroad id";
}
break;
case 2:{
src[0] = "2";
src[1] = "speechs file not found";
}
break;
default:{
src[0] = "0";
src[1] = "success";
}
break;
}
src[2] = speech_task->id;
core_sprintf(sysdep, &data, "{\"result\":%s,\"error_message\":\"%s\",\"task_id\":\"%s\"}", src, sizeof(src) / sizeof(char *), TAG );
memset(&msg, 0, sizeof(aiot_dm_msg_t));
if(speech_task->rrpc_id != NULL) {
msg.type = AIOT_DMMSG_SYNC_SERVICE_REPLY;
msg.data.sync_service_reply.rrpc_id = speech_task->rrpc_id;
msg.data.sync_service_reply.msg_id = speech_task->msg_id;
msg.data.sync_service_reply.service_id = "SyncSpeechBroadcast";
msg.data.sync_service_reply.code = 200;
msg.data.sync_service_reply.data = data;
} else {
memset(&msg, 0, sizeof(aiot_dm_msg_t));
msg.type = AIOT_DMMSG_ASYNC_SERVICE_REPLY;
msg.data.async_service_reply.msg_id = speech_task->msg_id;
msg.data.async_service_reply.code = 200;
msg.data.async_service_reply.service_id = "SpeechBroadcast";
msg.data.async_service_reply.data = data;
}
res = aiot_dm_send(linkspeech->dm_handle, &msg);
sysdep->core_sysdep_free(data);
return res;
}
static core_speech_task_t* core_speech_update_task(linkspeech_handle_t *linkspeech)
{
core_speech_task_t* speech_task = NULL;
sysdep->core_sysdep_mutex_lock(linkspeech->speech_task_mutex);
if(core_list_empty(&linkspeech->speech_task_list)) {
sysdep->core_sysdep_mutex_unlock(linkspeech->speech_task_mutex);
return NULL;
}
speech_task = core_list_first_entry(&linkspeech->speech_task_list, core_speech_task_t, linked_node);
core_list_del(&speech_task->linked_node);
sysdep->core_sysdep_mutex_unlock(linkspeech->speech_task_mutex);
return speech_task;
}
static int32_t core_check_play_task(linkspeech_handle_t *linkspeech)
{
play_node_t *node = NULL;
if( linkspeech->playing_task == NULL ) {
linkspeech->playing_task = core_speech_update_task(linkspeech);
if(linkspeech->playing_task == NULL || core_list_empty(&linkspeech->playing_task->play_list)) {
return -1;
}
} else {
/* 播放完单个文件,删除 */
node = core_list_first_entry(&linkspeech->playing_task->play_list, play_node_t, linked_node);
core_list_del(&node->linked_node);
sysdep->core_sysdep_free(node->filename);
sysdep->core_sysdep_free(node);
/* 查询任务是否播放完成 */
if(core_list_empty(&linkspeech->playing_task->play_list)) {
/* 异步回复任务播放完成 */
if(linkspeech->playing_task->rrpc_id == NULL) {
send_speech_task_result(linkspeech, linkspeech->playing_task, 0);
}
/* 回收任务资源 */
sysdep->core_sysdep_mutex_lock(linkspeech->speech_task_mutex);
core_list_del(&linkspeech->playing_task->linked_node);
core_delete_speech_task(linkspeech->playing_task);
linkspeech->speech_task_num--;
sysdep->core_sysdep_mutex_unlock(linkspeech->speech_task_mutex);
/* 更新下个播报任务 */
linkspeech->playing_task = core_speech_update_task(linkspeech);
if(linkspeech->playing_task == NULL || core_list_empty(&linkspeech->playing_task->play_list)) {
return -1;
}
}
}
if( linkspeech->playing_task != NULL && linkspeech->player != NULL ) {
node = core_list_first_entry(&linkspeech->playing_task->play_list, play_node_t, linked_node);
linkspeech->player(node->filename, &linkspeech->play_param);
}
return STATE_SUCCESS;
}
static core_speech_task_t *core_speech_broadcast_parse(linkspeech_handle_t *linkspeech, char *data, int datalen)
{
cJSON *speechs = NULL, *format = NULL, *id = NULL;
core_speech_task_t *speech_task = NULL;
cJSON *root = cJSON_Parse(data);
/* 如果是否json格式数据 */
if (root == NULL) {
return NULL;
}
speechs = cJSON_GetObjectItem(root, "speechs");
if (speechs == NULL) {
cJSON_Delete(root);
return NULL;
}
format = cJSON_GetObjectItem(root, "format");
if (format == NULL) {
cJSON_Delete(root);
return NULL;
}
id = cJSON_GetObjectItem(root, "id");
if (id == NULL) {
cJSON_Delete(root);
return NULL;
}
speech_task = sysdep->core_sysdep_malloc(sizeof(core_speech_task_t), TAG);
if(speech_task == NULL) {
cJSON_Delete(root);
return NULL;
}
memset(speech_task, 0, sizeof(*speech_task));
CORE_INIT_LIST_HEAD(&speech_task->play_list);
core_strdup(sysdep, &speech_task->format, format->valuestring, TAG);
core_strdup(sysdep, &speech_task->id, id->valuestring, TAG);
speech_task->speechs = cJSON_Print(speechs);
cJSON_Delete(root);
return speech_task;
}
/* 解析语料下载任务,并执行 */
static int32_t core_speech_download_parse(linkspeech_handle_t *linkspeech, char *data, int datalen)
{
char *jobcode = NULL;
char *value = NULL;
uint32_t value_len = 0;
core_download_node_t *node = NULL;
/* 解析下载jobcode*/
int res = core_json_value(data, datalen,
"jobcode", strlen("jobcode"), &value, &value_len);
if (res != STATE_SUCCESS) {
return -1;
}
jobcode = sysdep->core_sysdep_malloc(value_len + 1, TAG);
memcpy(jobcode, value, value_len);
jobcode[value_len] = 0;
res = core_json_value(data, datalen,
"url", strlen("url"), &value, &value_len);
if (res != STATE_SUCCESS) {
sysdep->core_sysdep_free(jobcode);
return -1;
}
/* 记录下载的url及jobcode,插入下载任务列表中 */
node = (core_download_node_t *)sysdep->core_sysdep_malloc(sizeof(core_download_node_t), TAG);
memset(node, 0, sizeof(*node));
node->json_url = sysdep->core_sysdep_malloc(value_len + 1, TAG);
memcpy(node->json_url, value, value_len);
*(node->json_url + value_len) = 0;
node->jobcode = jobcode;
sysdep->core_sysdep_mutex_lock(linkspeech->download_mutex);
core_list_add_tail(&node->linked_node, &linkspeech->download_task_list);
sysdep->core_sysdep_mutex_unlock(linkspeech->download_mutex);
return STATE_SUCCESS;
}
/* 批量删除语料 */
static int32_t core_speech_delete_parse(linkspeech_handle_t *linkspeech, char *data, uint32_t datalen)
{
cJSON *root = NULL, *speechs = NULL, *speech = NULL;
cJSON *formart, *id;
char *filename = NULL;
char *src[3] = {NULL, NULL, NULL};
int32_t res = STATE_SUCCESS;
//如果是否json格式数据
root = cJSON_Parse(data);
if (root == NULL) {
return -1;
}
speechs = cJSON_GetObjectItem(root, "speechs");
if (speechs == NULL) {
cJSON_Delete(root);
return -1;
}
for (int i = 0; i < cJSON_GetArraySize(speechs); i++)
{
speech = cJSON_GetArrayItem(speechs, i);
if(speech != NULL) {
formart = cJSON_GetObjectItem(speech, "format");
id = cJSON_GetObjectItem(speech, "id");
src[0] = linkspeech->work_dir;
src[1] = id->valuestring;
src[2] = formart->valuestring;
core_sprintf(sysdep, &filename, "%s/%s.%s", src, sizeof(src) / sizeof(char *), TAG );
/* 存在该文件就删除 */
if(fs->file_size(filename) > 0) {
fs->file_delete(filename);
}
sysdep->core_sysdep_free(filename);
}
}
cJSON_Delete(root);
return res;
}
/* 物模型服务调用处理 */
static void core_dm_recv_async_service_invoke(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
{
linkspeech_handle_t *linkspeech = (linkspeech_handle_t *)userdata;
core_speech_task_t *speech_task = NULL;
aiot_dm_msg_t msg;
int32_t res = STATE_SUCCESS;
if(linkspeech == NULL) {
return;
}
/* 消息回复 */
memset(&msg, 0, sizeof(aiot_dm_msg_t));
msg.type = AIOT_DMMSG_ASYNC_SERVICE_REPLY;
msg.data.async_service_reply.msg_id = recv->data.async_service_invoke.msg_id;
msg.data.async_service_reply.code = 200;
msg.data.async_service_reply.service_id = recv->data.async_service_invoke.service_id;
msg.data.async_service_reply.data = "{}";
if(0 == strcmp(recv->data.async_service_invoke.service_id, "SpeechPost")) {
/* 语料下发 */
core_speech_download_parse(linkspeech, recv->data.async_service_invoke.params, recv->data.async_service_invoke.params_len);
res = aiot_dm_send(dm_handle, &msg);
if (res < 0) {
core_log(sysdep, 0, "aiot_dm_send failed\r\n");
}
} else if(0 == strcmp(recv->data.async_service_invoke.service_id, "DeleteSpeech")) {
/* 语料批量删除 */
core_speech_delete_parse(linkspeech, recv->data.async_service_invoke.params, recv->data.async_service_invoke.params_len);
res = aiot_dm_send(dm_handle, &msg);
if (res < 0) {
core_log(sysdep, 0, "aiot_dm_send failed\r\n");
}
}
else if(0 == strcmp(recv->data.async_service_invoke.service_id, "SpeechBroadcast")) {
if(linkspeech->speech_task_num > linkspeech->speech_buffer_size) {
core_log2(sysdep, 0, "SpeechBroadcast task buff full [%d] > [%d]\r\n", &linkspeech->speech_task_num, &linkspeech->speech_buffer_size);
return;
}
/* 语料组合播报 */
speech_task = core_speech_broadcast_parse(linkspeech, recv->data.async_service_invoke.params, recv->data.async_service_invoke.params_len);
if(speech_task != NULL) {
speech_task->rrpc_id = NULL;
speech_task->msg_id = recv->data.async_service_invoke.msg_id;
if(STATE_SUCCESS != core_speech_duplicate_check(linkspeech, speech_task)) {
core_log1(sysdep, 0, "SpeechBroadcast duplicate id [%s]\r\n", speech_task->id);
send_speech_task_result(linkspeech, speech_task, 1);
core_delete_speech_task(speech_task);
return;
} else if(STATE_SUCCESS != core_speech_task_file_check(linkspeech, speech_task)){
send_speech_task_result(linkspeech, speech_task, 2);
core_delete_speech_task(speech_task);
return;
} else {
sysdep->core_sysdep_mutex_lock(linkspeech->speech_task_mutex);
core_list_add_tail(&speech_task->linked_node, &linkspeech->speech_task_list);
linkspeech->speech_task_num++;
sysdep->core_sysdep_mutex_unlock(linkspeech->speech_task_mutex);
if( linkspeech->playing_task == NULL ) {
core_check_play_task(linkspeech);
}
}
}
}
}
/* 物模型同步服务调用处理 */
static void core_dm_recv_sync_service_invoke(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
{
linkspeech_handle_t *linkspeech = (linkspeech_handle_t *)userdata;
core_speech_task_t *speech_task = NULL;
if(0 == strcmp(recv->data.sync_service_invoke.service_id, "SyncSpeechBroadcast")) {
if(linkspeech->speech_task_num > linkspeech->speech_buffer_size) {
core_log2(sysdep, 0, "SpeechBroadcast task buff full [%d] > [%d]\r\n", &linkspeech->speech_task_num, &linkspeech->speech_buffer_size);
return;
}
/* 语料组合播报 */
speech_task = core_speech_broadcast_parse(linkspeech, recv->data.sync_service_invoke.params, recv->data.sync_service_invoke.params_len);
if(speech_task != NULL) {
speech_task->rrpc_id = NULL;
core_strdup(sysdep, &speech_task->rrpc_id, recv->data.sync_service_invoke.rrpc_id, TAG);
speech_task->msg_id = recv->data.async_service_invoke.msg_id;
if(STATE_SUCCESS != core_speech_duplicate_check(linkspeech, speech_task)) {
core_log1(sysdep, 0, "SpeechBroadcast duplicate id [%s]\r\n", speech_task->id);
send_speech_task_result(linkspeech, speech_task, 1);
core_delete_speech_task(speech_task);
return;
} else if(STATE_SUCCESS != core_speech_task_file_check(linkspeech, speech_task)){
send_speech_task_result(linkspeech, speech_task, 2);
core_delete_speech_task(speech_task);
return;
} else {
sysdep->core_sysdep_mutex_lock(linkspeech->speech_task_mutex);
core_list_add_tail(&speech_task->linked_node, &linkspeech->speech_task_list);
linkspeech->speech_task_num++;
sysdep->core_sysdep_mutex_unlock(linkspeech->speech_task_mutex);
/* 同步调用需要立即返回立即返回 */
send_speech_task_result(linkspeech, speech_task, 0);
if( linkspeech->playing_task == NULL ) {
core_check_play_task(linkspeech);
}
}
}
}
}
/* 用户数据接收处理回调函数 */
static void core_dm_recv_handler(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
{
switch (recv->type) {
/* 异步服务调用 */
case AIOT_DMRECV_ASYNC_SERVICE_INVOKE: {
core_dm_recv_async_service_invoke(dm_handle, recv, userdata);
}
break;
/* 同步服务调用 */
case AIOT_DMRECV_SYNC_SERVICE_INVOKE: {
core_dm_recv_sync_service_invoke(dm_handle, recv, userdata);
}
break;
default:
break;
}
}
static void core_play_finished(char *filename, void *userdata)
{
linkspeech_handle_t *linkspeech = (linkspeech_handle_t *)userdata;
if(linkspeech == NULL) {
return;
}
core_log1(sysdep, 0, "core_play_finished file %s\r\n", filename);
core_check_play_task(linkspeech);
}
void *aiot_linkspeech_init()
{
linkspeech_handle_t *linkspeech = NULL;
int i = 0;
sysdep = aiot_sysdep_get_portfile();
if (NULL == sysdep) {
return NULL;
}
linkspeech = sysdep->core_sysdep_malloc(sizeof(linkspeech_handle_t), TAG);
if(linkspeech == NULL) {
return NULL;
}
memset(linkspeech, 0, sizeof(linkspeech_handle_t));
/* 配置项设置为空 */
linkspeech->dm_handle = NULL;
linkspeech->player = NULL;
linkspeech->work_dir = NULL;
linkspeech->speech_buffer_size = AIOT_LSOPT_SPEECH_BUFFER_SIZE_DEFAULT;
/* 下载管理初始化 */
linkspeech->download_mutex = sysdep->core_sysdep_mutex_init();
CORE_INIT_LIST_HEAD(&linkspeech->download_task_list);
/* 语音播报任务初始化 */
linkspeech->speech_task_mutex = sysdep->core_sysdep_mutex_init();
linkspeech->speech_task_num = 0;
CORE_INIT_LIST_HEAD(&linkspeech->speech_task_list);
linkspeech->play_param.on_finish = core_play_finished;
linkspeech->play_param.userdata = linkspeech;
linkspeech->playing_task = NULL;
linkspeech->https_enable = 0;
for(i = 0; i < sizeof(linkspeech->dup_id) / sizeof(char *); i++){
linkspeech->dup_id[i] = NULL;
}
linkspeech->write_pos = 0;
return linkspeech;
}
int32_t aiot_linkspeech_deinit(void **handle)
{
linkspeech_handle_t *linkspeech = NULL;
core_download_node_t *down_node = NULL, *down_next = NULL;
core_speech_task_t *speech_node = NULL, *speech_next = NULL;
int32_t res = STATE_SUCCESS;
int i = 0;
if (NULL == handle || NULL == *handle) {
return STATE_USER_INPUT_NULL_POINTER;
}
linkspeech = (linkspeech_handle_t *)*handle;
sysdep->core_sysdep_mutex_lock(linkspeech->speech_task_mutex);
/* 删除播报任务列表 */
core_list_for_each_entry_safe(speech_node, speech_next, &linkspeech->speech_task_list, linked_node, core_speech_task_t) {
core_list_del(&speech_node->linked_node);
core_delete_speech_task(speech_node);
speech_node = NULL;
}
linkspeech->speech_task_num = 0;
/* 删除正在播报的任务 */
if(linkspeech->playing_task != NULL) {
core_delete_speech_task(linkspeech->playing_task);
linkspeech->playing_task = NULL;
}
sysdep->core_sysdep_mutex_unlock(linkspeech->speech_task_mutex);
sysdep->core_sysdep_mutex_deinit(&linkspeech->speech_task_mutex);
sysdep->core_sysdep_mutex_lock(linkspeech->download_mutex);
core_list_for_each_entry_safe(down_node, down_next, &linkspeech->download_task_list, linked_node, core_download_node_t) {
core_list_del(&down_node->linked_node);
sysdep->core_sysdep_free(down_node->jobcode);
sysdep->core_sysdep_free(down_node->json_url);
sysdep->core_sysdep_free(down_node);
}
sysdep->core_sysdep_mutex_unlock(linkspeech->download_mutex);
sysdep->core_sysdep_mutex_deinit(&linkspeech->download_mutex);
if(linkspeech->work_dir != NULL) {
sysdep->core_sysdep_free(linkspeech->work_dir);
}
if(linkspeech->dm_handle != NULL) {
/* 销毁DATA-MODEL实例, 一般不会运行到这里 */
res = aiot_dm_deinit(&linkspeech->dm_handle);
if (res < STATE_SUCCESS) {
return res;
}
}
for(i = 0; i < sizeof(linkspeech->dup_id) / sizeof(char *); i++){
if(linkspeech->dup_id[i] != NULL) {
sysdep->core_sysdep_free(linkspeech->dup_id[i]);
}
linkspeech->dup_id[i] = NULL;
}
linkspeech->write_pos = 0;
sysdep->core_sysdep_free(linkspeech);
return STATE_SUCCESS;
}
int32_t aiot_linkspeech_setopt(void *handle, aiot_linkspeech_option_t option, void *data)
{
linkspeech_handle_t *linkspeech = NULL;
uint8_t post_reply = 1;
int32_t res = STATE_SUCCESS;
if (NULL == handle || NULL == data) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (option >= AIOT_LSOPT_MAX) {
return STATE_USER_INPUT_OUT_RANGE;
}
linkspeech = (linkspeech_handle_t *)handle;
switch(option) {
case AIOT_LSOPT_MQTT_HANDLE: {
if(linkspeech->dm_handle != NULL) {
return -1;
}
/* 创建DATA-MODEL实例 */
linkspeech->dm_handle = aiot_dm_init();
if (linkspeech->dm_handle == NULL) {
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
/* 配置MQTT实例句柄 */
aiot_dm_setopt(linkspeech->dm_handle, AIOT_DMOPT_MQTT_HANDLE, data );
/* 配置消息接收处理回调函数 */
aiot_dm_setopt(linkspeech->dm_handle, AIOT_DMOPT_RECV_HANDLER, (void *)core_dm_recv_handler);
/* 配置回调函数的userdata */
aiot_dm_setopt(linkspeech->dm_handle, AIOT_DMOPT_USERDATA, (void *)linkspeech);
/* 配置是云端否需要回复post_reply给设备. 如果为1, 表示需要云端回复, 否则表示不回复 */
aiot_dm_setopt(linkspeech->dm_handle, AIOT_DMOPT_POST_REPLY, (void *)&post_reply);
}
break;
case AIOT_LSOPT_FILE_OPS: {
fs = (aiot_fs_t *)data;
}
break;
case AIOT_LSOPT_PLAYER_CALLBACK: {
linkspeech->player = (player_cb_t)data;
}
break;
case AIOT_LSOPT_WORK_DIR: {
if (linkspeech->work_dir != NULL) {
sysdep->core_sysdep_free(linkspeech->work_dir);
linkspeech->work_dir = NULL;
}
core_strdup(sysdep, &linkspeech->work_dir, (char *)data, TAG);
}
break;
case AIOT_LSOPT_HTTPS_ENABLE: {
if(*(int32_t *)data != 0 && *(int32_t *)data != 1) {
return -1;
}
linkspeech->https_enable = *(int32_t *)data;
}
break;
case AIOT_LSOPT_SPEECH_BUFFER_SIZE: {
if(*(int32_t *)data <= 0 || *(int32_t *)data > 255) {
return -1;
}
linkspeech->speech_buffer_size = *(int32_t *)data;
}
break;
default:
break;
}
return res;
}
/* 语料下载url.json文件解析并下载语料 */
static int32_t core_speech_download_file_parse(linkspeech_handle_t *linkspeech, char *data, uint32_t datalen)
{
cJSON *root = NULL, *audios = NULL, *audio = NULL;
cJSON *formart, *id, *url;
char *local_file = NULL;
char *src[3] = {NULL, NULL, NULL};
int32_t res = STATE_SUCCESS;
//如果是否json格式数据
root = cJSON_Parse(data);
if (root == NULL) {
return -1;
}
audios = cJSON_GetObjectItem(root, "audios");
if (audios == NULL) {
cJSON_Delete(root);
return -1;
}
for (int i = 0; i < cJSON_GetArraySize(audios); i++)
{
audio = cJSON_GetArrayItem(audios, i);
if(audio != NULL) {
formart = cJSON_GetObjectItem(audio,"format");
id = cJSON_GetObjectItem(audio,"id");
url = cJSON_GetObjectItem(audio,"url");
src[0] = linkspeech->work_dir;
src[1] = id->valuestring;
src[2] = formart->valuestring;
core_sprintf(sysdep, &local_file, "%s/%s.%s", src, sizeof(src) / sizeof(char *), TAG );
if(fs->file_size(local_file) > 0) {
fs->file_delete(local_file);
}
res = core_download_file(url->valuestring, local_file, linkspeech->https_enable);
if(res != STATE_SUCCESS) {
sysdep->core_sysdep_free(local_file);
break;
}
sysdep->core_sysdep_free(local_file);
}
}
cJSON_Delete(root);
return res;
}
int32_t aiot_linkspeech_start(void *handle)
{
linkspeech_handle_t *linkspeech = (linkspeech_handle_t *)handle;
char *data = NULL;
char *response = NULL;
char *response_src[2] = {NULL};
char *json_file = NULL;
char *json_file_src[2] = { NULL, NULL };
int32_t res = STATE_SUCCESS;
char *result_success = "0";
char *result_error = "255";
int32_t file_len = 0, read_len = 0;
core_download_node_t *node = NULL;
if (NULL == handle || fs == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
if(linkspeech->work_dir == NULL || linkspeech->player == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
linkspeech->running = 1;
while(linkspeech->running) {
/* 读取下载任务 */
node = NULL;
sysdep->core_sysdep_mutex_lock(linkspeech->download_mutex);
node = core_list_first_entry(&linkspeech->download_task_list, core_download_node_t, linked_node);
if(&node->linked_node != &linkspeech->download_task_list) {
core_list_del(&node->linked_node);
} else {
node = NULL;
}
sysdep->core_sysdep_mutex_unlock(linkspeech->download_mutex);
/* 处理单个下载任务 */
if(node != NULL) {
/* 生成本地文件名 */
json_file_src[0] = linkspeech->work_dir;
json_file_src[1] = node->jobcode;
core_sprintf(sysdep, &json_file, "%s/%s.json", json_file_src, sizeof(json_file_src) / sizeof(char *), TAG);
/* 下载包含语料url的json文件至本地 */
core_download_file(node->json_url, json_file, linkspeech->https_enable);
/* 读取文件内容拷贝至data */
file_len = fs->file_size(json_file);
data = sysdep->core_sysdep_malloc(file_len + 1, TAG);
read_len = fs->file_read(json_file, 0, (uint8_t *)data, file_len);
core_log3(sysdep, 0, "[%s] read_len %d, file_len %d \r\n", json_file, &read_len, &file_len);
/* 删除不使用的记录语料地址的json文件 */
fs->file_delete(json_file);
sysdep->core_sysdep_free(json_file);
if(read_len == file_len) {
/* 解析语料文件内容,并触发下载 */
res = core_speech_download_file_parse(linkspeech, data, read_len);
if(res == STATE_SUCCESS) {
response_src[1] = result_success;
} else {
response_src[1] = result_error;
}
} else {
response_src[1] = result_error;
}
/* 删除文件相关内容 */
sysdep->core_sysdep_free(data);
/* 回复云端下载完成 */
response_src[0] = node->jobcode;
core_sprintf(sysdep, &response, "{\"jobcode\":\"%s\", \"result\":%s}", response_src, sizeof(response_src) / sizeof(char *), TAG);
core_send_event_post(linkspeech->dm_handle, "SpeechUpdateResponse", response);
sysdep->core_sysdep_free(response);
/* 删除下载任务节点 */
sysdep->core_sysdep_free(node->json_url);
sysdep->core_sysdep_free(node->jobcode);
sysdep->core_sysdep_free(node);
} else {
sysdep->core_sysdep_sleep(1000);
}
}
return res;
}
int32_t aiot_linkspeech_stop(void *handle)
{
linkspeech_handle_t *linkspeech = NULL;
int32_t res = STATE_SUCCESS;
if (NULL == handle) {
return STATE_USER_INPUT_NULL_POINTER;
}
linkspeech = (linkspeech_handle_t *)handle;
linkspeech->running = 0;
return res;
}

View File

@@ -0,0 +1,161 @@
/**
* @file aiot_linkspeech_api.h
* @brief LinkSpeech模块头文件, 提供设备千里传音的能力
* @date 2019-12-27
*
* @copyright Copyright (C) 2015-2018 Alibaba Group Holding Limited
*
* @details
*
*/
#ifndef __AIOT_LINKSPEECH_API_H__
#define __AIOT_LINKSPEECH_API_H__
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdint.h>
typedef struct {
/* 当音频播报结束后执行该回调, 需要带userdata */
void (*on_finish)(char *filename, void *userdata);
void *userdata;
} play_param_t;
/* 设置音频播放回调,该接口不能长时间阻塞 */
typedef int32_t (*player_cb_t)(char *filename, play_param_t *ext_params);
typedef enum {
/**
* @brief 模块依赖的MQTT句柄
*
* @details
*
* 千里传音模块依赖底层的MQTT模块, 用户必需配置正确的MQTT句柄, 否则无法正常工作
*
* 数据类型: (void *)
*/
AIOT_LSOPT_MQTT_HANDLE,
/**
* @brief 模块依赖的文件系统操作
*
* @details
*
* 千里传音模块依赖文件系统接口,需要用户实现并设置
*
* 数据类型: (aiot_fs_t *)
*/
AIOT_LSOPT_FILE_OPS,
/**
* @brief 模块依赖的语音播放的接口设置
*
* @details
*
* 千里传音模块依赖音频播放接口
*
* 数据类型: (player_cb_t *)
*/
AIOT_LSOPT_PLAYER_CALLBACK,
/**
* @brief 设置千里传音文件保存的文件夹路径
*
* @details
*
* 数据类型: (char *)
*/
AIOT_LSOPT_WORK_DIR,
/**
* @brief 是否使能https下载默认为http下载
*
* @details
*
* 数据类型: (int32_t *) 0:http下载 1:https下载
*/
AIOT_LSOPT_HTTPS_ENABLE,
/**
* @brief 组合播报任务大缓存个数
*
* @details
* 当组合播报的速度小于下发的速度时SDK会缓存播报任务该参数用于设置缓存播报任务的个数
*
* 数据类型: (int32_t *) 1255, 默认为10
*/
AIOT_LSOPT_SPEECH_BUFFER_SIZE,
AIOT_LSOPT_MAX,
} aiot_linkspeech_option_t;
/**
* @brief 创建一个LinkSpeech实例
*
* @return void*
* @retval 非NULL linkspeech实例句柄
* @retval NULL 初始化失败, 或者是因为没有设置portfile, 或者是内存分配失败导致
*
*/
void *aiot_linkspeech_init();
/**
* @brief 销毁LinkSpeech实例句柄
*
* @param[in] handle 指向linkspeech实例句柄的指针
*
* @return int32_t
* @retval STATE_SUCCESS 执行成功
*
*/
int32_t aiot_linkspeech_deinit(void **handle);
/**
* @brief 设置LinkSpeech句柄的参数
*
* @details
*
* 对LinkSpeech会话进行配置, 常见的配置选项包括
*
* + `AIOT_LSOPT_MQTT_HANDLE`: 把 @ref aiot_mqtt_init 返回的MQTT会话句柄跟OTA会话关联起来
* + `AIOT_LSOPT_RECV_HANDLER`: 设置OTA消息的数据处理回调, 这个用户回调在有OTA消息的时候, 会被 @ref aiot_mqtt_recv 调用到
*
* @param[in] handle linkspeech句柄
* @param[in] option 配置选项, 更多信息请参考@ref aiot_linkspeech_option_t
* @param[in] data 配置选项数据, 更多信息请参考@ref aiot_linkspeech_option_t
*
* @return int32_t
* @retval STATE_USER_INPUT_UNKNOWN_OPTION option不支持
* @retval STATE_SUCCESS 参数设置成功
*
*/
int32_t aiot_linkspeech_setopt(void *handle, aiot_linkspeech_option_t option, void *data);
/**
* @brief 启动linkspeech千里传音服务会一直阻塞
*
* @param[in] handle 指向linkspeech实例句柄的指针
*
* @return int32_t
* @retval STATE_SUCCESS 执行成功
*
*/
int32_t aiot_linkspeech_start(void *handle);
/**
* @brief 关闭linkspeech千里传音服务执行后aiot_linkspeech_start会退出
*
* @param[in] handle 指向linkspeech实例句柄的指针
*
* @return int32_t
* @retval STATE_SUCCESS 执行成功
*
*/
int32_t aiot_linkspeech_stop(void *handle);
#if defined(__cplusplus)
}
#endif
#endif /* #ifndef __AIOT_LINKSPEECH_API_H__ */

View File

@@ -0,0 +1,400 @@
#include <stdio.h>
#include "http_download.h"
#include "aiot_state_api.h"
#include "core_http.h"
#include "aiot_sysdep_api.h"
#define HTTP_DOWNLOAD_HOST_MAX_LEN 512
#define HTTP_DOWNLOAD_PATH_MAX_LEN 512
#define HTTP_DOWNLOAD_DEFAULT_TLS_PORT 80
#define HTTP_DOWNLOAD_BUFFER_LEN (2 * 1024)
#define HTTP_DOWNLOAD_RECV_TIMEOUT_MS (5 * 1000)
#define HTTP_DOWNLOAD_HTTPCLIENT_MAX_URL_LEN (256)
#define HTTP_DOWNLOAD_MAX_DIGIT_NUM_OF_UINT32 (20)
#define HTTP_DOWNLOAD_RESPONSE_PARTIAL (206)
#define HTTP_DOWNLOAD_RESPONSE_OK (200)
static const uint64_t crc64_table[256] = {
0x0000000000000000, 0xb32e4cbe03a75f6f, 0xf4843657a840a05b, 0x47aa7ae9abe7ff34,
0x7bd0c384ff8f5e33, 0xc8fe8f3afc28015c, 0x8f54f5d357cffe68, 0x3c7ab96d5468a107,
0xf7a18709ff1ebc66, 0x448fcbb7fcb9e309, 0x0325b15e575e1c3d, 0xb00bfde054f94352,
0x8c71448d0091e255, 0x3f5f08330336bd3a, 0x78f572daa8d1420e, 0xcbdb3e64ab761d61,
0x7d9ba13851336649, 0xceb5ed8652943926, 0x891f976ff973c612, 0x3a31dbd1fad4997d,
0x064b62bcaebc387a, 0xb5652e02ad1b6715, 0xf2cf54eb06fc9821, 0x41e11855055bc74e,
0x8a3a2631ae2dda2f, 0x39146a8fad8a8540, 0x7ebe1066066d7a74, 0xcd905cd805ca251b,
0xf1eae5b551a2841c, 0x42c4a90b5205db73, 0x056ed3e2f9e22447, 0xb6409f5cfa457b28,
0xfb374270a266cc92, 0x48190ecea1c193fd, 0x0fb374270a266cc9, 0xbc9d3899098133a6,
0x80e781f45de992a1, 0x33c9cd4a5e4ecdce, 0x7463b7a3f5a932fa, 0xc74dfb1df60e6d95,
0x0c96c5795d7870f4, 0xbfb889c75edf2f9b, 0xf812f32ef538d0af, 0x4b3cbf90f69f8fc0,
0x774606fda2f72ec7, 0xc4684a43a15071a8, 0x83c230aa0ab78e9c, 0x30ec7c140910d1f3,
0x86ace348f355aadb, 0x3582aff6f0f2f5b4, 0x7228d51f5b150a80, 0xc10699a158b255ef,
0xfd7c20cc0cdaf4e8, 0x4e526c720f7dab87, 0x09f8169ba49a54b3, 0xbad65a25a73d0bdc,
0x710d64410c4b16bd, 0xc22328ff0fec49d2, 0x85895216a40bb6e6, 0x36a71ea8a7ace989,
0x0adda7c5f3c4488e, 0xb9f3eb7bf06317e1, 0xfe5991925b84e8d5, 0x4d77dd2c5823b7ba,
0x64b62bcaebc387a1, 0xd7986774e864d8ce, 0x90321d9d438327fa, 0x231c512340247895,
0x1f66e84e144cd992, 0xac48a4f017eb86fd, 0xebe2de19bc0c79c9, 0x58cc92a7bfab26a6,
0x9317acc314dd3bc7, 0x2039e07d177a64a8, 0x67939a94bc9d9b9c, 0xd4bdd62abf3ac4f3,
0xe8c76f47eb5265f4, 0x5be923f9e8f53a9b, 0x1c4359104312c5af, 0xaf6d15ae40b59ac0,
0x192d8af2baf0e1e8, 0xaa03c64cb957be87, 0xeda9bca512b041b3, 0x5e87f01b11171edc,
0x62fd4976457fbfdb, 0xd1d305c846d8e0b4, 0x96797f21ed3f1f80, 0x2557339fee9840ef,
0xee8c0dfb45ee5d8e, 0x5da24145464902e1, 0x1a083bacedaefdd5, 0xa9267712ee09a2ba,
0x955cce7fba6103bd, 0x267282c1b9c65cd2, 0x61d8f8281221a3e6, 0xd2f6b4961186fc89,
0x9f8169ba49a54b33, 0x2caf25044a02145c, 0x6b055fede1e5eb68, 0xd82b1353e242b407,
0xe451aa3eb62a1500, 0x577fe680b58d4a6f, 0x10d59c691e6ab55b, 0xa3fbd0d71dcdea34,
0x6820eeb3b6bbf755, 0xdb0ea20db51ca83a, 0x9ca4d8e41efb570e, 0x2f8a945a1d5c0861,
0x13f02d374934a966, 0xa0de61894a93f609, 0xe7741b60e174093d, 0x545a57dee2d35652,
0xe21ac88218962d7a, 0x5134843c1b317215, 0x169efed5b0d68d21, 0xa5b0b26bb371d24e,
0x99ca0b06e7197349, 0x2ae447b8e4be2c26, 0x6d4e3d514f59d312, 0xde6071ef4cfe8c7d,
0x15bb4f8be788911c, 0xa6950335e42fce73, 0xe13f79dc4fc83147, 0x521135624c6f6e28,
0x6e6b8c0f1807cf2f, 0xdd45c0b11ba09040, 0x9aefba58b0476f74, 0x29c1f6e6b3e0301b,
0xc96c5795d7870f42, 0x7a421b2bd420502d, 0x3de861c27fc7af19, 0x8ec62d7c7c60f076,
0xb2bc941128085171, 0x0192d8af2baf0e1e, 0x4638a2468048f12a, 0xf516eef883efae45,
0x3ecdd09c2899b324, 0x8de39c222b3eec4b, 0xca49e6cb80d9137f, 0x7967aa75837e4c10,
0x451d1318d716ed17, 0xf6335fa6d4b1b278, 0xb199254f7f564d4c, 0x02b769f17cf11223,
0xb4f7f6ad86b4690b, 0x07d9ba1385133664, 0x4073c0fa2ef4c950, 0xf35d8c442d53963f,
0xcf273529793b3738, 0x7c0979977a9c6857, 0x3ba3037ed17b9763, 0x888d4fc0d2dcc80c,
0x435671a479aad56d, 0xf0783d1a7a0d8a02, 0xb7d247f3d1ea7536, 0x04fc0b4dd24d2a59,
0x3886b22086258b5e, 0x8ba8fe9e8582d431, 0xcc0284772e652b05, 0x7f2cc8c92dc2746a,
0x325b15e575e1c3d0, 0x8175595b76469cbf, 0xc6df23b2dda1638b, 0x75f16f0cde063ce4,
0x498bd6618a6e9de3, 0xfaa59adf89c9c28c, 0xbd0fe036222e3db8, 0x0e21ac88218962d7,
0xc5fa92ec8aff7fb6, 0x76d4de52895820d9, 0x317ea4bb22bfdfed, 0x8250e80521188082,
0xbe2a516875702185, 0x0d041dd676d77eea, 0x4aae673fdd3081de, 0xf9802b81de97deb1,
0x4fc0b4dd24d2a599, 0xfceef8632775faf6, 0xbb44828a8c9205c2, 0x086ace348f355aad,
0x34107759db5dfbaa, 0x873e3be7d8faa4c5, 0xc094410e731d5bf1, 0x73ba0db070ba049e,
0xb86133d4dbcc19ff, 0x0b4f7f6ad86b4690, 0x4ce50583738cb9a4, 0xffcb493d702be6cb,
0xc3b1f050244347cc, 0x709fbcee27e418a3, 0x3735c6078c03e797, 0x841b8ab98fa4b8f8,
0xadda7c5f3c4488e3, 0x1ef430e13fe3d78c, 0x595e4a08940428b8, 0xea7006b697a377d7,
0xd60abfdbc3cbd6d0, 0x6524f365c06c89bf, 0x228e898c6b8b768b, 0x91a0c532682c29e4,
0x5a7bfb56c35a3485, 0xe955b7e8c0fd6bea, 0xaeffcd016b1a94de, 0x1dd181bf68bdcbb1,
0x21ab38d23cd56ab6, 0x9285746c3f7235d9, 0xd52f0e859495caed, 0x6601423b97329582,
0xd041dd676d77eeaa, 0x636f91d96ed0b1c5, 0x24c5eb30c5374ef1, 0x97eba78ec690119e,
0xab911ee392f8b099, 0x18bf525d915feff6, 0x5f1528b43ab810c2, 0xec3b640a391f4fad,
0x27e05a6e926952cc, 0x94ce16d091ce0da3, 0xd3646c393a29f297, 0x604a2087398eadf8,
0x5c3099ea6de60cff, 0xef1ed5546e415390, 0xa8b4afbdc5a6aca4, 0x1b9ae303c601f3cb,
0x56ed3e2f9e224471, 0xe5c372919d851b1e, 0xa26908783662e42a, 0x114744c635c5bb45,
0x2d3dfdab61ad1a42, 0x9e13b115620a452d, 0xd9b9cbfcc9edba19, 0x6a978742ca4ae576,
0xa14cb926613cf817, 0x1262f598629ba778, 0x55c88f71c97c584c, 0xe6e6c3cfcadb0723,
0xda9c7aa29eb3a624, 0x69b2361c9d14f94b, 0x2e184cf536f3067f, 0x9d36004b35545910,
0x2b769f17cf112238, 0x9858d3a9ccb67d57, 0xdff2a94067518263, 0x6cdce5fe64f6dd0c,
0x50a65c93309e7c0b, 0xe388102d33392364, 0xa4226ac498dedc50, 0x170c267a9b79833f,
0xdcd7181e300f9e5e, 0x6ff954a033a8c131, 0x28532e49984f3e05, 0x9b7d62f79be8616a,
0xa707db9acf80c06d, 0x14299724cc279f02, 0x5383edcd67c06036, 0xe0ada17364673f59
};
uint64_t crc64_update(uint64_t crc, const unsigned char *s, uint64_t l) {
crc = ~crc;
while (l != 0) {
crc = crc64_table[*s++ ^ (crc & 0xFF)] ^ (crc >> 8);
--l;
}
return ~crc;
}
typedef struct {
char host[HTTP_DOWNLOAD_HOST_MAX_LEN];
char path[HTTP_DOWNLOAD_PATH_MAX_LEN];
uint16_t port;
file_save_func_t save_func;
void *userdata;
uint32_t range_start;
uint32_t range_end;
int32_t https_enable;
/* 运行时参数 */
void *http_handle;
uint32_t size_fetched;
uint32_t content_len;
int32_t http_rsp_status_code;
int32_t result;
uint64_t content_crc64;
uint64_t calc_crc64;
} http_download_t;
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* 解析URL, 从中解出来host, path */
static int32_t _download_parse_url(const char *url, char *host, uint32_t max_host_len, char *path,
uint32_t max_path_len)
{
char *host_ptr = (char *) strstr(url, "://");
uint32_t host_len = 0;
uint32_t path_len;
char *path_ptr;
char *fragment_ptr;
if (host_ptr == NULL) {
return HTTP_DOWNLOAD_ERR_URL_INVALID;
}
host_ptr += 3;
path_ptr = strchr(host_ptr, '/');
if (NULL == path_ptr) {
return HTTP_DOWNLOAD_ERR_URL_INVALID;
}
if (host_len == 0) {
host_len = path_ptr - host_ptr;
}
memcpy(host, host_ptr, host_len);
host[host_len] = '\0';
fragment_ptr = strchr(host_ptr, '#');
if (fragment_ptr != NULL) {
path_len = fragment_ptr - path_ptr;
} else {
path_len = strlen(path_ptr);
}
memcpy(path, path_ptr, path_len);
path[path_len] = '\0';
return STATE_SUCCESS;
}
static http_download_t *_http_download_init()
{
http_download_t *download = g_aiot_sysdep_portfile.core_sysdep_malloc(sizeof(http_download_t), "");
if(download == NULL) {
return download;
}
memset(download, 0, sizeof(http_download_t));
download->port = HTTP_DOWNLOAD_DEFAULT_TLS_PORT;
download->result = STATE_SUCCESS;
download->https_enable = 0;
return download;
}
static void _http_download_deinit(http_download_t *download)
{
if(download->http_handle != NULL) {
core_http_deinit(&download->http_handle);
}
g_aiot_sysdep_portfile.core_sysdep_free(download);
}
/* 对于收到的http报文进行处理的回调函数, 内部处理完后再调用用户的回调函数 */
void _http_download_recv_handler(void *handle, const aiot_http_recv_t *packet, void *userdata)
{
http_download_t *download = (http_download_t *)userdata;
if (NULL == download || NULL == packet) {
return;
}
switch (packet->type) {
case AIOT_HTTPRECV_STATUS_CODE : {
download->http_rsp_status_code = packet->data.status_code.code;
}
break;
case AIOT_HTTPRECV_HEADER: {
if ((strlen(packet->data.header.key) == strlen("Content-Length")) &&
(memcmp(packet->data.header.key, "Content-Length", strlen(packet->data.header.key)) == 0)) {
uint32_t size = 0;
/* 在用户指定的range并非全部固件的情况下, content_len < size_total, 所以不能简单替换 */
core_str2uint(packet->data.header.value, (uint8_t)strlen(packet->data.header.value), &size);
/* 该字段表示用户期望总共下载多少字节. 如果字段为0, 表示未设置过, 则设置为总共的字节数 */
download->content_len = (download->content_len > 0) ? download->content_len : size;
}
/* 只有整个文件下载触发校验 */
if(download->range_start == 0 && download->range_end == 0) {
if ((strlen(packet->data.header.key) == strlen("x-oss-hash-crc64ecma")) &&
(memcmp(packet->data.header.key, "x-oss-hash-crc64ecma", strlen(packet->data.header.key)) == 0)) {
core_str2uint64(packet->data.header.value, (uint8_t)strlen(packet->data.header.value), &download->content_crc64);
download->calc_crc64 = 0;
}
}
}
break;
case AIOT_HTTPRECV_BODY: {
if (HTTP_DOWNLOAD_RESPONSE_OK != download->http_rsp_status_code
/* HTTP回复报文的code应该是200或者206, 否则这个下载链接不可用 */
&& HTTP_DOWNLOAD_RESPONSE_PARTIAL != download->http_rsp_status_code) {
download->result = HTTP_DOWNLOAD_ERR_FETCH_FAILED;
core_log(NULL, 0, "wrong http respond code\r\n");
} else if (0 == download->content_len) {
/* HTTP回复报文的header里面应该有Content-Length, 否则这个下载链接为trunked编码, 不可用 */
download->result = HTTP_DOWNLOAD_ERR_FETCH_FAILED;
core_log(NULL, 0, "wrong http respond header\r\n");
} else {
/* 正常的固件的报文 */
/* 在按照多个range分片下载的情况下, 判断用户下载到的固件的累计大小是否超过了整体的值 */
if (download->size_fetched > download->content_len) {
core_log(NULL, 0, "downloaded exceeds expected\r\n");
break;
}
if(download->result >= 0) {
int32_t offset = download->size_fetched + download->range_start;
download->save_func(offset, packet->data.body.buffer, packet->data.body.len, download->userdata);
}
/* 该字段表示累计下载了多少字节, 不区分range */
download->size_fetched += packet->data.body.len;
/* 计算digest, 如果下载完成, 还要看看是否与云端计算出来的一致 */
download->calc_crc64 = crc64_update(download->calc_crc64, packet->data.body.buffer, packet->data.body.len);
if (download->size_fetched == download->content_len) {
/* 不需要校验或者校验成功 */
if(download->content_crc64 == 0) {
/* 不需要校验 */
download->result = STATE_SUCCESS;
}
else if(download->content_crc64 == download->calc_crc64) {
download->result = STATE_SUCCESS;
core_log(NULL, 0, "crc64 matched\r\n");
} else {
download->result = HTTP_DOWNLOAD_ERR_CHECKSUM_MISMATCH;
core_log(NULL, 0, "crc64 mismatch\r\n");
}
}
}
}
break;
default:
break;
}
}
static int32_t _http_download_connect(http_download_t *download)
{
int32_t res = STATE_SUCCESS;
uint32_t default_body_max_len = HTTP_DOWNLOAD_BUFFER_LEN;
uint32_t default_timeout_ms = HTTP_DOWNLOAD_RECV_TIMEOUT_MS;
void *http_handle = core_http_init();
if(http_handle == NULL) {
return STATE_PORT_MALLOC_FAILED;
}
aiot_sysdep_network_cred_t cred;
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
if(download->https_enable == 0) {
download->port = 80;
} else {
download->port = 443;
cred.option = AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA; /* 使用RSA证书校验MQTT服务端 */
cred.max_tls_fragment = 16384; /* 最大的分片长度为16K, 其它可选值还有4K, 2K, 1K, 0.5K */
cred.sni_enabled = 1; /* TLS建连时, 支持Server Name Indicator */
cred.x509_server_cert = ali_ca_cert; /* 用来验证MQTT服务端的RSA根证书 */
cred.x509_server_cert_len = strlen(ali_ca_cert); /* 用来验证MQTT服务端的RSA根证书长度 */
}
if ((STATE_SUCCESS != core_http_setopt(http_handle, CORE_HTTPOPT_RECV_HANDLER, _http_download_recv_handler)) ||
(STATE_SUCCESS != core_http_setopt(http_handle, CORE_HTTPOPT_USERDATA, (void *)download)) ||
(STATE_SUCCESS != core_http_setopt(http_handle, CORE_HTTPOPT_BODY_BUFFER_MAX_LEN, (void *)&default_body_max_len)) ||
(STATE_SUCCESS != core_http_setopt(http_handle, CORE_HTTPOPT_NETWORK_CRED, (void *)&cred)) ||
(STATE_SUCCESS != core_http_setopt(http_handle, CORE_HTTPOPT_HOST, (void *)download->host)) ||
(STATE_SUCCESS != core_http_setopt(http_handle, CORE_HTTPOPT_PORT, (void *)&download->port)) ||
(STATE_SUCCESS != core_http_setopt(http_handle, CORE_HTTPOPT_RECV_TIMEOUT_MS, (void *)&default_timeout_ms))) {
core_http_deinit(&http_handle);
return STATE_PORT_MALLOC_FAILED;
}
res = core_http_connect(http_handle);
if (res != STATE_SUCCESS) {
core_http_deinit(&http_handle);
return res;
}
download->http_handle = http_handle;
return res;
}
static char header[1024];
static int32_t _http_download_send_request(http_download_t *download)
{
int32_t pos = 0;
core_http_request_t request = {
.method = "GET",
.path = download->path,
.header = header,
.content = NULL,
.content_len = 0
};
memset(header, 0, sizeof(header));
pos += sprintf(header + pos, "Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8\r\n");
if(download->range_end != 0) {
pos += sprintf(header + pos, "Range: bytes=%d-%d\r\n", download->range_start, download->range_end);
} else {
pos += sprintf(header + pos, "Range: bytes=%d-\r\n", download->range_start);
}
if(pos < 0) {
return HTTP_DOWNLOAD_ERR_UNKOWN;
}
return core_http_send(download->http_handle, &request);
}
static int32_t _http_download_recv(http_download_t *download)
{
int32_t res = STATE_SUCCESS;
while(1) {
res = core_http_recv(download->http_handle);
if(res < 0) {
break;
}
}
if(res == STATE_HTTP_READ_BODY_FINISHED) {
res = STATE_SUCCESS;
}
return res;
}
int32_t core_http_download_request(char *url, http_download_params_t *extra_params, file_save_func_t save_func, void* userdata)
{
int32_t res = STATE_SUCCESS;
http_download_t *download = NULL;
if(url == NULL || save_func == NULL) {
return -1;
}
download = _http_download_init();
if(download == NULL) {
return -1;
}
res = _download_parse_url(url, download->host, HTTP_DOWNLOAD_HOST_MAX_LEN, download->path,
HTTP_DOWNLOAD_PATH_MAX_LEN);
if (res != STATE_SUCCESS) {
_http_download_deinit(download);
return HTTP_DOWNLOAD_ERR_URL_INVALID;
}
if(extra_params != NULL) {
download->range_start = extra_params->range_start;
download->range_end = extra_params->range_end;
download->https_enable = extra_params->https_enable;
}
download->save_func = save_func;
download->userdata = userdata;
/* 建立连接 */
res = _http_download_connect(download);
if(res < STATE_SUCCESS) {
_http_download_deinit(download);
return HTTP_DOWNLOAD_ERR_URL_INVALID;
}
/* 发送请求 */
res = _http_download_send_request(download);
if(res != STATE_SUCCESS) {
_http_download_deinit(download);
return res;
}
/* 接收数据 */
res = _http_download_recv(download);
res = download->result;
_http_download_deinit(download);
return res;
}

View File

@@ -0,0 +1,54 @@
#ifndef _HTTP_DOWNLOAD_H_
#define _HTTP_DOWNLOAD_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdint.h>
#define HTTP_DOWNLOAD_ERR_URL_INVALID -0x8001
#define HTTP_DOWNLOAD_ERR_UNKOWN -0x8002
#define HTTP_DOWNLOAD_ERR_FETCH_FAILED -0x8003
#define HTTP_DOWNLOAD_ERR_CHECKSUM_MISMATCH -0x8004
/**
* @brief 文件下载的额外参数
*/
typedef struct {
/* 文件区间下载,起始偏移地址, 默认为0 */
uint32_t range_start;
/* 文件区间下载终止偏移地址如若不设置置为0即可 */
uint32_t range_end;
/* 1: 使用https下载 0:使用http下载默认为0 */
int32_t https_enable;
} http_download_params_t;
/**
* @brief 文件数据保存回调函数类型定义
* @details
* 文件正常保存时,返回写入的数据长度
* 文件保存异常时,返回-1, 会中断下载流程
*/
typedef int32_t (*file_save_func_t)(uint32_t offset, uint8_t *data, uint32_t data_len, void *userdata);
/**
* @brief http下载文件
*
* @param[in] url 文件下载地址
* @param[in] extra_params 其它扩展参数没有时可填NULL
* @param[in] save_func 文件数据保存回调函数
* @param[in] userdata 执行回调时返回的用户指针
*
* @return http_download_result_t
* @retval <HTTP_DOWNLOAD_SUCCESS 执行失败
* @retval HTTP_DOWNLOAD_SUCCESS 执行成功
*/
int32_t core_http_download_request(char *url, http_download_params_t *extra_params, file_save_func_t save_func, void* userdata);
#if defined(__cplusplus)
}
#endif
#endif /* #ifndef _CORE_HTTP_H_ */

View File

@@ -0,0 +1,166 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "speech_trans.h"
#include "aiot_sysdep_api.h"
#include "core_string.h"
/* 系统默认的数字语音路径 */
static const char *sys_tone_number[] = {
"SYS_TONE_0",
"SYS_TONE_1",
"SYS_TONE_2",
"SYS_TONE_3",
"SYS_TONE_4",
"SYS_TONE_5",
"SYS_TONE_6",
"SYS_TONE_7",
"SYS_TONE_8",
"SYS_TONE_9",
};
/* 系统默认的单元路径*/
static const char *sys_tone_measure[] = {
"SYS_TONE_MONETARY_fen",
"SYS_TONE_MONETARY_jiao",
"SYS_TONE_MONETARY_yuan",
"SYS_TONE_MEASURE_WORD_shi",
"SYS_TONE_MEASURE_WORD_bai",
"SYS_TONE_MEASURE_WORD_qian",
"SYS_TONE_MEASURE_WORD_wan",
"SYS_TONE_MEASURE_WORD_yi",
};
/* 语料名字转换成文件添加进链表 */
void name_to_speech(const char *value, char *format, struct core_list_head *list)
{
aiot_sysdep_portfile_t *sysdep = NULL;
play_node_t *node = NULL;
char *src[] = { (char *)value, format };
sysdep = aiot_sysdep_get_portfile();
if(sysdep == NULL) {
return;
}
node = sysdep->core_sysdep_malloc(sizeof(play_node_t), "");
if (node == NULL) {
return;
}
memset(node, 0, sizeof(play_node_t));
CORE_INIT_LIST_HEAD(&node->linked_node);
core_sprintf(sysdep, &node->filename, "%s.%s", src, sizeof(src) / sizeof(char *), "");
if (node->filename == NULL) {
sysdep->core_sysdep_free(node);
return;
}
printf("add name %s\r\n", node->filename);
core_list_add_tail(&node->linked_node, list);
}
/* 转换整形数字 */
void play_integer_number(uint64_t integer, char *format, struct core_list_head *list)
{
uint64_t quotient = 0, remain = 0;
if(integer >= 100000000ul) {
quotient = integer / 100000000ul;
remain = integer % 100000000ul;
play_integer_number(quotient, format, list);
name_to_speech(sys_tone_measure[7], format, list);
if(remain > 0 && 100000000ul / remain > 10) {
name_to_speech(sys_tone_number[0], format, list);
}
play_integer_number(remain, format, list);
} else if (integer >= 10000ul) {
quotient = integer / 10000ul;
remain = integer % 10000ul;
play_integer_number(quotient, format, list);
name_to_speech(sys_tone_measure[6], format, list);
if(remain > 0 && 10000ul / remain > 10) {
name_to_speech(sys_tone_number[0], format, list);
}
play_integer_number(remain, format, list);
} else if (integer >= 1000ul) {
quotient = integer / 1000ul;
remain = integer % 1000ul;
play_integer_number(quotient, format, list);
name_to_speech(sys_tone_measure[5], format, list);
if(remain > 0 && 1000ul / remain > 10) {
name_to_speech(sys_tone_number[0], format, list);
}
play_integer_number(remain, format, list);
} else if (integer >= 100ul) {
quotient = integer / 100ul;
remain = integer % 100ul;
play_integer_number(quotient, format, list);
name_to_speech(sys_tone_measure[4], format, list);
if(remain > 0 && 100ul / remain > 10) {
name_to_speech(sys_tone_number[0], format, list);
}
play_integer_number(remain, format, list);
} else if (integer >= 10ul) {
quotient = integer / 10ul;
remain = integer % 10ul;
play_integer_number(quotient, format, list);
name_to_speech(sys_tone_measure[3], format, list);
play_integer_number(remain, format, list);
} else {
quotient = integer % 10ul;
if(quotient > 0) {
name_to_speech(sys_tone_number[quotient], format, list);
}
}
}
/* 将金钱转换成语料文件添加进链表 */
void money_to_speech(const char *value, char *format, struct core_list_head *list)
{
uint64_t integer = 0;
uint32_t decimal = 0;
uint32_t tmp_int = 0;
char *dot = NULL, *end = NULL;
end = strstr(value, "}");
if(end == NULL){
return;
}
dot = strstr(value, ".");
if(dot != NULL){
core_str2uint64((char *)value, dot - value, &integer);
} else {
core_str2uint64((char *)value, end - value, &integer);
}
/*读取分数:分*/
if(dot != NULL && dot[1] >= '0' && dot[1] <= '9') {
decimal = (dot[1] - '0') * 10;
if(dot[2] >= '0' && dot[2] <= '9') {
decimal += (dot[2] - '0');
}
}
//元
if(integer > 0) {
play_integer_number(integer, format, list);
name_to_speech(sys_tone_measure[2], format, list);
}
if(decimal > 0) {
//角
tmp_int = decimal / 10;
if(tmp_int > 0) {
name_to_speech(sys_tone_number[tmp_int], format, list);
name_to_speech(sys_tone_measure[1], format, list);
} else if(integer > 0) {
name_to_speech(sys_tone_number[0], format, list);
}
//分
tmp_int = decimal % 10;
if(tmp_int > 0) {
name_to_speech(sys_tone_number[tmp_int], format, list);
name_to_speech(sys_tone_measure[0], format, list);
}
}
}

View File

@@ -0,0 +1,17 @@
#ifndef __SPEECH_TRANS_H__
#define __SPEECH_TRANS_H__
#include <stdio.h>
#include "core_list.h"
typedef struct {
char *filename;
struct core_list_head linked_node;
} play_node_t;
/* 语料名字转换成文件添加进链表 */
void name_to_speech(const char *value, char *format, struct core_list_head *list);
/* 将金钱转换成语料文件添加进链表 */
void money_to_speech(const char *value, char *format, struct core_list_head *list);
#endif

View File

@@ -0,0 +1,3 @@
Name: 设备日志模块
LOGPOST Component for Link SDK V4.0.0

View File

@@ -0,0 +1,458 @@
/**
* @file aiot_logpost_api.c
* @brief logpost模块的API接口实现, 提供设备端日志上云的能力
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#include "logpost_private.h"
#include "core_string.h"
#include "core_log.h"
#include "core_mqtt.h"
#include "core_global.h"
#include "aiot_mqtt_api.h"
#define LOGPOST_JSON_KEY_MODE "mode"
const char *logpost_loglevel[] = {
"FATAL",
"ERROR",
"WARN",
"INFO",
"DEBUG",
};
static void _logpost_config_data_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata)
{
logpost_handle_t *logpost_handle = (logpost_handle_t *)userdata;
aiot_logpost_event_t event;
char *value = NULL;
uint32_t value_len = 0;
uint32_t log_switch = 0;
/* construct event message */
memset(&event, 0, sizeof(aiot_logpost_event_t));
event.type = AIOT_LOGPOSTEVT_CONFIG_DATA;
core_log(logpost_handle->sysdep, STATE_LOGPOST_LOG_RECV, "LOGPOST user log config arrived\r\n");
if ((core_json_value((char *)msg->data.pub.payload, msg->data.pub.payload_len,
LOGPOST_JSON_KEY_MODE, strlen(LOGPOST_JSON_KEY_MODE), &value, &value_len)) < 0 ||
(core_str2uint(value, value_len, &log_switch)) < 0) {
core_log(logpost_handle->sysdep, SATAE_LOGPOST_LOG_PARSE_MSG_FAILED, "LOGPOST parse log config failed\r\n");
return;
}
/* update log config */
logpost_handle->user_log_switch = log_switch;
/* invoke user callback */
if (logpost_handle->event_handler != NULL) {
event.data.config_data.on_off = log_switch;
logpost_handle->event_handler(logpost_handle, &event, logpost_handle->userdata);
}
}
static void _logpost_get_config(logpost_handle_t *logpost_handle)
{
char *topic = NULL;
char *payload = NULL;
char *fmt = "{\"id\":\"%s\",\"version\":\"1.0\",\"params\":{\"getType\":\"content\",\"configScope\":\"device\"}}";
int32_t res = STATE_SUCCESS;
int32_t id = 0;
char *pk = NULL, *dn = NULL;
char id_string[11] = { 0 };
if (NULL == logpost_handle || NULL == logpost_handle->mqtt_handle) {
return;
}
if (NULL == (pk = core_mqtt_get_product_key(logpost_handle->mqtt_handle))) {
return;
}
if (NULL == (dn = core_mqtt_get_device_name(logpost_handle->mqtt_handle))) {
return;
}
core_global_alink_id_next(logpost_handle->sysdep, &id);
core_int2str(id, id_string, NULL);
/* construct topic */
{
char *src[2] = { pk, dn };
res = core_sprintf(logpost_handle->sysdep, &topic, LOGPOST_CONFIG_GET_TOPIC_FMT, src, sizeof(src) / sizeof(char *),
LOGPOST_MODULE_NAME);
if (NULL == topic) {
return;
}
}
/* construct payload */
{
char *src[1] = { id_string };
res = core_sprintf(logpost_handle->sysdep, &payload, fmt,
src, sizeof(src) / sizeof(char *), LOGPOST_MODULE_NAME);
if (res < STATE_SUCCESS) {
logpost_handle->sysdep->core_sysdep_free(topic);
return;
}
}
res = aiot_mqtt_pub(logpost_handle->mqtt_handle, topic, (uint8_t *)payload, (uint32_t)strlen(payload), 0);
logpost_handle->sysdep->core_sysdep_free(topic);
logpost_handle->sysdep->core_sysdep_free(payload);
}
int32_t _logpost_send_nwkstats_rtt(logpost_handle_t *handle)
{
int32_t res = STATE_SUCCESS;
aiot_logpost_msg_t msg;
core_mqtt_nwkstats_info_t nwkstats_info;
char *content_fmt = NWKSTAT_RTT_INFO_FMT;
char timestamp_str[22] = {0};
char rtt_str[22] = {0};
char *content = NULL;
char *content_src[] = {timestamp_str, rtt_str};
core_mqtt_get_nwkstats(handle->mqtt_handle, &nwkstats_info);
core_uint642str(nwkstats_info.rtt, rtt_str, NULL);
core_uint642str(handle->sysdep->core_sysdep_time(), timestamp_str, NULL);
res = core_sprintf(handle->sysdep, &content, content_fmt, content_src, 2, LOGPOST_MODULE_NAME);
if (res < 0) {
return res;
}
memset(&msg, 0, sizeof(aiot_logpost_msg_t));
msg.timestamp = 0; /* 单位为ms的时间戳, 填写0则SDK将使用当前的时间戳 */
msg.loglevel = AIOT_LOGPOST_LEVEL_INFO; /* 日志级别 */
msg.module_name = NWKSTAT_NET_RT; /* 日志对应的模块 */
msg.code = 200; /* 状态码 */
msg.msg_id = 0;
msg.content = content; /* 日志内容 */
res = aiot_logpost_send(handle, &msg);
handle->sysdep->core_sysdep_free(content);
return res;
}
int32_t _logpost_send_nwkstats_conn(logpost_handle_t *handle)
{
int32_t res = STATE_SUCCESS;
aiot_logpost_msg_t msg;
core_mqtt_nwkstats_info_t nwkstats_info;
char *content_fmt = NULL;
char success_time[22] = {0};
char conn_time_str[22] = {0};
char failed_time[22] = {0};
char conn_code[12] = {0};
char *content = NULL;
char *content_src[] = {success_time, NULL, conn_time_str, failed_time, NULL, conn_code};
uint8_t content_src_cnt = 0;
core_mqtt_get_nwkstats(handle->mqtt_handle, &nwkstats_info);
if (nwkstats_info.connect_time_used == 0) {
return res;
}
core_uint642str(nwkstats_info.connect_timestamp, success_time, NULL);
content_src[1] = (nwkstats_info.network_type == 0) ? "TCP" : "TLS";
content_src[4] = (nwkstats_info.network_type == 0) ? "TCP" : "TLS";
core_uint642str(nwkstats_info.connect_time_used, conn_time_str, NULL);
core_uint642str(nwkstats_info.failed_timestamp, failed_time, NULL);
core_int2str(nwkstats_info.failed_error_code, conn_code, NULL);
/* check if connect failure happened */
if (nwkstats_info.failed_error_code != 0) {
content_fmt = NWKSTAT_CONN_INFO_FMT2;
content_src_cnt = sizeof(content_src) / sizeof(content_src[0]);
} else {
content_fmt = NWKSTAT_CONN_INFO_FMT;
content_src_cnt = 3;
}
res = core_sprintf(handle->sysdep, &content, content_fmt, content_src, content_src_cnt, LOGPOST_MODULE_NAME);
if (res < 0) {
return res;
}
memset(&msg, 0, sizeof(aiot_logpost_msg_t));
msg.timestamp = 0; /* 单位为ms的时间戳, 填写0则SDK将使用当前的时间戳 */
msg.loglevel = AIOT_LOGPOST_LEVEL_INFO; /* 日志级别 */
msg.module_name = NWKSTAT_NET_CONN; /* 日志对应的模块 */
msg.code = 200; /* 状态码 */
msg.msg_id = 0;
msg.content = content; /* 日志内容 */
res = aiot_logpost_send(handle, &msg);
handle->sysdep->core_sysdep_free(content);
return res;
}
int32_t _should_report_sys_log(logpost_handle_t *logpost_handle, char *module_name)
{
int result = 0;
if (0 == logpost_handle->sys_log_switch) {
return result;
}
if (0 == memcmp(NWKSTAT_NET_CONN, module_name, strlen(NWKSTAT_NET_CONN)) ||
0 == memcmp(NWKSTAT_NET_RT, module_name, strlen(NWKSTAT_NET_RT))) {
result = 1;
core_log(logpost_handle->sysdep, STATE_LOGPOST_LOG_RECV,
"sys log config is on, toggle it using AIOT_LOGPOSTOPT_SYS_LOG.\r\n");
}
return result;
}
void _logpost_process_handler(void *context, aiot_mqtt_event_t *event, core_mqtt_event_t *core_event)
{
logpost_handle_t *logpost_handle = (logpost_handle_t *)context;
if (core_event != NULL) {
switch (core_event->type) {
case CORE_MQTTEVT_DEINIT: {
logpost_handle->mqtt_handle = NULL;
return;
}
default: {
}
break;
}
}
if (NULL == context || NULL == event) {
if (logpost_handle->sys_log_switch == 0) {
return;
}
_logpost_send_nwkstats_conn(logpost_handle);
if ((logpost_handle->sysdep->core_sysdep_time() - logpost_handle->last_post_time) \
> LOGPOST_NWKSTATS_POST_INTERVAL) {
logpost_handle->last_post_time = logpost_handle->sysdep->core_sysdep_time();
_logpost_send_nwkstats_rtt(logpost_handle);
}
return;
}
if (event->type == AIOT_MQTTEVT_CONNECT) {
_logpost_get_config(logpost_handle);
}
}
void *aiot_logpost_init(void)
{
aiot_sysdep_portfile_t *sysdep = aiot_sysdep_get_portfile();
logpost_handle_t *logpost_handle = NULL;
if (NULL == sysdep) {
return NULL;
}
logpost_handle = sysdep->core_sysdep_malloc(sizeof(logpost_handle_t), LOGPOST_MODULE_NAME);
if (NULL == logpost_handle) {
return NULL;
}
memset(logpost_handle, 0, sizeof(logpost_handle_t));
logpost_handle->sysdep = sysdep;
logpost_handle->user_log_switch = LOGPOST_DEFAULT_LOG_ONOFF;
logpost_handle->sys_log_switch = LOGPOST_DEFAULT_LOG_ONOFF;
core_global_init(sysdep);
return logpost_handle;
}
int32_t aiot_logpost_setopt(void *handle, aiot_logpost_option_t option, void *data)
{
logpost_handle_t *logpost_handle;
int32_t res = STATE_SUCCESS;
if (NULL == handle || NULL == data) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (option >= AIOT_LOGPOSTOPT_MAX) {
return STATE_USER_INPUT_OUT_RANGE;
}
logpost_handle = (logpost_handle_t *)handle;
switch (option) {
case AIOT_LOGPOSTOPT_MQTT_HANDLE: {
aiot_mqtt_topic_map_t topic_mapping;
logpost_handle->mqtt_handle = data;
core_mqtt_process_data_t process_data;
/* setup mqtt topic mapping */
memset(&topic_mapping, 0, sizeof(aiot_mqtt_topic_map_t));
topic_mapping.topic = LOGPOST_CONFIG_PUSH_TOPIC;
topic_mapping.handler = _logpost_config_data_handler;
topic_mapping.userdata = handle;
res = aiot_mqtt_setopt(data, AIOT_MQTTOPT_APPEND_TOPIC_MAP, &topic_mapping);
if (res < 0) {
break;
}
topic_mapping.topic = LOGPOST_CONFIG_GET_REPLY_TOPIC;
res = aiot_mqtt_setopt(data, AIOT_MQTTOPT_APPEND_TOPIC_MAP, &topic_mapping);
if (res < 0) {
break;
}
/* setup mqtt process handler */
memset(&process_data, 0, sizeof(core_mqtt_process_data_t));
process_data.handler = _logpost_process_handler;
process_data.context = logpost_handle;
res = core_mqtt_setopt(data, CORE_MQTTOPT_APPEND_PROCESS_HANDLER, &process_data);
}
break;
case AIOT_LOGPOSTOPT_EVENT_HANDLER: {
logpost_handle->event_handler = (aiot_logpost_event_handler_t)data;
}
break;
case AIOT_LOGPOSTOPT_USERDATA: {
logpost_handle->userdata = data;
}
break;
case AIOT_LOGPOSTOPT_SYS_LOG: {
logpost_handle->sys_log_switch = *(uint8_t *)data;
}
break;
default:
break;
}
return res;
}
int32_t aiot_logpost_send(void *handle, aiot_logpost_msg_t *msg)
{
logpost_handle_t *logpost_handle = NULL;
char *topic = NULL;
char *payload = NULL;
char *fmt = "{\"id\":\"%s\",\"version\":\"1.0\",\"params\":" \
"[{\"utcTime\":\"%s\",\"logLevel\":\"%s\",\"module\":\"%s\",\"code\":\"%s\",\"traceContext\":\"%s\",\"logContent\":\"%s\"}]}";
char *pk = NULL, *dn = NULL;
int32_t id = 0;
int32_t res = STATE_SUCCESS;
if (NULL == handle || NULL == msg) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (msg->loglevel > AIOT_LOGPOST_LEVEL_DEBUG) {
return STATE_LOGPOST_LOGLEVEL_ERROR;
}
if (NULL == msg->module_name) {
return STATE_LOGPOST_LOG_MODULE_NAME_IS_NULL;
}
if (NULL == msg->content) {
return STATE_LOGPOST_LOG_CONTENT_IS_NULL;
}
if (strlen(msg->content) > LOGPOST_CONTENT_MAXIMUM_LEN) {
return STATE_LOGPOST_LOG_CONTENT_TOO_LONG;
}
if (msg->timestamp > 100000000000000) {
return STATE_USER_INPUT_OUT_RANGE;
}
logpost_handle = (logpost_handle_t *)handle;
if (NULL == logpost_handle->mqtt_handle) {
return STATE_LOGPOST_MQTT_HANDLE_IS_NULL;
}
if (NULL == (pk = core_mqtt_get_product_key(logpost_handle->mqtt_handle))) {
return STATE_USER_INPUT_MISSING_PRODUCT_KEY;
}
if (NULL == (dn = core_mqtt_get_device_name(logpost_handle->mqtt_handle))) {
return STATE_USER_INPUT_MISSING_DEVICE_NAME;
}
if (1 != _should_report_sys_log(logpost_handle, msg->module_name) && logpost_handle->user_log_switch == 0) {
return STATE_LOGPOST_POST_TURN_OFF;
}
/* construct topic */
{
char *src[2] = {pk, dn};
res = core_sprintf(logpost_handle->sysdep, &topic, LOGPOST_POST_TOPIC_FMT, src, sizeof(src) / sizeof(char *),
LOGPOST_MODULE_NAME);
if (NULL == topic) {
return res;
}
}
/* construct payload */
{
char id_string[11] = { 0 };
char utc[32] = { 0 };
char code[11] = { 0 };
char msg_id[22] = { 0 };
const char *src[] = { id_string, utc, logpost_loglevel[msg->loglevel], msg->module_name, code, msg_id, msg->content };
core_global_alink_id_next(logpost_handle->sysdep, &id);
core_int2str(id, id_string, NULL);
if (msg->timestamp == 0) {
_core_log_append_date(logpost_handle->sysdep, core_log_get_timestamp(logpost_handle->sysdep), utc);
} else {
_core_log_append_date(logpost_handle->sysdep, msg->timestamp, utc);
}
core_int2str(msg->code, code, NULL);
core_uint642str(msg->msg_id, msg_id, NULL);
res = core_sprintf(logpost_handle->sysdep, &payload, fmt,
(char **)src, sizeof(src) / sizeof(char *), LOGPOST_MODULE_NAME);
if (res < STATE_SUCCESS) {
logpost_handle->sysdep->core_sysdep_free(topic);
return res;
}
}
res = aiot_mqtt_pub(logpost_handle->mqtt_handle, topic, (uint8_t *)payload, (uint32_t)strlen(payload), 0);
logpost_handle->sysdep->core_sysdep_free(topic);
logpost_handle->sysdep->core_sysdep_free(payload);
if (res >= STATE_SUCCESS) {
res = id;
}
return res;
}
int32_t aiot_logpost_deinit(void **p_handle)
{
logpost_handle_t *logpost_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
aiot_mqtt_topic_map_t topic_mapping;
core_mqtt_process_data_t process_data;
if (NULL == p_handle || NULL == *p_handle) {
return STATE_USER_INPUT_NULL_POINTER;
}
logpost_handle = *p_handle;
sysdep = logpost_handle->sysdep;
*p_handle = NULL;
/* remove mqtt precess handler */
memset(&process_data, 0, sizeof(core_mqtt_process_data_t));
process_data.handler = _logpost_process_handler;
core_mqtt_setopt(logpost_handle->mqtt_handle, CORE_MQTTOPT_REMOVE_PROCESS_HANDLER, &process_data);
/* remove mqtt topic mapping */
memset(&topic_mapping, 0, sizeof(aiot_mqtt_topic_map_t));
topic_mapping.topic = LOGPOST_CONFIG_PUSH_TOPIC;
topic_mapping.handler = _logpost_config_data_handler;
aiot_mqtt_setopt(logpost_handle->mqtt_handle, AIOT_MQTTOPT_REMOVE_TOPIC_MAP, &topic_mapping);
topic_mapping.topic = LOGPOST_CONFIG_GET_REPLY_TOPIC;
aiot_mqtt_setopt(logpost_handle->mqtt_handle, AIOT_MQTTOPT_REMOVE_TOPIC_MAP, &topic_mapping);
sysdep->core_sysdep_free(logpost_handle);
core_global_deinit(sysdep);
return STATE_SUCCESS;
}

View File

@@ -0,0 +1,268 @@
/**
* @file aiot_logpost_api.h
* @brief logpost模块头文件, 提供设备端日志上云的能力
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
* @details
*
* 1. 在使用日志上云模块前, 用户应首先创建好一个MQTT实例
*
* 2. 调用`aiot_logpost_init`创建一个日志上云实例, 保存实例句柄
*
* 3. 调用`aiot_logpost_setopt`配置`AIOT_LOGPOSTOPT_MQTT_HANDLE`选项以设置MQTT句柄, 此选项为强制配置选项
*
* 4. 调用`aiot_logpost_setopt`配置`AIOT_LOGPOSTOPT_EVENT_HANDLER`和`AIOT_LOGPOSTOPT_USER_DATA`选项以注册事件接收回调函数和用户上下文数据指针
*
* 5. 在使用`aiot_logpost_send`发送日志消息前, 应先完成MQTT实例的建连
*
*/
#ifndef __AIOT_LOGPOST_API_H__
#define __AIOT_LOGPOST_API_H__
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdint.h>
/**
* @brief -0x1500~-0x15FF表达SDK在logpost模块内的状态码
*/
#define STATE_LOGPOST_BASE (-0x1500)
/**
* @brief 用户未调用@ref aiot_logpost_setopt 配置MQTT句柄
*/
#define STATE_LOGPOST_MQTT_HANDLE_IS_NULL (-0x1501)
/**
* @brief 日志上报被云端配置为关闭状态
*/
#define STATE_LOGPOST_POST_TURN_OFF (-0x1502)
/**
* @brief 日志消息的日志级别有误
*/
#define STATE_LOGPOST_LOGLEVEL_ERROR (-0x1503)
/**
* @brief 日志消息的模块名称字段为NULL
*/
#define STATE_LOGPOST_LOG_MODULE_NAME_IS_NULL (-0x1504)
/**
* @brief 日志消息的日志内容字段为NULL
*/
#define STATE_LOGPOST_LOG_CONTENT_IS_NULL (-0x1505)
/**
* @brief 日志消息的日志内容字段字符串长度大于4096个字节
*/
#define STATE_LOGPOST_LOG_CONTENT_TOO_LONG (-0x1506)
/**
* @brief 接收到服务器下行消息时的内部日志状态码
*/
#define STATE_LOGPOST_LOG_RECV (-0x1507)
/**
* @brief 解析服务器下行消息失败时的内部日志状态码
*/
#define SATAE_LOGPOST_LOG_PARSE_MSG_FAILED (-0x1508)
/**
* @brief @ref aiot_logpost_setopt 接口的option参数可选值.
*
* @details 下文每个选项中的数据类型, 指的是@ref aiot_logpost_setopt 中, data参数的数据类型
*/
typedef enum {
/**
* @brief 模块依赖的MQTT句柄
*
* @details
*
* LOGPOST模块依赖底层的MQTT模块, 用户必需配置正确的MQTT句柄, 否则无法正常工作, 数据类型为(void *)
*/
AIOT_LOGPOSTOPT_MQTT_HANDLE,
/**
* @brief 设置回调, 它在SDK收到网络报文的时候被调用, 告知用户, 数据类型为(aiot_logpost_recv_handler_t)
*/
AIOT_LOGPOSTOPT_EVENT_HANDLER,
/**
* @brief 用户需要SDK暂存的上下文, 数据类型为(void *)
*
* @details 这个上下文指针会在 AIOT_LOGPOSTOPT_RECV_HANDLER 设置的回调被调用时, 由SDK传给用户
*/
AIOT_LOGPOSTOPT_USERDATA,
/**
* @brief 系统日志的开关.
*
* @detail 设置为1表示要上报系统日志, 设置为0表示不上报系统日志. 这里的系统日志是指建连耗时和网络延时
*/
AIOT_LOGPOSTOPT_SYS_LOG,
/**
* @brief 配置选项数量最大值, 不可用作配置参数
*/
AIOT_LOGPOSTOPT_MAX,
} aiot_logpost_option_t;
/**
* @brief 日志级别枚举类型定义
*/
typedef enum {
AIOT_LOGPOST_LEVEL_FATAL,
AIOT_LOGPOST_LEVEL_ERR,
AIOT_LOGPOST_LEVEL_WARN,
AIOT_LOGPOST_LEVEL_INFO,
AIOT_LOGPOST_LEVEL_DEBUG,
} aiot_logpost_level_t;
/**
* @brief 日志数据结构体定义
*
*/
typedef struct {
/**
* @brief utc时间戳, 单位为ms, 此数值会直接展示在云端控制台设备日志页面
*/
uint64_t timestamp;
/**
* @brief 日志级别, 请查看@ref aiot_logpost_level_t 定义
*/
aiot_logpost_level_t loglevel;
/**
* @brief 模块名称, <b>必须为以结束符'\0'结尾的字符串</b>
*/
char *module_name;
/**
* @brief 状态码, 可用于标识日志对应的状态
*/
int32_t code;
/**
* @brief 消息标示符, 用于标识云端下行消息, 可从data-module模块的消息接收回调函数中获得对应的标识符, 如果用户设置为0, 此字段将不上传。
*/
uint64_t msg_id;
/**
* @brief 日志内容, <b>必须为以结束符'\0'结尾的字符串</b>
*/
char *content;
} aiot_logpost_msg_t;
/**
* @brief logpost模块内部发生值得用户关注的状态变化时, 通知用户的事件类型
*/
typedef enum {
/**
* @brief 接受到云端下发的日志配置数据
*/
AIOT_LOGPOSTEVT_CONFIG_DATA,
} aiot_logpost_event_type_t;
/**
* @brief logpost模块内部发生值得用户关注的状态变化时, 通知用户的事件内容
*/
typedef struct {
/**
* @brief 事件内容所对应的事件类型, 更多信息请参考@ref aiot_logpost_event_type_t
*/
aiot_logpost_event_type_t type;
union {
/**
* @brief 日志配置数据结构体
*/
struct {
/**
* @brief 日志开关状态, 0: 关闭日志上传; 1: 打开日志上传
*/
uint8_t on_off;
} config_data;
} data;
} aiot_logpost_event_t;
/**
* @brief logpost模块内部发生值得用户关注的状态变化时, 通知用户所调用的事件回调函数
*
* @param[in] handle logpost会话句柄
* @param[in] event logpost模块中发生的事件的内容
* @param[in] userdata 指向用户上下文数据的指针, 这个指针由用户通过调用@ref aiot_logpost_setopt 配置@ref AIOT_LOGPOSTOPT_USERDATA 选项设置
*
* @return void
*/
typedef void (*aiot_logpost_event_handler_t)(void *handle,
const aiot_logpost_event_t *event, void *userdata);
/**
* @brief 创建logpost会话实例, 并以默认值配置会话参数
*
* @return void *
* @retval 非NULL logpost实例的句柄
* @retval NULL 初始化失败, 一般是内存分配失败导致
*/
void *aiot_logpost_init(void);
/**
* @brief 配置logpost会话
*
* @param[in] handle logpost会话句柄
* @param[in] option 配置选项, 更多信息请参考@ref aiot_logpost_option_t
* @param[in] data 配置选项数据, 更多信息请参考@ref aiot_logpost_option_t
*
* @return int32_t
* @retval <STATE_SUCCESS 参数配置失败
* @retval STATE_SUCCESS 参数配置成功
* @retval STATE_USER_INPUT_NULL_POINTER 入参handle或data为NULL
* @retval STATE_USER_INPUT_OUT_RANGE 入参optioin的枚举值>=AIOT_LOGPOSTOPT_MAX
* @retval others 参考@ref aiot_state_api.h
*/
int32_t aiot_logpost_setopt(void *handle, aiot_logpost_option_t option, void *data);
/**
* @brief 向服务器发送日志消息
*
* @param[in] handle logpost会话句柄
* @param[in] msg 消息结构体, 可指定日志对应模块, 日志级别等, 更多信息请参考@ref aiot_logpost_msg_t
*
* @return int32_t
* @retval STATE_SUCCESS 请求发送成功
* @retval STATE_USER_INPUT_NULL_POINTER 入参<i>handle</i>或<i>msg</i>为NULL
* @retval STATE_SYS_DEPEND_MALLOC_FAILED 内存分配失败
* @retval STATE_LOGPOST_MQTT_HANDLE_IS_NULL 用户未调用@ref aiot_logpost_setopt 配置MQTT句柄
* @retval others 参考@ref aiot_state_api.h 或@ref STATE_SHADOW_BASE 中对应的错误码说明
*
*/
int32_t aiot_logpost_send(void *handle, aiot_logpost_msg_t *msg);
/**
* @brief 结束logpost会话, 销毁实例并回收资源
*
* @param[in] handle 指向logpost会话句柄的指针
*
* @return int32_t
* @retval <STATE_SUCCESS 执行失败
* @retval >=STATE_SUCCESS 执行成功
*/
int32_t aiot_logpost_deinit(void **handle);
#if defined(__cplusplus)
}
#endif
#endif /* __AIOT_LOGPOST_API_H__ */

View File

@@ -0,0 +1,64 @@
/**
* @file logpost_private.h
* @brief logpost模块内部的宏定义和数据结构声明, 不面向其它模块, 更不面向用户
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#ifndef __LOGPOST_PRIVATE_H__
#define __LOGPOST_PRIVATE_H__
#if defined(__cplusplus)
extern "C" {
#endif
#include "core_stdinc.h"
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
#include "aiot_logpost_api.h"
/* logpost模块内部的会话句柄结构体, SDK用户不可见, 只能得到void *handle类型的指针 */
typedef struct {
aiot_sysdep_portfile_t *sysdep;
void *mqtt_handle;
uint8_t user_log_switch;
uint8_t sys_log_switch;
aiot_logpost_event_handler_t event_handler;
void *userdata;
/* network info stats */
uint64_t last_post_time;
} logpost_handle_t;
#define LOGPOST_MODULE_NAME "logpost" /* 用于内存统计的模块名字符串 */
#define LOGPOST_DEFAULT_LOG_ONOFF (0)
#define LOGPOST_CONTENT_MAXIMUM_LEN (4096)
#define LOGPOST_NWKSTATS_POST_INTERVAL (1800000)
/* 上下行topic定义 */
#define LOGPOST_POST_TOPIC_FMT "/sys/%s/%s/thing/log/post"
#define LOGPOST_CONFIG_GET_TOPIC_FMT "/sys/%s/%s/thing/config/log/get"
#define LOGPOST_CONFIG_PUSH_TOPIC "/sys/+/+/thing/config/log/push"
#define LOGPOST_CONFIG_GET_REPLY_TOPIC "/sys/+/+/thing/config/log/get_reply"
#define NWKSTAT_RTT_INFO_FMT "time=%s^rtt=%s"
#define NWKSTAT_CONN_INFO_FMT "time=%s^conn_type=%s^conn_cost=%s^conn_ret=0"
#define NWKSTAT_CONN_INFO_FMT2 "time=%s^conn_type=%s^conn_cost=%s^conn_ret=0,time=%s^conn_type=%s^conn_cost=0^conn_ret=%s"
#define NWKSTAT_NET_RT "net_rt"
#define NWKSTAT_NET_CONN "net_conn"
#if defined(__cplusplus)
}
#endif
#endif /* __LOGPOST_PRIVATE_H__ */

View File

@@ -0,0 +1,2 @@
Name: 时间同步模块
NTP Component for Link SDK V4.0.0

View File

@@ -0,0 +1,342 @@
/**
* @file aiot_ntp_api.c
* @brief ntp模块的API接口实现, 提供获取utc时间的能力
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
/* TODO: 对本模块的头文件, 仅需包含ntp_private.h, 不需包含aiot_ntp_api.h */
#include "ntp_private.h"
/* TODO: 列出对core模块需要包含的头文件 */
#include "core_string.h"
#include "core_log.h"
#include "core_global.h"
#include "core_mqtt.h"
static void _core_ntp_exec_inc(ntp_handle_t *ntp_handle)
{
ntp_handle->sysdep->core_sysdep_mutex_lock(ntp_handle->data_mutex);
ntp_handle->exec_count++;
ntp_handle->sysdep->core_sysdep_mutex_unlock(ntp_handle->data_mutex);
}
static void _core_ntp_exec_dec(ntp_handle_t *ntp_handle)
{
ntp_handle->sysdep->core_sysdep_mutex_lock(ntp_handle->data_mutex);
ntp_handle->exec_count--;
ntp_handle->sysdep->core_sysdep_mutex_unlock(ntp_handle->data_mutex);
}
static void _ntp_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata)
{
ntp_handle_t *ntp_handle = (ntp_handle_t *)userdata;
switch (packet->type) {
case AIOT_MQTTRECV_PUB: {
char *dst_key = "deviceSendTime", *srt_key = "serverRecvTime", *sst_key = "serverSendTime";
char *dst_value = NULL, *srt_value = NULL, *sst_value = NULL;
uint32_t dst_value_len = 0, srt_value_len = 0, sst_value_len = 0;
uint64_t dst = 0, srt = 0, sst = 0, utc = 0;
if (core_json_value((char *)packet->data.pub.payload, packet->data.pub.payload_len, dst_key, (uint32_t)strlen(dst_key),
&dst_value, &dst_value_len) == STATE_SUCCESS &&
core_json_value((char *)packet->data.pub.payload, packet->data.pub.payload_len, srt_key, (uint32_t)strlen(srt_key),
&srt_value, &srt_value_len) == STATE_SUCCESS &&
core_json_value((char *)packet->data.pub.payload, packet->data.pub.payload_len, sst_key, (uint32_t)strlen(sst_key),
&sst_value, &sst_value_len) == STATE_SUCCESS) {
if (core_str2uint64(dst_value, (uint8_t)dst_value_len, &dst) == STATE_SUCCESS &&
core_str2uint64(srt_value, (uint8_t)srt_value_len, &srt) == STATE_SUCCESS &&
core_str2uint64(sst_value, (uint8_t)sst_value_len, &sst) == STATE_SUCCESS) {
core_date_t date;
utc = (srt + sst + ntp_handle->sysdep->core_sysdep_time() - dst) / 2;
core_log_set_timestamp(ntp_handle->sysdep, utc);
memset(&date, 0, sizeof(core_date_t));
core_utc2date(utc, ntp_handle->time_zone, &date);
if (ntp_handle->recv_handler != NULL) {
aiot_ntp_recv_t recv;
memset(&recv, 0, sizeof(aiot_ntp_recv_t));
recv.type = AIOT_NTPRECV_LOCAL_TIME;
recv.data.local_time.timestamp = utc;
recv.data.local_time.year = date.year;
recv.data.local_time.mon = date.mon;
recv.data.local_time.day = date.day;
recv.data.local_time.hour = date.hour;
recv.data.local_time.min = date.min;
recv.data.local_time.sec = date.sec;
recv.data.local_time.msec = date.msec;
ntp_handle->recv_handler(ntp_handle, &recv, ntp_handle->userdata);
}
} else {
if (ntp_handle->event_handler != NULL) {
aiot_ntp_event_t event;
memset(&event, 0, sizeof(aiot_ntp_event_t));
event.type = AIOT_NTPEVT_INVALID_TIME_FORMAT;
ntp_handle->event_handler(ntp_handle, &event, ntp_handle->userdata);
}
}
} else {
if (ntp_handle->event_handler != NULL) {
aiot_ntp_event_t event;
memset(&event, 0, sizeof(aiot_ntp_event_t));
event.type = AIOT_NTPEVT_INVALID_RESPONSE;
ntp_handle->event_handler(ntp_handle, &event, ntp_handle->userdata);
}
}
}
default: {
}
break;
}
}
static int32_t _ntp_operate_topic_map(ntp_handle_t *ntp_handle, aiot_mqtt_option_t option)
{
int32_t res = STATE_SUCCESS;
aiot_mqtt_topic_map_t map;
char *topic = NULL;
char *topic_src[] = { core_mqtt_get_product_key(ntp_handle->mqtt_handle), core_mqtt_get_device_name(ntp_handle->mqtt_handle) };
char *topic_fmt = NTP_RESPONSE_TOPIC_FMT;
memset(&map, 0, sizeof(aiot_mqtt_topic_map_t));
res = core_sprintf(ntp_handle->sysdep, &topic, topic_fmt, topic_src, sizeof(topic_src) / sizeof(char *),
NTP_MODULE_NAME);
if (res < STATE_SUCCESS) {
return res;
}
map.topic = topic;
map.handler = _ntp_recv_handler;
map.userdata = (void *)ntp_handle;
res = aiot_mqtt_setopt(ntp_handle->mqtt_handle, option, &map);
ntp_handle->sysdep->core_sysdep_free(topic);
return res;
}
static void _ntp_core_mqtt_process_handler(void *context, aiot_mqtt_event_t *event, core_mqtt_event_t *core_event)
{
ntp_handle_t *ntp_handle = (ntp_handle_t *)context;
if (core_event != NULL) {
switch (core_event->type) {
case CORE_MQTTEVT_DEINIT: {
ntp_handle->mqtt_handle = NULL;
return;
}
default: {
}
break;
}
}
}
static int32_t _ntp_core_mqtt_operate_process_handler(ntp_handle_t *ntp_handle, core_mqtt_option_t option)
{
core_mqtt_process_data_t process_data;
memset(&process_data, 0, sizeof(core_mqtt_process_data_t));
process_data.handler = _ntp_core_mqtt_process_handler;
process_data.context = ntp_handle;
return core_mqtt_setopt(ntp_handle->mqtt_handle, option, &process_data);
}
void *aiot_ntp_init(void)
{
ntp_handle_t *ntp_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
sysdep = aiot_sysdep_get_portfile();
if (sysdep == NULL) {
return NULL;
}
ntp_handle = sysdep->core_sysdep_malloc(sizeof(ntp_handle_t), NTP_MODULE_NAME);
if (ntp_handle == NULL) {
return NULL;
}
memset(ntp_handle, 0, sizeof(ntp_handle_t));
ntp_handle->sysdep = sysdep;
ntp_handle->deinit_timeout_ms = NTP_DEFAULT_DEINIT_TIMEOUT_MS;
ntp_handle->data_mutex = sysdep->core_sysdep_mutex_init();
ntp_handle->exec_enabled = 1;
return ntp_handle;
}
int32_t aiot_ntp_setopt(void *handle, aiot_ntp_option_t option, void *data)
{
int32_t res = STATE_SUCCESS;
ntp_handle_t *ntp_handle = (ntp_handle_t *)handle;
if (handle == NULL || data == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (option >= AIOT_NTPOPT_MAX) {
return STATE_USER_INPUT_OUT_RANGE;
}
if (ntp_handle->exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
_core_ntp_exec_inc(ntp_handle);
ntp_handle->sysdep->core_sysdep_mutex_lock(ntp_handle->data_mutex);
switch (option) {
case AIOT_NTPOPT_MQTT_HANDLE: {
ntp_handle->mqtt_handle = data;
ntp_handle->sysdep->core_sysdep_mutex_unlock(ntp_handle->data_mutex);
res = _ntp_operate_topic_map(ntp_handle, AIOT_MQTTOPT_APPEND_TOPIC_MAP);
if (res >= STATE_SUCCESS) {
res = _ntp_core_mqtt_operate_process_handler(ntp_handle, CORE_MQTTOPT_APPEND_PROCESS_HANDLER);
}
ntp_handle->sysdep->core_sysdep_mutex_lock(ntp_handle->data_mutex);
}
break;
case AIOT_NTPOPT_TIME_ZONE: {
ntp_handle->time_zone = *(int8_t *)data;
}
break;
case AIOT_NTPOPT_RECV_HANDLER: {
ntp_handle->recv_handler = (aiot_ntp_recv_handler_t)data;
}
break;
case AIOT_NTPOPT_EVENT_HANDLER: {
ntp_handle->event_handler = (aiot_ntp_event_handler_t)data;
}
break;
case AIOT_NTPOPT_USERDATA: {
ntp_handle->userdata = data;
}
break;
case AIOT_NTPOPT_DEINIT_TIMEOUT_MS: {
ntp_handle->deinit_timeout_ms = *(uint32_t *)data;
}
break;
default: {
res = STATE_USER_INPUT_UNKNOWN_OPTION;
}
break;
}
ntp_handle->sysdep->core_sysdep_mutex_unlock(ntp_handle->data_mutex);
_core_ntp_exec_dec(ntp_handle);
return res;
}
int32_t aiot_ntp_deinit(void **handle)
{
uint64_t deinit_timestart = 0;
ntp_handle_t *ntp_handle = NULL;
if (handle == NULL || *handle == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
ntp_handle = *(ntp_handle_t **)handle;
if (ntp_handle->exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
ntp_handle->exec_enabled = 0;
_ntp_core_mqtt_operate_process_handler(ntp_handle, CORE_MQTTOPT_REMOVE_PROCESS_HANDLER);
_ntp_operate_topic_map(ntp_handle, AIOT_MQTTOPT_REMOVE_TOPIC_MAP);
deinit_timestart = ntp_handle->sysdep->core_sysdep_time();
do {
if (ntp_handle->exec_count == 0) {
break;
}
ntp_handle->sysdep->core_sysdep_sleep(NTP_DEINIT_INTERVAL_MS);
} while ((ntp_handle->sysdep->core_sysdep_time() - deinit_timestart) < ntp_handle->deinit_timeout_ms);
if (ntp_handle->exec_count != 0) {
return STATE_MQTT_DEINIT_TIMEOUT;
}
*handle = NULL;
ntp_handle->sysdep->core_sysdep_mutex_deinit(&ntp_handle->data_mutex);
ntp_handle->sysdep->core_sysdep_free(ntp_handle);
return STATE_SUCCESS;
}
int32_t aiot_ntp_send_request(void *handle)
{
int32_t res = STATE_SUCCESS;
char *topic = NULL, *payload = NULL;
ntp_handle_t *ntp_handle = (ntp_handle_t *)handle;
if (handle == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (ntp_handle->mqtt_handle == NULL) {
return STATE_NTP_MISSING_MQTT_HANDLE;
}
if (ntp_handle->exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
_core_ntp_exec_inc(ntp_handle);
{
char *topic_src[] = { core_mqtt_get_product_key(ntp_handle->mqtt_handle), core_mqtt_get_device_name(ntp_handle->mqtt_handle) };
char *topic_fmt = NTP_REQUEST_TOPIC_FMT;
char time_str[21] = {0};
char *payload_src[] = { time_str };
char *payload_fmt = NTP_REQUEST_PAYLOAD_FMT;
res = core_sprintf(ntp_handle->sysdep, &topic, topic_fmt, topic_src, sizeof(topic_src) / sizeof(char *),
NTP_MODULE_NAME);
if (res < STATE_SUCCESS) {
_core_ntp_exec_dec(ntp_handle);
return res;
}
core_uint642str(ntp_handle->sysdep->core_sysdep_time(), time_str, NULL);
res = core_sprintf(ntp_handle->sysdep, &payload, payload_fmt, payload_src, sizeof(payload_src) / sizeof(char *),
NTP_MODULE_NAME);
if (res < STATE_SUCCESS) {
ntp_handle->sysdep->core_sysdep_free(topic);
_core_ntp_exec_dec(ntp_handle);
return res;
}
}
res = aiot_mqtt_pub(ntp_handle->mqtt_handle, topic, (uint8_t *)payload, (uint32_t)strlen(payload), 0);
ntp_handle->sysdep->core_sysdep_free(topic);
ntp_handle->sysdep->core_sysdep_free(payload);
if (res < STATE_SUCCESS) {
_core_ntp_exec_dec(ntp_handle);
return res;
}
_core_ntp_exec_dec(ntp_handle);
return STATE_SUCCESS;
}

View File

@@ -0,0 +1,264 @@
/**
* @file aiot_ntp_api.h
* @brief ntp模块头文件, 提供获取utc时间的能力
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
* @details
*
* NTP模块用于从阿里云物联网平台上获取UTC时间, API的使用流程如下:
*
* 1. 首先参考 @ref aiot_mqtt_api.h 的说明, 保证成功建立与物联网平台的`MQTT`连接
*
* 2. 调用 @ref aiot_ntp_init 初始化ntp会话, 获取会话句柄
*
* 3. 调用 @ref aiot_ntp_setopt 配置NTP会话的参数, 常用配置项见 @ref aiot_ntp_setopt 的说明
*
* 4. 调用 @ref aiot_ntp_send_request 发送NTP请求
*
* 5. 收到的UTC时间经SDK处理后会调用由 @ref aiot_ntp_setopt 配置的 @ref AIOT_NTPOPT_RECV_HANDLER 回调函数, 通知用户当前的时间
*
*/
#ifndef __AIOT_NTP_API_H__
#define __AIOT_NTP_API_H__
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdint.h>
/**
* @brief -0x1100~-0x11FF表达SDK在ntp模块内的状态码
*/
#define STATE_NTP_BASE (-0x1100)
/**
* @brief MQTT会话句柄未设置, 请通过 @ref aiot_ntp_setopt 设置MQTT会话句柄
*/
#define STATE_NTP_MISSING_MQTT_HANDLE (-0x1101)
/**
* @brief ntp模块收到从网络上来的报文时, 通知用户的报文类型
*/
typedef enum {
AIOT_NTPRECV_LOCAL_TIME
} aiot_ntp_recv_type_t;
/**
* @brief ntp模块收到从网络上来的报文时, 通知用户的报文内容
*/
typedef struct {
/**
* @brief 报文内容所对应的报文类型, 更多信息请参考@ref aiot_ntp_recv_type_t
*/
aiot_ntp_recv_type_t type;
union {
/**
* @brief utc事件戳以及时区换算后的日期, 以 @ref AIOT_NTPOPT_TIME_ZONE 设置的时区为准
*/
struct {
uint64_t timestamp;
uint32_t year;
uint32_t mon;
uint32_t day;
uint32_t hour;
uint32_t min;
uint32_t sec;
uint32_t msec;
} local_time;
} data;
} aiot_ntp_recv_t;
/**
* @brief ntp模块收到从网络上来的报文时, 通知用户所调用的数据回调函数
*
* @param[in] handle ntp会话句柄
* @param[in] packet ntp消息结构体, 存放收到的ntp报文内容
* @param[in] userdata 用户上下文
*
* @return void
*/
typedef void (* aiot_ntp_recv_handler_t)(void *handle,
const aiot_ntp_recv_t *packet, void *userdata);
/**
* @brief ntp内部事件类型
*/
typedef enum {
/**
* @brief 收到的ntp应答中字段不合法
*/
AIOT_NTPEVT_INVALID_RESPONSE,
/**
* @brief 收到的ntp应答中时间字段格式错误
*/
AIOT_NTPEVT_INVALID_TIME_FORMAT,
} aiot_ntp_event_type_t;
/**
* @brief NTP内部事件
*/
typedef struct {
/**
* @brief NTP内部事件类型. 更多信息请参考@ref aiot_ntp_event_type_t
*
*/
aiot_ntp_event_type_t type;
} aiot_ntp_event_t;
/**
* @brief ntp事件回调函数
*
* @details
*
* 当NTP内部事件被触发时, 调用此函数
*
*/
typedef void (*aiot_ntp_event_handler_t)(void *handle, const aiot_ntp_event_t *event, void *userdata);
/**
* @brief @ref aiot_ntp_setopt 接口的option参数可选值.
*
* @details 下文每个选项中的数据类型, 指的是@ref aiot_ntp_setopt 中, data参数的数据类型
*
* 1. data的数据类型是char *时, 以配置@ref AIOT_NTPOPT_MQTT_HANDLE 为例:
*
* void *mqtt_handle = aiot_mqtt_init();
* aiot_ntp_setopt(ntp_handle, AIOT_NTPOPT_MQTT_HANDLE, mqtt_handle);
*
* 2. data的数据类型是其他数据类型时, 以配置@ref AIOT_NTPOPT_TIME_ZONE 为例:
*
* int8_t time_zone = 8;
* aiot_mqtt_setopt(ntp_handle, AIOT_NTPOPT_TIME_ZONE, (void *)&time_zone);
*/
typedef enum {
/**
* @brief ntp会话 需要的MQTT句柄, 需要先建立MQTT连接, 再设置MQTT句柄
*
* @details
*
* 数据类型: (void *)
*/
AIOT_NTPOPT_MQTT_HANDLE,
/**
* @brief ntp会话 获取到utc时间后会根据此时区值转换成本地时间, 再通过 @ref aiot_ntp_recv_handler_t 通知
*
* @details
*
* 取值示例: 东8区, 取值为8; 西3区, 取值为-3
*
* 数据类型: (int8_t *)
*/
AIOT_NTPOPT_TIME_ZONE,
/**
* @brief 设置回调, 它在SDK收到网络报文的时候被调用, 告知用户
*
* @details
*
* 数据类型: ( @ref aiot_ntp_recv_handler_t )
*/
AIOT_NTPOPT_RECV_HANDLER,
/**
* @brief ntp内部发生的事件会从此回调函数进行通知
*
* @details
*
* 数据类型: ( @ref aiot_ntp_event_handler_t )
*/
AIOT_NTPOPT_EVENT_HANDLER,
/**
* @brief 用户需要SDK暂存的上下文
*
* @details 这个上下文指针会在 AIOT_NTPOPT_RECV_HANDLER 和 AIOT_NTPOPT_EVENT_HANDLER 设置的回调被调用时, 由SDK传给用户
*
* 数据类型: (void *)
*/
AIOT_NTPOPT_USERDATA,
/**
* @brief 销毁ntp实例时, 等待其他api执行完毕的时间
*
* @details
*
* 当调用@ref aiot_ntp_deinit 销毁NTP实例时, 若继续调用其他aiot_ntp_xxx API, API会返回@ref STATE_USER_INPUT_EXEC_DISABLED 错误
*
* 此时, 用户应该停止调用其他aiot_ntp_xxx API
*
* 数据类型: (uint32_t *) 默认值: (2 * 1000) ms
*/
AIOT_NTPOPT_DEINIT_TIMEOUT_MS,
AIOT_NTPOPT_MAX
} aiot_ntp_option_t;
/**
* @brief 创建ntp会话实例, 并以默认值配置会话参数
*
* @return void *
* @retval 非NULL ntp实例的句柄
* @retval NULL 初始化失败, 一般是内存分配失败导致
*
*/
void *aiot_ntp_init(void);
/**
* @brief 配置ntp会话
*
* @details
*
* 常见的配置项如下
*
* + `AIOT_NTPOPT_MQTT_HANDLE`: 已建立连接的MQTT会话句柄
*
* + `AIOT_NTPOPT_TIME_ZONE`: 时区设置, SDK会将收到的UTC时间按配置的时区进行转换
*
* + `AIOT_NTPOPT_RECV_HANDLER`: 时间数据接收回调函数, SDK将UTC时间转换完成后, 通过此回调函数输出
*
* @param[in] handle ntp会话句柄
* @param[in] option 配置选项, 更多信息请参考@ref aiot_ntp_option_t
* @param[in] data 配置选项数据, 更多信息请参考@ref aiot_ntp_option_t
*
* @return int32_t
* @retval <STATE_SUCCESS 参数配置失败
* @retval >=STATE_SUCCESS 参数配置成功
*
*/
int32_t aiot_ntp_setopt(void *handle, aiot_ntp_option_t option, void *data);
/**
* @brief 结束ntp会话, 销毁实例并回收资源
*
* @param[in] handle 指向ntp会话句柄的指针
*
* @return int32_t
* @retval <STATE_SUCCESS 执行失败
* @retval >=STATE_SUCCESS 执行成功
*
*/
int32_t aiot_ntp_deinit(void **handle);
/**
* @brief 向ntp服务器发送ntp消息请求
*
* @details
*
* 发送NTP请求, 然后SDK会调用通过 @ref aiot_ntp_setopt 配置的 @ref AIOT_NTPOPT_RECV_HANDLER 回调函数, 通知用户当前的时间
*
* @param handle ntp会话句柄
*
* @return int32_t
* @retval <STATE_SUCCESS 请求发送失败
* @retval >=STATE_SUCCESS 请求发送成功
*/
int32_t aiot_ntp_send_request(void *handle);
#if defined(__cplusplus)
}
#endif
#endif /* __AIOT_NTP_API_H__ */

View File

@@ -0,0 +1,58 @@
/**
* @file ntp_private.h
* @brief ntp模块内部的宏定义和数据结构声明, 不面向其它模块, 更不面向用户
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#ifndef __NTP_PRIVATE_H__
#define __NTP_PRIVATE_H__
#if defined(__cplusplus)
extern "C" {
#endif
/* 用这种方式包含标准C库的头文件 */
#include "core_stdinc.h"
/* TODO: 这一段列出需要包含SDK其它模块头文件, 与上一段落以1个空行隔开 */
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_ntp_api.h" /* 内部头文件是用户可见头文件的超集 */
/* TODO: 定义ntp模块内部的会话句柄结构体, SDK用户不可见, 只能得到void *handle类型的指针 */
typedef struct {
aiot_sysdep_portfile_t *sysdep; /* 底层依赖回调合集的引用指针 */
void *mqtt_handle;
int8_t time_zone;
uint32_t deinit_timeout_ms;
aiot_ntp_recv_handler_t recv_handler; /* 组件从协议栈读到内容时, 通知用户的回调 */
aiot_ntp_event_handler_t event_handler;
void *userdata; /* 组件调用以上2个 ntp_handler 时的入参之一 */
/*---- 以上都是用户在API可配 ----*/
void *data_mutex;
uint8_t exec_enabled;
uint32_t exec_count;
} ntp_handle_t;
#define NTP_MODULE_NAME "ntp" /* 用于内存统计的模块名字符串 */
#define NTP_DEFAULT_DEINIT_TIMEOUT_MS (2 * 1000)
#define NTP_DEFAULT_TIME_ZONE (0)
#define NTP_REQUEST_TOPIC_FMT "/ext/ntp/%s/%s/request"
#define NTP_REQUEST_PAYLOAD_FMT "{\"deviceSendTime\":\"%s\"}"
#define NTP_RESPONSE_TOPIC_FMT "/ext/ntp/%s/%s/response"
#define NTP_DEINIT_INTERVAL_MS (100)
#if defined(__cplusplus)
}
#endif
#endif /* __NTP_PRIVATE_H__ */

View File

@@ -0,0 +1,3 @@
Name: 固件升级模块
OTA Component for Link SDK V4.0.0

View File

@@ -0,0 +1,650 @@
/**
* @file aiot_mqtt_download_api.c
* @brief mqtt文件下载的实现文件
* @date 2019-12-27
*
* @copyright Copyright (C) 2015-2018 Alibaba Group Holding Limited
*
*/
#include "aiot_ota_api.h"
#include "core_mqtt.h"
#include "core_string.h"
#include "core_sha256.h"
#include "ota_md5.h"
#include "core_log.h"
#include "core_global.h"
#include "mqtt_download_private.h"
#include "aiot_mqtt_download_api.h"
#include "ota_private.h"
#include "core_string.h"
static int32_t _md_send_request(void *handle);
static void _md_recv_data_reply_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata);
/*
* CRC lookup table for bytes, generating polynomial is 0x8005
* input: reflexed (LSB first)
* output: reflexed also...
*/
const uint16_t crc_ibm_table[256] = {
0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040,
};
uint16_t crc_ibm(uint8_t const *buffer, size_t len)
{
uint16_t crc = 0x0000;
uint8_t lut;
while (len--) {
lut = (crc ^ (*buffer++)) & 0xFF;
crc = (crc >> 8) ^ crc_ibm_table[lut];
}
return crc;
}
int32_t _md_sub_response_topic(void *handle)
{
char topic[128];
mqtt_download_handle_t *md_handle = (mqtt_download_handle_t *)handle;
if(handle == NULL) {
return STATE_DOWNLOAD_DEINIT_HANDLE_IS_NULL;
}
memset(topic, 0, sizeof(topic));
snprintf(topic, sizeof(topic) - 1, MQTT_DOWNLOAD_RESPONSE_TOPIC,
core_mqtt_get_product_key(md_handle->task_desc->mqtt_handle), core_mqtt_get_device_name(md_handle->task_desc->mqtt_handle));
return aiot_mqtt_sub(md_handle->task_desc->mqtt_handle, topic, _md_recv_data_reply_handler, 0, handle);
}
int32_t _md_unsub_response_topic(void *handle)
{
char topic[128];
mqtt_download_handle_t *md_handle = (mqtt_download_handle_t *)handle;
if(handle == NULL) {
return STATE_DOWNLOAD_DEINIT_HANDLE_IS_NULL;
}
memset(topic, 0, sizeof(topic));
snprintf(topic, sizeof(topic) - 1, MQTT_DOWNLOAD_RESPONSE_TOPIC,
core_mqtt_get_product_key(md_handle->task_desc->mqtt_handle), core_mqtt_get_device_name(md_handle->task_desc->mqtt_handle));
return aiot_mqtt_unsub(md_handle->task_desc->mqtt_handle, topic);
}
void *aiot_mqtt_download_init(void)
{
mqtt_download_handle_t *md_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
sysdep = aiot_sysdep_get_portfile();
if (sysdep == NULL) {
return NULL;
}
md_handle = sysdep->core_sysdep_malloc(sizeof(mqtt_download_handle_t), MQTT_DOWNLOAD_MODULE_NAME);
if (md_handle == NULL) {
return NULL;
}
memset(md_handle, 0, sizeof(mqtt_download_handle_t));
md_handle->status = STATE_MQTT_DOWNLOAD_INIT;
md_handle->sysdep = sysdep;
md_handle->request_size = MQTT_DOWNLOAD_DEFAULT_REQUEST_SIZE;
md_handle->last_request_time = 0;
md_handle->data_mutex = sysdep->core_sysdep_mutex_init();
md_handle->recv_mutex = sysdep->core_sysdep_mutex_init();
return md_handle;
}
static void _md_core_mqtt_process_handler(void *context, aiot_mqtt_event_t *event, core_mqtt_event_t *core_event)
{
mqtt_download_handle_t *md_handle = (mqtt_download_handle_t *)context;
if (core_event != NULL) {
switch (core_event->type) {
case CORE_MQTTEVT_DEINIT: {
md_handle->task_desc->mqtt_handle = NULL;
return;
}
default: {
}
break;
}
}
}
int32_t aiot_mqtt_download_deinit(void **handle)
{
int32_t res = STATE_SUCCESS;
if (NULL == handle || NULL == *handle) {
return STATE_DOWNLOAD_DEINIT_HANDLE_IS_NULL;
}
mqtt_download_handle_t *md_handle = *(mqtt_download_handle_t **)handle;
if (NULL != md_handle->task_desc) {
if (AIOT_OTA_DIGEST_MD5 == md_handle->task_desc->digest_method) {
if (NULL != md_handle->digest_ctx) {
utils_md5_free(md_handle->digest_ctx);
md_handle->sysdep->core_sysdep_free(md_handle->digest_ctx);
md_handle->digest_ctx = NULL;
}
}
if(md_handle->task_desc->mqtt_handle != NULL) {
core_mqtt_process_data_t process_data;
memset(&process_data, 0, sizeof(core_mqtt_process_data_t));
process_data.handler = _md_core_mqtt_process_handler;
process_data.context = handle;
core_mqtt_setopt(md_handle->task_desc->mqtt_handle, CORE_MQTTOPT_REMOVE_PROCESS_HANDLER, &process_data);
_md_unsub_response_topic(md_handle);
}
_download_deep_free_task_desc(md_handle->sysdep, md_handle->task_desc);
md_handle->sysdep->core_sysdep_free(md_handle->task_desc);
md_handle->task_desc = NULL;
}
md_handle->sysdep->core_sysdep_mutex_deinit(&md_handle->data_mutex);
md_handle->sysdep->core_sysdep_mutex_deinit(&md_handle->recv_mutex);
md_handle->sysdep->core_sysdep_free(md_handle);
*handle = NULL;
return res;
}
/* 根据下载到的固件的内容, 计算其digest值 */
static int32_t _download_digest_update(mqtt_download_handle_t *download_handle, uint8_t *buffer, uint32_t buffer_len)
{
int32_t res = STATE_SUCCESS;
if (AIOT_OTA_DIGEST_SHA256 == download_handle->task_desc->digest_method) {
core_sha256_update(download_handle->digest_ctx, buffer, buffer_len);
} else if (AIOT_OTA_DIGEST_MD5 == download_handle->task_desc->digest_method) {
res = utils_md5_update(download_handle->digest_ctx, buffer, buffer_len);
}
return res;
}
/* 对计算出来的digest值, 与云端下发的digest值进行比较 */
static int32_t _download_digest_verify(mqtt_download_handle_t *download_handle)
{
uint8_t output[32] = {0};
uint8_t expected_digest[32] = {0};
if (AIOT_OTA_DIGEST_SHA256 == download_handle->task_desc->digest_method) {
core_str2hex(download_handle->task_desc->expect_digest, OTA_SHA256_LEN, expected_digest);
core_sha256_finish(download_handle->digest_ctx, output);
if (memcmp(output, expected_digest, 32) == 0) {
return STATE_SUCCESS;
}
} else if (AIOT_OTA_DIGEST_MD5 == download_handle->task_desc->digest_method) {
core_str2hex(download_handle->task_desc->expect_digest, OTA_MD5_LEN, expected_digest);
utils_md5_finish(download_handle->digest_ctx, output);
if (memcmp(output, expected_digest, 16) == 0) {
return STATE_SUCCESS;
}
}
return STATE_OTA_DIGEST_MISMATCH;
}
static int32_t _download_report_progress(void *handle, int32_t percent)
{
int32_t res = STATE_SUCCESS;
mqtt_download_handle_t *md_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
char out_buffer[4] = {0};
uint8_t out_len;
char *payload_string;
if (NULL == handle) {
return STATE_MQTT_DOWNLOAD_MQTT_HANDLE_NULL;
}
md_handle = (mqtt_download_handle_t *)handle;
sysdep = md_handle->sysdep;
if (NULL == md_handle->task_desc) {
return STATE_MQTT_DOWNLOAD_TASK_DEINIT;
}
core_int2str(percent, out_buffer, &out_len);
if (md_handle->task_desc->module) {
char *src[] = {"{\"step\":\"", out_buffer, "\",\"desc\":\"\",\"module\":\"", md_handle->task_desc->module, "\"}"};
uint8_t topic_len = sizeof(src) / sizeof(char *);
core_sprintf(sysdep, &payload_string, "%s%s%s%s%s", src, topic_len, OTA_MODULE_NAME);
} else {
char *src[] = {"{\"step\":\"", out_buffer, "\",\"desc\":\"\"}"};
uint8_t topic_len = sizeof(src) / sizeof(char *);
core_sprintf(sysdep, &payload_string, "%s%s%s", src, topic_len, OTA_MODULE_NAME);
}
res = _ota_publish_base(md_handle->task_desc->mqtt_handle, OTA_PROGRESS_TOPIC_PREFIX,
md_handle->task_desc->product_key,
md_handle->task_desc->device_name, NULL, payload_string);
sysdep->core_sysdep_free(payload_string);
return res;
}
static int32_t _parse_json_header(void *handle, char *data, uint32_t data_len, aiot_mqtt_download_recv_t *pakcet)
{
mqtt_download_handle_t *md_handle = (mqtt_download_handle_t *)handle;
char *header = data, *file_info = NULL, *value = NULL;
uint32_t header_len = data_len, file_info_len = 0, value_len = 0;
uint32_t code = 0;
uint32_t size = 0, offset = 0, fileLenth = 0;
int32_t res = STATE_SUCCESS;
if ((res = core_json_value((char *)header, header_len, "code", strlen("code"),
&value, &value_len)) < 0 ||
((res = core_str2uint(value, value_len, &code)) < 0) ||
code != 200 ) {
core_log1(md_handle->sysdep, 0, "recv handle parse err code %d\r\n", &code);
return res;
}
if ((res = core_json_value((char *)header, header_len, "data", strlen("data"),
&value, &value_len)) < 0 ) {
core_log(md_handle->sysdep, 0, "json parse error data\r\n");
return res;
}
/* 解析文件的描述信息 */
file_info = value;
file_info_len = value_len;
if ((res = core_json_value((char *)file_info, file_info_len, "bSize", strlen("bSize"),
&value, &value_len)) < 0 ||
((res = core_str2uint(value, value_len, &size)) < 0)) {
core_log(md_handle->sysdep, 0, "json parse error bSize\r\n");
return res;
}
if ((res = core_json_value((char *)file_info, file_info_len, "bOffset", strlen("bOffset"),
&value, &value_len)) < 0 ||
((res = core_str2uint(value, value_len, &offset)) < 0)) {
core_log(md_handle->sysdep, 0, "json parse error bOffset\r\n");
return res;
}
if ((res = core_json_value((char *)file_info, file_info_len, "fileLength", strlen("fileLength"),
&value, &value_len)) < 0 ||
((res = core_str2uint(value, value_len, &fileLenth)) < 0)) {
core_log(md_handle->sysdep, 0, "json parse error bOffset\r\n");
return res;
}
if ((res = core_json_value((char *)file_info, file_info_len, "fileToken", strlen("fileToken"),
&value, &value_len)) == 0 ) {
memcpy(pakcet->data.data_resp.filename, value, value_len);
}
pakcet->data.data_resp.data_size = size;
pakcet->data.data_resp.offset = offset;
pakcet->data.data_resp.file_lenth = fileLenth;
return res;
}
static void _md_resend_request(mqtt_download_handle_t *md_handle)
{
md_handle->failed_counter++;
if(md_handle->failed_counter > 3) {
md_handle->status = STATE_MQTT_DOWNLOAD_FAILED_TIMEOUT;
return;
}
_md_send_request(md_handle);
}
static void _md_recv_data_reply_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata)
{
mqtt_download_handle_t *md_handle = (mqtt_download_handle_t *)userdata;
/*有效文件数据*/
uint8_t *data = NULL;
uint32_t data_len = 0;
/*json头解析变量定义*/
char *header = NULL;
uint16_t header_len = 0;
char file_token[MQTT_DOWNLOAD_TOKEN_MAXLEN];
uint16_t crc16 = 0, cal_crc16 = 0;
aiot_mqtt_download_recv_t packet;
memset(file_token, 0, sizeof(file_token));
packet.type = AIOT_MDRECV_DATA_RESP;
packet.data.data_resp.filename = file_token;
if(md_handle == NULL || md_handle->status != STATE_MQTT_DOWNLOAD_ING) {
return;
}
/*获取并解析json头*/
header = (char *)(msg->data.pub.payload + 2);
header_len = *msg->data.pub.payload << 8 | *(msg->data.pub.payload + 1);
if(_parse_json_header(md_handle, header, header_len, &packet) < 0) {
_md_resend_request(md_handle);
return;
}
/* 校验数据长度 */
if(msg->data.pub.payload_len != sizeof(header_len) + header_len + packet.data.data_resp.data_size + sizeof(crc16)) {
core_log(md_handle->sysdep, 0, "payload lenth dismatch data lenth\r\n");
_md_resend_request(md_handle);
return;
}
/* 有效的文件数据 */
data = msg->data.pub.payload + sizeof(header_len) + header_len;
data_len = packet.data.data_resp.data_size;
/* CRC校验 */
crc16 = *(uint16_t *)(msg->data.pub.payload + msg->data.pub.payload_len - sizeof(crc16));
cal_crc16 = crc_ibm(data, data_len);
if(cal_crc16 != crc16) {
_md_resend_request(md_handle);
return;
}
/* 判断接收的数据偏移是否为请求的偏移,可能存在重复请求,这里校验失败,不主动重发请求,等超时 */
if(packet.data.data_resp.offset != md_handle->size_fetched + md_handle->range_start) {
return;
}
/* 生成回调packet包回调用户接口 */
md_handle->size_fetched += data_len;
packet.data.data_resp.data = (char *)data;
packet.data.data_resp.percent = md_handle->size_fetched * 100 / md_handle->range_size;
/* 计算digest, 如果下载完成, 还要看看是否与云端计算出来的一致 */
if(md_handle->md5_enabled) {
_download_digest_update(md_handle, (uint8_t *)packet.data.data_resp.data, packet.data.data_resp.data_size);
}
/* 数据接收成功,失败统计复位 */
md_handle->failed_counter = 0;
/* 回调用户接口, 通知存储数据 */
if(md_handle->recv_handler != NULL) {
md_handle->recv_handler(md_handle, &packet, md_handle->userdata);
}
if(md_handle->size_fetched < md_handle->range_size) {
/*请求下一包*/
_md_send_request(md_handle);
} else if(md_handle->size_fetched == md_handle->range_size) {
/*下载完成, 如果有md5还需要做整个文件的校验*/
md_handle->percent = 100;
md_handle->status = STATE_MQTT_DOWNLOAD_FINISHED;
} else {
md_handle->status = STATE_MQTT_DOWNLOAD_FAILED_RECVERROR;
}
}
int32_t aiot_mqtt_download_setopt(void *handle, aiot_mqtt_download_option_t option, void *data) {
int32_t res = STATE_SUCCESS;
if (NULL == handle) {
return STATE_DOWNLOAD_DEINIT_HANDLE_IS_NULL;
}
mqtt_download_handle_t *md_handle = (mqtt_download_handle_t *)handle;
md_handle->sysdep->core_sysdep_mutex_lock(md_handle->data_mutex);
switch (option) {
case AIOT_MDOPT_USERDATA: {
md_handle->userdata = data;
}
break;
case AIOT_MDOPT_TASK_DESC: {
void *new_task_desc = _download_deep_copy_task_desc(md_handle->sysdep, data);
if (NULL == new_task_desc) {
res = STATE_DOWNLOAD_SETOPT_COPIED_DATA_IS_NULL;
break;
}
md_handle->task_desc = (aiot_download_task_desc_t *)new_task_desc;
core_mqtt_process_data_t process_data;
memset(&process_data, 0, sizeof(core_mqtt_process_data_t));
process_data.handler = _md_core_mqtt_process_handler;
process_data.context = handle;
core_mqtt_setopt(md_handle->task_desc->mqtt_handle, CORE_MQTTOPT_APPEND_PROCESS_HANDLER, &process_data);
}
break;
case AIOT_MDOPT_RANGE_START: {
md_handle->range_start = *(uint32_t *)data;
}
break;
case AIOT_MDOPT_RANGE_END: {
md_handle->range_end = *(uint32_t *)data;
}
break;
case AIOT_MDOPT_RECV_HANDLE: {
md_handle->recv_handler = (aiot_mqtt_download_recv_handler_t)data;
}
break;
case AIOT_MDOPT_DATA_REQUEST_SIZE: {
md_handle->request_size = *(uint32_t *)data;
}
break;
default: {
res = STATE_USER_INPUT_OUT_RANGE;
}
break;
}
md_handle->sysdep->core_sysdep_mutex_unlock(md_handle->data_mutex);
return res;
}
static int32_t _md_send_request(void *handle)
{
mqtt_download_handle_t *md_handle = (mqtt_download_handle_t *)handle;
char *payload_fmt = "{\"id\":\"%s\",\"version\":\"1.0\",\"params\":%s}";
char *params_fmt = "{\"fileToken\":\"%s\",\"fileInfo\":{\"streamId\":%s,\"fileId\":%s,},\"fileBlock\":{\"size\":%s,\"offset\":%s}}";
char *params = NULL, *payload = NULL, *topic = NULL;
char stream_id_string[21], file_id_string[21], request_size_string[21], offset_string[21];
char *params_src[] = {"default", stream_id_string, file_id_string, request_size_string, offset_string };
char id_string[21];
char *payload_src[2] = { id_string };
char *topic_src[2] = { id_string };
uint32_t res = STATE_SUCCESS;
uint32_t offset = md_handle->size_fetched + md_handle->range_start;
/* 生成params */
int32_t size = md_handle->request_size;
if(md_handle->request_size + md_handle->size_fetched > md_handle->range_size)
{
size = md_handle->range_size - md_handle->size_fetched;
}
memset(stream_id_string, 0, sizeof(stream_id_string));
core_uint2str(md_handle->task_desc->stream_id, stream_id_string, NULL);
memset(file_id_string, 0, sizeof(file_id_string));
core_uint2str(md_handle->task_desc->stream_file_id, file_id_string, NULL);
memset(request_size_string, 0, sizeof(request_size_string));
core_uint2str(size, request_size_string, NULL);
memset(offset_string, 0, sizeof(offset_string));
core_uint2str(offset, offset_string, NULL);
core_sprintf(md_handle->sysdep, &params, params_fmt, params_src, sizeof(params_src) / sizeof(char *),
MQTT_DOWNLOAD_MODULE_NAME);
/*生成payload */
md_handle->msg_id++;
memset(id_string, 0, sizeof(id_string));
core_uint2str(md_handle->msg_id, id_string, NULL);
payload_src[1] = params;
core_sprintf(md_handle->sysdep, &payload, payload_fmt, payload_src, sizeof(payload_src) / sizeof(char *),
MQTT_DOWNLOAD_MODULE_NAME);
topic_src[0] = core_mqtt_get_product_key(md_handle->task_desc->mqtt_handle);
topic_src[1] = core_mqtt_get_device_name(md_handle->task_desc->mqtt_handle);
core_sprintf(md_handle->sysdep, &topic, MQTT_DOWNLOAD_REQUEST_TOPIC, topic_src, sizeof(topic_src) / sizeof(char *),
MQTT_DOWNLOAD_MODULE_NAME);
if( payload != NULL && topic != NULL) {
res = aiot_mqtt_pub(md_handle->task_desc->mqtt_handle, topic, (uint8_t *)payload, strlen(payload), 0);
}
md_handle->last_request_time = md_handle->sysdep->core_sysdep_time();
if(topic != NULL) {
md_handle->sysdep->core_sysdep_free(topic);
}
if(payload != NULL) {
md_handle->sysdep->core_sysdep_free(payload);
}
if(params != NULL) {
md_handle->sysdep->core_sysdep_free(params);
}
return res;
}
static int32_t _md_reset_handle(mqtt_download_handle_t *md_handle)
{
if(md_handle == NULL) {
return STATE_MQTT_DOWNLOAD_MQTT_HANDLE_NULL;
}
md_handle->msg_id = 0;
md_handle->size_fetched = 0;
md_handle->percent = 0;
md_handle->last_request_time = 0;
md_handle->failed_counter = 0;
md_handle->last_percent = 0;
md_handle->range_size = 0;
return STATE_SUCCESS;
}
int32_t aiot_mqtt_download_process(void *handle)
{
uint64_t now;
mqtt_download_handle_t *md_handle = (mqtt_download_handle_t *)handle;
int32_t percent = 0;
int32_t res = 0;
if(md_handle == NULL) {
return STATE_MQTT_DOWNLOAD_MQTT_HANDLE_NULL;
}
if(md_handle->task_desc == NULL || md_handle->task_desc->mqtt_handle == NULL) {
return STATE_MQTT_DOWNLOAD_TASK_DEINIT;
}
res = md_handle->status;
switch(md_handle->status) {
case STATE_MQTT_DOWNLOAD_INIT: {
/* 没有设置片段下载结尾,设置下载至文件结尾 */
if(md_handle->range_end == 0) {
md_handle->range_end = md_handle->task_desc->size_total;
}
md_handle->range_size = md_handle->range_end - md_handle->range_start;
if(md_handle->range_size <= 0) {
return STATE_MQTT_DOWNLOAD_FILESIZE_ERROR;
}
/* 订阅数据返回的topic */
_md_sub_response_topic(md_handle);
/* 完整的文件下载做md5校验 */
if(md_handle->range_size == md_handle->task_desc->size_total
&& AIOT_OTA_DIGEST_MD5 == md_handle->task_desc->digest_method
&& NULL != md_handle->task_desc->expect_digest) {
utils_md5_context_t *ctx = md_handle->sysdep->core_sysdep_malloc(sizeof(utils_md5_context_t), MQTT_DOWNLOAD_MODULE_NAME);
if (NULL == ctx) {
res = STATE_DOWNLOAD_SETOPT_MALLOC_MD5_CTX_FAILED;
break;
}
md_handle->md5_enabled = 1;
utils_md5_init(ctx);
utils_md5_starts(ctx);
md_handle->digest_ctx = (void *) ctx;
}
_md_send_request(handle);
md_handle->last_request_time = md_handle->sysdep->core_sysdep_time();
md_handle->status = STATE_MQTT_DOWNLOAD_ING;
res = STATE_MQTT_DOWNLOAD_ING;
}
break;
case STATE_MQTT_DOWNLOAD_ING: {
now = md_handle->sysdep->core_sysdep_time();
if(now - md_handle->last_request_time > MQTT_DOWNLOAD_DEFAULT_RECV_TIMEOUT) {
_md_resend_request(md_handle);
}
percent = md_handle->size_fetched * 100 / md_handle->range_size;
if(percent - md_handle->last_percent >= MQTT_DOWNLOAD_REPORT_INTERNEL) {
md_handle->last_percent = percent;
_download_report_progress(md_handle, percent);
}
}
break;
case STATE_MQTT_DOWNLOAD_FINISHED: {
if(md_handle->md5_enabled) {
int32_t ret = _download_digest_verify(md_handle);
if (ret != STATE_SUCCESS) {
md_handle->status = STATE_MQTT_DOWNLOAD_FAILED_MISMATCH;
core_log(md_handle->sysdep, ret, "digest mismatch\r\n");
} else {
md_handle->status = STATE_MQTT_DOWNLOAD_SUCCESS;
core_log(md_handle->sysdep, STATE_OTA_DIGEST_MATCH, "digest matched\r\n");
}
if (NULL != md_handle->digest_ctx) {
utils_md5_free(md_handle->digest_ctx);
md_handle->sysdep->core_sysdep_free(md_handle->digest_ctx);
md_handle->digest_ctx = NULL;
}
} else {
md_handle->status = STATE_MQTT_DOWNLOAD_SUCCESS;
}
}
break;
case STATE_MQTT_DOWNLOAD_SUCCESS: {
_download_report_progress(md_handle, 100);
_md_reset_handle(md_handle);
md_handle->status = STATE_MQTT_DOWNLOAD_INIT;
}
break;
case STATE_MQTT_DOWNLOAD_FAILED_MISMATCH: {
_download_report_progress(md_handle, AIOT_OTAERR_CHECKSUM_MISMATCH);
_md_reset_handle(md_handle);
md_handle->status = STATE_MQTT_DOWNLOAD_INIT;
}
break;
case STATE_MQTT_DOWNLOAD_FAILED_RECVERROR:
case STATE_MQTT_DOWNLOAD_FAILED_TIMEOUT: {
_download_report_progress(md_handle, AIOT_OTAERR_BURN_FAILED);
_md_reset_handle(md_handle);
md_handle->status = STATE_MQTT_DOWNLOAD_INIT;
}
break;
}
return res;
}

View File

@@ -0,0 +1,289 @@
/**
* @file aiot_mqtt_download_api.h
* @brief mqtt_download模块头文件, 提供mqtt文件传输的能力
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#ifndef __AIOT_MQTT_DOWNLOAD_API_H__
#define __AIOT_MQTT_DOWNLOAD_API_H__
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdint.h>
/**
* @brief -0x1D00~-0x1DFF表达SDK在mqtt_download模块内的状态码
*/
#define STATE_MQTT_DOWNLOAD_BASE (-0x1D00)
/**
* @brief mqtt_download_handle为空
*/
#define STATE_MQTT_DOWNLOAD_MQTT_HANDLE_NULL (-0x1D01)
/**
* @brief 任务未初始化
*/
#define STATE_MQTT_DOWNLOAD_TASK_DEINIT (-0x1D02)
/**
* @brief 初始化完,第一次执行请求
*/
#define STATE_MQTT_DOWNLOAD_INIT (-0x1D03)
/**
* @brief 正在下载文件
*/
#define STATE_MQTT_DOWNLOAD_ING (-0x1D04)
/**
* @brief 文件已经下载完,但未校验
*/
#define STATE_MQTT_DOWNLOAD_FINISHED (-0x1D05)
/**
* @brief 文件下载完成,并且校验通过
*/
#define STATE_MQTT_DOWNLOAD_SUCCESS (-0x1D06)
/**
* @brief 文件下载接收失败,重试几个后还是失败,认定为超时
*/
#define STATE_MQTT_DOWNLOAD_FAILED_TIMEOUT (-0x1D07)
/**
* @brief 完整文件下载时MD5校验失败
*/
#define STATE_MQTT_DOWNLOAD_FAILED_MISMATCH (-0x1D08)
/**
* @brief 接收长度校验错误
*/
#define STATE_MQTT_DOWNLOAD_FAILED_RECVERROR (-0x1D09)
/**
* @brief 用户主动取消下载任务
*/
#define STATE_MQTT_DOWNLOAD_ABORT (-0x1D0A)
/**
* @brief 设置的下载长度为空
*/
#define STATE_MQTT_DOWNLOAD_FILESIZE_ERROR (-0x1D0B)
/**
* @brief mqtt_download模块收到从网络上来的报文时, 通知用户的报文类型
*/
typedef enum {
/**
* @brief 文件分片下载请求的返回报文
*/
AIOT_MDRECV_DATA_RESP,
} aiot_mqtt_download_recv_type_t;
/**
* @brief mqtt_download模块收到从网络上来的报文时, 通知用户的报文内容
*/
typedef struct {
/**
* @brief 报文内容所对应的报文类型, 更多信息请参考@ref aiot_mqtt_download_recv_type_t
*/
aiot_mqtt_download_recv_type_t type;
union {
/**
* @brief data_resp_type的说明
*/
struct {
/**
* @brief 用户标准的文件名称,一般为设置的文件名
*/
char *filename;
/**
* @brief 文件的偏移
*/
uint32_t offset;
/**
* @brief 接收到的数据长度
*/
uint32_t data_size;
/**
* @brief 接收到的数据
*/
char *data;
/**
* @brief 接收到的数据
*/
int32_t percent;
/**
* @brief 文件的总大小
*/
int32_t file_lenth;
} data_resp;
} data;
} aiot_mqtt_download_recv_t;
/**
* @brief mqtt_download模块收到从网络上来的报文时, 通知用户所调用的数据回调函数
*
* @param[in] handle mqtt_download会话句柄
* @param[in] packet mqtt_download消息结构体, 存放收到的mqtt_download报文内容
* @param[in] userdata 用户上下文
*
* @return void
*/
typedef void (* aiot_mqtt_download_recv_handler_t)(void *handle,
const aiot_mqtt_download_recv_t *packet, void *userdata);
/**
* @brief @ref aiot_mqtt_download_setopt 接口的option参数可选值.
*
* @details 下文每个选项中的数据类型, 指的是@ref aiot_mqtt_download_setopt 中, data参数的数据类型
*/
typedef enum {
/**
* @brief 设置处理OTA消息的用户回调函数
*
* @details
*
* 数据类型: (aiot_mqtt_download_recv_handler_t)
*/
AIOT_MDOPT_RECV_HANDLE,
/**
* @brief 设置MQTT的handle
*
* @details
*
* 文件下载过程中使用MQTT的通道能力, 用以请求数据及接收数据
*
* 数据类型: (void *)
*/
AIOT_MDOPT_MQTT_HANDLE,
/**
* @brief 用户需要SDK暂存的上下文
*
* @details
*
* 该上下文会在@ref AIOT_OTAOPT_RECV_HANDLER 中传回给用户
*
* 数据类型: (void *)
*/
AIOT_MDOPT_USERDATA,
/**
* @brief 设置download实例句柄所包含下载任务的具体内容
*
* @details
*
* 用户在收到OTA的mqtt消息后, 如果决定升级, 则需要通过该选项, 在download实例句柄中开辟内存,
* 将OTA消息中携带的url, version, digest method, sign等信息复制过来, 有了这些信息后才能开始下载任务
*
* 数据类型: (aiot_download_task_desc_t *)
*
**/
AIOT_MDOPT_TASK_DESC,
/**
* @brief 设置按照range下载的起始地址
*
* @details
*
* MQTT 范围请求(range requests)特性中, 表示从第该byte开始下载
* 如果指定从头开始下载, 则start的值为0
*
* 数据类型: (uint32_t *)
*
**/
AIOT_MDOPT_RANGE_START,
/**
* @brief 设置按照range下载的结束地址
*
* @details
* MQTT 范围请求(range requests)特性中, 表示下载到该byte后结束.
* 如果指定从头开始下载到10个byte后结束,
* 则需要指定start = 0, end = 9, 这样总共10个byte
*
* 数据类型: (uint32_t *)
*
**/
AIOT_MDOPT_RANGE_END,
/**
* @brief 单次请求数据的最大长度
*
* @details
*
* 数据类型: (uint32_t *) 默认值: (2 *1024) Bytes
*/
AIOT_MDOPT_DATA_REQUEST_SIZE,
AIOT_MDOPT_MAX,
} aiot_mqtt_download_option_t;
/**
* @brief 创建mqtt_download会话实例, 并以默认值配置会话参数
*
* @return void *
* @retval 非NULL mqtt_download实例的句柄
* @retval NULL 初始化失败, 一般是内存分配失败导致
*
*/
void *aiot_mqtt_download_init(void);
/**
* @brief 配置mqtt_download会话
*
* @param[in] handle mqtt_download会话句柄
* @param[in] option 配置选项, 更多信息请参考@ref aiot_mqtt_download_option_t
* @param[in] data 配置选项数据, 更多信息请参考@ref aiot_mqtt_download_option_t
*
* @return int32_t
* @retval <STATE_SUCCESS 参数配置失败
* @retval >=STATE_SUCCESS 参数配置成功
*
*/
int32_t aiot_mqtt_download_setopt(void *handle, aiot_mqtt_download_option_t option, void *data);
/**
* @brief 结束mqtt_download会话, 销毁实例并回收资源
*
* @param[in] handle 指向mqtt_download会话句柄的指针
*
* @return int32_t
* @retval <STATE_SUCCESS 执行失败
* @retval >=STATE_SUCCESS 执行成功
*
*/
int32_t aiot_mqtt_download_deinit(void **handle);
/**
* @brief 处理下载逻辑
*
* @param handle mqtt_download会话句柄
*
* @return int32_t
* @retval STATE_MQTT_DOWNLOAD_INIT 初始化完成
* @retval STATE_MQTT_DOWNLOAD_ING 正在下载
* @retval STATE_MQTT_DOWNLOAD_SUCCESS 下载完成
* @retval STATE_MQTT_DOWNLOAD_FAILED_RECVERROR 数据接收错误
* @retval STATE_MQTT_DOWNLOAD_FAILED_TIMEOUT 接收超时
* @retval STATE_MQTT_DOWNLOAD_FAILED_MISMATCH 校验错误
* @retval STATE_MQTT_DOWNLOAD_MQTT_HANDLE_NULL handle没有初始化
* @retval STATE_MQTT_DOWNLOAD_TASK_DEINIT 没有设置task
*/
int32_t aiot_mqtt_download_process(void *handle);
#if defined(__cplusplus)
}
#endif
#endif /* __AIOT_MQTT_DOWNLOAD_API_H__ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,68 @@
/**
* @file mqtt_download_private.h
* @brief mqtt_download模块内部的宏定义和数据结构声明, 不面向其它模块, 更不面向用户
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#ifndef __MQTT_DOWNLOAD_PRIVATE_H__
#define __MQTT_DOWNLOAD_PRIVATE_H__
#if defined(__cplusplus)
extern "C" {
#endif
#include "core_stdinc.h"
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_ota_api.h"
#include "aiot_mqtt_download_api.h" /* 内部头文件是用户可见头文件的超集 */
/* 定义mqtt_download模块内部的会话句柄结构体, SDK用户不可见, 只能得到void *handle类型的指针 */
typedef struct {
aiot_sysdep_portfile_t *sysdep; /* 底层依赖回调合集的引用指针 */
aiot_mqtt_download_recv_handler_t recv_handler; /* 组件从协议栈读到内容时, 通知用户的回调 */
void *userdata; /* 组件调用以上2个 mqtt_download_handler 时的入参之一 */
aiot_download_task_desc_t *task_desc; /* 某次下载活动的目标描述信息, 如URL等 */
uint32_t range_start;
uint32_t range_end;
uint32_t request_size; /* 每次请求的size */
/*---- 以上都是用户在API可配 ----*/
uint32_t msg_id;
uint32_t size_fetched;
int32_t percent;
uint64_t last_request_time;
uint64_t failed_counter;
int32_t status;
int32_t last_percent;
uint32_t range_size;
int8_t md5_enabled;
void *digest_ctx;
void *data_mutex;
void *recv_mutex;
} mqtt_download_handle_t;
/* 用于内存统计的模块名字符串 */
#define MQTT_DOWNLOAD_MODULE_NAME "mqtt_download"
/* 发送请求后,等待回复的超时时间设置 */
#define MQTT_DOWNLOAD_DEFAULT_RECV_TIMEOUT (10 * 1000)
/* 默认的单次请求长度 */
#define MQTT_DOWNLOAD_DEFAULT_REQUEST_SIZE (5 * 1024)
/* 请求及回复的topic定义 */
#define MQTT_DOWNLOAD_REQUEST_TOPIC "/sys/%s/%s/thing/file/download"
#define MQTT_DOWNLOAD_RESPONSE_TOPIC "/sys/%s/%s/thing/file/download_reply"
/* TOKEN的最长长度 */
#define MQTT_DOWNLOAD_TOKEN_MAXLEN (128)
/* 上报进度的间隔,单位:% */
#define MQTT_DOWNLOAD_REPORT_INTERNEL (5)
#if defined(__cplusplus)
}
#endif
#endif /* __MQTT_DOWNLOAD_PRIVATE_H__ */

View File

@@ -0,0 +1,299 @@
#include "ota_md5.h"
/*
* 32-bit integer manipulation macros (little endian)
*/
#ifndef GET_UINT32_LE
#define GET_UINT32_LE(n,b,i) \
{ \
(n) = ( (uint32_t) (b)[(i) ] ) \
| ( (uint32_t) (b)[(i) + 1] << 8 ) \
| ( (uint32_t) (b)[(i) + 2] << 16 ) \
| ( (uint32_t) (b)[(i) + 3] << 24 ); \
}
#endif
#ifndef PUT_UINT32_LE
#define PUT_UINT32_LE(n,b,i) \
{ \
(b)[(i) ] = (unsigned char) ( ( (n) ) & 0xFF ); \
(b)[(i) + 1] = (unsigned char) ( ( (n) >> 8 ) & 0xFF ); \
(b)[(i) + 2] = (unsigned char) ( ( (n) >> 16 ) & 0xFF ); \
(b)[(i) + 3] = (unsigned char) ( ( (n) >> 24 ) & 0xFF ); \
}
#endif
static void utils_md5_zeroize(void *v, uint32_t n)
{
volatile unsigned char *p = v;
while (n--) {
*p++ = 0;
}
}
void utils_md5_init(utils_md5_context_t *ctx)
{
memset(ctx, 0, sizeof(utils_md5_context_t));
}
void utils_md5_free(utils_md5_context_t *ctx)
{
if (ctx == NULL) {
return;
}
utils_md5_zeroize(ctx, sizeof(utils_md5_context_t));
}
/*
* MD5 context setup
*/
int32_t utils_md5_starts(utils_md5_context_t *ctx)
{
ctx->total[0] = 0;
ctx->total[1] = 0;
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xEFCDAB89;
ctx->state[2] = 0x98BADCFE;
ctx->state[3] = 0x10325476;
return (0);
}
int32_t utils_internal_md5_process(utils_md5_context_t *ctx,
const unsigned char data[64])
{
uint32_t X[16], A, B, C, D;
GET_UINT32_LE(X[ 0], data, 0);
GET_UINT32_LE(X[ 1], data, 4);
GET_UINT32_LE(X[ 2], data, 8);
GET_UINT32_LE(X[ 3], data, 12);
GET_UINT32_LE(X[ 4], data, 16);
GET_UINT32_LE(X[ 5], data, 20);
GET_UINT32_LE(X[ 6], data, 24);
GET_UINT32_LE(X[ 7], data, 28);
GET_UINT32_LE(X[ 8], data, 32);
GET_UINT32_LE(X[ 9], data, 36);
GET_UINT32_LE(X[10], data, 40);
GET_UINT32_LE(X[11], data, 44);
GET_UINT32_LE(X[12], data, 48);
GET_UINT32_LE(X[13], data, 52);
GET_UINT32_LE(X[14], data, 56);
GET_UINT32_LE(X[15], data, 60);
#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
#define P(a,b,c,d,k,s,t) \
{ \
a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \
}
A = ctx->state[0];
B = ctx->state[1];
C = ctx->state[2];
D = ctx->state[3];
#define F(x,y,z) (z ^ (x & (y ^ z)))
P(A, B, C, D, 0, 7, 0xD76AA478);
P(D, A, B, C, 1, 12, 0xE8C7B756);
P(C, D, A, B, 2, 17, 0x242070DB);
P(B, C, D, A, 3, 22, 0xC1BDCEEE);
P(A, B, C, D, 4, 7, 0xF57C0FAF);
P(D, A, B, C, 5, 12, 0x4787C62A);
P(C, D, A, B, 6, 17, 0xA8304613);
P(B, C, D, A, 7, 22, 0xFD469501);
P(A, B, C, D, 8, 7, 0x698098D8);
P(D, A, B, C, 9, 12, 0x8B44F7AF);
P(C, D, A, B, 10, 17, 0xFFFF5BB1);
P(B, C, D, A, 11, 22, 0x895CD7BE);
P(A, B, C, D, 12, 7, 0x6B901122);
P(D, A, B, C, 13, 12, 0xFD987193);
P(C, D, A, B, 14, 17, 0xA679438E);
P(B, C, D, A, 15, 22, 0x49B40821);
#undef F
#define F(x,y,z) (y ^ (z & (x ^ y)))
P(A, B, C, D, 1, 5, 0xF61E2562);
P(D, A, B, C, 6, 9, 0xC040B340);
P(C, D, A, B, 11, 14, 0x265E5A51);
P(B, C, D, A, 0, 20, 0xE9B6C7AA);
P(A, B, C, D, 5, 5, 0xD62F105D);
P(D, A, B, C, 10, 9, 0x02441453);
P(C, D, A, B, 15, 14, 0xD8A1E681);
P(B, C, D, A, 4, 20, 0xE7D3FBC8);
P(A, B, C, D, 9, 5, 0x21E1CDE6);
P(D, A, B, C, 14, 9, 0xC33707D6);
P(C, D, A, B, 3, 14, 0xF4D50D87);
P(B, C, D, A, 8, 20, 0x455A14ED);
P(A, B, C, D, 13, 5, 0xA9E3E905);
P(D, A, B, C, 2, 9, 0xFCEFA3F8);
P(C, D, A, B, 7, 14, 0x676F02D9);
P(B, C, D, A, 12, 20, 0x8D2A4C8A);
#undef F
#define F(x,y,z) (x ^ y ^ z)
P(A, B, C, D, 5, 4, 0xFFFA3942);
P(D, A, B, C, 8, 11, 0x8771F681);
P(C, D, A, B, 11, 16, 0x6D9D6122);
P(B, C, D, A, 14, 23, 0xFDE5380C);
P(A, B, C, D, 1, 4, 0xA4BEEA44);
P(D, A, B, C, 4, 11, 0x4BDECFA9);
P(C, D, A, B, 7, 16, 0xF6BB4B60);
P(B, C, D, A, 10, 23, 0xBEBFBC70);
P(A, B, C, D, 13, 4, 0x289B7EC6);
P(D, A, B, C, 0, 11, 0xEAA127FA);
P(C, D, A, B, 3, 16, 0xD4EF3085);
P(B, C, D, A, 6, 23, 0x04881D05);
P(A, B, C, D, 9, 4, 0xD9D4D039);
P(D, A, B, C, 12, 11, 0xE6DB99E5);
P(C, D, A, B, 15, 16, 0x1FA27CF8);
P(B, C, D, A, 2, 23, 0xC4AC5665);
#undef F
#define F(x,y,z) (y ^ (x | ~z))
P(A, B, C, D, 0, 6, 0xF4292244);
P(D, A, B, C, 7, 10, 0x432AFF97);
P(C, D, A, B, 14, 15, 0xAB9423A7);
P(B, C, D, A, 5, 21, 0xFC93A039);
P(A, B, C, D, 12, 6, 0x655B59C3);
P(D, A, B, C, 3, 10, 0x8F0CCC92);
P(C, D, A, B, 10, 15, 0xFFEFF47D);
P(B, C, D, A, 1, 21, 0x85845DD1);
P(A, B, C, D, 8, 6, 0x6FA87E4F);
P(D, A, B, C, 15, 10, 0xFE2CE6E0);
P(C, D, A, B, 6, 15, 0xA3014314);
P(B, C, D, A, 13, 21, 0x4E0811A1);
P(A, B, C, D, 4, 6, 0xF7537E82);
P(D, A, B, C, 11, 10, 0xBD3AF235);
P(C, D, A, B, 2, 15, 0x2AD7D2BB);
P(B, C, D, A, 9, 21, 0xEB86D391);
#undef F
ctx->state[0] += A;
ctx->state[1] += B;
ctx->state[2] += C;
ctx->state[3] += D;
return (0);
}
/*
* MD5 process buffer
*/
int32_t utils_md5_update(utils_md5_context_t *ctx,
const unsigned char *input,
uint32_t ilen)
{
int32_t ret;
uint32_t fill;
uint32_t left;
if (ilen == 0) {
return (0);
}
left = ctx->total[0] & 0x3F;
fill = 64 - left;
ctx->total[0] += (uint32_t) ilen;
ctx->total[0] &= 0xFFFFFFFF;
if (ctx->total[0] < (uint32_t) ilen) {
ctx->total[1]++;
}
if (left && ilen >= fill) {
memcpy((void *)(ctx->buffer + left), input, fill);
if ((ret = utils_internal_md5_process(ctx, ctx->buffer)) != 0) {
return (ret);
}
input += fill;
ilen -= fill;
left = 0;
}
while (ilen >= 64) {
if ((ret = utils_internal_md5_process(ctx, input)) != 0) {
return (ret);
}
input += 64;
ilen -= 64;
}
if (ilen > 0) {
memcpy((void *)(ctx->buffer + left), input, ilen);
}
return (0);
}
/*
* MD5 final digest
*/
int32_t utils_md5_finish(utils_md5_context_t *ctx,
unsigned char output[16])
{
int32_t ret;
uint32_t used;
uint32_t high, low;
/*
* Add padding: 0x80 then 0x00 until 8 bytes remain for the length
*/
used = ctx->total[0] & 0x3F;
ctx->buffer[used++] = 0x80;
if (used <= 56) {
/* Enough room for padding + length in current block */
memset(ctx->buffer + used, 0, 56 - used);
} else {
/* We'll need an extra block */
memset(ctx->buffer + used, 0, 64 - used);
if ((ret = utils_internal_md5_process(ctx, ctx->buffer)) != 0) {
return (ret);
}
memset(ctx->buffer, 0, 56);
}
/*
* Add message length
*/
high = (ctx->total[0] >> 29)
| (ctx->total[1] << 3);
low = (ctx->total[0] << 3);
PUT_UINT32_LE(low, ctx->buffer, 56);
PUT_UINT32_LE(high, ctx->buffer, 60);
if ((ret = utils_internal_md5_process(ctx, ctx->buffer)) != 0) {
return (ret);
}
/*
* Output final state
*/
PUT_UINT32_LE(ctx->state[0], output, 0);
PUT_UINT32_LE(ctx->state[1], output, 4);
PUT_UINT32_LE(ctx->state[2], output, 8);
PUT_UINT32_LE(ctx->state[3], output, 12);
return (0);
}

View File

@@ -0,0 +1,136 @@
#ifndef __OTA_MD5_H__
#define __OTA_MD5_H__
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
/**
* \brief MD5 context structure
*
* \warning MD5 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
*/
typedef struct {
uint32_t total[2]; /*!< number of bytes processed */
uint32_t state[4]; /*!< intermediate digest state */
unsigned char buffer[64]; /*!< data block being processed */
} utils_md5_context_t;
/**
* \brief Initialize MD5 context
*
* \param ctx MD5 context to be initialized
*
* \warning MD5 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
*/
void utils_md5_init(utils_md5_context_t *ctx);
/**
* \brief Clear MD5 context
*
* \param ctx MD5 context to be cleared
*
* \warning MD5 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
*/
void utils_md5_free(utils_md5_context_t *ctx);
/**
* \brief MD5 context setup
*
* \param ctx context to be initialized
*
* \return 0 if successful
*
* \warning MD5 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
*/
int32_t utils_md5_starts(utils_md5_context_t *ctx);
/**
* \brief MD5 process buffer
*
* \param ctx MD5 context
* \param input buffer holding the data
* \param ilen length of the input data
*
* \return 0 if successful
*
* \warning MD5 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
*/
int32_t utils_md5_update(utils_md5_context_t *ctx,
const unsigned char *input,
uint32_t ilen);
/**
* \brief MD5 final digest
*
* \param ctx MD5 context
* \param output MD5 checksum result
*
* \return 0 if successful
*
* \warning MD5 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
*/
int32_t utils_md5_finish(utils_md5_context_t *ctx,
unsigned char output[16]);
/**
* \brief MD5 process data block (internal use only)
*
* \param ctx MD5 context
* \param data buffer holding one block of data
*
* \return 0 if successful
*
* \warning MD5 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
*/
int32_t utils_internal_md5_process(utils_md5_context_t *ctx,
const unsigned char data[64]);
/**
* \brief Output = MD5( input buffer )
*
* \param input buffer holding the data
* \param ilen length of the input data
* \param output MD5 checksum result
*
* \return 0 if successful
*
* \warning MD5 is considered a weak message digest and its use
* constitutes a security risk. We recommend considering
* stronger message digests instead.
*
*/
#if defined(__cplusplus)
}
#endif
#endif /* __OTA_MD5_H__ */

View File

@@ -0,0 +1,116 @@
#ifndef __OTA_PRIVATE_H__
#define __OTA_PRIVATE_H__
#if defined(__cplusplus)
extern "C" {
#endif
#include "core_stdinc.h"
#include "core_http.h"
#include "aiot_ota_api.h"
#define OTA_VERSION_TOPIC_PREFIX "/ota/device/inform"
#define OTA_PROGRESS_TOPIC_PREFIX "/ota/device/progress"
#define OTA_MODULE_NAME "OTA"
#define DOWNLOAD_MODULE_NAME "DOWNLOAD"
#define OTA_DEFAULT_DOWNLOAD_BUFLEN (2 * 1024)
#define OTA_DEFAULT_DOWNLOAD_TIMEOUT_MS (5 * 1000)
#define OTA_FOTA_TOPIC "/ota/device/upgrade/+/+"
#define OTA_FOTA_TOPIC_PREFIX "/ota/device/upgrade"
#define OTA_COTA_PUSH_TOPIC "/sys/+/+/thing/config/push"
#define OTA_COTA_PUSH_TOPIC_POSTFIX "/thing/config/push"
#define OTA_COTA_GET_REPLY_TOPIC "/sys/+/+/thing/config/get_reply"
#define OTA_COTA_GET_REPLY_TOPIC_POSTFIX "/thing/config/get_reply"
#define OTA_COTA_TOPIC_PREFIX "/sys/"
#define OTA_GET_TOPIC_PREFIX "/sys"
#define OTA_GET_TOPIC_SUFFIX "thing/ota/firmware/get"
#define OTA_GET_REPLY_TOPIC_SUFFIX "thing/ota/firmware/get_reply"
#define OTA_OTA_GET_REPLY_TOPIC "/sys/+/+/thing/ota/firmware/get_reply"
#define OTA_HTTPCLIENT_MAX_URL_LEN (256)
#define OTA_MAX_DIGIT_NUM_OF_UINT32 (20)
#define OTA_RESPONSE_PARTIAL (206)
#define OTA_RESPONSE_OK (200)
#define OTA_TOPIC_NUM (4)
#define OTA_MD5_LEN (32)
#define OTA_SHA256_LEN (64)
typedef enum {
DOWNLOAD_STATUS_START,
DOWNLOAD_STATUS_FETCH,
DOWNLOAD_STATUS_RENEWAL,
} download_status_t;
typedef enum {
OTA_TYPE_FOTA,
OTA_TYPE_CONFIG_PUSH,
OTA_TYPE_CONFIG_GET,
} ota_type_t;
/**
* @brief OTA过程中处理mqtt消息的句柄, 该句柄主要用于通过mqtt协议从云端收取固件升级消息, 包括固件的url等
*
*/
typedef struct {
void *userdata; /* 组件调用recv_handler的入参之一, 传入用户数据 */
aiot_ota_recv_handler_t recv_handler; /* OTA的mqtt消息到达设备端时, 通知用户的回调 */
aiot_sysdep_portfile_t *sysdep;
/*---- 以上都是用户在API可配 ----*/
/*---- 以下都是OTA内部使用, 用户无感知 ----*/
void *mqtt_handle;
void *module;
void *data_mutex;
} ota_handle_t;
/**
* @brief 处理下载任务的句柄, 该句柄主要用于通过http协议从指定的url下载固件
*
*/
typedef struct {
void
*userdata; /* 组件调用recv_handler 时的入参之一, 传入用户数据 */
aiot_download_recv_handler_t recv_handler; /* 设备收到分段的固件报文时的回调函数 */
aiot_download_task_desc_t *task_desc; /* 某次下载活动的目标描述信息, 如URL等 */
aiot_sysdep_portfile_t *sysdep;
uint32_t range_start;
uint32_t range_end;
/*---- 以上都是用户在API可配 ----*/
/*---- 以下都是downloader内部使用, 用户无感知 ----*/
uint8_t download_status;
void *http_handle;
uint32_t size_fetched;
uint32_t range_size_fetched;
uint32_t content_len;
int32_t percent;
int32_t http_rsp_status_code;
void *digest_ctx;
void *data_mutex;
void *recv_mutex;
} download_handle_t;
typedef struct {
char *pos;
int len;
} ota_list_json;
#define OTA_ARRAY_MAX (20)
void *_download_deep_copy_task_desc(aiot_sysdep_portfile_t *sysdep, void *data);
int32_t _download_deep_free_task_desc(aiot_sysdep_portfile_t *sysdep, void *data);
int32_t _ota_publish_base(void *handle, char *topic_prefix, char *product_key, char *device_name, char *suffix,
char *params);
#if defined(__cplusplus)
}
#endif
#endif /* __OTA_PRIVATE_H__ */

View File

@@ -0,0 +1,3 @@
Name: 设备影子模块
SHADOW Component for Link SDK V4.0.0

View File

@@ -0,0 +1,438 @@
/**
* @file aiot_shadow_api.c
* @brief shadow模块的API接口实现, 提供更新, 删除, 获取设备影子的能力
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#include "shadow_private.h"
#include "core_string.h"
#include "core_log.h"
#include "core_global.h"
#include "core_mqtt.h"
static int32_t _shadow_get_pk_dn(aiot_sysdep_portfile_t *sysdep, char *topic, uint16_t topic_len,
char **product_key, char **device_name)
{
uint16_t pk_offset = 12; /* length of "/shadow/get/" */
uint16_t idx = 0;
uint16_t pk_len = 0, dn_len = 0;
char *pk_pos = topic + pk_offset;
char *dn_pos = NULL;
char *tmp_pk = NULL, *tmp_dn = NULL;
for (idx = pk_offset; idx < topic_len - 1; idx++) {
if (topic[idx] == '/') {
dn_pos = topic + idx + 1;
pk_len = dn_pos - pk_pos - 1;
dn_len = topic_len - idx - 1;
}
}
if (pk_len == 0 || dn_len == 0) {
return STATE_SHADOW_INTERNAL_TOPIC_ERROR;
}
tmp_pk = sysdep->core_sysdep_malloc(pk_len + 1, SHADOW_MODULE_NAME);
if (tmp_pk == NULL) {
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
memset(tmp_pk, 0, pk_len + 1);
memcpy(tmp_pk, pk_pos, pk_len);
tmp_dn = sysdep->core_sysdep_malloc(dn_len + 1, SHADOW_MODULE_NAME);
if (tmp_dn == NULL) {
sysdep->core_sysdep_free(tmp_pk);
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
memset(tmp_dn, 0, dn_len + 1);
memcpy(tmp_dn, dn_pos, dn_len);
*product_key = tmp_pk;
*device_name = tmp_dn;
return STATE_SUCCESS;
}
int32_t _shadow_int642str(int64_t input, char *output, uint8_t *output_len)
{
uint64_t temp = 0;
uint8_t len = 0;
if (input < 0) {
*output = '-';
temp = -input;
core_uint642str(temp, output + 1, &len);
if (output_len != NULL) {
*output_len = len + 1;
}
} else {
temp = input;
core_uint642str(temp, output, output_len);
}
return STATE_SUCCESS;
}
static void _shadow_recv_message_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata)
{
shadow_handle_t *shadow_handle = (shadow_handle_t *)userdata;
aiot_shadow_recv_t recv;
char *value = NULL;
uint32_t value_len = 0;
char *pk = NULL;
char *dn = NULL;
int32_t res = STATE_SUCCESS;
char *method = NULL;
uint32_t method_len = 0;
char *payload = NULL;
uint32_t payload_len = 0;
uint64_t timestamp = 0;
uint64_t version = 0;
char status[10] = { 0 };
char *version_str = NULL;
uint32_t version_strlen = 0;
if (NULL == shadow_handle->recv_handler) {
return;
}
memset(&recv, 0, sizeof(aiot_shadow_recv_t));
do {
/* get pk & dn */
if ((res = _shadow_get_pk_dn(shadow_handle->sysdep, msg->data.pub.topic, msg->data.pub.topic_len, &pk, &dn)) < 0) {
break;
}
recv.product_key = pk;
recv.device_name = dn;
/* parse the message */
if ((res = core_json_value((char *)msg->data.pub.payload, msg->data.pub.payload_len,
SHADOW_JSON_KEY_METHOD, strlen(SHADOW_JSON_KEY_METHOD), &method, &method_len) < 0) ||
(res = core_json_value((char *)msg->data.pub.payload, msg->data.pub.payload_len,
SHADOW_JSON_KEY_TIMESTAMP, strlen(SHADOW_JSON_KEY_TIMESTAMP), &value, &value_len) < 0) ||
(res = core_str2uint64(value, value_len, &timestamp) < 0) ||
(res = core_json_value((char *)msg->data.pub.payload, msg->data.pub.payload_len,
SHADOW_JSON_KEY_PAYLOAD, strlen(SHADOW_JSON_KEY_PAYLOAD), &payload, &payload_len) < 0)) {
break;
}
if (core_json_value((char *)msg->data.pub.payload, msg->data.pub.payload_len,
SHADOW_JSON_KEY_VERSION, strlen(SHADOW_JSON_KEY_VERSION), &version_str, &version_strlen) == STATE_SUCCESS) {
core_str2uint64(version_str, version_strlen, &version);
}
/* control message */
if (method_len == strlen("control") && !memcmp(method, "control", method_len)) {
/* get version */
if (version_str == NULL) {
break;
}
core_log(shadow_handle->sysdep, STATE_SHADOW_LOG_RECV, "SHADOW recv control message\r\n");
recv.type = AIOT_SHADOWRECV_CONTROL;
recv.data.control.payload = payload;
recv.data.control.payload_len = payload_len;
recv.data.control.version = version;
} /* reply message */
else if (method_len == strlen("reply") && !memcmp(method, "reply", method_len)) {
/* get_reply */
if (core_json_value((char *)msg->data.pub.payload, msg->data.pub.payload_len,
SHADOW_JSON_KEY_STATE, strlen(SHADOW_JSON_KEY_STATE), &value, &value_len) < 0) {
if ((res = core_json_value((char *)msg->data.pub.payload, msg->data.pub.payload_len,
SHADOW_JSON_KEY_STATUS, strlen(SHADOW_JSON_KEY_STATUS), &value, &value_len) < 0) ||
(value_len >= sizeof(status))) {
break;
}
core_log(shadow_handle->sysdep, STATE_SHADOW_LOG_RECV, "SHADOW recv get_reply message\r\n");
memcpy(status, value, value_len);
recv.type = AIOT_SHADOWRECV_GENERIC_REPLY;
recv.data.generic_reply.payload = payload;
recv.data.generic_reply.payload_len = payload_len;
recv.data.generic_reply.status = status;
recv.data.generic_reply.timestamp = timestamp;
} /* get_reply */
else {
if (version_str == NULL) {
break;
}
core_log(shadow_handle->sysdep, STATE_SHADOW_LOG_RECV, "SHADOW recv generic_reply message\r\n");
recv.type = AIOT_SHADOWRECV_GET_REPLY;
recv.data.get_reply.payload = payload;
recv.data.get_reply.payload_len = payload_len;
recv.data.get_reply.version = version;
}
} else {
break;
}
shadow_handle->recv_handler(shadow_handle, &recv, shadow_handle->userdata);
shadow_handle->sysdep->core_sysdep_free(pk);
shadow_handle->sysdep->core_sysdep_free(dn);
return;
} while (0);
/* invalid message log */
core_log(shadow_handle->sysdep, SATAE_SHADOW_LOG_PARSE_RECV_MSG_FAILED, "SHADOW parse recv message failed\r\n");
if (pk != NULL) {
shadow_handle->sysdep->core_sysdep_free(pk);
}
if (dn != NULL) {
shadow_handle->sysdep->core_sysdep_free(dn);
}
}
static void _shadow_core_mqtt_process_handler(void *context, aiot_mqtt_event_t *event, core_mqtt_event_t *core_event)
{
shadow_handle_t *shadow_handle = (shadow_handle_t *)context;
if (core_event != NULL) {
switch (core_event->type) {
case CORE_MQTTEVT_DEINIT: {
shadow_handle->mqtt_handle = NULL;
return;
}
break;
default: {
}
break;
}
}
}
static int32_t _shadow_core_mqtt_operate_process_handler(shadow_handle_t *shadow_handle, core_mqtt_option_t option)
{
core_mqtt_process_data_t process_data;
memset(&process_data, 0, sizeof(core_mqtt_process_data_t));
process_data.handler = _shadow_core_mqtt_process_handler;
process_data.context = shadow_handle;
return core_mqtt_setopt(shadow_handle->mqtt_handle, option, &process_data);
}
void *aiot_shadow_init(void)
{
aiot_sysdep_portfile_t *sysdep = aiot_sysdep_get_portfile();
shadow_handle_t *shadow_handle = NULL;
if (NULL == sysdep) {
return NULL;
}
shadow_handle = sysdep->core_sysdep_malloc(sizeof(shadow_handle_t), SHADOW_MODULE_NAME);
if (NULL == shadow_handle) {
return NULL;
}
memset(shadow_handle, 0, sizeof(shadow_handle_t));
shadow_handle->sysdep = sysdep;
return shadow_handle;
}
int32_t aiot_shadow_setopt(void *handle, aiot_shadow_option_t option, void *data)
{
shadow_handle_t *shadow_handle;
int32_t res = STATE_SUCCESS;
if (NULL == handle || NULL == data) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (option >= AIOT_SHADOWOPT_MAX) {
return STATE_USER_INPUT_OUT_RANGE;
}
shadow_handle = (shadow_handle_t *)handle;
switch (option) {
case AIOT_SHADOWOPT_MQTT_HANDLE: {
aiot_mqtt_topic_map_t topic_mapping;
shadow_handle->mqtt_handle = data;
/* setup mqtt topic mapping */
topic_mapping.topic = SHADOW_GET_TOPIC;
topic_mapping.handler = _shadow_recv_message_handler;
topic_mapping.userdata = handle;
res = aiot_mqtt_setopt(data, AIOT_MQTTOPT_APPEND_TOPIC_MAP, &topic_mapping);
if (res >= STATE_SUCCESS) {
res = _shadow_core_mqtt_operate_process_handler(shadow_handle, CORE_MQTTOPT_APPEND_PROCESS_HANDLER);
}
}
break;
case AIOT_SHADOWOPT_RECV_HANDLER: {
shadow_handle->recv_handler = (aiot_shadow_recv_handler_t)data;
}
break;
case AIOT_SHADOWOPT_USERDATA: {
shadow_handle->userdata = data;
}
break;
default:
break;
}
return res;
}
int32_t aiot_shadow_send(void *handle, aiot_shadow_msg_t *msg)
{
shadow_handle_t *shadow_handle = NULL;
char *topic = NULL;
char *payload = NULL;
int32_t res = STATE_SUCCESS;
if (NULL == handle || NULL == msg) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (msg->type >= AIOT_SHADOWMSG_MAX) {
return STATE_USER_INPUT_OUT_RANGE;
}
shadow_handle = (shadow_handle_t *)handle;
if (NULL == shadow_handle->mqtt_handle) {
return STATE_SHADOW_MQTT_HANDLE_IS_NULL;
}
/* construct mqtt topic */
{
char *src[2];
char *pk = NULL, *dn = NULL;
pk = core_mqtt_get_product_key(shadow_handle->mqtt_handle);
dn = core_mqtt_get_device_name(shadow_handle->mqtt_handle);
if (NULL == msg->product_key && NULL == pk) {
return STATE_USER_INPUT_MISSING_PRODUCT_KEY;
}
if (NULL == msg->device_name && NULL == dn) {
return STATE_USER_INPUT_MISSING_DEVICE_NAME;
}
src[0] = (msg->product_key != NULL) ? msg->product_key : pk;
src[1] = (msg->device_name != NULL) ? msg->device_name : dn;
res = core_sprintf(shadow_handle->sysdep, &topic, SHADOW_UPDATE_TOPIC_FMT, src, sizeof(src) / sizeof(char *),
SHADOW_MODULE_NAME);
if (NULL == topic) {
return res;
}
}
/* construct payload */
switch (msg->type) {
case AIOT_SHADOWMSG_UPDATE: {
char version_string[21] = { 0 };
char *src[3] = { NULL };
if (NULL == msg->data.update.reported) {
res = STATE_SHADOW_REPORTED_DATA_IS_NULL;
break;
}
_shadow_int642str(msg->data.update.version, version_string, NULL);
src[0] = "update";
src[1] = msg->data.update.reported;
src[2] = version_string;
res = core_sprintf(shadow_handle->sysdep, &payload, SHADOW_PAYLOAD_REQ_FMT,
src, sizeof(src) / sizeof(char *), SHADOW_MODULE_NAME);
if (res < STATE_SUCCESS) {
break;
}
}
break;
case AIOT_SHADOWMSG_CLEAN_DESIRED: {
char version_string[21] = { 0 };
char *src[1] = { NULL };
_shadow_int642str(msg->data.clean_desired.version, version_string, NULL);
src[0] = version_string;
res = core_sprintf(shadow_handle->sysdep, &payload, SHADOW_PAYLOAD_CLEAN_FMT,
src, sizeof(src) / sizeof(char *), SHADOW_MODULE_NAME);
if (res < STATE_SUCCESS) {
break;
}
}
break;
case AIOT_SHADOWMSG_GET: {
payload = SHADOW_PAYLOAD_GET;
}
break;
case AIOT_SHADOWMSG_DELETE_REPORTED: {
char version_string[21] = { 0 };
char *src[3] = { NULL };
if (NULL == msg->data.delete_reporte.reported) {
res = STATE_SHADOW_REPORTED_DATA_IS_NULL;
break;
}
_shadow_int642str(msg->data.delete_reporte.version, version_string, NULL);
src[0] = "delete";
src[1] = msg->data.update.reported;
src[2] = version_string;
res = core_sprintf(shadow_handle->sysdep, &payload, SHADOW_PAYLOAD_REQ_FMT,
src, sizeof(src) / sizeof(char *), SHADOW_MODULE_NAME);
if (res < STATE_SUCCESS) {
break;
}
}
break;
default: {
res = STATE_USER_INPUT_OUT_RANGE;
}
break;
}
if (NULL == payload) {
shadow_handle->sysdep->core_sysdep_free(topic);
return res;
}
res = aiot_mqtt_pub(shadow_handle->mqtt_handle, topic, (uint8_t *)payload, strlen(payload), 0);
shadow_handle->sysdep->core_sysdep_free(topic);
if (msg->type != AIOT_SHADOWMSG_GET) {
shadow_handle->sysdep->core_sysdep_free(payload);
}
return res;
}
int32_t aiot_shadow_deinit(void **p_handle)
{
shadow_handle_t *shadow_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
aiot_mqtt_topic_map_t topic_mapping;
if (NULL == p_handle || NULL == *p_handle) {
return STATE_USER_INPUT_NULL_POINTER;
}
shadow_handle = *p_handle;
sysdep = shadow_handle->sysdep;
*p_handle = NULL;
_shadow_core_mqtt_operate_process_handler(shadow_handle, CORE_MQTTOPT_REMOVE_PROCESS_HANDLER);
/* remove mqtt topic mapping */
memset(&topic_mapping, 0, sizeof(aiot_mqtt_topic_map_t));
topic_mapping.topic = SHADOW_GET_TOPIC;
topic_mapping.handler = _shadow_recv_message_handler;
aiot_mqtt_setopt(shadow_handle->mqtt_handle, AIOT_MQTTOPT_REMOVE_TOPIC_MAP, &topic_mapping);
sysdep->core_sysdep_free(shadow_handle);
return STATE_SUCCESS;
}

View File

@@ -0,0 +1,411 @@
/**
* @file aiot_shadow_api.h
* @brief shadow模块头文件, 提供更新, 删除, 获取设备影子的能力
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
* @details
*
* 请按照以下流程使用API
*
* 1. 在使用设备影子模块前, 用户应首先创建好一个MQTT实例
*
* 2. 调用`aiot_shadow_init`创建一个设备影子实例, 保存实例句柄
*
* 3. 调用`aiot_shadow_setopt`配置`AIOT_SHADOWOPT_MQTT_HANDLE`选项以设置MQTT句柄, 此选项为强制配置选项
*
* 4. 调用`aiot_shadow_setopt`配置`AIOT_SHADOWOPT_RECV_HANDLER`和`AIOT_SHADOWOPT_USERDATA`选项以注册数据接受回调函数和用户上下文数据指针
*
* 5. 在使用`aiot_shadow_send`发送消息前, 应先完成MQTT实例的建连
*
* 6. 调动`aiot_shadow_send`发送更新设备影子, 获取设备影子或删除设备等消息到云平台, 在注册的回调函数中处理各种类型的云端应答消息或主动下推消息
*
*/
#ifndef __AIOT_SHADOW_API_H__
#define __AIOT_SHADOW_API_H__
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdint.h>
/**
* @brief -0x1300~-0x13FF表达SDK在data-model模块内的状态码
*/
#define STATE_SHADOW_BASE (-0x1300)
/**
* @brief 用户未调用@ref aiot_shadow_setopt 配置MQTT句柄
*/
#define STATE_SHADOW_MQTT_HANDLE_IS_NULL (-0x1301)
/**
* @brief 用户上报@ref AIOT_SHADOWMSG_UPDATE 或 @ref AIOT_SHADOWMSG_DELETE_REPORTED 消息时, 消息结构体中的reported为NULL
*
*/
#define STATE_SHADOW_REPORTED_DATA_IS_NULL (-0x1302)
/**
* @brief 解析下行数据对应的topic时发生错误
*/
#define STATE_SHADOW_INTERNAL_TOPIC_ERROR (-0x1303)
/**
* @brief 接收到服务器下行消息时的日志状态码
*/
#define STATE_SHADOW_LOG_RECV (-0x1304)
/**
* @brief 解析服务器下行消息失败时的日志状态码
*/
#define SATAE_SHADOW_LOG_PARSE_RECV_MSG_FAILED (-0x1305)
/**
* @brief @ref aiot_shadow_setopt 接口的option参数可选值.
*
* @details 下文每个选项中的数据类型, 指的是@ref aiot_shadow_setopt 中, data参数的数据类型
*
* 当data的数据类型是char *时, 以配置@ref AIOT_SHADOWOPT_PRODUCT_KEY 为例:
*
* char *product_key = "xxx";
* aiot_shadow_setopt(shadow_handle, AIOT_SHADOWOPT_PRODUCT_KEY, product_key);
*
*/
typedef enum {
/**
* @brief 模块依赖的MQTT句柄
*
* @details
*
* shadow模块依赖底层的MQTT模块, 用户必需配置正确的MQTT句柄, 否则无法正常工作, 数据类型为(void *)
*/
AIOT_SHADOWOPT_MQTT_HANDLE,
/**
* @brief 设置回调, 它在SDK收到网络报文的时候被调用, 告知用户, 数据类型为(aiot_shadow_recv_handler_t)
*/
AIOT_SHADOWOPT_RECV_HANDLER,
/**
* @brief 用户需要SDK暂存的上下文, 数据类型为(void *)
*
* @details 这个上下文指针会在 AIOT_SHADOWOPT_RECV_HANDLER 设置的回调被调用时, 由SDK传给用户
*/
AIOT_SHADOWOPT_USERDATA,
/**
* @brief 配置选项数量最大值, 不可用作配置参数
*/
AIOT_SHADOWOPT_MAX,
} aiot_shadow_option_t;
/**
* @brief shadow模块发送消息类型
*
* @details
*
* 这个枚举类型包括了shadow模块支持发送的所有数据类型, 不同的消息类型将对于不同的消息结构体
*
*/
typedef enum {
/**
* @brief 更新设备影子中的reported值, 消息结构体参考@ref aiot_shadow_msg_update_t
*
*/
AIOT_SHADOWMSG_UPDATE,
/**
* @brief 清空设备影子的desired值, 消息结构体参考@ref aiot_shadow_msg_clean_desired_t
*
*/
AIOT_SHADOWMSG_CLEAN_DESIRED,
/**
* @brief 获取设备影子, 消息结构体参考@ref aiot_shadow_msg_get_t
*
*/
AIOT_SHADOWMSG_GET,
/**
* @brief 删除设备影子的部分或全部reported值, 消息结构体参考@ref aiot_shadow_msg_delete_reported_t
*
*/
AIOT_SHADOWMSG_DELETE_REPORTED,
/**
* @brief 消息数量最大值, 不可用作消息类型
*/
AIOT_SHADOWMSG_MAX,
} aiot_shadow_msg_type_t;
/**
* @brief 用于<b>更新设备影子中的reported数据</b>的消息结构体
*/
typedef struct {
/**
* @brief 设备影子reported object字符串, <b>必须为以结束符'\0'结尾的字符串</b>, 如"{\"LightSwitch\": 1}"
*/
char *reported;
/**
* @brief 设备影子的目标版本, <b>必须大于设备影子的当前版本</b>, 若设置为-1将清空设备影子数据, 并将设备影子版本更新为0
*/
int64_t version;
} aiot_shadow_msg_update_t;
/**
* @brief 用于<b>清除设备影子中的desired数据</b>的消息结构体
*/
typedef struct {
/**
* @brief 设备影子的目标版本, <b>必须大于设备影子的当前版本</b>
*/
int64_t version;
} aiot_shadow_msg_clean_desired_t;
/**
* @brief 用于<b>获取设备影子</b>的消息结构体,
*/
typedef struct {
/**
* @brief 保留字段
*/
uint32_t resevered;
} aiot_shadow_msg_get_t;
/**
* @brief 用于<b>删除设备影子中的reported数据</b>的消息结构体
*/
typedef struct {
/**
* @brief 用户将要删除的reported数据, <b>必须为以结束符'\0'结尾的字符串</b>. \n
* 若要删除全部reported数据, 则应填写"\"null\""字符串 \n
* 若要删除部分reported数据, 则将对应的值定义为null, 如只清除LightSwitch的值应填写"{\"LightSwitch\":\"null\"}"
*/
char *reported;
/**
* @brief 设备影子的目标版本, <b>必须大于设备影子的当前版本</b>
*/
int64_t version;
} aiot_shadow_msg_delete_reported_t;
/**
* @brief data-model模块发送消息的消息结构体
*/
typedef struct {
/**
* @brief 消息所属设备的product_key, 若为NULL则使用通过aiot_shadow_setopt配置的product_key \n
* 在网关子设备场景下, 可通过指定为子设备的product_key来发送子设备的消息到云端
*/
char *product_key;
/**
* @brief 消息所属设备的device_name, 若为NULL则使用通过aiot_shadow_setopt配置的device_name \n
* 在网关子设备场景下, 可通过指定为子设备的product_key来发送子设备的消息到云端
*/
char *device_name;
/**
* @brief 消息类型, 可参考@ref aiot_shadow_msg_type_t
*/
aiot_shadow_msg_type_t type;
/**
* @brief 消息数据联合体, 不同的消息类型将使用不同的消息结构体
*/
union {
aiot_shadow_msg_update_t update;
aiot_shadow_msg_clean_desired_t clean_desired;
aiot_shadow_msg_get_t get;
aiot_shadow_msg_delete_reported_t delete_reporte;
} data;
} aiot_shadow_msg_t;
/**
* @brief shadow模块收到从网络上来的报文时, 通知用户的报文类型
*/
typedef enum {
/**
* @brief 设备发送 @ref AIOT_SHADOWMSG_UPDATE, @ref AIOT_SHADOWMSG_CLEAN_DESIRED 或 @ref AIOT_SHADOWMSG_DELETE_REPORTED 这3这类型消息后, 云端返回的应答消息, \n
* 消息数据结构体参考 @ref aiot_shadow_recv_generic_reply_t
*/
AIOT_SHADOWRECV_GENERIC_REPLY,
/**
* @brief 设备在线时, 云端自动下发的影子内容, 消息数据结构体参考 @ref aiot_shadow_recv_control_t
*/
AIOT_SHADOWRECV_CONTROL,
/**
* @brief 主动获取设备影子内容云端返回的影子内容, 消息数据结构体参考 @ref aiot_shadow_recv_get_reply_t
*/
AIOT_SHADOWRECV_GET_REPLY,
} aiot_shadow_recv_type_t;
/**
* @brief 设备发送 @ref AIOT_SHADOWMSG_UPDATE, @ref AIOT_SHADOWMSG_CLEAN_DESIRED 或 @ref AIOT_SHADOWMSG_DELETE_REPORTED 类型消息后, 云端返回的应答消息
*/
typedef struct {
/**
* @brief 指向应答数据的指针
*/
char *payload;
/**
* @brief 应答数据长度
*/
uint32_t payload_len;
/**
* @brief 应答状态字符串, 云端处理成功则为<b>success</b>, 发送消息错误则为<b>error</b>, 错误信息和错误码放在在payload中
*/
char *status;
/**
* @brief 应答报文对应的时间戳
*/
uint64_t timestamp;
} aiot_shadow_recv_generic_reply_t;
/**
* @brief 如果设备在线, 用户应用调用云端API<a href="https://help.aliyun.com/document_detail/69954.html#doc-api-Iot-UpdateDeviceShadow">UpdateDeviceShadow</a>后云端下推的消息
*/
typedef struct {
/**
* @brief 指向设备影子数据的指针
*/
char *payload;
/**
* @brief 设备影子数据长度
*/
uint32_t payload_len;
/**
* @brief 设备影子版本
*/
uint64_t version;
} aiot_shadow_recv_control_t;
/**
* @brief 设备发送 @ref AIOT_SHADOWMSG_GET 类型消息后, 云端返回的设备影子数据
*/
typedef struct {
/**
* @brief 指向设备影子数据的指针
*/
char *payload;
/**
* @brief 设备影子数据长度
*/
uint32_t payload_len;
/**
* @brief 设备影子版本号
*/
uint64_t version;
} aiot_shadow_recv_get_reply_t;
/**
* @brief shadow模块收到从网络上来的报文时, 通知用户的报文内容
*/
typedef struct {
/**
* @brief 消息所属设备的product_key
*/
char *product_key;
/**
* @brief 消息所属设备的device_name
*/
char *device_name;
/**
* @brief 报文内容所对应的报文类型, 更多信息请参考@ref aiot_shadow_recv_type_t
*/
aiot_shadow_recv_type_t type;
/**
* @brief 消息数据联合体, 不同的消息类型将使用不同的消息结构体
*/
union {
aiot_shadow_recv_generic_reply_t generic_reply;
aiot_shadow_recv_control_t control;
aiot_shadow_recv_get_reply_t get_reply;
} data;
} aiot_shadow_recv_t;
/**
* @brief shadow模块收到从网络上来的报文时, 通知用户所调用的数据回调函数
*
* @param[in] handle shadow会话句柄
* @param[in] recv shadow接受消息结构体, 存放收到的shadow报文内容
* @param[in] userdata 指向用户上下文数据的指针, 这个指针由用户通过调用@ref aiot_shadow_setopt 配置@ref AIOT_SHADOWOPT_USERDATA 选项设置
*
* @return void
*/
typedef void (* aiot_shadow_recv_handler_t)(void *handle,
const aiot_shadow_recv_t *recv, void *userdata);
/**
* @brief 创建shadow会话实例, 并以默认值配置会话参数
*
* @return void *
* @retval 非NULL shadow实例的句柄
* @retval NULL 初始化失败, 一般是内存分配失败导致
*
*/
void *aiot_shadow_init(void);
/**
* @brief 配置shadow会话
*
* @param[in] handle shadow会话句柄
* @param[in] option 配置选项, 更多信息请参考@ref aiot_shadow_option_t
* @param[in] data 配置选项数据, 更多信息请参考@ref aiot_shadow_option_t
*
* @return int32_t
* @retval STATE_SUCCESS 参数配置成功
* @retval STATE_USER_INPUT_NULL_POINTER 入参handle或data为NULL
* @retval STATE_USER_INPUT_OUT_RANGE 入参optioin的枚举值>=AIOT_SHADOWOPT_MAX
* @retval others 参考@ref aiot_state_api.h
*
*/
int32_t aiot_shadow_setopt(void *handle, aiot_shadow_option_t option, void *data);
/**
* @brief 向服务器发送shadow消息请求
*
* @param[in] handle shadow会话句柄
* @param[in] msg 消息结构体, 可指定发送消息的设备<i>productKey</i>, <i>deviceName</i>; 消息类型, 消息数据等, 更多信息请参考@ref aiot_shadow_msg_t
*
* @return int32_t
* @retval STATE_SUCCESS 请求发送成功
* @retval STATE_USER_INPUT_NULL_POINTER 入参<i>handle</i>或<i>msg</i>为NULL
* @retval STATE_USER_INPUT_OUT_RANGE 入参<i>msg</i>的结构体成员<i>type</i> >= AIOT_SHADOWMSG_MAX
* @retval STATE_SYS_DEPEND_MALLOC_FAILED 内存分配失败
* @retval STATE_SHADOW_MQTT_HANDLE_IS_NULL 用户未调用@ref aiot_shadow_setopt 配置MQTT句柄
* @retval others 参考@ref aiot_state_api.h 或@ref STATE_SHADOW_BASE 中对应的错误码说明
*
*/
int32_t aiot_shadow_send(void *handle, aiot_shadow_msg_t *msg);
/**
* @brief 结束shadow会话, 销毁实例并回收资源
*
* @param[in] handle 指向shadow会话句柄的指针
*
* @return int32_t
* @retval STATE_SUCCESS 执行成功
* @retval <STATE_SUCCESS 执行失败
*
*/
int32_t aiot_shadow_deinit(void **handle);
#if defined(__cplusplus)
}
#endif
#endif /* __AIOT_SHADOW_API_H__ */

View File

@@ -0,0 +1,53 @@
/**
* @file shadow_private.h
* @brief shadow模块内部的宏定义和数据结构声明, 不面向其它模块, 更不面向用户
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#ifndef __SHADOW_PRIVATE_H__
#define __SHADOW_PRIVATE_H__
#if defined(__cplusplus)
extern "C" {
#endif
/* 用这种方式包含标准C库的头文件 */
#include "core_stdinc.h"
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
#include "aiot_shadow_api.h"
/* shadow模块内部的会话句柄结构体, SDK用户不可见, 只能得到void *handle类型的指针 */
typedef struct {
aiot_sysdep_portfile_t *sysdep;
void *mqtt_handle;
aiot_shadow_recv_handler_t recv_handler;
void *userdata;
} shadow_handle_t;
#define SHADOW_MODULE_NAME "shadow"
#define SHADOW_UPDATE_TOPIC_FMT "/shadow/update/%s/%s"
#define SHADOW_GET_TOPIC "/shadow/get/+/+"
#define SHADOW_PAYLOAD_REQ_FMT "{\"method\":\"%s\",\"state\":{\"reported\":%s},\"version\":%s}"
#define SHADOW_PAYLOAD_CLEAN_FMT "{\"method\":\"update\",\"state\":{\"desired\":\"null\"},\"version\":%s}"
#define SHADOW_PAYLOAD_GET "{\"method\":\"get\"}"
#define SHADOW_JSON_KEY_METHOD "method"
#define SHADOW_JSON_KEY_PAYLOAD "payload"
#define SHADOW_JSON_KEY_STATUS "status"
#define SHADOW_JSON_KEY_TIMESTAMP "timestamp"
#define SHADOW_JSON_KEY_STATE "state"
#define SHADOW_JSON_KEY_VERSION "version"
#if defined(__cplusplus)
}
#endif
#endif /* __SHADOW_PRIVATE_H__ */

View File

@@ -0,0 +1,2 @@
Name: 任务管理模块
Task Component for Link SDK V4.0.0

View File

@@ -0,0 +1,777 @@
/**
* @file aiot_task_api.c
* @brief TASK模块接口实现文件, 其中包含了TASK的所有用户API
* @date 2019-12-27
*
* @copyright Copyright (C) 2015-2018 Alibaba Group Holding Limited
*
*/
#include "core_mqtt.h"
#include "core_string.h"
#include "core_log.h"
#include "core_global.h"
#include "task_private.h"
#include "aiot_task_api.h"
static void _task_recv_notify_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata);
static void _task_recv_get_reply_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata);
static void _task_recv_update_reply_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata);
static const task_status_map_t g_task_stauts_mapping[] = {
{"QUEUED", AIOT_TASK_STATUS_QUEUED},
{"SENT", AIOT_TASK_STATUS_SENT},
{"IN_PROGRESS", AIOT_TASK_STATUS_IN_PROGRESS},
{"SUCCEEDED", AIOT_TASK_STATUS_SUCCEEDED},
{"FAILED", AIOT_TASK_STATUS_FAILED},
{"REJECTED", AIOT_TASK_STATUS_REJECTED},
{"CANCELLED", AIOT_TASK_STATUS_CANCELLED},
{"REMOVED", AIOT_TASK_STATUS_REMOVED},
{"TIMED_OUT", AIOT_TASK_STATUS_TIMED_OUT}
};
static const task_recv_topic_map_t g_task_recv_topic_mapping[] = {
{
"/sys/+/+/thing/job/notify",
_task_recv_notify_handler,
},
{
"/sys/+/+/thing/job/get_reply",
_task_recv_get_reply_handler,
},
{
"/sys/+/+/thing/job/update_reply",
_task_recv_update_reply_handler,
}
};
static char *_task_convert_status_to_str(aiot_task_status_t status)
{
uint32_t i = 0;
for (i = 0; i < sizeof(g_task_stauts_mapping) / sizeof(task_status_map_t); i++) {
if (g_task_stauts_mapping[i].status == status) {
return g_task_stauts_mapping[i].str;
}
}
return NULL;
}
static aiot_task_status_t _task_cover_status_to_enum(char *str)
{
uint32_t i = 0;
if (NULL == str) {
return AIOT_TASK_STATUS_FAILED;
}
for (i = 0; i < sizeof(g_task_stauts_mapping) / sizeof(task_status_map_t); i++) {
if (strcmp(g_task_stauts_mapping[i].str, str) == 0) {
return g_task_stauts_mapping[i].status;
}
}
return AIOT_TASK_STATUS_FAILED;
}
static int32_t _task_parse_json(aiot_sysdep_portfile_t *sysdep, const void *input, uint32_t input_len, char *key_word,
char **out)
{
int32_t res = STATE_SUCCESS;
char *value = NULL, *buffer = NULL;
uint32_t value_len = 0, buffer_len = 0;
res = core_json_value((const char *)input, input_len, key_word, strlen(key_word), &value, &value_len);
if (res != STATE_SUCCESS) {
return STATE_TASK_PARSE_JSON_ERROR;
}
buffer_len = value_len + 1;
buffer = sysdep->core_sysdep_malloc(buffer_len, TASK_MODULE_NAME);
if (NULL == buffer) {
return STATE_TASK_PARSE_JSON_MALLOC_FAILED;
}
memset(buffer, 0, buffer_len);
memcpy(buffer, value, value_len);
*out = buffer;
return res;
}
static int32_t _task_parse_task_detail(task_handle_t *task_handle, const char *payload, uint32_t payload_len,
task_desc_t *task)
{
int32_t res = STATE_SUCCESS;
char *status = NULL;
char *job_file_key = "jobFile";
char *job_file_value = NULL;
uint32_t job_file_len = 0;
if (STATE_SUCCESS != _task_parse_json(task_handle->sysdep, payload, payload_len, "taskId", &(task->task_id))) {
return STATE_TASK_PARSE_JSON_MALLOC_FAILED;
}
if (STATE_SUCCESS != _task_parse_json(task_handle->sysdep, payload, payload_len, "status", &status)) {
return STATE_TASK_PARSE_JSON_MALLOC_FAILED;
}
task->status = _task_cover_status_to_enum(status);
task_handle->sysdep->core_sysdep_free(status);
if (STATE_SUCCESS != _task_parse_json(task_handle->sysdep, payload, payload_len, "jobDocument",
&(task->job_document))) {
core_log(task_handle->sysdep, STATE_TASK_RECV_NOTIFY, "parse jobDocument failed\r\n");
};
if ((res = core_json_value(payload, payload_len, job_file_key, (uint32_t)strlen(job_file_key),
&job_file_value, &job_file_len)) != STATE_SUCCESS) {
return STATE_SUCCESS;
}
if (STATE_SUCCESS != _task_parse_json(task_handle->sysdep, job_file_value, job_file_len, "signMethod",
&(task->sign_method))) {
return res;
}
if (STATE_SUCCESS != _task_parse_json(task_handle->sysdep, job_file_value, job_file_len, "sign", &(task->sign))) {
return res;
}
if (STATE_SUCCESS != _task_parse_json(task_handle->sysdep, job_file_value, job_file_len, "fileUrl",
&(task->document_file_url))) {
return res;
}
return res;
}
static void _task_free_task_list(task_handle_t *task_handle, task_get_list_reply_t *list)
{
int32_t i = 0;
if (NULL == list->tasks) {
return;
}
for (i = 0; i < list->number; i++) {
if (list->tasks[i].task_id) {
task_handle->sysdep->core_sysdep_free(list->tasks[i].task_id);
}
}
task_handle->sysdep->core_sysdep_free(list->tasks);
}
static void _task_free_task_detail(task_handle_t *task_handle, task_desc_t *task)
{
aiot_sysdep_portfile_t *sysdep = aiot_sysdep_get_portfile();
if (NULL == task) {
return;
}
if (task->task_id) {
sysdep->core_sysdep_free(task->task_id);
}
if (task->job_document) {
sysdep->core_sysdep_free(task->job_document);
}
if (task->sign_method) {
sysdep->core_sysdep_free(task->sign_method);
}
if (task->sign) {
sysdep->core_sysdep_free(task->sign);
}
if (task->document_file_url) {
sysdep->core_sysdep_free(task->document_file_url);
}
}
static void _task_free_update_reply(task_update_reply_t *reply)
{
aiot_sysdep_portfile_t *sysdep = aiot_sysdep_get_portfile();
if (NULL == reply) {
return;
}
if (reply->task_id) {
sysdep->core_sysdep_free(reply->task_id);
}
}
static int32_t _task_send_notify_reply(task_handle_t *task_handle, char *msg_id, uint32_t code)
{
char *topic = NULL, *payload = NULL;
char code_string[11] = { 0 };
char *src[2] = { NULL };
int32_t res = STATE_SUCCESS;
core_uint2str(code, code_string, NULL);
src[0] = msg_id;
src[1] = code_string;
res = core_sprintf(task_handle->sysdep, &payload, TASK_NOTIFY_REPLY_FMT, src, sizeof(src) / sizeof(char *),
TASK_MODULE_NAME);
if (res < 0) {
return res;
}
char *topic_src[] = { core_mqtt_get_product_key(task_handle->mqtt_handle), core_mqtt_get_device_name(task_handle->mqtt_handle) };
res = core_sprintf(task_handle->sysdep, &topic, TASK_NOTIFY_REPLY_TOPIC_FMT, topic_src,
sizeof(topic_src) / sizeof(char *),
TASK_MODULE_NAME);
if (res < STATE_SUCCESS) {
task_handle->sysdep->core_sysdep_free(payload);
return res;
}
res = aiot_mqtt_pub(task_handle->mqtt_handle, (char *)topic, (uint8_t *)payload, strlen(payload), 0);
task_handle->sysdep->core_sysdep_free(topic);
task_handle->sysdep->core_sysdep_free(payload);
return res;
}
static int32_t _task_parse_notify(task_handle_t *task_handle, const char *payload, uint32_t payload_len,
task_desc_t *task)
{
int32_t res = STATE_SUCCESS;
char *params_key = "params";
char *params_value = NULL;
uint32_t params_len = 0;
char *task_key = "task";
char *task_value = NULL;
uint32_t task_len = 0;
if ((res = core_json_value(payload, payload_len, params_key, (uint32_t)strlen(params_key),
&params_value, &params_len)) != STATE_SUCCESS) {
return res;
}
if ((res = core_json_value(params_value, params_len, task_key, (uint32_t)strlen(task_key),
&task_value, &task_len)) != STATE_SUCCESS) {
return res;
}
res = _task_parse_task_detail(task_handle, task_value, task_len, task);
if (STATE_SUCCESS != res) {
core_log(task_handle->sysdep, STATE_TASK_RECV_NOTIFY, "parse detail failed\r\n");
}
return res;
}
static void _task_recv_notify_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata)
{
task_handle_t *task_handle = (task_handle_t *)userdata;
aiot_task_recv_t recv;
int32_t res = STATE_SUCCESS;
char *id = NULL;
if (NULL == task_handle->recv_handler) {
return;
}
memset(&recv, 0, sizeof(aiot_task_recv_t));
recv.type = AIOT_TASKRECV_NOTIFY;
core_log(task_handle->sysdep, STATE_TASK_RECV_NOTIFY, "task recv notify\r\n");
if (STATE_SUCCESS != _task_parse_json(task_handle->sysdep, (char *)msg->data.pub.payload, msg->data.pub.payload_len,
"id", &id)) {
return;
}
_task_send_notify_reply(task_handle, id, 200);
if (NULL != id) {
task_handle->sysdep->core_sysdep_free(id);
}
if ((res = _task_parse_notify(task_handle, (char *)msg->data.pub.payload, msg->data.pub.payload_len,
&(recv.data.notify))) == STATE_SUCCESS) {
task_handle->recv_handler(task_handle, &recv, task_handle->userdata);
} else {
core_log(task_handle->sysdep, STATE_TASK_PARSE_NOTIFY_FAILED, "task parse notify failed\r\n");
}
_task_free_task_detail(task_handle, &(recv.data.notify));
}
static int32_t _task_parse_get_list_reply_array(char *str, int32_t str_len, task_list_json *array)
{
int32_t num = 0, len = 0;
int32_t i = 0, new = 0;
if ((NULL == str) || (str_len <= 2)) {
return 0;
}
str++;
if (*str == ']') { /* empty array */
return 0;
}
while ((i < str_len) && (num < TASK_GET_LIST_REPLY_ARRAY_MAX)) {
if (*str == '{') {
len = 1;
new = 1;
array[num].pos = str;
} else if (*str == '}') {
len++;
if (new == 1) {
array[num].len = len;
num++;
new = 0;
}
} else {
len++;
}
str++;
i++;
}
return num;
}
static uint32_t _task_parse_get_list_reply(task_handle_t *task_handle, void *input, uint32_t input_len,
task_get_list_reply_t *reply)
{
int32_t res = STATE_SUCCESS;
task_list_json json_array[TASK_GET_LIST_REPLY_ARRAY_MAX];
int32_t num = _task_parse_get_list_reply_array(input, input_len, json_array);
reply->number = num;
if (0 == num) {
return res;
}
task_summary_t *array = task_handle->sysdep->core_sysdep_malloc(sizeof(task_summary_t) * num, TASK_MODULE_NAME);
if (NULL == array) {
return STATE_TASK_PARSE_JSON_MALLOC_FAILED;
}
memset(array, 0, sizeof(task_summary_t)*num);
reply->tasks = array;
for (int32_t i = 0; i < num; i++) {
if (STATE_SUCCESS != _task_parse_json(task_handle->sysdep, json_array[i].pos, json_array[i].len, "taskId",
&(array[i].task_id))) {
return res;
}
char *status = NULL;
if (STATE_SUCCESS != _task_parse_json(task_handle->sysdep, json_array[i].pos, json_array[i].len, "status", &status)) {
return res;
}
array[i].status = _task_cover_status_to_enum(status);
task_handle->sysdep->core_sysdep_free(status);
}
return res;
}
static void _task_recv_get_reply_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata)
{
task_handle_t *task_handle = (task_handle_t *)userdata;
aiot_task_recv_t recv;
int32_t res = STATE_SUCCESS;
if (NULL == task_handle->recv_handler) {
return;
}
memset(&recv, 0, sizeof(aiot_task_recv_t));
char *code_key = "code";
char *code_value = NULL;
uint32_t code_len = 0;
if (core_json_value((char *)msg->data.pub.payload, msg->data.pub.payload_len, code_key, (uint32_t)strlen(code_key),
&code_value, &code_len) != STATE_SUCCESS) {
return;
}
if (core_str2uint(code_value, (uint8_t)code_len, &(recv.data.get_detail_reply.code)) != STATE_SUCCESS) {
return;
}
char *data_key = "data";
char *data_value = NULL;
uint32_t data_len = 0;
if (core_json_value((char *)msg->data.pub.payload, msg->data.pub.payload_len, data_key, (uint32_t)strlen(data_key),
&data_value, &data_len) != STATE_SUCCESS) {
return;
}
char *tasks_key = "tasks";
char *tasks_value = NULL;
uint32_t tasks_len = 0;
if (core_json_value(data_value, data_len, tasks_key, (uint32_t)strlen(tasks_key),
&tasks_value, &tasks_len) == STATE_SUCCESS) {
recv.type = AIOT_TASKRECV_GET_LIST_REPLY;
task_get_list_reply_t *reply = &(recv.data.get_list_reply);
if ((res = _task_parse_get_list_reply(task_handle, tasks_value, tasks_len, reply)) == STATE_SUCCESS) {
task_handle->recv_handler(task_handle, &recv, task_handle->userdata);
}
_task_free_task_list(task_handle, &(recv.data.get_list_reply));
return;
}
char *task_key = "task";
char *task_value = NULL;
uint32_t task_len = 0;
if (core_json_value(data_value, data_len, task_key, (uint32_t)strlen(task_key),
&task_value, &task_len) == STATE_SUCCESS) {
recv.type = AIOT_TASKRECV_GET_DETAIL_REPLY;
task_desc_t *task = &(recv.data.get_detail_reply.task);
if ((res = _task_parse_task_detail(task_handle, task_value, task_len, task)) == STATE_SUCCESS) {
task_handle->recv_handler(task_handle, &recv, task_handle->userdata);
}
_task_free_task_detail(task_handle, task);
return;
}
char *task_id_key = "taskId";
char *task_id_value = NULL;
uint32_t task_id_len = 0;
if (core_json_value(data_value, data_len, task_id_key, (uint32_t)strlen(task_id_key),
&task_id_value, &task_id_len) == STATE_SUCCESS) {
if (memcmp(task_id_value, TASK_GET_LIST_REPLY_TASK_ID, strlen(TASK_GET_LIST_REPLY_TASK_ID)) == 0) {
recv.type = AIOT_TASKRECV_GET_LIST_REPLY;
task_get_list_reply_t *reply = &(recv.data.get_list_reply);
reply->number = 0;
task_handle->recv_handler(task_handle, &recv, task_handle->userdata);
} else {
recv.type = AIOT_TASKRECV_GET_DETAIL_REPLY;
task_desc_t *task = &(recv.data.get_detail_reply.task);
char *id = task_handle->sysdep->core_sysdep_malloc(task_id_len + 1, TASK_MODULE_NAME);
memset(id, 0, task_id_len + 1);
memcpy(id, task_id_value, task_id_len);
task->task_id = id;
task->status = AIOT_TASK_STATUS_NOT_FOUND;
task_handle->recv_handler(task_handle, &recv, task_handle->userdata);
_task_free_task_detail(task_handle, task);
}
}
}
static void _task_recv_update_reply_handler(void *handle, const aiot_mqtt_recv_t *msg, void *userdata)
{
task_handle_t *task_handle = (task_handle_t *)userdata;
aiot_task_recv_t recv;
if (NULL == task_handle->recv_handler) {
return;
}
memset(&recv, 0, sizeof(aiot_task_recv_t));
recv.type = AIOT_TASKRECV_UPDATE_REPLY;
char *code_key = "code";
char *code_value = NULL;
uint32_t code_len = 0;
if (core_json_value((char *)msg->data.pub.payload, msg->data.pub.payload_len, code_key, (uint32_t)strlen(code_key),
&code_value, &code_len) != STATE_SUCCESS) {
return;
}
if (core_str2uint(code_value, (uint8_t)code_len, &(recv.data.get_detail_reply.code)) != STATE_SUCCESS) {
return;
}
char *data_key = "data";
char *data_value = NULL;
uint32_t data_len = 0;
if (core_json_value((char *)msg->data.pub.payload, msg->data.pub.payload_len, data_key, (uint32_t)strlen(data_key),
&data_value, &data_len) != STATE_SUCCESS) {
return;
}
if (STATE_SUCCESS != _task_parse_json(task_handle->sysdep, data_value, data_len, "taskId",
&(recv.data.update_reply.task_id))) {
return;
}
task_handle->recv_handler(task_handle, &recv, task_handle->userdata);
_task_free_update_reply(&(recv.data.update_reply));
}
static int32_t _task_setup_topic_mapping(void *mqtt_handle, void *task_handle)
{
uint32_t i = 0;
int32_t res = STATE_SUCCESS;
aiot_mqtt_topic_map_t topic_mapping;
for (i = 0; i < sizeof(g_task_recv_topic_mapping) / sizeof(task_recv_topic_map_t); i++) {
topic_mapping.topic = g_task_recv_topic_mapping[i].topic;
topic_mapping.handler = g_task_recv_topic_mapping[i].func;
topic_mapping.userdata = task_handle;
res = aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_APPEND_TOPIC_MAP, &topic_mapping);
if (STATE_SUCCESS != res) {
break;
}
}
return res;
}
static void _task_core_mqtt_process_handler(void *context, aiot_mqtt_event_t *event, core_mqtt_event_t *core_event)
{
task_handle_t *task_handle = (task_handle_t *)context;
if (core_event != NULL) {
switch (core_event->type) {
case CORE_MQTTEVT_DEINIT: {
task_handle->mqtt_handle = NULL;
return;
}
break;
default: {
}
break;
}
}
}
static int32_t _task_core_mqtt_operate_process_handler(task_handle_t *task_handle, core_mqtt_option_t option)
{
core_mqtt_process_data_t process_data;
memset(&process_data, 0, sizeof(core_mqtt_process_data_t));
process_data.handler = _task_core_mqtt_process_handler;
process_data.context = task_handle;
return core_mqtt_setopt(task_handle->mqtt_handle, option, &process_data);
}
static int32_t _task_send_query_task(task_handle_t *task_handle, char *task_id)
{
char *topic = NULL, *payload = NULL;
int32_t id = 0;
char id_string[11] = { 0 };
char *src[2] = { NULL };
int32_t res = STATE_SUCCESS;
if (NULL == task_id) {
return STATE_TASK_QUERY_TASK_ID_IS_NULL;
}
core_global_alink_id_next(task_handle->sysdep, &id);
core_int2str(id, id_string, NULL);
src[0] = id_string;
src[1] = task_id;
res = core_sprintf(task_handle->sysdep, &payload, TASK_REQUEST_QUERY_TASK_FMT, src, sizeof(src) / sizeof(char *),
TASK_MODULE_NAME);
if (res < 0) {
return res;
}
char *topic_src[] = { core_mqtt_get_product_key(task_handle->mqtt_handle), core_mqtt_get_device_name(task_handle->mqtt_handle) };
res = core_sprintf(task_handle->sysdep, &topic, TASK_QUERY_TASK_TOPIC_FMT, topic_src,
sizeof(topic_src) / sizeof(char *),
TASK_MODULE_NAME);
if (res < STATE_SUCCESS) {
task_handle->sysdep->core_sysdep_free(payload);
return res;
}
res = aiot_mqtt_pub(task_handle->mqtt_handle, (char *)topic, (uint8_t *)payload, strlen(payload), 0);
task_handle->sysdep->core_sysdep_free(payload);
task_handle->sysdep->core_sysdep_free(topic);
return res;
}
int32_t aiot_task_get_task_list(void *handle)
{
task_handle_t *task_handle = (task_handle_t *)handle;
if (NULL == handle) {
return STATE_USER_INPUT_NULL_POINTER;
}
return _task_send_query_task(task_handle, "$list");
}
int32_t aiot_task_get_task_detail(void *handle, char *task_id)
{
int32_t res = STATE_SUCCESS;
task_handle_t *task_handle = (task_handle_t *)handle;
if (NULL == handle) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (NULL == task_id) {
res = _task_send_query_task(task_handle, "$next");
} else {
res = _task_send_query_task(task_handle, task_id);
}
return res;
}
int32_t aiot_task_update(void *handle, task_desc_t *task)
{
int32_t res = STATE_SUCCESS;
char *topic = NULL, *payload = NULL, *status = NULL;
int32_t id = 0;
char id_string[11] = { 0 };
char progress_string[11] = { 0 };
char *src[5] = { NULL };
task_handle_t *task_handle = (task_handle_t *)handle;
if ((NULL == handle) || (NULL == task)) {
return STATE_USER_INPUT_NULL_POINTER;
}
if ((NULL != task->status_details) && (strcmp(task->status_details, "") == 0)) {
return STATE_TASK_UPDATE_STATUS_DETAILS_INVALID;
}
status = _task_convert_status_to_str(task->status);
if (NULL == status) {
return STATE_TASK_UPDATE_STATUS_INVALID;
}
core_global_alink_id_next(task_handle->sysdep, &id);
core_int2str(id, id_string, NULL);
char *topic_src[] = { core_mqtt_get_product_key(task_handle->mqtt_handle), core_mqtt_get_device_name(task_handle->mqtt_handle) };
res = core_sprintf(task_handle->sysdep, &topic, TASK_UPDATE_TASK_TOPIC_FMT, topic_src,
sizeof(topic_src) / sizeof(char *),
TASK_MODULE_NAME);
if (res < STATE_SUCCESS) {
return res;
}
core_uint2str(task->progress, progress_string, NULL);
src[0] = id_string;
src[1] = task->task_id;
src[2] = _task_convert_status_to_str(task->status);
src[3] = progress_string;
if (NULL == task->status_details) {
res = core_sprintf(task_handle->sysdep, &payload, TASK_REQUEST_UPDATE_TASK_NO_DETAIL_FMT, src, 4,
TASK_MODULE_NAME);
} else {
src[4] = task->status_details;
res = core_sprintf(task_handle->sysdep, &payload, TASK_REQUEST_UPDATE_TASK_FMT, src, sizeof(src) / sizeof(char *),
TASK_MODULE_NAME);
}
if (res < STATE_SUCCESS) {
task_handle->sysdep->core_sysdep_free(topic);
return res;
}
res = aiot_mqtt_pub(task_handle->mqtt_handle, (char *)topic, (uint8_t *)payload, strlen(payload), 0);
task_handle->sysdep->core_sysdep_free(topic);
task_handle->sysdep->core_sysdep_free(payload);
return res;
}
int32_t aiot_task_setopt(void *handle, aiot_task_option_t option, void *data)
{
int32_t res = STATE_SUCCESS;
task_handle_t *task_handle = (task_handle_t *)handle;
aiot_sysdep_portfile_t *sysdep = NULL;
if (NULL == task_handle) {
return STATE_TASK_SETOPT_HANDLE_IS_NULL;
}
if (NULL == data) {
return STATE_TASK_SETOPT_DATA_IS_NULL;
}
sysdep = task_handle->sysdep;
sysdep->core_sysdep_mutex_lock(task_handle->data_mutex);
switch (option) {
case AIOT_TASKOPT_RECV_HANDLER: {
task_handle->recv_handler = (aiot_task_recv_handler_t)data;
}
break;
case AIOT_TASKOPT_USERDATA: {
task_handle->userdata = data;
}
break;
case AIOT_TASKOPT_MQTT_HANDLE: {
task_handle->mqtt_handle = data;
res = _task_setup_topic_mapping(data, task_handle);
if (res >= STATE_SUCCESS) {
res = _task_core_mqtt_operate_process_handler(task_handle, CORE_MQTTOPT_APPEND_PROCESS_HANDLER);
}
}
break;
default: {
res = STATE_USER_INPUT_UNKNOWN_OPTION;
}
break;
}
sysdep->core_sysdep_mutex_unlock(task_handle->data_mutex);
return res;
}
void *aiot_task_init(void)
{
task_handle_t *task_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
sysdep = aiot_sysdep_get_portfile();
if (sysdep == NULL) {
return NULL;
}
task_handle = sysdep->core_sysdep_malloc(sizeof(task_handle_t), TASK_MODULE_NAME);
if (task_handle == NULL) {
return NULL;
}
memset(task_handle, 0, sizeof(task_handle_t));
task_handle->sysdep = sysdep;
task_handle->data_mutex = sysdep->core_sysdep_mutex_init();
core_global_init(sysdep);
return task_handle;
}
int32_t aiot_task_deinit(void **p_handle)
{
task_handle_t *task_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
uint8_t i = 0;
if (NULL == p_handle || NULL == *p_handle) {
return STATE_USER_INPUT_NULL_POINTER;
}
task_handle = *p_handle;
sysdep = task_handle->sysdep;
*p_handle = NULL;
_task_core_mqtt_operate_process_handler(task_handle, CORE_MQTTOPT_REMOVE_PROCESS_HANDLER);
/* remove mqtt topic mapping */
for (i = 0; i < sizeof(g_task_recv_topic_mapping) / sizeof(task_recv_topic_map_t); i++) {
aiot_mqtt_topic_map_t topic_mapping;
memset(&topic_mapping, 0, sizeof(aiot_mqtt_topic_map_t));
topic_mapping.topic = g_task_recv_topic_mapping[i].topic;
topic_mapping.handler = g_task_recv_topic_mapping[i].func;
aiot_mqtt_setopt(task_handle->mqtt_handle, AIOT_MQTTOPT_REMOVE_TOPIC_MAP, &topic_mapping);
}
if (task_handle->data_mutex != NULL) {
sysdep->core_sysdep_mutex_deinit(&task_handle->data_mutex);
}
sysdep->core_sysdep_free(task_handle);
core_global_deinit(sysdep);
return STATE_SUCCESS;
}

View File

@@ -0,0 +1,325 @@
/**
* @file aiot_task_api.h
* @brief task模块头文件, 提供任务管理的能力
* @date 2020-11-25
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
* @details
*
*
*/
#ifndef __AIOT_TASK_API_H__
#define __AIOT_TASK_API_H__
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdint.h>
/**
* @brief -0x0B00~-0x0BFF表达SDK在task模块内的状态码
*
*/
#define STATE_TASK_BASE (-0x0B00)
/**
* @brief 销毁task会话实例时, 发现会话句柄为空, 中止销毁动作
*
*/
#define STATE_TASK_DEINIT_HANDLE_IS_NULL (-0x0B01)
/**
* @brief 配置task会话实例时, 发现会话句柄为空, 中止配置动作
*
*/
#define STATE_TASK_SETOPT_HANDLE_IS_NULL (-0x0B02)
/**
* @brief 接收到服务器notify下行消息时的日志状态码
*/
#define STATE_TASK_RECV_NOTIFY (-0x0B03)
/**
* @brief 解析服务器下推的MQTT下行JSON报文时出错
*/
#define STATE_TASK_PARSE_NOTIFY_FAILED (-0x0B04)
/**
* @brief 为解析JSON报文而申请内存时, 未获取到所需内存而解析失败
*/
#define STATE_TASK_PARSE_JSON_MALLOC_FAILED (-0x0B05)
/**
* @brief 接收到服务器notify下行消息时的日志状态码
*/
#define STATE_TASK_PARSE_JSON_ERROR (-0x0B06)
/**
* @brief 接收到查询task id是空
*/
#define STATE_TASK_QUERY_TASK_ID_IS_NULL (-0x0B07)
/**
* @brief 接收到服务器get list reply下行消息时的日志状态码
*/
#define STATE_TASK_RECV_GET_LIST_REPLY (-0x0B08)
/**
* @brief 配置task会话实例时, 发现会话句柄为空, 中止配置动作
*
*/
#define STATE_TASK_SETOPT_DATA_IS_NULL (-0x0B09)
/**
* @brief 配置task 描述时状态设置不对
*
*/
#define STATE_TASK_UPDATE_STATUS_INVALID (-0x0B0A)
/**
* @brief aiot_task_setopt 接口的option参数可选值.
*/
/**
* @brief update task的时候task status_details只能为NULL或者json字符串对象
*
*/
#define STATE_TASK_UPDATE_STATUS_DETAILS_INVALID (-0x0B0B)
typedef enum {
/**
* @brief 设置MQTT的handle
*
* @details
*
* OTA过程中使用MQTT的通道能力, 用以向云端上报版本号, 进度, 以及错误码
*
* 数据类型: (void *)
*/
AIOT_TASKOPT_MQTT_HANDLE,
/**
* @brief 设置处理task消息的用户回调函数
*
* @details
*
* 从云端下发或者返回的数据的处理函数
*
* 数据类型: (void *)
*/
AIOT_TASKOPT_RECV_HANDLER,
/**
* @brief 用户需要SDK暂存的上下文
*
* @details
*
* 这个上下文指针会在 AIOT_TASKOPT_RECV_HANDLER设置的回调被调用时, 由SDK传给用户
*
* 数据类型: (void *)
*/
AIOT_TASKOPT_USERDATA,
AIOT_TASKOPT_MAX
} aiot_task_option_t;
/**
* @brief 任务的状态.
*/
typedef enum {
AIOT_TASK_STATUS_QUEUED, /* 服务端设置的状态: 任务处于队列中, 还没有推送 */
AIOT_TASK_STATUS_SENT, /* 服务端设置的状态: 任务已推送 */
AIOT_TASK_STATUS_IN_PROGRESS, /* 设备端设置的状态: 任务进行中. 设备端开始执行一个任务后, 将 */
AIOT_TASK_STATUS_SUCCEEDED, /* 设备端设置的状态: 任务完成 */
AIOT_TASK_STATUS_FAILED, /* 设备端设置的状态: 任务执行失败 */
AIOT_TASK_STATUS_REJECTED, /* 设备端设置的状态: 设备端拒绝执行任务 */
AIOT_TASK_STATUS_CANCELLED, /* 服务端设置的状态: 任务被服务端取消 */
AIOT_TASK_STATUS_REMOVED, /* 服务端设置的状态: 任务从服务端删除 */
AIOT_TASK_STATUS_TIMED_OUT, /* 服务端设置的状态: 任务执行超时 */
AIOT_TASK_STATUS_NOT_FOUND /* 服务端设置的状态: 没有找到此任务相关信息 */
} aiot_task_status_t;
/**
* @brief 下行有关的数据结构
*/
typedef enum {
AIOT_TASKRECV_NOTIFY, /* 对应/sys/{productKey}/{deviceName}/thing/job/notify 这个下行topic, 云端主动下推, 带任务详情 */
AIOT_TASKRECV_GET_DETAIL_REPLY, /* 对应/sys/{productKey}/{deviceName}/thing/job/get_reply 这个下行topic, 可以是单个任务的详情, 也可以是任务列表的简单描述 */
AIOT_TASKRECV_GET_LIST_REPLY, /* 对应/sys/{productKey}/{deviceName}/thing/job/get_reply 这个下行topic, 可以是单个任务的详情, 也可以是任务列表的简单描述 */
AIOT_TASKRECV_UPDATE_REPLY /* 对应/sys/{productKey}/{deviceName}/thing/job/update_reply 这个下行topic, 里面包含某个任务的update的结果, 即是否成功 */
} aiot_task_recv_type_t;
/**
* @brief 任务描述的数据结构
*/
typedef struct {
char *task_id; /* 任务ID */
aiot_task_status_t status; /* 任务的状态 */
char *job_document; /* 任务执行规则 */
char *sign_method; /* 文件签名的方法 */
char *sign; /* 文件的签名 */
char *document_file_url; /* 任务文件下载的url */
char *status_details; /* 客户自定义状态,透传到云端, 注意格式为json对象例如 "{\"key\": \"value\"", strlen("\"key\": \"value\"}"*/
uint8_t progress; /* 任务处理的进度数字从0-100 */
void *handle; /* 任务处理的句柄 */
} task_desc_t;
/**
* @brief 从云端拉取list时每个任务的简要描述
*/
typedef struct {
char *task_id; /* 任务ID */
aiot_task_status_t status; /* 任务的状态 */
} task_summary_t;
/**
* @brief 从云端拉取list返回的数据
*/
typedef struct {
uint32_t number; /* 从云端拉取的任务list的大小 */
task_summary_t *tasks; /* 拉取的任务数组指针 */
} task_get_list_reply_t;
/**
* @brief 从云端拉取任务详细信息时返回的数据
*/
typedef struct {
uint32_t code; /* 云端返回的code */
task_desc_t task; /* 任务描述的详细信息 */
} task_get_detail_reply_t;
/**
* @brief 更新任务状态到云端后,云端返回的数据
*/
typedef struct {
uint32_t code; /* 云端返回的code */
char *task_id; /* 更新任务后返回的任务id */
aiot_task_status_t status; /* 更新任务后返回的状态 */
} task_update_reply_t;
/**
* @brief 云端主动下发或更新任务云端返回的数据
*/
typedef struct {
aiot_task_recv_type_t type; /* 返回的数据类型 */
union {
task_desc_t notify; /* 云端主动推送任务的数据 */
task_get_list_reply_t get_list_reply; /* 请求任务list返回的数据 */
task_get_detail_reply_t get_detail_reply; /* 请求任务详细状态返回的数据 */
task_update_reply_t update_reply; /* 更新任务状态返回的数据 */
} data;
} aiot_task_recv_t;
/**
* @brief 设备收到task的mqtt下行报文时的接收回调函数
*
* @param[in] handle task实例句柄
* @param[in] recv 云端下行的消息
* @param[in] userdata 用户上下文
*
* @return void
*/
typedef void (* aiot_task_recv_handler_t)(void *handle, const aiot_task_recv_t *recv, void *userdata);
/**
* @brief 创建一个task实例
*
* @return void*
* @retval 非NULL task实例句柄
* @retval NULL 初始化失败, 或者是因为没有设置portfile, 或者是内存分配失败导致
*
*/
void *aiot_task_init(void);
/**
* @brief 销毁task实例句柄
*
* @param[in] handle 指向task实例句柄的指针
*
* @return int32_t
* @retval STATE_USER_INPUT_NULL_POINTER handle或者handle所指向的地址为空
* @retval STATE_SUCCESS 执行成功
*
*/
int32_t aiot_task_deinit(void **handle);
/**
* @brief 设置task句柄的参数
*
* @details
*
* 对task会话进行配置, 常见的配置选项包括
*
* @param[in] handle task句柄
* @param[in] option 配置选项, 更多信息请参考@ref aiot_task_option_t
* @param[in] data 配置选项数据, 更多信息请参考@ref aiot_task_option_t
*
* @return int32_t
* @retval STATE_TASK_SETOPT_HANDLE_IS_NULL task句柄为空
* @retval STATE_TASK_SETOPT_DATA_IS_NULL 参数data字段为空
* @retval STATE_USER_INPUT_UNKNOWN_OPTION option不支持
* @retval STATE_SUCCESS 参数设置成功
*
*/
int32_t aiot_task_setopt(void *handle, aiot_task_option_t option, void *data);
/**
* @brief 从云端获取task列表
*
* @details
*
* 从云端获取task列表
*
* @param[in] handle task句柄
*
* @return int32_t
* @retval STATE_TASK_SETOPT_DATA_IS_NULL 参数的handle字段为空
* @retval STATE_SUCCESS 发送成功
*/
int32_t aiot_task_get_task_list(void *handle);
/* 发送报文到/sys/{productKey}/{deviceName}/thing/job/get. 若函数入参user_task_id不为空, 则上行报文的payload为 "taskId": user_task_id, 返回该任务的详情; */
/* 若user_task_id为空, 则上行报文的payload为 "taskId": "$next", 云端返回未处于终态的任务队列中时间排在最前面一个任务, 该任务状态为QUEUED、SENT、IN_PROGRESS三者之一 */
/**
* @brief 从云端获取task详细内容
*
* @details
*
* 从云端获取task详细内容
*
* @param[in] handle task句柄
* @param[in] user_task_id task的id或者$next
*
* @return int32_t
* @retval STATE_TASK_SETOPT_DATA_IS_NULL或user_task_id 参数的handle字段为空
* @retval STATE_SUCCESS 发送成功
*
*/
int32_t aiot_task_get_task_detail(void *handle, char *user_task_id);
/**
* @brief 更新任务状态到云端
*
* @details
*
* 更新任务状态到云端
*
* @param[in] handle task句柄
* @param[in] task task信息
*
* @return int32_t
* @retval STATE_TASK_SETOPT_DATA_IS_NULL或task 参数的handle字段为空
* @retval STATE_SUCCESS 更新成功
*
*/
int32_t aiot_task_update(void *handle, task_desc_t *task);
#if defined(__cplusplus)
}
#endif
#endif /* #ifndef __AIOT_TASK_API_H__ */

View File

@@ -0,0 +1,74 @@
/**
* @file task_private.h
* @brief task模块内部的宏定义和数据结构声明, 不面向其它模块, 更不面向用户
*
* @copyright Copyright (C) 2015-2020 Alibaba Group Holding Limited
*
*/
#ifndef __TASK_PRIVATE_H__
#define __TASK_PRIVATE_H__
#if defined(__cplusplus)
extern "C" {
#endif
/* 用这种方式包含标准C库的头文件 */
#include "core_stdinc.h"
/* TODO: 这一段列出需要包含SDK其它模块头文件, 与上一段落以1个空行隔开 */
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
#include "aiot_task_api.h"
#define TASK_MODULE_NAME "TASK"
#define TASK_NOTIFY_TOPIC "/sys/+/+/thing/job/notify_reply"
#define TASK_QUERY_RESPONSE_TOPIC "/sys/+/+/thing/job/get_reply"
#define TASK_QUERY_TASK_TOPIC_FMT "/sys/%s/%s/thing/job/get"
#define TASK_UPDATE_TASK_TOPIC_FMT "/sys/%s/%s/thing/job/update"
#define TASK_NOTIFY_REPLY_TOPIC_FMT "/sys/%s/%s/thing/job/notify_reply"
/* ALINK请求的JSON格式 */
#define TASK_REQUEST_QUERY_TASK_FMT "{\"id\":\"%s\",\"version\":\"1.0.0\",\"params\":{\"taskId\":\"%s\"}}"
#define TASK_REQUEST_UPDATE_TASK_FMT "{\"id\":\"%s\",\"version\":\"1.0.0\",\"params\":{\"taskId\":\"%s\",\"status\":\"%s\",\"progress\":%s,\"statusDetails\":%s}}"
#define TASK_REQUEST_UPDATE_TASK_NO_DETAIL_FMT "{\"id\":\"%s\",\"version\":\"1.0.0\",\"params\":{\"taskId\":\"%s\",\"status\":\"%s\",\"progress\":%s}}"
#define TASK_NOTIFY_REPLY_FMT "{\"id\":\"%s\",\"code\":%s,\"data\":{}}"
#define TASK_GET_LIST_REPLY_ARRAY_MAX 10
#define TASK_GET_LIST_REPLY_TASK_ID "$list"
typedef struct {
aiot_sysdep_portfile_t *sysdep;
aiot_task_recv_handler_t recv_handler;
void *mqtt_handle;
void *userdata;
task_desc_t
*default_task_desc; /* handle中用于存储任务描述的数据结构. handle里面只存放一个任务. 如果用户要存放多个,可以自己在外部定义一个列表 */
void *data_mutex;
} task_handle_t;
/* 包含下行topic和对应处理函数的结构体定义 */
typedef struct {
char *topic;
aiot_mqtt_recv_handler_t func;
} task_recv_topic_map_t;
typedef struct {
char *str;
aiot_task_status_t status;
} task_status_map_t;
typedef struct {
char *pos;
int len;
} task_list_json;
#if defined(__cplusplus)
}
#endif
#endif /* __TASK_PRIVATE_H__ */