更新硬件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,34 @@
# 文件说明
记录`Link SDK-4.x`的更新历史
# 更新内容
+ 2020-05-06: SDK 4.0.0版本正式发布
+ 2021-04-20: SDK 4.1.0版本正式发布
+ 新增安全隧道和远程调试功能
+ 新增AT模组驱动框架,支持模组快速适配
+ mbedtls安全层抽象
+ 支持单报文多url的OTA
+ 新增基于mqtt的动态注册功能
+ 支持MQTT 5.0协议
# 模块状态
| 模块名 | 更新时间 | Commit ID
|---------------------------------------------|-------------|---------------------------------------------
| 核心模块(core) | 2022-08-26 | 9746d5384fb8eebbfd9ba2766511617bbf2d43fa
| 基于MQTT的动态注册(components/dynreg-mqtt) | 2021-10-14 | 3ec260703f401c925fbef6b6905de5eac4da4663
| 时间同步模块(components/ntp) | 2021-09-16 | cb96f929c231ad8ee8c48dcf82167f3f6eb66dad
| 设备诊断模块(components/diag) | 2021-10-18 | 18897e1421952e4eda11e82a61f573654f2bcc69
| 设备标签模块(components/devinfo) | 2021-09-15 | 9fe181e1c6e537410a7fe843db5c4af782f8061a
| 固件升级模块(components/ota) | 2021-11-10 | 13f8cc22568375d132be9c3e1ad26a57f199f294
| 设备日志模块(components/logpost) | 2021-09-15 | d0e41935909d0c7f593f9225e119f7698db67b2d
| 物模型模块(components/data-model) | 2021-09-06 | 1d9270de816f7ff0f60c0b2a53d08ca4da8bab66
| 动态注册(components/dynreg) | 2020-07-30 | 190e3bed4080eeb07c4f9e907cb7c3d966dfab53
| 设备影子模块(components/shadow) | 2021-09-22 | 8e36b636c72b38817382a5ca6f4ea80483b398b6
| 任务管理模块(components/task) | 2021-10-14 | 1fe061f31ad0e6b1616472335cad7e2f67761915

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

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__ */

View File

@@ -0,0 +1,2 @@
Name: 核心模块
Core Component for LinkSDK V4.0.0

View File

@@ -0,0 +1,473 @@
/**
* @file aiot_http_api.h
* @brief HTTP模块头文件, 提供用HTTP协议向阿里云物联网平台上报数据的能力
* @date 2019-12-27
*
* @copyright Copyright (C) 2015-2018 Alibaba Group Holding Limited
*
*/
#ifndef _AIOT_HTTP_API_H_
#define _AIOT_HTTP_API_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include "core_stdinc.h"
/**
* @brief 服务器返回的业务错误码
*
* @details
*
* 从云平台对上报消息回应的报文JSON中解析
*/
typedef enum {
/**
* @brief 0, 服务端成功接收到上报的消息
*/
AIOT_HTTP_RSPCODE_SUCCESS = 0,
/**
* @brief 10000, 服务端返回未知错误
*/
AIOT_HTTP_RSPCODE_COMMON_ERROR = 10000,
/**
* @brief 10001, 请求参数错误
*/
AIOT_HTTP_RSPCODE_PARAM_ERROR = 10001,
/**
* @brief 20001, token过期, 请调用 @ref aiot_http_auth 进行鉴权, 获取新token
*/
AIOT_HTTP_RSPCODE_TOKEN_EXPIRED = 20001,
/**
* @brief 20002, 请求的header中无token可表明设备端合法, 请调用 @ref aiot_http_auth 进行鉴权, 获取新token
*/
AIOT_HTTP_RSPCODE_TOKEN_NULL = 20002,
/**
* @brief 20003, token错误, 请调用 @ref aiot_http_auth 进行鉴权, 获取新token
*/
AIOT_HTTP_RSPCODE_TOKEN_CHECK_ERROR = 20003,
/**
* @brief 30001, 消息上报失败
*/
AIOT_HTTP_RSPCODE_PUBLISH_MESSAGE_ERROR = 30001,
/**
* @brief 40000, 设备端上报过于频繁, 触发服务端限流
*/
AIOT_HTTP_RSPCODE_REQUEST_TOO_MANY = 40000,
} aiot_http_response_code_t;
/**
* @brief @ref aiot_http_setopt 函数的 option 参数, 对于下文每一个选项中的数据类型, 指的是 @ref aiot_mqtt_setopt 中的data参数的数据类型
*
*/
typedef enum {
/**
* @brief HTTP 服务器的域名地址或者ip地址
*
* @details
*
* 阿里云物联网平台域名地址列表: (tcp使用80端口, tls使用443端口)
*
* | 域名地址 | 区域 | 端口号 |
* |---------------------------------------|---------|--------|
* | iot-as-http.cn-shanghai.aliyuncs.com | 上海 | 443 |
*
* 数据类型: (char *)
*/
AIOT_HTTPOPT_HOST,
/**
* @brief HTTP 服务器的端口号
*
* @details
*
* 连接阿里云物联网平台时:
*
* 1.如果使用的是tcp, 端口号设置为80
*
* 2. 如果使用的是tls, 端口号设置为443
*
* 数据类型: (uint16_t *)
*/
AIOT_HTTPOPT_PORT,
/**
* @brief HTTP建联时, 网络使用的安全凭据
*
* @details
*
* 该配置项用于为底层网络配置 @ref aiot_sysdep_network_cred_t 安全凭据数据
*
* 1. 若该选项不配置, 那么MQTT将以tcp方式直接建联
*
* 2. 若 @ref aiot_sysdep_network_cred_t 中option配置为 @ref AIOT_SYSDEP_NETWORK_CRED_NONE , HTTP将以tcp方式直接建联
*
* 3. 若 @ref aiot_sysdep_network_cred_t 中option配置为 @ref AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA , HTTP将以tls方式建联
*
* 数据类型: (aiot_sysdep_network_cred_t *)
*/
AIOT_HTTPOPT_NETWORK_CRED,
/**
* @brief HTTP建联时, 建立网络连接的超时时间
*
* @details
*
* 指建立socket连接的超时时间
*
* 数据类型: (uint32_t *) 默认值: (5 *1000) ms
*
*/
AIOT_HTTPOPT_CONNECT_TIMEOUT_MS,
/**
* @brief HTTP发送数据时, 在协议栈花费的最长时间
*
* @details
*
* 数据类型: (uint32_t *) 默认值: (5 * 1000) ms
*/
AIOT_HTTPOPT_SEND_TIMEOUT_MS,
/**
* @brief HTTP接收数据时, 在协议栈花费的最长时间
*
* @details
*
* 数据类型: (uint32_t *) 默认值: (5 * 1000) ms
*/
AIOT_HTTPOPT_RECV_TIMEOUT_MS,
/**
* @brief 销毁HTTP实例时, 等待其他api执行完毕的时间
*
* @details
*
* 当调用 @ref aiot_http_deinit 销毁HTTP实例时, 若继续调用其他 aiot_http_xxx API, API会返回STATE_USER_INPUT_EXEC_DISABLED 错误
*
* 此时, 用户应该停止调用其他 aiot_http_xxx API
*
* 数据类型: (uint32_t *) 默认值: (2 * 1000) ms
*/
AIOT_HTTPOPT_DEINIT_TIMEOUT_MS,
/**
* @brief 当接收服务器返回的http报文时, 单行http header的最大长度
*
* @details
*
* 当单行http header设置过短时, @ref aiot_http_recv 会返回 @ref STATE_HTTP_HEADER_BUFFER_TOO_SHORT 状态码
*
* 数据类型: (uint32_t *) 默认值: 128
*/
AIOT_HTTPOPT_HEADER_BUFFER_LEN,
/**
* @brief 当接收服务器返回的http报文时, 每次从 @ref aiot_http_recv_handler_t 回调函数中给出的body最大长度
*
* @details
*
* 数据类型: (uint32_t *) 默认值: 128
*/
AIOT_HTTPOPT_BODY_BUFFER_LEN,
/**
* @brief HTTP 内部事件回调函数
*
* @details
*
* 数据类型: (aiot_http_event_handler_t)
*/
AIOT_HTTPOPT_EVENT_HANDLER,
/* 以上选项配置的数据与 CORE_HTTPOPT_XXX 共用 */
/**
* @brief 用户需要SDK暂存的上下文
*
* @details
*
* 1. 当接收到HTTP数据时, 该上下文会从 @ref aiot_http_recv_handler_t 的 userdata 参数给出
*
* 2. 当HTTP内部有事件发生时, 该上下文会从 @ref aiot_http_event_handler_t 的 userdata 参数给出
*
* 数据类型: (void *)
*/
AIOT_HTTPOPT_USERDATA,
/**
* @brief HTTP 数据接收回调函数
*
* @details
*
* 数据类型: (aiot_http_recv_handler_t)
*/
AIOT_HTTPOPT_RECV_HANDLER,
/**
* @brief 设备的product key, 可从<a href="http://iot.console.aliyun.com/">阿里云物联网平台控制台</a>获取
*
* @details
*
* 数据类型: (char *)
*/
AIOT_HTTPOPT_PRODUCT_KEY,
/**
* @brief 设备的device name, 可从<a href="http://iot.console.aliyun.com/">阿里云物联网平台控制台</a>获取
*
* @details
*
* 数据类型: (char *)
*/
AIOT_HTTPOPT_DEVICE_NAME,
/**
* @brief 设备的device secret, 可从<a href="http://iot.console.aliyun.com/">阿里云物联网平台控制台</a>获取
*
* @details
*
* 数据类型: (char *)
*/
AIOT_HTTPOPT_DEVICE_SECRET,
/**
* @brief 上报设备的扩展信息, 比如模组商ID和模组ID
*
* @details
*
* 1. 模组商ID: 格式为pid=xxx
*
* 2. 模组ID: 格式为mid=xxx
*
* 如果需要同时上报多个信息, 那么它们之间用&连接, 例如: pid=xxx&mid=xxx
*
* 数据类型: (char *)
*/
AIOT_HTTPOPT_EXTEND_DEVINFO,
/**
* @brief 使用 @ref aiot_http_auth 进行认证并获取token的超时时间
*
* @details
*
* 数据类型: (uint32_t *) 默认值: (5 * 1000) ms
*/
AIOT_HTTPOPT_AUTH_TIMEOUT_MS,
/**
* @brief 是否使用http长连接
*
* @details
*
* 若该配置的值为0, 则每次使用 @ref aiot_http_auth 和 @ref aiot_http_send 时, SDK会重新与 HTTP 服务器建立简介
*
* 数据类型: (uint8_t *) 默认值: (5 * 1000) ms
*/
AIOT_HTTPOPT_LONG_CONNECTION,
AIOT_HTTPOPT_MAX
} aiot_http_option_t;
/**
* @brief SDK收到HTTP报文, 传递给用户数据回调函数时, 对报文类型的描述
*/
typedef enum {
/**
* @brief 获取到HTTP Status Code
*/
AIOT_HTTPRECV_STATUS_CODE,
/**
* @brief 获取到HTTP Header, 每次返回Header中的一组键值对
*/
AIOT_HTTPRECV_HEADER,
/**
* @brief 获取到HTTP Body, 返回完整的Body内容
*/
AIOT_HTTPRECV_BODY
} aiot_http_recv_type_t;
/**
* @brief SDK收到HTTP报文, 传递给用户数据回调函数时, 对报文内容的描述
*/
typedef struct {
/**
* @brief HTTP 消息类型, 更多信息请参考 @ref aiot_http_recv_type_t
*/
aiot_http_recv_type_t type;
union {
/**
* @brief HTTP 消息类型为 @ref AIOT_HTTPRECV_STATUS_CODE 时的数据
*/
struct {
/**
* @brief HTTP Status Code
*/
uint32_t code;
} status_code;
/**
* @brief HTTP 消息类型为 @ref AIOT_HTTPRECV_HEADER 时的数据
*/
struct {
/**
* @brief 单行 HTTP Header 的 key
*/
char *key;
/**
* @brief 单行 HTTP Header 的 value
*/
char *value;
} header;
/**
* @brief HTTP 消息类型为 @ref AIOT_HTTPRECV_BODY 时的数据
*/
struct {
/**
* @brief HTTP Body 的内容
*/
uint8_t *buffer;
/**
* @brief HTTP Body 的长度
*/
uint32_t len;
} body;
} data;
} aiot_http_recv_t;
/**
* @brief HTTP 消息接收回调函数原型, 可以通过 @ref aiot_http_setopt 接口的 @ref AIOT_HTTPOPT_RECV_HANDLER 参数指定
*
* @details
*
* 当SDK收到 HTTP 服务器的应答数据时, 通过此回调函数输出
*
* @param[out] handle HTTP 句柄
* @param[out] packet 从 HTTP 服务器接收到的数据
* @param[out] userdata 用户通过 @ref AIOT_HTTPOPT_USERDATA 交由SDK暂存的上下文
*
* @return void
*/
typedef void (*aiot_http_recv_handler_t)(void *handle, const aiot_http_recv_t *packet, void *userdata);
/**
* @brief SDK内部发生状态变化, 通过用户事件回调函数通知用户时, 对事件类型的描述
*/
typedef enum {
/**
* @brief token无效事件, 此时用户应该调用 @ref aiot_http_auth 获取新的token
*/
AIOT_HTTPEVT_TOKEN_INVALID
} aiot_http_event_type_t;
/**
* @brief SDK内部发生状态变化, 通过用户事件回调函数通知用户时, 对事件内容的描述
*/
typedef struct {
aiot_http_event_type_t type;
} aiot_http_event_t;
/**
* @brief HTTP 事件回调函数原型, 可以通过 @ref aiot_http_setopt 接口的 @ref AIOT_HTTPOPT_EVENT_HANDLER 参数指定
*
* @param[out] handle HTTP句柄
* @param[out] event 事件结构体
* @param[out] user_data 指向用户上下文数据的指针, 由 @ref aiot_http_setopt 的 @ref AIOT_HTTPOPT_USERDATA 选项设置
*/
typedef void (* aiot_http_event_handler_t)(void *handle, const aiot_http_event_t *event, void *userdata);
/**
* @brief 创建一个HTTP上云实例
*
* @return void*
*
* @retval 非NULL, HTTP 实例句柄
* @retval NULL,初始化 HTTP 实例失败
*/
void *aiot_http_init(void);
/**
* @brief 设置HTTP实例参数
*
* @param[in] handle HTTP句柄
* @param[in] option 配置选项, 更多信息请参考 @ref aiot_http_option_t
* @param[in] data 配置数据, 更多信息请参考 @ref aiot_http_option_t
*
* @return int32_t
*
* @retval STATE_SUCCESS, 成功
* @retval STATE_HTTP_HANDLE_IS_NULL, HTTP句柄为NULL
* @retval STATE_USER_INPUT_OUT_RANGE, 用户输入参数无效
* @retval STATE_SYS_DEPEND_MALLOC_FAILED, 内存分配失败
*/
int32_t aiot_http_setopt(void *handle, aiot_http_option_t option, void *data);
/**
* @brief 向服务器发送认证请求, 获取token
*
* @param[in] handle HTTP句柄
*
* @return int32_t
*
* @retval STATE_SUCCESS, 认证成功
* @retval STATE_HTTP_HANDLE_IS_NULL, HTTP句柄为NULL
* @retval STATE_USER_INPUT_MISSING_PRODUCT_KEY, 未设置必要选项ProductKey
* @retval STATE_USER_INPUT_MISSING_DEVICE_NAME, 未设置必要选项DeviceName
* @retval STATE_USER_INPUT_MISSING_DEVICE_SECRET, 未设置必要选项DeviceSecret
* @retval STATE_HTTP_TOKEN_LEN_ERROR, token长度错误
* @retval STATE_HTTP_GET_TOKEN_FAILED, 获取token失败
*/
int32_t aiot_http_auth(void *handle);
/**
* @brief 上报数据到物联网平台
*
* @param[in] handle HTTP句柄
* @param[in] topic 上报的目标topic, 在物联网平台控制的产品详情页面有设备的完整topic列表
* @param[in] payload 指向上报数据的指针
* @param[in] payload_len 上报数据的长度
*
* @return int32_t
*
* @retval STATE_SUCCESS, 上报成功
* @retval STATE_HTTP_HANDLE_IS_NULL, HTTP句柄为NULL
* @retval STATE_USER_INPUT_OUT_RANGE, 用户输入参数无效
* @retval STATE_HTTP_NOT_AUTH, 设备未认证
*/
int32_t aiot_http_send(void *handle, char *topic, uint8_t *payload, uint32_t payload_len);
/**
* 服务器响应数据格式为
* {
* "code": 0, // 业务状态码
* "message": "success", // 业务信息
* "info": {
* "messageId": 892687627916247040,
* }
* }
*/
/**
* @brief 接受HTTP应答数据, 数据会从用户设置的 @ref aiot_http_event_handler_t 回调函数输出
*
* @param[in] handle HTTP句柄
*
* @return int32_t
*
* @retval >= 0, 接受到的HTTP body数据长度
* @retval STATE_HTTP_HANDLE_IS_NULL, HTTP句柄为NULL
* @retval STATE_USER_INPUT_NULL_POINTER, 用户输入参数为NULL
* @retval STATE_USER_INPUT_OUT_RANGE, buffer_len为0
* @retval STATE_HTTP_RSP_MSG_ERROR, 服务器应答消息错误
* @retval STATE_SYS_DEPEND_NWK_CLOSED, 网络连接已关闭
* @retval STATE_SYS_DEPEND_NWK_READ_OVERTIME, 网络接收超时
* @retval STATE_HTTP_RECV_LINE_TOO_LONG, HTTP单行数据过长, 内部无法解析
* @retval STATE_HTTP_PARSE_STATUS_LINE_FAILED, 无法解析状态码
* @retval STATE_HTTP_GET_CONTENT_LEN_FAILED, 获取Content-Length失败
*/
int32_t aiot_http_recv(void *handle);
/**
* @brief 销毁参数p_handle所指定的HTTP实例
*
* @param[in] p_handle 指向HTTP句柄的指针
*
* @return int32_t
*
* @retval STATE_SUCCESS 成功
* @retval STATE_USER_INPUT_NULL_POINTER 参数p_handle为NULL或者p_handle指向的句柄为NULL
*/
int32_t aiot_http_deinit(void **p_handle);
#if defined(__cplusplus)
}
#endif
#endif /* #ifndef _AIOT_HTTP_API_H_ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,792 @@
/**
* @file aiot_mqtt_api.h
* @brief MQTT模块头文件, 提供用MQTT协议连接阿里云物联网平台的能力
* @date 2019-12-27
*
* @copyright Copyright (C) 2015-2018 Alibaba Group Holding Limited
*
* @details
*
* MQTT模块用于建立与阿里云物联网平台的连接, API使用流程如下:
*
* 1. 调用 @ref aiot_mqtt_init 初始化MQTT会话, 获取会话句柄
*
* 2. 调用 @ref aiot_mqtt_setopt 配置MQTT会话的参数, 常用配置项见 @ref aiot_mqtt_setopt 的说明
*
* 3. 调用 @ref aiot_mqtt_connect 建立与阿里云物联网平台的连接
*
* 4. 启动一个线程, 线程中间歇性调用 @ref aiot_mqtt_process 处理心跳和QoS1的消息
*
* 5. 启动一个线程, 线程中持续调用 @ref aiot_mqtt_recv 接收网络上的MQTT报文
*
* + 当接收到一条报文时, 按以下顺序检查当前MQTT会话的参数, 当满足某条的描述时, 会通过对应的回调函数进行通知, 并停止检查
*
* + 检查收到的报文topic是否已经通过 @ref aiot_mqtt_setopt 的 @ref AIOT_MQTTOPT_APPEND_TOPIC_MAP 参数配置回调函数
*
* + 检查收到的报文topic是否已经通过 @ref aiot_mqtt_sub API配置回调函数
*
* + 检查是否通过 @ref aiot_mqtt_setopt 的 @ref AIOT_MQTTOPT_RECV_HANDLER 参数配置默认回调函数
*
* 6. 经过以上步骤后, MQTT连接已建立并能保持与物联网平台的连接, 接下来按自己的场景用 @ref aiot_mqtt_sub 和 @ref aiot_mqtt_pub 等API实现业务逻辑即可
*
*/
#ifndef _AIOT_MQTT_API_H_
#define _AIOT_MQTT_API_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdint.h>
/**
* @brief MQTT报文类型
*
* @details
*
* 传入@ref aiot_mqtt_recv_handler_t 的MQTT报文类型
*/
typedef enum {
/**
* @brief MQTT PUBLISH报文
*/
AIOT_MQTTRECV_PUB,
/**
* @brief MQTT PINGRESP报文
*/
AIOT_MQTTRECV_HEARTBEAT_RESPONSE,
/**
* @brief MQTT SUBACK报文
*/
AIOT_MQTTRECV_SUB_ACK,
/**
* @brief MQTT UNSUB报文
*/
AIOT_MQTTRECV_UNSUB_ACK,
/**
* @brief MQTT PUBACK报文
*/
AIOT_MQTTRECV_PUB_ACK,
} aiot_mqtt_recv_type_t;
typedef struct {
/**
* @brief MQTT报文类型, 更多信息请参考@ref aiot_mqtt_recv_type_t
*/
aiot_mqtt_recv_type_t type;
/**
* @brief MQTT报文联合体, 内容根据type进行选择
*/
union {
/**
* @brief MQTT PUBLISH报文
*/
struct {
uint8_t qos;
char *topic;
uint16_t topic_len;
uint8_t *payload;
uint32_t payload_len;
} pub;
/**
* @brief AIOT_MQTTRECV_SUB_ACK
*/
struct {
int32_t res;
uint8_t max_qos;
uint16_t packet_id;
} sub_ack;
/**
* @brief AIOT_MQTTRECV_UNSUB_ACK
*/
struct {
uint16_t packet_id;
} unsub_ack;
/**
* @brief AIOT_MQTTRECV_PUB_ACK
*/
struct {
uint16_t packet_id;
} pub_ack;
} data;
} aiot_mqtt_recv_t;
/**
* @brief MQTT报文接收回调函数原型
*
* @param[in] handle MQTT实例句柄
* @param[in] packet MQTT报文结构体, 存放收到的MQTT报文
* @param[in] userdata 用户上下文
*
* @return void
*/
typedef void (*aiot_mqtt_recv_handler_t)(void *handle, const aiot_mqtt_recv_t *packet, void *userdata);
/**
* @brief MQTT内部事件类型
*/
typedef enum {
/**
* @brief 当MQTT实例第一次连接网络成功时, 触发此事件
*/
AIOT_MQTTEVT_CONNECT,
/**
* @brief 当MQTT实例断开网络连接后重连成功时, 触发此事件
*/
AIOT_MQTTEVT_RECONNECT,
/**
* @brief 当MQTT实例断开网络连接时, 触发此事件
*/
AIOT_MQTTEVT_DISCONNECT
} aiot_mqtt_event_type_t;
typedef enum {
/**
* @brief MQTT实例网络连接由于网络故障而断开
*/
AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT,
/**
* @brief MQTT实例网络连接由于心跳丢失超过指定次数(@ref AIOT_MQTTOPT_HEARTBEAT_MAX_LOST )而断开
*/
AIOT_MQTTDISCONNEVT_HEARTBEAT_DISCONNECT
} aiot_mqtt_disconnect_event_type_t;
/**
* @brief MQTT内部事件
*/
typedef struct {
/**
* @brief MQTT内部事件类型. 更多信息请参考@ref aiot_mqtt_event_type_t
*
*/
aiot_mqtt_event_type_t type;
/**
* @brief MQTT事件数据联合体
*/
union {
/**
* @brief MQTT连接断开时, 具体的断开原因
*/
aiot_mqtt_disconnect_event_type_t disconnect;
} data;
} aiot_mqtt_event_t;
/**
* @brief MQTT事件回调函数
*
* @details
*
* 当MQTT内部事件被触发时, 调用此函数. 如连接成功/断开连接/重连成功
*
*/
typedef void (*aiot_mqtt_event_handler_t)(void *handle, const aiot_mqtt_event_t *event, void *userdata);
/**
* @brief 使用 @ref aiot_mqtt_setopt 配置 @ref AIOT_MQTTOPT_APPEND_TOPIC_MAP 时的数据
*
* @details
*
* 用于在建立MQTT连接前配置topic与相应的回调函数
*
*/
typedef struct {
char *topic;
aiot_mqtt_recv_handler_t handler;
void *userdata;
} aiot_mqtt_topic_map_t;
/**
* @brief @ref aiot_mqtt_setopt 函数的option参数. 对于下文每一个选项中的数据类型, 指的是@ref aiot_mqtt_setopt 中的data参数的数据类型
*
* @details
*
* 1. data的数据类型是char *时, 以配置@ref AIOT_MQTTOPT_HOST 为例:
*
* char *host = "xxx";
*
* aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, host);
*
* 2. data的数据类型是其他数据类型时, 以配置@ref AIOT_MQTTOPT_PORT 为例:
*
* uint16_t port = 443;
*
* aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
*/
typedef enum {
/**
* @brief MQTT 服务器的域名地址或者ip地址
*
* @details
*
* 阿里云物联网平台域名地址列表(必须使用自己的product key替换${pk}):
*
* 使用tcp或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
*
* 使用tls psk方式建联:
*
* | 域名地址 | 区域 | 端口号
* |-----------------------------------------|---------|---------
* | ${pk}.itls.cn-shanghai.aliyuncs.com | 上海 | 1883
*
* 使用tls x509客户端证书方式建联:
*
* | 域名地址 | 区域 | 端口号
* |-------------------------------------|---------|---------
* | x509.itls.cn-shanghai.aliyuncs.com | 上海 | 1883
*
* 数据类型: (char *)
*/
AIOT_MQTTOPT_HOST,
/**
* @brief MQTT 服务器的端口号
*
* @details
*
* 连接阿里云物联网平台时:
*
* 1. 如果使用的是tcp或者tls证书方式, 端口号设置为443
*
* 2. 如果使用的是tls psk和tls x509客户端证书方式, 端口号设置为1883
*
* 数据类型: (uint16_t *)
*/
AIOT_MQTTOPT_PORT,
/**
* @brief 设备的product key, 可从<a href="http://iot.console.aliyun.com/">阿里云物联网平台控制台</a>获取
*
* @details
*
* 数据类型: (char *)
*/
AIOT_MQTTOPT_PRODUCT_KEY,
/**
* @brief 设备的device name, 可从<a href="http://iot.console.aliyun.com/">阿里云物联网平台控制台</a>获取
*
* @details
*
* 数据类型: (char *)
*/
AIOT_MQTTOPT_DEVICE_NAME,
/**
* @brief 设备的device secret, 可从<a href="http://iot.console.aliyun.com/">阿里云物联网平台控制台</a>获取
*
* @details
*
* 数据类型: (char *)
*/
AIOT_MQTTOPT_DEVICE_SECRET,
/**
* @brief 设备连接阿里云物联网平台时的扩展clientid
*
* @details
*
* 若需要上报模组商id和模组id以及os信息, 按以下格式填写:
*
* "mid=<模组ID>,pid=<模组商ID>,os=<操作系统>"
*
* 数据类型: (char *)
*/
AIOT_MQTTOPT_EXTEND_CLIENTID,
/**
* @brief 设备连接阿里云物联网平台时的安全模式, 使用标准的tcp或tls时无需配置
*
* @details
*
* 数据类型: (char *)
*/
AIOT_MQTTOPT_SECURITY_MODE,
/**
* @brief 使用自定义连接凭据连接mqtt服务器时, 凭据的username
*
* @details
*
* 数据类型: (char *)
*/
AIOT_MQTTOPT_USERNAME,
/**
* @brief 使用自定义连接凭据连接mqtt服务器时, 凭据的password
*
* @brief
*
* 数据类型: (char *)
*/
AIOT_MQTTOPT_PASSWORD,
/**
* @brief 使用自定义连接凭据连接mqtt服务器时, 凭据的clientid
*
* @details
*
* 数据类型: (char *)
*/
AIOT_MQTTOPT_CLIENTID,
/**
* @brief MQTT建联时, CONNECT报文中的心跳间隔参数
*
* @details
*
* 受阿里云物联网平台限制, 取值范围为30 ~ 1200s
*
* 1. 如果设置的值小于30, mqtt建联会被云端拒绝, @ref aiot_mqtt_connect 函数会返回@ref STATE_MQTT_CONNACK_RCODE_SERVER_UNAVAILABLE 错误
*
* 2. 如果设置的值大于1200, mqtt连接仍然可以建立, 但此参数会被服务器覆盖为1200
*
* 数据类型: (uint16_t *) 取值范围: 30 ~ 1200s 默认值: 1200s
*/
AIOT_MQTTOPT_KEEPALIVE_SEC,
/**
* @brief MQTT建联时, CONNECT报文中的clean session参数
*
* @details
*
* 1. 设备上线时如果clean session为0, 那么上线前服务器推送QoS1的消息会在此时推送给设备
*
* 2. 设备上线时如果clean session为1, 那么上线前服务器推送的QoS1的消息会被丢弃
*
* 数据类型: (uint8_t *) 取值范围: 0, 1 默认值: 1
*/
AIOT_MQTTOPT_CLEAN_SESSION,
/**
* @brief MQTT建联时, 网络使用的安全凭据
*
* @details
*
* 该配置项用于为底层网络配置@ref aiot_sysdep_network_cred_t 安全凭据数据
*
* 1. 若该选项不配置, 那么MQTT将以tcp方式直接建联
*
* 2. 若@ref aiot_sysdep_network_cred_t 中option配置为@ref AIOT_SYSDEP_NETWORK_CRED_NONE , MQTT将以tcp方式直接建联
*
* 3. 若@ref aiot_sysdep_network_cred_t 中option配置为@ref AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA , MQTT将以tls方式建联
*
* 4. 若@ref aiot_sysdep_network_cred_t 中option配置为@ref AIOT_SYSDEP_NETWORK_CRED_SVRCERT_PSK , MQTT将以tls psk方式建联
*
* 数据类型: (aiot_sysdep_network_cred_t *)
*/
AIOT_MQTTOPT_NETWORK_CRED,
/**
* @brief MQTT建联时, 建立网络连接的超时时间
*
* @details
*
* 指建立socket连接的超时时间
*
* 数据类型: (uint32_t *) 默认值: (5 *1000) ms
*
*/
AIOT_MQTTOPT_CONNECT_TIMEOUT_MS,
/**
* @brief 配置MQTT PINGREQ报文发送时间间隔. (心跳发送间隔)
*
* @details
*
* 数据类型: (uint32_t *) 默认值: (25 * 1000) ms
*/
AIOT_MQTTOPT_HEARTBEAT_INTERVAL_MS,
/**
* @brief 配置MQTT PINGRESP报文允许连续丢失的最大次数, 当超过这个次数时, 触发重连机制
*
* @details
*
* 数据类型: (uint8_t *) 默认值: (2)
*/
AIOT_MQTTOPT_HEARTBEAT_MAX_LOST,
/**
* @brief 打开/关闭MQTT重连机制
*
* @details
*
* 数据类型: (uint8_t *) 取值范围: 0, 1 默认值: 1
*/
AIOT_MQTTOPT_RECONN_ENABLED,
/**
* @brief 当由于心跳丢失或者网络断开触发重连机制时, 尝试重连的时间间隔
*
* @details
*
* 数据类型: (uint32_t *) 默认值: (2 * 1000) ms
*/
AIOT_MQTTOPT_RECONN_INTERVAL_MS,
/**
* @brief MQTT发送数据时, 在协议栈花费的最长时间
*
* @details
*
* 数据类型: (uint32_t *) 默认值: (5 * 1000) ms
*/
AIOT_MQTTOPT_SEND_TIMEOUT_MS,
/**
* @brief MQTT接收数据时, 在协议栈花费的最长时间
*
* @details
*
* 数据类型: (uint32_t *) 默认值: (5 * 1000) ms
*/
AIOT_MQTTOPT_RECV_TIMEOUT_MS,
/**
* @brief QoS1消息重发间隔
*
* @details
*
* 当发送qos1 MQTT PUBLISH报文后, 如果在@ref AIOT_MQTTOPT_REPUB_TIMEOUT_MS 时间内未收到mqtt PUBACK报文,
* @ref aiot_mqtt_process 会重新发送此qo1 MQTT PUBLISH报文, 直到收到PUBACK报文为止
*
* 数据类型: (uint32_t *) 默认值: (3 * 1000) ms
*/
AIOT_MQTTOPT_REPUB_TIMEOUT_MS,
/**
* @brief 销毁MQTT实例时, 等待其他api执行完毕的时间
*
* @details
*
* 当调用@ref aiot_mqtt_deinit 销毁MQTT实例时, 若继续调用其他aiot_mqtt_xxx API, API会返回@ref STATE_USER_INPUT_EXEC_DISABLED 错误
*
* 此时, 用户应该停止调用其他aiot_mqtt_xxx API
*
* 数据类型: (uint32_t *) 默认值: (2 * 1000) ms
*/
AIOT_MQTTOPT_DEINIT_TIMEOUT_MS,
/**
* @brief 从MQTT服务器收取的数据从此默认回调函数进行通知
*
* @details
*
* 1. 若没有配置该回调函数, 当有消息到达但找不到对应的已注册topic时, 消息会被丢弃
*
* 2. 若已配置该回调函数, 当有消息到达但找不到对应的已注册topic时, 消息从此默认回调函数进行通知
*
* 数据类型: ( @ref aiot_mqtt_recv_handler_t )
*/
AIOT_MQTTOPT_RECV_HANDLER,
/**
* @brief MQTT客户端内部发生的事件会从此回调函数进行通知, 如上线/断线/重新上线
*
* @details
*
* 数据类型: ( @ref aiot_mqtt_event_handler_t )
*/
AIOT_MQTTOPT_EVENT_HANDLER,
/**
* @brief 可在MQTT建立连接之前配置MQTT topic与其对应的回调函数
*
* @details
*
* 数据类型: ( @ref aiot_mqtt_topic_map_t )
*/
AIOT_MQTTOPT_APPEND_TOPIC_MAP,
/**
* @brief 取消之前建立的MQTT topic与其回调函数的对应关系
*
* @details
*
* 数据类型: ( @ref aiot_mqtt_topic_map_t )
*/
AIOT_MQTTOPT_REMOVE_TOPIC_MAP,
/**
* @brief 在publish消息的topic上附加请求ID字符串, 用于全链路日志追踪
*
* @details
*
* 数据类型: (uint8_t *) 默认值: 0
*
* 配置为0则不附加请求ID字符串, 配置为1将附加请求ID字符串
*/
AIOT_MQTTOPT_APPEND_REQUESTID,
/**
* @brief 用户需要SDK暂存的上下文
*
* @details
*
* 1. 该上下文会在@ref AIOT_MQTTOPT_RECV_HANDLER 和@ref AIOT_MQTTOPT_EVENT_HANDLER 中传回给用户
*
* 2. 当使用@ref AIOT_MQTTOPT_APPEND_TOPIC_MAP 或者@ref aiot_mqtt_sub 时未指定userdata, 该上下文也会传给这些回调函数
*
* 数据类型: (void *)
*/
AIOT_MQTTOPT_USERDATA,
/**
* @brief SDK缓存的QOS 1消息的数量的最大值
*
* @details
*
* 当发送qos1 MQTT PUBLISH报文后, 该消息会加入到一个重传的list中,收到pub ack报文后该消息会从该list中删除
* 该list需要设置一个最大值, 否则该list在弱网情况下可能会一直膨胀,直到内存开销耗完
*
* 数据类型: (uint16_t *) 默认值: 20
*/
AIOT_MQTTOPT_MAX_REPUB_NUM,
AIOT_MQTTOPT_MAX
} aiot_mqtt_option_t;
/**
* @brief 初始化mqtt实例并设置默认参数
*
* @return void*
* @retval 非NULL MQTT实例句柄
* @retval NULL 初始化失败, 一般是内存分配失败导致
*
*/
void *aiot_mqtt_init(void);
/**
* @brief 设置mqtt参数
*
* @details
*
* 下面列出常用的配置选项, 至少需要配置以下选项才可使用MQTT的基本功能
*
* 其余配置选项均设有默认值, 可按业务需要进行调整
*
* + `AIOT_MQTTOPT_HOST`: 配置连接的阿里云MQTT站点地址
*
* + `AIOT_MQTTOPT_PORT`: 配置连接的阿里云MQTT站点端口号
*
* + `AIOT_MQTTOPT_PRODUCT_KEY`: 配置设备的 productKey
*
* + `AIOT_MQTTOPT_DEVICE_NAME`: 配置设备的 deviceName
*
* + `AIOT_MQTTOPT_DEVICE_SECRET`: 配置设备的 deviceSecret
*
* + `AIOT_MQTTOPT_NETWORK_CRED`: 配置建立MQTT连接时的安全凭据
*
* + `AIOT_MQTTOPT_RECV_HANDLER`: 配置默认的数据接收回调函数
*
* + `AIOT_MQTTOPT_EVENT_HANDLER`: 配置MQTT事件通知回调函数
*
* @param[in] handle mqtt句柄
* @param[in] option 配置选项, 更多信息请参考@ref aiot_mqtt_option_t
* @param[in] data 配置选项数据, 更多信息请参考@ref aiot_mqtt_option_t
*
* @return int32_t
* @retval <STATE_SUCCESS 参数设置失败, 更多信息请参考@ref aiot_state_api.h
* @retval >=STATE_SUCCESS 参数设置成功
*
*/
int32_t aiot_mqtt_setopt(void *handle, aiot_mqtt_option_t option, void *data);
/**
* @brief 释放mqtt实例句柄的资源
*
* @param[in] handle 指向mqtt实例句柄的指针
*
* @return int32_t
* @retval <STATE_SUCCESS 执行失败, 更多信息请参考@ref aiot_state_api.h
* @retval >=STATE_SUCCESS 执行成功
*
*/
int32_t aiot_mqtt_deinit(void **handle);
/**
* @brief 与MQTT服务器建立连接
*
* @details
*
* 使用@ref aiot_mqtt_setopt 配置的mqtt连接参数连接mqtt服务器, 使用的建联参数按如下顺序选择
*
* 1. 若配置了以下选项, 直接用配置的连接参数连接 @ref AIOT_MQTTOPT_HOST 选项指定的任意MQTT服务器
*
* + @ref AIOT_MQTTOPT_USERNAME
* + @ref AIOT_MQTTOPT_PASSWORD
* + @ref AIOT_MQTTOPT_CLIENTID
*
* 2. 若配置了以下选项, 则强制以阿里云平台的签名算法计算连接参数作为MQTT的用户名/密码, 连接阿里云平台
*
* + @ref AIOT_MQTTOPT_PRODUCT_KEY
* + @ref AIOT_MQTTOPT_DEVICE_NAME
* + @ref AIOT_MQTTOPT_DEVICE_SECRET
*
* @param[in] handle MQTT实例句柄
*
* @return int32_t
* @retval <STATE_SUCCESS 执行失败, 更多信息请参考@ref aiot_state_api.h
* @retval >=STATE_SUCCESS 执行成功
*
* @note
*
* 当配置@ref AIOT_MQTTOPT_USERNAME , @ref AIOT_MQTTOPT_PASSWORD 和@ref AIOT_MQTTOPT_CLIENTID 配置自定义连接凭据时,
*
* 此函数会忽略@ref AIOT_MQTTOPT_PRODUCT_KEY , @ref AIOT_MQTTOPT_DEVICE_NAME 和@ref AIOT_MQTTOPT_DEVICE_SECRET,
*
* 直接使用自定义凭据连接指定的MQTT服务器
*/
int32_t aiot_mqtt_connect(void *handle);
/**
* @brief 与MQTT服务器断开连接
*
* @details
*
* 向MQTT服务器发送MQTT DISCONNECT报文, 然后断开网络连接
*
* 如果需要再次与MQTT服务器建立连接, 调用@ref aiot_mqtt_connect 即可
*
* @param[in] handle MQTT实例句柄
*
* @return int32_t
* @retval <STATE_SUCCESS 执行失败, 更多信息请参考@ref aiot_state_api.h
* @retval >=STATE_SUCCESS 执行成功
*/
int32_t aiot_mqtt_disconnect(void *handle);
/**
* @brief 发送MQTT PINGREQ报文, 用于维持心跳
*
* @details
*
* @ref aiot_mqtt_process 包含了定时发送心跳的机制, 如果有特殊需要的话, 可以使用此函数直接发送心跳报文
*
* @param[in] handle MQTT实例句柄
*
* @return int32_t
* @retval <STATE_SUCCESS 执行失败, 更多信息请参考@ref aiot_state_api.h
* @retval >=STATE_SUCCESS 执行成功
*/
int32_t aiot_mqtt_heartbeat(void *handle);
/**
* @brief 此函数用于处理定时心跳发送和qos1消息的重传逻辑
*
* @details
*
* 1. 发送心跳至mqtt broker以维护mqtt连接, 心跳发送间隔由@ref AIOT_MQTTOPT_HEARTBEAT_INTERVAL_MS 配置项控制
*
* 2. 如果一条qos1的mqtt PUBLISH报文在@ref AIOT_MQTTOPT_REPUB_TIMEOUT_MS 时间内没有收到mqtt PUBACK应答报文, 该函数会重发此消息, 直到成功为止
*
* @param[in] handle MQTT实例句柄
*
* @return int32_t
* @retval <STATE_SUCCESS 执行失败, 更多信息请参考@ref aiot_state_api.h
* @retval >=STATE_SUCCESS 执行成功
*
* @note
*
* 该函数为非阻塞, 需要间歇性被调用, 调用间隔应当小于@ref AIOT_MQTTOPT_HEARTBEAT_INTERVAL_MS 和@ref AIOT_MQTTOPT_REPUB_TIMEOUT_MS 时间内没有收到mqtt的最小值,
*
* 以确保心跳发送和QoS1消息的重传逻辑正常工作
*/
int32_t aiot_mqtt_process(void *handle);
/**
* @brief 发送一条PUBLISH报文到MQTT服务器, QoS为0, 用于发布指定的消息
*
* @param[in] handle MQTT实例句柄
* @param[in] topic 指定MQTT PUBLISH报文的topic
* @param[in] payload 指定MQTT PUBLISH报文的payload
* @param[in] payload_len 指定MQTT PUBLISH报文的payload_len
* @param[in] qos 指定mqtt的qos值, 仅支持qos0和qos1
*
* @return int32_t
* @retval <STATE_SUCCESS 执行失败, 更多信息请参考@ref aiot_state_api.h
* @retval >=STATE_SUCCESS 执行成功
*/
int32_t aiot_mqtt_pub(void *handle, char *topic, uint8_t *payload, uint32_t payload_len, uint8_t qos);
/**
* @brief 发送一条mqtt SUBSCRIBE报文到MQTT服务器, 用于订阅指定的topic
*
* @param[in] handle MQTT实例句柄
* @param[in] topic 指定MQTT SUBSCRIBE报文的topic
* @param[in] handler 与topic对应的MQTT PUBLISH报文回调函数, 当有消息发布到topic时, 该回调函数被调用
若handler为NULL传入, 则SDK调用@ref AIOT_MQTTOPT_RECV_HANDLER 配置的回调函数
若多次调用aiot_mqtt_sub()并对同一topic指定不同的handler, 有消息到达时不同handler都会被调用到
* @param[in] qos 指定topic期望mqtt服务器支持的最大qos值, 仅支持qos0和qos1
* @param[in] userdata 可让SDK代为保存的用户上下文, 当回调函数被调用时, 此上下文会通过handler传回给用户
* 若未指定该上下文, 那么通过@ref AIOT_MQTTOPT_USERDATA 配置的上下文会通过handler传回给用户
*
* @return int32_t
* @retval <STATE_SUCCESS 执行失败, 更多信息请参考@ref aiot_state_api.h
* @retval >=STATE_SUCCESS 执行成功
*/
int32_t aiot_mqtt_sub(void *handle, char *topic, aiot_mqtt_recv_handler_t handler, uint8_t qos, void *userdata);
/**
* @brief 发送一条mqtt UNSUBSCRIBE报文到MQTT服务器, 用于取消订阅指定的topic
*
* @param[in] handle MQTT实例句柄
* @param[in] topic 指定MQTT UNSUBSCRIBE报文的topic
*
* @return int32_t
* @retval <STATE_SUCCESS 执行失败, 更多信息请参考@ref aiot_state_api.h
* @retval >=STATE_SUCCESS 执行成功
*/
int32_t aiot_mqtt_unsub(void *handle, char *topic);
/**
* @brief 尝试从网络上接收MQTT报文
*
* @details
*
* 除了从网络上接收MQTT报文之外, 本函数也包含了重连机制
*
* 1. 当MQTT心跳丢失超过@ref AIOT_MQTTOPT_HEARTBEAT_MAX_LOST 配置的次数时, 触发重连机制
*
* + 重连间隔由 @ref AIOT_MQTTOPT_RECONN_INTERVAL_MS 指定
*
* 2. 当SDK检测到网络断开时, 触发重连机制
*
* + 重连间隔由 @ref AIOT_MQTTOPT_RECONN_INTERVAL_MS 指定
*
* @param[in] handle
*
* @retval STATE_SYS_DEPEND_NWK_READ_LESSDATA 执行成功, 此时网络上暂无可以收取的MQTT报文
* @retval >=STATE_SUCCESS 执行成功
* @retval 其他返回值 执行失败, 更多信息请参考@ref aiot_state_api.h
*
* @note
*
* 当网络连接正常并且@ref aiot_mqtt_deinit 未被调用时, 该函数为阻塞, 需要持续被调用
*
* 1. 当网络连接断开时, 该函数会立即返回, 此时返回值为@ref STATE_SYS_DEPEND_NWK_CLOSED
*
* 2. 当@ref aiot_mqtt_deinit 被调用时, 该函数会立即返回, 此时返回值为@ref STATE_USER_INPUT_EXEC_DISABLED
*/
int32_t aiot_mqtt_recv(void *handle);
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,20 @@
/**
* @file aiot_state_api.c
* @brief 状态码模块实现
* @date 2019-12-27
*
* @copyright Copyright (C) 2015-2018 Alibaba Group Holding Limited
*
*/
#include "core_stdinc.h"
#include "aiot_state_api.h"
aiot_state_logcb_t g_logcb_handler = NULL;
int32_t aiot_state_set_logcb(aiot_state_logcb_t handler)
{
g_logcb_handler = handler;
return 0;
}

View File

@@ -0,0 +1,746 @@
/**
* @file aiot_state_api.h
* @brief SDK Core状态码头文件, 所有Core中的api返回值均在此列出
* @date 2019-12-27
*
* @copyright Copyright (C) 2015-2018 Alibaba Group Holding Limited
*
*/
#ifndef _AIOT_STATE_API_H_
#define _AIOT_STATE_API_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdint.h>
/**
* @brief SDK的日志信息输出回调函数原型
*/
typedef int32_t (* aiot_state_logcb_t)(int32_t code, char *message);
/**
* @brief 设置SDK的日志信息输出使用的回调函数
*
* @param handler 日志回调函数
*
* @return int32_t 保留
*/
int32_t aiot_state_set_logcb(aiot_state_logcb_t handler);
/**
* @brief API执行成功
*
*/
#define STATE_SUCCESS (0x0000)
/**
* @brief -0x0100~-0x01FF表达SDK检查用户输入参数时反馈的状态码
*
*/
#define STATE_USER_INPUT_BASE (-0x0100)
/**
* @brief 用户输入参数中包含非法的空指针
*
*/
#define STATE_USER_INPUT_NULL_POINTER (-0x0101)
/**
* @brief 用户输入参数中包含越界的值
*
*/
#define STATE_USER_INPUT_OUT_RANGE (-0x0102)
/**
* @brief 用户输入的配置项不能被SDK的模块所理解
*
*/
#define STATE_USER_INPUT_UNKNOWN_OPTION (-0x0103)
/**
* @brief 用户输入参数中缺少productKey
*
*/
#define STATE_USER_INPUT_MISSING_PRODUCT_KEY (-0x0104)
/**
* @brief 用户输入参数中缺少deviceName
*
*/
#define STATE_USER_INPUT_MISSING_DEVICE_NAME (-0x0105)
/**
* @brief 用户输入参数中缺少deviceSecret
*
*/
#define STATE_USER_INPUT_MISSING_DEVICE_SECRET (-0x0106)
/**
* @brief 用户输入参数中缺少productSecret
*
*/
#define STATE_USER_INPUT_MISSING_PRODUCT_SECRET (-0x0107)
/**
* @brief 用户输入参数中缺少域名地址或IP地址
*
*/
#define STATE_USER_INPUT_MISSING_HOST (-0x0108)
/**
* @brief 用户已调用销毁函数销毁了实例(如@ref aiot_mqtt_deinit), 其余对该实例操作的API不应该再被调用
*
*/
#define STATE_USER_INPUT_EXEC_DISABLED (-0x0109)
/**
* @brief 用户输入的JSON字符串解析失败
*
*/
#define STATE_USER_INPUT_JSON_PARSE_FAILED (-0x010A)
/**
* @brief -0x0200~-0x02FF表达SDK调用系统依赖函数时反馈的状态码
*
*/
#define STATE_SYS_DEPEND_BASE (-0x0200)
/**
* @brief @ref aiot_sysdep_portfile_t::core_sysdep_malloc 申请内存失败
*
*/
#define STATE_SYS_DEPEND_MALLOC_FAILED (-0x0201)
/**
* @brief @ref aiot_sysdep_portfile_t::core_sysdep_network_setopt 的某个cred之外的入参非法
*
*/
#define STATE_SYS_DEPEND_NWK_INVALID_OPTION (-0x0202)
/**
* @brief @ref aiot_sysdep_portfile_t::core_sysdep_network_establish 建立网络失败
*
*/
#define STATE_SYS_DEPEND_NWK_EST_FAILED (-0x0203)
/**
* @brief SDK检测到网络已断开
*
*/
#define STATE_SYS_DEPEND_NWK_CLOSED (-0x0204)
/**
* @brief SDK从网络上实际读取的数据比期望读取的少
*
*/
#define STATE_SYS_DEPEND_NWK_READ_LESSDATA (-0x0205)
/**
* @brief SDK向网络上写入的实际数据比期望写入的少
*
*/
#define STATE_SYS_DEPEND_NWK_WRITE_LESSDATA (-0x0206)
/**
* @brief @ref aiot_sysdep_portfile_t::core_sysdep_network_recv 超时返回
*
*/
#define STATE_SYS_DEPEND_NWK_READ_OVERTIME (-0x0207)
/**
* @brief @ref aiot_sysdep_portfile_t::core_sysdep_network_setopt 的cred入参非法
*
*/
#define STATE_SYS_DEPEND_NWK_INVALID_CRED (-0x0208)
/**
* @brief @ref aiot_sysdep_portfile_t::core_sysdep_network_send 发送数据遭遇异常
*
*/
#define STATE_SYS_DEPEND_NWK_SEND_ERR (-0x0209)
/**
* @brief @ref aiot_sysdep_portfile_t::core_sysdep_network_recv 接收数据遭遇异常
*
*/
#define STATE_SYS_DEPEND_NWK_RECV_ERR (-0x020A)
/**
* @brief -0x0300~-0x03FF表达SDK在MQTT模块内的状态码
*
*/
#define STATE_MQTT_BASE (-0x0300)
/**
* @brief MQTT尝试建立连接时, 从服务端返回的CONNACK报文格式错误
*
*/
#define STATE_MQTT_CONNACK_FMT_ERROR (-0x0301)
/**
* @brief MQTT建连时, 服务端报错与当前客户端使用的MQTT协议版本不兼容
*
*/
#define STATE_MQTT_CONNACK_RCODE_UNACCEPTABLE_PROTOCOL_VERSION (-0x0302)
/**
* @brief MQTT建连时, 服务端报错服务不可用, 可能是clientId取值不正确或心跳间隔不合理
*
*/
#define STATE_MQTT_CONNACK_RCODE_SERVER_UNAVAILABLE (-0x0303)
/**
* @brief MQTT建连时, 服务端返回用户名密码不合法
*
*/
#define STATE_MQTT_CONNACK_RCODE_BAD_USERNAME_PASSWORD (-0x0304)
/**
* @brief MQTT建连时, 服务端返回认证失败, 用户名或者密码错误, 一般为三元组错误导致
*
*/
#define STATE_MQTT_CONNACK_RCODE_NOT_AUTHORIZED (-0x0305)
/**
* @brief MQTT建连时, 服务端返回未知的CONNACK报文
*
*/
#define STATE_MQTT_CONNACK_RCODE_UNKNOWN (-0x0306)
/**
* @brief MQTT缓存QoS1消息时, 检测到packetId发生卷绕
*
*/
#define STATE_MQTT_PUBLIST_PACKET_ID_ROLL (-0x0307)
/**
* @brief MQTT发布或订阅时, 检测到Topic格式不符合协议规范
*
*/
#define STATE_MQTT_TOPIC_INVALID (-0x0308)
/**
* @brief MQTT发布或订阅或取消订阅时, SDK反馈Topic内容的状态码
*
*/
#define STATE_MQTT_LOG_TOPIC (-0x0309)
/**
* @brief MQTT收发消息时, SDK反馈MQTT消息内容的状态码
*
*/
#define STATE_MQTT_LOG_HEXDUMP (-0x030A)
/**
* @brief MQTT连接建立成功
*
*/
#define STATE_MQTT_CONNECT_SUCCESS (-0x030B)
/**
* @brief SDK读取到的MQTT报文含有不符合协议规范的报文长度字段
*
*/
#define STATE_MQTT_MALFORMED_REMAINING_LEN (-0x030C)
/**
* @brief SDK读取到的MQTT报文不符合按协议规范所解析的字节数
*
*/
#define STATE_MQTT_MALFORMED_REMAINING_BYTES (-0x030D)
/**
* @brief SDK读取到尚不支持的MQTT报文类型
*
*/
#define STATE_MQTT_PACKET_TYPE_UNKNOWN (-0x030E)
/**
* @brief MQTT订阅或取消订阅时, 从服务端得到操作失败的回应
*
*/
#define STATE_MQTT_SUBACK_RCODE_FAILURE (-0x030F)
/**
* @brief MQTT订阅或取消订阅时, 从服务端得到无法解析的应答
*
*/
#define STATE_MQTT_SUBACK_RCODE_UNKNOWN (-0x0310)
/**
* @brief MQTT接收消息时, 消息中的Topic与可理解的Topic列表匹配失败
*
*/
#define STATE_MQTT_TOPIC_COMPARE_FAILED (-0x0311)
/**
* @brief 执行@ref aiot_mqtt_deinit 时, 为等待其他API执行结束, 超过设定的超时时间, MQTT实例销毁失败
*
*/
#define STATE_MQTT_DEINIT_TIMEOUT (-0x0312)
/**
* @brief MQTT主动连接服务器相关的日志状态码
*
*/
#define STATE_MQTT_LOG_CONNECT (-0x0313)
/**
* @brief MQTT断线后, 自动重连服务器相关的日志状态码
*
*/
#define STATE_MQTT_LOG_RECONNECTING (-0x0314)
/**
* @brief MQTT连接服务器超时相关的日志状态码
*
*/
#define STATE_MQTT_LOG_CONNECT_TIMEOUT (-0x0315)
/**
* @brief MQTT主动与服务器断开连接相关的日志状态码
*
*/
#define STATE_MQTT_LOG_DISCONNECT (-0x0316)
/**
* @brief MQTT连接服务器时, 使用的用户名相关的日志状态码
*
*/
#define STATE_MQTT_LOG_USERNAME (-0x0317)
/**
* @brief MQTT连接服务器时, 使用的密码相关的日志状态码
*
*/
#define STATE_MQTT_LOG_PASSWORD (-0x0318)
/**
* @brief MQTT连接服务器时, 使用的clientId相关的日志状态码
*
*/
#define STATE_MQTT_LOG_CLIENTID (-0x0319)
/**
* @brief MQTT连接服务器时, 使用的PSK-TLS密钥相关的日志状态码
*
*/
#define STATE_MQTT_LOG_TLS_PSK (-0x031A)
/**
* @brief MQTT发布或订阅或取消订阅时, Topic的长度超出物联网平台的限制, 中止执行
*
*/
#define STATE_MQTT_TOPIC_TOO_LONG (-0x031B)
/**
* @brief MQTT发布消息时, Payload的长度超出物联网平台的限制, 中止执行
*
*/
#define STATE_MQTT_PUB_PAYLOAD_TOO_LONG (-0x031C)
/**
* @brief MQTT连接服务器时, 使用的备用ip地址
*
*/
#define STATE_MQTT_LOG_BACKUP_IP (-0x031D)
/**
* @brief 接收到非法的MQTT PINRESP报文
*
*/
#define STATE_MQTT_RECV_INVALID_PINRESP_PACKET (-0x031E)
/**
* @brief 接收到非法的MQTT PUBLISH报文
*
*/
#define STATE_MQTT_RECV_INVALID_PUBLISH_PACKET (-0x031F)
/**
* @brief 接收到非法的MQTT PUBACK报文
*
*/
#define STATE_MQTT_RECV_INVALID_PUBACK_PACKET (-0x0320)
/**
* @brief MQTT连接服务器时, 使用的host的日志状态码
*
*/
#define STATE_MQTT_LOG_HOST (-0x032A)
/**
* @brief -0x0400~-0x04FF表达SDK在HTTP模块内的状态码
*
*/
#define STATE_HTTP_BASE (-0x0400)
/**
* @brief 解析收到的HTTP报文时, 无法获取有效的状态行, 得不到HTTP StatusCode
*
*/
#define STATE_HTTP_STATUS_LINE_INVALID (-0x0401)
/**
* @brief 解析收到的HTTP报文时, 报文的body部分已接收完毕, 没有更多数据
*
*/
#define STATE_HTTP_READ_BODY_FINISHED (-0x0402)
/**
* @brief 执行@ref aiot_http_deinit 时, 等待其他API执行结束超过设定的超时时间, HTTP实例销毁失败
*
*/
#define STATE_HTTP_DEINIT_TIMEOUT (-0x0403)
/**
* @brief 因为HTTP认证应答的StatusCode不是200, 认证失败
*
*/
#define STATE_HTTP_AUTH_CODE_FAILED (-0x0404)
/**
* @brief 因为HTTP认证应答接收未完成, 认证失败
*
*/
#define STATE_HTTP_AUTH_NOT_FINISHED (-0x0405)
/**
* @brief 因为HTTP认证应答中未能解析到Token, 认证失败
*
*/
#define STATE_HTTP_AUTH_TOKEN_FAILED (-0x0406)
/**
* @brief 设备尚未认证, 需要先调用@ref aiot_http_auth 进行设备认证
*
*/
#define STATE_HTTP_NEED_AUTH (-0x0407)
/**
* @brief HTTP应答数据接收未完成, 需确认网络是否异常
*
*/
#define STATE_HTTP_RECV_NOT_FINISHED (-0x0408)
/**
* @brief 内部buffer长度过短, 需调用@ref aiot_http_setopt 配置@ref AIOT_HTTPOPT_HEADER_BUFFER_LEN 选项增大buffer长度
*
*/
#define STATE_HTTP_HEADER_BUFFER_TOO_SHORT (-0x0409)
/**
* @brief HTTP首部接收异常
*
*/
#define STATE_HTTP_HEADER_INVALID (-0x040A)
/**
* @brief HTTP首部发送相关日志使用的状态码
*
*/
#define STATE_HTTP_LOG_SEND_HEADER (-0x040B)
/**
* @brief HTTP内容发送相关日志使用的状态码
*
*/
#define STATE_HTTP_LOG_SEND_CONTENT (-0x040C)
/**
* @brief HTTP首部接收相关日志使用的状态码
*
*/
#define STATE_HTTP_LOG_RECV_HEADER (-0x040D)
/**
* @brief HTTP内容接收相关日志使用的状态码
*
*/
#define STATE_HTTP_LOG_RECV_CONTENT (-0x040E)
/**
* @brief HTTP连接断开相关日志使用的状态码
*
*/
#define STATE_HTTP_LOG_DISCONNECT (-0x040F)
/**
* @brief HTTP认证交互相关日志使用的状态码
*
*/
#define STATE_HTTP_LOG_AUTH (-0x0410)
/**
* @brief 因为HTTP应答报文的内容不符合预期, 认证失败
*
*/
#define STATE_HTTP_AUTH_NOT_EXPECTED (-0x0411)
/**
* @brief HTTP报文的负载部分为空, 接收已完成
*
*/
#define STATE_HTTP_READ_BODY_EMPTY (-0x0412)
/**
* @brief -0x0F00~-0x0FFF表达SDK在系统底层依赖模块内的状态码
*
*/
#define STATE_PORT_BASE (-0x0F00)
/**
* @brief 底层依赖函数遇到非法的空指针参数, 执行失败
*
*/
#define STATE_PORT_INPUT_NULL_POINTER (-0x0F01)
/**
* @brief 底层依赖函数遇到输入参数超出合理值域, 执行失败
*
*/
#define STATE_PORT_INPUT_OUT_RANGE (-0x0F02)
/**
* @brief 底层依赖函数遇到申请内存错误, 执行失败
*
*/
#define STATE_PORT_MALLOC_FAILED (-0x0F03)
/**
* @brief 底层依赖函数遇到域名地址或IP地址缺失, 执行失败
*
*/
#define STATE_PORT_MISSING_HOST (-0x0F04)
/**
* @brief 底层依赖函数遇到TCP客户端建立过程尚未实现, 执行失败
*
*/
#define STATE_PORT_TCP_CLIENT_NOT_IMPLEMENT (-0x0F05)
/**
* @brief 底层依赖函数遇到TCP服务端建立过程尚未实现, 执行失败
*
*/
#define STATE_PORT_TCP_SERVER_NOT_IMPLEMENT (-0x0F06)
/**
* @brief 底层依赖函数遇到UDP客户端建立过程尚未实现, 执行失败
*
*/
#define STATE_PORT_UDP_CLIENT_NOT_IMPLEMENT (-0x0F07)
/**
* @brief 底层依赖函数遇到UDP服务端建立过程尚未实现, 执行失败
*
*/
#define STATE_PORT_UDP_SERVER_NOT_IMPLEMENT (-0x0F08)
/**
* @brief 底层依赖函数遇到不能理解的网络层设置选项, 执行失败
*
*/
#define STATE_PORT_NETWORK_UNKNOWN_OPTION (-0x0F09)
/**
* @brief 底层依赖函数遇到不能理解的socket类型, 执行失败
*
*/
#define STATE_PORT_NETWORK_UNKNOWN_SOCKET_TYPE (-0x0F0A)
/**
* @brief 底层依赖函数遇到域名DNS解析错误, 执行失败
*
*/
#define STATE_PORT_NETWORK_DNS_FAILED (-0x0F0B)
/**
* @brief 底层依赖函数建立网络连接时遇到socket创建错误, 执行失败
*
*/
#define STATE_PORT_NETWORK_SOCKET_CREATE_FAILED (-0x0F0C)
/**
* @brief 底层依赖函数建立网络连接时遇到socket配置错误, 执行失败
*
*/
#define STATE_PORT_NETWORK_SOCKET_CONFIG_FAILED (-0x0F0D)
/**
* @brief 底层依赖函数建立网络连接时遇到bind错误, 执行失败
*
*/
#define STATE_PORT_NETWORK_SOCKET_BIND_FAILED (-0x0F0E)
/**
* @brief 底层依赖函数遇到TCP连接超时未成功, 执行失败
*
*/
#define STATE_PORT_NETWORK_CONNECT_TIMEOUT (-0x0F0F)
/**
* @brief 底层依赖函数遇到TCP连接建立错误, 执行失败
*
*/
#define STATE_PORT_NETWORK_CONNECT_FAILED (-0x0F10)
/**
* @brief 底层依赖函数遇到套接字select错误, 执行失败
*
*/
#define STATE_PORT_NETWORK_SELECT_FAILED (-0x0F11)
/**
* @brief 底层依赖函数遇到网络层发送数据错误, 执行失败
*
*/
#define STATE_PORT_NETWORK_SEND_FAILED (-0x0F12)
/**
* @brief 底层依赖函数遇到网络层接收数据错误, 执行失败
*
*/
#define STATE_PORT_NETWORK_RECV_FAILED (-0x0F13)
/**
* @brief 底层依赖函数发送数据时遇到连接已关闭, 执行失败
*
*/
#define STATE_PORT_NETWORK_SEND_CONNECTION_CLOSED (-0x0F14)
/**
* @brief 底层依赖函数接收数据时遇到连接已关闭, 执行失败
*
*/
#define STATE_PORT_NETWORK_RECV_CONNECTION_CLOSED (-0x0F15)
/**
* @brief 底层依赖函数遇到不能理解的安全凭据选项, 执行失败
*
*/
#define STATE_PORT_TLS_INVALID_CRED_OPTION (-0x0F16)
/**
* @brief 底层依赖函数遇到不合法的最大TLS分片长度配置, 执行失败
*
*/
#define STATE_PORT_TLS_INVALID_MAX_FRAGMENT (-0x0F17)
/**
* @brief 底层依赖函数遇到不合法的服务端证书, 执行失败
*
*/
#define STATE_PORT_TLS_INVALID_SERVER_CERT (-0x0F18)
/**
* @brief 底层依赖函数遇到不合法的客户端证书, 执行失败
*
*/
#define STATE_PORT_TLS_INVALID_CLIENT_CERT (-0x0F19)
/**
* @brief 底层依赖函数遇到不合法的客户端密钥, 执行失败
*
*/
#define STATE_PORT_TLS_INVALID_CLIENT_KEY (-0x0F1A)
/**
* @brief 底层依赖函数建立TLS连接时遇到socket创建错误, 执行失败
*
*/
#define STATE_PORT_TLS_SOCKET_CREATE_FAILED (-0x0F1B)
/**
* @brief 底层依赖函数建立TLS连接时遇到socket连接建立错误, 执行失败
*
*/
#define STATE_PORT_TLS_SOCKET_CONNECT_FAILED (-0x0F1C)
/**
* @brief 底层依赖函数建立TLS连接时遇到握手失败, 执行失败
*
*/
#define STATE_PORT_TLS_INVALID_RECORD (-0x0F1D)
/**
* @brief 底层依赖函数在TLS连接上, 遇到数据接收错误, 执行失败
*
*/
#define STATE_PORT_TLS_RECV_FAILED (-0x0F1E)
/**
* @brief 底层依赖函数在TLS连接上, 遇到数据发送错误, 执行失败
*
*/
#define STATE_PORT_TLS_SEND_FAILED (-0x0F1F)
/**
* @brief 底层依赖函数在TLS连接上, 接收数据时遇到连接已关闭, 执行失败
*
*/
#define STATE_PORT_TLS_RECV_CONNECTION_CLOSED (-0x0F20)
/**
* @brief 底层依赖函数在TLS连接上, 发送数据时遇到连接已关闭, 执行失败
*
*/
#define STATE_PORT_TLS_SEND_CONNECTION_CLOSED (-0x0F21)
/**
* @brief 底层依赖函数建立TLS连接时遇到PSK配置错误, 执行失败
*
*/
#define STATE_PORT_TLS_CONFIG_PSK_FAILED (-0x0F22)
/**
* @brief 底层依赖函数建立TLS连接时遇到非法记录以外的握手错误, 执行失败
*
*/
#define STATE_PORT_TLS_INVALID_HANDSHAKE (-0x0F23)
/**
* @brief DTLS握手时设置PSK配置错误, 执行失败
*
*/
#define STATE_PORT_DTLS_CONFIG_PSK_FAILED (-0x0F24)
/**
* @brief DTLS握手失败, 执行失败
*
*/
#define STATE_PORT_DTLS_HANDSHAKE_FAILED (-0x0F25)
/**
* @brief DTLS建立连接时失败, 执行失败
*
*/
#define STATE_PORT_NETWORK_DTLS_CONNECT_FAILED (-0x0F26)
/**
* @brief 之前的DTLS握手还在进行中
*
*/
#define STATE_PORT_DTLS_HANDSHAKE_IN_PROGRESS (-0x0F27)
/**
* @brief 缓存的qos 1消息数量超出预期
*
*/
#define STATE_QOS_CACHE_EXCEEDS_LIMIT (-0x0F28)
/**
* @brief core_adapter适配模块
*
*/
#define STATE_ADAPTER_COMMON (-0x1000)
#if defined(__cplusplus)
}
#endif
#endif /* #ifndef _AIOT_STATE_API_H_ */

View File

@@ -0,0 +1,146 @@
/**
* @file aiot_sysdep_api.h
* @brief SDK Core系统依赖头文件, 所有Core中的系统依赖均在此文件中列出
* @date 2019-12-27
*
* @copyright Copyright (C) 2015-2018 Alibaba Group Holding Limited
*
*/
#ifndef _AIOT_SYSDEP_API_H_
#define _AIOT_SYSDEP_API_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdint.h>
typedef enum {
AIOT_SYSDEP_NETWORK_CRED_NONE,
AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA,
AIOT_SYSDEP_NETWORK_CRED_SVRCERT_PSK,
AIOT_SYSDEP_NETWORK_CRED_MAX
} aiot_sysdep_network_cred_option_t;
typedef struct {
aiot_sysdep_network_cred_option_t option; /* 安全策略 */
uint32_t max_tls_fragment;
uint8_t sni_enabled;
const char *x509_server_cert; /* 必须位于静态存储区, SDK内部不做拷贝 */
uint32_t x509_server_cert_len;
const char *x509_client_cert; /* 必须位于静态存储区, SDK内部不做拷贝 */
uint32_t x509_client_cert_len;
const char *x509_client_privkey; /* 必须位于静态存储区, SDK内部不做拷贝 */
uint32_t x509_client_privkey_len;
char *tls_extend_info;
} aiot_sysdep_network_cred_t;
typedef enum {
CORE_SYSDEP_SOCKET_TCP_CLIENT,
CORE_SYSDEP_SOCKET_TCP_SERVER,
CORE_SYSDEP_SOCKET_UDP_CLIENT,
CORE_SYSDEP_SOCKET_UDP_SERVER
} core_sysdep_socket_type_t;
typedef struct {
char *psk_id;
char *psk;
} core_sysdep_psk_t;
typedef enum {
CORE_SYSDEP_NETWORK_SOCKET_TYPE, /* 需要建立的socket类型 数据类型: (core_sysdep_socket_type_t *) */
CORE_SYSDEP_NETWORK_HOST, /* 用于建立网络连接的域名地址或ip地址, 内存与上层模块共用 数据类型: (char *) */
CORE_SYSDEP_NETWORK_BACKUP_IP, /* 当建联DNS解析失败时, 使用此备用ip重试 */
CORE_SYSDEP_NETWORK_PORT, /* 用于建立网络连接的端口号 数据类型: (uint16_t *) */
CORE_SYSDEP_NETWORK_CONNECT_TIMEOUT_MS, /* 建立网络连接的超时时间 数据类型: (uint32_t *) */
CORE_SYSDEP_NETWORK_CRED, /* 用于设置网络层安全参数 数据类型: (aiot_sysdep_network_cred_t *) */
CORE_SYSDEP_NETWORK_PSK, /* 用于配合PSK模式下的psk-id和psk 数据类型: (core_sysdep_psk_t *) */
CORE_SYSDEP_NETWORK_MAX
} core_sysdep_network_option_t;
typedef struct {
char addr[16]; /* ipv4地址点分十进制字符串, 最大长度15字节. */
uint16_t port; /* 端口号 */
} core_sysdep_addr_t;
/* 这不是一个面向用户的编译配置开关, 多数情况下, 不必用户关心 */
/**
* @brief 用以向SDK描述其运行硬件平台的资源如何使用的方法结构体
*/
typedef struct {
/**
* @brief 申请内存
*/
void *(*core_sysdep_malloc)(uint32_t size, char *name);
/**
* @brief 释放内存
*/
void (*core_sysdep_free)(void *ptr);
/**
* @brief 获取当前的时间戳SDK用于差值计算
*/
uint64_t (*core_sysdep_time)(void);
/**
* @brief 睡眠指定的毫秒数
*/
void (*core_sysdep_sleep)(uint64_t time_ms);
/**
* @brief 创建1个网络会话(L3层)
*/
void *(*core_sysdep_network_init)(void);
/**
* @brief 配置1个网络会话的连接参数
*/
int32_t (*core_sysdep_network_setopt)(void *handle, core_sysdep_network_option_t option, void *data);
/**
* @brief 建立1个网络会话, 作为MQTT/HTTP等协议的底层承载
*/
int32_t (*core_sysdep_network_establish)(void *handle);
/**
* @brief 从指定的网络会话上读取
*/
int32_t (*core_sysdep_network_recv)(void *handle, uint8_t *buffer, uint32_t len, uint32_t timeout_ms,
core_sysdep_addr_t *addr);
/**
* @brief 在指定的网络会话上发送
*/
int32_t (*core_sysdep_network_send)(void *handle, uint8_t *buffer, uint32_t len, uint32_t timeout_ms,
core_sysdep_addr_t *addr);
/**
* @brief 销毁1个网络会话
*/
int32_t (*core_sysdep_network_deinit)(void **handle);
/**
* @brief 随机数生成方法
*/
void (*core_sysdep_rand)(uint8_t *output, uint32_t output_len);
/**
* @brief 创建互斥锁
*/
void *(*core_sysdep_mutex_init)(void);
/**
* @brief 申请互斥锁
*/
void (*core_sysdep_mutex_lock)(void *mutex);
/**
* @brief 释放互斥锁
*/
void (*core_sysdep_mutex_unlock)(void *mutex);
/**
* @brief 销毁互斥锁
*/
void (*core_sysdep_mutex_deinit)(void **mutex);
} aiot_sysdep_portfile_t;
void aiot_sysdep_set_portfile(aiot_sysdep_portfile_t *portfile);
aiot_sysdep_portfile_t *aiot_sysdep_get_portfile(void);
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,711 @@
#include "core_adapter.h"
#include "aiot_state_api.h"
#include "core_log.h"
static aiot_sysdep_portfile_t *g_origin_portfile = NULL;
static aiot_sysdep_portfile_t g_aiot_portfile;
/*
* CORE_ADAPTER_MBEDTLS_ENABLED 不是一个用户需要关心的编译开关
*
* 大多数情况下, 就保持它如下的设置即可
* 只有少数时候, SDK的用户关心对接层代码的ROM尺寸, 并且也没有选择用TLS连接服务器
* 那时才会出现, 将 CORE_ADAPTER_MBEDTLS_ENABLED 宏定义关闭的改动, 以减小对接尺寸
*
* 我们不建议去掉 #define CORE_ADAPTER_MBEDTLS_ENABLED 这行代码
* 虽然物联网平台接收TCP方式的连接, 但我们不推荐这样做, TLS是更安全的通信方式
*
*/
#define CORE_ADAPTER_MBEDTLS_ENABLED
typedef struct {
void *(*core_sysdep_network_init)(void);
int32_t (*core_sysdep_network_setopt)(void *handle, core_sysdep_network_option_t option, void *data);
int32_t (*core_sysdep_network_establish)(void *handle);
int32_t (*core_sysdep_network_recv)(void *handle, uint8_t *buffer, uint32_t len, uint32_t timeout_ms,
core_sysdep_addr_t *addr);
int32_t (*core_sysdep_network_send)(void *handle, uint8_t *buffer, uint32_t len, uint32_t timeout_ms,
core_sysdep_addr_t *addr);
int32_t (*core_sysdep_network_deinit)(void **handle);
} aiot_network_t;
#ifdef CORE_ADAPTER_MBEDTLS_ENABLED
#include "mbedtls/net_sockets.h"
#include "mbedtls/ssl.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/debug.h"
#include "mbedtls/platform.h"
#include "mbedtls/timing.h"
typedef struct {
mbedtls_net_context net_ctx;
mbedtls_ssl_context ssl_ctx;
mbedtls_ssl_config ssl_config;
mbedtls_timing_delay_context timer_delay_ctx;
mbedtls_x509_crt x509_server_cert;
mbedtls_x509_crt x509_client_cert;
mbedtls_pk_context x509_client_pk;
} core_sysdep_mbedtls_t;
#endif
typedef struct {
void *network_handle;
core_sysdep_socket_type_t socket_type;
aiot_sysdep_network_cred_t *cred;
char *host;
char backup_ip[16];
uint16_t port;
uint32_t connect_timeout_ms;
#ifdef CORE_ADAPTER_MBEDTLS_ENABLED
core_sysdep_psk_t psk;
core_sysdep_mbedtls_t mbedtls;
#endif
} adapter_network_handle_t;
#ifdef CORE_ADAPTER_MBEDTLS_ENABLED
#define MBEDTLS_MEM_INFO_MAGIC (0x12345678)
static uint32_t g_mbedtls_total_mem_used = 0;
static uint32_t g_mbedtls_max_mem_used = 0;
typedef struct {
int32_t magic;
int32_t size;
} mbedtls_mem_info_t;
static uint8_t _host_is_ip(char *host)
{
uint32_t idx = 0;
if (strlen(host) >= 16) {
return 0;
}
for (idx = 0; idx < strlen(host); idx++) {
if ((host[idx] != '.') && (host[idx] < '0' || host[idx] > '9')) {
return 0;
}
}
return 1;
}
static void *_core_mbedtls_calloc(size_t n, size_t size)
{
uint8_t *buf = NULL;
mbedtls_mem_info_t *mem_info = NULL;
if (n == 0 || size == 0) {
return NULL;
}
buf = (uint8_t *)g_origin_portfile->core_sysdep_malloc(n * size + sizeof(mbedtls_mem_info_t), "TLS");
if (NULL == buf) {
core_log1(g_origin_portfile, STATE_ADAPTER_COMMON, "error -- mbedtls malloc: %d failed\r\n", &size);
return NULL;
} else {
memset(buf, 0, n * size + sizeof(mbedtls_mem_info_t));
}
mem_info = (mbedtls_mem_info_t *)buf;
mem_info->magic = MBEDTLS_MEM_INFO_MAGIC;
mem_info->size = n * size;
buf += sizeof(mbedtls_mem_info_t);
g_mbedtls_total_mem_used += mem_info->size;
if (g_mbedtls_total_mem_used > g_mbedtls_max_mem_used) {
g_mbedtls_max_mem_used = g_mbedtls_total_mem_used;
}
/*core_log3(g_origin_portfile, STATE_ADAPTER_COMMON, "INFO -- mbedtls malloc: %d total used: %d max used: %d\r\n",
&size, &g_mbedtls_total_mem_used, &g_mbedtls_max_mem_used);*/
return buf;
}
static void _core_mbedtls_free(void *ptr)
{
mbedtls_mem_info_t *mem_info = NULL;
if (NULL == ptr) {
return;
}
mem_info = (mbedtls_mem_info_t *)((uint8_t *)ptr - sizeof(mbedtls_mem_info_t));
if (mem_info->magic != MBEDTLS_MEM_INFO_MAGIC) {
core_log1(g_origin_portfile, STATE_ADAPTER_COMMON, "Warning - invalid mem info magic: %d\r\n", &mem_info->magic);
return;
}
g_mbedtls_total_mem_used -= mem_info->size;
/*core_log3(g_origin_portfile, STATE_ADAPTER_COMMON, "INFO -- mbedtls free: %d total used: %d max used: %d\r\n",
&mem_info->size, &g_mbedtls_total_mem_used, &g_mbedtls_max_mem_used);*/
g_origin_portfile->core_sysdep_free(mem_info);
}
static int32_t _core_mbedtls_random(void *handle, uint8_t *output, size_t output_len)
{
g_origin_portfile->core_sysdep_rand(output, output_len);
return 0;
}
static void _core_mbedtls_debug(void *ctx, int32_t level, const char *file, int32_t line, const char *str)
{
((void) level);
if (NULL != ctx) {
core_log1(g_origin_portfile, STATE_ADAPTER_COMMON, "%s\r\n", (char *)str);
}
}
static int32_t _core_mbedtls_net_send(void *ctx, const uint8_t *buf, size_t len)
{
int32_t ret = g_origin_portfile->core_sysdep_network_send(ctx, (uint8_t *)buf, len, 5000, NULL);
/*core_log2(g_origin_portfile, STATE_ADAPTER_COMMON, "_core_mbedtls_net_send %d, ret %d\r\n", &len, &ret);*/
return ret;
}
static int32_t _core_mbedtls_net_recv(void *ctx, uint8_t *buf, size_t len)
{
int32_t ret = g_origin_portfile->core_sysdep_network_recv(ctx, buf, len, 5000, NULL);
if (ret < 0) {
return (MBEDTLS_ERR_NET_RECV_FAILED);
} else {
return ret;
}
}
static int32_t _core_mbedtls_net_recv_timeout(void *ctx, uint8_t *buf, size_t len,
uint32_t timeout)
{
int32_t ret = g_origin_portfile->core_sysdep_network_recv(ctx, buf, len, timeout, NULL);
/*core_log2(g_origin_portfile, STATE_ADAPTER_COMMON, "_core_mbedtls_net_recv_timeout %d, ret %d\r\n", &len, &ret);*/
if (ret < 0) {
return (MBEDTLS_ERR_NET_RECV_FAILED);
} else {
return ret;
}
}
unsigned long _core_mbedtls_timing_get_timer(struct mbedtls_timing_hr_time *val, int32_t reset)
{
unsigned long delta;
unsigned long offset;
unsigned long *p_hr_time = (unsigned long *)&val->opaque;
offset = g_origin_portfile->core_sysdep_time();
if (reset) {
*p_hr_time = offset;
return (0);
}
delta = offset - *p_hr_time;
return (delta);
}
void _core_mbedtls_timing_set_delay(void *data, uint32_t int_ms, uint32_t fin_ms)
{
mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data;
ctx->int_ms = int_ms;
ctx->fin_ms = fin_ms;
if (fin_ms != 0) {
(void) _core_mbedtls_timing_get_timer(&ctx->timer, 1);
}
}
/*
* Get number of delays expired
*/
int32_t _core_mbedtls_timing_get_delay(void *data)
{
mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data;
unsigned long elapsed_ms;
if (ctx->fin_ms == 0) {
return (-1);
}
elapsed_ms = _core_mbedtls_timing_get_timer(&ctx->timer, 0);
if (elapsed_ms >= ctx->fin_ms) {
return (2);
}
if (elapsed_ms >= ctx->int_ms) {
return (1);
}
return (0);
}
int32_t _tls_network_establish(void *handle)
{
adapter_network_handle_t *adapter_handle = (adapter_network_handle_t *)handle;
int32_t res = 0;
core_log2(g_origin_portfile, STATE_ADAPTER_COMMON, "establish mbedtls connection with server(host='%s', port=[%d])\r\n",
adapter_handle->host, &adapter_handle->port);
if (adapter_handle->cred->max_tls_fragment == 0) {
core_log(g_origin_portfile, STATE_ADAPTER_COMMON, "invalid max_tls_fragment parameter\r\n");
return STATE_PORT_TLS_INVALID_MAX_FRAGMENT;
}
if (adapter_handle->cred->max_tls_fragment <= 512) {
res = mbedtls_ssl_conf_max_frag_len(&adapter_handle->mbedtls.ssl_config, MBEDTLS_SSL_MAX_FRAG_LEN_512);
} else if (adapter_handle->cred->max_tls_fragment <= 1024) {
res = mbedtls_ssl_conf_max_frag_len(&adapter_handle->mbedtls.ssl_config, MBEDTLS_SSL_MAX_FRAG_LEN_1024);
} else if (adapter_handle->cred->max_tls_fragment <= 2048) {
res = mbedtls_ssl_conf_max_frag_len(&adapter_handle->mbedtls.ssl_config, MBEDTLS_SSL_MAX_FRAG_LEN_2048);
} else if (adapter_handle->cred->max_tls_fragment <= 4096) {
res = mbedtls_ssl_conf_max_frag_len(&adapter_handle->mbedtls.ssl_config, MBEDTLS_SSL_MAX_FRAG_LEN_4096);
} else {
res = mbedtls_ssl_conf_max_frag_len(&adapter_handle->mbedtls.ssl_config, MBEDTLS_SSL_MAX_FRAG_LEN_NONE);
}
if (res < 0) {
core_log1(g_origin_portfile, STATE_ADAPTER_COMMON, "mbedtls_ssl_conf_max_frag_len error, res: %x\r\n", &res);
return res;
}
if (adapter_handle->socket_type == CORE_SYSDEP_SOCKET_TCP_CLIENT) {
res = mbedtls_ssl_config_defaults(&adapter_handle->mbedtls.ssl_config, MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
} else if (adapter_handle->socket_type == CORE_SYSDEP_SOCKET_UDP_CLIENT) {
res = mbedtls_ssl_config_defaults(&adapter_handle->mbedtls.ssl_config, MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_PRESET_DEFAULT);
}
if (res < 0) {
core_log1(g_origin_portfile, STATE_ADAPTER_COMMON, "mbedtls_ssl_config_defaults error, res: %x\r\n", &res);
return res;
}
mbedtls_ssl_conf_max_version(&adapter_handle->mbedtls.ssl_config, MBEDTLS_SSL_MAJOR_VERSION_3,
MBEDTLS_SSL_MINOR_VERSION_3);
mbedtls_ssl_conf_min_version(&adapter_handle->mbedtls.ssl_config, MBEDTLS_SSL_MAJOR_VERSION_3,
MBEDTLS_SSL_MINOR_VERSION_3);
mbedtls_ssl_conf_handshake_timeout(&adapter_handle->mbedtls.ssl_config, (MBEDTLS_SSL_DTLS_TIMEOUT_DFL_MIN * 2),
(MBEDTLS_SSL_DTLS_TIMEOUT_DFL_MIN * 2 * 4));
mbedtls_ssl_conf_rng(&adapter_handle->mbedtls.ssl_config, _core_mbedtls_random, NULL);
mbedtls_ssl_conf_dbg(&adapter_handle->mbedtls.ssl_config, _core_mbedtls_debug, stdout);
if (adapter_handle->cred->option == AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA) {
if (adapter_handle->cred->x509_server_cert == NULL && adapter_handle->cred->x509_server_cert_len == 0) {
core_log(g_origin_portfile, STATE_ADAPTER_COMMON, "invalid x509 server cert\r\n");
return STATE_PORT_TLS_INVALID_SERVER_CERT;
}
mbedtls_x509_crt_init(&adapter_handle->mbedtls.x509_server_cert);
res = mbedtls_x509_crt_parse(&adapter_handle->mbedtls.x509_server_cert,
(const uint8_t *)adapter_handle->cred->x509_server_cert, (size_t)adapter_handle->cred->x509_server_cert_len + 1);
if (res < 0) {
core_log1(g_origin_portfile, STATE_ADAPTER_COMMON, "mbedtls_x509_crt_parse server cert error, res: %x\r\n", &res);
return STATE_PORT_TLS_INVALID_SERVER_CERT;
}
if (adapter_handle->cred->x509_client_cert != NULL && adapter_handle->cred->x509_client_cert_len > 0 &&
adapter_handle->cred->x509_client_privkey != NULL && adapter_handle->cred->x509_client_privkey_len > 0) {
mbedtls_x509_crt_init(&adapter_handle->mbedtls.x509_client_cert);
mbedtls_pk_init(&adapter_handle->mbedtls.x509_client_pk);
res = mbedtls_x509_crt_parse(&adapter_handle->mbedtls.x509_client_cert,
(const uint8_t *)adapter_handle->cred->x509_client_cert, (size_t)adapter_handle->cred->x509_client_cert_len + 1);
if (res < 0) {
core_log1(g_origin_portfile, STATE_ADAPTER_COMMON, "mbedtls_x509_crt_parse client cert error, res: %x\r\n", &res);
return STATE_PORT_TLS_INVALID_CLIENT_CERT;
}
res = mbedtls_pk_parse_key(&adapter_handle->mbedtls.x509_client_pk,
(const uint8_t *)adapter_handle->cred->x509_client_privkey,
(size_t)adapter_handle->cred->x509_client_privkey_len + 1, NULL, 0);
if (res < 0) {
core_log1(g_origin_portfile, STATE_ADAPTER_COMMON, "mbedtls_pk_parse_key client pk error, res: %x\r\n", &res);
return STATE_PORT_TLS_INVALID_CLIENT_KEY;
}
res = mbedtls_ssl_conf_own_cert(&adapter_handle->mbedtls.ssl_config, &adapter_handle->mbedtls.x509_client_cert,
&adapter_handle->mbedtls.x509_client_pk);
if (res < 0) {
core_log1(g_origin_portfile, STATE_ADAPTER_COMMON, "mbedtls_ssl_conf_own_cert error, res: %x\r\n", &res);
return STATE_PORT_TLS_INVALID_CLIENT_CERT;
}
}
mbedtls_ssl_conf_ca_chain(&adapter_handle->mbedtls.ssl_config, &adapter_handle->mbedtls.x509_server_cert, NULL);
} else if (adapter_handle->cred->option == AIOT_SYSDEP_NETWORK_CRED_SVRCERT_PSK) {
static const int32_t ciphersuites[1] = {MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA};
res = mbedtls_ssl_conf_psk(&adapter_handle->mbedtls.ssl_config,
(const uint8_t *)adapter_handle->psk.psk, (size_t)strlen(adapter_handle->psk.psk),
(const uint8_t *)adapter_handle->psk.psk_id, (size_t)strlen(adapter_handle->psk.psk_id));
if (res < 0) {
core_log1(g_origin_portfile, STATE_ADAPTER_COMMON, "mbedtls_ssl_conf_psk error, res: %x\r\n", &res);
return STATE_PORT_TLS_CONFIG_PSK_FAILED;
}
mbedtls_ssl_conf_ciphersuites(&adapter_handle->mbedtls.ssl_config, ciphersuites);
} else {
core_log(g_origin_portfile, STATE_ADAPTER_COMMON, "unsupported security option\r\n");
return STATE_PORT_TLS_INVALID_CRED_OPTION;
}
res = mbedtls_ssl_setup(&adapter_handle->mbedtls.ssl_ctx, &adapter_handle->mbedtls.ssl_config);
if (res < 0) {
core_log1(g_origin_portfile, STATE_ADAPTER_COMMON, "mbedtls_ssl_setup error, res: %x\r\n", &res);
return res;
}
if (_host_is_ip(adapter_handle->host) == 0) {
res = mbedtls_ssl_set_hostname(&adapter_handle->mbedtls.ssl_ctx, adapter_handle->host);
if (res < 0) {
core_log1(g_origin_portfile, STATE_ADAPTER_COMMON, "mbedtls_ssl_set_hostname error, res: %x\r\n", &res);
return res;
}
}
if (adapter_handle->socket_type == CORE_SYSDEP_SOCKET_UDP_CLIENT) {
mbedtls_ssl_set_timer_cb(&adapter_handle->mbedtls.ssl_ctx, (void *)&adapter_handle->mbedtls.timer_delay_ctx,
_core_mbedtls_timing_set_delay, _core_mbedtls_timing_get_delay);
}
mbedtls_ssl_set_bio(&adapter_handle->mbedtls.ssl_ctx, adapter_handle->network_handle, _core_mbedtls_net_send,
_core_mbedtls_net_recv, _core_mbedtls_net_recv_timeout);
mbedtls_ssl_conf_read_timeout(&adapter_handle->mbedtls.ssl_config, adapter_handle->connect_timeout_ms);
while ((res = mbedtls_ssl_handshake(&adapter_handle->mbedtls.ssl_ctx)) != 0) {
if ((res != MBEDTLS_ERR_SSL_WANT_READ) && (res != MBEDTLS_ERR_SSL_WANT_WRITE)) {
core_log1(g_origin_portfile, STATE_ADAPTER_COMMON, "mbedtls_ssl_handshake error, res: %x\r\n", &res);
if (res == MBEDTLS_ERR_SSL_INVALID_RECORD) {
res = STATE_PORT_TLS_INVALID_RECORD;
} else {
res = STATE_PORT_TLS_INVALID_HANDSHAKE;
}
return res;
}
}
res = mbedtls_ssl_get_verify_result(&adapter_handle->mbedtls.ssl_ctx);
if (res < 0) {
core_log1(g_origin_portfile, STATE_ADAPTER_COMMON, "mbedtls_ssl_get_verify_result error, res: %x\r\n", &res);
return res;
}
core_log2(g_origin_portfile, STATE_ADAPTER_COMMON,
"success to establish mbedtls connection, (cost %d bytes in total, max used %d bytes)\r\n",
&g_mbedtls_total_mem_used, &g_mbedtls_max_mem_used);
return 0;
}
int32_t _tls_network_recv(void *handle, uint8_t *buffer, uint32_t len, uint32_t timeout_ms,
core_sysdep_addr_t *addr)
{
int32_t res = 0;
int32_t recv_bytes = 0;
adapter_network_handle_t *adapter_handle = (adapter_network_handle_t *)handle;
if (handle == NULL) {
return STATE_PORT_INPUT_NULL_POINTER;
}
/*core_log2(g_origin_portfile, STATE_ADAPTER_COMMON, "_tls_network_recv (cost %d bytes in total, max used %d bytes)\r\n",
&len, &timeout_ms);*/
_core_mbedtls_timing_set_delay(&adapter_handle->mbedtls.timer_delay_ctx, 0, 0);
mbedtls_ssl_conf_read_timeout(&adapter_handle->mbedtls.ssl_config, timeout_ms);
do {
res = mbedtls_ssl_read(&adapter_handle->mbedtls.ssl_ctx, buffer + recv_bytes, len - recv_bytes);
if (res < 0) {
if (res == MBEDTLS_ERR_SSL_TIMEOUT) {
break;
} else if (res != MBEDTLS_ERR_SSL_WANT_READ &&
res != MBEDTLS_ERR_SSL_WANT_WRITE &&
res != MBEDTLS_ERR_SSL_CLIENT_RECONNECT) {
if (recv_bytes == 0) {
core_log1(g_origin_portfile, STATE_ADAPTER_COMMON, "mbedtls_ssl_recv error, res: %x\r\n", &res);
if (res == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
return STATE_PORT_TLS_RECV_CONNECTION_CLOSED;
} else if (res == MBEDTLS_ERR_SSL_INVALID_RECORD) {
return STATE_PORT_TLS_INVALID_RECORD;
} else {
return STATE_PORT_TLS_RECV_FAILED;
}
}
break;
}
} else if (res == 0) {
break;
} else {
recv_bytes += res;
}
} while (recv_bytes < len);
return recv_bytes;
}
int32_t _tls_network_send(void *handle, uint8_t *buffer, uint32_t len, uint32_t timeout_ms,
core_sysdep_addr_t *addr)
{
int32_t res = 0;
int32_t send_bytes = 0;
uint64_t timestart_ms = 0, timenow_ms = 0;
adapter_network_handle_t *adapter_handle = (adapter_network_handle_t *)handle;
if (handle == NULL) {
return STATE_PORT_INPUT_NULL_POINTER;
}
/* Start Time */
timestart_ms = g_origin_portfile->core_sysdep_time();
timenow_ms = timestart_ms;
do {
timenow_ms = g_origin_portfile->core_sysdep_time();
if (timenow_ms - timestart_ms >= timenow_ms ||
timeout_ms - (timenow_ms - timestart_ms) > timeout_ms) {
break;
}
res = mbedtls_ssl_write(&adapter_handle->mbedtls.ssl_ctx, buffer + send_bytes, len - send_bytes);
if (res < 0) {
if (res != MBEDTLS_ERR_SSL_WANT_READ &&
res != MBEDTLS_ERR_SSL_WANT_WRITE) {
if (send_bytes == 0) {
core_log1(g_origin_portfile, STATE_ADAPTER_COMMON, "mbedtls_ssl_send error, res: %x\r\n", &res);
if (res == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
return STATE_PORT_TLS_SEND_CONNECTION_CLOSED;
} else if (res == MBEDTLS_ERR_SSL_INVALID_RECORD) {
return STATE_PORT_TLS_INVALID_RECORD;
} else {
return STATE_PORT_TLS_SEND_FAILED;
}
}
break;
} else {
g_origin_portfile->core_sysdep_sleep(100);
}
} else if (res == 0) {
break;
} else {
send_bytes += res;
}
} while (((timenow_ms - timestart_ms) < timeout_ms) && (send_bytes < len));
return send_bytes;
}
#endif
void *adapter_network_init(void)
{
adapter_network_handle_t *adapter_handle = NULL;
adapter_handle = g_origin_portfile->core_sysdep_malloc(sizeof(adapter_network_handle_t), "TLS");
if (adapter_handle == NULL) {
return NULL;
}
memset(adapter_handle, 0, sizeof(adapter_network_handle_t));
adapter_handle->network_handle = g_origin_portfile->core_sysdep_network_init();
#ifdef CORE_ADAPTER_MBEDTLS_ENABLED
adapter_handle->psk.psk_id = NULL;
adapter_handle->psk.psk = NULL;
mbedtls_debug_set_threshold(0);
mbedtls_ssl_init(&adapter_handle->mbedtls.ssl_ctx);
mbedtls_ssl_config_init(&adapter_handle->mbedtls.ssl_config);
//mbedtls_platform_set_calloc_free(_core_mbedtls_calloc, _core_mbedtls_free);
g_mbedtls_total_mem_used = g_mbedtls_max_mem_used = 0;
#endif
return adapter_handle;
}
int32_t adapter_network_setopt(void *handle, core_sysdep_network_option_t option, void *data)
{
adapter_network_handle_t *adapter_handle = (adapter_network_handle_t *)handle;
int32_t res = STATE_SUCCESS;
if (handle == NULL || data == NULL) {
return STATE_PORT_INPUT_NULL_POINTER;
}
if (option >= CORE_SYSDEP_NETWORK_MAX) {
return STATE_PORT_INPUT_OUT_RANGE;
}
res = g_origin_portfile->core_sysdep_network_setopt(adapter_handle->network_handle, option, data);
#ifdef CORE_ADAPTER_MBEDTLS_ENABLED
switch (option) {
case CORE_SYSDEP_NETWORK_SOCKET_TYPE: {
adapter_handle->socket_type = *(core_sysdep_socket_type_t *)data;
}
break;
case CORE_SYSDEP_NETWORK_HOST: {
adapter_handle->host = g_origin_portfile->core_sysdep_malloc(strlen(data) + 1, "TLS");
if (adapter_handle->host == NULL) {
core_log(g_origin_portfile, STATE_ADAPTER_COMMON, "malloc failed\r\n");
return STATE_PORT_MALLOC_FAILED;
}
memset(adapter_handle->host, 0, strlen(data) + 1);
memcpy(adapter_handle->host, data, strlen(data));
}
break;
case CORE_SYSDEP_NETWORK_BACKUP_IP: {
memcpy(adapter_handle->backup_ip, data, strlen(data));
}
break;
case CORE_SYSDEP_NETWORK_PORT: {
adapter_handle->port = *(uint16_t *)data;
}
break;
case CORE_SYSDEP_NETWORK_CONNECT_TIMEOUT_MS: {
adapter_handle->connect_timeout_ms = *(uint32_t *)data;
}
break;
case CORE_SYSDEP_NETWORK_CRED: {
adapter_handle->cred = g_origin_portfile->core_sysdep_malloc(sizeof(aiot_sysdep_network_cred_t), "TLS");
if (adapter_handle->cred == NULL) {
core_log(g_origin_portfile, STATE_ADAPTER_COMMON, "g_origin_portfile->core_sysdep_malloc failed\r\n");
return STATE_PORT_MALLOC_FAILED;
}
memset(adapter_handle->cred, 0, sizeof(aiot_sysdep_network_cred_t));
memcpy(adapter_handle->cred, data, sizeof(aiot_sysdep_network_cred_t));
}
break;
case CORE_SYSDEP_NETWORK_PSK: {
core_sysdep_psk_t *psk = (core_sysdep_psk_t *)data;
adapter_handle->psk.psk_id = g_origin_portfile->core_sysdep_malloc(strlen(psk->psk_id) + 1, "TLS");
if (adapter_handle->psk.psk_id == NULL) {
core_log(g_origin_portfile, STATE_ADAPTER_COMMON, "g_origin_portfile->core_sysdep_malloc failed\r\n");
return STATE_PORT_MALLOC_FAILED;
}
memset(adapter_handle->psk.psk_id, 0, strlen(psk->psk_id) + 1);
memcpy(adapter_handle->psk.psk_id, psk->psk_id, strlen(psk->psk_id));
adapter_handle->psk.psk = g_origin_portfile->core_sysdep_malloc(strlen(psk->psk) + 1, "TLS");
if (adapter_handle->psk.psk == NULL) {
g_origin_portfile->core_sysdep_free(adapter_handle->psk.psk_id);
core_log(g_origin_portfile, STATE_ADAPTER_COMMON, "g_origin_portfile->core_sysdep_malloc failed\r\n");
return STATE_PORT_MALLOC_FAILED;
}
memset(adapter_handle->psk.psk, 0, strlen(psk->psk) + 1);
memcpy(adapter_handle->psk.psk, psk->psk, strlen(psk->psk));
}
break;
default: {
core_log1(g_origin_portfile, STATE_ADAPTER_COMMON, "adapter_network_setopt unkown option %d\r\n", &option);
}
break;
}
#endif
return res;
}
int32_t adapter_network_establish(void *handle)
{
adapter_network_handle_t *adapter_handle = (adapter_network_handle_t *)handle;
if (handle == NULL) {
return STATE_PORT_INPUT_NULL_POINTER;
}
int32_t res = g_origin_portfile->core_sysdep_network_establish(adapter_handle->network_handle);
if (res < STATE_SUCCESS) {
return res;
}
#ifdef CORE_ADAPTER_MBEDTLS_ENABLED
if (adapter_handle->cred != NULL && adapter_handle->cred->option != AIOT_SYSDEP_NETWORK_CRED_NONE) {
res = _tls_network_establish(adapter_handle);
}
#endif
return res;
}
int32_t adapter_network_recv(void *handle, uint8_t *buffer, uint32_t len, uint32_t timeout_ms,
core_sysdep_addr_t *addr)
{
int32_t res = STATE_SUCCESS;
adapter_network_handle_t *adapter_handle = (adapter_network_handle_t *)handle;
if (handle == NULL) {
return STATE_PORT_INPUT_NULL_POINTER;
}
#ifdef CORE_ADAPTER_MBEDTLS_ENABLED
if (adapter_handle->cred != NULL && adapter_handle->cred->option != AIOT_SYSDEP_NETWORK_CRED_NONE) {
res = _tls_network_recv(handle, buffer, len, timeout_ms, addr);
} else
#endif
{
res = g_origin_portfile->core_sysdep_network_recv(adapter_handle->network_handle, buffer, len, timeout_ms, addr);
}
return res;
}
int32_t adapter_network_send(void *handle, uint8_t *buffer, uint32_t len, uint32_t timeout_ms,
core_sysdep_addr_t *addr)
{
int32_t res = STATE_SUCCESS;
adapter_network_handle_t *adapter_handle = (adapter_network_handle_t *)handle;
if (handle == NULL) {
return STATE_PORT_INPUT_NULL_POINTER;
}
#ifdef CORE_ADAPTER_MBEDTLS_ENABLED
if (adapter_handle->cred != NULL && adapter_handle->cred->option != AIOT_SYSDEP_NETWORK_CRED_NONE) {
res = _tls_network_send(handle, buffer, len, timeout_ms, addr);
} else
#endif
{
res = g_origin_portfile->core_sysdep_network_send(adapter_handle->network_handle, buffer, len, timeout_ms, addr);
}
return res;
}
int32_t adapter_network_deinit(void **handle)
{
adapter_network_handle_t *adapter_handle = NULL;
if (handle == NULL || *handle == NULL) {
return STATE_PORT_INPUT_NULL_POINTER;
}
adapter_handle = *(adapter_network_handle_t **)handle;
if (adapter_handle->host != NULL) {
g_origin_portfile->core_sysdep_free(adapter_handle->host);
adapter_handle->host = NULL;
}
#ifdef CORE_ADAPTER_MBEDTLS_ENABLED
mbedtls_ssl_close_notify(&adapter_handle->mbedtls.ssl_ctx);
if (adapter_handle->cred != NULL && adapter_handle->cred->option == AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA) {
mbedtls_x509_crt_free(&adapter_handle->mbedtls.x509_server_cert);
mbedtls_x509_crt_free(&adapter_handle->mbedtls.x509_client_cert);
mbedtls_pk_free(&adapter_handle->mbedtls.x509_client_pk);
}
mbedtls_ssl_free(&adapter_handle->mbedtls.ssl_ctx);
mbedtls_ssl_config_free(&adapter_handle->mbedtls.ssl_config);
g_mbedtls_total_mem_used = g_mbedtls_max_mem_used = 0;
if (adapter_handle->psk.psk_id != NULL) {
g_origin_portfile->core_sysdep_free(adapter_handle->psk.psk_id);
adapter_handle->psk.psk_id = NULL;
}
if (adapter_handle->psk.psk != NULL) {
g_origin_portfile->core_sysdep_free(adapter_handle->psk.psk);
adapter_handle->psk.psk = NULL;
}
#endif
if (adapter_handle->cred != NULL) {
g_origin_portfile->core_sysdep_free(adapter_handle->cred);
adapter_handle->cred = NULL;
}
core_log(g_origin_portfile, STATE_ADAPTER_COMMON, "adapter_network_deinit\r\n");
g_origin_portfile->core_sysdep_network_deinit(&adapter_handle->network_handle);
g_origin_portfile->core_sysdep_free(adapter_handle);
*handle = NULL;
return STATE_SUCCESS;
}
static aiot_network_t adapter_network = {
adapter_network_init,
adapter_network_setopt,
adapter_network_establish,
adapter_network_recv,
adapter_network_send,
adapter_network_deinit,
};
aiot_sysdep_portfile_t *aiot_sysdep_get_adapter_portfile(aiot_sysdep_portfile_t *portfile)
{
if (portfile == NULL) {
return NULL;
}
g_origin_portfile = portfile;
g_aiot_portfile = *portfile;
g_aiot_portfile.core_sysdep_network_init = adapter_network.core_sysdep_network_init;
g_aiot_portfile.core_sysdep_network_setopt = adapter_network.core_sysdep_network_setopt;
g_aiot_portfile.core_sysdep_network_establish = adapter_network.core_sysdep_network_establish;
g_aiot_portfile.core_sysdep_network_recv = adapter_network.core_sysdep_network_recv;
g_aiot_portfile.core_sysdep_network_send = adapter_network.core_sysdep_network_send;
g_aiot_portfile.core_sysdep_network_deinit = adapter_network.core_sysdep_network_deinit;
return &g_aiot_portfile;
}

View File

@@ -0,0 +1,15 @@
#ifndef _CORE_ADAPTER_H_
#define _CORE_ADAPTER_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include "core_stdinc.h"
#include "aiot_sysdep_api.h"
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,16 @@
#ifndef _CORE_STDINC_H_
#define _CORE_STDINC_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include <stdint.h>
#include <string.h>
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,33 @@
#include "core_sysdep.h"
#include "core_adapter.h"
static aiot_sysdep_portfile_t *g_sysdep_portfile = NULL;
void aiot_sysdep_set_portfile(aiot_sysdep_portfile_t *portfile)
{
g_sysdep_portfile = portfile;
}
aiot_sysdep_portfile_t * aiot_sysdep_get_portfile(void)
{
if (g_sysdep_portfile == NULL ||
g_sysdep_portfile->core_sysdep_malloc == NULL ||
g_sysdep_portfile->core_sysdep_free == NULL ||
g_sysdep_portfile->core_sysdep_time == NULL ||
g_sysdep_portfile->core_sysdep_sleep == NULL ||
g_sysdep_portfile->core_sysdep_network_init == NULL ||
g_sysdep_portfile->core_sysdep_network_setopt == NULL ||
g_sysdep_portfile->core_sysdep_network_establish == NULL ||
g_sysdep_portfile->core_sysdep_network_recv == NULL ||
g_sysdep_portfile->core_sysdep_network_send == NULL ||
g_sysdep_portfile->core_sysdep_network_deinit == NULL ||
g_sysdep_portfile->core_sysdep_rand == NULL ||
g_sysdep_portfile->core_sysdep_mutex_init == NULL ||
g_sysdep_portfile->core_sysdep_mutex_lock == NULL ||
g_sysdep_portfile->core_sysdep_mutex_unlock == NULL ||
g_sysdep_portfile->core_sysdep_mutex_deinit == NULL) {
return NULL;
}
return g_sysdep_portfile;
}

View File

@@ -0,0 +1,16 @@
#ifndef _CORE_SYSDEP_H_
#define _CORE_SYSDEP_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include "core_stdinc.h"
#include "aiot_sysdep_api.h"
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,114 @@
#include "core_auth.h"
int32_t core_auth_tls_psk(aiot_sysdep_portfile_t *sysdep, char **psk_id, char psk[65], char *product_key,
char *device_name, char *device_secret, char *module_name)
{
int32_t res = STATE_SUCCESS;
char *tmp_psk_id = NULL, *auth_type = "devicename", *sign_method = "hmacsha256";
char *psk_id_src[] = { auth_type, sign_method, product_key, device_name, CORE_AUTH_TIMESTAMP};
char *psk_plain_text = NULL, *psk_plain_text_src[] = { product_key, device_name, CORE_AUTH_TIMESTAMP};
uint8_t psk_hex[32] = {0};
if (NULL == device_secret) {
return STATE_USER_INPUT_MISSING_DEVICE_SECRET;
}
res = core_sprintf(sysdep, &tmp_psk_id, "%s|%s|%s&%s|%s", psk_id_src, sizeof(psk_id_src) / sizeof(char *), module_name);
if (res < STATE_SUCCESS) {
return res;
}
res = core_sprintf(sysdep, &psk_plain_text, "id%s&%stimestamp%s", psk_plain_text_src,
sizeof(psk_plain_text_src) / sizeof(char *), module_name);
if (res < STATE_SUCCESS) {
sysdep->core_sysdep_free(tmp_psk_id);
return res;
}
core_hmac_sha256((const uint8_t *)psk_plain_text, (uint32_t)strlen(psk_plain_text), (const uint8_t *)device_secret,
(uint32_t)strlen(device_secret), psk_hex);
core_hex2str(psk_hex, 32, psk, 0);
*psk_id = tmp_psk_id;
sysdep->core_sysdep_free(psk_plain_text);
return res;
}
int32_t core_auth_mqtt_username(aiot_sysdep_portfile_t *sysdep, char **dest, char *product_key, char *device_name,
char *module_name)
{
char *src[] = { device_name, product_key };
return core_sprintf(sysdep, dest, "%s&%s", src, sizeof(src) / sizeof(char *), module_name);
}
int32_t core_auth_mqtt_password(aiot_sysdep_portfile_t *sysdep, char **dest, char *product_key, char *device_name,
char *device_secret, char *module_name)
{
int32_t res = 0;
char *plain_text = NULL;
uint8_t sign[32] = {0};
char *src[] = { product_key, device_name, device_name, product_key, CORE_AUTH_TIMESTAMP };
res = core_sprintf(sysdep, &plain_text, "clientId%s.%sdeviceName%sproductKey%stimestamp%s", src,
sizeof(src) / sizeof(char *), module_name);
if (res < STATE_SUCCESS) {
return res;
}
*dest = sysdep->core_sysdep_malloc(65, module_name);
if (*dest == NULL) {
sysdep->core_sysdep_free(plain_text);
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
memset(*dest, 0, 65);
core_hmac_sha256((const uint8_t *)plain_text, (uint32_t)strlen(plain_text), (const uint8_t *)device_secret,
(uint32_t)strlen(device_secret), sign);
core_hex2str(sign, 32, *dest, 0);
sysdep->core_sysdep_free(plain_text);
return 0;
}
int32_t core_auth_mqtt_clientid(aiot_sysdep_portfile_t *sysdep, char **dest, char *product_key, char *device_name,
char *secure_mode, char *extend_clientid, char *module_name)
{
char *src[] = { product_key, device_name, CORE_AUTH_TIMESTAMP, CORE_AUTH_SDK_VERSION, secure_mode, extend_clientid};
return core_sprintf(sysdep, dest, "%s.%s|timestamp=%s,_ss=1,_v=%s,securemode=%s,signmethod=hmacsha256,ext=3,%s|", src,
sizeof(src) / sizeof(char *), module_name); /* ext bitmap: bit0-rrpc, bit1-ext_notify */
}
int32_t core_auth_http_body(aiot_sysdep_portfile_t *sysdep, char **dest, char *product_key, char *device_name,
char *device_secret, char *module_name)
{
int32_t res = 0;
char *sign_ele[] = { product_key, device_name, device_name, product_key, NULL };
char *plain_text = NULL;
uint8_t sign_hex[32] = {0};
char sign_str[65] = {0};
res = core_sprintf(sysdep, &plain_text, "clientId%s.%sdeviceName%sproductKey%s", sign_ele, 4, module_name);
if (res < STATE_SUCCESS) {
return res;
}
core_hmac_sha256((const uint8_t *)plain_text, (uint32_t)strlen(plain_text), (const uint8_t *)device_secret,
(uint32_t)strlen(device_secret), sign_hex);
core_hex2str(sign_hex, 32, sign_str, 0);
sysdep->core_sysdep_free(plain_text);
sign_ele[4] = sign_str;
res = core_sprintf(sysdep,
dest,
"{\"clientId\":\"%s.%s\",\"signmethod\":\"hmacsha256\",\"deviceName\":\"%s\",\"productKey\":\"%s\",\"sign\":\"%s\"}",
sign_ele,
sizeof(sign_ele) / sizeof(char *),
module_name);
return res;
}

View File

@@ -0,0 +1,33 @@
#ifndef _CORE_AUTH_H_
#define _CORE_AUTH_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include "core_stdinc.h"
#include "core_string.h"
#include "core_sha256.h"
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#define CORE_AUTH_SDK_VERSION "sdk-c-4.1.0"
#define CORE_AUTH_TIMESTAMP "2524608000000"
int32_t core_auth_tls_psk(aiot_sysdep_portfile_t *sysdep, char **psk_id, char psk[65], char *product_key,
char *device_name, char *device_secret, char *module_name);
int32_t core_auth_mqtt_username(aiot_sysdep_portfile_t *sysdep, char **dest, char *product_key, char *device_name,
char *module_name);
int32_t core_auth_mqtt_password(aiot_sysdep_portfile_t *sysdep, char **dest, char *product_key, char *device_name,
char *device_secret, char *module_name);
int32_t core_auth_mqtt_clientid(aiot_sysdep_portfile_t *sysdep, char **dest, char *product_key, char *device_name,
char *secure_mode, char *extend_clientid, char *module_name);
int32_t core_auth_http_body(aiot_sysdep_portfile_t *sysdep, char **dest, char *product_key, char *device_name,
char *device_secret, char *module_name);
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,27 @@
#include "core_diag.h"
static void _core_diag_default_callback(void *diag_handle, uint64_t timestamp, int32_t code, uint8_t *data, uint32_t data_len);
static void *g_diag_handle = NULL;
static core_diag_callback g_diag_cb = _core_diag_default_callback;
static void _core_diag_default_callback(void *diag_handle, uint64_t timestamp, int32_t code, uint8_t *data, uint32_t data_len)
{
}
void core_diag_set_cb(void *diag_handle, core_diag_callback cb)
{
if (cb == NULL) {
g_diag_cb = _core_diag_default_callback;
g_diag_handle = NULL;
}else{
g_diag_handle = diag_handle;
g_diag_cb = cb;
}
}
void core_diag(aiot_sysdep_portfile_t *sysdep, int32_t code, uint8_t *data, uint32_t data_len)
{
g_diag_cb(g_diag_handle, core_log_get_timestamp(sysdep), code, data, data_len);
}

View File

@@ -0,0 +1,20 @@
#ifndef _CORE_DIAG_H_
#define _CORE_DIAG_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include "core_stdinc.h"
#include "core_log.h"
typedef void (*core_diag_callback)(void * diag_handle, uint64_t timestamp, int32_t code, uint8_t *data, uint32_t data_len);
void core_diag_set_cb(void *diag_handle, core_diag_callback cb);
void core_diag(aiot_sysdep_portfile_t *sysdep, int32_t code, uint8_t *data, uint32_t data_len);
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,83 @@
#include "core_global.h"
typedef struct {
void *mutex;
uint8_t is_inited;
uint32_t used_count;
int32_t alink_id;
char mqtt_backup_ip[16];
} g_core_global_t;
g_core_global_t g_core_global = {NULL, 0, 0, 0, {0}};
int32_t core_global_init(aiot_sysdep_portfile_t *sysdep)
{
if (g_core_global.is_inited == 1) {
g_core_global.used_count++;
return STATE_SUCCESS;
}
g_core_global.is_inited = 1;
g_core_global.mutex = sysdep->core_sysdep_mutex_init();
g_core_global.used_count++;
return STATE_SUCCESS;
}
int32_t core_global_alink_id_next(aiot_sysdep_portfile_t *sysdep, int32_t *alink_id)
{
int32_t id = 0;
sysdep->core_sysdep_mutex_lock(g_core_global.mutex);
g_core_global.alink_id++;
if (g_core_global.alink_id < 0) {
g_core_global.alink_id = 0;
}
id = g_core_global.alink_id;
sysdep->core_sysdep_mutex_unlock(g_core_global.mutex);
*alink_id = id;
return STATE_SUCCESS;
}
int32_t core_global_set_mqtt_backup_ip(aiot_sysdep_portfile_t *sysdep, char ip[16])
{
uint8_t buffer_size = 16;
sysdep->core_sysdep_mutex_lock(g_core_global.mutex);
memset(g_core_global.mqtt_backup_ip, 0, buffer_size);
if(strlen(ip) > buffer_size) {
return STATE_USER_INPUT_OUT_RANGE;
}
memcpy(g_core_global.mqtt_backup_ip, ip, strlen(ip));
sysdep->core_sysdep_mutex_unlock(g_core_global.mutex);
return STATE_SUCCESS;
}
int32_t core_global_get_mqtt_backup_ip(aiot_sysdep_portfile_t *sysdep, char ip[16])
{
sysdep->core_sysdep_mutex_lock(g_core_global.mutex);
memcpy(ip, g_core_global.mqtt_backup_ip, strlen(g_core_global.mqtt_backup_ip));
sysdep->core_sysdep_mutex_unlock(g_core_global.mutex);
return STATE_SUCCESS;
}
int32_t core_global_deinit(aiot_sysdep_portfile_t *sysdep)
{
if (g_core_global.used_count > 0) {
g_core_global.used_count--;
}
if (g_core_global.used_count != 0) {
return STATE_SUCCESS;
}
sysdep->core_sysdep_mutex_deinit(&g_core_global.mutex);
g_core_global.mutex = NULL;
g_core_global.is_inited = 0;
g_core_global.used_count = 0;
return STATE_SUCCESS;
}

View File

@@ -0,0 +1,25 @@
#ifndef _CORE_GLOBAL_H_
#define _CORE_GLOBAL_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include "core_stdinc.h"
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#define CORE_GLOBAL_MODULE_NAME "global"
int32_t core_global_init(aiot_sysdep_portfile_t *sysdep);
int32_t core_global_alink_id_next(aiot_sysdep_portfile_t *sysdep, int32_t *alink_id);
int32_t core_global_set_mqtt_backup_ip(aiot_sysdep_portfile_t *sysdep, char ip[16]);
int32_t core_global_get_mqtt_backup_ip(aiot_sysdep_portfile_t *sysdep, char ip[16]);
int32_t core_global_deinit(aiot_sysdep_portfile_t *sysdep);
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,659 @@
/**
* @file aiot_http_api.c
* @brief HTTP模块实现, 其中包含了向物联网平台认证和上报数据的API接口
* @date 2019-12-27
*
* @copyright Copyright (C) 2015-2018 Alibaba Group Holding Limited
*
*/
#include "core_http.h"
static void _core_http_exec_inc(core_http_handle_t *http_handle)
{
http_handle->sysdep->core_sysdep_mutex_lock(http_handle->data_mutex);
http_handle->core_exec_count++;
http_handle->sysdep->core_sysdep_mutex_unlock(http_handle->data_mutex);
}
static void _core_http_exec_dec(core_http_handle_t *http_handle)
{
http_handle->sysdep->core_sysdep_mutex_lock(http_handle->data_mutex);
http_handle->core_exec_count--;
http_handle->sysdep->core_sysdep_mutex_unlock(http_handle->data_mutex);
}
static int32_t _core_http_sysdep_return(int32_t sysdep_code, int32_t core_code)
{
if (sysdep_code >= (STATE_PORT_BASE - 0x00FF) && sysdep_code < (STATE_PORT_BASE)) {
return sysdep_code;
} else {
return core_code;
}
}
static int32_t _core_http_connect(core_http_handle_t *http_handle)
{
int32_t res = STATE_SUCCESS;
/* disconnect first if network is already established */
if (http_handle->network_handle != NULL) {
http_handle->sysdep->core_sysdep_network_deinit(&http_handle->network_handle);
}
/* establish network connection */
core_sysdep_socket_type_t socket_type = CORE_SYSDEP_SOCKET_TCP_CLIENT;
if (http_handle->host == NULL) {
return STATE_USER_INPUT_MISSING_HOST;
}
http_handle->network_handle = http_handle->sysdep->core_sysdep_network_init();
if (http_handle->network_handle == NULL) {
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
if ((res = http_handle->sysdep->core_sysdep_network_setopt(http_handle->network_handle, CORE_SYSDEP_NETWORK_SOCKET_TYPE,
&socket_type)) < 0 ||
(res = http_handle->sysdep->core_sysdep_network_setopt(http_handle->network_handle, CORE_SYSDEP_NETWORK_HOST,
http_handle->host)) < 0 ||
(res = http_handle->sysdep->core_sysdep_network_setopt(http_handle->network_handle, CORE_SYSDEP_NETWORK_PORT,
&http_handle->port)) < 0 ||
(res = http_handle->sysdep->core_sysdep_network_setopt(http_handle->network_handle,
CORE_SYSDEP_NETWORK_CONNECT_TIMEOUT_MS,
&http_handle->connect_timeout_ms)) < 0) {
http_handle->sysdep->core_sysdep_network_deinit(&http_handle->network_handle);
return _core_http_sysdep_return(res, STATE_SYS_DEPEND_NWK_INVALID_OPTION);
}
if (http_handle->cred != NULL) {
res = http_handle->sysdep->core_sysdep_network_setopt(http_handle->network_handle, CORE_SYSDEP_NETWORK_CRED,
http_handle->cred);
if (res < STATE_SUCCESS) {
http_handle->sysdep->core_sysdep_network_deinit(&http_handle->network_handle);
return _core_http_sysdep_return(res, STATE_SYS_DEPEND_NWK_INVALID_CRED);
}
}
res = http_handle->sysdep->core_sysdep_network_establish(http_handle->network_handle);
if (res < STATE_SUCCESS) {
http_handle->sysdep->core_sysdep_network_deinit(&http_handle->network_handle);
return _core_http_sysdep_return(res, STATE_SYS_DEPEND_NWK_EST_FAILED);
}
return STATE_SUCCESS;
}
static int32_t _core_http_send(core_http_handle_t *http_handle, uint8_t *buffer, uint32_t len, uint32_t timeout_ms)
{
int32_t res = STATE_SUCCESS;
if (http_handle->network_handle != NULL) {
res = http_handle->sysdep->core_sysdep_network_send(http_handle->network_handle, buffer, len, timeout_ms, NULL);
if (res < STATE_SUCCESS) {
http_handle->sysdep->core_sysdep_network_deinit(&http_handle->network_handle);
core_log(http_handle->sysdep, STATE_HTTP_LOG_DISCONNECT, "HTTP network error when sending data, disconnect\r\n");
res = _core_http_sysdep_return(res, STATE_SYS_DEPEND_NWK_SEND_ERR);
} else if (res != len) {
res = STATE_SYS_DEPEND_NWK_WRITE_LESSDATA;
}
} else {
res = STATE_SYS_DEPEND_NWK_CLOSED;
}
return res;
}
static int32_t _core_http_send_body(core_http_handle_t *http_handle, uint8_t *content, uint32_t len)
{
int32_t res = STATE_SUCCESS;
core_log_hexdump(STATE_HTTP_LOG_SEND_CONTENT, '>', content, len);
http_handle->sysdep->core_sysdep_mutex_lock(http_handle->send_mutex);
res = _core_http_send(http_handle, content, len, http_handle->send_timeout_ms);
http_handle->sysdep->core_sysdep_mutex_unlock(http_handle->send_mutex);
return res;
}
static void _core_http_header_print(core_http_handle_t *http_handle, char *prefix, char *header, uint32_t header_len)
{
char *prev = header, *pos = header;
uint32_t line_len = 0;
while (pos != header + header_len) {
if (*(pos) == '\n' && *(pos - 1) == '\r') {
line_len = (uint32_t)(pos - prev + 1);
core_log3(http_handle->sysdep, STATE_HTTP_LOG_SEND_HEADER, "%s %.*s", prefix, &line_len, prev);
prev = pos + 1;
}
pos++;
}
}
static int32_t _core_http_send_header(core_http_handle_t *http_handle, char *method, char *path, char *host,
char *header, char *content_lenstr)
{
int32_t res = STATE_SUCCESS;
char *combine_header = NULL;
char *combine_header_src[] = { method, path, host, header, content_lenstr};
uint32_t combine_header_len = 0;
res = core_sprintf(http_handle->sysdep, &combine_header, "%s %s HTTP/1.1\r\nHost: %s\r\n%sContent-Length: %s\r\n\r\n",
combine_header_src, sizeof(combine_header_src) / sizeof(char *), CORE_HTTP_MODULE_NAME);
if (res < STATE_SUCCESS) {
return res;
}
combine_header_len = (uint32_t)strlen(combine_header);
_core_http_header_print(http_handle, ">", combine_header, combine_header_len);
http_handle->sysdep->core_sysdep_mutex_lock(http_handle->send_mutex);
res = _core_http_send(http_handle, (uint8_t *)combine_header, combine_header_len, http_handle->send_timeout_ms);
http_handle->sysdep->core_sysdep_mutex_unlock(http_handle->send_mutex);
http_handle->sysdep->core_sysdep_free(combine_header);
return res;
}
void *core_http_init(void)
{
core_http_handle_t *http_handle = NULL;
aiot_sysdep_portfile_t *sysdep = NULL;
sysdep = aiot_sysdep_get_portfile();
if (sysdep == NULL) {
return NULL;
}
http_handle = sysdep->core_sysdep_malloc(sizeof(core_http_handle_t), CORE_HTTP_MODULE_NAME);
if (http_handle == NULL) {
return NULL;
}
memset(http_handle, 0, sizeof(core_http_handle_t));
http_handle->sysdep = sysdep;
http_handle->connect_timeout_ms = CORE_HTTP_DEFAULT_CONNECT_TIMEOUT_MS;
http_handle->send_timeout_ms = CORE_HTTP_DEFAULT_SEND_TIMEOUT_MS;
http_handle->recv_timeout_ms = CORE_HTTP_DEFAULT_RECV_TIMEOUT_MS;
http_handle->header_line_max_len = CORE_HTTP_DEFAULT_HEADER_LINE_MAX_LEN;
http_handle->body_buffer_max_len = CORE_HTTP_DEFAULT_BODY_MAX_LEN;
http_handle->deinit_timeout_ms = CORE_HTTP_DEFAULT_DEINIT_TIMEOUT_MS;
http_handle->data_mutex = http_handle->sysdep->core_sysdep_mutex_init();
http_handle->send_mutex = http_handle->sysdep->core_sysdep_mutex_init();
http_handle->recv_mutex = http_handle->sysdep->core_sysdep_mutex_init();
http_handle->core_exec_enabled = 1;
return http_handle;
}
int32_t core_http_setopt(void *handle, core_http_option_t option, void *data)
{
int32_t res = STATE_SUCCESS;
core_http_handle_t *http_handle = (core_http_handle_t *)handle;
if (http_handle == NULL || data == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (option >= CORE_HTTPOPT_MAX) {
return STATE_USER_INPUT_OUT_RANGE;
}
if (http_handle->core_exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
_core_http_exec_inc(http_handle);
http_handle->sysdep->core_sysdep_mutex_lock(http_handle->data_mutex);
switch (option) {
case CORE_HTTPOPT_HOST: {
res = core_strdup(http_handle->sysdep, &http_handle->host, (char *)data, CORE_HTTP_MODULE_NAME);
}
break;
case CORE_HTTPOPT_PORT: {
http_handle->port = *(uint16_t *)data;
}
break;
case CORE_HTTPOPT_NETWORK_CRED: {
if (http_handle->cred != NULL) {
http_handle->sysdep->core_sysdep_free(http_handle->cred);
http_handle->cred = NULL;
}
http_handle->cred = http_handle->sysdep->core_sysdep_malloc(sizeof(aiot_sysdep_network_cred_t), CORE_HTTP_MODULE_NAME);
if (http_handle->cred != NULL) {
memset(http_handle->cred, 0, sizeof(aiot_sysdep_network_cred_t));
memcpy(http_handle->cred, data, sizeof(aiot_sysdep_network_cred_t));
} else {
res = STATE_SYS_DEPEND_MALLOC_FAILED;
}
}
break;
case CORE_HTTPOPT_CONNECT_TIMEOUT_MS: {
http_handle->connect_timeout_ms = *(uint32_t *)data;
}
break;
case CORE_HTTPOPT_SEND_TIMEOUT_MS: {
http_handle->send_timeout_ms = *(uint32_t *)data;
}
break;
case CORE_HTTPOPT_RECV_TIMEOUT_MS: {
http_handle->recv_timeout_ms = *(uint32_t *)data;
}
break;
case CORE_HTTPOPT_DEINIT_TIMEOUT_MS: {
http_handle->deinit_timeout_ms = *(uint32_t *)data;
}
break;
case CORE_HTTPOPT_HEADER_LINE_MAX_LEN: {
http_handle->header_line_max_len = *(uint32_t *)data;
}
break;
case CORE_HTTPOPT_BODY_BUFFER_MAX_LEN: {
http_handle->body_buffer_max_len = *(uint32_t *)data;
}
break;
case CORE_HTTPOPT_EVENT_HANDLER: {
http_handle->event_handler = (aiot_http_event_handler_t)data;
}
break;
case CORE_HTTPOPT_USERDATA: {
http_handle->core_userdata = data;
}
break;
case CORE_HTTPOPT_RECV_HANDLER: {
http_handle->core_recv_handler = (aiot_http_recv_handler_t)data;
}
break;
default: {
res = STATE_USER_INPUT_UNKNOWN_OPTION;
}
break;
}
http_handle->sysdep->core_sysdep_mutex_unlock(http_handle->data_mutex);
_core_http_exec_dec(http_handle);
return res;
}
int32_t core_http_connect(void *handle)
{
int32_t res = STATE_SUCCESS;
core_http_handle_t *http_handle = (core_http_handle_t *)handle;
if (http_handle == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (http_handle->core_exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
_core_http_exec_inc(http_handle);
/* connect to host */
http_handle->sysdep->core_sysdep_mutex_lock(http_handle->send_mutex);
http_handle->sysdep->core_sysdep_mutex_lock(http_handle->recv_mutex);
res = _core_http_connect(http_handle);
http_handle->sysdep->core_sysdep_mutex_unlock(http_handle->recv_mutex);
http_handle->sysdep->core_sysdep_mutex_unlock(http_handle->send_mutex);
_core_http_exec_dec(http_handle);
return res;
}
int32_t core_http_send(void *handle, const core_http_request_t *request)
{
int32_t res = STATE_SUCCESS;
char content_lenstr[11] = {0};
core_http_handle_t *http_handle = (core_http_handle_t *)handle;
if (http_handle == NULL || request == NULL ||
request->path == NULL || request->method == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (http_handle->host == NULL) {
return STATE_USER_INPUT_MISSING_HOST;
}
if (http_handle->network_handle == NULL) {
return STATE_SYS_DEPEND_NWK_CLOSED;
}
if (http_handle->core_exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
memset(&http_handle->session, 0, sizeof(core_http_session_t));
_core_http_exec_inc(http_handle);
/* send http header */
core_uint2str(request->content_len, content_lenstr, NULL);
res = _core_http_send_header(http_handle, request->method, request->path, http_handle->host, request->header,
content_lenstr);
if (res < STATE_SUCCESS) {
_core_http_exec_dec(http_handle);
return res;
} else {
res = STATE_SUCCESS;
}
/* send http content */
if (request->content != NULL && request->content_len > 0) {
res = _core_http_send_body(http_handle, request->content, request->content_len);
if (res < STATE_SUCCESS) {
_core_http_exec_dec(http_handle);
return res;
}
}
_core_http_exec_dec(http_handle);
return res;
}
static void _core_http_recv_status_code(core_http_handle_t *http_handle, uint32_t status_code)
{
aiot_http_recv_t packet;
if (http_handle->core_recv_handler == NULL) {
return;
}
memset(&packet, 0, sizeof(aiot_http_recv_t));
packet.type = AIOT_HTTPRECV_STATUS_CODE;
packet.data.status_code.code = status_code;
http_handle->core_recv_handler(http_handle, &packet, http_handle->core_userdata);
}
static void _core_http_recv_header_pair(core_http_handle_t *http_handle, char *key, uint32_t key_len, char *value,
uint32_t value_len)
{
char *pair_key = NULL, *pair_value = NULL;
aiot_http_recv_t packet;
if (http_handle->core_recv_handler == NULL) {
return;
}
pair_key = http_handle->sysdep->core_sysdep_malloc(key_len + 1, CORE_HTTP_MODULE_NAME);
if (pair_key == NULL) {
return;
}
memset(pair_key, 0, key_len + 1);
memcpy(pair_key, key, key_len);
pair_value = http_handle->sysdep->core_sysdep_malloc(value_len + 1, CORE_HTTP_MODULE_NAME);
if (pair_value == NULL) {
http_handle->sysdep->core_sysdep_free(pair_key);
return;
}
memset(pair_value, 0, value_len + 1);
memcpy(pair_value, value, value_len);
packet.type = AIOT_HTTPRECV_HEADER;
packet.data.header.key = pair_key;
packet.data.header.value = pair_value;
http_handle->core_recv_handler(http_handle, &packet, http_handle->core_userdata);
http_handle->sysdep->core_sysdep_free(pair_key);
http_handle->sysdep->core_sysdep_free(pair_value);
}
static int32_t _core_http_recv(core_http_handle_t *http_handle, uint8_t *buffer, uint32_t len, uint32_t timeout_ms)
{
int32_t res = STATE_SUCCESS;
if (http_handle->network_handle != NULL) {
res = http_handle->sysdep->core_sysdep_network_recv(http_handle->network_handle, buffer, len, timeout_ms, NULL);
if (res < STATE_SUCCESS) {
http_handle->sysdep->core_sysdep_network_deinit(&http_handle->network_handle);
core_log(http_handle->sysdep, STATE_HTTP_LOG_DISCONNECT, "HTTP network error when receving data, disconnect\r\n");
res = _core_http_sysdep_return(res, STATE_SYS_DEPEND_NWK_RECV_ERR);
}
} else {
res = STATE_SYS_DEPEND_NWK_CLOSED;
}
return res;
}
static int32_t _core_http_recv_header(core_http_handle_t *http_handle, uint32_t *body_total_len)
{
int32_t res = STATE_SUCCESS;
char *line = NULL;
uint32_t idx = 0, line_max_len = http_handle->header_line_max_len;
uint64_t timenow_ms = 0;
line = http_handle->sysdep->core_sysdep_malloc(line_max_len, CORE_HTTP_MODULE_NAME);
if (line == NULL) {
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
memset(line, 0, line_max_len);
timenow_ms = http_handle->sysdep->core_sysdep_time();
for (idx = 0; idx < line_max_len;) {
if (timenow_ms > http_handle->sysdep->core_sysdep_time()) {
timenow_ms = http_handle->sysdep->core_sysdep_time();
}
if (http_handle->sysdep->core_sysdep_time() - timenow_ms >= http_handle->recv_timeout_ms) {
res = STATE_HTTP_HEADER_INVALID;
break;
}
if (idx + 2 > line_max_len) {
res = STATE_HTTP_HEADER_BUFFER_TOO_SHORT;
break;
}
/* read http header, "\r\n" in the end */
if ((res = _core_http_recv(http_handle, (uint8_t *)&line[idx], 1,
http_handle->recv_timeout_ms)) < STATE_SUCCESS) {
break;
}
idx++;
if (res == 0 || (line[idx - 1] != '\r')) {
continue;
}
if ((res = _core_http_recv(http_handle, (uint8_t *)&line[idx], 1,
http_handle->recv_timeout_ms)) < STATE_SUCCESS) {
break;
}
idx++;
if (res == 0 || (line[idx - 1] != '\n')) {
continue;
}
core_log2(http_handle->sysdep, STATE_HTTP_LOG_RECV_HEADER, "< %.*s", &idx, line);
/* next line should be http response body */
if (idx == 2) {
break;
}
/* status code */
if ((idx > (strlen("HTTP/1.1 ") + 3)) && (memcmp(line, "HTTP/1.1 ", strlen("HTTP/1.1 "))) == 0) {
uint32_t status_code = 0, code_idx = 0;
for (code_idx = strlen("HTTP/1.1 "); code_idx < idx; code_idx++) {
if (line[code_idx] < '0' || line[code_idx] > '9') {
break;
}
}
res = core_str2uint(&line[strlen("HTTP/1.1 ")], (code_idx - strlen("HTTP/1.1 ")), &status_code);
if (res < STATE_SUCCESS) {
res = STATE_HTTP_STATUS_LINE_INVALID;
break;
}
_core_http_recv_status_code(http_handle, status_code);
}
/* header */
{
uint32_t deli_idx = 0;
for (deli_idx = 0; deli_idx < idx; deli_idx++) {
if (line[deli_idx] == ':' && line[deli_idx + 1] == ' ') {
if ((deli_idx + 2 == strlen("Content-Length: ")) && (memcmp(line, "Content-Length: ", deli_idx + 2) == 0)) {
core_str2uint(&line[deli_idx + 2], (uint32_t)(idx - deli_idx - 4), body_total_len);
}
_core_http_recv_header_pair(http_handle, line, deli_idx, &line[deli_idx + 2], (uint32_t)(idx - deli_idx - 4));
}
}
}
idx = 0;
memset(line, 0, line_max_len);
}
http_handle->sysdep->core_sysdep_free(line);
return res;
}
static int32_t _core_http_recv_body(core_http_handle_t *http_handle, uint32_t body_total_len)
{
int32_t res = STATE_SUCCESS;
char *buffer = NULL;
uint32_t remaining_len = 0, buffer_len = 0;
if (http_handle->session.body_total_len == 0 && body_total_len == 0) {
return STATE_HTTP_READ_BODY_EMPTY;
}
if (body_total_len != 0) {
http_handle->session.body_total_len = body_total_len;
remaining_len = body_total_len;
} else {
remaining_len = http_handle->session.body_total_len - http_handle->session.body_read_len;
}
if (remaining_len == 0) {
return STATE_HTTP_READ_BODY_FINISHED;
}
buffer_len = (remaining_len < http_handle->body_buffer_max_len) ? (remaining_len) : (http_handle->body_buffer_max_len);
buffer = http_handle->sysdep->core_sysdep_malloc(buffer_len, CORE_HTTP_MODULE_NAME);
if (buffer == NULL) {
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
memset(buffer, 0, buffer_len);
http_handle->sysdep->core_sysdep_mutex_lock(http_handle->recv_mutex);
res = _core_http_recv(http_handle, (uint8_t *)buffer, buffer_len, http_handle->recv_timeout_ms);
http_handle->sysdep->core_sysdep_mutex_unlock(http_handle->recv_mutex);
if (res > 0) {
aiot_http_recv_t packet;
core_log_hexdump(STATE_HTTP_LOG_RECV_CONTENT, '<', (uint8_t *)buffer, res);
if (http_handle->core_recv_handler != NULL) {
http_handle->session.body_read_len += res;
memset(&packet, 0, sizeof(aiot_http_recv_t));
packet.type = AIOT_HTTPRECV_BODY;
packet.data.body.buffer = (uint8_t *)buffer;
packet.data.body.len = res;
http_handle->core_recv_handler(http_handle, &packet, http_handle->core_userdata);
}
}
http_handle->sysdep->core_sysdep_free(buffer);
return res;
}
int32_t core_http_recv(void *handle)
{
int32_t res = STATE_SUCCESS;
uint32_t body_total_len = 0;
core_http_handle_t *http_handle = (core_http_handle_t *)handle;
if (http_handle == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
if (http_handle->network_handle == NULL) {
return STATE_SYS_DEPEND_NWK_CLOSED;
}
if (http_handle->core_exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
_core_http_exec_inc(http_handle);
if (http_handle->session.sm == CORE_HTTP_SM_READ_HEADER) {
http_handle->sysdep->core_sysdep_mutex_lock(http_handle->recv_mutex);
res = _core_http_recv_header(http_handle, &body_total_len);
http_handle->sysdep->core_sysdep_mutex_unlock(http_handle->recv_mutex);
if (res < STATE_SUCCESS) {
_core_http_exec_dec(http_handle);
return res;
}
}
http_handle->session.sm = CORE_HTTP_SM_READ_BODY;
res = _core_http_recv_body(http_handle, body_total_len);
if (res == STATE_HTTP_READ_BODY_FINISHED || res == STATE_HTTP_READ_BODY_EMPTY) {
memset(&http_handle->session, 0, sizeof(core_http_session_t));
}
_core_http_exec_dec(http_handle);
return res;
}
int32_t core_http_deinit(void **p_handle)
{
uint32_t deinit_timeout_ms = 0;
core_http_handle_t *http_handle = NULL;
if (p_handle == NULL || *p_handle == NULL) {
return STATE_USER_INPUT_NULL_POINTER;
}
http_handle = *(core_http_handle_t **)p_handle;
if (http_handle->core_exec_enabled == 0) {
return STATE_USER_INPUT_EXEC_DISABLED;
}
http_handle->exec_enabled = 0;
deinit_timeout_ms = http_handle->deinit_timeout_ms;
do {
if (http_handle->exec_count == 0) {
break;
}
http_handle->sysdep->core_sysdep_sleep(CORE_HTTP_DEINIT_INTERVAL_MS);
} while ((deinit_timeout_ms > CORE_HTTP_DEINIT_INTERVAL_MS) && (deinit_timeout_ms - CORE_HTTP_DEINIT_INTERVAL_MS > 0));
if (http_handle->exec_count != 0) {
return STATE_HTTP_DEINIT_TIMEOUT;
}
if (http_handle->network_handle != NULL) {
http_handle->sysdep->core_sysdep_network_deinit(&http_handle->network_handle);
}
if (http_handle->host != NULL) {
http_handle->sysdep->core_sysdep_free(http_handle->host);
}
if (http_handle->cred != NULL) {
http_handle->sysdep->core_sysdep_free(http_handle->cred);
}
http_handle->sysdep->core_sysdep_mutex_deinit(&http_handle->data_mutex);
http_handle->sysdep->core_sysdep_mutex_deinit(&http_handle->send_mutex);
http_handle->sysdep->core_sysdep_mutex_deinit(&http_handle->recv_mutex);
http_handle->sysdep->core_sysdep_free(http_handle);
*p_handle = NULL;
return STATE_SUCCESS;
}

View File

@@ -0,0 +1,191 @@
#ifndef _CORE_HTTP_H_
#define _CORE_HTTP_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include "core_stdinc.h"
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "core_string.h"
#include "core_log.h"
#include "core_auth.h"
#include "aiot_http_api.h"
typedef enum {
CORE_HTTP_SM_READ_HEADER,
CORE_HTTP_SM_READ_BODY
} core_http_sm_t;
typedef struct {
core_http_sm_t sm;
uint32_t body_total_len;
uint32_t body_read_len;
} core_http_session_t;
typedef struct {
uint32_t code;
uint8_t *content;
uint32_t content_len;
uint32_t content_total_len;
} core_http_response_t;
typedef struct {
aiot_sysdep_portfile_t *sysdep;
void *network_handle;
char *host;
uint16_t port;
char *product_key;
char *device_name;
char *device_secret;
char *extend_devinfo;
uint32_t connect_timeout_ms;
uint32_t send_timeout_ms;
uint32_t recv_timeout_ms;
uint32_t auth_timeout_ms;
uint32_t deinit_timeout_ms;
uint32_t header_line_max_len;
uint32_t body_buffer_max_len;
aiot_sysdep_network_cred_t *cred;
char *token;
uint8_t long_connection;
uint8_t exec_enabled;
uint32_t exec_count;
uint8_t core_exec_enabled;
uint32_t core_exec_count;
void *data_mutex;
void *send_mutex;
void *recv_mutex;
core_http_session_t session;
aiot_http_event_handler_t event_handler;
aiot_http_recv_handler_t recv_handler;
aiot_http_recv_handler_t core_recv_handler;
void *userdata;
void *core_userdata;
} core_http_handle_t;
#define CORE_HTTP_MODULE_NAME "HTTP"
#define CORE_HTTP_DEINIT_INTERVAL_MS (100)
#define CORE_HTTP_DEFAULT_CONNECT_TIMEOUT_MS (10 * 1000)
#define CORE_HTTP_DEFAULT_AUTH_TIMEOUT_MS (5 * 1000)
#define CORE_HTTP_DEFAULT_SEND_TIMEOUT_MS (5 * 1000)
#define CORE_HTTP_DEFAULT_RECV_TIMEOUT_MS (5 * 1000)
#define CORE_HTTP_DEFAULT_HEADER_LINE_MAX_LEN (128)
#define CORE_HTTP_DEFAULT_BODY_MAX_LEN (128)
#define CORE_HTTP_DEFAULT_DEINIT_TIMEOUT_MS (2 * 1000)
typedef enum {
CORE_HTTPOPT_HOST, /* 数据类型: (char *), 服务器域名, 默认值: iot-as-http.cn-shanghai.aliyuncs.com */
CORE_HTTPOPT_PORT, /* 数据类型: (uint16_t), 服务器端口号, 默认值: 443 */
CORE_HTTPOPT_NETWORK_CRED, /* 数据类型: (aiot_sysdep_network_cred_t *), 网络安全凭证, 默认值: NULL */
CORE_HTTPOPT_CONNECT_TIMEOUT_MS, /* 数据类型: (uint32_t), 建立网络连接的超时时间 */
CORE_HTTPOPT_SEND_TIMEOUT_MS, /* 数据类型: (uint32_t), 网络发送超时时间(单位ms), 默认值: 5000ms */
CORE_HTTPOPT_RECV_TIMEOUT_MS, /* 数据类型: (uint32_t), 网络接受超时时间(单位ms), 默认值: 5000ms */
CORE_HTTPOPT_DEINIT_TIMEOUT_MS, /* 数据类型: (uint32_t), 销毁http实例时, 等地啊其他api执行完毕的时间 */
CORE_HTTPOPT_HEADER_LINE_MAX_LEN, /* 数据类型: (uint32_t), http协议中单行header的最大长度 */
CORE_HTTPOPT_BODY_BUFFER_MAX_LEN, /* 数据类型: (uint32_t), 每次读取的body最大长度 */
CORE_HTTPOPT_EVENT_HANDLER, /* 数据类型: (aiot_http_event_handler_t), 用户事件回调函数, 默认值: NULL */
/* 以上选项配置的数据与 AIOT_HTTPOPT_XXX 共用 */
CORE_HTTPOPT_USERDATA, /* 数据类型: (void *), 用户上下文数据指针, 默认值: NULL */
CORE_HTTPOPT_RECV_HANDLER, /* 数据类型: (aiot_http_event_handler_t), 用户数据接受回调函数, 默认值: NULL */
CORE_HTTPOPT_MAX
} core_http_option_t;
typedef struct {
char *method; /* HTTP请求方法, 可为"POST", "GET"等 */
char *path; /* HTTP请求的路径 */
char *header; /* HTTP请求的头部, 必须以单个\r\n结尾, 无需包含Content-Length */
uint8_t *content; /* 指向用户待发送Content的指针 */
uint32_t content_len; /* 用户待发送Content的长度 */
} core_http_request_t;
/**
* @brief 初始化一个HTTP实例, 并返回实例句柄
*
* @return void*
* @retval NotNull core HTTP句柄
* @retval Null 初始化HTTP实例失败
*/
void *core_http_init(void);
/**
* @brief 设置HTTP实例选项
*
* @param[in] handle HTTP句柄
* @param option 配置选项, 可查看枚举类型 @ref core_http_option_t
* @param[in] data 配置数据, 每个选项对应的数据类型可查看 @ref core_http_option_t
* @return int32_t
* @retval STATE_SUCCESS, 成功
* @retval STATE_HTTP_HANDLE_IS_NULL, HTTP句柄为NULL
* @retval STATE_USER_INPUT_OUT_RANGE, 用户输入参数无效
* @retval STATE_SYS_DEPEND_MALLOC_FAILED, 内存分配失败
*
*/
int32_t core_http_setopt(void *handle, core_http_option_t option, void *data);
/**
* @brief 建立网络连接
*
* @param handle HTTP句柄
* @return int32_t
* @retval STATE_SUCCESS 网络连接建立成功
* @retval <STATE_SUCCESS 网络连接建立失败
*/
int32_t core_http_connect(void *handle);
/**
* @brief 发送HTTP请求
*
* @param[in] handle HTTP句柄
* @param request 请求结构体, 查看 @ref core_http_request_t
* @return int32_t
* @retval > 0, 已发送的数据长度
* @retval STATE_HTTP_HANDLE_IS_NULL, HTTP句柄为NULL
* @retval STATE_USER_INPUT_NULL_POINTER, 用户输入参数为NULL
* @retval STATE_USER_INPUT_MISSING_HOST, 用户未配置Host
* @retval STATE_SYS_DEPEND_MALLOC_FAILED, 内存分配失败
* @retval STATE_SYS_DEPEND_NWK_EST_FAILED, 网络建立连接失败
* @retval STATE_SYS_DEPEND_NWK_CLOSED, 网络连接已关闭
* @retval STATE_SYS_DEPEND_NWK_WRITE_LESSDATA, 网络发送超时
*/
int32_t core_http_send(void *handle, const core_http_request_t *request);
/**
* @brief 接受HTTP应答数据, 内部将解析状态码和Header并通过回调函数通知用户, 若应答中有body则保存到用户缓冲区中
*
* @param[in] handle HTTP句柄
* @param buffer 指向存放接受
* @param buffer_len
* @return int32_t
* @retval >= 0, 接受到的HTTP body数据长度
* @retval STATE_HTTP_HANDLE_IS_NULL, HTTP句柄为NULL
* @retval STATE_USER_INPUT_NULL_POINTER, 用户输入参数为NULL
* @retval STATE_USER_INPUT_OUT_RANGE, buffer_len为0
* @retval STATE_SYS_DEPEND_NWK_CLOSED, 网络连接已关闭
* @retval STATE_SYS_DEPEND_NWK_READ_OVERTIME, 网络接收超时
* @retval STATE_HTTP_RECV_LINE_TOO_LONG, HTTP单行数据过长, 内部无法解析
* @retval STATE_HTTP_PARSE_STATUS_LINE_FAILED, 无法解析状态码
* @retval STATE_HTTP_GET_CONTENT_LEN_FAILED, 获取Content-Length失败
*
*/
int32_t core_http_recv(void *handle);
/**
* @brief 销毁参数p_handle所指定的HTTP实例
*
* @param[in] p_handle 指向HTTP句柄的指针
* @return int32_t
* @retval STATE_SUCCESS 成功
* @retval STATE_USER_INPUT_NULL_POINTER 参数p_handle为NULL或者p_handle指向的句柄为NULL
*/
int32_t core_http_deinit(void **p_handle);
#if defined(__cplusplus)
}
#endif
#endif /* #ifndef _CORE_HTTP_H_ */

View File

@@ -0,0 +1,187 @@
#ifndef _CORE_LIST_H_
#define _CORE_LIST_H_
#if defined(__cplusplus)
extern "C" {
#endif
#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) || defined(__GNUC__)) && \
!defined(inline) && !defined(__cplusplus)
#define inline __inline
#endif
struct core_list_head {
struct core_list_head *prev;
struct core_list_head *next;
};
/*
* Get offset of a member variable.
*
* @param[in] type the type of the struct this is embedded in.
* @param[in] member the name of the variable within the struct.
*/
#ifdef offsetof
#undef offsetof
#endif
#define offsetof(type, member) ((size_t)&(((type *)0)->member))
/*
* Get the struct for this entry.
*
* @param[in] ptr the list head to take the element from.
* @param[in] type the type of the struct this is embedded in.
* @param[in] member the name of the variable within the struct.
*/
#define container_of(ptr, type, member) \
((type *) ((char *) (ptr) - offsetof(type, member)))
static inline void CORE_INIT_LIST_HEAD(struct core_list_head *list)
{
list->next = list;
list->prev = list;
}
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __core_list_add(struct core_list_head *node,
struct core_list_head *prev,
struct core_list_head *next)
{
next->prev = node;
node->next = next;
node->prev = prev;
prev->next = node;
}
/**
* core_list_add - add a new entry
* @node: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void core_list_add(struct core_list_head *node, struct core_list_head *head)
{
__core_list_add(node, head, head->next);
}
/**
* core_list_add_tail - add a new entry
* @node: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void core_list_add_tail(struct core_list_head *node, struct core_list_head *head)
{
__core_list_add(node, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __core_list_del(struct core_list_head *prev, struct core_list_head *next)
{
next->prev = prev;
prev->next = next;
}
/**
* core_list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty() on entry does not return true after this, the entry is
* in an undefined state.
*/
static inline void __core_list_del_entry(struct core_list_head *entry)
{
__core_list_del(entry->prev, entry->next);
}
static inline void core_list_del(struct core_list_head *entry)
{
__core_list_del_entry(entry);
entry->next = entry;
entry->prev = entry;
}
/**
* core_list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int core_list_empty(const struct core_list_head *head)
{
return head->next == head;
}
/**
* core_list_entry - get the struct for this entry
* @ptr: the &struct core_list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the core_list_head within the struct.
*/
#define core_list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* core_list_first_entry - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the core_list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define core_list_first_entry(ptr, type, member) \
core_list_entry((ptr)->next, type, member)
/**
* core_list_next_entry - get the next element in list
* @pos: the type * to cursor
* @member: the name of the core_list_head within the struct.
* @type: the type of the struct this is embedded in
*/
#define core_list_next_entry(pos, member, type) \
core_list_entry((pos)->member.next, type, member)
/**
* core_list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the core_list_head within the struct.
* @type: the type of the struct this is embedded in
*/
#define core_list_for_each_entry(pos, head, member, type) \
for (pos = core_list_first_entry(head, type, member); \
&pos->member != (head); \
pos = core_list_next_entry(pos, member, type))
/**
* core_list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the core_list_head within the struct.
* @type: the type of the struct this is embedded in
*/
#define core_list_for_each_entry_safe(pos, n, head, member, type) \
for (pos = core_list_first_entry(head, type, member), \
n = core_list_next_entry(pos, member, type); \
&pos->member != (head); \
pos = n, n = core_list_next_entry(n, member, type))
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,304 @@
#include "core_log.h"
extern aiot_state_logcb_t g_logcb_handler;
static core_log_t g_core_log = { .time_start = 0, .time_interval = 0, .timestamp = 0, .log_stamp = 1, .log_date = 0};
static void _core_log_append_code(int32_t code, char *buffer)
{
uint8_t code_hex[4] = {0};
char code_str[9] = {0};
code_hex[0] = ((uint16_t)(-code) >> 8) & 0x000000FF;
code_hex[1] = ((uint16_t)(-code)) & 0x000000FF;
core_hex2str(code_hex, 2, code_str, 0);
memcpy(buffer + strlen(buffer), "[LK-", strlen("[LK-"));
memcpy(buffer + strlen(buffer), code_str, strlen(code_str));
memcpy(buffer + strlen(buffer), "] ", strlen("] "));
}
static uint64_t _core_log_get_timestamp(aiot_sysdep_portfile_t *sysdep)
{
uint64_t timenow = sysdep->core_sysdep_time();
/*NTP同步过时间判断系统时间是否已更新没更新进入if分支使用网络时间log*/
if (g_core_log.timestamp != 0 && g_core_log.timestamp > timenow)
{
if (timenow >= g_core_log.time_start) {
g_core_log.time_interval += timenow - g_core_log.time_start;
}else{
/* loss (max_time - g_core_log.time_start) ms */
g_core_log.time_interval += timenow;
}
g_core_log.time_start = timenow;
timenow = g_core_log.timestamp + g_core_log.time_interval;
}
return timenow;
}
void _core_log_append_date(aiot_sysdep_portfile_t *sysdep, uint64_t timestamp, char *buffer)
{
int32_t res = STATE_SUCCESS;
char date_str[7][11] = {{0}};
char *time = NULL;
char *time_src[] = { date_str[0], date_str[1], date_str[2], date_str[3], date_str[4], date_str[5] };
char *time_fmt = "%s/%s/%s %s:%s:%s";
core_date_t date;
memset(&date, 0, sizeof(core_date_t));
core_utc2date(timestamp, 8, &date);
core_uint2str(date.year, date_str[0], NULL);
core_uint2str(date.mon, date_str[1], NULL);
core_uint2str(date.day, date_str[2], NULL);
core_uint2str(date.hour, date_str[3], NULL);
core_uint2str(date.min, date_str[4], NULL);
core_uint2str(date.sec, date_str[5], NULL);
res = core_sprintf(sysdep, &time, time_fmt, time_src, sizeof(time_src)/sizeof(char *), CORE_LOG_MODULE_NAME);
if (res >= STATE_SUCCESS) {
memcpy(buffer + strlen(buffer), time, strlen(time));
sysdep->core_sysdep_free(time);
}
}
static void _core_log_append_prefix(aiot_sysdep_portfile_t *sysdep, int32_t code, char *buffer)
{
uint64_t timenow = 0;
if (sysdep == NULL) {
return;
}
timenow = _core_log_get_timestamp(sysdep);
if (1 == g_core_log.log_date) {
memcpy(buffer + strlen(buffer), "[", strlen("["));
_core_log_append_date(sysdep, timenow, buffer);
memcpy(buffer + strlen(buffer), "]", strlen("]"));
}
if(1 == g_core_log.log_stamp){
char timestamp_str[24] = {0};
uint8_t timestamp_len = 0;
core_uint642str(timenow, timestamp_str, &timestamp_len);
if (timestamp_len > 3) {
memcpy(&timestamp_str[timestamp_len - 2], &timestamp_str[timestamp_len - 3], 3);
timestamp_str[timestamp_len - 3] = '.';
}
memcpy(buffer + strlen(buffer), "[", strlen("["));
memcpy(buffer + strlen(buffer), timestamp_str, strlen(timestamp_str));
memcpy(buffer + strlen(buffer), "]", strlen("]"));
}
_core_log_append_code(code, buffer);
}
static void _core_log(aiot_sysdep_portfile_t *sysdep, int32_t code, char *buffer, char *fmt, void *datas[],
uint8_t count)
{
uint32_t idx = 0, buffer_idx = 0, copy_len = 0, arg_flag = 0, arg_idx = 0;
void *arg = datas[arg_idx];
_core_log_append_prefix(sysdep, code, buffer);
buffer_idx += strlen(buffer);
for (idx = 0; idx < strlen(fmt);) {
if (buffer_idx >= CORE_LOG_MAXLEN) {
break;
}
if (arg_flag == 1) {
if (arg_idx < count - 1) {
arg = datas[++arg_idx];
} else {
arg = NULL;
}
arg_flag = 0;
}
if (fmt[idx] == '%' && fmt[idx + 1] == 's' && arg != NULL) {
char *value = arg;
copy_len = (strlen(buffer) + strlen(value) > CORE_LOG_MAXLEN) ? (CORE_LOG_MAXLEN - strlen(buffer)) : (strlen(value));
memcpy(buffer + strlen(buffer), value, copy_len);
buffer_idx += copy_len;
idx += 2;
arg_flag = 1;
} else if (memcmp(&fmt[idx], "%.*s", strlen("%.*s")) == 0 && arg != NULL && (arg_idx + 1) < count) {
char *value = (datas[arg_idx + 1] == NULL) ? ("") : (datas[arg_idx + 1]);
uint32_t len = (datas[arg_idx + 1] == NULL) ? (0) : (*(uint32_t *)arg);
copy_len = (strlen(buffer) + len > CORE_LOG_MAXLEN) ? (CORE_LOG_MAXLEN - strlen(buffer)) : (len);
memcpy(buffer + strlen(buffer), value, copy_len);
buffer_idx += copy_len;
idx += strlen("%.*s");
arg_flag = 1;
arg_idx++;
} else if (fmt[idx] == '%' && fmt[idx + 1] == 'd' && arg != NULL) {
char uint32_str[11] = {0};
core_uint2str(*(uint32_t *)arg, uint32_str, NULL);
copy_len = (strlen(buffer) + strlen(uint32_str) > CORE_LOG_MAXLEN) ? (CORE_LOG_MAXLEN - strlen(buffer)) : (strlen(
uint32_str));
memcpy(buffer + strlen(buffer), uint32_str, copy_len);
buffer_idx += copy_len;
idx += 2;
arg_flag = 1;
}
else if (fmt[idx] == '%' && fmt[idx + 1] == 'x' && arg != NULL) {
char uint32_str[12] = {0};
core_int2hexstr(*(int32_t *)arg, uint32_str, NULL);
copy_len = (strlen(buffer) + strlen(uint32_str) > CORE_LOG_MAXLEN) ? (CORE_LOG_MAXLEN - strlen(buffer)) : (strlen(
uint32_str));
memcpy(buffer + strlen(buffer), uint32_str, copy_len);
buffer_idx += copy_len;
idx += 2;
arg_flag = 1;
} else {
buffer[buffer_idx++] = fmt[idx++];
}
}
}
void core_log_set_timestamp(aiot_sysdep_portfile_t *sysdep, uint64_t timestamp)
{
g_core_log.timestamp = timestamp;
g_core_log.time_start = sysdep->core_sysdep_time();
g_core_log.time_interval = 0;
}
uint64_t core_log_get_timestamp(aiot_sysdep_portfile_t *sysdep)
{
return _core_log_get_timestamp(sysdep);
}
void core_log(aiot_sysdep_portfile_t *sysdep, int32_t code, char *data)
{
char buffer[CORE_LOG_MAXLEN + 3] = {0};
uint32_t len = 0;
if (g_logcb_handler == NULL) {
return;
}
buffer[CORE_LOG_MAXLEN] = '\r';
buffer[CORE_LOG_MAXLEN + 1] = '\n';
_core_log_append_prefix(sysdep, code, buffer);
len = (strlen(buffer) + strlen(data) > CORE_LOG_MAXLEN) ? (CORE_LOG_MAXLEN - strlen(buffer)) : (strlen(data));
memcpy(buffer + strlen(buffer), data, len);
g_logcb_handler(code, buffer);
}
void core_log1(aiot_sysdep_portfile_t *sysdep, int32_t code, char *fmt, void *data)
{
char buffer[CORE_LOG_MAXLEN + 3] = {0};
void *datas[] = {data};
if (g_logcb_handler == NULL) {
return;
}
buffer[CORE_LOG_MAXLEN] = '\r';
buffer[CORE_LOG_MAXLEN + 1] = '\n';
_core_log(sysdep, code, buffer, fmt, datas, 1);
g_logcb_handler(code, buffer);
}
void core_log2(aiot_sysdep_portfile_t *sysdep, int32_t code, char *fmt, void *data1, void *data2)
{
char buffer[CORE_LOG_MAXLEN + 3] = {0};
void *datas[] = {data1, data2};
if (g_logcb_handler == NULL) {
return;
}
buffer[CORE_LOG_MAXLEN] = '\r';
buffer[CORE_LOG_MAXLEN + 1] = '\n';
_core_log(sysdep, code, buffer, fmt, datas, 2);
g_logcb_handler(code, buffer);
}
void core_log3(aiot_sysdep_portfile_t *sysdep, int32_t code, char *fmt, void *data1, void *data2, void *data3)
{
char buffer[CORE_LOG_MAXLEN + 3] = {0};
void *datas[] = {data1, data2, data3};
if (g_logcb_handler == NULL) {
return;
}
buffer[CORE_LOG_MAXLEN] = '\r';
buffer[CORE_LOG_MAXLEN + 1] = '\n';
_core_log(sysdep, code, buffer, fmt, datas, 3);
g_logcb_handler(code, buffer);
}
#if 0
void core_log4(aiot_sysdep_portfile_t *sysdep, int32_t code, char *fmt, void *data1, void *data2, void *data3,
void *data4)
{
char buffer[CORE_LOG_MAXLEN + 3] = {0};
void *datas[] = {data1, data2, data3, data4};
if (g_logcb_handler == NULL) {
return;
}
buffer[CORE_LOG_MAXLEN] = '\r';
buffer[CORE_LOG_MAXLEN + 1] = '\n';
_core_log(sysdep, code, buffer, fmt, datas, 4);
g_logcb_handler(code, buffer);
}
#endif
void core_log_hexdump(int32_t code, char prefix, uint8_t *buffer, uint32_t len)
{
uint32_t idx = 0, line_idx = 0, ch_idx = 0, code_len = 0;
/* [LK-XXXX] + 1 + 1 + 16*3 + 1 + 1 + 1 + 16 + 2*/
char hexdump[25 + 72] = {0};
if (g_logcb_handler == NULL || len == 0) {
return;
}
g_logcb_handler(code, "\r\n");
_core_log_append_code(code, hexdump);
code_len = strlen(hexdump);
for (idx = 0; idx < len;) {
memset(hexdump + code_len, ' ', 71);
ch_idx = 2;
hexdump[code_len + 0] = prefix;
hexdump[code_len + 51] = '|';
hexdump[code_len + 52] = ' ';
for (line_idx = idx; ((line_idx - idx) < 16) && (line_idx < len); line_idx++) {
if ((line_idx - idx) == 8) {
ch_idx++;
}
core_hex2str((uint8_t *)&buffer[line_idx], 1, &hexdump[code_len + ch_idx], 0);
hexdump[code_len + ch_idx + 2] = ' ';
if (buffer[line_idx] >= 0x20 && buffer[line_idx] <= 0x7E) {
hexdump[code_len + 53 + (line_idx - idx)] = buffer[line_idx];
} else {
hexdump[code_len + 53 + (line_idx - idx)] = '.';
}
ch_idx += 3;
}
hexdump[code_len + 69] = '\r';
hexdump[code_len + 70] = '\n';
idx += (line_idx - idx);
g_logcb_handler(code, hexdump);
}
g_logcb_handler(code, "\r\n");
}

View File

@@ -0,0 +1,39 @@
#ifndef _CORE_LOG_H_
#define _CORE_LOG_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include "core_stdinc.h"
#include "core_string.h"
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#define CORE_LOG_MODULE_NAME "LOG"
#define CORE_LOG_MAXLEN (160)
typedef struct {
uint64_t time_start;
uint64_t time_interval;
uint64_t timestamp;
uint8_t log_stamp;
uint8_t log_date;
} core_log_t;
void core_log_set_timestamp(aiot_sysdep_portfile_t *sysdep, uint64_t timestamp);
uint64_t core_log_get_timestamp(aiot_sysdep_portfile_t *sysdep);
void core_log(aiot_sysdep_portfile_t *sysdep, int32_t code, char *data);
void core_log1(aiot_sysdep_portfile_t *sysdep, int32_t code, char *fmt, void *data);
void core_log2(aiot_sysdep_portfile_t *sysdep, int32_t code, char *fmt, void *data1, void *data2);
void core_log3(aiot_sysdep_portfile_t *sysdep, int32_t code, char *fmt, void *data1, void *data2, void *data3);
void core_log4(aiot_sysdep_portfile_t *sysdep, int32_t code, char *fmt, void *data1, void *data2, void *data3, void *data4);
void core_log_hexdump(int32_t code, char prefix, uint8_t *buffer, uint32_t len);
void _core_log_append_date(aiot_sysdep_portfile_t *sysdep, uint64_t timestamp, char *buffer);
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,292 @@
#ifndef _CORE_MQTT_H_
#define _CORE_MQTT_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include "core_stdinc.h"
#include "core_list.h"
#include "core_string.h"
#include "core_log.h"
#include "core_auth.h"
#include "core_global.h"
#include "core_diag.h"
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
/**
*
* MQTT3.1 Fixed Header
* | Bit | 7 ~ 4 | 3 ~ 0 |
* | byte 1 | MQTT Control Packet Type | Flags specific to each Type |
* | byte 2... | Remaining Length(1 ~ 4 bytes) |
*
* MQTT3.1 UTF-8 Encoded Strings
* | Bit | 7 ~ 0 |
* | byte 1 | String Length MSB |
* | byte 2 | String Length LSB |
* | byte 3... | UTF-8 Encoded Character Data |
*
*/
#define CORE_MQTT_FIXED_HEADER_LEN (1)
#define CORE_MQTT_REMAINLEN_MAXLEN (4)
#define CORE_MQTT_UTF8_STR_EXTRA_LEN (2)
#define CORE_MQTT_PACKETID_LEN (2)
#define CORE_MQTT_REQUEST_QOS_LEN (1)
#define CORE_MQTT_QOS0 (0x00)
#define CORE_MQTT_QOS1 (0x01)
#define CORE_MQTT_QOS_MAX (1)
#define CORE_MQTT_TOPIC_MAXLEN (128)
#define CORE_MQTT_PAYLOAD_MAXLEN (1024 * 1024 + 1)
/* MQTT 3.1 Connect Packet */
#define CORE_MQTT_CONN_PKT_TYPE (0x10)
#define CORE_MQTT_CONN_FIXED_HEADER_LEN (CORE_MQTT_FIXED_HEADER_LEN) /* value: 0x10 */
#define CORE_MQTT_CONN_REMAINLEN_MAXLEN (CORE_MQTT_REMAINLEN_MAXLEN)
#define CORE_MQTT_CONN_PROTOCOL_NAME_LEN (6) /* value: 0x00, 0x04, 0x4D, 0x51, 0x54, 0x54 */
#define CORE_MQTT_CONN_PROTOCOL_LEVEL_LEN (1) /* value: 0x04 */
#define CORE_MQTT_CONN_CONNECT_FLAG_LEN (1) /* value: 0xC0 */
#define CORE_MQTT_CONN_KEEP_ALIVE_LEN (2) /* value: CORE_MQTT_conn_pkt_t.keep_alive */
#define CORE_MQTT_CONN_FIXED_HEADER_TOTAL_LEN (CORE_MQTT_CONN_FIXED_HEADER_LEN + \
CORE_MQTT_CONN_REMAINLEN_MAXLEN + \
CORE_MQTT_CONN_PROTOCOL_NAME_LEN + \
CORE_MQTT_CONN_PROTOCOL_LEVEL_LEN + \
CORE_MQTT_CONN_CONNECT_FLAG_LEN + \
CORE_MQTT_CONN_KEEP_ALIVE_LEN)
#define CORE_MQTT_CONN_REMAINLEN_FIXED_LEN (CORE_MQTT_CONN_PROTOCOL_NAME_LEN + \
CORE_MQTT_CONN_PROTOCOL_LEVEL_LEN + \
CORE_MQTT_CONN_CONNECT_FLAG_LEN + \
CORE_MQTT_CONN_KEEP_ALIVE_LEN)
/* MQTT 3.1 Connect ACK Packet */
#define CORE_MQTT_CONNACK_PKT_TYPE (0x20)
#define CORE_MQTT_CONNACK_FIXED_HEADER_LEN (CORE_MQTT_FIXED_HEADER_LEN) /* value: 0x20 */
#define CORE_MQTT_CONNACK_REMAINLEN_MAXLEN (1) /* value: 0x02 */
#define CORE_MQTT_CONNACK_FLAGS_LEN (1) /* value: 0x00 */
#define CORE_MQTT_CONNACK_RETURN_CODE_LEN (1)
#define CORE_MQTT_CONNACK_FIXED_HEADER_TOTAL_LEN (CORE_MQTT_CONNACK_FIXED_HEADER_LEN + \
CORE_MQTT_CONNACK_REMAINLEN_MAXLEN + \
CORE_MQTT_CONNACK_FLAGS_LEN + \
CORE_MQTT_CONNACK_RETURN_CODE_LEN)
#define CORE_MQTT_CONNACK_RCODE_ACCEPTED (0x00)
#define CORE_MQTT_CONNACK_RCODE_UNACCEPTABLE_PROTOCOL_VERSION (0x01)
#define CORE_MQTT_CONNACK_RCODE_SERVER_UNAVAILABLE (0x02)
#define CORE_MQTT_CONNACK_RCODE_BAD_USERNAME_PASSWORD (0x03)
#define CORE_MQTT_CONNACK_RCODE_NOT_AUTHORIZED (0x04)
#define CORE_MQTT_DYNREG_CONNACK_RCODE_ACCEPTED (0x00)
#define CORE_MQTT_DYNREG_CONNACK_RCODE_IDENTIFIER_REJECTED (0x02)
#define CORE_MQTT_DYNREG_CONNACK_RCODE_SERVER_UNAVAILABLE (0x03)
#define CORE_MQTT_DYNREG_CONNACK_RCODE_BAD_USERNAME_PASSWORD (0x04)
/* MQTT 3.1 Disconnect Packet */
#define CORE_MQTT_DISCONNECT_PKT_TYPE (0xE0)
/* MQTT 3.1 Ping Request Packet */
#define CORE_MQTT_PINGREQ_PKT_TYPE (0xC0)
/* MQTT 3.1 Ping Response Packet */
#define CORE_MQTT_PINGRESP_PKT_TYPE (0xD0)
#define CORE_MQTT_PINGRESP_FIXED_HEADER_LEN (CORE_MQTT_FIXED_HEADER_LEN) /* value: 0xD0 */
#define CORE_MQTT_PINGRESP_REMAINLEN_MAXLEN (1) /* value: 0x00 */
/* MQTT 3.1 Publish Packet */
#define CORE_MQTT_PUBLISH_PKT_TYPE (0x30)
#define CORE_MQTT_PUBLISH_TOPICLEN_LEN (2)
/* MQTT 3.1 Publish ACK Packet */
#define CORE_MQTT_PUBACK_PKT_TYPE (0x40)
/* MQTT 3.1 Subscribe Packet */
#define CORE_MQTT_SUB_PKT_TYPE (0x80)
#define CORE_MQTT_SUB_PKT_RESERVE (0x02)
#define CORE_MQTT_UNSUB_PKT_RESERVE (0x02)
/* MQTT 3.1 Subscribe ACK Packet */
#define CORE_MQTT_SUBACK_PKT_TYPE (0x90)
#define CORE_MQTT_SUBACK_RCODE_MAXQOS0 (0x00)
#define CORE_MQTT_SUBACK_RCODE_MAXQOS1 (0x01)
#define CORE_MQTT_SUBACK_RCODE_MAXQOS2 (0x02)
#define CORE_MQTT_SUBACK_RCODE_FAILURE (0x80)
/* MQTT 3.1 Unsubscribe Packet */
#define CORE_MQTT_UNSUB_PKT_TYPE (0xA0)
/* MQTT 3.1 Unsubscribe ACK Packet */
#define CORE_MQTT_UNSUBACK_PKT_TYPE (0xB0)
/* MQTT 3.1 unimplemented Packet */
#define CORE_MQTT_PUBREC_PKT_TYPE (0x50)
#define CORE_MQTT_PUBREL_PKT_TYPE (0x60)
#define CORE_MQTT_PUBCOMP_PKT_TYPE (0x70)
typedef struct {
uint8_t *buffer;
uint32_t len;
} core_mqtt_buff_t;
typedef struct {
aiot_mqtt_recv_handler_t handler;
void *userdata;
struct core_list_head linked_node;
} core_mqtt_sub_handler_node_t;
typedef struct {
char *topic;
struct core_list_head linked_node;
struct core_list_head handle_list;
} core_mqtt_sub_node_t;
typedef struct {
uint16_t packet_id;
uint8_t *packet;
uint32_t len;
uint64_t last_send_time;
struct core_list_head linked_node;
} core_mqtt_pub_node_t;
typedef enum {
CORE_MQTTEVT_DEINIT
} core_mqtt_event_type_t;
typedef struct {
core_mqtt_event_type_t type;
} core_mqtt_event_t;
typedef void (*core_mqtt_process_handler_t)(void *context, aiot_mqtt_event_t *event, core_mqtt_event_t *core_event);
typedef struct {
core_mqtt_process_handler_t handler;
void *context;
} core_mqtt_process_data_t;
typedef struct {
core_mqtt_process_data_t process_data;
struct core_list_head linked_node;
} core_mqtt_process_data_node_t;
typedef struct {
uint32_t interval_ms;
uint8_t max_lost_times;
uint32_t lost_times;
uint64_t last_send_time;
} core_mqtt_heartbeat_t;
typedef struct {
uint8_t enabled;
uint32_t interval_ms;
uint64_t last_retry_time;
uint8_t backoff_enabled; /*enabled backoff algorithm*/
int32_t rand_ms;
int32_t reconnect_counter;
} core_mqtt_reconnect_t;
typedef struct {
/* network info */
uint8_t network_type; /* 0: TCP, 1: TLS */
uint64_t connect_timestamp;
uint32_t connect_time_used;
uint64_t failed_timestamp;
int32_t failed_error_code;
/* heartbeat rtt info */
uint64_t rtt;
} core_mqtt_nwkstats_info_t;
typedef struct {
aiot_sysdep_portfile_t *sysdep;
void *network_handle;
char *host;
uint16_t port;
char *product_key;
char *device_name;
char *device_secret;
char *username;
char *password;
char *clientid;
char *extend_clientid;
char *security_mode;
uint16_t keep_alive_s;
uint8_t clean_session;
uint8_t append_requestid;
uint32_t connect_timeout_ms;
core_mqtt_heartbeat_t heartbeat_params;
core_mqtt_reconnect_t reconnect_params;
uint32_t send_timeout_ms;
uint32_t recv_timeout_ms;
uint32_t repub_timeout_ms;
aiot_sysdep_network_cred_t *cred;
uint8_t has_connected;
uint8_t disconnected;
uint8_t disconnect_api_called;
uint8_t exec_enabled;
uint32_t exec_count;
uint32_t deinit_timeout_ms;
uint16_t packet_id;
void *data_mutex;
void *send_mutex;
void *recv_mutex;
void *sub_mutex;
void *pub_mutex;
void *process_handler_mutex;
struct core_list_head sub_list;
struct core_list_head pub_list;
struct core_list_head process_data_list;
aiot_mqtt_recv_handler_t recv_handler;
aiot_mqtt_event_handler_t event_handler;
/* network info stats */
core_mqtt_nwkstats_info_t nwkstats_info;
void *userdata;
uint16_t repub_list_limit;
} core_mqtt_handle_t;
/* default configuration */
#define CORE_MQTT_MODULE_NAME "MQTT"
#define CORE_MQTT_DEINIT_INTERVAL_MS (100)
#define CORE_MQTT_DEFAULT_KEEPALIVE_S (1200)
#define CORE_MQTT_DEFAULT_CLEAN_SESSION (1)
#define CORE_MQTT_DEFAULT_CONNECT_TIMEOUT_MS (10 * 1000)
#define CORE_MQTT_DEFAULT_HEARTBEAT_INTERVAL_MS (25 * 1000)
#define CORE_MQTT_DEFAULT_REPUB_LIST_LIMIT (50)
#define CORE_MQTT_DEFAULT_HEARTBEAT_MAX_LOST_TIMES (2)
#define CORE_MQTT_DEFAULT_SEND_TIMEOUT_MS (5 * 1000)
#define CORE_MQTT_DEFAULT_RECV_TIMEOUT_MS (5 * 1000)
#define CORE_MQTT_DEFAULT_REPUB_TIMEOUT_MS (3 * 1000)
#define CORE_MQTT_DEFAULT_RECONN_ENABLED (1)
#define CORE_MQTT_DEFAULT_RECONN_INTERVAL_MS (2 * 1000)
#define CORE_MQTT_DEFAULT_RECONN_RANDLIMIT_MS (1 * 1000)
#define CORE_MQTT_DEFAULT_RECONN_MAX_COUNTERS (60) /*mqtt 断线重连退避算法的最大计数*/
#define CORE_MQTT_DEFAULT_DEINIT_TIMEOUT_MS (2 * 1000)
#define CORE_MQTT_DIAG_TLV_MQTT_CONNECTION (0x0010)
#define CORE_MQTT_DIAG_TLV_MQTT_HEARTBEAT (0x0020)
#define CORE_MQTT_NWKSTATS_RTT_THRESHOLD (10000)
typedef enum {
CORE_MQTTOPT_APPEND_PROCESS_HANDLER,
CORE_MQTTOPT_REMOVE_PROCESS_HANDLER,
CORE_MQTTOPT_MAX
} core_mqtt_option_t;
int32_t core_mqtt_setopt(void *handle, core_mqtt_option_t option, void *data);
char *core_mqtt_get_product_key(void *handle);
char *core_mqtt_get_device_name(void *handle);
uint16_t core_mqtt_get_port(void *handle);
int32_t core_mqtt_get_nwkstats(void *handle, core_mqtt_nwkstats_info_t *nwk_stats_info);
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,309 @@
#include "core_sha256.h"
#define MINI_SHA256_SMALLER
#define SHA256_KEY_IOPAD_SIZE (64)
#define SHA256_DIGEST_SIZE (32)
/*
* 32-bit integer manipulation macros (big endian)
*/
#ifndef GET_UINT32_BE
#define GET_UINT32_BE(n,b,i) \
do { \
(n) = ( (uint32_t) (b)[(i) ] << 24 ) \
| ( (uint32_t) (b)[(i) + 1] << 16 ) \
| ( (uint32_t) (b)[(i) + 2] << 8 ) \
| ( (uint32_t) (b)[(i) + 3] ); \
} while( 0 )
#endif
#ifndef PUT_UINT32_BE
#define PUT_UINT32_BE(n,b,i) \
do { \
(b)[(i) ] = (unsigned char) ( (n) >> 24 ); \
(b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \
(b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \
(b)[(i) + 3] = (unsigned char) ( (n) ); \
} while( 0 )
#endif
static void utils_sha256_zeroize(void *v, uint32_t n)
{
volatile unsigned char *p = v;
while (n--) {
*p++ = 0;
}
}
void core_sha256_init(core_sha256_context_t *ctx)
{
memset(ctx, 0, sizeof(core_sha256_context_t));
}
void core_sha256_free(core_sha256_context_t *ctx)
{
if (NULL == ctx) {
return;
}
utils_sha256_zeroize(ctx, sizeof(core_sha256_context_t));
}
void core_sha256_starts(core_sha256_context_t *ctx)
{
uint8_t is224 = 0;
ctx->total[0] = 0;
ctx->total[1] = 0;
if (is224 == 0) {
/* SHA-256 */
ctx->state[0] = 0x6A09E667;
ctx->state[1] = 0xBB67AE85;
ctx->state[2] = 0x3C6EF372;
ctx->state[3] = 0xA54FF53A;
ctx->state[4] = 0x510E527F;
ctx->state[5] = 0x9B05688C;
ctx->state[6] = 0x1F83D9AB;
ctx->state[7] = 0x5BE0CD19;
}
ctx->is224 = is224;
}
static const uint32_t K[] = {
0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2,
};
#define SHR(x,n) ((x & 0xFFFFFFFF) >> n)
#define ROTR(x,n) (SHR(x,n) | (x << (32 - n)))
#define S0(x) (ROTR(x, 7) ^ ROTR(x,18) ^ SHR(x, 3))
#define S1(x) (ROTR(x,17) ^ ROTR(x,19) ^ SHR(x,10))
#define S2(x) (ROTR(x, 2) ^ ROTR(x,13) ^ ROTR(x,22))
#define S3(x) (ROTR(x, 6) ^ ROTR(x,11) ^ ROTR(x,25))
#define F0(x,y,z) ((x & y) | (z & (x | y)))
#define F1(x,y,z) (z ^ (x & (y ^ z)))
#define R(t) \
( \
W[t] = S1(W[t - 2]) + W[t - 7] + \
S0(W[t - 15]) + W[t - 16] \
)
#define P(a,b,c,d,e,f,g,h,x,K) \
{ \
temp1 = h + S3(e) + F1(e,f,g) + K + x; \
temp2 = S2(a) + F0(a,b,c); \
d += temp1; h = temp1 + temp2; \
}
void core_sha256_process(core_sha256_context_t *ctx, const unsigned char data[64])
{
uint32_t temp1, temp2, W[64];
uint32_t A[8];
unsigned int i;
for (i = 0; i < 8; i++) {
A[i] = ctx->state[i];
}
#if defined(MINI_SHA256_SMALLER)
for (i = 0; i < 64; i++) {
if (i < 16) {
GET_UINT32_BE(W[i], data, 4 * i);
} else {
R(i);
}
P(A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], W[i], K[i]);
temp1 = A[7];
A[7] = A[6];
A[6] = A[5];
A[5] = A[4];
A[4] = A[3];
A[3] = A[2];
A[2] = A[1];
A[1] = A[0];
A[0] = temp1;
}
#else /* MINI_SHA256_SMALLER */
for (i = 0; i < 16; i++) {
GET_UINT32_BE(W[i], data, 4 * i);
}
for (i = 0; i < 16; i += 8) {
P(A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], W[i + 0], K[i + 0]);
P(A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], W[i + 1], K[i + 1]);
P(A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], W[i + 2], K[i + 2]);
P(A[5], A[6], A[7], A[0], A[1], A[2], A[3], A[4], W[i + 3], K[i + 3]);
P(A[4], A[5], A[6], A[7], A[0], A[1], A[2], A[3], W[i + 4], K[i + 4]);
P(A[3], A[4], A[5], A[6], A[7], A[0], A[1], A[2], W[i + 5], K[i + 5]);
P(A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], W[i + 6], K[i + 6]);
P(A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], W[i + 7], K[i + 7]);
}
for (i = 16; i < 64; i += 8) {
P(A[0], A[1], A[2], A[3], A[4], A[5], A[6], A[7], R(i + 0), K[i + 0]);
P(A[7], A[0], A[1], A[2], A[3], A[4], A[5], A[6], R(i + 1), K[i + 1]);
P(A[6], A[7], A[0], A[1], A[2], A[3], A[4], A[5], R(i + 2), K[i + 2]);
P(A[5], A[6], A[7], A[0], A[1], A[2], A[3], A[4], R(i + 3), K[i + 3]);
P(A[4], A[5], A[6], A[7], A[0], A[1], A[2], A[3], R(i + 4), K[i + 4]);
P(A[3], A[4], A[5], A[6], A[7], A[0], A[1], A[2], R(i + 5), K[i + 5]);
P(A[2], A[3], A[4], A[5], A[6], A[7], A[0], A[1], R(i + 6), K[i + 6]);
P(A[1], A[2], A[3], A[4], A[5], A[6], A[7], A[0], R(i + 7), K[i + 7]);
}
#endif /* MINI_SHA256_SMALLER */
for (i = 0; i < 8; i++) {
ctx->state[i] += A[i];
}
}
void core_sha256_update(core_sha256_context_t *ctx, const unsigned char *input, uint32_t ilen)
{
size_t fill;
uint32_t left;
if (ilen == 0) {
return;
}
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);
core_sha256_process(ctx, ctx->buffer);
input += fill;
ilen -= fill;
left = 0;
}
while (ilen >= 64) {
core_sha256_process(ctx, input);
input += 64;
ilen -= 64;
}
if (ilen > 0) {
memcpy((void *)(ctx->buffer + left), input, ilen);
}
}
static const unsigned char sha256_padding[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
void core_sha256_finish(core_sha256_context_t *ctx, uint8_t output[32])
{
uint32_t last, padn;
uint32_t high, low;
unsigned char msglen[8];
high = (ctx->total[0] >> 29)
| (ctx->total[1] << 3);
low = (ctx->total[0] << 3);
PUT_UINT32_BE(high, msglen, 0);
PUT_UINT32_BE(low, msglen, 4);
last = ctx->total[0] & 0x3F;
padn = (last < 56) ? (56 - last) : (120 - last);
core_sha256_update(ctx, sha256_padding, padn);
core_sha256_update(ctx, msglen, 8);
PUT_UINT32_BE(ctx->state[0], output, 0);
PUT_UINT32_BE(ctx->state[1], output, 4);
PUT_UINT32_BE(ctx->state[2], output, 8);
PUT_UINT32_BE(ctx->state[3], output, 12);
PUT_UINT32_BE(ctx->state[4], output, 16);
PUT_UINT32_BE(ctx->state[5], output, 20);
PUT_UINT32_BE(ctx->state[6], output, 24);
if (ctx->is224 == 0) {
PUT_UINT32_BE(ctx->state[7], output, 28);
}
}
void core_sha256(const uint8_t *input, uint32_t ilen, uint8_t output[32])
{
core_sha256_context_t ctx;
core_sha256_init(&ctx);
core_sha256_starts(&ctx);
core_sha256_update(&ctx, input, ilen);
core_sha256_finish(&ctx, output);
core_sha256_free(&ctx);
}
void core_hmac_sha256(const uint8_t *msg, uint32_t msg_len, const uint8_t *key, uint32_t key_len, uint8_t output[32])
{
core_sha256_context_t context;
uint8_t k_ipad[SHA256_KEY_IOPAD_SIZE]; /* inner padding - key XORd with ipad */
uint8_t k_opad[SHA256_KEY_IOPAD_SIZE]; /* outer padding - key XORd with opad */
int32_t i;
if ((NULL == msg) || (NULL == key) || (NULL == output)) {
return;
}
if (key_len > SHA256_KEY_IOPAD_SIZE) {
return;
}
/* start out by storing key in pads */
memset(k_ipad, 0, sizeof(k_ipad));
memset(k_opad, 0, sizeof(k_opad));
memcpy(k_ipad, key, key_len);
memcpy(k_opad, key, key_len);
/* XOR key with ipad and opad values */
for (i = 0; i < SHA256_KEY_IOPAD_SIZE; i++) {
k_ipad[i] ^= 0x36;
k_opad[i] ^= 0x5c;
}
/* perform inner SHA */
core_sha256_init(&context); /* init context for 1st pass */
core_sha256_starts(&context); /* setup context for 1st pass */
core_sha256_update(&context, k_ipad, SHA256_KEY_IOPAD_SIZE); /* start with inner pad */
core_sha256_update(&context, msg, msg_len); /* then text of datagram */
core_sha256_finish(&context, output); /* finish up 1st pass */
/* perform outer SHA */
core_sha256_init(&context); /* init context for 2nd pass */
core_sha256_starts(&context); /* setup context for 2nd pass */
core_sha256_update(&context, k_opad, SHA256_KEY_IOPAD_SIZE); /* start with outer pad */
core_sha256_update(&context, output, SHA256_DIGEST_SIZE); /* then results of 1st hash */
core_sha256_finish(&context, output); /* finish up 2nd pass */
}

View File

@@ -0,0 +1,83 @@
#ifndef _CORE_SHA256_H_
#define _CORE_SHA256_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include "core_stdinc.h"
#define CORE_SHA256_DIGEST_LENGTH (32)
#define CORE_SHA256_BLOCK_LENGTH (64)
#define CORE_SHA256_SHORT_BLOCK_LENGTH (CORE_SHA256_BLOCK_LENGTH - 8)
#define CORE_SHA256_DIGEST_STRING_LENGTH (CORE_SHA256_DIGEST_LENGTH * 2 + 1)
/**
* \brief SHA-256 context structure
*/
typedef struct {
uint32_t total[2]; /*!< number of bytes processed */
uint32_t state[8]; /*!< intermediate digest state */
unsigned char buffer[64]; /*!< data block being processed */
uint8_t is224; /*!< 0 => SHA-256, else SHA-224 */
} core_sha256_context_t;
/**
* \brief Initialize SHA-256 context
*
* \param ctx SHA-256 context to be initialized
*/
void core_sha256_init(core_sha256_context_t *ctx);
/**
* \brief Clear SHA-256 context
*
* \param ctx SHA-256 context to be cleared
*/
void core_sha256_free(core_sha256_context_t *ctx);
/**
* \brief SHA-256 context setup
*
* \param ctx context to be initialized
*/
void core_sha256_starts(core_sha256_context_t *ctx);
/**
* \brief SHA-256 process buffer
*
* \param ctx SHA-256 context
* \param input buffer holding the data
* \param ilen length of the input data
*/
void core_sha256_update(core_sha256_context_t *ctx, const unsigned char *input, uint32_t ilen);
/**
* \brief SHA-256 final digest
*
* \param ctx SHA-256 context
* \param output SHA-256 checksum result
*/
void core_sha256_finish(core_sha256_context_t *ctx, uint8_t output[32]);
/* Internal use */
void core_sha256_process(core_sha256_context_t *ctx, const unsigned char data[64]);
/**
* \brief Output = SHA-256( input buffer )
*
* \param input buffer holding the data
* \param ilen length of the input data
* \param output SHA-256 checksum result
*/
void core_sha256(const uint8_t *input, uint32_t ilen, uint8_t output[32]);
void core_hmac_sha256(const uint8_t *msg, uint32_t msg_len, const uint8_t *key, uint32_t key_len, uint8_t output[32]);
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,351 @@
#include "core_string.h"
int32_t core_str2uint(char *input, uint8_t input_len, uint32_t *output)
{
uint8_t index = 0;
uint32_t temp = 0;
for (index = 0; index < input_len; index++) {
if (input[index] < '0' || input[index] > '9') {
return STATE_USER_INPUT_OUT_RANGE;
}
temp = temp * 10 + input[index] - '0';
}
*output = temp;
return STATE_SUCCESS;
}
int32_t core_str2uint64(char *input, uint8_t input_len, uint64_t *output)
{
uint8_t index = 0;
uint64_t temp = 0;
for (index = 0; index < input_len; index++) {
if (input[index] < '0' || input[index] > '9') {
return STATE_USER_INPUT_OUT_RANGE;
}
temp = temp * 10 + input[index] - '0';
}
*output = temp;
return STATE_SUCCESS;
}
int32_t core_uint2str(uint32_t input, char *output, uint8_t *output_len)
{
uint8_t i = 0, j = 0;
char temp[10] = {0};
do {
temp[i++] = input % 10 + '0';
} while ((input /= 10) > 0);
do {
output[--i] = temp[j++];
} while (i > 0);
if (output_len) {
*output_len = j;
}
return STATE_SUCCESS;
}
int32_t core_int2hexstr(int32_t input, char *output, uint8_t *output_len)
{
char *lower = "0123456789abcdef";
uint8_t i = 0, j = 0;
uint8_t nagative = 0;
char temp[12] = {0};
if(input < 0){
nagative = 1;
input *= -1;
}
do {
temp[i++] = lower[input % 16];
} while ((input /= 16) > 0);
temp[i++] = 'x';
temp[i++] = '0';
if(nagative){
temp[i++] = '-';
}
do {
output[--i] = temp[j++];
} while (i > 0);
if (output_len) {
*output_len = j;
}
return STATE_SUCCESS;
}
int32_t core_uint642str(uint64_t input, char *output, uint8_t *output_len)
{
uint8_t i = 0, j = 0;
char temp[20] = {0};
do {
temp[i++] = input % 10 + '0';
} while ((input /= 10) > 0);
do {
output[--i] = temp[j++];
} while (i > 0);
if (output_len) {
*output_len = j;
}
return STATE_SUCCESS;
}
int32_t core_int2str(int32_t input, char *output, uint8_t *output_len)
{
uint8_t i = 0, j = 0, minus = 0;
char temp[11] = {0};
int64_t in = input;
if (in < 0) {
minus = 1;
in = -in;
}
do {
temp[minus + (i++)] = in % 10 + '0';
} while ((in /= 10) > 0);
do {
output[minus + (--i)] = temp[minus + (j++)];
} while (i > 0);
if (minus == 1) {
output[0] = '-';
}
if (output_len) {
*output_len = j + minus;
}
return STATE_SUCCESS;
}
int32_t core_hex2str(uint8_t *input, uint32_t input_len, char *output, uint8_t lowercase)
{
char *upper = "0123456789ABCDEF";
char *lower = "0123456789abcdef";
char *encode = upper;
int i = 0, j = 0;
if (lowercase) {
encode = lower;
}
for (i = 0; i < input_len; i++) {
output[j++] = encode[(input[i] >> 4) & 0xf];
output[j++] = encode[(input[i]) & 0xf];
}
return STATE_SUCCESS;
}
int32_t core_str2hex(char *input, uint32_t input_len, uint8_t *output)
{
uint32_t idx = 0;
if (input_len % 2 != 0) {
return STATE_USER_INPUT_OUT_RANGE;
}
for (idx = 0; idx < input_len; idx += 2) {
if (input[idx] >= '0' && input[idx] <= '9') {
output[idx / 2] = (input[idx] - '0') << 4;
} else if (input[idx] >= 'A' && input[idx] <= 'F') {
output[idx / 2] = (input[idx] - 'A' + 0x0A) << 4;
} else if (input[idx] >= 'a' && input[idx] <= 'f') {
output[idx / 2] = (input[idx] - 'a' + 0x0A) << 4;
}
if (input[idx + 1] >= '0' && input[idx + 1] <= '9') {
output[idx / 2] |= (input[idx + 1] - '0');
} else if (input[idx + 1] >= 'A' && input[idx + 1] <= 'F') {
output[idx / 2] |= (input[idx + 1] - 'A' + 0x0A);
} else if (input[idx + 1] >= 'a' && input[idx + 1] <= 'f') {
output[idx / 2] |= (input[idx + 1] - 'a' + 0x0A);
}
}
return STATE_SUCCESS;
}
int32_t core_strdup(aiot_sysdep_portfile_t *sysdep, char **dest, char *src, char *module_name)
{
if (*dest != NULL) {
sysdep->core_sysdep_free(*dest);
*dest = NULL;
}
*dest = sysdep->core_sysdep_malloc((uint32_t)(strlen(src) + 1), module_name);
if (*dest == NULL) {
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
memset(*dest, 0, strlen(src) + 1);
memcpy(*dest, src, strlen(src));
return STATE_SUCCESS;
}
int32_t core_sprintf(aiot_sysdep_portfile_t *sysdep, char **dest, char *fmt, char *src[], uint8_t count,
char *module_name)
{
char *buffer = NULL, *value = NULL;
uint8_t idx = 0, percent_idx = 0;
uint32_t buffer_len = 0;
buffer_len += strlen(fmt) - 2 * count;
for (percent_idx = 0; percent_idx < count; percent_idx++) {
value = (*(src + percent_idx) == NULL) ? ("") : (*(src + percent_idx));
buffer_len += strlen(value);
}
buffer = sysdep->core_sysdep_malloc(buffer_len + 1, module_name);
if (buffer == NULL) {
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
memset(buffer, 0, buffer_len + 1);
for (idx = 0, percent_idx = 0; idx < strlen(fmt);) {
if (fmt[idx] == '%' && fmt[idx + 1] == 's') {
value = (*(src + percent_idx) == NULL) ? ("") : (*(src + percent_idx));
memcpy(buffer + strlen(buffer), value, strlen(value));
percent_idx++;
idx += 2;
} else {
buffer[strlen(buffer)] = fmt[idx++];
}
}
*dest = buffer;
return STATE_SUCCESS;
}
int32_t core_json_value(const char *input, uint32_t input_len, const char *key, uint32_t key_len, char **value,
uint32_t *value_len)
{
uint32_t idx = 0;
for (idx = 0; idx < input_len; idx++) {
if (idx + key_len >= input_len) {
return STATE_USER_INPUT_JSON_PARSE_FAILED;
}
if ((memcmp(&input[idx], key, key_len) == 0) &&
((idx > 0) && (input[idx - 1] == '"')) &&
((idx + key_len < input_len) && (input[idx + key_len] == '"'))) {
idx += key_len;
/* shortest ":x, or ":x} or ":x] */
if ((idx + 2 >= input_len) ||
(input[idx + 1] != ':')) {
return STATE_USER_INPUT_JSON_PARSE_FAILED;
}
idx += 2;
if (input[idx] == '"') {
*value = (char *)&input[++idx];
for (; idx < input_len; idx++) {
if ((input[idx] == '"')) {
*value_len = (uint32_t)(idx - (*value - input));
return STATE_SUCCESS;
}
}
} else if (input[idx] == '{' || input[idx] == '[') {
char start = input[idx];
char end = (start == '{') ? ('}') : (']');
uint8_t count = 0;
*value = (char *)&input[idx];
for (; idx < input_len; idx++) {
if ((input[idx] == start)) {
count++;
} else if ((input[idx] == end)) {
if (--count == 0) {
*value_len = (uint32_t)(idx - (*value - input) + 1);
return STATE_SUCCESS;
}
}
}
} else {
*value = (char *)&input[idx];
for (; idx < input_len; idx++) {
if ((input[idx] == ',' || input[idx] == ']' || input[idx] == '}')) {
*value_len = (uint32_t)(idx - (*value - input));
return STATE_SUCCESS;
}
}
}
}
}
return STATE_USER_INPUT_JSON_PARSE_FAILED;
}
int32_t core_utc2date(uint64_t utc, int8_t zone, core_date_t *date)
{
uint32_t day_sec = 0, day_num = 0;
uint64_t utc_zone_s = 0;
#define DAYS_IN_YEAR(year) (IS_LEAP_YEAR(year) ? 366 : 365)
#define IS_LEAP_YEAR(year) (!((year) % 400) || (((year) % 100) && !((year) % 4)))
#define DAYS_IN_MONTH(lpyr, mon) ((mon == 1)?(28 + lpyr):((mon > 6)?(((mon - 1) & 1)?(30):(31)):((mon & 1)?(30):(31))))
date->msec = utc % 1000;
utc_zone_s = (utc / 1000) + (zone * 60 * 60);
day_sec = utc_zone_s % (24 * 60 * 60);
day_num = utc_zone_s / (24 * 60 * 60);
date->sec = day_sec % 60;
date->min = (day_sec % 3600) / 60;
date->hour = day_sec / 3600;
date->year = 1970;
while (day_num >= DAYS_IN_YEAR(date->year)) {
day_num -= DAYS_IN_YEAR(date->year);
date->year++;
}
date->mon = 0;
while (day_num >= DAYS_IN_MONTH(IS_LEAP_YEAR(date->year), date->mon)) {
day_num -= DAYS_IN_MONTH(IS_LEAP_YEAR(date->year), date->mon);
date->mon++;
}
date->mon++;
date->day = day_num + 1;
return 0;
}
#if 0
int32_t core_strcat(aiot_sysdep_portfile_t *sysdep, char **dest, char *src1, char *src2, char *module_name)
{
uint32_t dest_len = 0;
char *tmp_dest = NULL;
dest_len = strlen(src1) + strlen(src2) + 1;
tmp_dest = sysdep->core_sysdep_malloc(dest_len, module_name);
if (tmp_dest == NULL) {
return STATE_SYS_DEPEND_MALLOC_FAILED;
}
memset(tmp_dest, 0, dest_len);
memcpy(tmp_dest + strlen(tmp_dest), src1, strlen(src1));
memcpy(tmp_dest + strlen(tmp_dest), src2, strlen(src2));
if (*dest != NULL) {
sysdep->core_sysdep_free(*dest);
*dest = NULL;
}
*dest = tmp_dest;
return STATE_SUCCESS;
}
#endif

View File

@@ -0,0 +1,43 @@
#ifndef _CORE_STRING_H_
#define _CORE_STRING_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include "core_stdinc.h"
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
typedef struct {
uint32_t year;
uint32_t mon;
uint32_t day;
uint32_t hour;
uint32_t min;
uint32_t sec;
uint32_t msec;
} core_date_t;
int32_t core_str2uint(char *input, uint8_t input_len, uint32_t *output);
int32_t core_str2uint64(char *input, uint8_t input_len, uint64_t *output);
int32_t core_uint2str(uint32_t input, char *output, uint8_t *output_len);
int32_t core_uint642str(uint64_t input, char *output, uint8_t *output_len);
int32_t core_int2str(int32_t input, char *output, uint8_t *output_len);
int32_t core_int2hexstr(int32_t input, char *output, uint8_t *output_len);
int32_t core_hex2str(uint8_t *input, uint32_t input_len, char *output, uint8_t lowercase);
int32_t core_str2hex(char *input, uint32_t input_len, uint8_t *output);
int32_t core_strdup(aiot_sysdep_portfile_t *sysdep, char **dest, char *src, char *module_name);
int32_t core_sprintf(aiot_sysdep_portfile_t *sysdep, char **dest, char *fmt, char *src[], uint8_t count,
char *module_name);
int32_t core_json_value(const char *input, uint32_t input_len, const char *key, uint32_t key_len, char **value,
uint32_t *value_len);
int32_t core_utc2date(uint64_t utc, int8_t zone, core_date_t *date);
int32_t core_strcat(aiot_sysdep_portfile_t *sysdep, char **dest, char *src1, char *src2, char *module_name);
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,330 @@
/*
* 这个例子主要用于演示如何从设备端获取存储在云端的关于这个设备的远程配置
*
* 需要用户关注或修改的部分, 已用 `TODO` 在注释中标明
*
*/
/* TODO: 本例子用到了sleep函数, 所以包含了unistd.h. 如果用户自己的库中有可以替代的函数, 则可以将unistd.h替换掉
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
#include "aiot_ota_api.h"
/* TODO: 替换为自己设备的三元组 */
char *product_key = "${YourProductKey}";
char *device_name = "${YourDeviceName}";
char *device_secret = "${YourDeviceSecret}";
/*
TODO: 替换为自己实例的接入点
对于企业实例, 或者2021年07月30日之后含当日开通的物联网平台服务下公共实例
mqtt_host的格式为"${YourInstanceId}.mqtt.iothub.aliyuncs.com"
其中${YourInstanceId}: 请替换为您企业/公共实例的Id
对于2021年07月30日之前不含当日开通的物联网平台服务下公共实例
需要将mqtt_host修改为: mqtt_host = "${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com"
其中, ${YourProductKey}请替换为设备所属产品的ProductKey。可登录物联网平台控制台在对应实例的设备详情页获取。
${YourRegionId}:请替换为您的物联网平台设备所在地域代码, 比如cn-shanghai等
该情况下完整mqtt_host举例: a1TTmBPIChA.iot-as-mqtt.cn-shanghai.aliyuncs.com
详情请见: https://help.aliyun.com/document_detail/147356.html
*/
char *mqtt_host = "${YourInstanceId}.mqtt.iothub.aliyuncs.com";
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
void *g_dl_handle = NULL;
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
*
* 例如: [1578463098.611][LK-0309] pub: /ota/device/upgrade/a13FN5TplKq/ota_demo
*
* 上面这条日志的code就是0309(十六进制), code值的定义见core/aiot_state_api.h
*
*/
/* 日志回调函数, SDK的日志会从这里输出 */
int32_t demo_state_logcb(int32_t code, char *message)
{
/* 下载远程配置的时候会有大量的HTTP收包日志, 通过code筛选出来关闭 */
if (STATE_HTTP_LOG_RECV_CONTENT != code) {
printf("%s", message);
}
return 0;
}
/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *const event, void *userdata)
{
switch (event->type) {
/* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */
case AIOT_MQTTEVT_CONNECT: {
printf("AIOT_MQTTEVT_CONNECT\r\n");
/* TODO: 处理SDK建连成功, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
/* SDK因为网络状况被动断连后, 自动发起重连已成功 */
case AIOT_MQTTEVT_RECONNECT: {
printf("AIOT_MQTTEVT_RECONNECT\r\n");
/* TODO: 处理SDK重连成功, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
/* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */
case AIOT_MQTTEVT_DISCONNECT: {
char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
("heartbeat disconnect");
printf("AIOT_MQTTEVT_DISCONNECT: %s\r\n", cause);
/* TODO: 处理SDK被动断连, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
default: {
}
}
}
/* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */
void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *const packet, void *userdata)
{
switch (packet->type) {
case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: {
printf("heartbeat response\r\n");
/* TODO: 处理服务器对心跳的回应, 一般不处理 */
}
break;
case AIOT_MQTTRECV_SUB_ACK: {
printf("suback, res: -0x%04X, packet id: %d, max qos: %d\r\n",
-packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);
/* TODO: 处理服务器对订阅请求的回应, 一般不处理 */
}
break;
case AIOT_MQTTRECV_PUB: {
printf("pub, qos: %d, topic: %.*s\r\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic);
printf("pub, payload: %.*s\r\n", packet->data.pub.payload_len, packet->data.pub.payload);
/* TODO: 处理服务器下发的业务报文 */
}
break;
case AIOT_MQTTRECV_PUB_ACK: {
printf("puback, packet id: %d\r\n", packet->data.pub_ack.packet_id);
/* TODO: 处理服务器对QoS1上报消息的回应, 一般不处理 */
}
break;
default: {
}
}
}
/* 下载收包回调, 用户调用 aiot_download_recv() 后, SDK收到数据会进入这个函数, 把下载到的数据交给用户 */
/* TODO: 在用户收到远程配置的数据后, 需要自己决定如何处理 */
void demo_download_recv_handler(void *handle, const aiot_download_recv_t *packet, void *userdata)
{
/* 目前只支持 packet->type 为 AIOT_DLRECV_HTTPBODY 的情况 */
if (!packet || AIOT_DLRECV_HTTPBODY != packet->type) {
return;
}
int32_t percent = packet->data.percent;
uint8_t *src_buffer = packet->data.buffer;
uint32_t src_buffer_len = packet->data.len;
/* 如果 percent 为负数, 说明发生了收包异常或者digest校验错误 */
if (percent < 0) {
/* 收包异常或者digest校验错误 */
printf("exception happend, percent is %d\r\n", percent);
return;
}
aiot_download_report_progress(handle, percent);
printf("config len is %d, config content is %.*s\r\n", src_buffer_len, src_buffer_len, (char *)src_buffer);
}
/* 用户通过 aiot_ota_setopt() 注册的OTA消息处理回调, 如果SDK收到了OTA相关的MQTT消息, 会自动识别, 调用这个回调函数 */
void demo_ota_recv_handler(void *ota_handle, aiot_ota_recv_t *ota_msg, void *userdata)
{
switch (ota_msg->type) {
case AIOT_OTARECV_COTA: {
uint16_t port = 443;
uint32_t max_buffer_len = 2048;
aiot_sysdep_network_cred_t cred;
void *dl_handle = aiot_download_init();
if (NULL == dl_handle || NULL == ota_msg->task_desc || ota_msg->task_desc->protocol_type != AIOT_OTA_PROTOCOL_HTTPS) {
return;
}
printf("configId: %s, configSize: %u Bytes\r\n", ota_msg->task_desc->version,
ota_msg->task_desc->size_total);
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
cred.option = AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA;
cred.max_tls_fragment = 16384;
cred.x509_server_cert = ali_ca_cert;
cred.x509_server_cert_len = strlen(ali_ca_cert);
/* 设置下载时为TLS下载 */
if ((STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_CRED, (void *)(&cred))) ||
/* 设置下载时访问的服务器端口号 */
(STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_PORT, (void *)(&port))) ||
/* 设置下载的任务信息, 通过输入参数 ota_msg 中的 task_desc 成员得到, 内含下载地址, 远程配置的大小和版本号*/
(STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_TASK_DESC, (void *)(ota_msg->task_desc))) ||
/* 设置下载内容到达时, SDK将调用的回调函数 */
(STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_RECV_HANDLER, (void *)(demo_download_recv_handler))) ||
/* 设置单次下载最大的buffer长度, 每当这个长度的内存读满了后会通知用户 */
(STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_BODY_BUFFER_MAX_LEN, (void *)(&max_buffer_len))) ||
/* 发送http的GET请求给http服务器 */
(STATE_SUCCESS != aiot_download_send_request(dl_handle))) {
aiot_download_deinit(&dl_handle);
break;
}
g_dl_handle = dl_handle;
break;
}
default:
break;
}
}
int main(int argc, char *argv[])
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
uint16_t port = 443; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
void *ota_handle = NULL;
uint32_t timeout_ms = 0;
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(demo_state_logcb);
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
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根证书长度 */
/* 创建1个MQTT客户端实例并内部初始化默认参数 */
mqtt_handle = aiot_mqtt_init();
if (mqtt_handle == NULL) {
return -1;
}
/* 配置MQTT服务器地址 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);
/* 配置MQTT服务器端口 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
/* 配置设备productKey */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
/* 配置设备deviceName */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
/* 配置设备deviceSecret */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
/* 配置网络连接的安全凭据, 上面已经创建好了 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
/* 配置MQTT默认消息接收回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler);
/* 配置MQTT事件回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);
/* 与MQTT例程不同的是, 这里需要增加创建OTA会话实例的语句 */
ota_handle = aiot_ota_init();
if (NULL == ota_handle) {
goto exit;
}
/* 用以下语句, 把OTA会话和MQTT会话关联起来 */
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_MQTT_HANDLE, mqtt_handle);
/* 用以下语句, 设置OTA会话的数据接收回调, SDK收到OTA相关推送时, 会进入这个回调函数 */
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_RECV_HANDLER, demo_ota_recv_handler);
/* 与服务器建立MQTT连接 */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_connect failed: -0x%04X\r\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
goto exit;
}
/* 发送请求, 获取云端的远程配置. TODO:替换自己设备的productKey, deviceName */
/*
{
char *topic_string = "/sys/{YourProductKey}/{YourDeviceName}/thing/config/get";
char *payload_string = "{\"id\":\"123\",\"params\":{\"configScope\":\"product\",\"getType\":\"file\"}}";
res = aiot_mqtt_pub(mqtt_handle, topic_string, (uint8_t *)payload_string, strlen(payload_string), 0);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_pub failed: -0x%04X\r\n", -res);
goto exit;
}
}
*/
while (1) {
aiot_mqtt_process(mqtt_handle);
res = aiot_mqtt_recv(mqtt_handle);
if (res == STATE_SYS_DEPEND_NWK_CLOSED) {
sleep(1);
}
if (NULL != g_dl_handle) {
/* 完成远程配置的接收前, 将mqtt的收包超时调整到100ms, 以减少两次远程配置的下载间隔*/
int32_t ret = aiot_download_recv(g_dl_handle);
timeout_ms = 100;
if (STATE_DOWNLOAD_FINISHED == ret) {
aiot_download_deinit(&g_dl_handle);
/* 完成远程配置的接收后, 将mqtt的收包超时调整回到默认值5000ms */
timeout_ms = 5000;
}
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_TIMEOUT_MS, (void *)&timeout_ms);
}
}
/* 断开MQTT连接, 一般不会运行到这里 */
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_disconnect failed: -0x%04X\r\n", -res);
goto exit;
}
exit:
while (1) {
/* 销毁MQTT实例, 一般不会运行到这里 */
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x%04X\r\n", -res);
return -1;
} else {
break;
}
}
/* 销毁OTA实例, 一般不会运行到这里 */
aiot_ota_deinit(&ota_handle);
return 0;
}

View File

@@ -0,0 +1,533 @@
/*
* 这个例程适用于`Linux`这类支持pthread的POSIX设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建2个线程
*
* + 一个线程用于保活长连接
* + 一个线程用于接收消息, 并在有消息到达时进入默认的数据回调, 在连接状态变化时进入事件回调
*
* 接着演示了在MQTT连接上进行属性上报, 事件上报, 以及处理收到的属性设置, 服务调用, 取消这些代码段落的注释即可观察运行效果
*
* 需要用户关注或修改的部分, 已经用 TODO 在注释中标明
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
#include "aiot_dm_api.h"
/* TODO: 替换为自己设备的三元组 */
char *product_key = "${YourProductKey}";
char *device_name = "${YourDeviceName}";
char *device_secret = "${YourDeviceSecret}";
/*
TODO: 替换为自己实例的接入点
对于企业实例, 或者2021年07月30日之后含当日开通的物联网平台服务下公共实例
mqtt_host的格式为"${YourInstanceId}.mqtt.iothub.aliyuncs.com"
其中${YourInstanceId}: 请替换为您企业/公共实例的Id
对于2021年07月30日之前不含当日开通的物联网平台服务下公共实例
需要将mqtt_host修改为: mqtt_host = "${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com"
其中, ${YourProductKey}请替换为设备所属产品的ProductKey。可登录物联网平台控制台在对应实例的设备详情页获取。
${YourRegionId}:请替换为您的物联网平台设备所在地域代码, 比如cn-shanghai等
该情况下完整mqtt_host举例: a1TTmBPIChA.iot-as-mqtt.cn-shanghai.aliyuncs.com
详情请见: https://help.aliyun.com/document_detail/147356.html
*/
char *mqtt_host = "${YourInstanceId}.mqtt.iothub.aliyuncs.com";
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
static pthread_t g_mqtt_process_thread;
static pthread_t g_mqtt_recv_thread;
static uint8_t g_mqtt_process_thread_running = 0;
static uint8_t g_mqtt_recv_thread_running = 0;
/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
*
* 上面这条日志的code就是0317(十六进制), code值的定义见core/aiot_state_api.h
*
*/
/* 日志回调函数, SDK的日志会从这里输出 */
int32_t demo_state_logcb(int32_t code, char *message)
{
printf("%s", message);
return 0;
}
/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata)
{
switch (event->type) {
/* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */
case AIOT_MQTTEVT_CONNECT: {
printf("AIOT_MQTTEVT_CONNECT\n");
}
break;
/* SDK因为网络状况被动断连后, 自动发起重连已成功 */
case AIOT_MQTTEVT_RECONNECT: {
printf("AIOT_MQTTEVT_RECONNECT\n");
}
break;
/* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */
case AIOT_MQTTEVT_DISCONNECT: {
char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
("heartbeat disconnect");
printf("AIOT_MQTTEVT_DISCONNECT: %s\n", cause);
}
break;
default: {
}
}
}
/* 执行aiot_mqtt_process的线程, 包含心跳发送和QoS1消息重发 */
void *demo_mqtt_process_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_process_thread_running) {
res = aiot_mqtt_process(args);
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
return NULL;
}
/* 执行aiot_mqtt_recv的线程, 包含网络自动重连和从服务器收取MQTT消息 */
void *demo_mqtt_recv_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_recv_thread_running) {
res = aiot_mqtt_recv(args);
if (res < STATE_SUCCESS) {
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
}
return NULL;
}
static void demo_dm_recv_generic_reply(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
{
printf("demo_dm_recv_generic_reply msg_id = %d, code = %d, data = %.*s, message = %.*s\r\n",
recv->data.generic_reply.msg_id,
recv->data.generic_reply.code,
recv->data.generic_reply.data_len,
recv->data.generic_reply.data,
recv->data.generic_reply.message_len,
recv->data.generic_reply.message);
}
static void demo_dm_recv_property_set(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
{
printf("demo_dm_recv_property_set msg_id = %ld, params = %.*s\r\n",
(unsigned long)recv->data.property_set.msg_id,
recv->data.property_set.params_len,
recv->data.property_set.params);
/* TODO: 以下代码演示如何对来自云平台的属性设置指令进行应答, 用户可取消注释查看演示效果 */
/*
{
aiot_dm_msg_t msg;
memset(&msg, 0, sizeof(aiot_dm_msg_t));
msg.type = AIOT_DMMSG_PROPERTY_SET_REPLY;
msg.data.property_set_reply.msg_id = recv->data.property_set.msg_id;
msg.data.property_set_reply.code = 200;
msg.data.property_set_reply.data = "{}";
int32_t res = aiot_dm_send(dm_handle, &msg);
if (res < 0) {
printf("aiot_dm_send failed\r\n");
}
}
*/
}
static void demo_dm_recv_async_service_invoke(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
{
printf("demo_dm_recv_async_service_invoke msg_id = %ld, service_id = %s, params = %.*s\r\n",
(unsigned long)recv->data.async_service_invoke.msg_id,
recv->data.async_service_invoke.service_id,
recv->data.async_service_invoke.params_len,
recv->data.async_service_invoke.params);
/* TODO: 以下代码演示如何对来自云平台的异步服务调用进行应答, 用户可取消注释查看演示效果
*
* 注意: 如果用户在回调函数外进行应答, 需要自行保存msg_id, 因为回调函数入参在退出回调函数后将被SDK销毁, 不可以再访问到
*/
/*
{
aiot_dm_msg_t msg;
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 = "ToggleLightSwitch";
msg.data.async_service_reply.data = "{\"dataA\": 20}";
int32_t res = aiot_dm_send(dm_handle, &msg);
if (res < 0) {
printf("aiot_dm_send failed\r\n");
}
}
*/
}
static void demo_dm_recv_sync_service_invoke(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
{
printf("demo_dm_recv_sync_service_invoke msg_id = %ld, rrpc_id = %s, service_id = %s, params = %.*s\r\n",
(unsigned long)recv->data.sync_service_invoke.msg_id,
recv->data.sync_service_invoke.rrpc_id,
recv->data.sync_service_invoke.service_id,
recv->data.sync_service_invoke.params_len,
recv->data.sync_service_invoke.params);
/* TODO: 以下代码演示如何对来自云平台的同步服务调用进行应答, 用户可取消注释查看演示效果
*
* 注意: 如果用户在回调函数外进行应答, 需要自行保存msg_id和rrpc_id字符串, 因为回调函数入参在退出回调函数后将被SDK销毁, 不可以再访问到
*/
/*
{
aiot_dm_msg_t msg;
memset(&msg, 0, sizeof(aiot_dm_msg_t));
msg.type = AIOT_DMMSG_SYNC_SERVICE_REPLY;
msg.data.sync_service_reply.rrpc_id = recv->data.sync_service_invoke.rrpc_id;
msg.data.sync_service_reply.msg_id = recv->data.sync_service_invoke.msg_id;
msg.data.sync_service_reply.code = 200;
msg.data.sync_service_reply.service_id = "SetLightSwitchTimer";
msg.data.sync_service_reply.data = "{}";
int32_t res = aiot_dm_send(dm_handle, &msg);
if (res < 0) {
printf("aiot_dm_send failed\r\n");
}
}
*/
}
static void demo_dm_recv_raw_data(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
{
printf("demo_dm_recv_raw_data raw data len = %d\r\n", recv->data.raw_data.data_len);
/* TODO: 以下代码演示如何发送二进制格式数据, 若使用需要有相应的数据透传脚本部署在云端 */
/*
{
aiot_dm_msg_t msg;
uint8_t raw_data[] = {0x01, 0x02};
memset(&msg, 0, sizeof(aiot_dm_msg_t));
msg.type = AIOT_DMMSG_RAW_DATA;
msg.data.raw_data.data = raw_data;
msg.data.raw_data.data_len = sizeof(raw_data);
aiot_dm_send(dm_handle, &msg);
}
*/
}
static void demo_dm_recv_raw_sync_service_invoke(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
{
printf("demo_dm_recv_raw_sync_service_invoke raw sync service rrpc_id = %s, data_len = %d\r\n",
recv->data.raw_service_invoke.rrpc_id,
recv->data.raw_service_invoke.data_len);
}
static void demo_dm_recv_raw_data_reply(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
{
printf("demo_dm_recv_raw_data_reply receive reply for up_raw msg, data len = %d\r\n", recv->data.raw_data.data_len);
/* TODO: 用户处理下行的二进制数据, 位于recv->data.raw_data.data中 */
}
/* 用户数据接收处理回调函数 */
static void demo_dm_recv_handler(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
{
printf("demo_dm_recv_handler, type = %d\r\n", recv->type);
switch (recv->type) {
/* 属性上报, 事件上报, 获取期望属性值或者删除期望属性值的应答 */
case AIOT_DMRECV_GENERIC_REPLY: {
demo_dm_recv_generic_reply(dm_handle, recv, userdata);
}
break;
/* 属性设置 */
case AIOT_DMRECV_PROPERTY_SET: {
demo_dm_recv_property_set(dm_handle, recv, userdata);
}
break;
/* 异步服务调用 */
case AIOT_DMRECV_ASYNC_SERVICE_INVOKE: {
demo_dm_recv_async_service_invoke(dm_handle, recv, userdata);
}
break;
/* 同步服务调用 */
case AIOT_DMRECV_SYNC_SERVICE_INVOKE: {
demo_dm_recv_sync_service_invoke(dm_handle, recv, userdata);
}
break;
/* 下行二进制数据 */
case AIOT_DMRECV_RAW_DATA: {
demo_dm_recv_raw_data(dm_handle, recv, userdata);
}
break;
/* 二进制格式的同步服务调用, 比单纯的二进制数据消息多了个rrpc_id */
case AIOT_DMRECV_RAW_SYNC_SERVICE_INVOKE: {
demo_dm_recv_raw_sync_service_invoke(dm_handle, recv, userdata);
}
break;
/* 上行二进制数据后, 云端的回复报文 */
case AIOT_DMRECV_RAW_DATA_REPLY: {
demo_dm_recv_raw_data_reply(dm_handle, recv, userdata);
}
break;
default:
break;
}
}
/* 属性上报函数演示 */
int32_t demo_send_property_post(void *dm_handle, char *params)
{
aiot_dm_msg_t msg;
memset(&msg, 0, sizeof(aiot_dm_msg_t));
msg.type = AIOT_DMMSG_PROPERTY_POST;
msg.data.property_post.params = params;
return aiot_dm_send(dm_handle, &msg);
}
int32_t demo_send_property_batch_post(void *dm_handle, char *params)
{
aiot_dm_msg_t msg;
memset(&msg, 0, sizeof(aiot_dm_msg_t));
msg.type = AIOT_DMMSG_PROPERTY_BATCH_POST;
msg.data.property_post.params = params;
return aiot_dm_send(dm_handle, &msg);
}
/* 事件上报函数演示 */
int32_t demo_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);
}
/* 演示了获取属性LightSwitch的期望值, 用户可将此函数加入到main函数中运行演示 */
int32_t demo_send_get_desred_requset(void *dm_handle)
{
aiot_dm_msg_t msg;
memset(&msg, 0, sizeof(aiot_dm_msg_t));
msg.type = AIOT_DMMSG_GET_DESIRED;
msg.data.get_desired.params = "[\"LightSwitch\"]";
return aiot_dm_send(dm_handle, &msg);
}
/* 演示了删除属性LightSwitch的期望值, 用户可将此函数加入到main函数中运行演示 */
int32_t demo_send_delete_desred_requset(void *dm_handle)
{
aiot_dm_msg_t msg;
memset(&msg, 0, sizeof(aiot_dm_msg_t));
msg.type = AIOT_DMMSG_DELETE_DESIRED;
msg.data.get_desired.params = "{\"LightSwitch\":{}}";
return aiot_dm_send(dm_handle, &msg);
}
int main(int argc, char *argv[])
{
int32_t res = STATE_SUCCESS;
void *dm_handle = NULL;
void *mqtt_handle = NULL;
uint16_t port = 443; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
uint8_t post_reply = 1;
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(demo_state_logcb);
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
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根证书长度 */
/* 创建1个MQTT客户端实例并内部初始化默认参数 */
mqtt_handle = aiot_mqtt_init();
if (mqtt_handle == NULL) {
printf("aiot_mqtt_init failed\n");
return -1;
}
/* 配置MQTT服务器地址 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);
/* 配置MQTT服务器端口 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
/* 配置设备productKey */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
/* 配置设备deviceName */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
/* 配置设备deviceSecret */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
/* 配置网络连接的安全凭据, 上面已经创建好了 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
/* 配置MQTT事件回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);
/* 创建DATA-MODEL实例 */
dm_handle = aiot_dm_init();
if (dm_handle == NULL) {
printf("aiot_dm_init failed");
return -1;
}
/* 配置MQTT实例句柄 */
aiot_dm_setopt(dm_handle, AIOT_DMOPT_MQTT_HANDLE, mqtt_handle);
/* 配置消息接收处理回调函数 */
aiot_dm_setopt(dm_handle, AIOT_DMOPT_RECV_HANDLER, (void *)demo_dm_recv_handler);
/* 配置是云端否需要回复post_reply给设备. 如果为1, 表示需要云端回复, 否则表示不回复 */
aiot_dm_setopt(dm_handle, AIOT_DMOPT_POST_REPLY, (void *)&post_reply);
/* 与服务器建立MQTT连接 */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
aiot_dm_deinit(&dm_handle);
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_connect failed: -0x%04X\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
return -1;
}
/* 向服务器订阅property/batch/post_reply这个topic */
aiot_mqtt_sub(mqtt_handle, "/sys/${YourProductKey}/${YourDeviceName}/thing/event/property/batch/post_reply", NULL, 1,
NULL);
/* 创建一个单独的线程, 专用于执行aiot_mqtt_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */
g_mqtt_process_thread_running = 1;
res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_process_thread failed: %d\n", res);
aiot_dm_deinit(&dm_handle);
aiot_mqtt_disconnect(mqtt_handle);
aiot_mqtt_deinit(&mqtt_handle);
return -1;
}
/* 创建一个单独的线程用于执行aiot_mqtt_recv, 它会循环收取服务器下发的MQTT消息, 并在断线时自动重连 */
g_mqtt_recv_thread_running = 1;
res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_recv_thread failed: %d\n", res);
aiot_dm_deinit(&dm_handle);
aiot_mqtt_disconnect(mqtt_handle);
aiot_mqtt_deinit(&mqtt_handle);
return -1;
}
/* 主循环进入休眠 */
while (1) {
/* TODO: 以下代码演示了简单的属性上报和事件上报, 用户可取消注释观察演示效果 */
demo_send_property_post(dm_handle, "{\"LightSwitch\": 0}");
/*
demo_send_event_post(dm_handle, "Error", "{\"ErrorCode\": 0}");
*/
/* TODO: 以下代码演示了基于模块的物模型的上报, 用户可取消注释观察演示效果
* 本例需要用户在产品的功能定义的页面中, 点击"编辑草稿", 增加一个名为demo_extra_block的模块,
* 再到该模块中, 通过添加标准功能, 选择一个名为NightLightSwitch的物模型属性, 再点击"发布上线".
* 有关模块化的物模型的概念, 请见 https://help.aliyun.com/document_detail/73727.html
*/
/*
demo_send_property_post(dm_handle, "{\"demo_extra_block:NightLightSwitch\": 1}");
*/
/* TODO: 以下代码显示批量上报用户数据, 用户可取消注释观察演示效果
* 具体数据格式请见https://help.aliyun.com/document_detail/89301.html 的"设备批量上报属性、事件"一节
*/
/*
demo_send_property_batch_post(dm_handle,
"{\"properties\":{\"Power\": [ {\"value\":\"on\",\"time\":1612684518}],\"WF\": [{\"value\": 3,\"time\":1612684518}]}}");
*/
sleep(5);
}
/* 停止收发动作 */
g_mqtt_process_thread_running = 0;
g_mqtt_recv_thread_running = 0;
/* 断开MQTT连接, 一般不会运行到这里 */
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_dm_deinit(&dm_handle);
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);
return -1;
}
/* 销毁DATA-MODEL实例, 一般不会运行到这里 */
res = aiot_dm_deinit(&dm_handle);
if (res < STATE_SUCCESS) {
printf("aiot_dm_deinit failed: -0x%04X\n", -res);
return -1;
}
/* 销毁MQTT实例, 一般不会运行到这里 */
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x%04X\n", -res);
return -1;
}
pthread_join(g_mqtt_process_thread, NULL);
pthread_join(g_mqtt_recv_thread, NULL);
return 0;
}

View File

@@ -0,0 +1,432 @@
/*
* 这个例程适用于`Linux`这类支持pthread的POSIX设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建2个线程
*
* + 一个线程用于保活长连接
* + 一个线程用于接收消息, 并在有消息到达时进入默认的数据回调, 在连接状态变化时进入事件回调
*
* 接着在MQTT连接上发送设备标签更新或删除请求, 如果云平台的回应报文到达, 从接收线程会调用devinfo消息处理的回调函数, 把上报结果打印出来
*
* 需要用户关注或修改的部分, 已经用 TODO 在注释中标明
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
#include "aiot_devinfo_api.h"
/* TODO: 替换为自己设备的三元组 */
char *product_key = "${YourProductKey}";
char *device_name = "${YourDeviceName}";
char *device_secret = "${YourDeviceSecret}";
/*
TODO: 替换为自己实例的接入点
对于企业实例, 或者2021年07月30日之后含当日开通的物联网平台服务下公共实例
mqtt_host的格式为"${YourInstanceId}.mqtt.iothub.aliyuncs.com"
其中${YourInstanceId}: 请替换为您企业/公共实例的Id
对于2021年07月30日之前不含当日开通的物联网平台服务下公共实例
需要将mqtt_host修改为: mqtt_host = "${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com"
其中, ${YourProductKey}请替换为设备所属产品的ProductKey。可登录物联网平台控制台在对应实例的设备详情页获取。
${YourRegionId}:请替换为您的物联网平台设备所在地域代码, 比如cn-shanghai等
该情况下完整mqtt_host举例: a1TTmBPIChA.iot-as-mqtt.cn-shanghai.aliyuncs.com
详情请见: https://help.aliyun.com/document_detail/147356.html
*/
char *mqtt_host = "${YourInstanceId}.mqtt.iothub.aliyuncs.com";
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
static pthread_t g_mqtt_process_thread;
static pthread_t g_mqtt_recv_thread;
static uint8_t g_mqtt_process_thread_running = 0;
static uint8_t g_mqtt_recv_thread_running = 0;
/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
*
* 例如: [1581501698.455][LK-0309] pub: /sys/gb80sFmX7yX/devinfo_basic_demo/thing/deviceinfo/update
*
* 上面这条日志的code就是0309(十六进制), code值的定义见core/aiot_state_api.h
*
*/
/* 日志回调函数, SDK的日志会从这里输出 */
static int32_t demo_state_logcb(int32_t code, char *message)
{
printf("%s", message);
return 0;
}
/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata)
{
switch (event->type) {
/* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */
case AIOT_MQTTEVT_CONNECT: {
printf("AIOT_MQTTEVT_CONNECT\n");
}
break;
/* SDK因为网络状况被动断连后, 自动发起重连已成功 */
case AIOT_MQTTEVT_RECONNECT: {
printf("AIOT_MQTTEVT_RECONNECT\n");
}
break;
/* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */
case AIOT_MQTTEVT_DISCONNECT: {
char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
("heartbeat disconnect");
printf("AIOT_MQTTEVT_DISCONNECT: %s\n", cause);
}
break;
default: {
}
}
}
/* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */
void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata)
{
switch (packet->type) {
case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: {
printf("heartbeat response\n");
}
break;
case AIOT_MQTTRECV_SUB_ACK: {
printf("suback, res: -0x%04X, packet id: %d, max qos: %d\n",
-packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);
}
break;
case AIOT_MQTTRECV_PUB: {
printf("pub, qos: %d, topic: %.*s\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic);
printf("pub, payload: %.*s\n", packet->data.pub.payload_len, packet->data.pub.payload);
}
break;
case AIOT_MQTTRECV_PUB_ACK: {
printf("puback, packet id: %d\n", packet->data.pub_ack.packet_id);
}
break;
default: {
}
}
}
/* 执行aiot_mqtt_process的线程, 包含心跳发送和QoS1消息重发 */
void *demo_mqtt_process_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_process_thread_running) {
res = aiot_mqtt_process(args);
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
return NULL;
}
/* 执行aiot_mqtt_recv的线程, 包含网络自动重连和从服务器收取MQTT消息 */
void *demo_mqtt_recv_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_recv_thread_running) {
res = aiot_mqtt_recv(args);
if (res < STATE_SUCCESS) {
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
}
return NULL;
}
int32_t demo_mqtt_start(void **handle, char *product_key, char *device_name, char *device_secret)
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
uint16_t port = 443; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(demo_state_logcb);
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
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根证书长度 */
/* 创建1个MQTT客户端实例并内部初始化默认参数 */
mqtt_handle = aiot_mqtt_init();
if (mqtt_handle == NULL) {
printf("aiot_mqtt_init failed\n");
return -1;
}
/* 如果以下代码不被注释, 则例程会用TCP而不是TLS连接云平台 */
/*
{
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
cred.option = AIOT_SYSDEP_NETWORK_CRED_NONE;
}
*/
/* 配置MQTT服务器地址 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);
/* 配置MQTT服务器端口 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
/* 配置设备productKey */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
/* 配置设备deviceName */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
/* 配置设备deviceSecret */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
/* 配置网络连接的安全凭据, 上面已经创建好了 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
/* 配置MQTT默认消息接收回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler);
/* 配置MQTT事件回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);
/* 与服务器建立MQTT连接 */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_connect failed: -0x%04X\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
return -1;
}
/* 创建一个单独的线程, 专用于执行aiot_mqtt_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */
g_mqtt_process_thread_running = 1;
res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_process_thread failed: %d\n", res);
aiot_mqtt_deinit(&mqtt_handle);
return -1;
}
/* 创建一个单独的线程用于执行aiot_mqtt_recv, 它会循环收取服务器下发的MQTT消息, 并在断线时自动重连 */
g_mqtt_recv_thread_running = 1;
res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_recv_thread failed: %d\n", res);
g_mqtt_process_thread_running = 0;
pthread_join(g_mqtt_process_thread, NULL);
aiot_mqtt_deinit(&mqtt_handle);
return -1;
}
*handle = mqtt_handle;
return 0;
}
int32_t demo_mqtt_stop(void **handle)
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
mqtt_handle = *handle;
g_mqtt_process_thread_running = 0;
g_mqtt_recv_thread_running = 0;
pthread_join(g_mqtt_process_thread, NULL);
pthread_join(g_mqtt_recv_thread, NULL);
/* 断开MQTT连接 */
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);
return -1;
}
/* 销毁MQTT实例 */
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x%04X\n", -res);
return -1;
}
return 0;
}
/* 事件处理回调, */
void demo_devinfo_event_handler(void *handle, const aiot_devinfo_event_t *event, void *userdata)
{
switch (event->type) {
case AIOT_DEVINFOEVT_INVALID_DEVINFO: {
printf("AIOT_DEVINFOEVT_INVALID_DEVINFO\n");
}
break;
case AIOT_DEVINFOEVT_INVALID_RESPONSE: {
printf("AIOT_DEVINFOEVT_INVALID_RESPONSE\n");
}
break;
case AIOT_DEVINFOEVT_INVALID_RESPONSE_FORMAT: {
printf("AIOT_DEVINFOEVT_INVALID_RESPONSE_FORMAT\n");
}
break;
default: {
}
}
}
/* TODO: 数据处理回调, 当SDK从网络上收到devinfo消息时被调用 */
void demo_devinfo_recv_handler(void *handle, const aiot_devinfo_recv_t *packet, void *userdata)
{
switch (packet->type) {
/* 这是云端对devinfo消息的应答报文 */
case AIOT_DEVINFORECV_GENERIC_REPLY: {
printf("pk: %s, dn: %s, code: %d, msg id: %d, data: %.*s, message: %.*s\n", packet->product_key, packet->device_name,
packet->data.generic_reply.code, packet->data.generic_reply.msg_id, packet->data.generic_reply.data_len,
packet->data.generic_reply.data, packet->data.generic_reply.message_len, packet->data.generic_reply.message);
}
break;
default: {
}
}
}
int main(int argc, char *argv[])
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL, *devinfo_handle = NULL;
/* 建立MQTT连接, 并开启保活线程和接收线程 */
res = demo_mqtt_start(&mqtt_handle, product_key, device_name, device_secret);
if (res < 0) {
printf("demo_mqtt_start failed\n");
return -1;
}
/* 创建1个devinfo客户端实例并内部初始化默认参数 */
devinfo_handle = aiot_devinfo_init();
if (devinfo_handle == NULL) {
demo_mqtt_stop(&mqtt_handle);
printf("aiot_devinfo_init failed\n");
return -1;
}
/* 配置devinfo会话, 把它和MQTT会话的句柄关联起来 */
res = aiot_devinfo_setopt(devinfo_handle, AIOT_DEVINFOOPT_MQTT_HANDLE, mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_devinfo_setopt AIOT_DEVINFOOPT_MQTT_HANDLE failed, res: -0x%04X\n", -res);
aiot_devinfo_deinit(&devinfo_handle);
demo_mqtt_stop(&mqtt_handle);
return -1;
}
/* TODO: DEVINFO消息回应从云端到达设备时, 会进入此处设置的回调函数 */
res = aiot_devinfo_setopt(devinfo_handle, AIOT_DEVINFOOPT_RECV_HANDLER, (void *)demo_devinfo_recv_handler);
if (res < STATE_SUCCESS) {
printf("aiot_devinfo_setopt AIOT_DEVINFOOPT_RECV_HANDLER failed, res: -0x%04X\n", -res);
aiot_devinfo_deinit(&devinfo_handle);
demo_mqtt_stop(&mqtt_handle);
return -1;
}
res = aiot_devinfo_setopt(devinfo_handle, AIOT_DEVINFOOPT_EVENT_HANDLER, (void *)demo_devinfo_event_handler);
if (res < STATE_SUCCESS) {
printf("aiot_devinfo_setopt AIOT_DEVINFOOPT_EVENT_HANDLER failed, res: -0x%04X\n", -res);
aiot_devinfo_deinit(&devinfo_handle);
demo_mqtt_stop(&mqtt_handle);
return -1;
}
/* 示例: 发送delete删除请求给云平台 */
/* TODO: 替换示例JSON中的 testKey 部分, 效果就会变成删除其他Key标识的设备标签 */
/* {
aiot_devinfo_msg_t devinfo_delete;
char *delete = "[{\"attrKey\":\"testKey\"}]";
memset(&devinfo_delete, 0, sizeof(aiot_devinfo_msg_t));
devinfo_delete.product_key = product_key;
devinfo_delete.device_name = device_name;
devinfo_delete.type = AIOT_DEVINFO_MSG_DELETE;
devinfo_delete.data.delete.params = delete;
res = aiot_devinfo_send(devinfo_handle, &devinfo_delete);
if (res < STATE_SUCCESS) {
aiot_devinfo_deinit(&devinfo_handle);
demo_mqtt_stop(&mqtt_handle);
return -1;
}
printf("aiot_devinfo_send delete msg id: %d\n", res);
} */
/* 示例: 发送update请求给云平台 */
/* TODO: 替换示例JSON中的 testKey 部分, 效果就会变成更新其他Key标识的设备标签 */
/* TODO: 替换示例JSON中的 testValue 部分, 效果就会变成更新其他Value到设备标签 */
/* {
aiot_devinfo_msg_t devinfo_update;
char *update = "[{\"attrKey\":\"testKey\",\"attrValue\":\"testValue\"}]";
memset(&devinfo_update, 0, sizeof(aiot_devinfo_msg_t));
devinfo_update.product_key = product_key;
devinfo_update.device_name = device_name;
devinfo_update.type = AIOT_DEVINFO_MSG_UPDATE;
devinfo_update.data.update.params = update;
res = aiot_devinfo_send(devinfo_handle, &devinfo_update);
if (res < STATE_SUCCESS) {
aiot_devinfo_deinit(&devinfo_handle);
demo_mqtt_stop(&mqtt_handle);
return -1;
}
printf("aiot_devinfo_send update msg id: %d\n", res);
} */
/* 主线程进入休眠, 等云平台的DEVINFO回应到达时, 接收线程会调用 demo_devinfo_recv_handler() */
while (1) {
sleep(1);
}
/* 销毁DEVINFO实例, 一般不会运行到这里 */
res = aiot_devinfo_deinit(&devinfo_handle);
if (res < STATE_SUCCESS) {
demo_mqtt_stop(&mqtt_handle);
printf("aiot_devinfo_deinit failed: -0x%04X\n", -res);
return -1;
}
/* 销毁MQTT实例, 退出线程, 一般不会运行到这里 */
res = demo_mqtt_stop(&mqtt_handle);
if (res < 0) {
printf("demo_start_stop failed\n");
return -1;
}
return 0;
}

View File

@@ -0,0 +1,554 @@
/*
* 这个例程适用于`Linux`这类支持pthread的POSIX设备, 它演示了......
*
* 需要用户关注或修改的部分, 已经用 TODO 在注释中标明
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
#include "aiot_dm_api.h"
#include "aiot_diag_api.h"
/* TODO: 替换为自己设备的三元组 */
char *product_key = "${YourProductKey}";
char *device_name = "${YourDeviceName}";
char *device_secret = "${YourDeviceSecret}";
/*
TODO: 替换为自己实例的接入点
对于企业实例, 或者2021年07月30日之后含当日开通的物联网平台服务下公共实例
mqtt_host的格式为"${YourInstanceId}.mqtt.iothub.aliyuncs.com"
其中${YourInstanceId}: 请替换为您企业/公共实例的Id
对于2021年07月30日之前不含当日开通的物联网平台服务下公共实例
需要将mqtt_host修改为: mqtt_host = "${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com"
其中, ${YourProductKey}请替换为设备所属产品的ProductKey。可登录物联网平台控制台在对应实例的设备详情页获取。
${YourRegionId}:请替换为您的物联网平台设备所在地域代码, 比如cn-shanghai等
该情况下完整mqtt_host举例: a1TTmBPIChA.iot-as-mqtt.cn-shanghai.aliyuncs.com
详情请见: https://help.aliyun.com/document_detail/147356.html
*/
char *mqtt_host = "${YourInstanceId}.mqtt.iothub.aliyuncs.com";
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
static pthread_t g_mqtt_process_thread;
static pthread_t g_mqtt_recv_thread;
static uint8_t g_mqtt_process_thread_running = 0;
static uint8_t g_mqtt_recv_thread_running = 0;
/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
*
* 例如: [1577589489.033][LK-0317] diag_basic_demo&gb80sFmX7yX
*
* 上面这条日志的code就是0317(十六进制), code值的定义见core/aiot_state_api.h
*
*/
/* 日志回调函数, SDK的日志会从这里输出 */
static int32_t demo_state_logcb(int32_t code, char *message)
{
printf("%s", message);
return 0;
}
/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata)
{
switch (event->type) {
/* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */
case AIOT_MQTTEVT_CONNECT: {
printf("AIOT_MQTTEVT_CONNECT\n");
}
break;
/* SDK因为网络状况被动断连后, 自动发起重连已成功 */
case AIOT_MQTTEVT_RECONNECT: {
printf("AIOT_MQTTEVT_RECONNECT\n");
}
break;
/* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */
case AIOT_MQTTEVT_DISCONNECT: {
char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
("heartbeat disconnect");
printf("AIOT_MQTTEVT_DISCONNECT: %s\n", cause);
}
break;
default: {
}
}
}
/* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */
void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata)
{
switch (packet->type) {
case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: {
printf("heartbeat response\n");
}
break;
case AIOT_MQTTRECV_SUB_ACK: {
printf("suback, res: -0x%04X, packet id: %d, max qos: %d\n",
-packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);
}
break;
case AIOT_MQTTRECV_PUB: {
printf("pub, qos: %d, topic: %.*s\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic);
printf("pub, payload: %.*s\n", packet->data.pub.payload_len, packet->data.pub.payload);
}
break;
case AIOT_MQTTRECV_PUB_ACK: {
printf("puback, packet id: %d\n", packet->data.pub_ack.packet_id);
}
break;
default: {
}
}
}
/* 执行aiot_mqtt_process的线程, 包含心跳发送和QoS1消息重发 */
void *demo_mqtt_process_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_process_thread_running) {
res = aiot_mqtt_process(args);
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
return NULL;
}
/* 执行aiot_mqtt_recv的线程, 包含网络自动重连和从服务器收取MQTT消息 */
void *demo_mqtt_recv_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_recv_thread_running) {
res = aiot_mqtt_recv(args);
if (res < STATE_SUCCESS) {
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
}
return NULL;
}
int32_t demo_mqtt_start(void *handle, char *product_key, char *device_name, char *device_secret)
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = handle;
uint16_t port = 443; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
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根证书长度 */
/* TODO: 如果以下代码不被注释, 则例程会用TCP而不是TLS连接云平台 */
/*
{
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
cred.option = AIOT_SYSDEP_NETWORK_CRED_NONE;
}
*/
/* 配置MQTT服务器地址 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);
/* 配置MQTT服务器端口 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
/* 配置设备productKey */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
/* 配置设备deviceName */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
/* 配置设备deviceSecret */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
/* 配置网络连接的安全凭据, 上面已经创建好了 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
/* 配置MQTT默认消息接收回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler);
/* 配置MQTT事件回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);
/* 与服务器建立MQTT连接 */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_connect failed: -0x%04X\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
return -1;
}
/* 创建一个单独的线程, 专用于执行aiot_mqtt_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */
g_mqtt_process_thread_running = 1;
res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_process_thread failed: %d\n", res);
aiot_mqtt_deinit(&mqtt_handle);
return -1;
}
/* 创建一个单独的线程用于执行aiot_mqtt_recv, 它会循环收取服务器下发的MQTT消息, 并在断线时自动重连 */
g_mqtt_recv_thread_running = 1;
res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_recv_thread failed: %d\n", res);
g_mqtt_process_thread_running = 0;
pthread_join(g_mqtt_process_thread, NULL);
aiot_mqtt_deinit(&mqtt_handle);
return -1;
}
return 0;
}
int32_t demo_mqtt_stop(void **handle)
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
mqtt_handle = *handle;
g_mqtt_process_thread_running = 0;
g_mqtt_recv_thread_running = 0;
pthread_join(g_mqtt_process_thread, NULL);
pthread_join(g_mqtt_recv_thread, NULL);
/* 断开MQTT连接 */
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);
return -1;
}
/* 销毁MQTT实例 */
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x%04X\n", -res);
return -1;
}
return 0;
}
/* 用户数据接收处理回调函数 */
static void demo_dm_recv_handler(void *dm_handle, const aiot_dm_recv_t *recv, void *userdata)
{
printf("demo_dm_recv_handler, type = %d\r\n", recv->type);
switch (recv->type) {
/* 属性上报, 事件上报, 获取期望属性值或者删除期望属性值的应答 */
case AIOT_DMRECV_GENERIC_REPLY: {
printf("msg_id = %d, code = %d, data = %.*s, message = %.*s\r\n",
recv->data.generic_reply.msg_id,
recv->data.generic_reply.code,
recv->data.generic_reply.data_len,
recv->data.generic_reply.data,
recv->data.generic_reply.message_len,
recv->data.generic_reply.message);
}
break;
/* 属性设置 */
case AIOT_DMRECV_PROPERTY_SET: {
printf("msg_id = %ld, params = %.*s\r\n",
(unsigned long)recv->data.property_set.msg_id,
recv->data.property_set.params_len,
recv->data.property_set.params);
/* TODO: 以下代码演示如何对来自云平台的属性设置指令进行应答, 用户可取消注释查看演示效果 */
/*
{
aiot_dm_msg_t msg;
memset(&msg, 0, sizeof(aiot_dm_msg_t));
msg.type = AIOT_DMMSG_PROPERTY_SET_REPLY;
msg.data.property_set_reply.msg_id = recv->data.property_set.msg_id;
msg.data.property_set_reply.code = 200;
msg.data.property_set_reply.data = "{}";
int32_t res = aiot_dm_send(dm_handle, &msg);
if (res < 0) {
printf("aiot_dm_send failed\r\n");
}
}
*/
}
break;
/* 异步服务调用 */
case AIOT_DMRECV_ASYNC_SERVICE_INVOKE: {
printf("msg_id = %ld, service_id = %s, params = %.*s\r\n",
(unsigned long)recv->data.async_service_invoke.msg_id,
recv->data.async_service_invoke.service_id,
recv->data.async_service_invoke.params_len,
recv->data.async_service_invoke.params);
/* TODO: 以下代码演示如何对来自云平台的异步服务调用进行应答, 用户可取消注释查看演示效果
*
* 注意: 如果用户在回调函数外进行应答, 需要自行保存msg_id, 因为回调函数入参在退出回调函数后将被SDK销毁, 不可以再访问到
*/
/*
{
aiot_dm_msg_t msg;
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 = "ToggleLightSwitch";
msg.data.async_service_reply.data = "{\"dataA\": 20}";
int32_t res = aiot_dm_send(dm_handle, &msg);
if (res < 0) {
printf("aiot_dm_send failed\r\n");
}
}
*/
}
break;
/* 同步服务调用 */
case AIOT_DMRECV_SYNC_SERVICE_INVOKE: {
printf("msg_id = %ld, rrpc_id = %s, service_id = %s, params = %.*s\r\n",
(unsigned long)recv->data.sync_service_invoke.msg_id,
recv->data.sync_service_invoke.rrpc_id,
recv->data.sync_service_invoke.service_id,
recv->data.sync_service_invoke.params_len,
recv->data.sync_service_invoke.params);
/* TODO: 以下代码演示如何对来自云平台的同步服务调用进行应答, 用户可取消注释查看演示效果
*
* 注意: 如果用户在回调函数外进行应答, 需要自行保存msg_id和rrpc_id字符串, 因为回调函数入参在退出回调函数后将被SDK销毁, 不可以再访问到
*/
/*
{
aiot_dm_msg_t msg;
memset(&msg, 0, sizeof(aiot_dm_msg_t));
msg.type = AIOT_DMMSG_SYNC_SERVICE_REPLY;
msg.data.sync_service_reply.rrpc_id = recv->data.sync_service_invoke.rrpc_id;
msg.data.sync_service_reply.msg_id = recv->data.sync_service_invoke.msg_id;
msg.data.sync_service_reply.code = 200;
msg.data.sync_service_reply.service_id = "SetLightSwitchTimer";
msg.data.sync_service_reply.data = "{}";
int32_t res = aiot_dm_send(dm_handle, &msg);
if (res < 0) {
printf("aiot_dm_send failed\r\n");
}
}
*/
}
break;
/* 下行二进制数据 */
case AIOT_DMRECV_RAW_DATA: {
printf("raw data len = %d\r\n", recv->data.raw_data.data_len);
/* TODO: 以下代码演示如何发送二进制格式数据, 若使用需要有相应的数据透传脚本部署在云端 */
/*
{
aiot_dm_msg_t msg;
uint8_t raw_data[] = {0x01, 0x02};
memset(&msg, 0, sizeof(aiot_dm_msg_t));
msg.type = AIOT_DMMSG_RAW_DATA;
msg.data.raw_data.data = raw_data;
msg.data.raw_data.data_len = sizeof(raw_data);
aiot_dm_send(dm_handle, &msg);
}
*/
}
break;
/* 二进制格式的同步服务调用, 比单纯的二进制数据消息多了个rrpc_id */
case AIOT_DMRECV_RAW_SYNC_SERVICE_INVOKE: {
printf("raw sync service rrpc_id = %s, data_len = %d\r\n",
recv->data.raw_service_invoke.rrpc_id,
recv->data.raw_service_invoke.data_len);
}
break;
default:
break;
}
}
int32_t demo_dm_start(void **handle, void *mqtt_handle, char *product_key, char *device_name)
{
void *dm_handle = NULL;
dm_handle = aiot_dm_init();
if (dm_handle == NULL) {
return -1;
}
/* 配置MQTT实例句柄 */
aiot_dm_setopt(dm_handle, AIOT_DMOPT_MQTT_HANDLE, mqtt_handle);
/* 配置消息接收处理回调函数 */
aiot_dm_setopt(dm_handle, AIOT_DMOPT_RECV_HANDLER, (void *)demo_dm_recv_handler);
*handle = dm_handle;
return STATE_SUCCESS;
}
int32_t demo_dm_stop(void **handle)
{
int32_t res = STATE_SUCCESS;
/* 销毁DATA-MODEL实例, 一般不会运行到这里 */
res = aiot_dm_deinit(handle);
if (res < STATE_SUCCESS) {
printf("aiot_dm_deinit failed: -0x%04X\n", -res);
return -1;
}
return STATE_SUCCESS;
}
/* 事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_diag_api.h */
void demo_diag_event_handler(void *handle, const aiot_diag_event_t *event, void *userdata)
{
switch (event->type) {
case AIOT_DIAGEVT_ALERT: {
printf("AIOT_DIAGEVT_ALERT, module name: %s, level: %s, desc: %s\n",
event->data.alert.module_name, event->data.alert.level, event->data.alert.desc);
}
break;
default: {
}
}
}
/* 数据处理回调, 当SDK从网络上收到diag消息时被调用 */
void demo_diag_recv_handler(void *handle, const aiot_diag_recv_t *packet, void *userdata)
{
switch (packet->type) {
case AIOT_DIAGRECV_DIAG_CONTROL: {
}
break;
default: {
}
}
}
int main(int argc, char *argv[])
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL, *diag_handle = NULL, *dm_handle = NULL;
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(demo_state_logcb);
/* 创建1个MQTT客户端实例并内部初始化默认参数 */
mqtt_handle = aiot_mqtt_init();
if (mqtt_handle == NULL) {
printf("aiot_mqtt_init failed\n");
return -1;
}
diag_handle = aiot_diag_init();
if (diag_handle == NULL) {
printf("aiot_diag_init failed\n");
return -1;
}
res = aiot_diag_setopt(diag_handle, AIOT_DIAGOPT_MQTT_HANDLE, mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_diag_deinit(&diag_handle);
printf("aiot_diag_setopt failed\n");
return -1;
}
res = aiot_diag_setopt(diag_handle, AIOT_DIAGOPT_EVENT_HANDLER, demo_diag_event_handler);
if (res < STATE_SUCCESS) {
aiot_diag_deinit(&diag_handle);
printf("aiot_diag_setopt failed\n");
return -1;
}
res = aiot_diag_start(diag_handle);
if (res < STATE_SUCCESS) {
aiot_diag_deinit(&diag_handle);
printf("aiot_diag_start failed\n");
return -1;
}
/* 建立MQTT连接, 并开启保活线程和接收线程 */
res = demo_mqtt_start(mqtt_handle, product_key, device_name, device_secret);
if (res < STATE_SUCCESS) {
aiot_diag_deinit(&diag_handle);
printf("demo_mqtt_start failed\n");
return -1;
}
res = demo_dm_start(&dm_handle, mqtt_handle, product_key, device_name);
if (res < STATE_SUCCESS) {
aiot_diag_deinit(&diag_handle);
demo_mqtt_stop(&mqtt_handle);
printf("demo_dm_start failed\n");
return -1;
}
/* {
aiot_dm_msg_t msg;
memset(&msg, 0, sizeof(aiot_dm_msg_t));
msg.type = AIOT_DMMSG_PROPERTY_POST;
msg.data.property_post.params = "{\"LightSwitch\":1}";
res = aiot_dm_send(dm_handle, &msg);
} */
while (1) {
sleep(1);
}
/* 销毁dm实例, 一般不会运行到这里 */
demo_dm_stop(&dm_handle);
/* 销毁diag实例, 一般不会运行到这里 */
aiot_diag_deinit(&diag_handle);
/* 销毁MQTT实例, 退出线程, 一般不会运行到这里 */
demo_mqtt_stop(&mqtt_handle);
return 0;
}

View File

@@ -0,0 +1,203 @@
/* 这个例程演示了用SDK配置动态注册会话实例的参数, 并发起请求和接收应答, 之后
*
* + 如果接收应答失败了, 销毁实例, 回收资源, 结束程序退出
* + 如果接收应答成功, 在`demo_dynreg_recv_handler()`的应答处理回调函数中, 演示解析获取服务端应答的内容
*
* 需要用户关注或修改的部分, 已用 `TODO` 在注释中标明
*
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_dynreg_api.h"
typedef struct {
uint32_t code;
char *device_secret;
} demo_info_t;
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
*
* 例如: [1580995015.811][LK-040B] > POST /auth/register/device HTTP/1.1
*
* 上面这条日志的code就是040B(十六进制), code值的定义见components/dynreg/aiot_dynreg_api.h
*
*/
/* 日志回调函数, SDK的日志会从这里输出 */
static int32_t demo_state_logcb(int32_t code, char *message)
{
printf("%s", message);
return 0;
}
/* 数据处理回调, 当SDK从网络上收到dynreg消息时被调用 */
void demo_dynreg_recv_handler(void *handle, const aiot_dynreg_recv_t *packet, void *userdata)
{
demo_info_t *demo_info = (demo_info_t *)userdata;
switch (packet->type) {
case AIOT_DYNREGRECV_STATUS_CODE: {
demo_info->code = packet->data.status_code.code;
}
break;
/* TODO: 回调中需要将packet指向的空间内容复制保存好, 因为回调返回后, 这些空间就会被SDK释放 */
case AIOT_DYNREGRECV_DEVICE_INFO: {
demo_info->device_secret = malloc(strlen(packet->data.device_info.device_secret) + 1);
if (demo_info->device_secret != NULL) {
memset(demo_info->device_secret, 0, strlen(packet->data.device_info.device_secret) + 1);
memcpy(demo_info->device_secret, packet->data.device_info.device_secret,
strlen(packet->data.device_info.device_secret));
}
}
break;
default: {
}
break;
}
}
int main(int argc, char *argv[])
{
int32_t res = STATE_SUCCESS;
void *dynreg_handle = NULL;
char *host = "iot-auth.cn-shanghai.aliyuncs.com"; /* 阿里云平台动态注册国内站点 */
uint16_t port = 443; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
demo_info_t demo_info;
/* TODO: 替换为自己设备的productKey, productSecret和deviceName */
char *product_key = "a13FN5TplKq";
char *product_secret = "y7GSILD480lBSsP8";
char *device_name = "dynreg_basic_demo";
memset(&demo_info, 0, sizeof(demo_info_t));
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(demo_state_logcb);
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
cred.option = AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA; /* 使用RSA证书校验DYNREG服务端 */
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; /* 用来验证服务端的RSA根证书 */
cred.x509_server_cert_len = strlen(ali_ca_cert); /* 用来验证服务端的RSA根证书长度 */
/* 创建1个dynreg客户端实例并内部初始化默认参数 */
dynreg_handle = aiot_dynreg_init();
if (dynreg_handle == NULL) {
printf("aiot_dynreg_init failed\n");
return -1;
}
/* 配置连接的服务器地址 */
res = aiot_dynreg_setopt(dynreg_handle, AIOT_DYNREGOPT_HOST, (void *)host);
if (res < STATE_SUCCESS) {
printf("aiot_dynreg_setopt AIOT_DYNREGOPT_HOST failed, res: -0x%04X\n", -res);
aiot_dynreg_deinit(&dynreg_handle);
return -1;
}
/* 配置连接的服务器端口 */
res = aiot_dynreg_setopt(dynreg_handle, AIOT_DYNREGOPT_PORT, (void *)&port);
if (res < STATE_SUCCESS) {
printf("aiot_dynreg_setopt AIOT_DYNREGOPT_PORT failed, res: -0x%04X\n", -res);
aiot_dynreg_deinit(&dynreg_handle);
return -1;
}
/* 配置设备productKey */
res = aiot_dynreg_setopt(dynreg_handle, AIOT_DYNREGOPT_PRODUCT_KEY, (void *)product_key);
if (res < STATE_SUCCESS) {
printf("aiot_dynreg_setopt AIOT_DYNREGOPT_PRODUCT_KEY failed, res: -0x%04X\n", -res);
aiot_dynreg_deinit(&dynreg_handle);
return -1;
}
/* 配置设备productSecret */
res = aiot_dynreg_setopt(dynreg_handle, AIOT_DYNREGOPT_PRODUCT_SECRET, (void *)product_secret);
if (res < STATE_SUCCESS) {
printf("aiot_dynreg_setopt AIOT_DYNREGOPT_PRODUCT_SECRET failed, res: -0x%04X\n", -res);
aiot_dynreg_deinit(&dynreg_handle);
return -1;
}
/* 配置设备deviceName */
res = aiot_dynreg_setopt(dynreg_handle, AIOT_DYNREGOPT_DEVICE_NAME, (void *)device_name);
if (res < STATE_SUCCESS) {
printf("aiot_dynreg_setopt AIOT_DYNREGOPT_DEVICE_NAME failed, res: -0x%04X\n", -res);
aiot_dynreg_deinit(&dynreg_handle);
return -1;
}
/* 配置网络连接的安全凭据, 上面已经创建好了 */
res = aiot_dynreg_setopt(dynreg_handle, AIOT_DYNREGOPT_NETWORK_CRED, (void *)&cred);
if (res < STATE_SUCCESS) {
printf("aiot_dynreg_setopt AIOT_DYNREGOPT_NETWORK_CRED failed, res: -0x%04X\n", -res);
aiot_dynreg_deinit(&dynreg_handle);
return -1;
}
/* 配置DYNREG默认消息接收回调函数 */
res = aiot_dynreg_setopt(dynreg_handle, AIOT_DYNREGOPT_RECV_HANDLER, (void *)demo_dynreg_recv_handler);
if (res < STATE_SUCCESS) {
printf("aiot_dynreg_setopt AIOT_DYNREGOPT_RECV_HANDLER failed, res: -0x%04X\n", -res);
aiot_dynreg_deinit(&dynreg_handle);
return -1;
}
/* 设置用户上下文,该上下文会在 demo_dynreg_recv_handler 被调用时传回 */
res = aiot_dynreg_setopt(dynreg_handle, AIOT_DYNREGOPT_USERDATA, (void *)&demo_info);
if (res < STATE_SUCCESS) {
printf("aiot_dynreg_setopt AIOT_DYNREGOPT_USERDATA failed, res: -0x%04X\n", -res);
aiot_dynreg_deinit(&dynreg_handle);
return -1;
}
/* 发送动态注册请求 */
res = aiot_dynreg_send_request(dynreg_handle);
if (res < STATE_SUCCESS) {
printf("aiot_dynreg_send_request failed: -0x%04X\n", -res);
return -1;
}
/* 接收动态注册请求 */
res = aiot_dynreg_recv(dynreg_handle);
if (res < STATE_SUCCESS) {
printf("aiot_dynreg_recv failed: -0x%04X\n", -res);
return -1;
}
printf("status code: %d\n", demo_info.code);
/* 把服务应答中的 deviceSecret 打印出来 */
if (demo_info.device_secret != NULL) {
printf("device secret: %s\n", demo_info.device_secret);
free(demo_info.device_secret);
}
/* 销毁动态注册会话实例 */
res = aiot_dynreg_deinit(&dynreg_handle);
if (res < STATE_SUCCESS) {
printf("aiot_dynreg_deinit failed: -0x%04X\n", -res);
return -1;
}
return 0;
}

View File

@@ -0,0 +1,261 @@
/* 这个例程演示了用SDK配置动态注册会话实例的参数, 并发起请求和接收应答, 之后
*
* + 如果接收应答失败了, 销毁实例, 回收资源, 结束程序退出
* + 如果接收应答成功, 在`demo_dynregmq_recv_handler()`的应答处理回调函数中, 演示解析获取服务端应答的内容
*
* 需要用户关注或修改的部分, 已用 `TODO` 在注释中标明
*
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_dynregmq_api.h"
/* TODO: 替换为自己设备的三元组 */
char *product_key = "${YourProductKey}";
char *device_name = "${YourDeviceName}";
char *product_secret = "${YourProductSecret}";
/*
TODO: 替换为自己实例的接入点
对于企业实例, 或者2021年07月30日之后含当日开通的物联网平台服务下公共实例
mqtt_host的格式为"${YourInstanceId}.mqtt.iothub.aliyuncs.com"
其中${YourInstanceId}: 请替换为您企业/公共实例的Id
对于2021年07月30日之前不含当日开通的物联网平台服务下公共实例
需要将mqtt_host修改为: mqtt_host = "${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com"
其中, ${YourProductKey}请替换为设备所属产品的ProductKey。可登录物联网平台控制台在对应实例的设备详情页获取。
${YourRegionId}:请替换为您的物联网平台设备所在地域代码, 比如cn-shanghai等
该情况下完整mqtt_host举例: a1TTmBPIChA.iot-as-mqtt.cn-shanghai.aliyuncs.com
详情请见: https://help.aliyun.com/document_detail/147356.html
*/
char *mqtt_host = "${YourInstanceId}.mqtt.iothub.aliyuncs.com";
uint8_t skip_pre_regist =
1; /* TODO: 如果要免预注册, 需要将该值设置为1;如果需要在控制台预先注册设备, 置为0 */
/* 白名单模式下用于保存deviceSecret的结构体定义 */
typedef struct {
char device_secret[64];
} demo_devinfo_wl_t;
/* 免白名单模式下用于保存mqtt建连信息clientid, username和password的结构体定义 */
typedef struct {
char conn_clientid[128];
char conn_username[128];
char conn_password[64];
} demo_devinfo_nwl_t;
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
/* 用户保存白名单模式动态注册, 服务器返回的deviceSecret */
static demo_devinfo_wl_t demo_devinfo_wl;
/* 用户保存免白名单模式动态注册, 服务器返回的mqtt建连信息 */
static demo_devinfo_nwl_t demo_devinfo_nwl;
/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
*
* 例如: [1580995015.811][LK-040B] > POST /auth/register/device HTTP/1.1
*
* 上面这条日志的code就是040B(十六进制), code值的定义见components/dynregmq/aiot_dynregmq_api.h
*
*/
/* 日志回调函数, SDK的日志会从这里输出 */
static int32_t demo_state_logcb(int32_t code, char *message)
{
printf("%s", message);
return 0;
}
/* 数据处理回调, 当SDK从网络上收到dynregmq消息时被调用 */
void demo_dynregmq_recv_handler(void *handle, const aiot_dynregmq_recv_t *packet, void *userdata)
{
switch (packet->type) {
/* TODO: 回调中需要将packet指向的空间内容复制保存好, 因为回调返回后, 这些空间就会被SDK释放 */
case AIOT_DYNREGMQRECV_DEVICEINFO_WL: {
if (strlen(packet->data.deviceinfo_wl.device_secret) >= sizeof(demo_devinfo_wl.device_secret)) {
break;
}
/* 白名单模式, 用户务必要对device_secret进行持久化保存 */
memset(&demo_devinfo_wl, 0, sizeof(demo_devinfo_wl_t));
memcpy(demo_devinfo_wl.device_secret, packet->data.deviceinfo_wl.device_secret,
strlen(packet->data.deviceinfo_wl.device_secret));
}
break;
/* TODO: 回调中需要将packet指向的空间内容复制保存好, 因为回调返回后, 这些空间就会被SDK释放 */
case AIOT_DYNREGMQRECV_DEVICEINFO_NWL: {
if (strlen(packet->data.deviceinfo_nwl.clientid) >= sizeof(demo_devinfo_nwl.conn_clientid) ||
strlen(packet->data.deviceinfo_nwl.username) >= sizeof(demo_devinfo_nwl.conn_username) ||
strlen(packet->data.deviceinfo_nwl.password) >= sizeof(demo_devinfo_nwl.conn_password)) {
break;
}
/* 免白名单模式, 用户务必要对MQTT的建连信息clientid, username和password进行持久化保存 */
memset(&demo_devinfo_nwl, 0, sizeof(demo_devinfo_nwl_t));
memcpy(demo_devinfo_nwl.conn_clientid, packet->data.deviceinfo_nwl.clientid,
strlen(packet->data.deviceinfo_nwl.clientid));
memcpy(demo_devinfo_nwl.conn_username, packet->data.deviceinfo_nwl.username,
strlen(packet->data.deviceinfo_nwl.username));
memcpy(demo_devinfo_nwl.conn_password, packet->data.deviceinfo_nwl.password,
strlen(packet->data.deviceinfo_nwl.password));
}
break;
default: {
}
break;
}
}
int main(int argc, char *argv[])
{
int32_t res = STATE_SUCCESS;
void *dynregmq_handle = NULL;
uint16_t port = 443; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t
cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(demo_state_logcb);
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
cred.option = AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA; /* 使用RSA证书校验DYNREGMQ服务端 */
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; /* 用来验证服务端的RSA根证书 */
cred.x509_server_cert_len = strlen(ali_ca_cert); /* 用来验证服务端的RSA根证书长度 */
/* 创建1个dynregmq客户端实例并内部初始化默认参数 */
dynregmq_handle = aiot_dynregmq_init();
if (dynregmq_handle == NULL) {
printf("aiot_dynregmq_init failed\n");
return -1;
}
/* 配置连接的服务器地址 */
res = aiot_dynregmq_setopt(dynregmq_handle, AIOT_DYNREGMQOPT_HOST, (void *)mqtt_host);
if (res < STATE_SUCCESS) {
printf("aiot_dynregmq_setopt AIOT_DYNREGMQOPT_HOST failed, res: -0x%04X\n", -res);
aiot_dynregmq_deinit(&dynregmq_handle);
return -1;
}
/* 配置连接的服务器端口 */
res = aiot_dynregmq_setopt(dynregmq_handle, AIOT_DYNREGMQOPT_PORT, (void *)&port);
if (res < STATE_SUCCESS) {
printf("aiot_dynregmq_setopt AIOT_DYNREGMQOPT_PORT failed, res: -0x%04X\n", -res);
aiot_dynregmq_deinit(&dynregmq_handle);
return -1;
}
/* 配置设备productKey */
res = aiot_dynregmq_setopt(dynregmq_handle, AIOT_DYNREGMQOPT_PRODUCT_KEY, (void *)product_key);
if (res < STATE_SUCCESS) {
printf("aiot_dynregmq_setopt AIOT_DYNREGMQOPT_PRODUCT_KEY failed, res: -0x%04X\n", -res);
aiot_dynregmq_deinit(&dynregmq_handle);
return -1;
}
/* 配置设备productSecret */
res = aiot_dynregmq_setopt(dynregmq_handle, AIOT_DYNREGMQOPT_PRODUCT_SECRET, (void *)product_secret);
if (res < STATE_SUCCESS) {
printf("aiot_dynregmq_setopt AIOT_DYNREGMQOPT_PRODUCT_SECRET failed, res: -0x%04X\n", -res);
aiot_dynregmq_deinit(&dynregmq_handle);
return -1;
}
/* 配置设备deviceName */
res = aiot_dynregmq_setopt(dynregmq_handle, AIOT_DYNREGMQOPT_DEVICE_NAME, (void *)device_name);
if (res < STATE_SUCCESS) {
printf("aiot_dynregmq_setopt AIOT_DYNREGMQOPT_DEVICE_NAME failed, res: -0x%04X\n", -res);
aiot_dynregmq_deinit(&dynregmq_handle);
return -1;
}
/* 配置网络连接的安全凭据, 上面已经创建好了 */
res = aiot_dynregmq_setopt(dynregmq_handle, AIOT_DYNREGMQOPT_NETWORK_CRED, (void *)&cred);
if (res < STATE_SUCCESS) {
printf("aiot_dynregmq_setopt AIOT_DYNREGMQOPT_NETWORK_CRED failed, res: -0x%04X\n", -res);
aiot_dynregmq_deinit(&dynregmq_handle);
return -1;
}
/* 配置DYNREGMQ默认消息接收回调函数 */
res = aiot_dynregmq_setopt(dynregmq_handle, AIOT_DYNREGMQOPT_RECV_HANDLER, (void *)demo_dynregmq_recv_handler);
if (res < STATE_SUCCESS) {
printf("aiot_dynregmq_setopt AIOT_DYNREGMQOPT_RECV_HANDLER failed, res: -0x%04X\n", -res);
aiot_dynregmq_deinit(&dynregmq_handle);
return -1;
}
/* 配置DYNREGMQ动态注册模式,
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句柄中。
*/
res = aiot_dynregmq_setopt(dynregmq_handle, AIOT_DYNREGMQOPT_NO_WHITELIST, (void *)&skip_pre_regist);
if (res < STATE_SUCCESS) {
printf("aiot_dynregmq_setopt AIOT_DYNREGMQOPT_NO_WHITELIST failed, res: -0x%04X\n", -res);
aiot_dynregmq_deinit(&dynregmq_handle);
return -1;
}
/* 发送动态注册请求 */
res = aiot_dynregmq_send_request(dynregmq_handle);
if (res < STATE_SUCCESS) {
printf("aiot_dynregmq_send_request failed: -0x%04X\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, product_secret in demo\r\n");
aiot_dynregmq_deinit(&dynregmq_handle);
return -1;
}
/* 接收动态注册请求 */
res = aiot_dynregmq_recv(dynregmq_handle);
if (res < STATE_SUCCESS) {
printf("aiot_dynregmq_recv failed: -0x%04X\n", -res);
aiot_dynregmq_deinit(&dynregmq_handle);
return -1;
}
/* 把服务应答中的信息打印出来 */
if (skip_pre_regist == 0) {
printf("device secret: %s\n", demo_devinfo_wl.device_secret);
} else {
printf("clientid: %s\n", demo_devinfo_nwl.conn_clientid);
printf("username: %s\n", demo_devinfo_nwl.conn_username);
printf("password: %s\n", demo_devinfo_nwl.conn_password);
}
/* 销毁动态注册会话实例 */
res = aiot_dynregmq_deinit(&dynregmq_handle);
if (res < STATE_SUCCESS) {
printf("aiot_dynregmq_deinit failed: -0x%04X\n", -res);
return -1;
}
return 0;
}

View File

@@ -0,0 +1,464 @@
/*
* 这个例程适用于不支持通过pthread函数在主线程外开独立的固件下载线程的设备
* 它演示了用SDK配置MQTT参数并建立连接, 并在接收到OTA的mqtt消息后开始下载升级固件的过程
* 同时, 它演示了如何将固件的大小分为两半, 每次下载一半的做法. 用户可以进一步将固件分成更多更小的分段
*
* 需要用户关注或修改的部分, 已用 `TODO` 在注释中标明
*
*/
/* TODO: 本例子用到了sleep函数, 所以包含了unistd.h. 如果用户自己的库中有可以替代的函数, 则可以将unistd.h替换掉
*
* 本例子用到了malloc/free函数, 所以用到了stdlib.h, 用户如果自己库中有可以替代的函数, 则需要将stdlib.h替换掉
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
#include "aiot_ota_api.h"
/* TODO: 替换为自己设备的三元组 */
char *product_key = "${YourProductKey}";
char *device_name = "${YourDeviceName}";
char *device_secret = "${YourDeviceSecret}";
/*
TODO: 替换为自己实例的接入点
对于企业实例, 或者2021年07月30日之后含当日开通的物联网平台服务下公共实例
mqtt_host的格式为"${YourInstanceId}.mqtt.iothub.aliyuncs.com"
其中${YourInstanceId}: 请替换为您企业/公共实例的Id
对于2021年07月30日之前不含当日开通的物联网平台服务下公共实例
需要将mqtt_host修改为: mqtt_host = "${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com"
其中, ${YourProductKey}请替换为设备所属产品的ProductKey。可登录物联网平台控制台在对应实例的设备详情页获取。
${YourRegionId}:请替换为您的物联网平台设备所在地域代码, 比如cn-shanghai等
该情况下完整mqtt_host举例: a1TTmBPIChA.iot-as-mqtt.cn-shanghai.aliyuncs.com
详情请见: https://help.aliyun.com/document_detail/147356.html
*/
char *mqtt_host = "${YourInstanceId}.mqtt.iothub.aliyuncs.com";
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
void *g_ota_handle = NULL;
void *g_dl_handle = NULL;
uint32_t g_firmware_size = 0;
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
*
* 例如: [1578463098.611][LK-0309] pub: /ota/device/upgrade/a13FN5TplKq/ota_demo
*
* 上面这条日志的code就是0309(十六进制), code值的定义见core/aiot_state_api.h
*
*/
/* 日志回调函数, SDK的日志会从这里输出 */
int32_t demo_state_logcb(int32_t code, char *message)
{
/* 下载固件的时候会有大量的HTTP收包日志, 通过code筛选出来关闭 */
if (STATE_HTTP_LOG_RECV_CONTENT != code) {
printf("%s", message);
}
return 0;
}
/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *const event, void *userdata)
{
switch (event->type) {
/* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */
case AIOT_MQTTEVT_CONNECT: {
printf("AIOT_MQTTEVT_CONNECT\r\n");
/* TODO: 处理SDK建连成功, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
/* SDK因为网络状况被动断连后, 自动发起重连已成功 */
case AIOT_MQTTEVT_RECONNECT: {
printf("AIOT_MQTTEVT_RECONNECT\r\n");
/* TODO: 处理SDK重连成功, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
/* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */
case AIOT_MQTTEVT_DISCONNECT: {
char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
("heartbeat disconnect");
printf("AIOT_MQTTEVT_DISCONNECT: %s\r\n", cause);
/* TODO: 处理SDK被动断连, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
default: {
}
}
}
/* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */
void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *const packet, void *userdata)
{
switch (packet->type) {
case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: {
printf("heartbeat response\r\n");
/* TODO: 处理服务器对心跳的回应, 一般不处理 */
}
break;
case AIOT_MQTTRECV_SUB_ACK: {
printf("suback, res: -0x%04X, packet id: %d, max qos: %d\r\n",
-packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);
/* TODO: 处理服务器对订阅请求的回应, 一般不处理 */
}
break;
case AIOT_MQTTRECV_PUB: {
printf("pub, qos: %d, topic: %.*s\r\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic);
printf("pub, payload: %.*s\r\n", packet->data.pub.payload_len, packet->data.pub.payload);
/* TODO: 处理服务器下发的业务报文 */
}
break;
case AIOT_MQTTRECV_PUB_ACK: {
printf("puback, packet id: %d\r\n", packet->data.pub_ack.packet_id);
/* TODO: 处理服务器对QoS1上报消息的回应, 一般不处理 */
}
break;
default: {
}
}
}
/* 下载收包回调, 用户调用 aiot_download_recv() 后, SDK收到数据会进入这个函数, 把下载到的数据交给用户 */
/* TODO: 一般来说, 设备升级时, 会在这个回调中, 把下载到的数据写到Flash上 */
void user_download_recv_handler(void *handle, const aiot_download_recv_t *packet, void *userdata)
{
int32_t percent = 0;
int32_t last_percent = 0;
uint32_t data_buffer_len = 0;
/* 目前只支持 packet->type 为 AIOT_DLRECV_HTTPBODY 的情况 */
if (!packet || AIOT_DLRECV_HTTPBODY != packet->type) {
return;
}
percent = packet->data.percent;
/* 如果 percent 为负数, 说明发生了收包异常或者digest校验错误 */
if (percent < 0) {
/* digest校验错误 */
printf("exception happend, percent is %d\r\n", percent);
if (userdata) {
free(userdata);
}
return;
}
/* userdata可以存放 demo_download_recv_handler() 的不同次进入之间, 需要共享的数据 */
/* 这里用来存放上一次进入本回调函数时, 下载的固件进度百分比 */
if (userdata) {
last_percent = *((uint32_t *)(userdata));
}
data_buffer_len = packet->data.len;
/*
* TODO: 下载一段固件成功, 这个时候, 用户应该将
* 起始地址为 packet->data.buffer, 长度为 packet->data.len 的内存, 保存到flash上
*
* 如果烧写flash失败, 还应该调用 aiot_download_report_progress(handle, -4) 将失败上报给云平台
* 备注:协议中, 与云平台商定的错误码在 aiot_ota_protocol_errcode_t 类型中, 例如
* -1: 表示升级失败
* -2: 表示下载失败
* -3: 表示校验失败
* -4: 表示烧写失败
*
* 详情可见 https://help.aliyun.com/document_detail/85700.html
*/
/* percent 入参的值为 100 时, 说明SDK已经下载固件内容全部完成 */
if (percent == 100) {
/* 上报版本号 */
/*
* TODO: 这个时候, 一般用户就应该完成所有的固件烧录, 保存当前工作, 重启设备, 切换到新的固件上启动了
* 并且, 新的固件必须要以
*
* aiot_ota_report_version(g_ota_handle, new_version);
*
* 这样的操作, 将升级后的新版本号(比如1.0.0升到1.1.0, 则new_version的值是"1.1.0")上报给云平台
* 云平台收到了新的版本号上报后, 才会判定升级成功, 否则会认为本次升级失败了
*
*/
}
/* 简化输出, 只有距离上次的下载进度增加5%以上时, 才会打印进度, 并向服务器上报进度 */
if (percent - last_percent >= 5 || percent == 100) {
printf("download %03d%% done, +%d bytes\r\n", percent, data_buffer_len);
aiot_download_report_progress(handle, percent);
if (userdata) {
*((uint32_t *)(userdata)) = percent;
}
if (percent == 100 && userdata) {
free(userdata);
}
}
}
/* 用户通过 aiot_ota_setopt() 注册的OTA消息处理回调, 如果SDK收到了OTA相关的MQTT消息, 会自动识别, 调用这个回调函数 */
void user_ota_recv_handler(void *ota_handle, aiot_ota_recv_t *ota_msg, void *userdata)
{
switch (ota_msg->type) {
case AIOT_OTARECV_FOTA: {
uint16_t port = 443;
uint32_t max_buffer_len = 2048;
aiot_sysdep_network_cred_t cred;
void *dl_handle = NULL;
void *last_percent = NULL;
if (NULL == ota_msg->task_desc || ota_msg->task_desc->protocol_type != AIOT_OTA_PROTOCOL_HTTPS) {
break;
}
dl_handle = aiot_download_init();
if (NULL == dl_handle) {
break;
}
last_percent = malloc(sizeof(uint32_t));
if (NULL == last_percent) {
aiot_download_deinit(&dl_handle);
break;
}
memset(last_percent, 0, sizeof(uint32_t));
printf("OTA target firmware version: %s, size: %u Bytes\r\n", ota_msg->task_desc->version,
ota_msg->task_desc->size_total);
if (NULL != ota_msg->task_desc->extra_data) {
printf("extra data: %s\r\n", ota_msg->task_desc->extra_data);
}
g_firmware_size = ota_msg->task_desc->size_total;
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
cred.option = AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA;
cred.max_tls_fragment = 16384;
cred.x509_server_cert = ali_ca_cert;
cred.x509_server_cert_len = strlen(ali_ca_cert);
uint32_t end = g_firmware_size / 2;
/* 设置下载时为TLS下载 */
if ((STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_CRED, (void *)(&cred)))
/* 设置下载时访问的服务器端口号 */
|| (STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_PORT, (void *)(&port)))
/* 设置下载的任务信息, 通过输入参数 ota_msg 中的 task_desc 成员得到, 内含下载地址, 固件大小, 固件签名等 */
|| (STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_TASK_DESC, (void *)(ota_msg->task_desc)))
/* 设置下载内容到达时, SDK将调用的回调函数 */
|| (STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_RECV_HANDLER, (void *)(user_download_recv_handler)))
/* 设置单次下载最大的buffer长度, 每当这个长度的内存读满了后会通知用户 */
|| (STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_BODY_BUFFER_MAX_LEN, (void *)(&max_buffer_len)))
/* 设置 AIOT_DLOPT_RECV_HANDLER 的不同次调用之间共享的数据, 比如例程把进度存在这里 */
|| (STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_USERDATA, (void *)last_percent))
/* 指明下载方式是按照range下载, 并且当前只下载一半 */
|| (STATE_SUCCESS != aiot_download_setopt(dl_handle, AIOT_DLOPT_RANGE_END, (void *)&end))
/* 发送http的GET请求给http服务器 */
|| (STATE_SUCCESS != aiot_download_send_request(dl_handle))) {
aiot_download_deinit(&dl_handle);
free(last_percent);
break;
}
g_dl_handle = dl_handle;
break;
}
default:
break;
}
}
int main(int argc, char *argv[])
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
uint16_t port = 443; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
char *cur_version = NULL;
void *ota_handle = NULL;
uint32_t timeout_ms = 0;
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(demo_state_logcb);
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
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根证书长度 */
/* 创建1个MQTT客户端实例并内部初始化默认参数 */
mqtt_handle = aiot_mqtt_init();
if (mqtt_handle == NULL) {
return -1;
}
/* 配置MQTT服务器地址 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);
/* 配置MQTT服务器端口 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
/* 配置设备productKey */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
/* 配置设备deviceName */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
/* 配置设备deviceSecret */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
/* 配置网络连接的安全凭据, 上面已经创建好了 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
/* 配置MQTT默认消息接收回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler);
/* 配置MQTT事件回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);
/* 与MQTT例程不同的是, 这里需要增加创建OTA会话实例的语句 */
ota_handle = aiot_ota_init();
if (NULL == ota_handle) {
goto exit;
}
/* 用以下语句, 把OTA会话和MQTT会话关联起来 */
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_MQTT_HANDLE, mqtt_handle);
/* 用以下语句, 设置OTA会话的数据接收回调, SDK收到OTA相关推送时, 会进入这个回调函数 */
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_RECV_HANDLER, user_ota_recv_handler);
g_ota_handle = ota_handle;
/* 与服务器建立MQTT连接 */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_connect failed: -0x%04X\r\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
goto exit;
}
/* TODO: 非常重要!!!
*
* cur_version 要根据用户实际情况, 改成从设备的配置区获取, 要反映真实的版本号, 而不能像示例这样写为固定值
*
* 1. 如果设备从未上报过版本号, 在控制台网页将无法部署升级任务
* 2. 如果设备升级完成后, 上报的不是新的版本号, 在控制台网页将会显示升级失败
*
*/
cur_version = "1.0.0";
/* 演示MQTT连接建立起来之后, 就可以上报当前设备的版本号了 */
res = aiot_ota_report_version(ota_handle, cur_version);
if (res < STATE_SUCCESS) {
printf("report version failed, code is -0x%04X\r\n", -res);
}
while (1) {
aiot_mqtt_process(mqtt_handle);
res = aiot_mqtt_recv(mqtt_handle);
if (res == STATE_SYS_DEPEND_NWK_CLOSED) {
sleep(1);
}
if (NULL != g_dl_handle) {
/* 完成固件的接收前, 将mqtt的收包超时调整到100ms, 以减少两次固件下载动作之间的时间间隔 */
int32_t ret = aiot_download_recv(g_dl_handle);
timeout_ms = 100;
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_TIMEOUT_MS, (void *)&timeout_ms);
/* 本例子中, 将整个固件分为2个等份来下载, 每次下载一半, 第一半下载完成时会进入这个分支 */
if (STATE_DOWNLOAD_RANGE_FINISHED == ret) {
printf("the first half download finished\n");
/* 当第一次下载完成后, 要重新设置range的start/end值, 下载另外一半 */
/*
* 非常重要:
*
* 上一次下载的范围是[0, g_firmware_size/2],
* 因此这一次下载需要从 [g_firmware_size/2 + 1, 固件结束地址],
* 其中固件结束地址填0的话就表示直到下载结束
*
*/
uint32_t start = g_firmware_size / 2 + 1;
uint32_t end = 0;
aiot_download_setopt(g_dl_handle, AIOT_DLOPT_RANGE_START, (void *)&start);
/* range_end如果指定为0, 服务器就理解为下载到文件末尾后结束 */
aiot_download_setopt(g_dl_handle, AIOT_DLOPT_RANGE_END, (void *)&end);
/* 向服务器发起下一次下载的请求 */
aiot_download_send_request(g_dl_handle);
continue;
}
/* 整个固件下载完成 */
if (ret == STATE_DOWNLOAD_FINISHED) {
printf("down finished all\n");
aiot_download_deinit(&g_dl_handle);
/* 完成固件的接收后, 将mqtt的收包超时调整回到默认值5000ms */
timeout_ms = 5000;
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_TIMEOUT_MS, (void *)&timeout_ms);
continue;
}
/* 下载的内容超出了固件的总大小, 可能是用户把range划分错了, range之间有重叠 */
if (ret == STATE_DOWNLOAD_FETCH_TOO_MANY) {
printf("downloaded more than expeced bytes, please check range start/end settings\n");
aiot_download_deinit(&g_dl_handle);
/* 完成固件的接收后, 将mqtt的收包超时调整回到默认值5000ms */
timeout_ms = 5000;
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_TIMEOUT_MS, (void *)&timeout_ms);
continue;
}
if (STATE_DOWNLOAD_RENEWAL_REQUEST_SENT == ret) {
printf("download renewal request has been sent successfully\r\n");
continue;
}
if (ret <= STATE_SUCCESS) {
printf("download failed, error code is %d, try to send renewal request\r\n", ret);
continue;
}
}
}
/* 断开MQTT连接, 一般不会运行到这里 */
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_disconnect failed: -0x%04X\r\n", -res);
goto exit;
}
exit:
while (1) {
/* 销毁MQTT实例, 一般不会运行到这里 */
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x%04X\r\n", -res);
return -1;
} else {
break;
}
}
/* 销毁OTA实例, 一般不会运行到这里 */
aiot_ota_deinit(&ota_handle);
return 0;
}

View File

@@ -0,0 +1,510 @@
/*
* 这个例程适用于`Linux`这类支持pthread的POSIX设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建3个线程
*
* + 一个线程用于保活长连接
* + 一个线程用于接收消息, 并在有消息到达时进入默认的数据回调, 在连接状态变化时进入事件回调
* + 一个线程用于从网络上HTTP下载待升级的固件, 这个线程由接收消息线程得到OTA升级的MQTT消息后启动
*
* 本例讲述了单个升级包中多个url情况下设备如何升级的用例
* 需要用户关注或修改的部分, 已用 `TODO` 在注释中标明
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_ota_api.h"
#include "aiot_mqtt_api.h"
/* TODO: 替换为自己设备的三元组 */
char *product_key = "${YourProductKey}";
char *device_name = "${YourDeviceName}";
char *device_secret = "${YourDeviceSecret}";
/*
TODO: 替换为自己实例的接入点
对于企业实例, 或者2021年07月30日之后含当日开通的物联网平台服务下公共实例
mqtt_host的格式为"${YourInstanceId}.mqtt.iothub.aliyuncs.com"
其中${YourInstanceId}: 请替换为您企业/公共实例的Id
对于2021年07月30日之前不含当日开通的物联网平台服务下公共实例
需要将mqtt_host修改为: mqtt_host = "${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com"
其中, ${YourProductKey}请替换为设备所属产品的ProductKey。可登录物联网平台控制台在对应实例的设备详情页获取。
${YourRegionId}:请替换为您的物联网平台设备所在地域代码, 比如cn-shanghai等
该情况下完整mqtt_host举例: a1TTmBPIChA.iot-as-mqtt.cn-shanghai.aliyuncs.com
详情请见: https://help.aliyun.com/document_detail/147356.html
*/
char *mqtt_host = "${YourInstanceId}.mqtt.iothub.aliyuncs.com";
/* 收发包动作是要结束 */
int should_stop = 0;
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
static pthread_t g_mqtt_process_thread; /* 用于MQTT的长连接保活线程 */
static pthread_t g_mqtt_recv_thread; /* 用于MQTT的循环收消息线程 */
static pthread_t g_download_thread; /* 用于HTTP的固件下载线程 */
/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
*
* 例如: [1578463098.611][LK-0309] pub: /ota/device/upgrade/a13FN5TplKq/ota_demo
*
* 上面这条日志的code就是0309(十六进制), code值的定义见core/aiot_state_api.h
*
*/
/* 单升级包多文件情况下的整体任务进度描述 */
typedef struct {
int32_t last_percent;
uint32_t file_id;
uint32_t file_num;
} multi_download_status_t;
int32_t g_finished_task_num = 0;
/* 日志回调函数, SDK的日志会从这里输出 */
int32_t demo_state_logcb(int32_t code, char *message)
{
/* 下载固件的时候会有大量的HTTP收包日志, 通过code筛选出来关闭 */
if (STATE_HTTP_LOG_RECV_CONTENT != code) {
printf("%s", message);
}
return 0;
}
/* 下载收包回调, 用户调用 aiot_download_recv() 后, SDK收到数据会进入这个函数, 把下载到的数据交给用户 */
/* TODO: 一般来说, 设备升级时, 会在这个回调中, 把下载到的数据写到Flash上 */
void demo_download_recv_handler(void *handle, const aiot_download_recv_t *packet, void *userdata)
{
uint32_t data_buffer_len = 0;
int32_t last_percent = 0;
int32_t percent = 0;
multi_download_status_t *download_status = (multi_download_status_t *)userdata;
/* 目前只支持 packet->type 为 AIOT_DLRECV_HTTPBODY 的情况 */
if (!packet || AIOT_DLRECV_HTTPBODY != packet->type) {
return;
}
percent = packet->data.percent;
/* userdata可以存放 demo_download_recv_handler() 的不同次进入之间, 需要共享的数据 */
/* 这里用来存放上一次进入本回调函数时, 下载的固件进度百分比 */
if (userdata) {
last_percent = (download_status->last_percent);
}
data_buffer_len = packet->data.len;
/* 如果 percent 为负数, 说明发生了收包异常或者digest校验错误 */
if (percent < 0) {
printf("exception: percent = %d\r\n", percent);
if (userdata) {
free(userdata);
}
return;
}
/*
* TODO: 下载一段固件成功, 这个时候, 用户应该将
* 起始地址为 packet->data.buffer, 长度为 packet->data.len 的内存, 保存到flash上
*
* 如果烧写flash失败, 还应该调用 aiot_download_report_progress(handle, -4) 将失败上报给云平台
* 备注:协议中, 与云平台商定的错误码在 aiot_ota_protocol_errcode_t 类型中, 例如
* -1: 表示升级失败
* -2: 表示下载失败
* -3: 表示校验失败
* -4: 表示烧写失败
*
* 详情可见 https://help.aliyun.com/document_detail/85700.html
*/
/* percent 入参的值为 100 时, 说明SDK已经下载固件内容全部完成 */
if (percent == 100) {
g_finished_task_num++;
/*
* TODO: 这个时候, 一般用户就应该完成所有的固件烧录, 保存当前工作, 重启设备, 切换到新的固件上启动了
并且, 新的固件必须要以
aiot_ota_report_version(ota_handle, new_version);
这样的操作, 将升级后的新版本号(比如1.0.0升到1.1.0, 则new_version的值是"1.1.0")上报给云平台
云平台收到了新的版本号上报后, 才会判定升级成功, 否则会认为本次升级失败了
如果下载成功后升级失败, 还应该调用 aiot_download_report_progress(handle, -1) 将失败上报给云平台
*/
}
/* 简化输出, 只有距离上次的下载进度增加5%以上时, 才会打印进度, 并向服务器上报进度 */
if (percent - last_percent >= 5 || percent == 100) {
if (NULL != download_status) {
printf("file_id %d, download %03d%% done, +%d bytes\r\n", download_status->file_id, percent, data_buffer_len);
download_status->last_percent = percent;
if (g_finished_task_num == download_status->file_num) {
/* 考虑到多个线程并发下载, 仅仅在所有文件下载完成后才上报100%的进度 */
aiot_download_report_progress(handle, 100);
}
}
if (percent == 100 && userdata) {
free(userdata);
}
}
}
/* 执行aiot_download_recv的线程, 实现固件内容的请求和接收 */
void *demo_ota_download_thread(void *dl_handle)
{
int32_t ret = 0;
printf("\r\nstarting download thread in 2 seconds ......\r\n");
sleep(2);
/* 向固件服务器请求下载 */
/*
* TODO: 下面这样的写法, 就是以1个请求, 获取全部的固件内容
* 设备资源比较少, 或者网络较差时, 也可以分段下载, 需要组合
*
* aiot_download_setopt(dl_handle, AIOT_DLOPT_RANGE_START, ...);
* aiot_download_setopt(dl_handle, AIOT_DLOPT_RANGE_END, ...);
* aiot_download_send_request(dl_handle);
*
* 实现, 这种情况下, 需要把以上组合语句放置到循环中, 多次 send_request 和 recv
*
*/
aiot_download_send_request(dl_handle);
while (should_stop == 0) {
/* 从网络收取服务器回应的固件内容 */
ret = aiot_download_recv(dl_handle);
/* 固件全部下载完时, aiot_download_recv() 的返回值会等于 STATE_DOWNLOAD_FINISHED, 否则是当次获取的字节数 */
if (STATE_DOWNLOAD_FINISHED == ret) {
printf("download completed\r\n");
break;
}
if (STATE_DOWNLOAD_RENEWAL_REQUEST_SENT == ret) {
printf("download renewal request has been sent successfully\r\n");
continue;
}
if (ret <= STATE_SUCCESS) {
printf("download failed, error code is %d, try to send renewal request\r\n", ret);
continue;
}
}
/* 下载所有固件内容完成, 销毁下载会话, 线程自行退出 */
aiot_download_deinit(&dl_handle);
printf("download thread exit\r\n");
return NULL;
}
/* 用户通过 aiot_ota_setopt() 注册的OTA消息处理回调, 如果SDK收到了OTA相关的MQTT消息, 会自动识别, 调用这个回调函数 */
void demo_ota_recv_handler(void *ota_handle, aiot_ota_recv_t *ota_msg, void *userdata)
{
switch (ota_msg->type) {
case AIOT_OTARECV_FOTA: {
uint32_t res = 0;
uint16_t port = 443;
uint32_t max_buffer_len = (8 * 1024);
aiot_sysdep_network_cred_t cred;
void *dl_handle = NULL;
multi_download_status_t *download_status = NULL;
if (NULL == ota_msg->task_desc || ota_msg->task_desc->protocol_type != AIOT_OTA_PROTOCOL_HTTPS) {
break;
}
dl_handle = aiot_download_init();
if (NULL == dl_handle) {
break;
}
if (NULL != ota_msg->task_desc->file_name) {
printf("\r\nTotal file number is %d, current file id is %d, with file_name %s\r\n", ota_msg->task_desc->file_num,
ota_msg->task_desc->file_id, ota_msg->task_desc->file_name);
}
printf("OTA target firmware version: %s, size: %u Bytes \r\n", ota_msg->task_desc->version,
ota_msg->task_desc->size_total);
if (NULL != ota_msg->task_desc->extra_data) {
printf("extra data: %s\r\n", ota_msg->task_desc->extra_data);
}
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
cred.option = AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA;
cred.max_tls_fragment = 16384;
cred.x509_server_cert = ali_ca_cert;
cred.x509_server_cert_len = strlen(ali_ca_cert);
/* 设置下载时为TLS下载 */
aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_CRED, (void *)(&cred));
/* 设置下载时访问的服务器端口号 */
aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_PORT, (void *)(&port));
/* 设置下载的任务信息, 通过输入参数 ota_msg 中的 task_desc 成员得到, 内含下载地址, 固件大小, 固件签名等 */
aiot_download_setopt(dl_handle, AIOT_DLOPT_TASK_DESC, (void *)(ota_msg->task_desc));
/* 设置下载内容到达时, SDK将调用的回调函数 */
aiot_download_setopt(dl_handle, AIOT_DLOPT_RECV_HANDLER, (void *)(demo_download_recv_handler));
/* 设置单次下载最大的buffer长度, 每当这个长度的内存读满了后会通知用户 */
aiot_download_setopt(dl_handle, AIOT_DLOPT_BODY_BUFFER_MAX_LEN, (void *)(&max_buffer_len));
/* 设置 AIOT_DLOPT_RECV_HANDLER 的不同次调用之间共享的数据, 比如例程把进度存在这里 */
download_status = malloc(sizeof(multi_download_status_t));
if (NULL == download_status) {
aiot_download_deinit(&dl_handle);
break;
}
memset(download_status, 0, sizeof(multi_download_status_t));
download_status->file_id = ota_msg->task_desc->file_id;
download_status->file_num = ota_msg->task_desc->file_num;
aiot_download_setopt(dl_handle, AIOT_DLOPT_USERDATA, (void *)download_status);
/* 如果是第一个下载任务, 则上报进度0 */
if (0 == ota_msg->task_desc->file_id) {
aiot_download_report_progress(dl_handle, 0);
}
/* 启动专用的下载线程, 去完成固件内容的下载 */
res = pthread_create(&g_download_thread, NULL, demo_ota_download_thread, dl_handle);
if (res != 0) {
printf("pthread_create demo_ota_download_thread failed: %d\r\n", res);
aiot_download_deinit(&dl_handle);
free(download_status);
} else {
/* 下载线程被设置为 detach 类型, 固件内容获取完毕后可自主退出 */
pthread_detach(g_download_thread);
}
break;
}
default:
break;
}
}
/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata)
{
switch (event->type) {
/* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */
case AIOT_MQTTEVT_CONNECT: {
printf("AIOT_MQTTEVT_CONNECT\r\n");
/* TODO: 处理SDK建连成功, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
/* SDK因为网络状况被动断连后, 自动发起重连已成功 */
case AIOT_MQTTEVT_RECONNECT: {
printf("AIOT_MQTTEVT_RECONNECT\r\n");
/* TODO: 处理SDK重连成功, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
/* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */
case AIOT_MQTTEVT_DISCONNECT: {
char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
("heartbeat disconnect");
printf("AIOT_MQTTEVT_DISCONNECT: %s\r\n", cause);
/* TODO: 处理SDK被动断连, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
default: {
}
}
}
/* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */
void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata)
{
switch (packet->type) {
case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: {
printf("heartbeat response\r\n");
/* TODO: 处理服务器对心跳的回应, 一般不处理 */
}
break;
case AIOT_MQTTRECV_SUB_ACK: {
printf("suback, res: -0x%04X, packet id: %d, max qos: %d\r\n",
-packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);
/* TODO: 处理服务器对订阅请求的回应, 一般不处理 */
}
break;
case AIOT_MQTTRECV_PUB: {
printf("pub, qos: %d, topic: %.*s\r\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic);
printf("pub, payload: %.*s\r\n", packet->data.pub.payload_len, packet->data.pub.payload);
/* TODO: 处理服务器下发的业务报文 */
}
break;
case AIOT_MQTTRECV_PUB_ACK: {
printf("puback, packet id: %d\r\n", packet->data.pub_ack.packet_id);
/* TODO: 处理服务器对QoS1上报消息的回应, 一般不处理 */
}
break;
default: {
}
}
}
/* 执行aiot_mqtt_process的线程, 包含心跳发送和QoS1消息重发 */
void *demo_mqtt_process_thread(void *args)
{
while (should_stop == 0) {
aiot_mqtt_process(args);
sleep(1);
}
return NULL;
}
/* 执行aiot_mqtt_recv的线程, 包含网络自动重连和从服务器收取MQTT消息 */
void *demo_mqtt_recv_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (should_stop == 0) {
res = aiot_mqtt_recv(args);
if (res < STATE_SUCCESS) {
sleep(1);
}
}
return NULL;
}
int main(int argc, char *argv[])
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
uint16_t port = 443; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
void *ota_handle = NULL;
char *cur_version = NULL;
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(demo_state_logcb);
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
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根证书长度 */
/* 创建1个MQTT客户端实例并内部初始化默认参数 */
mqtt_handle = aiot_mqtt_init();
if (NULL == mqtt_handle) {
printf("aiot_mqtt_init failed\r\n");
return -1;
}
/* 配置MQTT服务器地址 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);
/* 配置MQTT服务器端口 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
/* 配置设备productKey */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
/* 配置设备deviceName */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
/* 配置设备deviceSecret */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
/* 配置网络连接的安全凭据, 上面已经创建好了 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
/* 配置MQTT默认消息接收回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler);
/* 配置MQTT事件回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);
/* 与MQTT例程不同的是, 这里需要增加创建OTA会话实例的语句 */
ota_handle = aiot_ota_init();
if (NULL == ota_handle) {
printf("aiot_ota_init failed\r\n");
aiot_mqtt_deinit(&mqtt_handle);
return -2;
}
/* 用以下语句, 把OTA会话和MQTT会话关联起来 */
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_MQTT_HANDLE, mqtt_handle);
/* 用以下语句, 设置OTA会话的数据接收回调, SDK收到OTA相关推送时, 会进入这个回调函数 */
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_RECV_HANDLER, demo_ota_recv_handler);
/* 与服务器建立MQTT连接 */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
aiot_mqtt_deinit(&mqtt_handle);
aiot_ota_deinit(&ota_handle);
printf("aiot_mqtt_connect failed: -0x%04X\r\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
return -3;
}
/* TODO: 非常重要!!!
*
* cur_version 要根据用户实际情况, 改成从设备的配置区获取, 要反映真实的版本号, 而不能像示例这样写为固定值
*
* 1. 如果设备从未上报过版本号, 在控制台网页将无法部署升级任务
* 2. 如果设备升级完成后, 上报的不是新的版本号, 在控制台网页将会显示升级失败
*
*/
/* 演示MQTT连接建立起来之后, 就可以上报当前设备的版本号了 */
cur_version = "1.0.0";
res = aiot_ota_report_version(ota_handle, cur_version);
if (res < STATE_SUCCESS) {
printf("aiot_ota_report_version failed: -0x%04X\r\n", -res);
}
/* 创建一个单独的线程, 专用于执行aiot_mqtt_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */
res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle);
if (res != 0) {
printf("pthread_create demo_mqtt_process_thread failed: %d\r\n", res);
return -1;
}
/* 创建一个单独的线程用于执行aiot_mqtt_recv, 它会循环收取服务器下发的MQTT消息, 并在断线时自动重连 */
res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle);
if (res != 0) {
printf("pthread_create demo_mqtt_recv_thread failed: %d\r\n", res);
return -1;
}
/* 主循环进入休眠 */
while (1) {
sleep(1);
}
should_stop = 1;
sleep(5);
/* 断开MQTT连接, 一般不会运行到这里 */
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&mqtt_handle);
aiot_ota_deinit(&ota_handle);
printf("aiot_mqtt_disconnect failed: -0x%04X\r\n", -res);
return -1;
}
/* 销毁OTA实例, 一般不会运行到这里 */
aiot_ota_deinit(&ota_handle);
return 0;
}

View File

@@ -0,0 +1,482 @@
/*
* 这个例程适用于`Linux`这类支持pthread的POSIX设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建3个线程
*
* + 一个线程用于保活长连接
* + 一个线程用于接收消息, 并在有消息到达时进入默认的数据回调, 在连接状态变化时进入事件回调
* + 一个线程用于从网络上HTTP下载待升级的固件, 这个线程由接收消息线程得到OTA升级的MQTT消息后启动
*
* 需要用户关注或修改的部分, 已用 `TODO` 在注释中标明
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_ota_api.h"
#include "aiot_mqtt_api.h"
/* TODO: 替换为自己设备的三元组 */
char *product_key = "${YourProductKey}";
char *device_name = "${YourDeviceName}";
char *device_secret = "${YourDeviceSecret}";
/*
TODO: 替换为自己实例的接入点
对于企业实例, 或者2021年07月30日之后含当日开通的物联网平台服务下公共实例
mqtt_host的格式为"${YourInstanceId}.mqtt.iothub.aliyuncs.com"
其中${YourInstanceId}: 请替换为您企业/公共实例的Id
对于2021年07月30日之前不含当日开通的物联网平台服务下公共实例
需要将mqtt_host修改为: mqtt_host = "${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com"
其中, ${YourProductKey}请替换为设备所属产品的ProductKey。可登录物联网平台控制台在对应实例的设备详情页获取。
${YourRegionId}:请替换为您的物联网平台设备所在地域代码, 比如cn-shanghai等
该情况下完整mqtt_host举例: a1TTmBPIChA.iot-as-mqtt.cn-shanghai.aliyuncs.com
详情请见: https://help.aliyun.com/document_detail/147356.html
*/
char *mqtt_host = "${YourInstanceId}.mqtt.iothub.aliyuncs.com";
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
static pthread_t g_mqtt_process_thread; /* 用于MQTT的长连接保活线程 */
static pthread_t g_mqtt_recv_thread; /* 用于MQTT的循环收消息线程 */
static pthread_t g_download_thread; /* 用于HTTP的固件下载线程 */
/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
*
* 例如: [1578463098.611][LK-0309] pub: /ota/device/upgrade/a13FN5TplKq/ota_demo
*
* 上面这条日志的code就是0309(十六进制), code值的定义见core/aiot_state_api.h
*
*/
/* 日志回调函数, SDK的日志会从这里输出 */
int32_t demo_state_logcb(int32_t code, char *message)
{
/* 下载固件的时候会有大量的HTTP收包日志, 通过code筛选出来关闭 */
if (STATE_HTTP_LOG_RECV_CONTENT != code) {
printf("%s", message);
}
return 0;
}
/* 下载收包回调, 用户调用 aiot_download_recv() 后, SDK收到数据会进入这个函数, 把下载到的数据交给用户 */
/* TODO: 一般来说, 设备升级时, 会在这个回调中, 把下载到的数据写到Flash上 */
void demo_download_recv_handler(void *handle, const aiot_download_recv_t *packet, void *userdata)
{
uint32_t data_buffer_len = 0;
uint32_t last_percent = 0;
int32_t percent = 0;
/* 目前只支持 packet->type 为 AIOT_DLRECV_HTTPBODY 的情况 */
if (!packet || AIOT_DLRECV_HTTPBODY != packet->type) {
return;
}
percent = packet->data.percent;
/* userdata可以存放 demo_download_recv_handler() 的不同次进入之间, 需要共享的数据 */
/* 这里用来存放上一次进入本回调函数时, 下载的固件进度百分比 */
if (userdata) {
last_percent = *((uint32_t *)(userdata));
}
data_buffer_len = packet->data.len;
/* 如果 percent 为负数, 说明发生了收包异常或者digest校验错误 */
if (percent < 0) {
printf("exception: percent = %d\r\n", percent);
if (userdata) {
free(userdata);
}
return;
}
/*
* TODO: 下载一段固件成功, 这个时候, 用户应该将
* 起始地址为 packet->data.buffer, 长度为 packet->data.len 的内存, 保存到flash上
*
* 如果烧写flash失败, 还应该调用 aiot_download_report_progress(handle, -4) 将失败上报给云平台
* 备注:协议中, 与云平台商定的错误码在 aiot_ota_protocol_errcode_t 类型中, 例如
* -1: 表示升级失败
* -2: 表示下载失败
* -3: 表示校验失败
* -4: 表示烧写失败
*
* 详情可见 https://help.aliyun.com/document_detail/85700.html
*/
/* percent 入参的值为 100 时, 说明SDK已经下载固件内容全部完成 */
if (percent == 100) {
/*
* TODO: 这个时候, 一般用户就应该完成所有的固件烧录, 保存当前工作, 重启设备, 切换到新的固件上启动了
并且, 新的固件必须要以
aiot_ota_report_version(ota_handle, new_version);
这样的操作, 将升级后的新版本号(比如1.0.0升到1.1.0, 则new_version的值是"1.1.0")上报给云平台
云平台收到了新的版本号上报后, 才会判定升级成功, 否则会认为本次升级失败了
如果下载成功后升级失败, 还应该调用 aiot_download_report_progress(handle, -1) 将失败上报给云平台
*/
}
/* 简化输出, 只有距离上次的下载进度增加5%以上时, 才会打印进度, 并向服务器上报进度 */
if (percent - last_percent >= 5 || percent == 100) {
printf("download %03d%% done, +%d bytes\r\n", percent, data_buffer_len);
aiot_download_report_progress(handle, percent);
if (userdata) {
*((uint32_t *)(userdata)) = percent;
}
if (percent == 100 && userdata) {
free(userdata);
}
}
}
/* 执行aiot_download_recv的线程, 实现固件内容的请求和接收 */
void *demo_ota_download_thread(void *dl_handle)
{
int32_t ret = 0;
printf("starting download thread in 2 seconds ......\r\n");
sleep(2);
/* 向固件服务器请求下载 */
/*
* TODO: 下面这样的写法, 就是以1个请求, 获取全部的固件内容
* 设备资源比较少, 或者网络较差时, 也可以分段下载, 需要组合
*
* aiot_download_setopt(dl_handle, AIOT_DLOPT_RANGE_START, ...);
* aiot_download_setopt(dl_handle, AIOT_DLOPT_RANGE_END, ...);
* aiot_download_send_request(dl_handle);
*
* 实现, 这种情况下, 需要把以上组合语句放置到循环中, 多次 send_request 和 recv
*
*/
aiot_download_send_request(dl_handle);
while (1) {
/* 从网络收取服务器回应的固件内容 */
ret = aiot_download_recv(dl_handle);
/* 固件全部下载完时, aiot_download_recv() 的返回值会等于 STATE_DOWNLOAD_FINISHED, 否则是当次获取的字节数 */
if (STATE_DOWNLOAD_FINISHED == ret) {
printf("download completed\r\n");
break;
}
if (STATE_DOWNLOAD_RENEWAL_REQUEST_SENT == ret) {
printf("download renewal request has been sent successfully\r\n");
continue;
}
if (ret <= STATE_SUCCESS) {
printf("download failed, error code is %d, try to send renewal request\r\n", ret);
continue;
}
}
/* 下载所有固件内容完成, 销毁下载会话, 线程自行退出 */
aiot_download_deinit(&dl_handle);
printf("download thread exit\r\n");
return NULL;
}
/* 用户通过 aiot_ota_setopt() 注册的OTA消息处理回调, 如果SDK收到了OTA相关的MQTT消息, 会自动识别, 调用这个回调函数 */
void demo_ota_recv_handler(void *ota_handle, aiot_ota_recv_t *ota_msg, void *userdata)
{
switch (ota_msg->type) {
case AIOT_OTARECV_FOTA: {
uint32_t res = 0;
uint16_t port = 443;
uint32_t max_buffer_len = (8 * 1024);
aiot_sysdep_network_cred_t cred;
void *dl_handle = NULL;
void *last_percent = NULL;
if (NULL == ota_msg->task_desc || ota_msg->task_desc->protocol_type != AIOT_OTA_PROTOCOL_HTTPS) {
break;
}
dl_handle = aiot_download_init();
if (NULL == dl_handle) {
break;
}
printf("OTA target firmware version: %s, size: %u Bytes \r\n", ota_msg->task_desc->version,
ota_msg->task_desc->size_total);
if (NULL != ota_msg->task_desc->extra_data) {
printf("extra data: %s\r\n", ota_msg->task_desc->extra_data);
}
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
cred.option = AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA;
cred.max_tls_fragment = 16384;
cred.x509_server_cert = ali_ca_cert;
cred.x509_server_cert_len = strlen(ali_ca_cert);
/* 设置下载时为TLS下载 */
aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_CRED, (void *)(&cred));
/* 设置下载时访问的服务器端口号 */
aiot_download_setopt(dl_handle, AIOT_DLOPT_NETWORK_PORT, (void *)(&port));
/* 设置下载的任务信息, 通过输入参数 ota_msg 中的 task_desc 成员得到, 内含下载地址, 固件大小, 固件签名等 */
aiot_download_setopt(dl_handle, AIOT_DLOPT_TASK_DESC, (void *)(ota_msg->task_desc));
/* 设置下载内容到达时, SDK将调用的回调函数 */
aiot_download_setopt(dl_handle, AIOT_DLOPT_RECV_HANDLER, (void *)(demo_download_recv_handler));
/* 设置单次下载最大的buffer长度, 每当这个长度的内存读满了后会通知用户 */
aiot_download_setopt(dl_handle, AIOT_DLOPT_BODY_BUFFER_MAX_LEN, (void *)(&max_buffer_len));
/* 设置 AIOT_DLOPT_RECV_HANDLER 的不同次调用之间共享的数据, 比如例程把进度存在这里 */
last_percent = malloc(sizeof(uint32_t));
if (NULL == last_percent) {
aiot_download_deinit(&dl_handle);
break;
}
memset(last_percent, 0, sizeof(uint32_t));
aiot_download_setopt(dl_handle, AIOT_DLOPT_USERDATA, (void *)last_percent);
/* 启动专用的下载线程, 去完成固件内容的下载 */
res = pthread_create(&g_download_thread, NULL, demo_ota_download_thread, dl_handle);
if (res != 0) {
printf("pthread_create demo_ota_download_thread failed: %d\r\n", res);
aiot_download_deinit(&dl_handle);
free(last_percent);
} else {
/* 下载线程被设置为 detach 类型, 固件内容获取完毕后可自主退出 */
pthread_detach(g_download_thread);
}
break;
}
default:
break;
}
}
/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata)
{
switch (event->type) {
/* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */
case AIOT_MQTTEVT_CONNECT: {
printf("AIOT_MQTTEVT_CONNECT\r\n");
/* TODO: 处理SDK建连成功, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
/* SDK因为网络状况被动断连后, 自动发起重连已成功 */
case AIOT_MQTTEVT_RECONNECT: {
printf("AIOT_MQTTEVT_RECONNECT\r\n");
/* TODO: 处理SDK重连成功, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
/* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */
case AIOT_MQTTEVT_DISCONNECT: {
char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
("heartbeat disconnect");
printf("AIOT_MQTTEVT_DISCONNECT: %s\r\n", cause);
/* TODO: 处理SDK被动断连, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
default: {
}
}
}
/* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */
void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata)
{
switch (packet->type) {
case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: {
printf("heartbeat response\r\n");
/* TODO: 处理服务器对心跳的回应, 一般不处理 */
}
break;
case AIOT_MQTTRECV_SUB_ACK: {
printf("suback, res: -0x%04X, packet id: %d, max qos: %d\r\n",
-packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);
/* TODO: 处理服务器对订阅请求的回应, 一般不处理 */
}
break;
case AIOT_MQTTRECV_PUB: {
printf("pub, qos: %d, topic: %.*s\r\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic);
printf("pub, payload: %.*s\r\n", packet->data.pub.payload_len, packet->data.pub.payload);
/* TODO: 处理服务器下发的业务报文 */
}
break;
case AIOT_MQTTRECV_PUB_ACK: {
printf("puback, packet id: %d\r\n", packet->data.pub_ack.packet_id);
/* TODO: 处理服务器对QoS1上报消息的回应, 一般不处理 */
}
break;
default: {
}
}
}
/* 执行aiot_mqtt_process的线程, 包含心跳发送和QoS1消息重发 */
void *demo_mqtt_process_thread(void *args)
{
while (1) {
aiot_mqtt_process(args);
sleep(1);
}
return NULL;
}
/* 执行aiot_mqtt_recv的线程, 包含网络自动重连和从服务器收取MQTT消息 */
void *demo_mqtt_recv_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (1) {
res = aiot_mqtt_recv(args);
if (res < STATE_SUCCESS) {
sleep(1);
}
}
return NULL;
}
int main(int argc, char *argv[])
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
uint16_t port = 443; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
void *ota_handle = NULL;
char *cur_version = NULL;
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(demo_state_logcb);
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
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根证书长度 */
/* 创建1个MQTT客户端实例并内部初始化默认参数 */
mqtt_handle = aiot_mqtt_init();
if (NULL == mqtt_handle) {
printf("aiot_mqtt_init failed\r\n");
return -1;
}
/* 配置MQTT服务器地址 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);
/* 配置MQTT服务器端口 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
/* 配置设备productKey */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
/* 配置设备deviceName */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
/* 配置设备deviceSecret */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
/* 配置网络连接的安全凭据, 上面已经创建好了 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
/* 配置MQTT默认消息接收回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler);
/* 配置MQTT事件回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);
/* 与MQTT例程不同的是, 这里需要增加创建OTA会话实例的语句 */
ota_handle = aiot_ota_init();
if (NULL == ota_handle) {
printf("aiot_ota_init failed\r\n");
aiot_mqtt_deinit(&mqtt_handle);
return -2;
}
/* 用以下语句, 把OTA会话和MQTT会话关联起来 */
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_MQTT_HANDLE, mqtt_handle);
/* 用以下语句, 设置OTA会话的数据接收回调, SDK收到OTA相关推送时, 会进入这个回调函数 */
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_RECV_HANDLER, demo_ota_recv_handler);
/* 与服务器建立MQTT连接 */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
aiot_mqtt_deinit(&mqtt_handle);
aiot_ota_deinit(&ota_handle);
printf("aiot_mqtt_connect failed: -0x%04X\r\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
return -3;
}
/* TODO: 非常重要!!!
*
* cur_version 要根据用户实际情况, 改成从设备的配置区获取, 要反映真实的版本号, 而不能像示例这样写为固定值
*
* 1. 如果设备从未上报过版本号, 在控制台网页将无法部署升级任务
* 2. 如果设备升级完成后, 上报的不是新的版本号, 在控制台网页将会显示升级失败
*
*/
/* 演示MQTT连接建立起来之后, 就可以上报当前设备的版本号了 */
cur_version = "1.0.0";
res = aiot_ota_report_version(ota_handle, cur_version);
if (res < STATE_SUCCESS) {
printf("aiot_ota_report_version failed: -0x%04X\r\n", -res);
}
/* 创建一个单独的线程, 专用于执行aiot_mqtt_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */
res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle);
if (res != 0) {
printf("pthread_create demo_mqtt_process_thread failed: %d\r\n", res);
return -1;
}
/* 创建一个单独的线程用于执行aiot_mqtt_recv, 它会循环收取服务器下发的MQTT消息, 并在断线时自动重连 */
res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle);
if (res != 0) {
printf("pthread_create demo_mqtt_recv_thread failed: %d\r\n", res);
return -1;
}
/* 主循环进入休眠 */
while (1) {
sleep(1);
}
/* 断开MQTT连接, 一般不会运行到这里 */
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&mqtt_handle);
aiot_ota_deinit(&ota_handle);
printf("aiot_mqtt_disconnect failed: -0x%04X\r\n", -res);
return -1;
}
/* 销毁MQTT实例, 一般不会运行到这里 */
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x%04X\r\n", -res);
aiot_ota_deinit(&ota_handle);
return -1;
}
/* 销毁OTA实例, 一般不会运行到这里 */
aiot_ota_deinit(&ota_handle);
return 0;
}

View File

@@ -0,0 +1,392 @@
/*
* 这个例程适用于`Linux`这类支持pthread的POSIX设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建2个线程
*
* + 一个线程用于保活长连接
* + 一个线程用于接收消息, 并在有消息到达时进入默认的数据回调, 在连接状态变化时进入事件回调
*
* 接着在MQTT连接上发送设备端日志到云端, 注意日志上云通道默认是关闭的, 用户需要到云端控制台打开设备端日志服务开关。
*
* 需要用户关注或修改的部分, 已经用 TODO 在注释中标明
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
#include "aiot_logpost_api.h"
/* TODO: 替换为自己设备的三元组 */
char *product_key = "${YourProductKey}";
char *device_name = "${YourDeviceName}";
char *device_secret = "${YourDeviceSecret}";
/*
TODO: 替换为自己实例的接入点
对于企业实例, 或者2021年07月30日之后含当日开通的物联网平台服务下公共实例
mqtt_host的格式为"${YourInstanceId}.mqtt.iothub.aliyuncs.com"
其中${YourInstanceId}: 请替换为您企业/公共实例的Id
对于2021年07月30日之前不含当日开通的物联网平台服务下公共实例
需要将mqtt_host修改为: mqtt_host = "${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com"
其中, ${YourProductKey}请替换为设备所属产品的ProductKey。可登录物联网平台控制台在对应实例的设备详情页获取。
${YourRegionId}:请替换为您的物联网平台设备所在地域代码, 比如cn-shanghai等
该情况下完整mqtt_host举例: a1TTmBPIChA.iot-as-mqtt.cn-shanghai.aliyuncs.com
详情请见: https://help.aliyun.com/document_detail/147356.html
*/
char *mqtt_host = "${YourInstanceId}.mqtt.iothub.aliyuncs.com";
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
static pthread_t g_mqtt_process_thread;
static pthread_t g_mqtt_recv_thread;
static uint8_t g_mqtt_process_thread_running = 0;
static uint8_t g_mqtt_recv_thread_running = 0;
/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
*
* 例如: [1581501698.455][LK-0309] pub: /sys/a13FN5TplKq/logpost_basic_demo/thing/deviceinfo/update
*
* 上面这条日志的code就是0309(十六进制), code值的定义见core/aiot_state_api.h
*
*/
/* 日志回调函数, SDK的日志会从这里输出, 禁止在此函数中调用SDK API */
static int32_t demo_state_logcb(int32_t code, char *message)
{
printf("%s", message);
return 0;
}
/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata)
{
switch (event->type) {
/* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */
case AIOT_MQTTEVT_CONNECT: {
printf("AIOT_MQTTEVT_CONNECT\r\n");
}
break;
/* SDK因为网络状况被动断连后, 自动发起重连已成功 */
case AIOT_MQTTEVT_RECONNECT: {
printf("AIOT_MQTTEVT_RECONNECT\r\n");
}
break;
/* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */
case AIOT_MQTTEVT_DISCONNECT: {
char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
("heartbeat disconnect");
printf("AIOT_MQTTEVT_DISCONNECT: %s\r\n", cause);
}
break;
default: {
}
}
}
/* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */
void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata)
{
switch (packet->type) {
case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: {
printf("heartbeat response\r\n");
}
break;
case AIOT_MQTTRECV_SUB_ACK: {
printf("suback, res: -0x%04X, packet id: %d, max qos: %d\r\n",
-packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);
}
break;
case AIOT_MQTTRECV_PUB: {
printf("pub, qos: %d, topic: %.*s\r\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic);
printf("pub, payload: %.*s\r\n", packet->data.pub.payload_len, packet->data.pub.payload);
}
break;
case AIOT_MQTTRECV_PUB_ACK: {
printf("puback, packet id: %d\r\n", packet->data.pub_ack.packet_id);
}
break;
default: {
}
}
}
/* 执行aiot_mqtt_process的线程, 包含心跳发送和QoS1消息重发 */
void *demo_mqtt_process_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_process_thread_running) {
res = aiot_mqtt_process(args);
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
return NULL;
}
/* 执行aiot_mqtt_recv的线程, 包含网络自动重连和从服务器收取MQTT消息 */
void *demo_mqtt_recv_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_recv_thread_running) {
res = aiot_mqtt_recv(args);
if (res < STATE_SUCCESS) {
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
}
return NULL;
}
int32_t demo_mqtt_start(void **handle, char *product_key, char *device_name, char *device_secret)
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
uint16_t port = 443; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(demo_state_logcb);
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
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根证书长度 */
/* 创建1个MQTT客户端实例并内部初始化默认参数 */
mqtt_handle = aiot_mqtt_init();
if (mqtt_handle == NULL) {
printf("aiot_mqtt_init failed\r\n");
return -1;
}
/* 如果以下代码不被注释, 则例程会用TCP而不是TLS连接云平台 */
/*
{
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
cred.option = AIOT_SYSDEP_NETWORK_CRED_NONE;
}
*/
/* 配置MQTT服务器地址 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);
/* 配置MQTT服务器端口 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
/* 配置设备productKey */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
/* 配置设备deviceName */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
/* 配置设备deviceSecret */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
/* 配置网络连接的安全凭据, 上面已经创建好了 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
/* 配置MQTT默认消息接收回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler);
/* 配置MQTT事件回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);
/* 与服务器建立MQTT连接 */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_connect failed: -0x%04X\r\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
return -1;
}
/* 创建一个单独的线程, 专用于执行aiot_mqtt_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */
g_mqtt_process_thread_running = 1;
res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_process_thread failed: %d\r\n", res);
aiot_mqtt_deinit(&mqtt_handle);
return -1;
}
/* 创建一个单独的线程用于执行aiot_mqtt_recv, 它会循环收取服务器下发的MQTT消息, 并在断线时自动重连 */
g_mqtt_recv_thread_running = 1;
res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_recv_thread failed: %d\r\n", res);
g_mqtt_process_thread_running = 0;
pthread_join(g_mqtt_process_thread, NULL);
aiot_mqtt_deinit(&mqtt_handle);
return -1;
}
*handle = mqtt_handle;
return 0;
}
int32_t demo_mqtt_stop(void **handle)
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
mqtt_handle = *handle;
g_mqtt_process_thread_running = 0;
g_mqtt_recv_thread_running = 0;
pthread_join(g_mqtt_process_thread, NULL);
pthread_join(g_mqtt_recv_thread, NULL);
/* 断开MQTT连接 */
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_disconnect failed: -0x%04X\r\n", -res);
return -1;
}
/* 销毁MQTT实例 */
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x%04X\r\n", -res);
return -1;
}
return 0;
}
/* 事件处理回调, 用户可通过此回调获取日志上报通道的开关状态 */
void demo_logpost_event_handler(void *handle, const aiot_logpost_event_t *event, void *userdata)
{
switch (event->type) {
/* 日志配置事件, 当设备连云成功或者用户在控制台页面控制日志开关时会收到此事件 */
case AIOT_LOGPOSTEVT_CONFIG_DATA: {
printf("user log switch state is: %d\r\n", event->data.config_data.on_off);
printf("toggle it using the switch in device detail page in https://iot.console.aliyun.com\r\n");
}
default:
break;
}
}
/* 上报日志到云端 */
void demo_send_log(void *handle, char *log)
{
int32_t res = 0;
aiot_logpost_msg_t msg;
memset(&msg, 0, sizeof(aiot_logpost_msg_t));
msg.timestamp = 0; /* 单位为ms的时间戳, 填写0则SDK将使用当前的时间戳 */
msg.loglevel = AIOT_LOGPOST_LEVEL_DEBUG; /* 日志级别 */
msg.module_name = "APP"; /* 日志对应的模块 */
msg.code = 200; /* 状态码 */
msg.msg_id = 0; /* 云端下行报文的消息标示符, 若无对应消息可直接填0 */
msg.content = log; /* 日志内容 */
res = aiot_logpost_send(handle, &msg);
if (res < 0) {
printf("aiot_logpost_send failed: -0x%04X\r\n", -res);
}
}
int main(int argc, char *argv[])
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL, *logpost_handle = NULL;
uint8_t sys_log_switch = 1;
/* 建立MQTT连接, 并开启保活线程和接收线程 */
res = demo_mqtt_start(&mqtt_handle, product_key, device_name, device_secret);
if (res < 0) {
printf("demo_mqtt_start failed\r\n");
return -1;
}
/* 创建1个logpost客户端实例并内部初始化默认参数 */
logpost_handle = aiot_logpost_init();
if (logpost_handle == NULL) {
demo_mqtt_stop(&mqtt_handle);
printf("aiot_logpost_init failed\r\n");
return -1;
}
/* 配置logpost的系统日志开关, 打开后将上报网络延时信息 */
res = aiot_logpost_setopt(logpost_handle, AIOT_LOGPOSTOPT_SYS_LOG, (void *)&sys_log_switch);
if (res < STATE_SUCCESS) {
printf("aiot_logpost_setopt AIOT_LOGPOSTOPT_SYS_LOG failed, res: -0x%04X\r\n", -res);
aiot_logpost_deinit(&logpost_handle);
demo_mqtt_stop(&mqtt_handle);
return -1;
}
/* 配置logpost会话, 把它和MQTT会话的句柄关联起来 */
res = aiot_logpost_setopt(logpost_handle, AIOT_LOGPOSTOPT_MQTT_HANDLE, mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_logpost_setopt AIOT_LOGPOSTOPT_MQTT_HANDLE failed, res: -0x%04X\r\n", -res);
aiot_logpost_deinit(&logpost_handle);
demo_mqtt_stop(&mqtt_handle);
return -1;
}
res = aiot_logpost_setopt(logpost_handle, AIOT_LOGPOSTOPT_EVENT_HANDLER, (void *)demo_logpost_event_handler);
if (res < STATE_SUCCESS) {
printf("aiot_logpost_setopt AIOT_LOGPOSTOPT_EVENT_HANDLER failed, res: -0x%04X\r\n", -res);
aiot_logpost_deinit(&logpost_handle);
demo_mqtt_stop(&mqtt_handle);
return -1;
}
/* 主线程进入休眠, 等云平台的logpost回应到达时, 接收线程会调用 demo_logpost_recv_handler() */
while (1) {
sleep(10);
/* TODO: 用户可取消注释上报日志到云端, 注意: 日志模块完成初始化后上报通道默认为关闭状态, 日志模块会在收到设备建连内部事件后立即同步云端控制台的开关状态。
demo_send_log(logpost_handle, "log in while(1)");
*/
}
/* 销毁logpost实例, 一般不会运行到这里 */
res = aiot_logpost_deinit(&logpost_handle);
if (res < STATE_SUCCESS) {
demo_mqtt_stop(&mqtt_handle);
printf("aiot_logpost_deinit failed: -0x%04X\r\n", -res);
return -1;
}
/* 销毁MQTT实例, 退出线程, 一般不会运行到这里 */
res = demo_mqtt_stop(&mqtt_handle);
if (res < 0) {
printf("demo_start_stop failed\r\n");
return -1;
}
return 0;
}

View File

@@ -0,0 +1,325 @@
/*
* 这个例程适用于不支持通过pthread函数在主线程外开独立的固件下载线程的设备
* 它演示了用SDK配置MQTT参数并建立连接, 并在接收到OTA的mqtt消息后开始下载升级固件的过程
* 同时, 它演示了如何将固件的大小分为两半, 每次下载一半的做法. 用户可以进一步将固件分成更多更小的分段
*
* 需要用户关注或修改的部分, 已用 `TODO` 在注释中标明
*
*/
/* TODO: 本例子用到了sleep函数, 所以包含了unistd.h. 如果用户自己的库中有可以替代的函数, 则可以将unistd.h替换掉
*
* 本例子用到了malloc/free函数, 所以用到了stdlib.h, 用户如果自己库中有可以替代的函数, 则需要将stdlib.h替换掉
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
#include "aiot_ota_api.h"
#include "aiot_mqtt_download_api.h"
/* TODO: 替换为自己设备的三元组 */
char *product_key = "${YourProductKey}";
char *device_name = "${YourDeviceName}";
char *device_secret = "${YourDeviceSecret}";
/*
TODO: 替换为自己实例的接入点
对于企业实例, 或者2021年07月30日之后含当日开通的物联网平台服务下公共实例
mqtt_host的格式为"${YourInstanceId}.mqtt.iothub.aliyuncs.com"
其中${YourInstanceId}: 请替换为您企业/公共实例的Id
对于2021年07月30日之前不含当日开通的物联网平台服务下公共实例
需要将mqtt_host修改为: mqtt_host = "${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com"
其中, ${YourProductKey}请替换为设备所属产品的ProductKey。可登录物联网平台控制台在对应实例的设备详情页获取。
${YourRegionId}:请替换为您的物联网平台设备所在地域代码, 比如cn-shanghai等
该情况下完整mqtt_host举例: a1TTmBPIChA.iot-as-mqtt.cn-shanghai.aliyuncs.com
详情请见: https://help.aliyun.com/document_detail/147356.html
*/
char *mqtt_host = "${YourInstanceId}.mqtt.iothub.aliyuncs.com";
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
void *g_ota_handle = NULL;
void *g_dl_handle = NULL;
uint32_t g_firmware_size = 0;
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
*
* 例如: [1578463098.611][LK-0309] pub: /ota/device/upgrade/a13FN5TplKq/ota_demo
*
* 上面这条日志的code就是0309(十六进制), code值的定义见core/aiot_state_api.h
*
*/
/* 日志回调函数, SDK的日志会从这里输出 */
int32_t demo_state_logcb(int32_t code, char *message)
{
printf("%s", message);
return 0;
}
/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata)
{
switch (event->type) {
/* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */
case AIOT_MQTTEVT_CONNECT: {
printf("AIOT_MQTTEVT_CONNECT\r\n");
/* TODO: 处理SDK建连成功, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
/* SDK因为网络状况被动断连后, 自动发起重连已成功 */
case AIOT_MQTTEVT_RECONNECT: {
printf("AIOT_MQTTEVT_RECONNECT\r\n");
/* TODO: 处理SDK重连成功, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
/* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */
case AIOT_MQTTEVT_DISCONNECT: {
char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
("heartbeat disconnect");
printf("AIOT_MQTTEVT_DISCONNECT: %s\r\n", cause);
/* TODO: 处理SDK被动断连, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
default: {
}
}
}
/* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */
void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *const packet, void *userdata)
{
switch (packet->type) {
case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: {
printf("heartbeat response\r\n");
/* TODO: 处理服务器对心跳的回应, 一般不处理 */
}
break;
case AIOT_MQTTRECV_SUB_ACK: {
printf("suback, res: -0x%04X, packet id: %d, max qos: %d\r\n",
-packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);
/* TODO: 处理服务器对订阅请求的回应, 一般不处理 */
}
break;
case AIOT_MQTTRECV_PUB: {
printf("pub, qos: %d, topic: %.*s\r\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic);
printf("pub, payload: %.*s\r\n", packet->data.pub.payload_len, packet->data.pub.payload);
/* TODO: 处理服务器下发的业务报文 */
}
break;
case AIOT_MQTTRECV_PUB_ACK: {
printf("puback, packet id: %d\r\n", packet->data.pub_ack.packet_id);
/* TODO: 处理服务器对QoS1上报消息的回应, 一般不处理 */
}
break;
default: {
}
}
}
/* 下载收包回调, 用户调用 aiot_download_recv() 后, SDK收到数据会进入这个函数, 把下载到的数据交给用户 */
/* TODO: 一般来说, 设备升级时, 会在这个回调中, 把下载到的数据写到Flash上 */
void user_download_recv_handler(void *handle, const aiot_mqtt_download_recv_t *packet, void *userdata)
{
uint32_t data_buffer_len = 0;
/* 目前只支持 packet->type 为 AIOT_DLRECV_HTTPBODY 的情况 */
if (!packet || AIOT_MDRECV_DATA_RESP != packet->type) {
return;
}
/* 用户应在此实现文件本地固化的操作 */
FILE *file = fopen("mota_demo.bin", "ab");
fwrite(packet->data.data_resp.data, packet->data.data_resp.data_size, sizeof(int8_t), file);
fclose(file);
data_buffer_len = packet->data.data_resp.data_size;
printf("download %03d%% done, +%d bytes\r\n", packet->data.data_resp.percent, data_buffer_len);
}
/* 用户通过 aiot_ota_setopt() 注册的OTA消息处理回调, 如果SDK收到了OTA相关的MQTT消息, 会自动识别, 调用这个回调函数 */
void user_ota_recv_handler(void *ota_handle, aiot_ota_recv_t *ota_msg, void *userdata)
{
uint32_t request_size = 10 * 1024;
switch (ota_msg->type) {
case AIOT_OTARECV_FOTA: {
if (NULL == ota_msg->task_desc || ota_msg->task_desc->protocol_type != AIOT_OTA_PROTOCOL_MQTT) {
break;
}
if(g_dl_handle != NULL) {
aiot_mqtt_download_deinit(&g_dl_handle);
}
printf("OTA target firmware version: %s, size: %u Bytes\r\n", ota_msg->task_desc->version,
ota_msg->task_desc->size_total);
void *md_handler = aiot_mqtt_download_init();
aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_TASK_DESC, ota_msg->task_desc);
/* 设置下载一包的大小,对于资源受限设备可以调整该值大小 */
aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_DATA_REQUEST_SIZE, &request_size);
/* 部分场景下用户如果只需要下载文件的一部分即下载指定range的文件可以设置文件起始位置、终止位置。
* 若设置range区间下载单包报文的数据有CRC校验但SDK将不进行完整文件MD5校验
* 默认下载全部文件单包报文的数据有CRC校验并且SDK会对整个文件进行md5校验 */
// uint32_t range_start = 10, range_end = 50 * 1024 + 10;
// aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_RANGE_START, &range_start);
// aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_RANGE_END, &range_end);
aiot_mqtt_download_setopt(md_handler, AIOT_MDOPT_RECV_HANDLE, user_download_recv_handler);
g_dl_handle = md_handler;
}
break;
default:
break;
}
}
int main(int argc, char *argv[])
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
uint16_t port = 1883; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
char *cur_version = NULL;
void *ota_handle = NULL;
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(demo_state_logcb);
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
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根证书长度 */
/* 创建1个MQTT客户端实例并内部初始化默认参数 */
mqtt_handle = aiot_mqtt_init();
if (mqtt_handle == NULL) {
return -1;
}
/* 配置MQTT服务器地址 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);
/* 配置MQTT服务器端口 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
/* 配置设备productKey */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
/* 配置设备deviceName */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
/* 配置设备deviceSecret */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
/* 配置网络连接的安全凭据, 上面已经创建好了 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
/* 配置MQTT默认消息接收回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler);
/* 配置MQTT事件回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);
/* 与MQTT例程不同的是, 这里需要增加创建OTA会话实例的语句 */
ota_handle = aiot_ota_init();
if (NULL == ota_handle) {
goto exit;
}
/* 用以下语句, 把OTA会话和MQTT会话关联起来 */
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_MQTT_HANDLE, mqtt_handle);
/* 用以下语句, 设置OTA会话的数据接收回调, SDK收到OTA相关推送时, 会进入这个回调函数 */
aiot_ota_setopt(ota_handle, AIOT_OTAOPT_RECV_HANDLER, user_ota_recv_handler);
g_ota_handle = ota_handle;
/* 与服务器建立MQTT连接 */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_connect failed: -0x%04X\r\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
goto exit;
}
/* TODO: 非常重要!!!
*
* cur_version 要根据用户实际情况, 改成从设备的配置区获取, 要反映真实的版本号, 而不能像示例这样写为固定值
*
* 1. 如果设备从未上报过版本号, 在控制台网页将无法部署升级任务
* 2. 如果设备升级完成后, 上报的不是新的版本号, 在控制台网页将会显示升级失败
*
*/
cur_version = "1.0.0";
/* 演示MQTT连接建立起来之后, 就可以上报当前设备的版本号了 */
res = aiot_ota_report_version(ota_handle, cur_version);
if (res < STATE_SUCCESS) {
printf("report version failed, code is -0x%04X\r\n", -res);
}
while (1) {
aiot_mqtt_process(mqtt_handle);
aiot_mqtt_recv(mqtt_handle);
if(g_dl_handle != NULL) {
int32_t res = aiot_mqtt_download_process(g_dl_handle);
if(STATE_MQTT_DOWNLOAD_SUCCESS == res) {
/* 升级成功,这里重启并且上报新的版本号 */
printf("mqtt download ota success \r\n");
aiot_mqtt_download_deinit(&g_dl_handle);
break;
} else if(STATE_MQTT_DOWNLOAD_FAILED_RECVERROR == res
|| STATE_MQTT_DOWNLOAD_FAILED_TIMEOUT == res
|| STATE_MQTT_DOWNLOAD_FAILED_MISMATCH == res) {
printf("mqtt download ota failed \r\n");
aiot_mqtt_download_deinit(&g_dl_handle);
break;
}
}
}
aiot_ota_deinit(&ota_handle);
/* 断开MQTT连接, 一般不会运行到这里 */
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_disconnect failed: -0x%04X\r\n", -res);
goto exit;
}
exit:
while (1) {
/* 销毁MQTT实例, 一般不会运行到这里 */
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x%04X\r\n", -res);
return -1;
} else {
break;
}
}
/* 销毁OTA实例, 一般不会运行到这里 */
aiot_ota_deinit(&ota_handle);
return 0;
}

View File

@@ -0,0 +1,297 @@
/*
* 这个例程适用于`Linux`这类支持pthread的POSIX设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建2个线程
*
* + 一个线程用于保活长连接
* + 一个线程用于接收消息, 并在有消息到达时进入默认的数据回调, 在连接状态变化时进入事件回调
*
* 需要用户关注或修改的部分, 已经用 TODO 在注释中标明
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
/* TODO: 替换为自己设备的三元组 */
char *product_key = "${YourProductKey}";
char *device_name = "${YourDeviceName}";
char *device_secret = "${YourDeviceSecret}";
/*
TODO: 替换为自己实例的接入点
对于企业实例, 或者2021年07月30日之后含当日开通的物联网平台服务下公共实例
mqtt_host的格式为"${YourInstanceId}.mqtt.iothub.aliyuncs.com"
其中${YourInstanceId}: 请替换为您企业/公共实例的Id
对于2021年07月30日之前不含当日开通的物联网平台服务下公共实例
需要将mqtt_host修改为: mqtt_host = "${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com"
其中, ${YourProductKey}请替换为设备所属产品的ProductKey。可登录物联网平台控制台在对应实例的设备详情页获取。
${YourRegionId}:请替换为您的物联网平台设备所在地域代码, 比如cn-shanghai等
该情况下完整mqtt_host举例: a1TTmBPIChA.iot-as-mqtt.cn-shanghai.aliyuncs.com
详情请见: https://help.aliyun.com/document_detail/147356.html
*/
char *mqtt_host = "${YourInstanceId}.mqtt.iothub.aliyuncs.com";
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
static pthread_t g_mqtt_process_thread;
static pthread_t g_mqtt_recv_thread;
static uint8_t g_mqtt_process_thread_running = 0;
static uint8_t g_mqtt_recv_thread_running = 0;
/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
*
* 例如: [1577589489.033][LK-0317] mqtt_basic_demo&gb80sFmX7yX
*
* 上面这条日志的code就是0317(十六进制), code值的定义见core/aiot_state_api.h
*
*/
/* 日志回调函数, SDK的日志会从这里输出 */
int32_t demo_state_logcb(int32_t code, char *message)
{
printf("%s", message);
return 0;
}
/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata)
{
switch (event->type) {
/* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */
case AIOT_MQTTEVT_CONNECT: {
printf("AIOT_MQTTEVT_CONNECT\n");
/* TODO: 处理SDK建连成功, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
/* SDK因为网络状况被动断连后, 自动发起重连已成功 */
case AIOT_MQTTEVT_RECONNECT: {
printf("AIOT_MQTTEVT_RECONNECT\n");
/* TODO: 处理SDK重连成功, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
/* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */
case AIOT_MQTTEVT_DISCONNECT: {
char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
("heartbeat disconnect");
printf("AIOT_MQTTEVT_DISCONNECT: %s\n", cause);
/* TODO: 处理SDK被动断连, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
default: {
}
}
}
/* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */
void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata)
{
switch (packet->type) {
case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: {
printf("heartbeat response\n");
/* TODO: 处理服务器对心跳的回应, 一般不处理 */
}
break;
case AIOT_MQTTRECV_SUB_ACK: {
printf("suback, res: -0x%04X, packet id: %d, max qos: %d\n",
-packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);
/* TODO: 处理服务器对订阅请求的回应, 一般不处理 */
}
break;
case AIOT_MQTTRECV_PUB: {
printf("pub, qos: %d, topic: %.*s\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic);
printf("pub, payload: %.*s\n", packet->data.pub.payload_len, packet->data.pub.payload);
/* TODO: 处理服务器下发的业务报文 */
}
break;
case AIOT_MQTTRECV_PUB_ACK: {
printf("puback, packet id: %d\n", packet->data.pub_ack.packet_id);
/* TODO: 处理服务器对QoS1上报消息的回应, 一般不处理 */
}
break;
default: {
}
}
}
/* 执行aiot_mqtt_process的线程, 包含心跳发送和QoS1消息重发 */
void *demo_mqtt_process_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_process_thread_running) {
res = aiot_mqtt_process(args);
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
return NULL;
}
/* 执行aiot_mqtt_recv的线程, 包含网络自动重连和从服务器收取MQTT消息 */
void *demo_mqtt_recv_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_recv_thread_running) {
res = aiot_mqtt_recv(args);
if (res < STATE_SUCCESS) {
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
}
return NULL;
}
int main(int argc, char *argv[])
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
uint16_t port = 443; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(demo_state_logcb);
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
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根证书长度 */
/* 创建1个MQTT客户端实例并内部初始化默认参数 */
mqtt_handle = aiot_mqtt_init();
if (mqtt_handle == NULL) {
printf("aiot_mqtt_init failed\n");
return -1;
}
/* TODO: 如果以下代码不被注释, 则例程会用TCP而不是TLS连接云平台 */
/*
{
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
cred.option = AIOT_SYSDEP_NETWORK_CRED_NONE;
}
*/
/* 配置MQTT服务器地址 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);
/* 配置MQTT服务器端口 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
/* 配置设备productKey */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
/* 配置设备deviceName */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
/* 配置设备deviceSecret */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
/* 配置网络连接的安全凭据, 上面已经创建好了 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
/* 配置MQTT默认消息接收回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler);
/* 配置MQTT事件回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);
/* 与服务器建立MQTT连接 */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_connect failed: -0x%04X\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
return -1;
}
/* MQTT 订阅topic功能示例, 请根据自己的业务需求进行使用 */
/* {
char *sub_topic = "/sys/${YourProductKey}/${YourDeviceName}/thing/event/+/post_reply";
res = aiot_mqtt_sub(mqtt_handle, sub_topic, NULL, 1, NULL);
if (res < 0) {
printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res);
return -1;
}
} */
/* MQTT 发布消息功能示例, 请根据自己的业务需求进行使用 */
/* {
char *pub_topic = "/sys/${YourProductKey}/${YourDeviceName}/thing/event/property/post";
char *pub_payload = "{\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":0}}";
res = aiot_mqtt_pub(mqtt_handle, pub_topic, (uint8_t *)pub_payload, (uint32_t)strlen(pub_payload), 0);
if (res < 0) {
printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res);
return -1;
}
} */
/* 创建一个单独的线程, 专用于执行aiot_mqtt_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */
g_mqtt_process_thread_running = 1;
res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_process_thread failed: %d\n", res);
return -1;
}
/* 创建一个单独的线程用于执行aiot_mqtt_recv, 它会循环收取服务器下发的MQTT消息, 并在断线时自动重连 */
g_mqtt_recv_thread_running = 1;
res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_recv_thread failed: %d\n", res);
return -1;
}
/* 主循环进入休眠 */
while (1) {
sleep(1);
}
/* 断开MQTT连接, 一般不会运行到这里 */
g_mqtt_process_thread_running = 0;
g_mqtt_recv_thread_running = 0;
sleep(1);
pthread_join(g_mqtt_process_thread, NULL);
pthread_join(g_mqtt_recv_thread, NULL);
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);
return -1;
}
/* 销毁MQTT实例, 一般不会运行到这里 */
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x%04X\n", -res);
return -1;
}
return 0;
}

View File

@@ -0,0 +1,277 @@
/*
* 这个例程适用于`Linux`这类支持pthread的POSIX设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建2个线程
*
* + 一个线程用于保活长连接
* + 一个线程用于接收消息, 并在有消息到达时进入默认的数据回调, 在连接状态变化时进入事件回调
*
* 需要用户关注或修改的部分, 已经用 TODO 在注释中标明
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
/* TODO: 替换为自己设备的三元组 */
char *product_key = "${YourProductKey}";
char *device_name = "${YourDeviceName}";
char *device_secret = "${YourDeviceSecret}";
/*
TODO: 替换为自己实例的接入点
对于企业实例, 或者2021年07月30日之后含当日开通的物联网平台服务下公共实例
mqtt_host的格式为"${YourInstanceId}.mqtt.iothub.aliyuncs.com"
其中${YourInstanceId}: 请替换为您企业/公共实例的Id
对于2021年07月30日之前不含当日开通的物联网平台服务下公共实例
需要将mqtt_host修改为: mqtt_host = "${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com"
其中, ${YourProductKey}请替换为设备所属产品的ProductKey。可登录物联网平台控制台在对应实例的设备详情页获取。
${YourRegionId}:请替换为您的物联网平台设备所在地域代码, 比如cn-shanghai等
该情况下完整mqtt_host举例: a1TTmBPIChA.iot-as-mqtt.cn-shanghai.aliyuncs.com
详情请见: https://help.aliyun.com/document_detail/147356.html
*/
char *mqtt_host = "${YourInstanceId}.mqtt.iothub.aliyuncs.com";
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
static pthread_t g_mqtt_process_thread;
static pthread_t g_mqtt_recv_thread;
static uint8_t g_mqtt_process_thread_running = 0;
static uint8_t g_mqtt_recv_thread_running = 0;
/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
*
* 例如: [1577589489.033][LK-0317] mqtt_broadcast_demo&gb80sFmX7yX
*
* 上面这条日志的code就是0317(十六进制), code值的定义见core/aiot_state_api.h
*
*/
/* 日志回调函数, SDK的日志会从这里输出 */
int32_t demo_state_logcb(int32_t code, char *message)
{
printf("%s", message);
return 0;
}
/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata)
{
switch (event->type) {
/* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */
case AIOT_MQTTEVT_CONNECT: {
printf("AIOT_MQTTEVT_CONNECT\n");
/* TODO: 处理SDK建连成功, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
/* SDK因为网络状况被动断连后, 自动发起重连已成功 */
case AIOT_MQTTEVT_RECONNECT: {
printf("AIOT_MQTTEVT_RECONNECT\n");
/* TODO: 处理SDK重连成功, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
/* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */
case AIOT_MQTTEVT_DISCONNECT: {
char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
("heartbeat disconnect");
printf("AIOT_MQTTEVT_DISCONNECT: %s\n", cause);
/* TODO: 处理SDK被动断连, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
default: {
}
}
}
/* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */
void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata)
{
switch (packet->type) {
case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: {
printf("heartbeat response\n");
/* TODO: 处理服务器对心跳的回应, 一般不处理 */
}
break;
case AIOT_MQTTRECV_SUB_ACK: {
printf("suback, res: -0x%04X, packet id: %d, max qos: %d\n",
-packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);
/* TODO: 处理服务器对订阅请求的回应, 一般不处理 */
}
break;
case AIOT_MQTTRECV_PUB: {
printf("pub, qos: %d, topic: %.*s\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic);
printf("pub, payload: %.*s\n", packet->data.pub.payload_len, packet->data.pub.payload);
/* TODO: 处理服务器下发的业务报文, 此处打印broadcast的报文 */
/* 值得注意的是云端下发的广播topic格式为 /sys/${product_key}/${device_name}/broadcast/request/+ */
}
break;
case AIOT_MQTTRECV_PUB_ACK: {
printf("puback, packet id: %d\n", packet->data.pub_ack.packet_id);
/* TODO: 处理服务器对QoS1上报消息的回应, 一般不处理 */
}
break;
default: {
}
}
}
/* 执行aiot_mqtt_process的线程, 包含心跳发送和QoS1消息重发 */
void *demo_mqtt_process_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_process_thread_running) {
res = aiot_mqtt_process(args);
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
return NULL;
}
/* 执行aiot_mqtt_recv的线程, 包含网络自动重连和从服务器收取MQTT消息 */
void *demo_mqtt_recv_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_recv_thread_running) {
res = aiot_mqtt_recv(args);
if (res < STATE_SUCCESS) {
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
}
return NULL;
}
int main(int argc, char *argv[])
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
uint16_t port = 443; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(demo_state_logcb);
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
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根证书长度 */
/* 创建1个MQTT客户端实例并内部初始化默认参数 */
mqtt_handle = aiot_mqtt_init();
if (mqtt_handle == NULL) {
printf("aiot_mqtt_init failed\n");
return -1;
}
/* TODO: 如果以下代码不被注释, 则例程会用TCP而不是TLS连接云平台 */
/*
{
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
cred.option = AIOT_SYSDEP_NETWORK_CRED_NONE;
}
*/
/* 配置MQTT服务器地址 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);
/* 配置MQTT服务器端口 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
/* 配置设备productKey */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
/* 配置设备deviceName */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
/* 配置设备deviceSecret */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
/* 配置网络连接的安全凭据, 上面已经创建好了 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
/* 配置MQTT默认消息接收回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler);
/* 配置MQTT事件回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);
/* 与服务器建立MQTT连接 */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_connect failed: -0x%04X\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
return -1;
}
/* 创建一个单独的线程, 专用于执行aiot_mqtt_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */
g_mqtt_process_thread_running = 1;
res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_process_thread failed: %d\n", res);
return -1;
}
/* 创建一个单独的线程用于执行aiot_mqtt_recv, 它会循环收取服务器下发的MQTT消息, 并在断线时自动重连 */
g_mqtt_recv_thread_running = 1;
res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_recv_thread failed: %d\n", res);
return -1;
}
/* 主循环进入休眠 */
while (1) {
sleep(1);
}
/* 断开MQTT连接, 一般不会运行到这里 */
g_mqtt_process_thread_running = 0;
g_mqtt_recv_thread_running = 0;
sleep(1);
pthread_join(g_mqtt_process_thread, NULL);
pthread_join(g_mqtt_recv_thread, NULL);
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);
return -1;
}
/* 销毁MQTT实例, 一般不会运行到这里 */
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x%04X\n", -res);
return -1;
}
return 0;
}

View File

@@ -0,0 +1,313 @@
/*
* 这个例程适用于`Linux`这类支持pthread的POSIX设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建2个线程
*
* + 一个线程用于保活长连接
* + 一个线程用于接收消息, 并在有消息到达时进入默认的数据回调, 在连接状态变化时进入事件回调
*
* 需要用户关注或修改的部分, 已经用 TODO 在注释中标明
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
/* TODO: 替换为自己设备的三元组 */
char *product_key = "${YourProductKey}";
char *device_name = "${YourDeviceName}";
char *device_secret = "${YourDeviceSecret}";
/*
TODO: 替换为自己实例的接入点
对于企业实例, 或者2021年07月30日之后含当日开通的物联网平台服务下公共实例
mqtt_host的格式为"${YourInstanceId}.mqtt.iothub.aliyuncs.com"
其中${YourInstanceId}: 请替换为您企业/公共实例的Id
对于2021年07月30日之前不含当日开通的物联网平台服务下公共实例
需要将mqtt_host修改为: mqtt_host = "${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com"
其中, ${YourProductKey}请替换为设备所属产品的ProductKey。可登录物联网平台控制台在对应实例的设备详情页获取。
${YourRegionId}:请替换为您的物联网平台设备所在地域代码, 比如cn-shanghai等
该情况下完整mqtt_host举例: a1TTmBPIChA.iot-as-mqtt.cn-shanghai.aliyuncs.com
详情请见: https://help.aliyun.com/document_detail/147356.html
*/
char *mqtt_host = "${YourInstanceId}.mqtt.iothub.aliyuncs.com";
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
static pthread_t g_mqtt_process_thread;
static pthread_t g_mqtt_recv_thread;
static uint8_t g_mqtt_process_thread_running = 0;
static uint8_t g_mqtt_recv_thread_running = 0;
/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
*
* 例如: [1577589489.033][LK-0317] {YourDeviceName}&{YourProductKey}
*
* 上面这条日志的code就是0317(十六进制), code值的定义见core/aiot_state_api.h
*
*/
/* 日志回调函数, SDK的日志会从这里输出 */
int32_t demo_state_logcb(int32_t code, char *message)
{
printf("%s", message);
return 0;
}
/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata)
{
switch (event->type) {
/* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */
case AIOT_MQTTEVT_CONNECT: {
printf("AIOT_MQTTEVT_CONNECT\n");
/* TODO: 处理SDK建连成功, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
/* SDK因为网络状况被动断连后, 自动发起重连已成功 */
case AIOT_MQTTEVT_RECONNECT: {
printf("AIOT_MQTTEVT_RECONNECT\n");
/* TODO: 处理SDK重连成功, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
/* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */
case AIOT_MQTTEVT_DISCONNECT: {
char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
("heartbeat disconnect");
printf("AIOT_MQTTEVT_DISCONNECT: %s\n", cause);
/* TODO: 处理SDK被动断连, 不可以在这里调用耗时较长的阻塞函数 */
}
break;
default: {
}
}
}
/* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */
void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata)
{
switch (packet->type) {
case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: {
printf("heartbeat response\n");
/* TODO: 处理服务器对心跳的回应, 一般不处理 */
}
break;
case AIOT_MQTTRECV_SUB_ACK: {
printf("suback, res: -0x%04X, packet id: %d, max qos: %d\n",
-packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);
/* TODO: 处理服务器对订阅请求的回应, 一般不处理 */
}
break;
case AIOT_MQTTRECV_PUB: {
printf("pub, qos: %d, topic: %.*s\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic);
printf("pub, payload: %.*s\n", packet->data.pub.payload_len, packet->data.pub.payload);
/* TODO: 处理服务器下发的业务报文, 此处如果收到rrpc的报文, 那么应答时使用相同的topic即可 */
/* 更多信息请参考 https://help.aliyun.com/document_detail/90570.html */
/* 下面是一个rrpc的应答示例 */
/* {
char *payload = "pong";
char resp_topic[256];
if(packet->data.pub.topic_len > 256) {
break;
}
memset(resp_topic, 0, sizeof(resp_topic));
memcpy(resp_topic, packet->data.pub.topic, packet->data.pub.topic_len);
aiot_mqtt_pub(handle, resp_topic, (uint8_t *)payload, (uint32_t)strlen(payload), 0);
} */
}
break;
case AIOT_MQTTRECV_PUB_ACK: {
printf("puback, packet id: %d\n", packet->data.pub_ack.packet_id);
/* TODO: 处理服务器对QoS1上报消息的回应, 一般不处理 */
}
break;
default: {
}
}
}
/* 执行aiot_mqtt_process的线程, 包含心跳发送和QoS1消息重发 */
void *demo_mqtt_process_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_process_thread_running) {
res = aiot_mqtt_process(args);
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
return NULL;
}
/* 执行aiot_mqtt_recv的线程, 包含网络自动重连和从服务器收取MQTT消息 */
void *demo_mqtt_recv_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_recv_thread_running) {
res = aiot_mqtt_recv(args);
if (res < STATE_SUCCESS) {
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
}
return NULL;
}
int main(int argc, char *argv[])
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
uint16_t port = 443; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(demo_state_logcb);
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
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根证书长度 */
/* 创建1个MQTT客户端实例并内部初始化默认参数 */
mqtt_handle = aiot_mqtt_init();
if (mqtt_handle == NULL) {
printf("aiot_mqtt_init failed\n");
return -1;
}
/* TODO: 如果以下代码不被注释, 则例程会用TCP而不是TLS连接云平台 */
/*
{
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
cred.option = AIOT_SYSDEP_NETWORK_CRED_NONE;
}
*/
/* 配置MQTT服务器地址 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);
/* 配置MQTT服务器端口 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
/* 配置设备productKey */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
/* 配置设备deviceName */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
/* 配置设备deviceSecret */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
/* 配置网络连接的安全凭据, 上面已经创建好了 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
/* 配置MQTT默认消息接收回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler);
/* 配置MQTT事件回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);
/* 与服务器建立MQTT连接 */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_connect failed: -0x%04X\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
return -1;
}
/* MQTT 订阅topic功能示例, 请根据自己的业务需求进行使用 */
{
char *sub_topic = "/{YourProductKey}/{YourDeviceName}/user/get";
res = aiot_mqtt_sub(mqtt_handle, sub_topic, NULL, 1, NULL);
if (res < 0) {
printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res);
return -1;
}
}
/* MQTT 发布消息功能示例, 请根据自己的业务需求进行使用 */
/* {
char *pub_topic = "/sys/{YourProductKey}/{YourDeviceName}/thing/event/property/post";
char *pub_payload = "{\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":0}}";
res = aiot_mqtt_pub(mqtt_handle, pub_topic, (uint8_t *)pub_payload, (uint32_t)strlen(pub_payload), 0);
if (res < 0) {
printf("aiot_mqtt_pub failed, res: -0x%04X\n", -res);
return -1;
}
} */
/* 创建一个单独的线程, 专用于执行aiot_mqtt_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */
g_mqtt_process_thread_running = 1;
res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_process_thread failed: %d\n", res);
return -1;
}
/* 创建一个单独的线程用于执行aiot_mqtt_recv, 它会循环收取服务器下发的MQTT消息, 并在断线时自动重连 */
g_mqtt_recv_thread_running = 1;
res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_recv_thread failed: %d\n", res);
return -1;
}
/* 主循环进入休眠 */
while (1) {
sleep(1);
}
/* 断开MQTT连接, 一般不会运行到这里 */
g_mqtt_process_thread_running = 0;
g_mqtt_recv_thread_running = 0;
sleep(1);
pthread_join(g_mqtt_process_thread, NULL);
pthread_join(g_mqtt_recv_thread, NULL);
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);
return -1;
}
/* 销毁MQTT实例, 一般不会运行到这里 */
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x%04X\n", -res);
return -1;
}
return 0;
}

View File

@@ -0,0 +1,413 @@
/*
* 这个例程适用于`Linux`这类支持pthread的POSIX设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建2个线程
*
* + 一个线程用于保活长连接
* + 一个线程用于接收消息, 并在有消息到达时进入默认的数据回调, 在连接状态变化时进入事件回调
*
* 接着在MQTT连接上发送NTP查询请求, 如果云平台的回应报文到达, 从接收线程会调用NTP消息处理的回调函数, 把对时后的本地时间打印出来
*
* 需要用户关注或修改的部分, 已经用 TODO 在注释中标明
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
#include "aiot_ntp_api.h"
/* TODO: 替换为自己设备的三元组 */
char *product_key = "${YourProductKey}";
char *device_name = "${YourDeviceName}";
char *device_secret = "${YourDeviceSecret}";
/*
TODO: 替换为自己实例的接入点
对于企业实例, 或者2021年07月30日之后含当日开通的物联网平台服务下公共实例
mqtt_host的格式为"${YourInstanceId}.mqtt.iothub.aliyuncs.com"
其中${YourInstanceId}: 请替换为您企业/公共实例的Id
对于2021年07月30日之前不含当日开通的物联网平台服务下公共实例
需要将mqtt_host修改为: mqtt_host = "${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com"
其中, ${YourProductKey}请替换为设备所属产品的ProductKey。可登录物联网平台控制台在对应实例的设备详情页获取。
${YourRegionId}:请替换为您的物联网平台设备所在地域代码, 比如cn-shanghai等
该情况下完整mqtt_host举例: a1TTmBPIChA.iot-as-mqtt.cn-shanghai.aliyuncs.com
详情请见: https://help.aliyun.com/document_detail/147356.html
*/
char *mqtt_host = "${YourInstanceId}.mqtt.iothub.aliyuncs.com";
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
static pthread_t g_mqtt_process_thread;
static pthread_t g_mqtt_recv_thread;
static uint8_t g_mqtt_process_thread_running = 0;
static uint8_t g_mqtt_recv_thread_running = 0;
/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
*
* 例如: [1581501698.455][LK-0309] pub: /ext/ntp/a13FN5TplKq/ntp_basic_demo/request
*
* 上面这条日志的code就是0309(十六进制), code值的定义见core/aiot_state_api.h
*
*/
/* 日志回调函数, SDK的日志会从这里输出 */
static int32_t demo_state_logcb(int32_t code, char *message)
{
printf("%s", message);
return 0;
}
/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata)
{
switch (event->type) {
/* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */
case AIOT_MQTTEVT_CONNECT: {
printf("AIOT_MQTTEVT_CONNECT\n");
}
break;
/* SDK因为网络状况被动断连后, 自动发起重连已成功 */
case AIOT_MQTTEVT_RECONNECT: {
printf("AIOT_MQTTEVT_RECONNECT\n");
}
break;
/* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */
case AIOT_MQTTEVT_DISCONNECT: {
char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
("heartbeat disconnect");
printf("AIOT_MQTTEVT_DISCONNECT: %s\n", cause);
}
break;
default: {
}
}
}
/* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */
void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata)
{
switch (packet->type) {
case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: {
printf("heartbeat response\n");
}
break;
case AIOT_MQTTRECV_SUB_ACK: {
printf("suback, res: -0x%04X, packet id: %d, max qos: %d\n",
-packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);
}
break;
case AIOT_MQTTRECV_PUB: {
printf("pub, qos: %d, topic: %.*s\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic);
printf("pub, payload: %.*s\n", packet->data.pub.payload_len, packet->data.pub.payload);
}
break;
case AIOT_MQTTRECV_PUB_ACK: {
printf("puback, packet id: %d\n", packet->data.pub_ack.packet_id);
}
break;
default: {
}
}
}
/* 执行aiot_mqtt_process的线程, 包含心跳发送和QoS1消息重发 */
void *demo_mqtt_process_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_process_thread_running) {
res = aiot_mqtt_process(args);
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
return NULL;
}
/* 执行aiot_mqtt_recv的线程, 包含网络自动重连和从服务器收取MQTT消息 */
void *demo_mqtt_recv_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_recv_thread_running) {
res = aiot_mqtt_recv(args);
if (res < STATE_SUCCESS) {
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
}
return NULL;
}
int32_t demo_mqtt_start(void **handle)
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
uint16_t port = 443; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(demo_state_logcb);
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
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根证书长度 */
/* 创建1个MQTT客户端实例并内部初始化默认参数 */
mqtt_handle = aiot_mqtt_init();
if (mqtt_handle == NULL) {
printf("aiot_mqtt_init failed\n");
return -1;
}
/* TODO: 如果以下代码不被注释, 则例程会用TCP而不是TLS连接云平台 */
/*
{
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
cred.option = AIOT_SYSDEP_NETWORK_CRED_NONE;
}
*/
/* 配置MQTT服务器地址 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);
/* 配置MQTT服务器端口 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
/* 配置设备productKey */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
/* 配置设备deviceName */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
/* 配置设备deviceSecret */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
/* 配置网络连接的安全凭据, 上面已经创建好了 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
/* 配置MQTT默认消息接收回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler);
/* 配置MQTT事件回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);
/* 与服务器建立MQTT连接 */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_connect failed: -0x%04X\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
return -1;
}
/* 创建一个单独的线程, 专用于执行aiot_mqtt_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */
g_mqtt_process_thread_running = 1;
res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_process_thread failed: %d\n", res);
aiot_mqtt_deinit(&mqtt_handle);
return -1;
}
/* 创建一个单独的线程用于执行aiot_mqtt_recv, 它会循环收取服务器下发的MQTT消息, 并在断线时自动重连 */
g_mqtt_recv_thread_running = 1;
res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_recv_thread failed: %d\n", res);
g_mqtt_process_thread_running = 0;
pthread_join(g_mqtt_process_thread, NULL);
aiot_mqtt_deinit(&mqtt_handle);
return -1;
}
*handle = mqtt_handle;
return 0;
}
int32_t demo_mqtt_stop(void **handle)
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
mqtt_handle = *handle;
g_mqtt_process_thread_running = 0;
g_mqtt_recv_thread_running = 0;
pthread_join(g_mqtt_process_thread, NULL);
pthread_join(g_mqtt_recv_thread, NULL);
/* 断开MQTT连接 */
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);
return -1;
}
/* 销毁MQTT实例 */
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x%04X\n", -res);
return -1;
}
return 0;
}
/* 事件处理回调, */
void demo_ntp_event_handler(void *handle, const aiot_ntp_event_t *event, void *userdata)
{
switch (event->type) {
case AIOT_NTPEVT_INVALID_RESPONSE: {
printf("AIOT_NTPEVT_INVALID_RESPONSE\n");
}
break;
case AIOT_NTPEVT_INVALID_TIME_FORMAT: {
printf("AIOT_NTPEVT_INVALID_TIME_FORMAT\n");
}
break;
default: {
}
}
}
/* TODO: 数据处理回调, 当SDK从网络上收到ntp消息时被调用 */
void demo_ntp_recv_handler(void *handle, const aiot_ntp_recv_t *packet, void *userdata)
{
switch (packet->type) {
/* TODO: 结构体 aiot_ntp_recv_t{} 中包含当前时区下, 年月日时分秒的数值, 可在这里把它们解析储存起来 */
case AIOT_NTPRECV_LOCAL_TIME: {
printf("local time: %llu, %02d/%02d/%02d-%02d:%02d:%02d:%d\n",
(long long unsigned int)packet->data.local_time.timestamp,
packet->data.local_time.year,
packet->data.local_time.mon, packet->data.local_time.day, packet->data.local_time.hour, packet->data.local_time.min,
packet->data.local_time.sec, packet->data.local_time.msec);
}
break;
default: {
}
}
}
int main(int argc, char *argv[])
{
int32_t res = STATE_SUCCESS;
/*
* 这里用中国所在的东8区演示功能, 因此例程运行时打印的是北京时间
*
* TODO: 若是其它时区, 可做相应调整, 如西3区则把8改为-3, time_zone的合理取值是-12到+12的整数
*
*/
int8_t time_zone = 8;
void *mqtt_handle = NULL, *ntp_handle = NULL;
/* 建立MQTT连接, 并开启保活线程和接收线程 */
res = demo_mqtt_start(&mqtt_handle);
if (res < 0) {
printf("demo_mqtt_start failed\n");
return -1;
}
/* 创建1个ntp客户端实例并内部初始化默认参数 */
ntp_handle = aiot_ntp_init();
if (ntp_handle == NULL) {
demo_mqtt_stop(&mqtt_handle);
printf("aiot_ntp_init failed\n");
return -1;
}
res = aiot_ntp_setopt(ntp_handle, AIOT_NTPOPT_MQTT_HANDLE, mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_ntp_setopt AIOT_NTPOPT_MQTT_HANDLE failed, res: -0x%04X\n", -res);
aiot_ntp_deinit(&ntp_handle);
demo_mqtt_stop(&mqtt_handle);
return -1;
}
res = aiot_ntp_setopt(ntp_handle, AIOT_NTPOPT_TIME_ZONE, (int8_t *)&time_zone);
if (res < STATE_SUCCESS) {
printf("aiot_ntp_setopt AIOT_NTPOPT_TIME_ZONE failed, res: -0x%04X\n", -res);
aiot_ntp_deinit(&ntp_handle);
demo_mqtt_stop(&mqtt_handle);
return -1;
}
/* TODO: NTP消息回应从云端到达设备时, 会进入此处设置的回调函数 */
res = aiot_ntp_setopt(ntp_handle, AIOT_NTPOPT_RECV_HANDLER, (void *)demo_ntp_recv_handler);
if (res < STATE_SUCCESS) {
printf("aiot_ntp_setopt AIOT_NTPOPT_RECV_HANDLER failed, res: -0x%04X\n", -res);
aiot_ntp_deinit(&ntp_handle);
demo_mqtt_stop(&mqtt_handle);
return -1;
}
res = aiot_ntp_setopt(ntp_handle, AIOT_NTPOPT_EVENT_HANDLER, (void *)demo_ntp_event_handler);
if (res < STATE_SUCCESS) {
printf("aiot_ntp_setopt AIOT_NTPOPT_EVENT_HANDLER failed, res: -0x%04X\n", -res);
aiot_ntp_deinit(&ntp_handle);
demo_mqtt_stop(&mqtt_handle);
return -1;
}
/* 发送NTP查询请求给云平台 */
res = aiot_ntp_send_request(ntp_handle);
if (res < STATE_SUCCESS) {
aiot_ntp_deinit(&ntp_handle);
demo_mqtt_stop(&mqtt_handle);
return -1;
}
/* 主线程进入休眠, 等云平台的NTP回应到达时, 接收线程会调用 demo_ntp_recv_handler() */
while (1) {
sleep(1);
}
/* 销毁NTP实例, 一般不会运行到这里 */
res = aiot_ntp_deinit(&ntp_handle);
if (res < STATE_SUCCESS) {
demo_mqtt_stop(&mqtt_handle);
printf("aiot_ntp_deinit failed: -0x%04X\n", -res);
return -1;
}
/* 销毁MQTT实例, 退出线程, 一般不会运行到这里 */
res = demo_mqtt_stop(&mqtt_handle);
if (res < 0) {
printf("demo_start_stop failed\n");
return -1;
}
return 0;
}

View File

@@ -0,0 +1,377 @@
/*
* 这个例程适用于`Linux`这类支持pthread的POSIX设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建2个线程
*
* + 一个线程用于保活长连接
* + 一个线程用于接收消息, 并在有消息到达时进入默认的数据回调, 在连接状态变化时进入事件回调
*
* 接着演示了在MQTT连接上进行设备影子更新, 删除, 拉取等操作, 取消这些代码段落的注释即可观察运行效果
*
* 需要用户关注或修改的部分, 已经用 TODO 在注释中标明
*
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
#include "aiot_shadow_api.h"
/* TODO: 替换为自己设备的三元组 */
char *product_key = "${YourProductKey}";
char *device_name = "${YourDeviceName}";
char *device_secret = "${YourDeviceSecret}";
/*
TODO: 替换为自己实例的接入点
对于企业实例, 或者2021年07月30日之后含当日开通的物联网平台服务下公共实例
mqtt_host的格式为"${YourInstanceId}.mqtt.iothub.aliyuncs.com"
其中${YourInstanceId}: 请替换为您企业/公共实例的Id
对于2021年07月30日之前不含当日开通的物联网平台服务下公共实例
需要将mqtt_host修改为: mqtt_host = "${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com"
其中, ${YourProductKey}请替换为设备所属产品的ProductKey。可登录物联网平台控制台在对应实例的设备详情页获取。
${YourRegionId}:请替换为您的物联网平台设备所在地域代码, 比如cn-shanghai等
该情况下完整mqtt_host举例: a1TTmBPIChA.iot-as-mqtt.cn-shanghai.aliyuncs.com
详情请见: https://help.aliyun.com/document_detail/147356.html
*/
char *mqtt_host = "${YourInstanceId}.mqtt.iothub.aliyuncs.com";
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
static pthread_t g_mqtt_process_thread;
static pthread_t g_mqtt_recv_thread;
static uint8_t g_mqtt_process_thread_running = 0;
static uint8_t g_mqtt_recv_thread_running = 0;
/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印
*
* 例如: [1577589489.033][LK-0317] shadow_basic_demo&a13FN5TplKq
*
* 上面这条日志的code就是0317(十六进制), code值的定义见core/aiot_state_api.h
*
*/
/* 日志回调函数, SDK的日志会从这里输出 */
int32_t demo_state_logcb(int32_t code, char *message)
{
printf("%s", message);
return 0;
}
/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata)
{
switch (event->type) {
/* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */
case AIOT_MQTTEVT_CONNECT: {
printf("AIOT_MQTTEVT_CONNECT\n");
}
break;
/* SDK因为网络状况被动断连后, 自动发起重连已成功 */
case AIOT_MQTTEVT_RECONNECT: {
printf("AIOT_MQTTEVT_RECONNECT\n");
}
break;
/* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */
case AIOT_MQTTEVT_DISCONNECT: {
char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
("heartbeat disconnect");
printf("AIOT_MQTTEVT_DISCONNECT: %s\n", cause);
}
break;
default: {
}
}
}
/* 执行aiot_mqtt_process的线程, 包含心跳发送和QoS1消息重发 */
void *demo_mqtt_process_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_process_thread_running) {
res = aiot_mqtt_process(args);
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
return NULL;
}
/* 执行aiot_mqtt_recv的线程, 包含网络自动重连和从服务器收取MQTT消息 */
void *demo_mqtt_recv_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_recv_thread_running) {
res = aiot_mqtt_recv(args);
if (res < STATE_SUCCESS) {
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
}
return NULL;
}
/* 数据处理回调, 当SDK从网络上收到shadow消息时被调用 */
void demo_shadow_recv_handler(void *handle, const aiot_shadow_recv_t *recv, void *userdata)
{
printf("demo_shadow_recv_handler, type = %d, productKey = %s, deviceName = %s\r\n",
recv->type, recv->product_key, recv->device_name);
switch (recv->type) {
/* 当设备发送AIOT_SHADOWMSG_UPDATE, AIOT_SHADOWMSG_CLEAN_DESIRED或者AIOT_SHADOWMSG_DELETE_REPORTED消息后, 会收到此应答消息 */
case AIOT_SHADOWRECV_GENERIC_REPLY: {
const aiot_shadow_recv_generic_reply_t *generic_reply = &recv->data.generic_reply;
printf("payload = \"%.*s\", status = %s, timestamp = %ld\r\n",
generic_reply->payload_len,
generic_reply->payload,
generic_reply->status,
(unsigned long)generic_reply->timestamp);
}
break;
/* 当设备在线时, 若用户应用程序调用云端API主动更新设备影子, 设备便会收到此消息 */
case AIOT_SHADOWRECV_CONTROL: {
const aiot_shadow_recv_control_t *control = &recv->data.control;
printf("payload = \"%.*s\", version = %ld\r\n",
control->payload_len,
control->payload,
(unsigned long)control->version);
}
break;
/* 当设备发送AIOT_SHADOWMSG_GET消息主动获取设备影子时, 会收到此消息 */
case AIOT_SHADOWRECV_GET_REPLY: {
const aiot_shadow_recv_get_reply_t *get_reply = &recv->data.get_reply;
printf("payload = \"%.*s\", version = %ld\r\n",
get_reply->payload_len,
get_reply->payload,
(unsigned long)get_reply->version);
}
default:
break;
}
}
/* 发送更新设备影子reported值的请求 */
int32_t demo_update_shadow(void *shadow_handle, char *reported_data, int64_t version)
{
aiot_shadow_msg_t message;
memset(&message, 0, sizeof(aiot_shadow_msg_t));
message.type = AIOT_SHADOWMSG_UPDATE;
message.data.update.reported = reported_data;
message.data.update.version = version;
return aiot_shadow_send(shadow_handle, &message);
}
/* 发送删除设备影子中特定reported值的请求 */
int32_t demo_delete_shadow_report(void *shadow_handle, char *reported, int64_t version)
{
aiot_shadow_msg_t message;
memset(&message, 0, sizeof(aiot_shadow_msg_t));
message.type = AIOT_SHADOWMSG_DELETE_REPORTED;
message.data.delete_reporte.reported = reported;
message.data.delete_reporte.version = version;
return aiot_shadow_send(shadow_handle, &message);
}
/* 发送清除设备影子中所有desired值的请求 */
int32_t demo_clean_shadow_desired(void *shadow_handle, int64_t version)
{
aiot_shadow_msg_t message;
memset(&message, 0, sizeof(aiot_shadow_msg_t));
message.type = AIOT_SHADOWMSG_CLEAN_DESIRED;
message.data.clean_desired.version = version;
return aiot_shadow_send(shadow_handle, &message);
}
/* 发送获取设备影子的请求 */
int32_t demo_get_shadow(void *shadow_handle)
{
aiot_shadow_msg_t message;
memset(&message, 0, sizeof(aiot_shadow_msg_t));
message.type = AIOT_SHADOWMSG_GET;
return aiot_shadow_send(shadow_handle, &message);
}
int main(int argc, char *argv[])
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
void *shadow_handle = NULL;
uint16_t port = 1883; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(demo_state_logcb);
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
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根证书长度 */
/* 创建1个MQTT客户端实例并内部初始化默认参数 */
mqtt_handle = aiot_mqtt_init();
if (mqtt_handle == NULL) {
printf("aiot_mqtt_init failed\n");
return -1;
}
/* 配置MQTT服务器地址 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);
/* 配置MQTT服务器端口 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
/* 配置设备productKey */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
/* 配置设备deviceName */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
/* 配置设备deviceSecret */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
/* 配置网络连接的安全凭据, 上面已经创建好了 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
/* 配置MQTT事件回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);
/* 创建1个shadow客户端实例并内部初始化默认参数 */
shadow_handle = aiot_shadow_init();
if (shadow_handle == NULL) {
printf("aiot_shadow_init failed\n");
return -1;
}
/* 配置MQTT实例句柄 */
aiot_shadow_setopt(shadow_handle, AIOT_SHADOWOPT_MQTT_HANDLE, mqtt_handle);
/* 配置SHADOW默认消息接收回调函数 */
aiot_shadow_setopt(shadow_handle, AIOT_SHADOWOPT_RECV_HANDLER, (void *)demo_shadow_recv_handler);
/* 与服务器建立MQTT连接 */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_connect failed: -0x%04X\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
return -1;
}
/* 创建一个单独的线程, 专用于执行aiot_mqtt_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */
g_mqtt_process_thread_running = 1;
res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_process_thread failed: %d\n", res);
return -1;
}
/* 创建一个单独的线程用于执行aiot_mqtt_recv, 它会循环收取服务器下发的MQTT消息, 并在断线时自动重连 */
g_mqtt_recv_thread_running = 1;
res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_recv_thread failed: %d\n", res);
return -1;
}
/* 主循环进入休眠 */
while (1) {
/* TODO: 以下代码演示了发送请求拉取设备影子, 设备影子将在demo_shadow_recv_handler回调函数中返回, 用户可取消注释观察演示效果 */
/*
res = demo_get_shadow(shadow_handle);
if (res < 0) {
printf("demo_get_shadow failed, res = -0x%04x\r\n", -res);
}
*/
sleep(2);
/* TODO: 以下代码演示了如何清除设备影子中的desired值, 同时将version设置为1, 用户可取消注释观察演示效果 */
/*
res = demo_clean_shadow_desired(shadow_handle, 1);
if (res < 0) {
printf("demo_clean_shadow_desired failed, res = -0x%04x\r\n", -res);
}
*/
sleep(2);
/* TODO: 以下代码演示了如何删除设备影子中特定的reported值, 同时将version设置为2, 用户可取消注释观察演示效果 */
/*
res = demo_delete_shadow_report(shadow_handle, "{\"LightSwitch\":\"null\"}", 2);
if (res < 0) {
printf("demo_delete_shadow_report failed, res = -0x%04x\r\n", -res);
}
*/
sleep(2);
/* TODO: 以下代码演示了如何更新设备影子中的reported值, 同时将version重置为0, 用户可取消注释观察演示效果 */
/*
res = demo_update_shadow(shadow_handle, "{\"LightSwitch\":1}", 0);
if (res < 0) {
printf("demo_delete_shadow_report failed, res = -0x%04x\r\n", -res);
}
*/
sleep(10);
}
/* 断开MQTT连接, 一般不会运行到这里 */
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);
return -1;
}
/* 销毁SHADOW实例, 一般不会运行到这里 */
res = aiot_shadow_deinit(&shadow_handle);
if (res < STATE_SUCCESS) {
printf("aiot_shadow_deinit failed: -0x%04X\n", -res);
return -1;
}
/* 销毁MQTT实例, 一般不会运行到这里 */
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x%04X\n", -res);
return -1;
}
g_mqtt_process_thread_running = 0;
g_mqtt_recv_thread_running = 0;
pthread_join(g_mqtt_process_thread, NULL);
pthread_join(g_mqtt_recv_thread, NULL);
return 0;
}

View File

@@ -0,0 +1,832 @@
/*
* 这个例程用于用户在移植后验证是否移植成功的。
* 通过一系列的测试case验证移植的相关接口是否正常工作
* + 正常结束后会输出TEST SUCCESS其它会输出TEST ERROR + ERRORCODE
* + 如遇长时间卡住也为测试失败
*
* 该测试工具会使用多线程/多任务的能力在RTOS下使用需要自行适配任务创建。
* 需要用户关注或修改的部分, 已经用 TODO 在注释中标明
*
*/
#include "aiot_sysdep_api.h"
#include "aiot_state_api.h"
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#define DEBUG_INFO(...) do{ printf("Line[%d]: ",__LINE__); printf(__VA_ARGS__); printf("\r\n");}while(0);
/* 定义入口函数类型 */
typedef void *(*TASK_FUNC)(void* argv);
/* 定义适配任务创建函数*/
void task_start(TASK_FUNC entry,void* argv);
/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TODO START >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/
/*
* TODO: task_start功能实现创建任务并执行任务待任务结束后自行退出
* @param[in] entry 任务函数入口
* @param[in] argv 任务函数参数
*/
#include<pthread.h>
void task_start(TASK_FUNC entry,void* argv)
{
pthread_t id;
pthread_create(&id, NULL, (void*(*)(void *))entry, argv);
}
/*TODO: 堆最大空间,单位字节 */
#define HEAP_MAX ( 20 * 1024 )
/*<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TODO END <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
/**
* 系统接口测试结果
*/
typedef enum {
TEST_SUCCESS,
TEST_ERR_RANDOM,
TEST_ERR_MALLOC,
TEST_ERR_HEAP,
TEST_ERR_SLEEP,
TEST_ERR_MUTEX,
TEST_ERR_NETWORK,
TEST_ERR_GENERIC,
} sysdep_test_result_t;
static const char *result_string[] = {
"TEST_SUCCESS",
"TEST_ERR_RANDOM",
"TEST_ERR_MALLOC",
"TEST_ERR_HEAP",
"TEST_ERR_SLEEP",
"TEST_ERR_MUTEX",
"TEST_ERR_NETWORK",
"TEST_ERR_GENERIC",
};
typedef struct {
aiot_sysdep_portfile_t *sysdep;
void* user_data;
char* name;
} task_handler_input_t;
/**
* sysdep测试入口原型定义
*/
typedef sysdep_test_result_t (*sysdep_test_func)(aiot_sysdep_portfile_t* sysdep);
typedef struct
{
uint32_t total_size;//堆总大小
} heap_cfg_t;
typedef struct
{
uint16_t count;
/* 最大重复次数 */
uint16_t repeat_cnt_max;
} random_cfg_t;
typedef struct
{
uint64_t sleep_ms;
uint8_t sleep_end;
} time_cfg_t;
typedef struct
{
void* mutex;
int32_t value;
uint32_t timeout_ms;
} mutex_cfg_t;
typedef struct
{
char* host;
uint16_t port;
uint32_t connect_timeout_ms;
uint32_t recv_timeout_ms;
int32_t recv_buf_len;
uint32_t send_timeout_ms;
uint32_t cycle_test_times;
} network_cfg_t;
/**
* 随机生成随机数列表,重复出现次数不高于指定值
*/
static sysdep_test_result_t random_repeat_test(aiot_sysdep_portfile_t* sysdep, random_cfg_t* rd)
{
typedef uint8_t random_value_t;
random_value_t *random_values = NULL;
int count = 0;
random_value_t temp = 0;
sysdep_test_result_t ret = TEST_SUCCESS;
/* 差值 */
int diff = 0;
/* 递变标志 */
int flag = 0;
random_values = sysdep->core_sysdep_malloc(sizeof(random_value_t) * rd->count, NULL);
if(random_values == NULL){
DEBUG_INFO("\r\nmalloc fail");
ret = TEST_ERR_MALLOC;
goto end;
}
memset(random_values, 0, sizeof(random_value_t) * rd->count);
/* 生成随机数,并统计重复次数 计算差值*/
for(count = 0; count < rd->count; count++) {
sysdep->core_sysdep_rand((uint8_t*)&temp, sizeof(random_value_t));
temp %= rd->count;
/* 检测是否为规律递变 */
if(count == 1 ){
diff = random_values[count] - random_values[count - 1];
}else if (count >= 2){
if(diff == random_values[count] - random_values[count - 1]){
flag++;
}
}
if(random_values[temp] <= rd->repeat_cnt_max) {
random_values[temp]++;
}
else {
DEBUG_INFO("randowm value [%d] times [%d] over max counter [%d]", temp, random_values[temp], rd->repeat_cnt_max);
ret = TEST_ERR_RANDOM;
goto end;
}
}
/* 规律递变则返回错误 */
if(rd->count >= 3 && flag >= rd->count - 2){
DEBUG_INFO("random value error");
ret = TEST_ERR_RANDOM;
goto end;
}
ret = TEST_SUCCESS;
end:
if(random_values != NULL){
sysdep->core_sysdep_free(random_values);
}
return ret;
}
/**
* 随机数测试入口
*/
sysdep_test_result_t random_test(aiot_sysdep_portfile_t* sysdep)
{
random_cfg_t rd;
/* 随机数接口要求生成的随机数按字节随机 */
rd.count = 256;
/* 随机数重复次数不能超过该值 */
rd.repeat_cnt_max = 10;
return random_repeat_test(sysdep, &rd);
}
static sysdep_test_result_t heap_malloc_max_test(aiot_sysdep_portfile_t* sysdep, heap_cfg_t* hp)
{
/**
* 最大空间申请、释放
*/
uint32_t user_malloc_max = hp->total_size - 100;
void * ptr = sysdep->core_sysdep_malloc(user_malloc_max, "");
if(ptr == NULL) {
DEBUG_INFO("malloc error");
return TEST_ERR_MALLOC;
}
/* 空间读写测试 */
uint8_t read_value;
for(int i = 0; i < user_malloc_max; i++) {
((uint8_t*)ptr)[i] = (uint8_t)i;
read_value = ((uint8_t*)ptr)[i];
if(read_value != (uint8_t)i) {
sysdep->core_sysdep_free(ptr);
DEBUG_INFO("heap read and write fail");
return TEST_ERR_MALLOC;
}
}
sysdep->core_sysdep_free(ptr);
return TEST_SUCCESS;
}
/**
* 堆回收测试
*/
static sysdep_test_result_t heap_recycle_test(aiot_sysdep_portfile_t* sysdep, heap_cfg_t* hp)
{
/* 待申请的堆空间大小 */
const uint32_t size_list[]= {1,2,4,8,16,32,64,128,256,512,1024,10*1024,100*1024,1024*1024};
/* 存储堆指针 */
void* malloc_list[sizeof(size_list) / sizeof(uint32_t)] = {NULL};
int malloc_list_size = sizeof(size_list) / sizeof(uint32_t);
/* 申请空间 */
int malloc_cnt = 0;
int i = 0;
for(i = 0; i < malloc_list_size; i++) {
malloc_list[i] = sysdep->core_sysdep_malloc(size_list[i], "");
if(malloc_list[i] == NULL) {
break;
}
}
malloc_cnt = i;
/**
* 非相邻空间释放
*/
for(i = 0; i < malloc_cnt; i+=2) {
sysdep->core_sysdep_free(malloc_list[i]);
}
for(i = 1; i < malloc_cnt; i+=2) {
sysdep->core_sysdep_free(malloc_list[i]);
}
/**
* 最后申请最大空间
*/
return heap_malloc_max_test(sysdep, hp);
}
/**
* 堆测试入口
*/
sysdep_test_result_t heap_test(aiot_sysdep_portfile_t* sysdep)
{
sysdep_test_result_t ret = TEST_SUCCESS;
heap_cfg_t hp;
hp.total_size = HEAP_MAX;
ret = heap_malloc_max_test(sysdep,&hp);
if(ret != TEST_SUCCESS) {
return ret;
}
return heap_recycle_test(sysdep, &hp);
}
static sysdep_test_result_t network_tcp_test(aiot_sysdep_portfile_t* sysdep, network_cfg_t* cfg)
{
void* network_hd = sysdep->core_sysdep_network_init();
core_sysdep_socket_type_t type = CORE_SYSDEP_SOCKET_TCP_CLIENT;
sysdep->core_sysdep_network_setopt(network_hd, CORE_SYSDEP_NETWORK_SOCKET_TYPE, &type);
sysdep->core_sysdep_network_setopt(network_hd, CORE_SYSDEP_NETWORK_HOST, cfg->host);
sysdep->core_sysdep_network_setopt(network_hd, CORE_SYSDEP_NETWORK_PORT, (void*)&cfg->port);
sysdep->core_sysdep_network_setopt(network_hd, CORE_SYSDEP_NETWORK_CONNECT_TIMEOUT_MS, &cfg->connect_timeout_ms);
/**
* 建连测试
*/
if(0 != sysdep->core_sysdep_network_establish(network_hd)) {
DEBUG_INFO("network establish test error:%d", TEST_ERR_NETWORK);
return TEST_ERR_NETWORK;
}
/**
* 接收等待超时测试
*/
char* buf = sysdep->core_sysdep_malloc(cfg->recv_buf_len + 1, "");
if(buf == NULL){
DEBUG_INFO("malloc fail");
return TEST_ERR_MALLOC;
}
uint64_t start = sysdep->core_sysdep_time();
sysdep->core_sysdep_network_recv(network_hd, (uint8_t*)buf, cfg->recv_buf_len, cfg->recv_timeout_ms, NULL);
uint64_t stop = sysdep->core_sysdep_time();
if(stop - start < cfg->recv_timeout_ms) {
DEBUG_INFO("network receive test error:%d, start [%"PRIu64"], end [%"PRIu64"]", TEST_ERR_NETWORK, start, stop);
return TEST_ERR_NETWORK;
}
/**
* 通过Http Get请求来验证TCP是否工作正常
*/
char* http_get = "GET / HTTP/1.1\r\n"\
"Host: www.aliyun.com\r\n"\
"Connection: keep-alive\r\n"\
"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36\r\n"\
"Origin: https://www.aliyun.com\r\n"\
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\n"\
"\r\n";
int32_t send_len = strlen(http_get);
int32_t send_result = sysdep->core_sysdep_network_send(network_hd, (uint8_t*)http_get, send_len, cfg->send_timeout_ms, NULL);
if(send_result != send_len) {
DEBUG_INFO("network send length test error:%d, send [%d], result [%d]", TEST_ERR_NETWORK, send_len, send_result);
return TEST_ERR_NETWORK;
}
int32_t buf_len = 0;
uint8_t byte_read;
int32_t content_len = 0;
int32_t ret;
char* str_ptr = NULL;
char* target_str = "Content-Length:";
int state = 0;
while(1) {
ret = sysdep->core_sysdep_network_recv(network_hd, (uint8_t*)&byte_read, 1, cfg->recv_timeout_ms, NULL);
/* 需支持按字节读取 */
if(ret == 1) {
if(state == 0) {
/* 提取一行 */
buf[buf_len++] = byte_read;
if(byte_read != '\n') {
continue;
}
buf[buf_len] = '\0';
/*空行表示header结束*/
if(buf_len == 2 && buf[0] == '\r' && buf[1] == '\n') {
state = 1;
buf_len = 0;
continue;
}
/*搜索应答长度*/
str_ptr = strstr(buf,target_str);
if(str_ptr == NULL) {
buf_len = 0;
continue;
}
str_ptr += strlen(target_str);
if(str_ptr >= &buf[buf_len]) {
buf_len = 0;
continue;
}
if( 1 == sscanf(str_ptr,"%d",&content_len)) {
buf_len = 0;
}
} else {
buf[buf_len++] = byte_read;
}
}
else {
break;
}
}
sysdep->core_sysdep_network_deinit(&network_hd);
sysdep->core_sysdep_free(buf);
buf = NULL;
if(network_hd != NULL){
DEBUG_INFO("network deinit error");
return TEST_ERR_NETWORK;
}
/* http的正文长度校验 */
if(buf_len == 0 || buf_len != content_len) {
DEBUG_INFO("[NETWORK_TEST.RECV] test error:%d, send [%d], result [%d]", TEST_ERR_NETWORK, buf_len, content_len);
return TEST_ERR_NETWORK;
} else {
DEBUG_INFO("[NETWORK_TEST.RECV] test success");
return TEST_SUCCESS;
}
}
sysdep_test_result_t network_tcp_cycle_test(aiot_sysdep_portfile_t* sysdep, network_cfg_t* cfg)
{
void* network_hd = NULL;
int count = cfg->cycle_test_times;
while(count-- > 0) {
network_hd = sysdep->core_sysdep_network_init();
if(network_hd == NULL) {
DEBUG_INFO("network tcp init test error");
return TEST_ERR_NETWORK;
}
core_sysdep_socket_type_t type = CORE_SYSDEP_SOCKET_TCP_CLIENT;
sysdep->core_sysdep_network_setopt(network_hd, CORE_SYSDEP_NETWORK_SOCKET_TYPE, &type);
sysdep->core_sysdep_network_setopt(network_hd, CORE_SYSDEP_NETWORK_HOST, cfg->host);
sysdep->core_sysdep_network_setopt(network_hd, CORE_SYSDEP_NETWORK_PORT, (void*)&cfg->port);
sysdep->core_sysdep_network_setopt(network_hd, CORE_SYSDEP_NETWORK_CONNECT_TIMEOUT_MS, &cfg->connect_timeout_ms);
if(0 != sysdep->core_sysdep_network_establish(network_hd)) {
DEBUG_INFO("network tcp establish test error:%d", TEST_ERR_NETWORK);
return TEST_ERR_NETWORK;
}
sysdep->core_sysdep_network_deinit(&network_hd);
}
if( TEST_SUCCESS != heap_test(sysdep) ) {
DEBUG_INFO("network deinit then heap test error:%d", TEST_ERR_NETWORK);
return TEST_ERR_NETWORK;
} else {
return TEST_SUCCESS;
}
}
static sysdep_test_result_t network_tcp_send_length_test(aiot_sysdep_portfile_t* sysdep, network_cfg_t* cfg)
{
sysdep_test_result_t ret = TEST_SUCCESS;
void* network_hd = sysdep->core_sysdep_network_init();
core_sysdep_socket_type_t type = CORE_SYSDEP_SOCKET_TCP_CLIENT;
sysdep->core_sysdep_network_setopt(network_hd, CORE_SYSDEP_NETWORK_SOCKET_TYPE, &type);
sysdep->core_sysdep_network_setopt(network_hd, CORE_SYSDEP_NETWORK_HOST, cfg->host);
sysdep->core_sysdep_network_setopt(network_hd, CORE_SYSDEP_NETWORK_PORT, (void*)&cfg->port);
sysdep->core_sysdep_network_setopt(network_hd, CORE_SYSDEP_NETWORK_CONNECT_TIMEOUT_MS, &cfg->connect_timeout_ms);
/**
* 建连测试
*/
if(0 != sysdep->core_sysdep_network_establish(network_hd)) {
DEBUG_INFO("network establish test error:%d", TEST_ERR_NETWORK);
return TEST_ERR_NETWORK;
}
/**
* 通过Http Get请求来验证TCP发送接口是否具备长包发送能力
*/
const char* http_get = "GET / HTTP/1.1\r\n"\
"Host: www.aliyun.com\r\n"\
"Connection: keep-alive\r\n"\
"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36\r\n"\
"Origin: https://www.aliyun.com\r\n"\
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\n"\
"\r\n"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,"\
"invalid test append data,invalid test append data,invalid test append data,invalid test append data,";
int32_t send_len = strlen(http_get);
int32_t send_result = sysdep->core_sysdep_network_send(network_hd, (uint8_t*)http_get, send_len, cfg->send_timeout_ms, NULL);
if(send_result != send_len) {
DEBUG_INFO("network send length test error:%d, send [%d], result [%d]", TEST_ERR_NETWORK, send_len, send_result);
ret = TEST_ERR_NETWORK;
}
sysdep->core_sysdep_network_deinit(&network_hd);
return ret;
}
sysdep_test_result_t network_test(aiot_sysdep_portfile_t* sysdep)
{
sysdep_test_result_t ret;
sysdep->core_sysdep_sleep(100);
network_cfg_t cfg;
cfg.host = "www.aliyun.com";
cfg.port = 80;
cfg.connect_timeout_ms = 5000;
cfg.recv_timeout_ms = 5000;
cfg.recv_buf_len = 1024;
cfg.send_timeout_ms = 5000;
cfg.cycle_test_times = 10;
ret = network_tcp_test(sysdep, &cfg);
if(ret != TEST_SUCCESS) {
return ret;
}
ret = network_tcp_send_length_test(sysdep,&cfg);
if(ret != TEST_SUCCESS){
return ret;
}
ret = network_tcp_cycle_test(sysdep, &cfg);
return ret;
}
static void *enter_sleep(void* user_data)
{
task_handler_input_t* user = (task_handler_input_t*)user_data;
time_cfg_t* tm = user->user_data;
DEBUG_INFO("%s enter wanna sleep: %"PRIu64"ms", user->name, tm->sleep_ms);
user->sysdep->core_sysdep_sleep(tm->sleep_ms);
tm->sleep_end = 1;
return 0;
}
sysdep_test_result_t time_sleep_test(aiot_sysdep_portfile_t* sysdep)
{
uint64_t start,stop;
time_cfg_t time_cfg;
sysdep_test_result_t ret= TEST_SUCCESS;
time_cfg_t* sleep1 = NULL;
time_cfg_t* sleep2 = NULL;
task_handler_input_t input1;
task_handler_input_t input2;
int64_t wait_total;
uint64_t temp;
time_cfg.sleep_ms = 30 * 1000;
DEBUG_INFO("sleep %llu ms test",(long long unsigned int)time_cfg.sleep_ms);
start = sysdep->core_sysdep_time();
sysdep->core_sysdep_sleep(time_cfg.sleep_ms);
stop = sysdep->core_sysdep_time();
/* 睡眠异常 */
if(stop - start < time_cfg.sleep_ms) {
DEBUG_INFO("sleep test error: %d",TEST_ERR_SLEEP);
ret = TEST_ERR_SLEEP;
goto end;
}
/* 睡眠时间超过10% */
if(stop - start > (uint64_t)(time_cfg.sleep_ms * 1.1)) {
DEBUG_INFO("sleep test error: %d",TEST_ERR_SLEEP);
ret = TEST_ERR_SLEEP;
goto end;
}
/**
* 并发睡眠测试,两个并发睡眠消耗的时间应小于两次睡眠时间之和
*/
sleep1 = sysdep->core_sysdep_malloc(sizeof(time_cfg_t),"");
sleep2 = sysdep->core_sysdep_malloc(sizeof(time_cfg_t),"");
if(sleep1 == NULL || sleep2 == NULL){
ret = TEST_ERR_SLEEP;
goto end;
}
sleep1->sleep_ms = 10 * 1000;
sleep1->sleep_end = 0;
sleep2->sleep_ms = 10 * 1000;
sleep2->sleep_end = 0;
input1.name = "sleep_test_task_1";
input1.sysdep = sysdep;
input1.user_data = sleep1;
input2.name = "sleep_test_task_2";
input2.sysdep = sysdep;
input2.user_data = sleep2;
start = sysdep->core_sysdep_time();
task_start(enter_sleep,&input1);
task_start(enter_sleep,&input2);
/**
* 等待睡眠结束
*/
wait_total = sleep1->sleep_ms + sleep2->sleep_ms + 100;
while((sleep1->sleep_end == 0 || sleep2->sleep_end == 0) && wait_total-- > 0) {
sysdep->core_sysdep_sleep(1);
}
/**
* 检查所有线程是否都退出
*/
while (sleep1->sleep_end == 0 || sleep2->sleep_end == 0){
sysdep->core_sysdep_sleep(1);
}
/* 两个线程应该在规定时间内退出 */
if(wait_total < 0) {
DEBUG_INFO("concurrent sleep test error: wait sleep timeout fail");
ret = TEST_ERR_SLEEP;
goto end;
}
/* 总睡眠时间应小于各睡眠时间之和 */
stop = sysdep->core_sysdep_time();
temp = sleep1->sleep_ms + sleep2->sleep_ms;
if(stop - start >= temp) {
DEBUG_INFO("sleep %"PRIu64"ms start:[%"PRIu64"] stop:[%"PRIu64"]\r\n unexpected ", sleep1->sleep_ms, start, stop);
ret = TEST_ERR_SLEEP;
}
else {
DEBUG_INFO("sleep %"PRIu64"ms start:[%"PRIu64"] stop:[%"PRIu64"] expected ", sleep1->sleep_ms, start, stop);
ret = TEST_SUCCESS;
}
end:
if(sleep1 != NULL)
sysdep->core_sysdep_free(sleep1);
if(sleep2 != NULL)
sysdep->core_sysdep_free(sleep2);
return ret;
}
static void* mutex_synchronize_test(void* user_data)
{
task_handler_input_t* input = (task_handler_input_t*)user_data;
mutex_cfg_t* cfg = input->user_data;
while(1) {
input->sysdep->core_sysdep_mutex_lock(cfg->mutex);
if(cfg->value < 0) {
input->sysdep->core_sysdep_mutex_unlock(cfg->mutex);
break;
}
cfg->value++;
input->sysdep->core_sysdep_mutex_unlock(cfg->mutex);
input->sysdep->core_sysdep_sleep(cfg->timeout_ms);
}
return NULL;
}
/**
* 互斥锁测试
*/
sysdep_test_result_t mutex_test(aiot_sysdep_portfile_t* sysdep)
{
sysdep_test_result_t ret;
void* mutex = NULL;
mutex_cfg_t *mutex_cfg_2 = NULL;
task_handler_input_t *input_1 = NULL;
task_handler_input_t *input_2 = NULL;
int32_t wait_ms = 3000;
/**
* 互斥锁申请释放及内存泄漏测试
*/
for(int i = 0; i < 1000; i++) {
mutex = sysdep->core_sysdep_mutex_init();
sysdep->core_sysdep_mutex_lock(mutex);
sysdep->core_sysdep_mutex_unlock(mutex);
sysdep->core_sysdep_mutex_deinit(&mutex);
}
if( TEST_SUCCESS != heap_test(sysdep)) {
DEBUG_INFO("[MUTEX_TEST.CYCLE] test failed\r\n");
return ret = TEST_ERR_MUTEX;
}
/**
* 互斥锁功能测试
*/
mutex_cfg_t *mutex_cfg_1 = sysdep->core_sysdep_malloc(sizeof(mutex_cfg_t), "");
if(mutex_cfg_1 == NULL) {
DEBUG_INFO("mutex test but malloc fail");
ret = TEST_ERR_MUTEX;
goto end;
}
mutex_cfg_1->mutex = sysdep->core_sysdep_mutex_init();
if(mutex_cfg_1->mutex == NULL) {
ret = TEST_ERR_MALLOC;
DEBUG_INFO("[MUTEX_TEST]malloc fail:%d", ret);
goto end;
}
input_1 = sysdep->core_sysdep_malloc(sizeof(task_handler_input_t), "");
if(input_1 == NULL){
DEBUG_INFO("mutex test but malloc fail");
ret = TEST_ERR_MALLOC;
goto end;
}
input_1->name = "mutex_test_task1";
input_1->sysdep = sysdep;
input_1->user_data = mutex_cfg_1;
mutex_cfg_1->timeout_ms = 100;
mutex_cfg_1->value = 0;
task_start(mutex_synchronize_test,input_1);
mutex_cfg_2 = sysdep->core_sysdep_malloc(sizeof(mutex_cfg_t), "");
if(mutex_cfg_2 == NULL) {
ret = TEST_ERR_MALLOC;
DEBUG_INFO("[MUTEX_TEST]malloc fail:%d", ret);
goto end;
}
mutex_cfg_2->mutex = sysdep->core_sysdep_mutex_init();
if(mutex_cfg_2->mutex == NULL) {
ret = TEST_ERR_MALLOC;
DEBUG_INFO("[MUTEX_TEST]malloc fail:%d", ret);
goto end;
}
input_2 = sysdep->core_sysdep_malloc(sizeof(task_handler_input_t), "");
input_2->name = "mutex_test_task2";
input_2->sysdep = sysdep;
input_2->user_data = mutex_cfg_2;
mutex_cfg_2->timeout_ms = 100;
mutex_cfg_2->value = 0;
task_start(mutex_synchronize_test,input_2);
sysdep->core_sysdep_sleep(wait_ms);
/**
* 对mutex_cfg_1->mutex加锁mutex_test_task1会被锁住mutex_cfg_1->value应该不会发生变化
*/
int32_t v1,v2;
DEBUG_INFO("mutex lock task1, unlock task2 %d ms", wait_ms);
sysdep->core_sysdep_mutex_lock(mutex_cfg_1->mutex);
v1 = mutex_cfg_1->value;
v2 = mutex_cfg_2->value;
sysdep->core_sysdep_sleep(wait_ms);
int32_t v1_,v2_;
v1_ = mutex_cfg_1->value;
v2_ = mutex_cfg_2->value;
DEBUG_INFO("task1 value [%d --> %d], task2 value [%d --> %d] ", v1, v1_, v2, v2_);
if(v1 != v1_ || v2 == v2_) {
DEBUG_INFO("mutex test failed ");
ret = TEST_ERR_MUTEX;
goto end;
}
sysdep->core_sysdep_mutex_unlock(mutex_cfg_1->mutex);
/**
* 对mutex_cfg_2->mutex加锁mutex_test_task2会被锁住mutex_cfg_2->value应该不会发生变化
*/
DEBUG_INFO("unlock task1, lock task2 %d ms", wait_ms);
sysdep->core_sysdep_mutex_lock(mutex_cfg_2->mutex);
v1 = mutex_cfg_1->value;
v2 = mutex_cfg_2->value;
sysdep->core_sysdep_sleep(wait_ms);
v1_ = mutex_cfg_1->value;
v2_ = mutex_cfg_2->value;
DEBUG_INFO("task1 value [%d --> %d], task2 value [%d --> %d] ", v1, v1_, v2, v2_);
if(v1 == v1_ || v2 != v2_) {
DEBUG_INFO("mutex test failed");
ret = TEST_ERR_MUTEX;
goto end;
}
sysdep->core_sysdep_mutex_unlock(mutex_cfg_2->mutex);
/**
* mutex_cfg_2->mutex不加锁mutex_cfg_1->mutex不加锁对应value应该正常变化
*/
DEBUG_INFO("unlock task1, lock task2 %d ms\r\n", wait_ms);
v1 = mutex_cfg_1->value;
v2 = mutex_cfg_2->value;
sysdep->core_sysdep_sleep(wait_ms);
v1_ = mutex_cfg_1->value;
v2_ = mutex_cfg_2->value;
DEBUG_INFO("task1 value [%d --> %d], task2 value [%d --> %d]\r\n", v1, v1_, v2, v2_);
if(v1 == v1_ || v2 == v2_) {
DEBUG_INFO("[MUTEX_TEST.UNLOCK] test failed \r\n");
ret = TEST_ERR_MUTEX;
goto end;
}
end:
/**
* 线程退出,回归
*/
if(mutex_cfg_1 != NULL) {
mutex_cfg_1->value = -10;
sysdep->core_sysdep_sleep(500);
if(mutex_cfg_1->mutex != NULL){
sysdep->core_sysdep_mutex_deinit(&(mutex_cfg_1->mutex));
}
sysdep->core_sysdep_free(mutex_cfg_1);
}
if(mutex_cfg_2 != NULL){
mutex_cfg_2->value = -10;
sysdep->core_sysdep_sleep(500);
if(mutex_cfg_2->mutex != NULL){
sysdep->core_sysdep_mutex_deinit(&(mutex_cfg_2->mutex));
}
sysdep->core_sysdep_free(mutex_cfg_2);
}
if(input_1 != NULL){
sysdep->core_sysdep_free(input_1);
}
if(input_2 != NULL){
sysdep->core_sysdep_free(input_2);
}
return TEST_SUCCESS;
}
typedef struct {
char* name;
sysdep_test_func func;
} sysdep_test_suite;
/**
* 测试项列表
*/
sysdep_test_suite test_list[]= {
{"RANDOM_TEST ", random_test},
{"HEAP_TEST ", heap_test},
{"TIME_TEST ", time_sleep_test},
{"NETWORK_TEST", network_test},
{"MUTEX_TEST ", mutex_test},
};
/**
* sysdep的接口实现包含系统时间、内存管理、网络、锁、随机数、等接口实现
*/
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
int main(int argc, char *argv[])
{
aiot_sysdep_portfile_t *sysdep = &g_aiot_sysdep_portfile;
int32_t size = sizeof(test_list) / sizeof(test_list[0]);
sysdep_test_result_t ret = TEST_SUCCESS;
DEBUG_INFO("TOTAL TEST START");
for(int32_t i = 0; i < size; i++) {
DEBUG_INFO("TEST [%d/%d] [%s] .....................................[START]", i + 1, size, test_list[i].name);
ret = (test_list[i].func)(sysdep);
if(TEST_SUCCESS != ret) {
DEBUG_INFO("TEST [%d/%d] [%s] .....................................[FAILED] [%s]", i + 1, size, test_list[i].name, result_string[ret]);
break;
} else {
DEBUG_INFO("TEST [%d/%d] [%s] .....................................[SUCCESS]", i + 1, size, test_list[i].name);
}
}
if(ret == TEST_SUCCESS) {
DEBUG_INFO("TOTAL TEST SUCCESS");
} else {
DEBUG_INFO("TOTAL TEST FAILED %s", result_string[ret]);
}
return 0;
}

View File

@@ -0,0 +1,520 @@
/*
* 这个例程适用于`Linux`这类支持pthread的POSIX设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建2个线程
*
* + 一个线程用于保活长连接
* + 一个线程用于接收消息, 并在有消息到达时进入默认的数据回调, 在连接状态变化时进入事件回调
*
* 接着在MQTT连接上发送TASK查询请求, 如果云平台的回应报文到达, 从接收线程会调用TASK消息处理的回调函数, 把对时后的本地时间打印出来
*
* 需要用户关注或修改的部分, 已经用 TODO 在注释中标明
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
#include "aiot_task_api.h"
/* TODO: 替换为自己设备的三元组 */
char *product_key = "${YourProductKey}";
char *device_name = "${YourDeviceName}";
char *device_secret = "${YourDeviceSecret}";
/*
TODO: 替换为自己实例的接入点
对于企业实例, 或者2021年07月30日之后含当日开通的物联网平台服务下公共实例
mqtt_host的格式为"${YourInstanceId}.mqtt.iothub.aliyuncs.com"
其中${YourInstanceId}: 请替换为您企业/公共实例的Id
对于2021年07月30日之前不含当日开通的物联网平台服务下公共实例
需要将mqtt_host修改为: mqtt_host = "${YourProductKey}.iot-as-mqtt.${YourRegionId}.aliyuncs.com"
其中, ${YourProductKey}请替换为设备所属产品的ProductKey。可登录物联网平台控制台在对应实例的设备详情页获取。
${YourRegionId}:请替换为您的物联网平台设备所在地域代码, 比如cn-shanghai等
该情况下完整mqtt_host举例: a1TTmBPIChA.iot-as-mqtt.cn-shanghai.aliyuncs.com
详情请见: https://help.aliyun.com/document_detail/147356.html
*/
char *mqtt_host = "${YourInstanceId}.mqtt.iothub.aliyuncs.com";
/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;
/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;
static task_desc_t *g_local_task_desc = NULL;
static pthread_t g_mqtt_process_thread;
static pthread_t g_mqtt_recv_thread;
static pthread_t g_task_thread; /* 用于执行任务的线程 */
static uint8_t g_mqtt_process_thread_running = 0;
static uint8_t g_mqtt_recv_thread_running = 0;
void demo_free_local_task(task_desc_t **task);
void *demo_task_thread(void *data)
{
#if 0
while (1) {
/* TODO: 执行任务 */
}
#endif
sleep(5);
return NULL;
}
/* 日志回调函数, SDK的日志会从这里输出 */
static int32_t demo_state_logcb(int32_t code, char *message)
{
printf("%s", message);
return 0;
}
/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata)
{
switch (event->type) {
/* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */
case AIOT_MQTTEVT_CONNECT: {
printf("AIOT_MQTTEVT_CONNECT\n");
}
break;
/* SDK因为网络状况被动断连后, 自动发起重连已成功 */
case AIOT_MQTTEVT_RECONNECT: {
printf("AIOT_MQTTEVT_RECONNECT\n");
}
break;
/* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */
case AIOT_MQTTEVT_DISCONNECT: {
char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :
("heartbeat disconnect");
printf("AIOT_MQTTEVT_DISCONNECT: %s\n", cause);
}
break;
default: {
}
}
}
/* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */
void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata)
{
switch (packet->type) {
case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: {
printf("heartbeat response\n");
}
break;
case AIOT_MQTTRECV_SUB_ACK: {
printf("suback, res: -0x%04X, packet id: %d, max qos: %d\n",
-packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);
}
break;
case AIOT_MQTTRECV_PUB: {
printf("pub, qos: %d, topic: %.*s\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic);
printf("pub, payload: %.*s\n", packet->data.pub.payload_len, packet->data.pub.payload);
}
break;
case AIOT_MQTTRECV_PUB_ACK: {
printf("puback, packet id: %d\n", packet->data.pub_ack.packet_id);
}
break;
default: {
}
}
}
/* 执行aiot_mqtt_process的线程, 包含心跳发送和QoS1消息重发 */
void *demo_mqtt_process_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_process_thread_running) {
res = aiot_mqtt_process(args);
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
return NULL;
}
/* 执行aiot_mqtt_recv的线程, 包含网络自动重连和从服务器收取MQTT消息 */
void *demo_mqtt_recv_thread(void *args)
{
int32_t res = STATE_SUCCESS;
while (g_mqtt_recv_thread_running) {
res = aiot_mqtt_recv(args);
if (res < STATE_SUCCESS) {
if (res == STATE_USER_INPUT_EXEC_DISABLED) {
break;
}
sleep(1);
}
}
return NULL;
}
int32_t demo_mqtt_start(void **handle)
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
uint16_t port = 1883; /* 无论设备是否使用TLS连接阿里云平台, 目的端口都是443 */
aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 */
/* 配置SDK的底层依赖 */
aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);
/* 配置SDK的日志输出 */
aiot_state_set_logcb(demo_state_logcb);
/* 创建SDK的安全凭据, 用于建立TLS连接 */
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
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根证书长度 */
/* 创建1个MQTT客户端实例并内部初始化默认参数 */
mqtt_handle = aiot_mqtt_init();
if (mqtt_handle == NULL) {
printf("aiot_mqtt_init failed\n");
return -1;
}
/* TODO: 如果以下代码不被注释, 则例程会用TCP而不是TLS连接云平台 */
/*
{
memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));
cred.option = AIOT_SYSDEP_NETWORK_CRED_NONE;
}
*/
/* 配置MQTT服务器地址 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);
/* 配置MQTT服务器端口 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);
/* 配置设备productKey */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);
/* 配置设备deviceName */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);
/* 配置设备deviceSecret */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);
/* 配置网络连接的安全凭据, 上面已经创建好了 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);
/* 配置MQTT默认消息接收回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler);
/* 配置MQTT事件回调函数 */
aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);
/* 与服务器建立MQTT连接 */
res = aiot_mqtt_connect(mqtt_handle);
if (res < STATE_SUCCESS) {
/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_connect failed: -0x%04X\n\r\n", -res);
printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");
return -1;
}
/* 创建一个单独的线程, 专用于执行aiot_mqtt_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */
g_mqtt_process_thread_running = 1;
res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_process_thread failed: %d\n", res);
aiot_mqtt_deinit(&mqtt_handle);
return -1;
}
/* 创建一个单独的线程用于执行aiot_mqtt_recv, 它会循环收取服务器下发的MQTT消息, 并在断线时自动重连 */
g_mqtt_recv_thread_running = 1;
res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle);
if (res < 0) {
printf("pthread_create demo_mqtt_recv_thread failed: %d\n", res);
g_mqtt_process_thread_running = 0;
pthread_join(g_mqtt_process_thread, NULL);
aiot_mqtt_deinit(&mqtt_handle);
return -1;
}
*handle = mqtt_handle;
return 0;
}
int32_t demo_mqtt_stop(void **handle)
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL;
mqtt_handle = *handle;
g_mqtt_process_thread_running = 0;
g_mqtt_recv_thread_running = 0;
pthread_join(g_mqtt_process_thread, NULL);
pthread_join(g_mqtt_recv_thread, NULL);
/* 断开MQTT连接 */
res = aiot_mqtt_disconnect(mqtt_handle);
if (res < STATE_SUCCESS) {
aiot_mqtt_deinit(&mqtt_handle);
printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);
return -1;
}
/* 销毁MQTT实例 */
res = aiot_mqtt_deinit(&mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_mqtt_deinit failed: -0x%04X\n", -res);
return -1;
}
return 0;
}
static void _demo_deep_free_base(char *in)
{
if (NULL == in) {
return;
}
free(in);
}
static void *_demo_deep_copy_base(char *in)
{
uint32_t len = 0;
void *tmp = NULL;
if (NULL == in) {
return NULL;
}
len = strlen(in) + 1;
tmp = (char *)malloc(len);
if (NULL == tmp) {
return NULL;
}
memset(tmp, 0, len);
memcpy(tmp, in, strlen(in));
return tmp;
}
void demo_copy_task_to_local_task(task_desc_t **dst_desc, const task_desc_t *in_desc)
{
if (NULL == *dst_desc) {
*dst_desc = (task_desc_t *)malloc(sizeof(task_desc_t));
}
(*dst_desc)->status = in_desc->status;
(*dst_desc)->progress = in_desc->progress;
(*dst_desc)->handle = in_desc->handle;
(*dst_desc)->task_id = _demo_deep_copy_base(in_desc->task_id);
(*dst_desc)->job_document = _demo_deep_copy_base(in_desc->job_document);
(*dst_desc)->sign_method = _demo_deep_copy_base(in_desc->sign_method);
(*dst_desc)->sign = _demo_deep_copy_base(in_desc->sign);
(*dst_desc)->document_file_url = _demo_deep_copy_base(in_desc->document_file_url);
(*dst_desc)->status_details = _demo_deep_copy_base(in_desc->status_details);
}
void demo_free_local_task(task_desc_t **task)
{
if (NULL != *task) {
_demo_deep_free_base((*task)->task_id);
_demo_deep_free_base((*task)->job_document);
_demo_deep_free_base((*task)->sign_method);
_demo_deep_free_base((*task)->sign);
_demo_deep_free_base((*task)->document_file_url);
_demo_deep_free_base((*task)->status_details);
free(*task);
}
*task = NULL;
}
void demo_task_recv_handler(void *handle, const aiot_task_recv_t *packet, void *userdata)
{
switch (packet->type) {
case AIOT_TASKRECV_NOTIFY: {
const task_desc_t *in_desc = &(packet->data.notify);
printf("revice task notify, task_id:[%s],status:[%d],job_document[%s],document_file_url:[%s],\
sign_method:[%s],sign[%s]\r\n",
in_desc->task_id, in_desc->status, in_desc->job_document,
in_desc->document_file_url, in_desc->sign_method, in_desc->sign);
/* 1.在handle里面没有任务记录的情况下, 把云端下行的task保存到handle里面的default_task_desc字段 */
if (NULL == g_local_task_desc) {
demo_copy_task_to_local_task(&g_local_task_desc, in_desc);
/* 启动任务. 这里仅为参考实现, 用户可以根据实际情况来适配 */
int res = pthread_create(&g_task_thread, NULL, demo_task_thread, g_local_task_desc);
if (res != 0) {
printf("pthread_create demo_task_thread failed: %d\r\n", res);
} else {
/* 下载线程被设置为 detach 类型, 固件内容获取完毕后可自主退出 */
pthread_detach(g_task_thread);
}
/* 变更任务状态.TODO: 这里仅为参考实现, 任务执行完毕时, 要将状态设置为AIOT_TASK_STATUS_SUCCEEDED*/
g_local_task_desc->status = AIOT_TASK_STATUS_IN_PROGRESS;
aiot_task_update(handle, g_local_task_desc);
demo_free_local_task(&g_local_task_desc);
break;
}
/* 2.如果状态被云端设置为终态, 则在这里将本地的任务清理掉 */
if (in_desc->status == AIOT_TASK_STATUS_CANCELLED || in_desc->status == AIOT_TASK_STATUS_REMOVED
|| in_desc->status == AIOT_TASK_STATUS_TIMED_OUT) {
/* TODO: 清理本地任务, 停下线程 */
/* 如果该任务是记录在handle里面的默认任务, 则要把它的内存清理掉; 如果是记录在handle外的, 需要用户自己维护内存 */
if (NULL != g_local_task_desc && 0 == strcmp(in_desc->task_id, g_local_task_desc->task_id)) {
/* 释放本地任务内存 */
demo_free_local_task(&g_local_task_desc);
}
break;
}
/* 3.在本地已有任务记录的情况,云端可能更新了当前这个任务的描述, 需要检查一下更新的内容 */
if (in_desc->status == AIOT_TASK_STATUS_IN_PROGRESS) {
if (NULL != g_local_task_desc && 0 == strcmp(in_desc->task_id, g_local_task_desc->task_id)) {
/* TODO: 更新本地的任务描述. 用户可能要暂停当前的任务再更新, 这一点取决于用户 */
break;
}
}
/* 4.如果不是上述情况, 说明来了一个新任务. 此时用户已经有一个任务在执行了, 又来了一个任务, 用户可以在main中自己创建一个列表 */
/* 将这个列表作为userdata传进来, 并把任务记录在这个列表里面, 自己维护*/
break;
}
case AIOT_TASKRECV_GET_DETAIL_REPLY: {
const task_get_detail_reply_t *in_reply = &(packet->data.get_detail_reply);
printf("revice task get detail, code:[%d]\r\n", in_reply->code);
if (200 == in_reply->code) {
printf("revice task get detail reply, task_id:[%s],status:[%d]\r\n",
in_reply->task.task_id, in_reply->task.status);
if (in_reply->task.status != AIOT_TASK_STATUS_NOT_FOUND) {
printf("job_document[%s],document_file_url:[%s], sign_method:[%s],sign[%s]\r\n",
in_reply->task.job_document, in_reply->task.document_file_url,
in_reply->task.sign_method, in_reply->task.sign);
task_desc_t task;
memset(&task, 0, sizeof(task));
memcpy(&task, &(in_reply->task), sizeof(task));
/* TODO: 执行任务, 可以通过起线程的方式 */
/* 变更任务状态. TODO: 这里仅为参考实现, 用户可以根据实际情况来适配. 任务执行完毕时, 要将状态设置为AIOT_TASK_STATUS_SUCCEEDED */
task.status = AIOT_TASK_STATUS_IN_PROGRESS;
task.progress = 88;
aiot_task_update(handle, &task);
}
}
break;
}
case AIOT_TASKRECV_GET_LIST_REPLY: {
const task_get_list_reply_t *in_reply = &(packet->data.get_list_reply);
printf("revice task get list reply, number:[%d]\r\n", in_reply->number);
for (int i = 0; i < in_reply->number; i++) {
printf("task list[%d]:task_id[%s],status[%d]\r\n", i, in_reply->tasks[i].task_id,
in_reply->tasks[i].status);
aiot_task_get_task_detail(handle, in_reply->tasks[i].task_id);
}
break;
}
case AIOT_TASKRECV_UPDATE_REPLY: {
const task_update_reply_t *update_reply = &(packet->data.update_reply);
printf("revice task update reply, code:[%d]\r\n", update_reply->code);
if (200 == update_reply->code) {
printf("revice task update reply, task_id:[%s]\r\n", update_reply->task_id);
}
if (71012 == update_reply->code) {
printf("aiot_task_update task's status_details value must be json format\r\n");
}
/* TODO */
break;
}
default:
/* TODO */
break;
}
}
int main(int argc, char *argv[])
{
int32_t res = STATE_SUCCESS;
void *mqtt_handle = NULL, *task_handle = NULL;
/* 建立MQTT连接, 并开启保活线程和接收线程 */
res = demo_mqtt_start(&mqtt_handle);
if (res < 0) {
printf("demo_mqtt_start failed\n");
return -1;
}
/* 创建1个task客户端实例并内部初始化默认参数 */
task_handle = aiot_task_init();
if (task_handle == NULL) {
demo_mqtt_stop(&mqtt_handle);
printf("aiot_task_init failed\n");
return -1;
}
res = aiot_task_setopt(task_handle, AIOT_TASKOPT_MQTT_HANDLE, mqtt_handle);
if (res < STATE_SUCCESS) {
printf("aiot_task_setopt AIOT_TASKOPT_MQTT_HANDLE failed, res: -0x%04X\n", -res);
aiot_task_deinit(&task_handle);
demo_mqtt_stop(&mqtt_handle);
return -1;
}
res = aiot_task_setopt(task_handle, AIOT_TASKOPT_RECV_HANDLER, demo_task_recv_handler);
if (res < STATE_SUCCESS) {
printf("aiot_task_setopt AIOT_TASKOPT_RECV_HANDLER failed, res: -0x%04X\n", -res);
aiot_task_deinit(&task_handle);
demo_mqtt_stop(&mqtt_handle);
return -1;
}
/* 发送task获取下一个可执行的任务的请求给云平台 */
res = aiot_task_get_task_detail(task_handle, NULL);
if (res < STATE_SUCCESS) {
aiot_task_deinit(&task_handle);
demo_mqtt_stop(&mqtt_handle);
return -1;
}
/* 主线程进入休眠, 等云平台的task回应到达时, 接收线程会调用 demo_task_recv_handler() */
while (1) {
sleep(1);
}
/* 销毁task实例, 一般不会运行到这里 */
res = aiot_task_deinit(&task_handle);
if (res < STATE_SUCCESS) {
demo_mqtt_stop(&mqtt_handle);
printf("aiot_task_deinit failed: -0x%04X\n", -res);
return -1;
}
/* 销毁MQTT实例, 退出线程, 一般不会运行到这里 */
res = demo_mqtt_stop(&mqtt_handle);
if (res < 0) {
printf("demo_start_stop failed\n");
return -1;
}
return 0;
}

Some files were not shown because too many files have changed in this diff Show More