add new qloud-c-sdk component
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
file(GLOB src_cos ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
|
||||
set(src_services ${src_services} ${src_cos} PARENT_SCOPE)
|
||||
|
||||
file(GLOB src_cos_download_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/cos_download_sample.c)
|
||||
add_executable(cos_download_sample ${src_cos_download_sample})
|
||||
target_link_libraries(cos_download_sample ${libsdk})
|
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file cos_download_sample.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-10-27
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-10-27 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "qcloud_iot_cos.h"
|
||||
|
||||
#include "utils_log.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Main
|
||||
// ----------------------------------------------------------------------------
|
||||
static int sg_main_exit = 0;
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void _main_exit(int sig)
|
||||
{
|
||||
Log_e("demo exit by signal:%d\n", sig);
|
||||
sg_main_exit = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#ifdef __linux__
|
||||
signal(SIGINT, _main_exit);
|
||||
#endif
|
||||
|
||||
int rc = 0;
|
||||
|
||||
// init log level
|
||||
LogHandleFunc func = {0};
|
||||
|
||||
func.log_malloc = HAL_Malloc;
|
||||
func.log_free = HAL_Free;
|
||||
func.log_get_current_time_str = HAL_Timer_Current;
|
||||
func.log_printf = HAL_Printf;
|
||||
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
|
||||
|
||||
IotCosDownloadParams connect_params = {
|
||||
.url = "http://localhost", // your cos url
|
||||
.file_size = 0, // your cos file size
|
||||
.offset = 0,
|
||||
.is_fragmentation = false,
|
||||
.is_https_enabled = false,
|
||||
};
|
||||
|
||||
uint8_t buf[1024];
|
||||
|
||||
void *handle = IOT_COS_DownloadInit(&connect_params);
|
||||
if (!handle) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
while (!IOT_COS_DownloadIsFinished(handle)) {
|
||||
rc = IOT_COS_DownloadFetch(handle, buf, sizeof(buf), 5000);
|
||||
if (rc < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
IOT_COS_DownloadDeinit(handle);
|
||||
exit:
|
||||
utils_log_deinit();
|
||||
return rc;
|
||||
}
|
@@ -0,0 +1,261 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file cos_download.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-10-25
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-10-25 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "qcloud_iot_cos.h"
|
||||
|
||||
/**
|
||||
* @brief COS request download header.
|
||||
*
|
||||
*/
|
||||
#define HTTP_COS_DOWNLOAD_REQUEST_HEADER_LEN 256
|
||||
|
||||
/**
|
||||
* @brief Download handle.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
IotCosDownloadParams params;
|
||||
void *http_client;
|
||||
IotHTTPRequestParams http_request;
|
||||
int download_size;
|
||||
int is_first_run;
|
||||
} HTTPCosDownloadHandle;
|
||||
|
||||
/**
|
||||
* @brief Connect cos http server.
|
||||
*
|
||||
* @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
|
||||
* @return 0 for success. others @see IotReturnCode
|
||||
*/
|
||||
static int _cos_download_connect(HTTPCosDownloadHandle *handle)
|
||||
{
|
||||
IotHTTPConnectParams connect_params = {
|
||||
.url = handle->params.url,
|
||||
.port = handle->params.is_https_enabled ? "443" : "80",
|
||||
.ca_crt = NULL, // TODO: support cert
|
||||
|
||||
};
|
||||
return IOT_HTTP_Connect(handle->http_client, &connect_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Construct cos download request header.
|
||||
*
|
||||
* @param[out] header pointer to request header
|
||||
* @param[in] is_fragmentation http fragmentation
|
||||
* @param[in] begin_byte download begin byte
|
||||
* @param[in] end_byte download end byte
|
||||
* @return > 0 for header len, others fail
|
||||
*/
|
||||
static int _cos_download_request_header_construct(char *header, int is_fragmentation, int begin_byte, int end_byte)
|
||||
{
|
||||
int len = HAL_Snprintf(header, HTTP_COS_DOWNLOAD_REQUEST_HEADER_LEN,
|
||||
"Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
|
||||
"Accept-Encoding:gzip, deflate\r\nRange:bytes=%d-%d\r\n",
|
||||
begin_byte, end_byte);
|
||||
if (len <= 0) {
|
||||
return QCLOUD_ERR_BUF_TOO_SHORT;
|
||||
}
|
||||
|
||||
if (is_fragmentation) {
|
||||
const char *keep_alive = "Connection:keep-alive\r\n";
|
||||
int keep_alive_len = strlen(keep_alive);
|
||||
if (HTTP_COS_DOWNLOAD_REQUEST_HEADER_LEN - len - 1 <= keep_alive_len) {
|
||||
return QCLOUD_ERR_BUF_TOO_SHORT;
|
||||
}
|
||||
strncpy(header + len, keep_alive, keep_alive_len);
|
||||
header[len + keep_alive_len] = '\0';
|
||||
len += keep_alive_len;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Request cos download.
|
||||
*
|
||||
* @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
|
||||
* @param[in] max_len max_len for buffer to download in fragmentation
|
||||
* @return 0 for success. others @see IotReturnCode
|
||||
*/
|
||||
static int _cos_download_request(HTTPCosDownloadHandle *handle, int max_len)
|
||||
{
|
||||
int rc, begin_byte, end_byte = 0;
|
||||
|
||||
begin_byte = handle->download_size;
|
||||
#define min_http(x, y) (((x) < (y)) ? (x) : (y))
|
||||
end_byte = handle->params.is_fragmentation
|
||||
? begin_byte + min_http(max_len, handle->params.file_size - handle->download_size) - 1
|
||||
: handle->params.file_size - 1;
|
||||
#undef min_http
|
||||
|
||||
rc = _cos_download_request_header_construct(handle->http_request.header, handle->params.is_fragmentation,
|
||||
begin_byte, end_byte);
|
||||
if (rc <= 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
handle->http_request.url = handle->params.url;
|
||||
handle->http_request.method = IOT_HTTP_METHOD_GET;
|
||||
handle->http_request.content_length = 0;
|
||||
handle->http_request.content = handle->http_request.content_type = NULL;
|
||||
return IOT_HTTP_Request(handle->http_client, &handle->http_request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief HTTP recv data.
|
||||
*
|
||||
* @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
|
||||
* @param[out] buf buf to store received data
|
||||
* @param[in] buf_len buf_len
|
||||
* @param[in] timeout_ms read socket timeout
|
||||
* @return >= 0 for recv data len. others @see IotReturnCode
|
||||
*/
|
||||
static int _cos_download_recv_data(HTTPCosDownloadHandle *handle, uint8_t *buf, int buf_len, uint32_t timeout_ms)
|
||||
{
|
||||
int rc = IOT_HTTP_Recv(handle->http_client, buf, buf_len, timeout_ms);
|
||||
if (rc > 0) {
|
||||
handle->download_size += rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**************************************************************************************
|
||||
* API
|
||||
**************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Init cos download handle.
|
||||
*
|
||||
* @param[in] params @see IotCosDownloadParams
|
||||
* @return pointer to cos download handle
|
||||
*/
|
||||
void *IOT_COS_DownloadInit(IotCosDownloadParams *params)
|
||||
{
|
||||
POINTER_SANITY_CHECK(params, NULL);
|
||||
|
||||
HTTPCosDownloadHandle *handle = HAL_Malloc(sizeof(HTTPCosDownloadHandle));
|
||||
if (!handle) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
handle->http_client = IOT_HTTP_Init();
|
||||
if (!handle->http_client) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
handle->http_request.header = HAL_Malloc(HTTP_COS_DOWNLOAD_REQUEST_HEADER_LEN);
|
||||
if (!handle->http_request.header) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
handle->params = *params;
|
||||
handle->download_size = params->offset;
|
||||
|
||||
if (_cos_download_connect(handle)) {
|
||||
goto exit;
|
||||
}
|
||||
handle->is_first_run = 1;
|
||||
return handle;
|
||||
exit:
|
||||
if (handle) {
|
||||
HAL_Free(handle->http_request.header);
|
||||
IOT_HTTP_Deinit(handle->http_client);
|
||||
HAL_Free(handle);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fetch data from cos.
|
||||
*
|
||||
* @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
|
||||
* @param[out] buf buffer to store data
|
||||
* @param[in] buf_len buffer length
|
||||
* @param timeout_ms timeout for fetching
|
||||
* @return >= 0 for recv data len. others @see IotReturnCode
|
||||
*/
|
||||
int IOT_COS_DownloadFetch(void *handle, uint8_t *buf, uint32_t buf_len, uint32_t timeout_ms)
|
||||
{
|
||||
POINTER_SANITY_CHECK(handle, QCLOUD_ERR_INVAL);
|
||||
int rc = 0;
|
||||
|
||||
HTTPCosDownloadHandle *download_handle = (HTTPCosDownloadHandle *)handle;
|
||||
|
||||
// download finish
|
||||
if (IOT_COS_DownloadIsFinished(handle)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (download_handle->is_first_run) {
|
||||
rc = _cos_download_request(download_handle, buf_len);
|
||||
if (rc) {
|
||||
Log_e("cos request failed %d", rc);
|
||||
return rc;
|
||||
}
|
||||
download_handle->is_first_run = 0;
|
||||
return _cos_download_recv_data(download_handle, buf, buf_len, timeout_ms);
|
||||
}
|
||||
|
||||
if (download_handle->params.is_fragmentation && IOT_HTTP_IsRecvFinished(download_handle->http_client)) {
|
||||
rc = _cos_download_request(download_handle, buf_len);
|
||||
if (rc) {
|
||||
Log_e("cos request failed %d", rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return _cos_download_recv_data(download_handle, buf, buf_len, timeout_ms);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Is download finished.
|
||||
*
|
||||
* @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
|
||||
* @return true for finished
|
||||
*/
|
||||
int IOT_COS_DownloadIsFinished(void *handle)
|
||||
{
|
||||
POINTER_SANITY_CHECK(handle, QCLOUD_ERR_INVAL);
|
||||
HTTPCosDownloadHandle *download_handle = (HTTPCosDownloadHandle *)handle;
|
||||
return download_handle->download_size == download_handle->params.file_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deinit cos download.
|
||||
*
|
||||
* @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
|
||||
*/
|
||||
void IOT_COS_DownloadDeinit(void *handle)
|
||||
{
|
||||
POINTER_SANITY_CHECK_RTN(handle);
|
||||
HTTPCosDownloadHandle *download_handle = (HTTPCosDownloadHandle *)handle;
|
||||
IOT_HTTP_Disconnect(download_handle->http_client);
|
||||
IOT_HTTP_Deinit(download_handle->http_client);
|
||||
HAL_Free(download_handle->http_request.header);
|
||||
HAL_Free(download_handle);
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
file(GLOB src_dynreg ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
|
||||
set(src_services ${src_services} ${src_dynreg} PARENT_SCOPE)
|
||||
file(GLOB src_dynreg_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/dynreg_sample.c)
|
||||
add_executable(dynreg_sample ${src_dynreg_sample})
|
||||
target_link_libraries(dynreg_sample ${libsdk})
|
||||
|
||||
if( ${CONFIG_IOT_TEST} STREQUAL "ON")
|
||||
file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
|
||||
set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
|
||||
endif()
|
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file cos_download_sample.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-10-27
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-10-27 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "qcloud_iot_common.h"
|
||||
#include "utils_log.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Main
|
||||
// ----------------------------------------------------------------------------
|
||||
static int sg_main_exit = 0;
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void _main_exit(int sig)
|
||||
{
|
||||
Log_e("demo exit by signal:%d\n", sig);
|
||||
sg_main_exit = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#ifdef __linux__
|
||||
signal(SIGINT, _main_exit);
|
||||
#endif
|
||||
|
||||
int rc = 0;
|
||||
|
||||
// init log level
|
||||
LogHandleFunc func = {0};
|
||||
|
||||
func.log_malloc = HAL_Malloc;
|
||||
func.log_free = HAL_Free;
|
||||
func.log_get_current_time_str = HAL_Timer_Current;
|
||||
func.log_printf = HAL_Printf;
|
||||
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
|
||||
|
||||
DeviceInfo device_info;
|
||||
// 1. get device info
|
||||
rc = HAL_GetDevInfo((void *)&device_info);
|
||||
if (rc) {
|
||||
Log_e("get device info failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
// 2. do dynreg
|
||||
rc = IOT_DynReg_Device(&device_info);
|
||||
if (rc) {
|
||||
Log_e("dynreg error");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// 3. get device secret just save it. then you can do mqtt
|
||||
HAL_SetDevInfo(&device_info);
|
||||
Log_i("device secret : %s", device_info.device_secret);
|
||||
|
||||
exit:
|
||||
utils_log_deinit();
|
||||
return rc;
|
||||
}
|
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* @file dynreg.c
|
||||
* @author {hubert} ({hubertxxu@tencent.com})
|
||||
* @brief
|
||||
* @version 1.0
|
||||
* @date 2022-01-26
|
||||
*
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* Date Version Author Description
|
||||
* 2022-01-26 1.0 hubertxxu first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "qcloud_iot_common.h"
|
||||
#include "utils_json.h"
|
||||
#include "utils_base64.h"
|
||||
#include "mbedtls/aes.h"
|
||||
|
||||
|
||||
#define DYN_RESPONSE_BUFF_LEN (256)
|
||||
#define DECODE_BUFF_LEN (256)
|
||||
#define UTILS_AES_BLOCK_LEN (16)
|
||||
|
||||
/**
|
||||
* @brief aes cbc handle
|
||||
*
|
||||
* @return int 0 is success other is error.
|
||||
*/
|
||||
static int _aes_cbc(uint8_t *input_data, uint32_t input_data_len, uint8_t *output_data, uint32_t output_data_len,
|
||||
DeviceInfo *device_info)
|
||||
{
|
||||
int i;
|
||||
uint8_t iv[16] = {0};
|
||||
char key[UTILS_AES_BLOCK_LEN + 1] = {0};
|
||||
for (i = 0; i < 16; i++) {
|
||||
iv[i] = '0';
|
||||
}
|
||||
strncpy(key, device_info->product_secret, UTILS_AES_BLOCK_LEN);
|
||||
mbedtls_aes_context aes_ctx;
|
||||
mbedtls_aes_init(&aes_ctx);
|
||||
mbedtls_aes_setkey_dec(&aes_ctx, (const unsigned char *)key, 128);
|
||||
if (mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, input_data_len, iv, (unsigned char *)input_data,
|
||||
(unsigned char *)output_data)) {
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
mbedtls_aes_free(&aes_ctx);
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief parse device register response buffer
|
||||
*
|
||||
* @param response_buf
|
||||
* @param response_len
|
||||
* @return int 0 success other error
|
||||
*/
|
||||
static int _parse_response_result(char *response_buf, int response_len, DeviceInfo *device_info)
|
||||
{
|
||||
int rc = 0;
|
||||
UtilsJsonValue value;
|
||||
char base64_decode_buf[DECODE_BUFF_LEN] = {0};
|
||||
size_t olen = 0;
|
||||
|
||||
// 1. find payload
|
||||
rc = utils_json_value_get("Response.Payload", strlen("Response.Payload"), response_buf, response_len, &value);
|
||||
if (rc) {
|
||||
Log_e("can not find payload. json : %s", response_buf);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// 2. base64 decode
|
||||
rc = utils_base64decode((uint8_t *)base64_decode_buf, DECODE_BUFF_LEN, &olen, (const uint8_t *)value.value,
|
||||
value.value_len);
|
||||
if (rc) {
|
||||
Log_e("base64 decode error.");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// 3. aes cbc
|
||||
olen = olen + (16 - olen % 16);
|
||||
rc = _aes_cbc((uint8_t *)base64_decode_buf, (uint32_t)olen, (uint8_t *)base64_decode_buf, DECODE_BUFF_LEN,
|
||||
device_info);
|
||||
if (rc) {
|
||||
Log_e("aes cbc error. ");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// 4. find psk
|
||||
uint32_t encryption_type = 0;
|
||||
rc = utils_json_get_uint32("encryptionType", strlen("encryptionType"), base64_decode_buf, DECODE_BUFF_LEN,
|
||||
&encryption_type);
|
||||
if (rc) {
|
||||
Log_e("parse encryptionType error. json : %s", base64_decode_buf);
|
||||
goto exit;
|
||||
}
|
||||
rc = utils_json_value_get("psk", strlen("psk"), base64_decode_buf, DECODE_BUFF_LEN, &value);
|
||||
if (rc) {
|
||||
Log_e("parse psk error. json : %s", base64_decode_buf);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// 5. copy psk to device info
|
||||
strncpy(device_info->device_secret, value.value, value.value_len);
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief dynreg device, get device secret or device cert file and private key file from iot platform
|
||||
*
|
||||
* @param[in] params @see DeviceInfo
|
||||
* @return 0 is success other is failed
|
||||
*/
|
||||
int IOT_DynReg_Device(DeviceInfo *device_info)
|
||||
{
|
||||
int rc = 0;
|
||||
POINTER_SANITY_CHECK(device_info, QCLOUD_ERR_INVAL);
|
||||
|
||||
if (strlen(device_info->product_secret) < UTILS_AES_BLOCK_LEN) {
|
||||
Log_e("product key illegal");
|
||||
rc = QCLOUD_ERR_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
char dynreg_buf[DYN_RESPONSE_BUFF_LEN] = {0};
|
||||
|
||||
/* constructor dynreg http body */
|
||||
int request_body_len =
|
||||
HAL_Snprintf(dynreg_buf, DYN_RESPONSE_BUFF_LEN, "{\"ProductId\":\"%s\",\"DeviceName\":\"%s\"}",
|
||||
device_info->product_id, device_info->device_name);
|
||||
|
||||
HttpSignedParams params = {
|
||||
.host = DYN_REG_SERVER_URL,
|
||||
.uri = DYN_REG_URI_PATH,
|
||||
.need_recv = true,
|
||||
.recv_timeout_ms = 2000,
|
||||
.secretkey = device_info->product_secret,
|
||||
};
|
||||
rc = IOT_HTTP_Signed_Request(¶ms, dynreg_buf, request_body_len, (uint8_t *)dynreg_buf, DYN_RESPONSE_BUFF_LEN);
|
||||
if (rc < 0) {
|
||||
goto exit;
|
||||
}
|
||||
rc = _parse_response_result(dynreg_buf, rc, device_info);
|
||||
exit:
|
||||
return rc;
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 120
|
||||
DerivePointerAlignment: true
|
||||
PointerAlignment: Left
|
||||
SortIncludes: true
|
||||
IncludeBlocks: Preserve
|
||||
IndentPPDirectives: AfterHash
|
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @file test_dynreg.cc
|
||||
* @author {hubert} ({hubertxxu@tencent.com})
|
||||
* @brief
|
||||
* @version 1.0
|
||||
* @date 2022-01-26
|
||||
*
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* Date Version Author Description
|
||||
* 2022-01-26 1.0 hubertxxu first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "mqtt_client_test.h"
|
||||
#include "qcloud_iot_common.h"
|
||||
|
||||
namespace mqtt_client_unittest {
|
||||
|
||||
TEST_F(MqttClientTest, dynreg_test) {
|
||||
DeviceInfo device_info;
|
||||
ASSERT_EQ(HAL_GetDevInfo(reinterpret_cast<void *>(&device_info)), 0);
|
||||
strncpy(device_info.device_name, "dynreg_test", 12);
|
||||
ASSERT_EQ(IOT_DynReg_Device(&device_info), 0);
|
||||
ASSERT_EQ(HAL_SetDevInfo(reinterpret_cast<void *>(&device_info)), 0);
|
||||
}
|
||||
|
||||
} // namespace mqtt_client_unittest
|
@@ -0,0 +1,2 @@
|
||||
file(GLOB src_http_client ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
|
||||
set(src_services ${src_services} ${src_http_client} PARENT_SCOPE)
|
@@ -0,0 +1,623 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file http_client.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-10-22
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-10-22 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "qcloud_iot_http_client.h"
|
||||
|
||||
#include "network_interface.h"
|
||||
|
||||
/**
|
||||
* @brief Http data.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
const char *data;
|
||||
int data_len;
|
||||
} HTTPData;
|
||||
|
||||
/**
|
||||
* @brief Only focus on response code and content,
|
||||
* @ref https://datatracker.ietf.org/doc/html/rfc7231
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
int status_code; /**< https://en.wikipedia.org/wiki/List_of_HTTP_status_codes */
|
||||
int content_length; /**< content length */
|
||||
uint8_t *content_buf; /**< content buffer */
|
||||
int content_buf_len; /**< content buffer length */
|
||||
int recv_len; /**< already recv length */
|
||||
int need_recv_len; /**< need recv length */
|
||||
int is_chunked; /**< support chunked */
|
||||
} IotHTTPResponseData;
|
||||
|
||||
/**
|
||||
* @brief HTTP client.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
IotHTTPResponseData response;
|
||||
IotNetwork network;
|
||||
} IotHTTPClient;
|
||||
|
||||
/**************************************************************************************
|
||||
* network
|
||||
**************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Connect with host.
|
||||
*
|
||||
* @param[in,out] client pointer to http client. @see IotHTTPClient
|
||||
* @param[in] host http host
|
||||
* @param[in] port http port, 80(http)/443(https)
|
||||
* @param[in] ca_crt https ca
|
||||
* @return 0 for success. others @see IotReturnCode
|
||||
*/
|
||||
static int _http_client_connect(IotHTTPClient *client, const char *host, const char *port, const char *ca_crt)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
client->network.type = IOT_NETWORK_TYPE_TCP;
|
||||
#if !defined(AUTH_WITH_NO_TLS) && defined(AUTH_MODE_CERT)
|
||||
client->network.ssl_connect_params.ca_crt = ca_crt;
|
||||
client->network.ssl_connect_params.ca_crt_len = strlen(ca_crt);
|
||||
client->network.ssl_connect_params.cert_file = NULL;
|
||||
client->network.ssl_connect_params.key_file = NULL;
|
||||
client->network.ssl_connect_params.timeout_ms = HTTPS_READ_TIMEOUT_MS;
|
||||
#endif
|
||||
client->network.host = host;
|
||||
client->network.port = port;
|
||||
|
||||
rc = qcloud_iot_network_init(&(client->network));
|
||||
return rc ? rc : client->network.connect(&(client->network));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnect with host.
|
||||
*
|
||||
* @param[in,out] client pointer to http client. @see IotHTTPClient
|
||||
*/
|
||||
static void _http_client_disconnect(IotHTTPClient *client)
|
||||
{
|
||||
return client->network.disconnect(&(client->network));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send data to http server.
|
||||
*
|
||||
* @param[in,out] client pointer to http client. @see IotHTTPClient
|
||||
* @param[in] buf buffer tot send
|
||||
* @param[in] len buffer length
|
||||
* @return 0 for success. others @see IotReturnCode
|
||||
*/
|
||||
static int _http_client_send(IotHTTPClient *client, char *buf, uint32_t len)
|
||||
{
|
||||
size_t written_len;
|
||||
return client->network.write(&(client->network), (uint8_t *)buf, len, HTTP_WRITE_TIMEOUT_MS, &written_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Recv data from http server.
|
||||
*
|
||||
* @param[in,out] client pointer to http client. @see IotHTTPClient
|
||||
* @param[out] buf
|
||||
* @param[in] len
|
||||
* @param[in] timeout_ms
|
||||
* @return >0 for read data length, others @see IotReturnCode
|
||||
*/
|
||||
static int _http_client_recv(IotHTTPClient *client, uint8_t *buf, uint32_t len, uint32_t timeout_ms)
|
||||
{
|
||||
size_t read_len;
|
||||
|
||||
int rc = client->network.read(&client->network, buf, len, timeout_ms, &read_len);
|
||||
switch (rc) {
|
||||
case QCLOUD_ERR_TCP_NOTHING_TO_READ:
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
default:
|
||||
return rc ? rc : read_len;
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************************
|
||||
* request
|
||||
**************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Parse url and get host & path.
|
||||
*
|
||||
* @param[in] url url str
|
||||
* @param[out] host pointer to host str and length. @see HTTPData
|
||||
* @param[out] path pointer to path str and length. @see HTTPData
|
||||
* @return 0 for success. others @see IotReturnCode
|
||||
*/
|
||||
static int _http_client_parse_url(const char *url, HTTPData *host, HTTPData *path)
|
||||
{
|
||||
// start from: http:// or https://
|
||||
char *host_ptr = (char *)strstr(url, "://");
|
||||
if (!host_ptr) {
|
||||
return QCLOUD_ERR_HTTP_PARSE;
|
||||
}
|
||||
host->data = host_ptr + 3;
|
||||
|
||||
path->data = strchr(host->data, '/');
|
||||
if (!path->data) {
|
||||
host->data_len = url + strlen(url) - host->data;
|
||||
path->data_len = 1;
|
||||
path->data = "/";
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
host->data_len = path->data - host->data;
|
||||
|
||||
char *fragment_ptr = strchr(host_ptr, '#');
|
||||
path->data_len = fragment_ptr ? fragment_ptr - path->data : strlen(path->data);
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send http request line.
|
||||
*
|
||||
* @param[in,out] client pointer to http client. @see IotHTTPClient
|
||||
* @param[in] params params needed to send request to http server, @see IotHTTPRequestParams
|
||||
* @return int
|
||||
*/
|
||||
static int _http_client_send_request_line(IotHTTPClient *client, const IotHTTPRequestParams *params)
|
||||
{
|
||||
int len, rc, buf_len;
|
||||
char *buf;
|
||||
HTTPData host, path;
|
||||
|
||||
/**
|
||||
* @brief order @see IotHTTPMethod
|
||||
*
|
||||
*/
|
||||
const char *method_str[] = {"GET", "POST", "PUT", "DELETE", "HEAD"};
|
||||
|
||||
rc = _http_client_parse_url(params->url, &host, &path);
|
||||
if (rc) {
|
||||
Log_e("http parse url failed %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
buf_len = path.data_len + host.data_len + 128;
|
||||
buf = HAL_Malloc(buf_len);
|
||||
if (!buf) {
|
||||
Log_e("http malloc request line failed %d", rc);
|
||||
return QCLOUD_ERR_MALLOC;
|
||||
}
|
||||
|
||||
len = HAL_Snprintf(buf, buf_len, "%s %.*s HTTP/1.1\r\nHost:%.*s\r\n", method_str[params->method], path.data_len,
|
||||
path.data, host.data_len, host.data);
|
||||
rc = _http_client_send(client, buf, len);
|
||||
HAL_Free(buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send http request content(optional).
|
||||
*
|
||||
* @param[in,out] client pointer to http client. @see IotHTTPClient
|
||||
* @param[in] params params needed to send request to http server, @see IotHTTPRequestParams
|
||||
* @return int
|
||||
*/
|
||||
static int _http_client_send_request_content(IotHTTPClient *client, const IotHTTPRequestParams *params)
|
||||
{
|
||||
int rc = 0, len, buf_len;
|
||||
char *buf;
|
||||
|
||||
buf_len = params->content_type ? 32 + strlen(params->content_type) : 32;
|
||||
buf = HAL_Malloc(buf_len);
|
||||
if (!buf) {
|
||||
Log_e("http malloc request line failed %d", rc);
|
||||
return QCLOUD_ERR_MALLOC;
|
||||
}
|
||||
|
||||
len = HAL_Snprintf(buf, buf_len, "Content-Length:%d\r\n", params->content_length);
|
||||
rc = _http_client_send(client, buf, len);
|
||||
if (rc) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (params->content_type) {
|
||||
len = HAL_Snprintf(buf, buf_len, "Content-Type:%s\r\n", params->content_type);
|
||||
rc = _http_client_send(client, buf, len);
|
||||
if (rc) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
rc = _http_client_send(client, "\r\n", 2);
|
||||
if (rc) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
HAL_Free(buf);
|
||||
return _http_client_send(client, (char *)params->content, params->content_length);
|
||||
exit:
|
||||
HAL_Free(buf);
|
||||
Log_e("send request content failed %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send http request to http server.
|
||||
*
|
||||
* @param[in,out] client pointer to http client. @see IotHTTPClient
|
||||
* @param[in] params @see IotHTTPRequestParams
|
||||
* @return 0 for success. others @see IotReturnCode
|
||||
*/
|
||||
static int _http_client_send_request(IotHTTPClient *client, const IotHTTPRequestParams *params)
|
||||
{
|
||||
int rc;
|
||||
|
||||
// 1. send request line
|
||||
rc = _http_client_send_request_line(client, params);
|
||||
if (rc) {
|
||||
Log_e("http send request line failed, rc=%d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// 2. send request header
|
||||
if (params->header) {
|
||||
rc = _http_client_send(client, (char *)params->header, strlen(params->header));
|
||||
if (rc) {
|
||||
Log_e("write failed %d", rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if (!params->content || !params->content_length) { // no payload body
|
||||
return _http_client_send(client, "\r\n", 2);
|
||||
}
|
||||
|
||||
// 3. send payload body
|
||||
return _http_client_send_request_content(client, params);
|
||||
}
|
||||
|
||||
/**************************************************************************************
|
||||
* response
|
||||
**************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Recv data chunked.
|
||||
*
|
||||
* @param[in,out] client pointer to http client. @see IotHTTPClient
|
||||
* @param[in] timeout_ms timeout for recv
|
||||
* @return length of content data recv stored in content buf
|
||||
*/
|
||||
static int _http_client_chunked_recv(IotHTTPClient *client, uint32_t timeout_ms)
|
||||
{
|
||||
uint8_t *buf = client->response.content_buf;
|
||||
int buf_len = client->response.content_buf_len;
|
||||
|
||||
IotHTTPResponseData *response = &client->response;
|
||||
|
||||
char *crlf_pointer = NULL;
|
||||
char *find_from = (char *)buf;
|
||||
int rc = 0, read_size = 0;
|
||||
int read_size_flag = 1;
|
||||
|
||||
Timer read_timer;
|
||||
HAL_Timer_CountdownMs(&read_timer, timeout_ms);
|
||||
|
||||
do {
|
||||
while (1) {
|
||||
// find \r\n from find_from, start with beginning
|
||||
crlf_pointer = strstr(find_from, "\r\n");
|
||||
if (crlf_pointer) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (HAL_Timer_Expired(&read_timer)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// try to read more to found \r\n
|
||||
rc = _http_client_recv(client, buf + response->recv_len, buf_len - response->recv_len, 100);
|
||||
if (rc < 0) {
|
||||
return response->recv_len;
|
||||
}
|
||||
response->recv_len += rc;
|
||||
}
|
||||
|
||||
// read size before \r\n
|
||||
if (read_size_flag) {
|
||||
*crlf_pointer = '\0';
|
||||
read_size = strtoul(find_from, NULL, 16);
|
||||
response->recv_len -= crlf_pointer - find_from + 2;
|
||||
if (!read_size) { // last chunk
|
||||
response->need_recv_len = 0;
|
||||
return response->recv_len;
|
||||
}
|
||||
memmove(find_from, crlf_pointer + 2, buf + response->recv_len - (uint8_t *)find_from);
|
||||
read_size_flag = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// remove /r/n and set find from
|
||||
response->recv_len -= 2;
|
||||
memmove(crlf_pointer, crlf_pointer + 2, buf + response->recv_len - (uint8_t *)crlf_pointer);
|
||||
find_from += read_size;
|
||||
read_size_flag = true;
|
||||
} while (!HAL_Timer_Expired(&read_timer));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Recv data according content length.
|
||||
*
|
||||
* @param[in,out] client pointer to http client. @see IotHTTPClient
|
||||
* @param[in] offset offset of content buf
|
||||
* @param[in] timeout_ms timeout for recv
|
||||
* @return length of content data recv stored in content buf
|
||||
*/
|
||||
static int _http_client_content_length_recv(IotHTTPClient *client, int offset, uint32_t timeout_ms)
|
||||
{
|
||||
int rc, len = 0;
|
||||
|
||||
uint8_t *buf = client->response.content_buf;
|
||||
int buf_len = client->response.content_buf_len;
|
||||
|
||||
IotHTTPResponseData *response = &client->response;
|
||||
|
||||
if (response->need_recv_len <= 0) {
|
||||
// return offset for data already received
|
||||
return offset;
|
||||
}
|
||||
|
||||
// need recv length may much longger than buffer length
|
||||
len = response->need_recv_len > buf_len - offset ? buf_len - offset : response->need_recv_len;
|
||||
rc = _http_client_recv(client, buf + offset, len, timeout_ms);
|
||||
if (rc <= 0) {
|
||||
return offset ? offset : rc;
|
||||
}
|
||||
response->recv_len += rc;
|
||||
response->need_recv_len -= rc;
|
||||
return rc + offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Recv content data.
|
||||
*
|
||||
* @param[in,out] client pointer to http client. @see IotHTTPClient
|
||||
* @param[in] offset offset of content buf
|
||||
* @param[in] timeout_ms timeout for recv
|
||||
* @return length of content data recv stored in content buf
|
||||
*/
|
||||
static int _http_client_recv_content(IotHTTPClient *client, int offset, uint32_t timeout_ms)
|
||||
{
|
||||
return client->response.is_chunked ? _http_client_chunked_recv(client, timeout_ms)
|
||||
: _http_client_content_length_recv(client, offset, timeout_ms);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Recv response from http server.
|
||||
*
|
||||
* @param[in,out] client pointer to http client. @see IotHTTPClient
|
||||
* @param[in] timeout_ms timeout for recv
|
||||
* @return 0 for success. others @see IotReturnCode
|
||||
*/
|
||||
static int _http_client_recv_response(IotHTTPClient *client, uint32_t timeout_ms)
|
||||
{
|
||||
Timer timer;
|
||||
char *content_length, *body_end;
|
||||
int rc, len = 0;
|
||||
char *buf = (char *)client->response.content_buf;
|
||||
int buf_len = client->response.content_buf_len;
|
||||
|
||||
IotHTTPResponseData *response = &client->response;
|
||||
memset(buf, 0, buf_len);
|
||||
HAL_Timer_CountdownMs(&timer, timeout_ms);
|
||||
|
||||
// 1. found body end
|
||||
while (NULL == (body_end = strstr(buf, "\r\n\r\n"))) {
|
||||
// timeout
|
||||
if (HAL_Timer_Expired(&timer)) {
|
||||
return QCLOUD_ERR_HTTP_TIMEOUT;
|
||||
}
|
||||
// timeout 100ms for header less than buff len
|
||||
rc = _http_client_recv(client, (uint8_t *)buf + len, buf_len - len - 1, 100);
|
||||
if (rc < 0) {
|
||||
Log_e("read failed, rc %d", rc);
|
||||
return rc;
|
||||
}
|
||||
len += rc;
|
||||
}
|
||||
|
||||
body_end += 4; // \r\n\r\n
|
||||
len = len - (body_end - buf); // body length
|
||||
|
||||
// 2. get response code
|
||||
response->status_code = atoi(buf + 9);
|
||||
switch (response->status_code) {
|
||||
case 403:
|
||||
return QCLOUD_ERR_HTTP_AUTH;
|
||||
case 404:
|
||||
return QCLOUD_ERR_HTTP_NOT_FOUND;
|
||||
default:
|
||||
if (response->status_code < 200 || response->status_code >= 400) {
|
||||
Log_w("HTTP status code %d", response->status_code);
|
||||
return QCLOUD_ERR_HTTP;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 3. parse header
|
||||
// content length
|
||||
content_length = strstr(buf, "Content-Length");
|
||||
if (content_length) {
|
||||
response->is_chunked = 0;
|
||||
response->content_length = atoi(content_length + strlen("Content-Length: "));
|
||||
response->need_recv_len = response->content_length - len;
|
||||
goto recv_content;
|
||||
}
|
||||
|
||||
// chunked
|
||||
if (strstr(buf, "Transfer-Encoding: chunked")) {
|
||||
response->is_chunked = 1;
|
||||
response->need_recv_len = 1; // means always need recv
|
||||
goto recv_content;
|
||||
}
|
||||
|
||||
Log_e("Could not parse header");
|
||||
response->content_length = -1;
|
||||
return QCLOUD_ERR_HTTP;
|
||||
|
||||
// 4. remove data and receive body data
|
||||
recv_content:
|
||||
memmove(buf, body_end, len);
|
||||
response->recv_len = len;
|
||||
return _http_client_recv_content(client, len, HAL_Timer_Remain(&timer));
|
||||
}
|
||||
|
||||
/**************************************************************************************
|
||||
* API
|
||||
**************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Malloc http client.
|
||||
*
|
||||
* @return pointer to http client
|
||||
*/
|
||||
void *IOT_HTTP_Init(void)
|
||||
{
|
||||
return HAL_Malloc(sizeof(IotHTTPClient));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Free http client.
|
||||
*
|
||||
*/
|
||||
void IOT_HTTP_Deinit(void *client)
|
||||
{
|
||||
HAL_Free(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connect http server.
|
||||
*
|
||||
* @param[in,out] client pointer to http client
|
||||
* @param[in] params params needed to connect http server, @see IotHTTPConnectParams
|
||||
* @return 0 for success. others @see IotReturnCode
|
||||
*/
|
||||
int IOT_HTTP_Connect(void *client, IotHTTPConnectParams *params)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
IotHTTPClient *http_client = (IotHTTPClient *)client;
|
||||
memset(http_client, 0, sizeof(IotHTTPClient));
|
||||
|
||||
int rc;
|
||||
|
||||
// parse url
|
||||
HTTPData host, path;
|
||||
rc = _http_client_parse_url(params->url, &host, &path);
|
||||
if (rc) {
|
||||
Log_e("http parse url failed %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// copy host
|
||||
char *host_str = HAL_Malloc(host.data_len + 1);
|
||||
if (!host_str) {
|
||||
return QCLOUD_ERR_MALLOC;
|
||||
}
|
||||
strncpy(host_str, host.data, host.data_len);
|
||||
host_str[host.data_len] = '\0';
|
||||
|
||||
// http connect
|
||||
rc = _http_client_connect(http_client, host_str, params->port, params->ca_crt);
|
||||
HAL_Free(host_str);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Request http server.
|
||||
*
|
||||
* @param[in,out] client pointer to http client
|
||||
* @param[in] params params needed to send request to http server, @see IotHTTPRequestParams
|
||||
* @return 0 for success. others @see IotReturnCode
|
||||
*/
|
||||
int IOT_HTTP_Request(void *client, IotHTTPRequestParams *params)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
return _http_client_send_request((IotHTTPClient *)client, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send data to http server.
|
||||
*
|
||||
* @param[in,out] client pointer to http client
|
||||
* @param[in] data data to send
|
||||
* @param[out] data_len data len
|
||||
* @return 0 for success. others @see IotReturnCode
|
||||
*/
|
||||
int IOT_HTTP_Send(void *client, uint8_t *data, int data_len)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
return _http_client_send((IotHTTPClient *)client, (char *)data, data_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Recv data from http server.
|
||||
*
|
||||
* @param[in,out] client pointer to http client
|
||||
* @param[out] buf buffer to store recv data
|
||||
* @param[in] buf_len buffer len
|
||||
* @param timeout_ms timeout for recv
|
||||
* @return >= 0 for recv data len. others @see IotReturnCode
|
||||
*/
|
||||
int IOT_HTTP_Recv(void *client, uint8_t *buf, int buf_len, uint32_t timeout_ms)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
IotHTTPClient *http_client = (IotHTTPClient *)client;
|
||||
http_client->response.content_buf = buf;
|
||||
http_client->response.content_buf_len = buf_len;
|
||||
return http_client->response.need_recv_len ? _http_client_recv_content(http_client, 0, timeout_ms)
|
||||
: _http_client_recv_response(http_client, timeout_ms);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check is recv finished.
|
||||
*
|
||||
* @param[in,out] client pointer to http client
|
||||
* @return true for finished.
|
||||
*/
|
||||
int IOT_HTTP_IsRecvFinished(void *client)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
IotHTTPClient *http_client = (IotHTTPClient *)client;
|
||||
return http_client->response.need_recv_len == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnect http server.
|
||||
*
|
||||
* @param[in,out] client pointer to http client
|
||||
*/
|
||||
void IOT_HTTP_Disconnect(void *client)
|
||||
{
|
||||
POINTER_SANITY_CHECK_RTN(client);
|
||||
_http_client_disconnect((IotHTTPClient *)client);
|
||||
}
|
@@ -0,0 +1,2 @@
|
||||
file(GLOB src_http_signed ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
|
||||
set(src_services ${src_services} ${src_http_signed} PARENT_SCOPE)
|
@@ -0,0 +1,278 @@
|
||||
/**
|
||||
* @file http_signed.c
|
||||
* @author {hubert} ({hubertxxu@tencent.com})
|
||||
* @brief
|
||||
* @version 1.0
|
||||
* @date 2022-01-21
|
||||
*
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* Date Version Author Description
|
||||
* 2022-01-21 1.0 hubertxxu first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "utils_base64.h"
|
||||
|
||||
#include "qcloud_iot_http_signed.h"
|
||||
|
||||
/**
|
||||
* @brief http signed request header.
|
||||
*
|
||||
*/
|
||||
#define HTTP_SIGNED_REQUEST_HEADER_LEN (1024)
|
||||
|
||||
/**
|
||||
* @brief http signed request header signed length
|
||||
*
|
||||
*/
|
||||
#define HTTP_SIGNED_STRING_BUFFER_LEN (256)
|
||||
|
||||
/**
|
||||
* @brief http signed request url length
|
||||
*
|
||||
*/
|
||||
#define HTTP_SIGNED_URL_LEN (128)
|
||||
|
||||
#define QCLOUD_SHA256_RESULT_LEN (32)
|
||||
#define QCLOUD_SHA1_RESULT_LEN (20)
|
||||
|
||||
typedef struct {
|
||||
HttpSignedParams params;
|
||||
void *http_client;
|
||||
IotHTTPRequestParams http_request;
|
||||
char *sign_string;
|
||||
char *url;
|
||||
} HTTPSignedHandle;
|
||||
|
||||
/**
|
||||
* @brief get rand
|
||||
*
|
||||
* @param[in, out] seed
|
||||
* @return int
|
||||
*/
|
||||
static int _get_http_header_nonce(void)
|
||||
{
|
||||
srand(HAL_Timer_CurrentSec());
|
||||
return rand();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connect http server.
|
||||
*
|
||||
* @param[in,out] handle pointer to http signed handle, @see HTTPSignedHandle
|
||||
* @return 0 for success. others @see IotReturnCode
|
||||
*/
|
||||
static int _http_signed_connect(HTTPSignedHandle *handle)
|
||||
{
|
||||
IotHTTPConnectParams connect_params = {
|
||||
.url = handle->url,
|
||||
.port = "80",
|
||||
.ca_crt = NULL, // TODO: support cert
|
||||
|
||||
};
|
||||
return IOT_HTTP_Connect(handle->http_client, &connect_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief http signed header construct
|
||||
*
|
||||
* @param[in,out] handle pointer to http signed handle, @see HTTPSignedHandle
|
||||
* @param request_body_buf
|
||||
* @param request_body_buf_len
|
||||
* @return 0 if success
|
||||
*/
|
||||
static void _http_signed_upload_header_construct(HTTPSignedHandle *handle, const char *request_body_buf,
|
||||
int request_body_buf_len)
|
||||
{
|
||||
/**
|
||||
* @brief http signed header format
|
||||
*
|
||||
*/
|
||||
#define QCLOUD_HTTP_HEADER_FORMAT \
|
||||
"Accept: %s*/*\r\n" \
|
||||
"X-TC-Algorithm: %s\r\n" \
|
||||
"X-TC-Timestamp: %d\r\n" \
|
||||
"X-TC-Nonce: %d\r\n" \
|
||||
"X-TC-Signature: %s\r\n"
|
||||
|
||||
#define QCLOUD_SUPPORT_HMACSHA1 "hmacsha1"
|
||||
#define QCLOUD_SUPPORT_RSASHA256 "rsa-sha256"
|
||||
#define QCLOUD_SUPPORT_ACCEPT_HEADER "application/json;"
|
||||
|
||||
char sign[QCLOUD_SHA1_RESULT_LEN] = {0};
|
||||
char signout[QCLOUD_SHA1_RESULT_LEN * 2] = {0};
|
||||
char request_buf_sha1[QCLOUD_SHA1_RESULT_LEN * 2 + 1] = {0};
|
||||
size_t olen = 0;
|
||||
int nonce = _get_http_header_nonce();
|
||||
uint32_t timestamp = HAL_Timer_CurrentSec();
|
||||
|
||||
memset(handle->sign_string, 0, HTTP_SIGNED_STRING_BUFFER_LEN);
|
||||
/* cal hmac sha1 */
|
||||
utils_sha1_hex((const uint8_t *)request_body_buf, request_body_buf_len, (uint8_t *)request_buf_sha1);
|
||||
/* create sign string */
|
||||
HAL_Snprintf(handle->sign_string, HTTP_SIGNED_STRING_BUFFER_LEN, "%s\n%s\n%s\n%s\n%s\n%d\n%d\n%s", "POST",
|
||||
handle->params.host, handle->params.uri, "", QCLOUD_SUPPORT_HMACSHA1, timestamp, nonce,
|
||||
request_buf_sha1);
|
||||
|
||||
utils_hmac_sha1_hex(handle->sign_string, strlen(handle->sign_string), (uint8_t *)handle->params.secretkey,
|
||||
strlen(handle->params.secretkey), sign);
|
||||
|
||||
/* base64 encode */
|
||||
utils_base64encode((uint8_t *)signout, QCLOUD_SHA1_RESULT_LEN * 2, &olen, (const uint8_t *)sign,
|
||||
QCLOUD_SHA1_RESULT_LEN);
|
||||
|
||||
HAL_Snprintf(handle->http_request.header, HTTP_SIGNED_REQUEST_HEADER_LEN, QCLOUD_HTTP_HEADER_FORMAT,
|
||||
QCLOUD_SUPPORT_ACCEPT_HEADER, QCLOUD_SUPPORT_HMACSHA1, timestamp, nonce, signout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief http signed init.
|
||||
*
|
||||
* @param params @see HttpSignedParams
|
||||
* @return pointer to http signed @see HTTPSignedHandle
|
||||
*/
|
||||
void *_http_signed_init(HttpSignedParams *params)
|
||||
{
|
||||
HTTPSignedHandle *handle = HAL_Malloc(sizeof(HTTPSignedHandle));
|
||||
if (!handle) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
handle->http_client = IOT_HTTP_Init();
|
||||
if (!handle->http_client) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
handle->http_request.header = HAL_Malloc(HTTP_SIGNED_REQUEST_HEADER_LEN);
|
||||
if (!handle->http_request.header) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
handle->sign_string = HAL_Malloc(HTTP_SIGNED_STRING_BUFFER_LEN);
|
||||
if (!handle->sign_string) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
handle->url = HAL_Malloc(HTTP_SIGNED_URL_LEN);
|
||||
if (!handle->url) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
handle->params = *params;
|
||||
memset(handle->url, 0, HTTP_SIGNED_URL_LEN);
|
||||
HAL_Snprintf(handle->url, HTTP_SIGNED_URL_LEN, "%s://%s%s", "http", handle->params.host, handle->params.uri);
|
||||
|
||||
return handle;
|
||||
exit:
|
||||
if (handle) {
|
||||
HAL_Free(handle->http_request.header);
|
||||
HAL_Free(handle->sign_string);
|
||||
HAL_Free(handle->url);
|
||||
IOT_HTTP_Deinit(handle->http_client);
|
||||
HAL_Free(handle);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief send to server
|
||||
*
|
||||
* @param[in] handle pointer to http signed @see HTTPSignedHandle
|
||||
* @param[in] upload_buf need send data
|
||||
* @param[in] upload_len send length
|
||||
* @return int 0 for success. others @see IotReturnCode
|
||||
*/
|
||||
int _http_signed_upload(HTTPSignedHandle *handle, const char *upload_buf, size_t upload_len)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc = _http_signed_connect(handle);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
_http_signed_upload_header_construct(handle, upload_buf, upload_len);
|
||||
|
||||
handle->http_request.url = handle->url;
|
||||
handle->http_request.method = IOT_HTTP_METHOD_POST;
|
||||
handle->http_request.content_type = "application/json;charset=utf-8";
|
||||
handle->http_request.content = (char *)upload_buf;
|
||||
handle->http_request.content_length = upload_len;
|
||||
|
||||
rc = IOT_HTTP_Request(handle->http_client, &handle->http_request);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief http signed deinit
|
||||
*
|
||||
* @param[in] handle pointer to http signed @see HTTPSignedHandle
|
||||
*/
|
||||
static void _http_signed_deinit(HTTPSignedHandle *signed_handle)
|
||||
{
|
||||
POINTER_SANITY_CHECK_RTN(signed_handle);
|
||||
IOT_HTTP_Disconnect(signed_handle->http_client);
|
||||
IOT_HTTP_Deinit(signed_handle->http_client);
|
||||
HAL_Free(signed_handle->http_request.header);
|
||||
HAL_Free(signed_handle->sign_string);
|
||||
HAL_Free(signed_handle->url);
|
||||
HAL_Free(signed_handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief post message and recv response
|
||||
*
|
||||
* @param params @see HttpSignedParams
|
||||
* @param request_buf request buffer
|
||||
* @param request_buf_len request buffer length
|
||||
* @param response_buf response buffer if need recv
|
||||
* @param response_buf_len response buffer length if need recv
|
||||
* @return int if need recv return recv length else return 0 is success
|
||||
*/
|
||||
int IOT_HTTP_Signed_Request(HttpSignedParams *params, const char *request_buf, size_t request_buf_len,
|
||||
uint8_t *response_buf, int response_buf_len)
|
||||
{
|
||||
int rc = 0;
|
||||
POINTER_SANITY_CHECK(params, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(request_buf, QCLOUD_ERR_INVAL);
|
||||
if (params->need_recv) {
|
||||
POINTER_SANITY_CHECK(response_buf, QCLOUD_ERR_INVAL);
|
||||
}
|
||||
HTTPSignedHandle *handle = _http_signed_init(params);
|
||||
if (!handle) {
|
||||
Log_e("http signed init error.");
|
||||
goto exit;
|
||||
}
|
||||
rc = _http_signed_upload(handle, request_buf, request_buf_len);
|
||||
if (rc) {
|
||||
Log_e("http signed upload error.");
|
||||
goto exit;
|
||||
}
|
||||
if (params->need_recv) {
|
||||
rc = IOT_HTTP_Recv(handle->http_client, response_buf, response_buf_len, params->recv_timeout_ms);
|
||||
}
|
||||
|
||||
exit:
|
||||
_http_signed_deinit(handle);
|
||||
return rc;
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
file(GLOB src_log_upload ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
|
||||
set(inc_log_upload ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
|
||||
|
||||
set(src_services ${src_services} ${src_log_upload} PARENT_SCOPE)
|
||||
set(inc_services ${inc_services} ${inc_log_upload} PARENT_SCOPE)
|
||||
|
||||
file(GLOB src_log_upload_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/log_upload_sample.c)
|
||||
add_executable(log_upload_sample ${src_log_upload_sample})
|
||||
target_link_libraries(log_upload_sample ${libsdk})
|
||||
|
||||
if( ${CONFIG_IOT_TEST} STREQUAL "ON")
|
||||
file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
|
||||
set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
|
||||
endif()
|
@@ -0,0 +1,215 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file mqtt_client.h
|
||||
* @brief mqtt client internel api
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-31
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-31 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_LOG_UPLOAD_INC_LOG_UPLOAD_H_
|
||||
#define IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_LOG_UPLOAD_INC_LOG_UPLOAD_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "qcloud_iot_log_upload.h"
|
||||
|
||||
#include "utils_list.h"
|
||||
#include "utils_base64.h"
|
||||
#include "utils_hmac.h"
|
||||
|
||||
/**
|
||||
* @brief To check log http server return msg or not
|
||||
*
|
||||
*/
|
||||
#define LOG_CHECK_HTTP_RET_CODE
|
||||
|
||||
/**
|
||||
* @brief enable log upload debug or not
|
||||
*
|
||||
*/
|
||||
// #define LOG_UPLOAD_DEBUG
|
||||
|
||||
/**
|
||||
* @brief http recv length
|
||||
*
|
||||
*/
|
||||
#define HTTP_RET_JSON_LENGTH 256
|
||||
|
||||
/**
|
||||
* @brief wait http recv time out
|
||||
*
|
||||
*/
|
||||
#define HTTP_WAIT_RET_TIMEOUT_MS 1000
|
||||
|
||||
/**
|
||||
* @brief do immediate log update if buffer is lower than this threshold (about two max log item)
|
||||
*
|
||||
*/
|
||||
#define LOG_LOW_BUFFER_THRESHOLD (LOG_UPLOAD_BUFFER_SIZE / 4)
|
||||
|
||||
/**
|
||||
* @brief sing key length
|
||||
*
|
||||
*/
|
||||
#define SIGN_KEY_SIZE (24)
|
||||
|
||||
#ifdef LOG_UPLOAD_DEBUG
|
||||
#define UPLOAD_DBG(fmt, ...) HAL_Printf(">>LOG-DBG>>%s(%d): " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define UPLOAD_DBG(...)
|
||||
#endif
|
||||
#define UPLOAD_ERR(fmt, ...) HAL_Printf(">>LOG-ERR>>%s(%d): " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief clear log upload buffer
|
||||
*
|
||||
*/
|
||||
void log_upload_clear_buffer(void);
|
||||
|
||||
/**
|
||||
* @brief set log upload in comm err state
|
||||
*
|
||||
* @param state true or false
|
||||
*/
|
||||
void log_upload_set_upload_log_in_comm_err(bool state);
|
||||
|
||||
/**
|
||||
* @brief change log upload level
|
||||
*
|
||||
* @param level
|
||||
*/
|
||||
void log_upload_set_log_upload_level(LogLevel level);
|
||||
|
||||
/**
|
||||
* @brief get log upload level
|
||||
*
|
||||
* @return LogLevel
|
||||
*/
|
||||
LogLevel log_upload_get_log_upload_level(void);
|
||||
|
||||
/**
|
||||
* @brief log upload buffer init
|
||||
*
|
||||
* @param init_params @see LogUploadInitParams
|
||||
* @return void* if success return @see LogUploadBuffer
|
||||
*/
|
||||
void *log_upload_buffer_init(LogUploadInitParams *init_params);
|
||||
/**
|
||||
* @brief deinit log buffer handle
|
||||
*
|
||||
* @param upload_buffer_handle
|
||||
*/
|
||||
void log_upload_buffer_deinit(void *upload_buffer_handle);
|
||||
/**
|
||||
* @brief Get the log buffer write index object
|
||||
*
|
||||
* @param log_upload_buffer_handle
|
||||
* @return uint32_t
|
||||
*/
|
||||
uint32_t get_log_buffer_write_index(void *log_upload_buffer_handle);
|
||||
/**
|
||||
* @brief Get the log buffer head len object
|
||||
*
|
||||
* @param log_upload_buffer_handle
|
||||
* @return uint32_t
|
||||
*/
|
||||
uint32_t get_log_buffer_head_len(void *log_upload_buffer_handle);
|
||||
/**
|
||||
* @brief check log buffer is empty or not
|
||||
*
|
||||
* @param log_upload_buffer_handle
|
||||
* @return true empty
|
||||
* @return false not empty
|
||||
*/
|
||||
bool have_data_need_update(void *log_upload_buffer_handle);
|
||||
/**
|
||||
* @brief When a new log appears during the reporting period, move the log to the specified position in the buffer
|
||||
*
|
||||
*/
|
||||
void remove_log_from_buffer(void *log_upload_buffer_handle, uint32_t need_remove_size);
|
||||
/**
|
||||
* @brief copy log buffer head
|
||||
*
|
||||
* @param[out] dst Destination address
|
||||
* @param log_upload_buffer_handle
|
||||
*/
|
||||
void copy_log_buffer_header(char *dst, void *log_upload_buffer_handle);
|
||||
|
||||
/**
|
||||
* @brief lock log buffer
|
||||
*
|
||||
*/
|
||||
void log_buffer_lock(void);
|
||||
|
||||
/**
|
||||
* @brief unlock log buffer
|
||||
*
|
||||
*/
|
||||
void log_buffer_unlock(void);
|
||||
|
||||
/**
|
||||
* @brief post data to tencent cloud over http
|
||||
*
|
||||
* @param[in] post_buf need post data buffer
|
||||
* @param[in] post_size need post data buffer length
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int post_one_http_to_server(void *handle, char *post_buf, size_t post_size);
|
||||
|
||||
/**
|
||||
* @brief Get the log buffer object
|
||||
*
|
||||
* @param log_upload_buffer_handle
|
||||
* @return char*
|
||||
*/
|
||||
char *get_log_buffer(void *log_upload_buffer_handle);
|
||||
|
||||
/**
|
||||
* @brief push data to log buffer
|
||||
*
|
||||
* @param[in] log_content need push data
|
||||
* @return int if 0 success else QCLOUD_ERR_FAILURE
|
||||
*/
|
||||
int append_data_to_log_buffer(const char *log_content);
|
||||
/**
|
||||
* @brief init log upload module
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int log_upload_init(void *client);
|
||||
/**
|
||||
* @brief Get the log buffer size object
|
||||
*
|
||||
* @return uint32_t log buffer size
|
||||
*/
|
||||
uint32_t get_log_buffer_size(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_LOG_UPLOAD_INC_LOG_UPLOAD_H_
|
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file broadcast_sample.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-07-18
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "qcloud_iot_common.h"
|
||||
|
||||
#include "utils_log.h"
|
||||
|
||||
/**
|
||||
* @brief MQTT event callback, @see MQTTEventHandleFun
|
||||
*
|
||||
* @param[in] client pointer to mqtt client
|
||||
* @param[in] handle_context context
|
||||
* @param[in] msg msg
|
||||
*/
|
||||
static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
|
||||
{
|
||||
MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
|
||||
uintptr_t packet_id = (uintptr_t)msg->msg;
|
||||
|
||||
switch (msg->event_type) {
|
||||
case MQTT_EVENT_UNDEF:
|
||||
Log_i("undefined event occur.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DISCONNECT:
|
||||
Log_i("MQTT disconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_RECONNECT:
|
||||
Log_i("MQTT reconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_RECEIVED:
|
||||
Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
|
||||
mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
|
||||
mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
|
||||
break;
|
||||
case MQTT_EVENT_SUBSCRIBE_SUCCESS:
|
||||
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
|
||||
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_NACK:
|
||||
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
|
||||
Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
|
||||
Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_NACK:
|
||||
Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_SUCCESS:
|
||||
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_TIMEOUT:
|
||||
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_NACK:
|
||||
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
default:
|
||||
Log_i("Should NOT arrive here.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Setup MQTT construct parameters.
|
||||
*
|
||||
* @param[in,out] initParams @see MQTTInitParams
|
||||
* @param[in] device_info @see DeviceInfo
|
||||
*/
|
||||
static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
|
||||
{
|
||||
init_params->device_info = device_info;
|
||||
init_params->command_timeout = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT;
|
||||
init_params->keep_alive_interval_ms = QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL;
|
||||
init_params->auto_connect_enable = 1;
|
||||
init_params->event_handle.h_fp = _mqtt_event_handler;
|
||||
init_params->event_handle.context = NULL;
|
||||
}
|
||||
|
||||
static void _setup_log_upload_init_params(LogUploadInitParams *init_params, DeviceInfo *device_info)
|
||||
{
|
||||
init_params->device_name = device_info->device_name;
|
||||
init_params->product_id = device_info->product_id;
|
||||
init_params->sign_key = device_info->device_secret;
|
||||
|
||||
init_params->log_buffer_size = LOG_UPLOAD_BUFFER_SIZE;
|
||||
init_params->save_log_filename = "./tmp/upload-fail-save.log";
|
||||
init_params->read_func = HAL_File_Read;
|
||||
init_params->save_func = HAL_File_Save;
|
||||
init_params->del_func = HAL_File_Del;
|
||||
init_params->get_size_func = HAL_File_Get_Size;
|
||||
}
|
||||
// ----------------------------------------------------------------------------
|
||||
// Main
|
||||
// ----------------------------------------------------------------------------
|
||||
static int sg_main_exit = 0;
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void _main_exit(int sig)
|
||||
{
|
||||
Log_e("demo exit by signal:%d\n", sig);
|
||||
sg_main_exit = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#ifdef __linux__
|
||||
signal(SIGINT, _main_exit);
|
||||
#endif
|
||||
|
||||
int rc;
|
||||
int loop_cnt = 5;
|
||||
// init log level
|
||||
LogHandleFunc func = {0};
|
||||
|
||||
func.log_malloc = HAL_Malloc;
|
||||
func.log_free = HAL_Free;
|
||||
func.log_get_current_time_str = HAL_Timer_Current;
|
||||
func.log_printf = HAL_Printf;
|
||||
func.log_upload = IOT_Log_Upload_AppendToUploadBuffer;
|
||||
|
||||
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
|
||||
|
||||
DeviceInfo device_info;
|
||||
|
||||
rc = HAL_GetDevInfo((void *)&device_info);
|
||||
if (rc) {
|
||||
Log_e("get device info failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// init connection
|
||||
MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
|
||||
_setup_connect_init_params(&init_params, &device_info);
|
||||
// init log upload
|
||||
LogUploadInitParams log_upload_init_params = DEFAULT_LOG_UPLOAD_INIT_PARAMS;
|
||||
|
||||
_setup_log_upload_init_params(&log_upload_init_params, &device_info);
|
||||
|
||||
IOT_Log_Upload_InitPre(&log_upload_init_params);
|
||||
// create MQTT client and connect with server
|
||||
void *client = IOT_MQTT_Construct(&init_params);
|
||||
if (client) {
|
||||
Log_i("Cloud Device Construct Success");
|
||||
rc = IOT_Log_Upload_Init(client);
|
||||
if (rc) {
|
||||
Log_e("Log upload init failed: %d", rc);
|
||||
}
|
||||
IOT_Log_Upload(true);
|
||||
} else {
|
||||
Log_e("MQTT Construct failed!");
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
do {
|
||||
Log_d("log upload test debug %d...", loop_cnt);
|
||||
Log_i("log upload test info %d...", loop_cnt);
|
||||
Log_w("log upload test waring %d...", loop_cnt);
|
||||
Log_e("log upload test error %d...", loop_cnt);
|
||||
rc = IOT_MQTT_Yield(client, 200);
|
||||
IOT_Log_Upload(rc ? true : false);
|
||||
} while (loop_cnt--);
|
||||
// If you have to report some urgent messages, you can call this function
|
||||
IOT_Log_Upload(true);
|
||||
rc = IOT_MQTT_Destroy(&client);
|
||||
IOT_Log_Upload_Deinit();
|
||||
utils_log_deinit();
|
||||
return rc;
|
||||
}
|
@@ -0,0 +1,741 @@
|
||||
/**
|
||||
* @file log_buffer.c
|
||||
* @author {hubert} ({hubertxxu@tencent.com})
|
||||
* @brief
|
||||
* @version 1.0
|
||||
* @date 2022-01-23
|
||||
*
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* Date Version Author Description
|
||||
* 2022-01-23 1.0 hubertxxu first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "log_upload.h"
|
||||
|
||||
typedef struct {
|
||||
void *lock_buf;
|
||||
char *log_buffer;
|
||||
const char *host;
|
||||
uint32_t log_buffer_size;
|
||||
uint32_t log_buffer_head_len;
|
||||
uint32_t write_index;
|
||||
char sign_key[SIGN_KEY_SIZE + 1];
|
||||
} LogUploadBuffer;
|
||||
|
||||
static LogUploadBuffer *sg_log_upload_buffer_handle = NULL;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Common
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief reset log upload buffer
|
||||
*
|
||||
* @param[in,out] log_upload_buffer_handle handle of log upload buffer
|
||||
*/
|
||||
static void _reset_log_buffer(LogUploadBuffer *log_upload_buffer_handle)
|
||||
{
|
||||
log_upload_buffer_handle->write_index = log_upload_buffer_handle->log_buffer_head_len;
|
||||
memset(log_upload_buffer_handle->log_buffer + log_upload_buffer_handle->log_buffer_head_len, 0,
|
||||
log_upload_buffer_handle->log_buffer_size - log_upload_buffer_handle->log_buffer_head_len);
|
||||
}
|
||||
|
||||
static LogUploadBuffer *_log_upload_buffer_init_pre(LogUploadInitParams *init_params)
|
||||
{
|
||||
LogUploadBuffer *handle = (LogUploadBuffer *)HAL_Malloc(sizeof(LogUploadBuffer));
|
||||
if (!handle) {
|
||||
UPLOAD_ERR("log upload buffer handle malloc error.");
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
memset(handle, 0, sizeof(LogUploadBuffer));
|
||||
|
||||
handle->lock_buf = HAL_MutexCreate();
|
||||
if (!handle->lock_buf) {
|
||||
UPLOAD_ERR("mutex create failed");
|
||||
goto err_exit;
|
||||
}
|
||||
handle->log_buffer_size = init_params->log_buffer_size;
|
||||
handle->log_buffer = HAL_Malloc(handle->log_buffer_size);
|
||||
if (!handle->log_buffer) {
|
||||
UPLOAD_ERR("malloc log buffer failed");
|
||||
goto err_exit;
|
||||
}
|
||||
memset(handle->log_buffer, 0, handle->log_buffer_size);
|
||||
|
||||
/*init sign key*/
|
||||
int key_len = strlen(init_params->sign_key);
|
||||
if (!key_len) {
|
||||
UPLOAD_ERR("invalid sign key length");
|
||||
goto err_exit;
|
||||
}
|
||||
memcpy(handle->sign_key, init_params->sign_key, key_len > SIGN_KEY_SIZE ? SIGN_KEY_SIZE : key_len);
|
||||
|
||||
sg_log_upload_buffer_handle = handle;
|
||||
return handle;
|
||||
err_exit:
|
||||
if (handle) {
|
||||
HAL_MutexDestroy(handle->lock_buf);
|
||||
HAL_Free(handle);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief deinit log buffer handle
|
||||
*
|
||||
* @param upload_buffer_handle
|
||||
*/
|
||||
void log_upload_buffer_deinit(void *upload_buffer_handle)
|
||||
{
|
||||
LogUploadBuffer *handle = (LogUploadBuffer *)upload_buffer_handle;
|
||||
if (!handle) {
|
||||
return;
|
||||
}
|
||||
HAL_MutexLock(handle->lock_buf);
|
||||
_reset_log_buffer(handle);
|
||||
HAL_Free(handle->log_buffer);
|
||||
handle->log_buffer = NULL;
|
||||
HAL_MutexUnlock(handle->lock_buf);
|
||||
HAL_MutexDestroy(handle->lock_buf);
|
||||
handle->lock_buf = NULL;
|
||||
HAL_Free(handle);
|
||||
sg_log_upload_buffer_handle = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the log buffer write index object
|
||||
*
|
||||
* @param log_upload_buffer_handle
|
||||
* @return uint32_t
|
||||
*/
|
||||
uint32_t get_log_buffer_write_index(void *log_upload_buffer_handle)
|
||||
{
|
||||
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
|
||||
HAL_MutexLock(handle->lock_buf);
|
||||
uint32_t write_index = handle->write_index;
|
||||
HAL_MutexUnlock(handle->lock_buf);
|
||||
return write_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the log buffer object
|
||||
*
|
||||
* @param log_upload_buffer_handle
|
||||
* @return char*
|
||||
*/
|
||||
char *get_log_buffer(void *log_upload_buffer_handle)
|
||||
{
|
||||
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
|
||||
return handle->log_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the log buffer head len object
|
||||
*
|
||||
* @param log_upload_buffer_handle
|
||||
* @return uint32_t
|
||||
*/
|
||||
uint32_t get_log_buffer_head_len(void *log_upload_buffer_handle)
|
||||
{
|
||||
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
|
||||
return handle->log_buffer_head_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief check log buffer is empty or not
|
||||
*
|
||||
* @param log_upload_buffer_handle
|
||||
* @return true empty
|
||||
* @return false not empty
|
||||
*/
|
||||
bool have_data_need_update(void *log_upload_buffer_handle)
|
||||
{
|
||||
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
|
||||
return handle->write_index == handle->log_buffer_head_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief When a new log appears during the reporting period, move the log to the specified position in the buffer
|
||||
*
|
||||
*/
|
||||
void remove_log_from_buffer(void *log_upload_buffer_handle, uint32_t need_remove_size)
|
||||
{
|
||||
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
|
||||
if (need_remove_size > handle->write_index || need_remove_size > handle->log_buffer_size) {
|
||||
return;
|
||||
}
|
||||
HAL_MutexLock(handle->lock_buf);
|
||||
memmove(handle->log_buffer + handle->log_buffer_head_len, handle->log_buffer + need_remove_size,
|
||||
handle->write_index - need_remove_size);
|
||||
handle->write_index = handle->write_index - need_remove_size + handle->log_buffer_head_len;
|
||||
memset(handle->log_buffer + handle->write_index, 0, handle->log_buffer_size - handle->write_index);
|
||||
HAL_MutexUnlock(handle->lock_buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief copy log buffer head
|
||||
*
|
||||
* @param[out] dst Destination address
|
||||
* @param log_upload_buffer_handle
|
||||
*/
|
||||
void copy_log_buffer_header(char *dst, void *log_upload_buffer_handle)
|
||||
{
|
||||
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
|
||||
memcpy(dst, handle->log_buffer, handle->log_buffer_head_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief clear log upload buffer
|
||||
*
|
||||
*/
|
||||
void log_upload_clear_buffer(void)
|
||||
{
|
||||
if (!sg_log_upload_buffer_handle) {
|
||||
UPLOAD_ERR("log upload buffer not init.");
|
||||
return;
|
||||
}
|
||||
HAL_MutexLock(sg_log_upload_buffer_handle->lock_buf);
|
||||
_reset_log_buffer(sg_log_upload_buffer_handle);
|
||||
HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the log buffer size object
|
||||
*
|
||||
* @return uint32_t log buffer size
|
||||
*/
|
||||
uint32_t get_log_buffer_size(void)
|
||||
{
|
||||
if (!sg_log_upload_buffer_handle) {
|
||||
UPLOAD_ERR("log upload buffer not init.");
|
||||
return 0;
|
||||
}
|
||||
return sg_log_upload_buffer_handle->log_buffer_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
*/
|
||||
void log_buffer_lock(void)
|
||||
{
|
||||
HAL_MutexLock(sg_log_upload_buffer_handle->lock_buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
*/
|
||||
void log_buffer_unlock(void)
|
||||
{
|
||||
HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// New log fromat by json
|
||||
// ----------------------------------------------------------------------------
|
||||
#ifdef LOG_UPLOAD_TYPE_JSON
|
||||
/**
|
||||
* @brief log upload buffer init
|
||||
*
|
||||
* @param init_params @see LogUploadInitParams
|
||||
* @return void* if success return @see LogUploadBuffer
|
||||
*/
|
||||
void *log_upload_buffer_init(LogUploadInitParams *init_params)
|
||||
{
|
||||
LogUploadBuffer *handle = _log_upload_buffer_init_pre(init_params);
|
||||
if (!handle) {
|
||||
return NULL;
|
||||
}
|
||||
handle->log_buffer_head_len = HAL_Snprintf(handle->log_buffer, handle->log_buffer_size,
|
||||
"{\"DeviceName\":\"%s\",\"ProductId\":\"%s\",\"Message\":[",
|
||||
init_params->device_name, init_params->product_id);
|
||||
handle->write_index = handle->log_buffer_head_len;
|
||||
handle->host = init_params->host;
|
||||
_reset_log_buffer(handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
#ifdef LOG_CHECK_HTTP_RET_CODE
|
||||
/**
|
||||
* @brief get http respose result
|
||||
*
|
||||
* @param[in] json http response buffer
|
||||
* @param[in] json_len http response buffer length
|
||||
* @return true log report success
|
||||
* @return false log report error
|
||||
*/
|
||||
static bool _get_json_ret_code(char *json, size_t json_len)
|
||||
{
|
||||
int rc = 0;
|
||||
UtilsJsonValue resposse;
|
||||
// get response
|
||||
rc = utils_json_value_get("Response.Error", strlen("Response.Error"), json, json_len, &resposse);
|
||||
if (rc) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief when upload. append endchar in the upload buffer ending
|
||||
*
|
||||
* @param[in,out] log_buf upload buffer
|
||||
* @param[in] log_size upload buffer index
|
||||
*/
|
||||
static void _append_endchar_to_upload_buffer(char *log_buf, size_t log_size)
|
||||
{
|
||||
log_buf[log_size - 2] = ']';
|
||||
log_buf[log_size - 1] = '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief when upload. remove endchar in the upload buffer ending
|
||||
*
|
||||
* @param[in,out] log_buf upload buffer
|
||||
* @param[in] log_size upload buffer index
|
||||
*/
|
||||
static void _remove_endchar_from_upload_buffer(char *log_buf, size_t log_size)
|
||||
{
|
||||
log_buf[log_size - 2] = ',';
|
||||
log_buf[log_size - 1] = ' ';
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief post data to tencent cloud over http
|
||||
*
|
||||
* @param[in] post_buf need post data buffer
|
||||
* @param[in] post_size need post data buffer length
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int post_one_http_to_server(void *log_upload_buffer_handle, char *post_buf, size_t post_size)
|
||||
{
|
||||
int rc = 0;
|
||||
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
|
||||
if (!post_size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
_append_endchar_to_upload_buffer(post_buf, post_size);
|
||||
|
||||
HttpSignedParams params = {
|
||||
.host = handle->host ? handle->host : LOG_UPLOAD_SERVER_URL,
|
||||
.uri = LOG_UPLOAD_URI_PATH,
|
||||
.secretkey = handle->sign_key,
|
||||
#ifdef LOG_CHECK_HTTP_RET_CODE
|
||||
.need_recv = true,
|
||||
.recv_timeout_ms = HTTP_WAIT_RET_TIMEOUT_MS,
|
||||
#else
|
||||
.need_recv = false,
|
||||
.recv_timeout_ms = 0,
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef LOG_CHECK_HTTP_RET_CODE
|
||||
char buf[HTTP_RET_JSON_LENGTH] = {0};
|
||||
rc = IOT_HTTP_Signed_Request(¶ms, post_buf, post_size, (uint8_t *)buf, HTTP_RET_JSON_LENGTH);
|
||||
if (rc < 0) {
|
||||
UPLOAD_ERR("IOT_HTTP_Signed_Request failed, rc = %d", rc);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
UPLOAD_ERR("HTTP Recv length = %d", rc);
|
||||
rc = QCLOUD_ERR_HTTP;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
buf[rc - 1] = '\0'; // json_parse relies on a string
|
||||
if (strlen(buf) > 0 && _get_json_ret_code(buf, rc)) {
|
||||
UPLOAD_DBG("Log server return SUCCESS: %s", buf);
|
||||
rc = QCLOUD_RET_SUCCESS;
|
||||
} else {
|
||||
UPLOAD_ERR("Log server return FAIL: %s", buf);
|
||||
rc = QCLOUD_ERR_HTTP;
|
||||
}
|
||||
|
||||
#else
|
||||
rc = IOT_HTTP_Signed_Request(¶ms, post_buf, post_size, NULL, 0);
|
||||
#endif
|
||||
|
||||
exit:
|
||||
_remove_endchar_from_upload_buffer(post_buf, post_size);
|
||||
UPLOAD_DBG("%ld data have be post to server. rc : %d", post_size, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief push data to log buffer
|
||||
*
|
||||
* @param[in] log_content need push data
|
||||
* @return int if 0 success else QCLOUD_ERR_FAILURE
|
||||
*/
|
||||
int append_data_to_log_buffer(const char *log_content)
|
||||
{
|
||||
uint16_t log_size = strlen(log_content);
|
||||
if (HAL_MutexTryLock(sg_log_upload_buffer_handle->lock_buf) != 0) {
|
||||
UPLOAD_ERR("trylock buffer failed!");
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
if ((sg_log_upload_buffer_handle->write_index + log_size + 5) > sg_log_upload_buffer_handle->log_buffer_size) {
|
||||
HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
|
||||
UPLOAD_ERR("log upload buffer is not enough!");
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\"';
|
||||
int index = 0;
|
||||
while (index < (log_size - 2)) {
|
||||
// replace may overflow buffer size
|
||||
if ((sg_log_upload_buffer_handle->write_index + 5) > sg_log_upload_buffer_handle->log_buffer_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (log_content[index]) {
|
||||
case '\"':
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\"';
|
||||
break;
|
||||
case '\\':
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
|
||||
break;
|
||||
case '/':
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '/';
|
||||
break;
|
||||
case '\b':
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 'b';
|
||||
break;
|
||||
case '\f':
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 'f';
|
||||
break;
|
||||
case '\n':
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 'n';
|
||||
break;
|
||||
case '\r':
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 'r';
|
||||
break;
|
||||
case '\t':
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 't';
|
||||
break;
|
||||
default:
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] =
|
||||
log_content[index];
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
/* replace \r\n to \", add as delimiter */
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\"';
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = ',';
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = ' ';
|
||||
|
||||
HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
#else
|
||||
// ----------------------------------------------------------------------------
|
||||
// Old log format by text
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/* log post header format */
|
||||
#define TIMESTAMP_SIZE (10)
|
||||
#define SIGNATURE_SIZE (40)
|
||||
#define CTRL_BYTES_SIZE (4)
|
||||
// LOG_BUF_FIXED_HEADER_SIZE = 112
|
||||
#define LOG_BUF_FIXED_HEADER_SIZE \
|
||||
(SIGNATURE_SIZE + CTRL_BYTES_SIZE + MAX_SIZE_OF_PRODUCT_ID + MAX_SIZE_OF_DEVICE_NAME + TIMESTAMP_SIZE)
|
||||
|
||||
#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
|
||||
#include "mbedtls/aes.h"
|
||||
|
||||
/**
|
||||
* @brief header of encrypt
|
||||
*
|
||||
*/
|
||||
static char sg_head_content[128] = {0};
|
||||
|
||||
/**
|
||||
* @brief log data encrypt by aes cbc
|
||||
*
|
||||
* @param handle
|
||||
* @param buf need encrypt data
|
||||
* @param datalen need encrypt data length
|
||||
* @return int
|
||||
*/
|
||||
static int _log_aes_cbc_encrypt(LogUploadBuffer *handle, uint8_t *buf, uint32_t datalen)
|
||||
{
|
||||
int ret = QCLOUD_RET_SUCCESS;
|
||||
|
||||
mbedtls_aes_context ctx;
|
||||
|
||||
unsigned char iv[16];
|
||||
memset(iv, '0', 16);
|
||||
|
||||
mbedtls_aes_init(&ctx);
|
||||
|
||||
ret = mbedtls_aes_setkey_enc(&ctx, (uint8_t *)handle->sign_key, 128);
|
||||
if (ret) {
|
||||
UPLOAD_ERR("Set encry key err,ret:%d", ret);
|
||||
ret = QCLOUD_ERR_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_ENCRYPT, datalen, iv, buf, buf);
|
||||
if (ret) {
|
||||
Log_e("encrypt err,ret:%d", ret);
|
||||
ret = QCLOUD_ERR_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
mbedtls_aes_free(&ctx);
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
|
||||
exit:
|
||||
mbedtls_aes_free(&ctx);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief log upload buffer init
|
||||
*
|
||||
* @param init_params @see LogUploadInitParams
|
||||
* @return void* if success return @see LogUploadBuffer
|
||||
*/
|
||||
void *log_upload_buffer_init(LogUploadInitParams *init_params)
|
||||
{
|
||||
LogUploadBuffer *handle = _log_upload_buffer_init_pre(init_params);
|
||||
if (!handle) {
|
||||
return NULL;
|
||||
}
|
||||
handle->log_buffer_head_len = LOG_BUF_FIXED_HEADER_SIZE;
|
||||
memset(handle->log_buffer, '#', handle->log_buffer_head_len);
|
||||
handle->log_buffer[SIGNATURE_SIZE] = 'P';
|
||||
memcpy(handle->log_buffer + SIGNATURE_SIZE + CTRL_BYTES_SIZE, init_params->product_id, MAX_SIZE_OF_PRODUCT_ID);
|
||||
memcpy(handle->log_buffer + SIGNATURE_SIZE + CTRL_BYTES_SIZE + MAX_SIZE_OF_PRODUCT_ID, init_params->device_name,
|
||||
strlen(init_params->device_name));
|
||||
handle->write_index = handle->log_buffer_head_len;
|
||||
handle->host = init_params->host;
|
||||
_reset_log_buffer(handle);
|
||||
|
||||
#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
|
||||
HAL_Snprintf(sg_head_content, 128,
|
||||
"Accept:application/json;*/*\r\n"
|
||||
"X-TC-IotEncryType:%c\r\n"
|
||||
"X-TC-IotCid:%s%s\r\n",
|
||||
handle->log_buffer[SIGNATURE_SIZE], init_params->product_id, init_params->device_name);
|
||||
#endif
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
#ifdef LOG_CHECK_HTTP_RET_CODE
|
||||
/**
|
||||
* @brief get http respose result
|
||||
*
|
||||
* @param[in] json http response buffer
|
||||
* @param[in] json_len http response buffer length
|
||||
* @return true log report success
|
||||
* @return false log report error
|
||||
*/
|
||||
static bool _get_json_ret_code(char *json, size_t json_len)
|
||||
{
|
||||
int rc = 0;
|
||||
uint32_t ret_code;
|
||||
rc = utils_json_get_uint32("Retcode", strlen("Retcode"), json, json_len, &ret_code);
|
||||
if (!rc) {
|
||||
if (!ret_code) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief update timestamp add signature post buffer
|
||||
*
|
||||
*/
|
||||
static void _update_time_and_signature(LogUploadBuffer *handle, char *post_buf, size_t post_size)
|
||||
{
|
||||
char timestamp[TIMESTAMP_SIZE + 1] = {0};
|
||||
char signature[SIGNATURE_SIZE + 1] = {0};
|
||||
uint32_t now_time = HAL_Timer_CurrentSec();
|
||||
|
||||
/* record the timestamp for this log uploading */
|
||||
HAL_Snprintf(timestamp, TIMESTAMP_SIZE + 1, "%010d", now_time);
|
||||
memcpy(post_buf + sg_log_upload_buffer_handle->log_buffer_head_len - TIMESTAMP_SIZE, timestamp, strlen(timestamp));
|
||||
|
||||
/* signature of this log uploading */
|
||||
utils_hmac_sha1(post_buf + SIGNATURE_SIZE, post_size - SIGNATURE_SIZE, (uint8_t *)handle->sign_key,
|
||||
strlen(handle->sign_key), signature);
|
||||
memcpy(post_buf, signature, SIGNATURE_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief post data to tencent cloud over http
|
||||
*
|
||||
* @param[in] post_buf need post data buffer
|
||||
* @param[in] post_size need post data buffer length
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int post_one_http_to_server(void *log_upload_buffer_handle, char *post_buf, size_t post_size)
|
||||
{
|
||||
int rc = 0;
|
||||
#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
|
||||
char *log_encrypt_buffer = NULL;
|
||||
#endif
|
||||
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
|
||||
_update_time_and_signature(handle, post_buf, post_size);
|
||||
void *http_client = IOT_HTTP_Init();
|
||||
if (!http_client) {
|
||||
UPLOAD_ERR("http client init error");
|
||||
goto exit;
|
||||
}
|
||||
IotHTTPConnectParams connect_params = {
|
||||
.url = handle->host ? handle->host : LOG_UPLOAD_OLD_SERVER_URL,
|
||||
.port = LOG_UPLOAD_OLD_SERVER_PORT,
|
||||
.ca_crt = NULL, // TODO: support cert
|
||||
};
|
||||
|
||||
rc = IOT_HTTP_Connect(http_client, &connect_params);
|
||||
if (rc) {
|
||||
UPLOAD_ERR("http connect error.");
|
||||
goto exit;
|
||||
}
|
||||
IotHTTPRequestParams request = {
|
||||
.url = handle->host ? handle->host : LOG_UPLOAD_OLD_SERVER_URL,
|
||||
.method = IOT_HTTP_METHOD_POST,
|
||||
.content_length = post_size,
|
||||
.content_type = "text/plain;charset=utf-8",
|
||||
.content = post_buf,
|
||||
.header = "Accept:application/json;*/*\r\n",
|
||||
};
|
||||
|
||||
#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
|
||||
/*ANSI X.923: zero-padding with last byte as padlen */
|
||||
int padlen = 16 - post_size % 16;
|
||||
int datalen = post_size + padlen;
|
||||
log_encrypt_buffer = HAL_Malloc(datalen + 1);
|
||||
if (!log_encrypt_buffer) {
|
||||
UPLOAD_ERR("malloc log encrypt buffer error. malloc length : %d", datalen);
|
||||
rc = QCLOUD_ERR_MALLOC;
|
||||
goto exit;
|
||||
}
|
||||
memset(log_encrypt_buffer, 0, datalen + 1);
|
||||
memcpy(log_encrypt_buffer, post_buf, post_size);
|
||||
log_encrypt_buffer[datalen - 1] = padlen;
|
||||
|
||||
// aes cbc
|
||||
rc = _log_aes_cbc_encrypt(handle, (uint8_t *)log_encrypt_buffer, datalen);
|
||||
if (QCLOUD_RET_SUCCESS != rc) {
|
||||
UPLOAD_ERR("log encrypt failed, ret = %d", rc);
|
||||
goto exit;
|
||||
}
|
||||
request.header = sg_head_content;
|
||||
request.content_type = "application/octet-stream";
|
||||
request.content_length = datalen;
|
||||
request.content = log_encrypt_buffer;
|
||||
#endif
|
||||
|
||||
rc = IOT_HTTP_Request(http_client, &request);
|
||||
// 2. check rc
|
||||
if (rc) {
|
||||
UPLOAD_ERR("http post error.");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
#ifdef LOG_CHECK_HTTP_RET_CODE
|
||||
char buf[HTTP_RET_JSON_LENGTH] = {0};
|
||||
rc = IOT_HTTP_Recv(http_client, (uint8_t *)buf, HTTP_RET_JSON_LENGTH, HTTP_WAIT_RET_TIMEOUT_MS);
|
||||
if (rc < 0) {
|
||||
UPLOAD_ERR("qcloud_http_recv_data failed, rc = %d", rc);
|
||||
} else if (rc == 0) {
|
||||
UPLOAD_ERR("HTTP Recv length == %d", rc);
|
||||
rc = QCLOUD_ERR_HTTP_TIMEOUT;
|
||||
} else {
|
||||
buf[rc - 1] = '\0'; // json_parse relies on a string
|
||||
if (strlen(buf) > 0 && _get_json_ret_code(buf, rc)) {
|
||||
UPLOAD_DBG("Log server return SUCCESS: %s", buf);
|
||||
rc = QCLOUD_RET_SUCCESS;
|
||||
} else {
|
||||
UPLOAD_ERR("Log server return FAIL: %s", buf);
|
||||
rc = QCLOUD_ERR_HTTP;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
exit:
|
||||
if (http_client) {
|
||||
IOT_HTTP_Disconnect(http_client);
|
||||
IOT_HTTP_Deinit(http_client);
|
||||
}
|
||||
#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
|
||||
if (log_encrypt_buffer) {
|
||||
HAL_Free(log_encrypt_buffer);
|
||||
log_encrypt_buffer = NULL;
|
||||
}
|
||||
#endif
|
||||
UPLOAD_DBG("%ld data have be post to server. rc : %d", post_size, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief push data to log buffer
|
||||
*
|
||||
* @param[in] log_content need push data
|
||||
* @return int if 0 success else QCLOUD_ERR_FAILURE
|
||||
*/
|
||||
int append_data_to_log_buffer(const char *log_content)
|
||||
{
|
||||
uint16_t log_size = strlen(log_content);
|
||||
if (HAL_MutexTryLock(sg_log_upload_buffer_handle->lock_buf) != 0) {
|
||||
UPLOAD_ERR("trylock buffer failed!");
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
if ((sg_log_upload_buffer_handle->write_index + log_size + 1) > sg_log_upload_buffer_handle->log_buffer_size) {
|
||||
HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
|
||||
UPLOAD_ERR("log upload buffer is not enough! remain : %d, need upload length: %d",
|
||||
sg_log_upload_buffer_handle->log_buffer_size - sg_log_upload_buffer_handle->write_index, log_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(sg_log_upload_buffer_handle->log_buffer + sg_log_upload_buffer_handle->write_index, log_content, log_size);
|
||||
sg_log_upload_buffer_handle->write_index += log_size;
|
||||
|
||||
/* replace \r\n to \n\f as delimiter */
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index - 1] = '\f';
|
||||
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index - 2] = '\n';
|
||||
|
||||
HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file log_mqtt.c
|
||||
* @brief
|
||||
* @author hubertxxu (hubertxxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2022-01-05
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2022-01-05 <td>1.0 <td>hubertxxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "log_upload.h"
|
||||
|
||||
/**
|
||||
* @brief LogLevelResultInfo handle
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
bool result_recv_ok;
|
||||
uint32_t log_level;
|
||||
} LogLevelResultInfo;
|
||||
|
||||
/**
|
||||
* @brief MQTTLogUploadHandle handle
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
void *mqtt_client;
|
||||
Timer time_update_timer;
|
||||
} MQTTLogUploadHandle;
|
||||
|
||||
static MQTTLogUploadHandle sg_mqtt_log_upload_handle;
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] message message from topic
|
||||
* @param[in,out] usr_data pointer to @see LogLevelResultInfo
|
||||
*/
|
||||
static void _log_mqtt_message_callback(void *client, const MQTTMessage *message, void *usr_data)
|
||||
{
|
||||
int rc;
|
||||
LogLevelResultInfo *result = (LogLevelResultInfo *)usr_data;
|
||||
Log_d("Receive log result message:%.*s", message->payload_len, message->payload_str);
|
||||
// get log level
|
||||
rc = utils_json_get_uint32("log_level", strlen("log_level"), message->payload_str, message->payload_len,
|
||||
&result->log_level);
|
||||
if (rc) {
|
||||
return;
|
||||
}
|
||||
switch (result->log_level) {
|
||||
case LOG_LEVEL_DISABLE:
|
||||
log_upload_clear_buffer();
|
||||
log_upload_set_upload_log_in_comm_err(true);
|
||||
#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
|
||||
// when use aes encrypt. no need report log
|
||||
log_upload_set_log_upload_level(LOG_LEVEL_DISABLE);
|
||||
UPLOAD_DBG("disable log upload.");
|
||||
#else
|
||||
log_upload_set_log_upload_level(LOG_LEVEL_ERROR);
|
||||
UPLOAD_DBG("Upload log level change to: %d", LOG_LEVEL_ERROR);
|
||||
#endif
|
||||
|
||||
break;
|
||||
case LOG_LEVEL_ERROR:
|
||||
case LOG_LEVEL_WARN:
|
||||
case LOG_LEVEL_INFO:
|
||||
case LOG_LEVEL_DEBUG:
|
||||
if (result->log_level < log_upload_get_log_upload_level()) {
|
||||
log_upload_clear_buffer();
|
||||
}
|
||||
log_upload_set_upload_log_in_comm_err(false);
|
||||
log_upload_set_log_upload_level((LogLevel)result->log_level);
|
||||
UPLOAD_DBG("Upload log level change to: %d", result->log_level);
|
||||
break;
|
||||
|
||||
default:
|
||||
UPLOAD_ERR("Invalid log level: %d", result->log_level);
|
||||
break;
|
||||
}
|
||||
|
||||
result->result_recv_ok = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check and subscribe log result topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _log_upload_topic_check_and_sub(void *client, const char *topic)
|
||||
{
|
||||
int rc = 0;
|
||||
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
|
||||
LogLevelResultInfo *log_level_info = (LogLevelResultInfo *)HAL_Malloc(sizeof(LogLevelResultInfo));
|
||||
if (!log_level_info) {
|
||||
return QCLOUD_ERR_MALLOC;
|
||||
}
|
||||
log_level_info->result_recv_ok = false;
|
||||
sub_params.on_message_handler = _log_mqtt_message_callback;
|
||||
sub_params.qos = QOS1;
|
||||
sub_params.user_data = log_level_info;
|
||||
sub_params.user_data_free = HAL_Free;
|
||||
|
||||
rc = IOT_MQTT_SubscribeSync(client, topic, &sub_params);
|
||||
if (rc) {
|
||||
HAL_Free(log_level_info);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish get log level message to log topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return >= 0 for success
|
||||
*/
|
||||
static int _mqtt_get_log_level_publish(void *client)
|
||||
{
|
||||
static int sg_get_log_level_token_count = 0;
|
||||
char topic_name[MAX_SIZE_OF_CLOUD_TOPIC];
|
||||
char payload[128] = {0};
|
||||
|
||||
HAL_Snprintf(topic_name, sizeof(topic_name), "$log/operation/%s/%s",
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
|
||||
|
||||
HAL_Snprintf(payload, sizeof(payload), "{\"type\": \"get_log_level\",\"clientToken\": \"get_log_level-%u\"}",
|
||||
sg_get_log_level_token_count++);
|
||||
|
||||
PublishParams pub_params = DEFAULT_PUB_PARAMS;
|
||||
pub_params.qos = QOS0;
|
||||
pub_params.payload = payload;
|
||||
pub_params.payload_len = strlen(payload);
|
||||
return IOT_MQTT_Publish(client, topic_name, &pub_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief init log upload module
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int log_upload_init(void *client)
|
||||
{
|
||||
int rc = 0;
|
||||
char log_upload_topic[MAX_SIZE_OF_CLOUD_TOPIC];
|
||||
/* subscribe the log topic: "$log/operation/result/${productId}/${deviceName}" */
|
||||
HAL_Snprintf(log_upload_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$log/operation/result/%s/%s",
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
|
||||
|
||||
rc = _log_upload_topic_check_and_sub(client, log_upload_topic);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = _mqtt_get_log_level_publish(client);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
sg_mqtt_log_upload_handle.mqtt_client = client;
|
||||
return rc;
|
||||
}
|
@@ -0,0 +1,539 @@
|
||||
/**
|
||||
* @file log_upload.c
|
||||
* @author {hubert} ({hubertxxu@tencent.com})
|
||||
* @brief
|
||||
* @version 1.0
|
||||
* @date 2022-01-24
|
||||
*
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* Date Version Author Description
|
||||
* 2022-01-24 1.0 hubertxxu first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "log_upload.h"
|
||||
|
||||
#ifdef LOG_UPLOAD_TYPE_JSON
|
||||
#define LOG_DELIMITER "\", "
|
||||
#else
|
||||
#define LOG_DELIMITER "\n\f"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief data structure for log upload
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
bool upload_only_in_comm_err;
|
||||
LogLevel log_level;
|
||||
Timer upload_timer;
|
||||
void *upload_buffer_handle;
|
||||
|
||||
const char *save_log_filename;
|
||||
LogSaveFunc save_func;
|
||||
LogReadFunc read_func;
|
||||
LogDelFunc del_func;
|
||||
LogGetSizeFunc get_size_func;
|
||||
|
||||
bool log_save_enabled;
|
||||
bool log_client_init_done;
|
||||
} QcloudIoTLogUpload;
|
||||
|
||||
static QcloudIoTLogUpload *sg_log_uploader_client;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Internal API
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @return QcloudIoTLogUpload* @see QcloudIoTLogUpload
|
||||
*/
|
||||
static QcloudIoTLogUpload *_get_log_uploader_client(void)
|
||||
{
|
||||
if (!sg_log_uploader_client) {
|
||||
return NULL;
|
||||
}
|
||||
if (!sg_log_uploader_client->log_client_init_done) {
|
||||
return NULL;
|
||||
}
|
||||
return sg_log_uploader_client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param client @see QcloudIoTLogUpload
|
||||
*/
|
||||
static void _set_log_uploader_client(void *client)
|
||||
{
|
||||
sg_log_uploader_client = client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set log upload in comm err state
|
||||
*
|
||||
* @param state true or false
|
||||
*/
|
||||
void log_upload_set_upload_log_in_comm_err(bool state)
|
||||
{
|
||||
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
|
||||
POINTER_SANITY_CHECK_RTN(log_uploader_handle);
|
||||
log_uploader_handle->upload_only_in_comm_err = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief change log upload level
|
||||
*
|
||||
* @param level
|
||||
*/
|
||||
void log_upload_set_log_upload_level(LogLevel level)
|
||||
{
|
||||
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
|
||||
POINTER_SANITY_CHECK_RTN(log_uploader_handle);
|
||||
log_uploader_handle->log_level = level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get log upload level
|
||||
*
|
||||
* @return LogLevel
|
||||
*/
|
||||
LogLevel log_upload_get_log_upload_level(void)
|
||||
{
|
||||
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
|
||||
POINTER_SANITY_CHECK(log_uploader_handle, LOG_LEVEL_DISABLE);
|
||||
return log_uploader_handle->log_level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief check if need force upload
|
||||
*
|
||||
* @param[in] force_upload
|
||||
* @return true need force upload
|
||||
* @return false not needed force upload
|
||||
*/
|
||||
static bool _check_force_upload(bool force_upload)
|
||||
{
|
||||
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
|
||||
if (!log_uploader_handle) {
|
||||
return false;
|
||||
}
|
||||
/* 1: check if have data to upload */
|
||||
if (have_data_need_update(log_uploader_handle->upload_buffer_handle)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t write_index = get_log_buffer_write_index(log_uploader_handle->upload_buffer_handle);
|
||||
bool is_low_buffer = (get_log_buffer_size() - write_index) < LOG_LOW_BUFFER_THRESHOLD ? true : false;
|
||||
|
||||
/* 2. check if upload only in comm err */
|
||||
if (!force_upload && log_uploader_handle->upload_only_in_comm_err) {
|
||||
/* buffer is low but we couldn't upload now, reset buffer */
|
||||
if (is_low_buffer) {
|
||||
log_upload_clear_buffer();
|
||||
}
|
||||
HAL_Timer_CountdownMs(&log_uploader_handle->upload_timer, LOG_UPLOAD_INTERVAL_MS);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 3: force upload is true, handle it right now */
|
||||
if (force_upload) {
|
||||
return true;
|
||||
}
|
||||
/* 4 : check if buffer is low */
|
||||
if (is_low_buffer) {
|
||||
/* buffer is low, handle it right now */
|
||||
return true;
|
||||
}
|
||||
/*5: check if timeout, handle it right now*/
|
||||
return HAL_Timer_Expired(&log_uploader_handle->upload_timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief save log to file or flash
|
||||
*
|
||||
* @param[in] log_buf need saved buffer
|
||||
* @param[in] log_size need saved buffer length
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _save_log(char *log_buf, size_t log_size)
|
||||
{
|
||||
int rc = 0;
|
||||
size_t write_size, current_size;
|
||||
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
|
||||
POINTER_SANITY_CHECK(log_uploader_handle, QCLOUD_ERR_INVAL);
|
||||
if (!log_size) {
|
||||
UPLOAD_ERR("nothing need to save. log_size : %ld", log_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
current_size = log_uploader_handle->get_size_func(log_uploader_handle->save_log_filename);
|
||||
|
||||
/* overwrite the previous saved log to avoid too many saved logs */
|
||||
if ((current_size + log_size) > MAX_LOG_SAVE_SIZE) {
|
||||
UPLOAD_ERR("overwrite the previous saved log. %ld", current_size);
|
||||
rc = log_uploader_handle->del_func(log_uploader_handle->save_log_filename);
|
||||
if (rc) {
|
||||
Log_e("fail to delete previous log");
|
||||
}
|
||||
}
|
||||
UPLOAD_DBG("saved data [%ld]", log_size);
|
||||
write_size = log_uploader_handle->save_func(log_uploader_handle->save_log_filename, log_buf, log_size);
|
||||
if (write_size != log_size) {
|
||||
Log_e("fail to save log. RC %ld - log size %ld", write_size, log_size);
|
||||
}
|
||||
|
||||
return write_size != log_size ? -1 : 0;
|
||||
}
|
||||
|
||||
static uint32_t _buffer_get_at_most_message(char *post_buf)
|
||||
{
|
||||
uint32_t possible_size = 0;
|
||||
char *next_log_buf = NULL;
|
||||
size_t delimiter_len = strlen(LOG_DELIMITER);
|
||||
while (possible_size < MAX_HTTP_LOG_POST_SIZE) {
|
||||
/* locate the delimiter */
|
||||
next_log_buf = strstr(post_buf + possible_size, LOG_DELIMITER);
|
||||
if (!next_log_buf) {
|
||||
return 0;
|
||||
}
|
||||
possible_size = (size_t)(next_log_buf - post_buf + delimiter_len);
|
||||
/* end of log */
|
||||
if (next_log_buf[delimiter_len] == 0 && possible_size < MAX_HTTP_LOG_POST_SIZE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return possible_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief report these to the server
|
||||
*
|
||||
* @param post_buf
|
||||
* @param post_size
|
||||
* @param is_log_buffer
|
||||
* @return int
|
||||
*/
|
||||
static int _post_log_buffer_to_cloud(QcloudIoTLogUpload *log_uploader_handle, char *post_buf, size_t post_size,
|
||||
bool is_log_buffer)
|
||||
{
|
||||
int rc = 0;
|
||||
uint32_t log_buffer_head_len = get_log_buffer_head_len(log_uploader_handle->upload_buffer_handle);
|
||||
|
||||
size_t orig_post_size = post_size;
|
||||
size_t post_payload, possible_size, actual_post_payload = 0;
|
||||
do {
|
||||
// 1. get message in buffer
|
||||
possible_size = _buffer_get_at_most_message(post_buf);
|
||||
if (!possible_size) {
|
||||
UPLOAD_ERR("Upload size should not be 0! Total sent: %ld. Left: %ld",
|
||||
actual_post_payload + log_buffer_head_len, post_size);
|
||||
if (is_log_buffer) {
|
||||
log_upload_clear_buffer();
|
||||
}
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
if (is_log_buffer && (possible_size > get_log_buffer_size() || possible_size > orig_post_size ||
|
||||
possible_size <= log_buffer_head_len)) {
|
||||
log_upload_clear_buffer();
|
||||
UPLOAD_ERR("possible size (%ld) is too large.", possible_size);
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
// 2. post to server
|
||||
rc = post_one_http_to_server(log_uploader_handle->upload_buffer_handle, post_buf, possible_size);
|
||||
if (rc) {
|
||||
UPLOAD_ERR("Send log failed. Total sent: %ld. Left: %ld", actual_post_payload + log_buffer_head_len,
|
||||
post_size);
|
||||
// when send failed save log in file.
|
||||
if (log_uploader_handle->log_save_enabled && is_log_buffer) {
|
||||
_save_log(post_buf + log_buffer_head_len, possible_size - log_buffer_head_len);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. remove have be post message
|
||||
memmove(post_buf + log_buffer_head_len, post_buf + possible_size, post_size - possible_size);
|
||||
post_payload = possible_size - log_buffer_head_len;
|
||||
post_size -= post_payload;
|
||||
actual_post_payload += post_payload;
|
||||
memset(post_buf + post_size, 0, orig_post_size - post_size);
|
||||
UPLOAD_DBG("post log %d OK. Total sent: %d. Left: %d ", possible_size,
|
||||
actual_post_payload + log_buffer_head_len, post_size);
|
||||
} while (post_size > log_buffer_head_len);
|
||||
|
||||
if (is_log_buffer) {
|
||||
remove_log_from_buffer(log_uploader_handle->upload_buffer_handle, orig_post_size);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief handle log to save flash or file
|
||||
*
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _handle_saved_log(void)
|
||||
{
|
||||
int rc = QCLOUD_RET_SUCCESS;
|
||||
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
|
||||
if (!log_uploader_handle) {
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
size_t whole_log_size = log_uploader_handle->get_size_func(log_uploader_handle->save_log_filename);
|
||||
if (!whole_log_size) {
|
||||
return rc;
|
||||
}
|
||||
uint32_t log_buffer_head_len = get_log_buffer_head_len(log_uploader_handle->upload_buffer_handle);
|
||||
size_t buf_size = whole_log_size + log_buffer_head_len + 1;
|
||||
char *log_buf = HAL_Malloc(buf_size);
|
||||
if (!log_buf) {
|
||||
UPLOAD_ERR("Malloc failed, size: %ld", buf_size);
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
/* read the whole log to buffer */
|
||||
size_t read_len = log_uploader_handle->read_func(log_uploader_handle->save_log_filename,
|
||||
log_buf + log_buffer_head_len, whole_log_size);
|
||||
if (read_len != whole_log_size) {
|
||||
Log_e("fail to read whole saved log. Size: %ld - read: %ld", whole_log_size, read_len);
|
||||
rc = QCLOUD_ERR_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* copy header from global log buffer */
|
||||
size_t upload_size = whole_log_size + log_buffer_head_len;
|
||||
copy_log_buffer_header(log_buf, log_uploader_handle->upload_buffer_handle);
|
||||
log_buf[buf_size - 1] = 0;
|
||||
|
||||
/* post server */
|
||||
rc = _post_log_buffer_to_cloud(log_uploader_handle, log_buf, upload_size, false);
|
||||
if (rc == QCLOUD_RET_SUCCESS || rc == QCLOUD_ERR_INVAL || rc == QCLOUD_ERR_HTTP) {
|
||||
UPLOAD_DBG("handle saved log done! Size: %ld. ", whole_log_size);
|
||||
log_uploader_handle->del_func(log_uploader_handle->save_log_filename);
|
||||
}
|
||||
exit:
|
||||
HAL_Free(log_buf);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief append need report log to log upload buffer
|
||||
*
|
||||
* @param[in] log_content data of need to report
|
||||
*/
|
||||
static void _log_upload_append_to_log_buffer(int log_level, const char *log_content)
|
||||
{
|
||||
int rc = 0;
|
||||
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
|
||||
// POINTER_SANITY_CHECK_RTN(log_uploader_handle);
|
||||
if (!log_uploader_handle) {
|
||||
return;
|
||||
}
|
||||
// if not necessary. just give up upload.
|
||||
if (log_level > log_uploader_handle->log_level) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t log_size = strlen(log_content);
|
||||
if (!log_size) {
|
||||
UPLOAD_ERR("log_size is not 0!");
|
||||
return;
|
||||
}
|
||||
rc = append_data_to_log_buffer(log_content);
|
||||
if (rc) {
|
||||
/* log buffer is full. upload data right now */
|
||||
HAL_Timer_CountdownMs(&log_uploader_handle->upload_timer, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief upload data to tencent cloud
|
||||
*
|
||||
* @param force_upload if necessary
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _do_log_upload(bool force_upload)
|
||||
{
|
||||
int rc;
|
||||
static bool sg_need_handle_saved_log = true;
|
||||
|
||||
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
|
||||
POINTER_SANITY_CHECK(log_uploader_handle, QCLOUD_ERR_INVAL);
|
||||
|
||||
/* step1: handle previously saved log */
|
||||
if (log_uploader_handle->log_save_enabled && sg_need_handle_saved_log) {
|
||||
rc = _handle_saved_log();
|
||||
if (!rc) {
|
||||
sg_need_handle_saved_log = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* step2: check if have data to upload */
|
||||
if (!_check_force_upload(force_upload)) {
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
// log_buffer_lock();
|
||||
|
||||
/* step3: post log buffer data to cloud */
|
||||
uint32_t upload_log_size = get_log_buffer_write_index(log_uploader_handle->upload_buffer_handle);
|
||||
rc = _post_log_buffer_to_cloud(log_uploader_handle, get_log_buffer(log_uploader_handle->upload_buffer_handle),
|
||||
upload_log_size, true);
|
||||
if (rc) {
|
||||
sg_need_handle_saved_log = true;
|
||||
}
|
||||
|
||||
HAL_Timer_CountdownMs(&log_uploader_handle->upload_timer, LOG_UPLOAD_INTERVAL_MS);
|
||||
|
||||
// log_buffer_unlock();
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief init log upload previously
|
||||
*
|
||||
* @param[in] init_params @see LogUploadInitParams
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int log_upload_init_pre(LogUploadInitParams *init_params)
|
||||
{
|
||||
if (_get_log_uploader_client()) {
|
||||
UPLOAD_DBG("log upload initialized.");
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
QcloudIoTLogUpload *log_uploader_handle = HAL_Malloc(sizeof(QcloudIoTLogUpload));
|
||||
if (!log_uploader_handle) {
|
||||
UPLOAD_ERR("allocate for log client failed");
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
log_uploader_handle->upload_only_in_comm_err = false;
|
||||
|
||||
/* all the call back functions are necessary to handle log save and re-upload*/
|
||||
if (init_params->save_func && init_params->read_func && init_params->del_func && init_params->get_size_func &&
|
||||
init_params->save_log_filename) {
|
||||
log_uploader_handle->save_log_filename = init_params->save_log_filename;
|
||||
log_uploader_handle->save_func = init_params->save_func;
|
||||
log_uploader_handle->read_func = init_params->read_func;
|
||||
log_uploader_handle->del_func = init_params->del_func;
|
||||
log_uploader_handle->get_size_func = init_params->get_size_func;
|
||||
log_uploader_handle->log_save_enabled = true;
|
||||
} else {
|
||||
log_uploader_handle->log_save_enabled = false;
|
||||
}
|
||||
|
||||
log_uploader_handle->upload_buffer_handle = log_upload_buffer_init(init_params);
|
||||
if (!log_uploader_handle->upload_buffer_handle) {
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
_set_log_uploader_client(log_uploader_handle);
|
||||
log_uploader_handle->log_level = LOG_LEVEL_DEBUG;
|
||||
log_uploader_handle->log_client_init_done = true;
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
|
||||
err_exit:
|
||||
if (log_uploader_handle) {
|
||||
log_upload_buffer_deinit(log_uploader_handle->upload_buffer_handle);
|
||||
HAL_Free(log_uploader_handle);
|
||||
log_uploader_handle = NULL;
|
||||
}
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief stop log upload add release resources
|
||||
*
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int log_upload_deinit(void)
|
||||
{
|
||||
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
|
||||
POINTER_SANITY_CHECK(log_uploader_handle, QCLOUD_ERR_INVAL);
|
||||
log_upload_buffer_deinit(log_uploader_handle->upload_buffer_handle);
|
||||
HAL_Free(log_uploader_handle);
|
||||
log_uploader_handle = NULL;
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// External API
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief init log upload previously
|
||||
*
|
||||
* @param[in] init_params @see LogUploadInitParams
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int IOT_Log_Upload_InitPre(LogUploadInitParams *init_params)
|
||||
{
|
||||
POINTER_SANITY_CHECK(init_params, QCLOUD_ERR_INVAL);
|
||||
STRING_PTR_SANITY_CHECK(init_params->product_id, QCLOUD_ERR_INVAL);
|
||||
STRING_PTR_SANITY_CHECK(init_params->device_name, QCLOUD_ERR_INVAL);
|
||||
STRING_PTR_SANITY_CHECK(init_params->sign_key, QCLOUD_ERR_INVAL);
|
||||
return log_upload_init_pre(init_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief init log upload module
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int IOT_Log_Upload_Init(void *client)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
return log_upload_init(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief stop log upload add release resources
|
||||
*
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int IOT_Log_Upload_Deinit(void)
|
||||
{
|
||||
return log_upload_deinit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief do log upload
|
||||
*
|
||||
* @param force_upload if necessary
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int IOT_Log_Upload(bool force_upload)
|
||||
{
|
||||
return _do_log_upload(force_upload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief append need report log to log upload buffer
|
||||
*
|
||||
* @param[in] log_content data of need to report
|
||||
*/
|
||||
void IOT_Log_Upload_AppendToUploadBuffer(int log_level, const char *log_content)
|
||||
{
|
||||
POINTER_SANITY_CHECK_RTN(log_content);
|
||||
_log_upload_append_to_log_buffer(log_level, log_content);
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 120
|
||||
DerivePointerAlignment: true
|
||||
PointerAlignment: Left
|
||||
SortIncludes: true
|
||||
IncludeBlocks: Preserve
|
||||
IndentPPDirectives: AfterHash
|
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file test_broadcast.cc
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-07-18
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "mqtt_client_test.h"
|
||||
#include "qcloud_iot_common.h"
|
||||
|
||||
namespace mqtt_client_unittest {
|
||||
|
||||
/**
|
||||
* @brief Test log upload
|
||||
*
|
||||
*/
|
||||
TEST_F(MqttClientTest, log_upload_test) {
|
||||
LogHandleFunc func = {0};
|
||||
func.log_malloc = HAL_Malloc;
|
||||
func.log_free = HAL_Free;
|
||||
func.log_get_current_time_str = HAL_Timer_Current;
|
||||
func.log_printf = HAL_Printf;
|
||||
func.log_upload = IOT_Log_Upload_AppendToUploadBuffer;
|
||||
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
|
||||
|
||||
LogUploadInitParams log_upload_init_params = DEFAULT_LOG_UPLOAD_INIT_PARAMS;
|
||||
log_upload_init_params.device_name = device_info.device_name;
|
||||
log_upload_init_params.product_id = device_info.product_id;
|
||||
log_upload_init_params.sign_key = device_info.device_secret;
|
||||
|
||||
log_upload_init_params.log_buffer_size = LOG_UPLOAD_BUFFER_SIZE;
|
||||
log_upload_init_params.save_log_filename = "./tmp/upload-fail-save.log";
|
||||
log_upload_init_params.read_func = HAL_File_Read;
|
||||
log_upload_init_params.save_func = HAL_File_Save;
|
||||
log_upload_init_params.del_func = HAL_File_Del;
|
||||
log_upload_init_params.get_size_func = HAL_File_Get_Size;
|
||||
|
||||
ASSERT_EQ(IOT_Log_Upload_InitPre(&log_upload_init_params), 0);
|
||||
|
||||
uint32_t loop_cnt = 10;
|
||||
int rc = 0;
|
||||
do {
|
||||
Log_d("log upload test debug %d...", loop_cnt);
|
||||
Log_i("log upload test info %d...", loop_cnt);
|
||||
Log_w("log upload test waring %d...", loop_cnt);
|
||||
Log_e("log upload test error %d...", loop_cnt);
|
||||
rc = IOT_MQTT_Yield(client, 200);
|
||||
ASSERT_EQ(IOT_Log_Upload(rc ? true : false), 0);
|
||||
} while (loop_cnt--);
|
||||
|
||||
ASSERT_EQ(IOT_Log_Upload_Deinit(), 0);
|
||||
|
||||
func.log_upload = NULL;
|
||||
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
|
||||
}
|
||||
|
||||
} // namespace mqtt_client_unittest
|
@@ -0,0 +1,16 @@
|
||||
file(GLOB src_mqtt_client ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
|
||||
set(inc_mqtt_client ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
|
||||
|
||||
set(src_services ${src_services} ${src_mqtt_client} PARENT_SCOPE)
|
||||
set(inc_services ${inc_services} ${inc_mqtt_client} PARENT_SCOPE)
|
||||
|
||||
file(GLOB src_mqtt_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/mqtt_sample.c)
|
||||
add_executable(mqtt_sample ${src_mqtt_sample})
|
||||
target_link_libraries(mqtt_sample ${libsdk})
|
||||
|
||||
if( ${CONFIG_IOT_TEST} STREQUAL "ON")
|
||||
file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
|
||||
set(inc_mqtt_client_test ${CMAKE_CURRENT_SOURCE_DIR}/test)
|
||||
set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
|
||||
set(inc_test ${inc_test} ${inc_mqtt_client_test} PARENT_SCOPE)
|
||||
endif()
|
@@ -0,0 +1,446 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file mqtt_client.h
|
||||
* @brief mqtt client internel api
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-31
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-31 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_MQTT_CLIENT_INC_MQTT_CLIENT_H_
|
||||
#define IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_MQTT_CLIENT_INC_MQTT_CLIENT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "qcloud_iot_mqtt_client.h"
|
||||
|
||||
#include "mqtt_packet.h"
|
||||
|
||||
#include "network_interface.h"
|
||||
|
||||
#include "utils_list.h"
|
||||
#include "utils_base64.h"
|
||||
#include "utils_hmac.h"
|
||||
|
||||
/**
|
||||
* @brief Packet id, random from [1 - 65536]
|
||||
*
|
||||
*/
|
||||
#define MAX_PACKET_ID (65535)
|
||||
|
||||
/**
|
||||
* @brief Max size of conn Id
|
||||
*
|
||||
*/
|
||||
#define MAX_CONN_ID_LEN (6)
|
||||
|
||||
/**
|
||||
* @brief Max number of topic subscribed
|
||||
*
|
||||
*/
|
||||
#define MAX_MESSAGE_HANDLERS (10)
|
||||
|
||||
/**
|
||||
* @brief Max number in repub list
|
||||
*
|
||||
*/
|
||||
#define MAX_REPUB_NUM (20)
|
||||
|
||||
/**
|
||||
* @brief Minimal wait interval when reconnect
|
||||
*
|
||||
*/
|
||||
#define MIN_RECONNECT_WAIT_INTERVAL (1000)
|
||||
|
||||
/**
|
||||
* @brief Minimal MQTT timeout value
|
||||
*
|
||||
*/
|
||||
#define MIN_COMMAND_TIMEOUT (500)
|
||||
|
||||
/**
|
||||
* @brief Maxmal MQTT timeout value
|
||||
*
|
||||
*/
|
||||
#define MAX_COMMAND_TIMEOUT (20000)
|
||||
|
||||
/**
|
||||
* @brief Minimal TLS handshaking timeout value (unit: ms)
|
||||
*
|
||||
*/
|
||||
#define QCLOUD_IOT_TLS_HANDSHAKE_TIMEOUT (5 * 1000)
|
||||
|
||||
/**
|
||||
* @brief Enable repeat packet id filter
|
||||
*
|
||||
*/
|
||||
#define MQTT_RMDUP_MSG_ENABLED
|
||||
|
||||
/**
|
||||
* @brief Connect status of mqtt server
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
NOTCONNECTED = 0,
|
||||
CONNECTED = 1,
|
||||
} ConnStatus;
|
||||
|
||||
/**
|
||||
* @brief mqtt connection mode
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
FIRST_CONNECT = 0,
|
||||
RECONNECT = 1,
|
||||
} ConnMode;
|
||||
|
||||
/*
|
||||
* @brief Subscribe status of topic.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
SUB_ACK_NOT_RECEIVED = 0,
|
||||
SUB_ACK_RECEIVED = 1,
|
||||
} SubStatus;
|
||||
|
||||
/**
|
||||
* @brief data structure for topic subscription handle
|
||||
*/
|
||||
typedef struct {
|
||||
char *topic_filter; /**< topic name, wildcard filter is supported */
|
||||
SubStatus status; /**< status of sub handle */
|
||||
SubscribeParams params; /**< params needed to subscribe */
|
||||
} SubTopicHandle;
|
||||
|
||||
/**
|
||||
* @brief MQTT QCloud IoT Client structure
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
DeviceInfo *device_info; /**< device info needed to connect server */
|
||||
|
||||
uint16_t next_packet_id; /**< MQTT random packet id */
|
||||
uint32_t command_timeout_ms; /**< MQTT command timeout, unit:ms */
|
||||
|
||||
uint8_t write_buf[QCLOUD_IOT_MQTT_TX_BUF_LEN]; /**< MQTT write buffer */
|
||||
uint8_t read_buf[QCLOUD_IOT_MQTT_RX_BUF_LEN]; /**< MQTT read buffer */
|
||||
size_t write_buf_size; /**< size of MQTT write buffer */
|
||||
size_t read_buf_size; /**< size of MQTT read buffer */
|
||||
|
||||
MQTTEventHandler event_handle; /**< callback for MQTT event */
|
||||
uint8_t auto_connect_enable; /**< enable auto connection or not */
|
||||
uint8_t default_subscribe; /**< no subscribe packet send, only add subhandle */
|
||||
|
||||
void *lock_generic; /**< mutex/lock for this client struture */
|
||||
void *lock_write_buf; /**< mutex/lock for write buffer */
|
||||
void *list_pub_wait_ack; /**< puback waiting list */
|
||||
void *list_sub_wait_ack; /**< suback waiting list */
|
||||
|
||||
char host_addr[HOST_STR_LENGTH]; /**< MQTT server host */
|
||||
MQTTGetNextHostIp get_next_host_ip; // get host ip
|
||||
const char *main_host;
|
||||
const char *backup_host; // if host not connect will try this.
|
||||
IotNetwork network_stack; /**< MQTT network stack */
|
||||
|
||||
MQTTPacketConnectOption options; /**< handle to connection parameters */
|
||||
char conn_id[MAX_CONN_ID_LEN]; /**< connect id */
|
||||
|
||||
SubTopicHandle sub_handles[MAX_MESSAGE_HANDLERS]; /**< subscription handle array */
|
||||
Timer ping_timer; /**< MQTT ping timer */
|
||||
Timer reconnect_delay_timer; /**< MQTT reconnect delay timer */
|
||||
uint8_t was_manually_disconnected; /**< was disconnect by server or device */
|
||||
uint8_t is_ping_outstanding; /**< 1 = ping request is sent while ping response not arrived yet */
|
||||
uint32_t current_reconnect_wait_interval; /**< unit:ms */
|
||||
|
||||
uint8_t is_connected; /**< is connected or not */
|
||||
uint32_t counter_network_disconnected; /**< number of disconnection*/
|
||||
|
||||
#ifdef MQTT_RMDUP_MSG_ENABLED
|
||||
#define MQTT_MAX_REPEAT_BUF_LEN 10
|
||||
uint16_t repeat_packet_id_buf[MQTT_MAX_REPEAT_BUF_LEN]; /**< repeat packet id buffer */
|
||||
unsigned int current_packet_id_cnt; /**< index of packet id buffer */
|
||||
#endif
|
||||
} QcloudIotClient;
|
||||
|
||||
/**
|
||||
* @brief topic publish info
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t *buf; /**< msg buffer */
|
||||
uint32_t len; /**< msg length */
|
||||
uint16_t packet_id; /**< packet id */
|
||||
Timer pub_start_time; /**< timer for puback waiting */
|
||||
} QcloudIotPubInfo;
|
||||
|
||||
/**
|
||||
* @brief topic subscribe/unsubscribe info
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t *buf; /**< msg buffer */
|
||||
uint16_t len; /**< msg length */
|
||||
MQTTPacketType type; /**< type: sub or unsub */
|
||||
uint16_t packet_id; /**< packet id */
|
||||
Timer sub_start_time; /**< timer for suback waiting */
|
||||
SubTopicHandle handler; /**< handle of topic subscribed(unsubscribed) */
|
||||
} QcloudIotSubInfo;
|
||||
|
||||
/**************************************************************************************
|
||||
* common
|
||||
**************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Get the next packet id object.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return packet id
|
||||
*/
|
||||
uint16_t get_next_packet_id(QcloudIotClient *client);
|
||||
|
||||
/**
|
||||
* @brief Get the next conn id object.
|
||||
*
|
||||
* @param[out] conn_id buffer to save conn id
|
||||
*/
|
||||
void get_next_conn_id(char *conn_id);
|
||||
|
||||
/**
|
||||
* @brief Set the client conn state object.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] connected connect status, @see ConnStatus
|
||||
*/
|
||||
void set_client_conn_state(QcloudIotClient *client, uint8_t connected);
|
||||
|
||||
/**
|
||||
* @brief Get the client conn state object.
|
||||
*
|
||||
* @param[in,out] client
|
||||
* @return @see ConnStatus
|
||||
*/
|
||||
uint8_t get_client_conn_state(QcloudIotClient *client);
|
||||
|
||||
/**
|
||||
* @brief Send mqtt packet, timeout = command_timeout_ms.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] length length of data to be sent, data is saved in client write_buf
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int send_mqtt_packet(QcloudIotClient *client, size_t length);
|
||||
|
||||
/**************************************************************************************
|
||||
* connect
|
||||
**************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Connect MQTT server.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] mode when connect error. Choose different connection strategies according to the mode
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_connect(QcloudIotClient *client, ConnMode mode);
|
||||
|
||||
/**
|
||||
* @brief Reconnect MQTT server.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_attempt_reconnect(QcloudIotClient *client);
|
||||
|
||||
/**
|
||||
* @brief Disconnect MQTT server.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_disconnect(QcloudIotClient *client);
|
||||
|
||||
/**
|
||||
* @brief Serialize and send pingreq packet.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] try_times if failed, retry times
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_pingreq(QcloudIotClient *client, int try_times);
|
||||
|
||||
/**************************************************************************************
|
||||
* publish
|
||||
**************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Serialize and send publish packet.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @param[in] topic_name topic to publish
|
||||
* @param[in] params publish params
|
||||
* @return >=0 for packet id, < 0 for failed @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_publish(QcloudIotClient *client, const char *topic_name, const PublishParams *params);
|
||||
|
||||
/**
|
||||
* @brief Deserialize publish packet and deliver_message.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @param[in] params publish params
|
||||
* @return >=0 for packet id, < 0 for failed @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_handle_publish(QcloudIotClient *client);
|
||||
|
||||
/**
|
||||
* @brief Deserialize puback packet.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @return 0 for success.
|
||||
*/
|
||||
int qcloud_iot_mqtt_handle_puback(QcloudIotClient *client);
|
||||
|
||||
/**
|
||||
* @brief Process puback waiting timout.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
*/
|
||||
void qcloud_iot_mqtt_check_pub_timeout(QcloudIotClient *client);
|
||||
|
||||
/**************************************************************************************
|
||||
* subscribe
|
||||
**************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Serialize and send subscribe packet.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] topic_filter topic to subscribe
|
||||
* @param[in] params subscribe params
|
||||
* @return >=0 for packet id, < 0 for failed @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_subscribe(QcloudIotClient *client, const char *topic_filter, const SubscribeParams *params);
|
||||
|
||||
/**
|
||||
* @brief Deserialize suback packet and return sub result.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_handle_suback(QcloudIotClient *client);
|
||||
|
||||
/**
|
||||
* @brief Serialize and send unsubscribe packet.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] topic_filter topic to unsubscribe
|
||||
* @return >=0 packet id, < 0 for failed @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_unsubscribe(QcloudIotClient *client, const char *topic_filter);
|
||||
|
||||
/**
|
||||
* @brief Deserialize unsuback packet and remove from list.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_handle_unsuback(QcloudIotClient *client);
|
||||
|
||||
/**
|
||||
* @brief Process suback waiting timeout.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
*/
|
||||
void qcloud_iot_mqtt_check_sub_timeout(QcloudIotClient *client);
|
||||
|
||||
/**
|
||||
* @brief Resubscribe topic when reconnect.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_resubscribe(QcloudIotClient *client);
|
||||
|
||||
/**
|
||||
* @brief Return if topic is sub ready.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] topic_filter topic filter
|
||||
* @return true for ready
|
||||
* @return false for not ready
|
||||
*/
|
||||
bool qcloud_iot_mqtt_is_sub_ready(QcloudIotClient *client, const char *topic_filter);
|
||||
|
||||
/**
|
||||
* @brief Get usr data, usr should handle lock/unlock usrdata itself in callback and caller.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] topic_filter topic filter
|
||||
* @return NULL or user data
|
||||
*/
|
||||
void *qcloud_iot_mqtt_get_subscribe_usr_data(QcloudIotClient *client, const char *topic_filter);
|
||||
|
||||
/**
|
||||
* @brief Clear sub handle array.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
*/
|
||||
void qcloud_iot_mqtt_sub_handle_array_clear(QcloudIotClient *client);
|
||||
|
||||
/**
|
||||
* @brief Clear suback wait list and clear sub handle.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
*/
|
||||
void qcloud_iot_mqtt_suback_wait_list_clear(QcloudIotClient *client);
|
||||
|
||||
/**************************************************************************************
|
||||
* yield
|
||||
**************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Check connection and keep alive state, read/handle MQTT message in synchronized way.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] timeout_ms timeout value (unit: ms) for this operation
|
||||
*
|
||||
* @return QCLOUD_RET_SUCCESS when success, QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT when try reconnecting, others @see
|
||||
* IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_yield(QcloudIotClient *client, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Wait read specific mqtt packet, such as connack.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] packet_type packet type except to read
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_wait_for_read(QcloudIotClient *client, uint8_t packet_type);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_MQTT_CLIENT_INC_MQTT_CLIENT_H_
|
@@ -0,0 +1,306 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file mqtt_sample.c
|
||||
* @brief a simple sample for mqtt client
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-31
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-31 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "qcloud_iot_common.h"
|
||||
|
||||
#include "utils_log.h"
|
||||
|
||||
/**
|
||||
* @brief MQTT event callback, @see MQTTEventHandleFun
|
||||
*
|
||||
* @param[in] client pointer to mqtt client
|
||||
* @param[in] handle_context context
|
||||
* @param[in] msg msg
|
||||
*/
|
||||
static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
|
||||
{
|
||||
MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
|
||||
uintptr_t packet_id = (uintptr_t)msg->msg;
|
||||
|
||||
switch (msg->event_type) {
|
||||
case MQTT_EVENT_UNDEF:
|
||||
Log_i("undefined event occur.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DISCONNECT:
|
||||
Log_i("MQTT disconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_RECONNECT:
|
||||
Log_i("MQTT reconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_RECEIVED:
|
||||
Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
|
||||
mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
|
||||
mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
|
||||
break;
|
||||
case MQTT_EVENT_SUBSCRIBE_SUCCESS:
|
||||
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
|
||||
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_NACK:
|
||||
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
|
||||
Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
|
||||
Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_NACK:
|
||||
Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_SUCCESS:
|
||||
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_TIMEOUT:
|
||||
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_NACK:
|
||||
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
default:
|
||||
Log_i("Should NOT arrive here.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Setup MQTT construct parameters.
|
||||
*
|
||||
* @param[in,out] initParams @see MQTTInitParams
|
||||
* @param[in] device_info @see DeviceInfo
|
||||
*/
|
||||
static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
|
||||
{
|
||||
init_params->device_info = device_info;
|
||||
init_params->command_timeout = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT;
|
||||
init_params->keep_alive_interval_ms = QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL;
|
||||
init_params->auto_connect_enable = 1;
|
||||
init_params->event_handle.h_fp = _mqtt_event_handler;
|
||||
init_params->event_handle.context = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish a test mqtt message.
|
||||
*
|
||||
* @param[in, out] client pointer to mqtt client
|
||||
* @param[in] topic_keyword topic keyword
|
||||
* @param[in] qos qos to publish
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
static int _publish_test_msg(void *client, const char *topic_keyword, QoS qos)
|
||||
{
|
||||
char topic_name[MAX_SIZE_OF_CLOUD_TOPIC] = {0};
|
||||
|
||||
DeviceInfo *dev_info = IOT_MQTT_GetDeviceInfo(client);
|
||||
|
||||
HAL_Snprintf(topic_name, sizeof(topic_name), "%s/%s/%s", STRING_PTR_PRINT_SANITY_CHECK(dev_info->product_id),
|
||||
STRING_PTR_PRINT_SANITY_CHECK(dev_info->device_name), STRING_PTR_PRINT_SANITY_CHECK(topic_keyword));
|
||||
|
||||
static int test_count = 0;
|
||||
PublishParams pub_params = DEFAULT_PUB_PARAMS;
|
||||
pub_params.qos = qos;
|
||||
|
||||
char publish_content[128] = {0};
|
||||
HAL_Snprintf(publish_content, sizeof(publish_content), "{\"action\": \"publish_test\", \"count\": \"%d\"}",
|
||||
test_count++);
|
||||
pub_params.payload = publish_content;
|
||||
pub_params.payload_len = strlen(publish_content);
|
||||
return IOT_MQTT_Publish(client, topic_name, &pub_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback when MQTT msg arrives @see OnMessageHandler
|
||||
*
|
||||
* @param[in, out] client pointer to mqtt client
|
||||
* @param[in] message publish message from server
|
||||
* @param[in] usr_data user data of SubscribeParams, @see SubscribeParams
|
||||
*/
|
||||
static void _on_message_callback(void *client, const MQTTMessage *message, void *user_data)
|
||||
{
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log_i("Receive Message With topicName:%.*s, payload:%.*s", message->topic_len,
|
||||
STRING_PTR_PRINT_SANITY_CHECK(message->topic_name), message->payload_len,
|
||||
STRING_PTR_PRINT_SANITY_CHECK(message->payload_str));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Subscribe MQTT topic and wait for sub result.
|
||||
*
|
||||
* @param[in, out] client pointer to mqtt client
|
||||
* @param[in] topic_keyword topic keyword
|
||||
* @param[in] qos qos to subscribe
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _subscribe_topic_wait_result(void *client, char *topic_keyword, QoS qos)
|
||||
{
|
||||
char topic_name[MAX_SIZE_OF_CLOUD_TOPIC] = {0};
|
||||
|
||||
DeviceInfo *dev_info = IOT_MQTT_GetDeviceInfo(client);
|
||||
|
||||
HAL_Snprintf(topic_name, sizeof(topic_name), "%s/%s/%s", STRING_PTR_PRINT_SANITY_CHECK(dev_info->product_id),
|
||||
STRING_PTR_PRINT_SANITY_CHECK(dev_info->device_name), STRING_PTR_PRINT_SANITY_CHECK(topic_keyword));
|
||||
|
||||
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
|
||||
sub_params.qos = qos;
|
||||
sub_params.on_message_handler = _on_message_callback;
|
||||
|
||||
return IOT_MQTT_SubscribeSync(client, topic_name, &sub_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe MQTT topic.
|
||||
*
|
||||
* @param[in, out] client pointer to mqtt client
|
||||
* @param[in] topic_keyword topic keyword
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _unsubscribe_topic(void *client, char *topic_keyword)
|
||||
{
|
||||
char topic_name[MAX_SIZE_OF_CLOUD_TOPIC] = {0};
|
||||
|
||||
DeviceInfo *dev_info = IOT_MQTT_GetDeviceInfo(client);
|
||||
|
||||
HAL_Snprintf(topic_name, sizeof(topic_name), "%s/%s/%s", STRING_PTR_PRINT_SANITY_CHECK(dev_info->product_id),
|
||||
STRING_PTR_PRINT_SANITY_CHECK(dev_info->device_name), STRING_PTR_PRINT_SANITY_CHECK(topic_keyword));
|
||||
|
||||
int rc = IOT_MQTT_Unsubscribe(client, topic_name);
|
||||
if (rc < 0) {
|
||||
Log_e("MQTT unsubscribe FAILED: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return IOT_MQTT_Yield(client, 500); // wait for unsuback
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Main
|
||||
// ----------------------------------------------------------------------------
|
||||
static int sg_main_exit = 0;
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void _main_exit(int sig)
|
||||
{
|
||||
Log_e("demo exit by signal:%d\n", sig);
|
||||
sg_main_exit = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#ifdef __linux__
|
||||
signal(SIGINT, _main_exit);
|
||||
#endif
|
||||
|
||||
int rc;
|
||||
|
||||
// init log level
|
||||
LogHandleFunc func = {0};
|
||||
|
||||
func.log_malloc = HAL_Malloc;
|
||||
func.log_free = HAL_Free;
|
||||
func.log_get_current_time_str = HAL_Timer_Current;
|
||||
func.log_printf = HAL_Printf;
|
||||
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
|
||||
|
||||
DeviceInfo device_info;
|
||||
|
||||
rc = HAL_GetDevInfo((void *)&device_info);
|
||||
if (rc) {
|
||||
Log_e("get device info failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// init connection
|
||||
MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
|
||||
_setup_connect_init_params(&init_params, &device_info);
|
||||
|
||||
// create MQTT client and connect with server
|
||||
void *client = IOT_MQTT_Construct(&init_params);
|
||||
if (client) {
|
||||
Log_i("Cloud Device Construct Success");
|
||||
} else {
|
||||
Log_e("MQTT Construct failed!");
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
// subscribe normal topics and wait result
|
||||
rc = _subscribe_topic_wait_result(client, "data", QOS0);
|
||||
if (rc) {
|
||||
Log_e("Client Subscribe Topic Failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
do {
|
||||
rc = _publish_test_msg(client, "data", QOS1);
|
||||
if (rc < 0) {
|
||||
Log_e("client publish topic failed :%d.", rc);
|
||||
}
|
||||
|
||||
rc = IOT_MQTT_Yield(client, 2000);
|
||||
if (rc == QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT) {
|
||||
HAL_SleepMs(1000);
|
||||
continue;
|
||||
} else if (rc != QCLOUD_RET_SUCCESS && rc != QCLOUD_RET_MQTT_RECONNECTED) {
|
||||
Log_e("exit with error: %d", rc);
|
||||
break;
|
||||
}
|
||||
} while (!sg_main_exit);
|
||||
|
||||
rc = _unsubscribe_topic(client, "data");
|
||||
rc |= IOT_MQTT_Destroy(&client);
|
||||
|
||||
utils_log_deinit();
|
||||
return rc;
|
||||
}
|
@@ -0,0 +1,592 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file mqtt_client.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-28
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2021-07-07 <td>1.1 <td>fancyxu <td>support user host for unittest
|
||||
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "mqtt_client.h"
|
||||
|
||||
#define MQTT_VERSION_3_1_1 4
|
||||
// 20 for timestamp length & delimiter
|
||||
#define MAX_MQTT_CONNECT_USR_NAME_LEN (MAX_SIZE_OF_CLIENT_ID + QCLOUD_IOT_DEVICE_SDK_APPID_LEN + MAX_CONN_ID_LEN + 20)
|
||||
#define MAX_MQTT_CONNECT_PASSWORD_LEN (51)
|
||||
|
||||
/**************************************************************************************
|
||||
* static method
|
||||
**************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Get random packet id, when start.
|
||||
*
|
||||
* @return packet id
|
||||
*/
|
||||
static uint16_t _get_random_start_packet_id(void)
|
||||
{
|
||||
srand(HAL_Timer_CurrentSec());
|
||||
return rand() % 65536 + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Init list_pub_wait_ack and list_sub_wait_ack
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _mqtt_client_list_init(QcloudIotClient *client)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
|
||||
UtilsListFunc func = {
|
||||
.list_malloc = HAL_Malloc,
|
||||
.list_free = HAL_Free,
|
||||
.list_lock_init = HAL_MutexCreate,
|
||||
.list_lock_deinit = HAL_MutexDestroy,
|
||||
.list_lock = HAL_MutexLock,
|
||||
.list_unlock = HAL_MutexUnlock,
|
||||
};
|
||||
|
||||
client->list_pub_wait_ack = utils_list_create(func, MAX_REPUB_NUM);
|
||||
if (!client->list_pub_wait_ack) {
|
||||
Log_e("create pub wait list failed.");
|
||||
goto error;
|
||||
}
|
||||
|
||||
client->list_sub_wait_ack = utils_list_create(func, MAX_MESSAGE_HANDLERS);
|
||||
if (!client->list_sub_wait_ack) {
|
||||
Log_e("create sub wait list failed.");
|
||||
goto error;
|
||||
}
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
|
||||
error:
|
||||
utils_list_destroy(client->list_pub_wait_ack);
|
||||
client->list_pub_wait_ack = NULL;
|
||||
utils_list_destroy(client->list_sub_wait_ack);
|
||||
client->list_sub_wait_ack = NULL;
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Init network, tcp(with AUTH_WITH_NO_TLS) or tls.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
*/
|
||||
static void _mqtt_client_network_init(QcloudIotClient *client, const MQTTInitParams *params)
|
||||
{
|
||||
HAL_Snprintf(client->host_addr, HOST_STR_LENGTH, "%s.%s", client->device_info->product_id,
|
||||
params->host ? params->host : QCLOUD_IOT_MQTT_DIRECT_DOMAIN);
|
||||
client->main_host = params->host;
|
||||
client->backup_host = params->backup_host;
|
||||
client->get_next_host_ip = params->get_next_host_ip;
|
||||
#ifndef AUTH_WITH_NO_TLS
|
||||
// device param for TLS connection
|
||||
#ifdef AUTH_MODE_CERT
|
||||
Log_d("cert file: %s", STRING_PTR_PRINT_SANITY_CHECK(client->device_info->cert_file));
|
||||
Log_d("key file: %s", STRING_PTR_PRINT_SANITY_CHECK(client->device_info->key_file));
|
||||
|
||||
client->network_stack.ssl_connect_params.cert_file = client->device_info->cert_file;
|
||||
client->network_stack.ssl_connect_params.key_file = client->device_info->key_file;
|
||||
client->network_stack.ssl_connect_params.ca_crt = iot_ca_get();
|
||||
client->network_stack.ssl_connect_params.ca_crt_len = strlen(client->network_stack.ssl_connect_params.ca_crt);
|
||||
#else
|
||||
client->network_stack.ssl_connect_params.psk = (char *)client->device_info->device_secret_decode;
|
||||
client->network_stack.ssl_connect_params.psk_length = client->device_info->device_secret_decode_len;
|
||||
client->network_stack.ssl_connect_params.psk_id = client->device_info->client_id;
|
||||
client->network_stack.ssl_connect_params.ca_crt = NULL;
|
||||
client->network_stack.ssl_connect_params.ca_crt_len = 0;
|
||||
#endif
|
||||
client->network_stack.host = client->host_addr;
|
||||
client->network_stack.port = MQTT_SERVER_PORT_TLS;
|
||||
client->network_stack.ssl_connect_params.timeout_ms = client->command_timeout_ms > QCLOUD_IOT_TLS_HANDSHAKE_TIMEOUT
|
||||
? client->command_timeout_ms
|
||||
: QCLOUD_IOT_TLS_HANDSHAKE_TIMEOUT;
|
||||
client->network_stack.type = IOT_NETWORK_TYPE_TLS;
|
||||
#else
|
||||
client->network_stack.host = client->host_addr;
|
||||
client->network_stack.port = MQTT_SERVER_PORT_NO_TLS;
|
||||
client->network_stack.type = IOT_NETWORK_TYPE_TCP;
|
||||
#endif
|
||||
qcloud_iot_network_init(&(client->network_stack));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Init mqtt connection options.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] params mqtt init params, @see MQTTInitParams
|
||||
* @return int
|
||||
*/
|
||||
static int _mqtt_client_connect_option_init(QcloudIotClient *client, const MQTTInitParams *params)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
|
||||
int rc = 0;
|
||||
long cur_timesec = 0;
|
||||
|
||||
client->options.mqtt_version = MQTT_VERSION_3_1_1;
|
||||
// Upper limit of keep alive interval is (11.5 * 60) seconds
|
||||
client->options.client_id = client->device_info->client_id;
|
||||
client->options.keep_alive_interval =
|
||||
params->keep_alive_interval_ms / 1000 > 690 ? 690 : params->keep_alive_interval_ms / 1000;
|
||||
client->options.clean_session = params->clean_session;
|
||||
|
||||
// calculate user name & password
|
||||
client->options.username = (char *)HAL_Malloc(MAX_MQTT_CONNECT_USR_NAME_LEN);
|
||||
if (!client->options.username) {
|
||||
Log_e("malloc username failed!");
|
||||
rc = QCLOUD_ERR_MALLOC;
|
||||
goto error;
|
||||
}
|
||||
|
||||
cur_timesec =
|
||||
(MAX_ACCESS_EXPIRE_TIMEOUT <= 0) ? 0x7fffffffL : (HAL_Timer_CurrentSec() + MAX_ACCESS_EXPIRE_TIMEOUT / 1000);
|
||||
get_next_conn_id(client->conn_id);
|
||||
HAL_Snprintf(client->options.username, MAX_MQTT_CONNECT_USR_NAME_LEN, "%s;%s;%s;%ld", client->options.client_id,
|
||||
QCLOUD_IOT_DEVICE_SDK_APPID, client->conn_id, cur_timesec);
|
||||
|
||||
#if defined(AUTH_WITH_NO_TLS) && defined(AUTH_MODE_KEY)
|
||||
char sign[41] = {0};
|
||||
client->options.password = (char *)HAL_Malloc(MAX_MQTT_CONNECT_PASSWORD_LEN);
|
||||
if (!client->options.password) {
|
||||
Log_e("malloc password failed!");
|
||||
rc = QCLOUD_ERR_MALLOC;
|
||||
goto error;
|
||||
}
|
||||
utils_hmac_sha1(client->options.username, strlen(client->options.username),
|
||||
client->device_info->device_secret_decode, client->device_info->device_secret_decode_len, sign);
|
||||
HAL_Snprintf(client->options.password, MAX_MQTT_CONNECT_PASSWORD_LEN, "%s;hmacsha1", sign);
|
||||
#endif
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
error:
|
||||
HAL_Free(client->options.username);
|
||||
client->options.username = NULL;
|
||||
HAL_Free(client->options.password);
|
||||
client->options.password = NULL;
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Init mqtt client.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] params mqtt init params, @see MQTTInitParams
|
||||
* @return @see IotReturnCode
|
||||
*
|
||||
* @note
|
||||
* 1. init device info.
|
||||
* 2. init mutex and var
|
||||
* 3. init list @see _mqtt_client_list_init
|
||||
* 4. init connect option @see _mqtt_client_connect_option_init
|
||||
* 5. init network @see _mqtt_client_network_init
|
||||
*/
|
||||
static int _qcloud_iot_mqtt_client_init(QcloudIotClient *client, const MQTTInitParams *params)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
int rc = 0;
|
||||
memset(client, 0x0, sizeof(QcloudIotClient));
|
||||
|
||||
// set device info
|
||||
client->device_info = params->device_info;
|
||||
rc = HAL_Snprintf(client->device_info->client_id, MAX_SIZE_OF_CLIENT_ID, "%s%s", client->device_info->product_id,
|
||||
client->device_info->device_name);
|
||||
#ifdef AUTH_MODE_KEY
|
||||
utils_base64decode(client->device_info->device_secret_decode, MAX_SIZE_OF_DECODE_PSK_LENGTH,
|
||||
&client->device_info->device_secret_decode_len, (uint8_t *)client->device_info->device_secret,
|
||||
strlen(client->device_info->device_secret));
|
||||
#endif
|
||||
Log_i("SDK_Ver: %s, Product_ID: %s, Device_Name: %s", QCLOUD_IOT_DEVICE_SDK_VERSION,
|
||||
client->device_info->product_id, client->device_info->device_name);
|
||||
|
||||
// command timeout should be in range [MIN_COMMAND_TIMEOUT, MAX_COMMAND_TIMEOUT]
|
||||
client->command_timeout_ms =
|
||||
(params->command_timeout < MIN_COMMAND_TIMEOUT)
|
||||
? MIN_COMMAND_TIMEOUT
|
||||
: (params->command_timeout > MAX_COMMAND_TIMEOUT ? MAX_COMMAND_TIMEOUT : params->command_timeout);
|
||||
|
||||
// packet id, random from [1 - 65536]
|
||||
client->next_packet_id = _get_random_start_packet_id();
|
||||
client->write_buf_size = QCLOUD_IOT_MQTT_TX_BUF_LEN;
|
||||
client->read_buf_size = QCLOUD_IOT_MQTT_RX_BUF_LEN;
|
||||
client->event_handle = params->event_handle;
|
||||
client->auto_connect_enable = params->auto_connect_enable;
|
||||
client->default_subscribe = params->default_subscribe;
|
||||
|
||||
client->lock_generic = HAL_MutexCreate();
|
||||
if (!client->lock_generic) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
client->lock_write_buf = HAL_MutexCreate();
|
||||
if (!client->lock_write_buf) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = _mqtt_client_list_init(client);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = _mqtt_client_connect_option_init(client, params);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
_mqtt_client_network_init(client, params);
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
|
||||
|
||||
error:
|
||||
HAL_MutexDestroy(client->lock_generic);
|
||||
client->lock_generic = NULL;
|
||||
HAL_MutexDestroy(client->lock_write_buf);
|
||||
client->lock_write_buf = NULL;
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deinit mqtt client.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
*/
|
||||
static void _qcloud_iot_mqtt_client_deinit(QcloudIotClient *client)
|
||||
{
|
||||
HAL_Free(client->options.username);
|
||||
HAL_Free(client->options.password);
|
||||
HAL_MutexDestroy(client->lock_generic);
|
||||
HAL_MutexDestroy(client->lock_write_buf);
|
||||
qcloud_iot_mqtt_sub_handle_array_clear(client);
|
||||
qcloud_iot_mqtt_suback_wait_list_clear(client);
|
||||
utils_list_destroy(client->list_pub_wait_ack);
|
||||
utils_list_destroy(client->list_sub_wait_ack);
|
||||
Log_i("release mqtt client resources");
|
||||
}
|
||||
|
||||
/**************************************************************************************
|
||||
* API
|
||||
**************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Create MQTT client and connect to MQTT server.
|
||||
*
|
||||
* @param[in] params MQTT init parameters
|
||||
* @return a valid MQTT client handle when success, or NULL otherwise
|
||||
*/
|
||||
void *IOT_MQTT_Construct(const MQTTInitParams *params)
|
||||
{
|
||||
POINTER_SANITY_CHECK(params, NULL);
|
||||
POINTER_SANITY_CHECK(params->device_info, NULL);
|
||||
STRING_PTR_SANITY_CHECK(params->device_info->product_id, NULL);
|
||||
STRING_PTR_SANITY_CHECK(params->device_info->device_name, NULL);
|
||||
#ifdef AUTH_MODE_CERT
|
||||
STRING_PTR_SANITY_CHECK(params->device_info->dev_cert_file_name, NULL);
|
||||
STRING_PTR_SANITY_CHECK(params->device_info->dev_key_file_name, NULL);
|
||||
#else
|
||||
STRING_PTR_SANITY_CHECK(params->device_info->device_secret, NULL);
|
||||
#endif
|
||||
|
||||
int rc = 0;
|
||||
|
||||
QcloudIotClient *client = NULL;
|
||||
|
||||
// create and init MQTTClient
|
||||
client = (QcloudIotClient *)HAL_Malloc(sizeof(QcloudIotClient));
|
||||
if (!client) {
|
||||
Log_e("malloc MQTTClient failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc = _qcloud_iot_mqtt_client_init(client, params);
|
||||
if (rc) {
|
||||
Log_e("mqtt init failed: %d", rc);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!params->connect_when_construct) {
|
||||
return client;
|
||||
}
|
||||
|
||||
rc = qcloud_iot_mqtt_connect(client, FIRST_CONNECT);
|
||||
if (rc) {
|
||||
Log_e("mqtt connect with id: %s failed: %d", STRING_PTR_PRINT_SANITY_CHECK(client->conn_id), rc);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
Log_i("mqtt connect with id: %s success", client->conn_id);
|
||||
return client;
|
||||
exit:
|
||||
_qcloud_iot_mqtt_client_deinit(client);
|
||||
HAL_Free(client);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connect Mqtt server if not connect.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client pointer, should using the pointer of IOT_MQTT_Construct return.
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int IOT_MQTT_Connect(void *client)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
if (!get_client_conn_state(client)) {
|
||||
IOT_FUNC_EXIT_RC(qcloud_iot_mqtt_connect(client, FIRST_CONNECT));
|
||||
}
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Close connection and destroy MQTT client.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client pointer, should using the pointer of IOT_MQTT_Construct return.
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int IOT_MQTT_Destroy(void **client)
|
||||
{
|
||||
POINTER_SANITY_CHECK(*client, QCLOUD_ERR_INVAL);
|
||||
|
||||
QcloudIotClient *mqtt_client = (QcloudIotClient *)(*client);
|
||||
|
||||
int rc = qcloud_iot_mqtt_disconnect(mqtt_client);
|
||||
if (rc) {
|
||||
// disconnect network stack by force
|
||||
mqtt_client->network_stack.disconnect(&(mqtt_client->network_stack));
|
||||
set_client_conn_state(mqtt_client, NOTCONNECTED);
|
||||
}
|
||||
|
||||
_qcloud_iot_mqtt_client_deinit(mqtt_client);
|
||||
|
||||
HAL_Free(*client);
|
||||
*client = NULL;
|
||||
Log_i("mqtt release!");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check connection and keep alive state, read/handle MQTT packet in synchronized way.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] timeout_ms timeout value (unit: ms) for this operation
|
||||
* @return QCLOUD_RET_SUCCESS when success, QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT when try reconnecting, others @see
|
||||
* IotReturnCode
|
||||
*/
|
||||
int IOT_MQTT_Yield(void *client, uint32_t timeout_ms)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
|
||||
return qcloud_iot_mqtt_yield(mqtt_client, timeout_ms);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish MQTT message.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] topic_name topic to publish
|
||||
* @param[in] params @see PublishParams
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int IOT_MQTT_Publish(void *client, const char *topic_name, const PublishParams *params)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(params, QCLOUD_ERR_INVAL);
|
||||
STRING_PTR_SANITY_CHECK(topic_name, QCLOUD_ERR_INVAL);
|
||||
|
||||
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
|
||||
|
||||
if (!get_client_conn_state(client)) {
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
if (strlen(topic_name) > MAX_SIZE_OF_CLOUD_TOPIC) {
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MAX_TOPIC_LENGTH);
|
||||
}
|
||||
|
||||
if (QOS2 == params->qos) {
|
||||
Log_e("QoS2 is not supported currently");
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_QOS_NOT_SUPPORT);
|
||||
}
|
||||
|
||||
return qcloud_iot_mqtt_publish(mqtt_client, topic_name, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Subscribe MQTT topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] topic_filter topic filter to subscribe
|
||||
* @param[in] params @see SubscribeParams
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int IOT_MQTT_Subscribe(void *client, const char *topic_filter, const SubscribeParams *params)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(params, QCLOUD_ERR_INVAL);
|
||||
STRING_PTR_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
|
||||
|
||||
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
|
||||
|
||||
if (!get_client_conn_state(client) && !mqtt_client->default_subscribe) {
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
if (strlen(topic_filter) > MAX_SIZE_OF_CLOUD_TOPIC) {
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MAX_TOPIC_LENGTH);
|
||||
}
|
||||
|
||||
if (QOS2 == params->qos) {
|
||||
Log_e("QoS2 is not supported currently");
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_QOS_NOT_SUPPORT);
|
||||
}
|
||||
|
||||
return qcloud_iot_mqtt_subscribe(mqtt_client, topic_filter, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe MQTT topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] topic_filter topic filter to unsubscribe
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int IOT_MQTT_Unsubscribe(void *client, const char *topic_filter)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
STRING_PTR_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
|
||||
|
||||
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
|
||||
|
||||
if (!get_client_conn_state(client)) {
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
if (strlen(topic_filter) > MAX_SIZE_OF_CLOUD_TOPIC) {
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MAX_TOPIC_LENGTH);
|
||||
}
|
||||
|
||||
return qcloud_iot_mqtt_unsubscribe(mqtt_client, topic_filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if MQTT topic has been subscribed or not
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] topic_filter topic filter to subscribe
|
||||
* @return true already subscribed
|
||||
* @return false not ready
|
||||
*/
|
||||
bool IOT_MQTT_IsSubReady(void *client, const char *topic_filter)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, false);
|
||||
STRING_PTR_SANITY_CHECK(topic_filter, false);
|
||||
|
||||
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
|
||||
return qcloud_iot_mqtt_is_sub_ready(mqtt_client, topic_filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get user data in subscribe.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] topic_filter topic filter to subscribe
|
||||
* @return NULL or user data
|
||||
*/
|
||||
void *IOT_MQTT_GetSubUsrData(void *client, const char *topic_filter)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, NULL);
|
||||
STRING_PTR_SANITY_CHECK(topic_filter, NULL);
|
||||
|
||||
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
|
||||
return qcloud_iot_mqtt_get_subscribe_usr_data(mqtt_client, topic_filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Subscribe and wait sub ready.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] topic_filter topic filter to subscribe
|
||||
* @param[in] params @see SubscribeParams
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int IOT_MQTT_SubscribeSync(void *client, const char *topic_filter, const SubscribeParams *params)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(params, QCLOUD_ERR_INVAL);
|
||||
STRING_PTR_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
|
||||
|
||||
int rc;
|
||||
|
||||
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
|
||||
|
||||
int cnt_sub = mqtt_client->command_timeout_ms / QCLOUD_IOT_MQTT_YIELD_TIMEOUT;
|
||||
|
||||
if (IOT_MQTT_IsSubReady(client, topic_filter)) {
|
||||
// if already sub, free the user data
|
||||
if (params->user_data_free) {
|
||||
params->user_data_free(params->user_data);
|
||||
}
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
rc = IOT_MQTT_Subscribe(client, topic_filter, params);
|
||||
if (rc < 0) {
|
||||
Log_e("topic subscribe failed: %d, cnt: %d", rc, cnt_sub);
|
||||
return rc;
|
||||
}
|
||||
|
||||
while (cnt_sub-- >= 0 && rc >= 0 && !IOT_MQTT_IsSubReady(client, topic_filter)) {
|
||||
/**
|
||||
* @brief wait for subscription result
|
||||
*
|
||||
*/
|
||||
rc = IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT);
|
||||
}
|
||||
return IOT_MQTT_IsSubReady(client, topic_filter) ? QCLOUD_RET_SUCCESS : QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if MQTT is connected.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return true connected
|
||||
* @return false no connected
|
||||
*/
|
||||
bool IOT_MQTT_IsConnected(void *client)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
|
||||
return get_client_conn_state(mqtt_client);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get device info using to connect mqtt server.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see DeviceInfo
|
||||
*/
|
||||
DeviceInfo *IOT_MQTT_GetDeviceInfo(void *client)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, NULL);
|
||||
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
|
||||
return mqtt_client->device_info;
|
||||
}
|
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file mqtt_client_common.c
|
||||
* @brief common api
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-28
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "mqtt_client.h"
|
||||
|
||||
/**
|
||||
* @brief Get the next packet id object.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return packet id
|
||||
*/
|
||||
uint16_t get_next_packet_id(QcloudIotClient *client)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
|
||||
HAL_MutexLock(client->lock_generic);
|
||||
client->next_packet_id = (uint16_t)((MAX_PACKET_ID == client->next_packet_id) ? 1 : (client->next_packet_id + 1));
|
||||
HAL_MutexUnlock(client->lock_generic);
|
||||
|
||||
IOT_FUNC_EXIT_RC(client->next_packet_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the next conn id object.
|
||||
*
|
||||
* @param[out] conn_id buffer to save conn id
|
||||
*/
|
||||
void get_next_conn_id(char *conn_id)
|
||||
{
|
||||
int i;
|
||||
srand(HAL_Timer_CurrentSec());
|
||||
for (i = 0; i < MAX_CONN_ID_LEN - 1; i++) {
|
||||
int flag = rand() % 3;
|
||||
switch (flag) {
|
||||
case 0:
|
||||
conn_id[i] = (rand() % 26) + 'a';
|
||||
break;
|
||||
case 1:
|
||||
conn_id[i] = (rand() % 26) + 'A';
|
||||
break;
|
||||
case 2:
|
||||
conn_id[i] = (rand() % 10) + '0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
conn_id[MAX_CONN_ID_LEN - 1] = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the client conn state object.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] connected connect status, @see ConnStatus
|
||||
*/
|
||||
void set_client_conn_state(QcloudIotClient *client, uint8_t connected)
|
||||
{
|
||||
HAL_MutexLock(client->lock_generic);
|
||||
client->is_connected = connected;
|
||||
HAL_MutexUnlock(client->lock_generic);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the client conn state object.
|
||||
*
|
||||
* @param[in,out] client
|
||||
* @return @see ConnStatus
|
||||
*/
|
||||
uint8_t get_client_conn_state(QcloudIotClient *client)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
uint8_t is_connected = 0;
|
||||
HAL_MutexLock(client->lock_generic);
|
||||
is_connected = client->is_connected;
|
||||
HAL_MutexUnlock(client->lock_generic);
|
||||
IOT_FUNC_EXIT_RC(is_connected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send mqtt packet, timeout = command_timeout_ms.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] length length of data to be sent, data is saved in client write_buf
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int send_mqtt_packet(QcloudIotClient *client, size_t length)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
|
||||
int rc = QCLOUD_RET_SUCCESS;
|
||||
|
||||
size_t sent_len = 0;
|
||||
|
||||
if (length >= client->write_buf_size) {
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
|
||||
}
|
||||
|
||||
rc = client->network_stack.write(&(client->network_stack), client->write_buf, length, client->command_timeout_ms,
|
||||
&sent_len);
|
||||
rc = QCLOUD_ERR_TCP_WRITE_TIMEOUT == rc ? QCLOUD_ERR_MQTT_REQUEST_TIMEOUT : rc;
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
@@ -0,0 +1,279 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file mqtt_client_connect.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-28
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "mqtt_client.h"
|
||||
/**
|
||||
* @brief switch to domain mode
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
HOST_DOMAIN = 1,
|
||||
BACKUP_DOMAIN = 0,
|
||||
} DomainMode;
|
||||
|
||||
/**
|
||||
* @brief Serialize and send connect packet.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _mqtt_connect(QcloudIotClient *client)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
uint8_t session_present, connack_rc;
|
||||
int rc, packet_len;
|
||||
|
||||
// TCP or TLS network connect
|
||||
rc = client->network_stack.connect(&(client->network_stack));
|
||||
if (rc) {
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
// send MQTT CONNECT packet
|
||||
HAL_MutexLock(client->lock_write_buf);
|
||||
packet_len = mqtt_connect_packet_serialize(client->write_buf, client->write_buf_size, &client->options);
|
||||
if (packet_len > 0) {
|
||||
rc = send_mqtt_packet(client, packet_len);
|
||||
} else {
|
||||
rc = packet_len == MQTT_ERR_SHORT_BUFFER ? QCLOUD_ERR_BUF_TOO_SHORT : QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
HAL_MutexUnlock(client->lock_write_buf);
|
||||
if (rc) {
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
// recv MQTT CONNACK packet
|
||||
rc = qcloud_iot_mqtt_wait_for_read(client, CONNACK);
|
||||
if (rc) {
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
rc = mqtt_connack_packet_deserialize(client->read_buf, client->read_buf_size, &session_present, &connack_rc);
|
||||
if (rc) {
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
if (CONNACK_CONNECTION_ACCEPTED != connack_rc) {
|
||||
IOT_FUNC_EXIT_RC(connack_rc);
|
||||
}
|
||||
|
||||
// set connect state
|
||||
set_client_conn_state(client, CONNECTED);
|
||||
|
||||
HAL_MutexLock(client->lock_generic);
|
||||
client->was_manually_disconnected = client->is_ping_outstanding = 0;
|
||||
HAL_Timer_Countdown(&client->ping_timer, client->options.keep_alive_interval);
|
||||
HAL_MutexUnlock(client->lock_generic);
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief switch domain
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] mode @see DomainMode
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static void _mqtt_switch_domain(QcloudIotClient *client, DomainMode mode)
|
||||
{
|
||||
if (mode == BACKUP_DOMAIN && client->backup_host) {
|
||||
HAL_Snprintf(client->host_addr, HOST_STR_LENGTH, "%s.%s", client->device_info->product_id, client->backup_host);
|
||||
client->network_stack.host = client->host_addr;
|
||||
} else if (mode == HOST_DOMAIN) {
|
||||
HAL_Snprintf(client->host_addr, HOST_STR_LENGTH, "%s.%s", client->device_info->product_id, client->main_host);
|
||||
client->network_stack.host = client->host_addr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set connect host
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] srv_ip server ip string
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _mqtt_set_srv_ip(QcloudIotClient *client, char *srv_ip)
|
||||
{
|
||||
int size = HAL_Snprintf(client->host_addr, HOST_STR_LENGTH, "%s", srv_ip);
|
||||
if (size < 0 || size > HOST_STR_LENGTH - 1) {
|
||||
Log_e("gen host name failed: %d", size);
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
} else {
|
||||
client->network_stack.host = client->host_addr;
|
||||
Log_i("using HOST IP : %s", srv_ip);
|
||||
}
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Connect MQTT server.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] mode when connect error. Choose different connection strategies according to the mode
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_connect(QcloudIotClient *client, ConnMode mode)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
int rc = 0;
|
||||
|
||||
// check connection state first
|
||||
if (get_client_conn_state(client)) {
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_MQTT_ALREADY_CONNECTED);
|
||||
}
|
||||
|
||||
int connect_fail_cnt = 0;
|
||||
int max_connect_fail_switch_cnt = (mode == FIRST_CONNECT) ? 3 : 1;
|
||||
|
||||
while ((rc = _mqtt_connect(client))) {
|
||||
client->network_stack.disconnect(&(client->network_stack));
|
||||
connect_fail_cnt++;
|
||||
|
||||
if (connect_fail_cnt == max_connect_fail_switch_cnt && client->backup_host) {
|
||||
_mqtt_switch_domain(client, BACKUP_DOMAIN);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connect_fail_cnt > max_connect_fail_switch_cnt) {
|
||||
if (!client->get_next_host_ip) {
|
||||
break;
|
||||
}
|
||||
|
||||
char *srv_ip = client->get_next_host_ip();
|
||||
if (!srv_ip) {
|
||||
break;
|
||||
}
|
||||
_mqtt_set_srv_ip(client, srv_ip);
|
||||
}
|
||||
}
|
||||
|
||||
// In reconnection mode, the connection is successfully switched back to the primary domain name
|
||||
if (!rc && connect_fail_cnt && mode == RECONNECT) {
|
||||
_mqtt_switch_domain(client, HOST_DOMAIN);
|
||||
}
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reconnect MQTT server.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_attempt_reconnect(QcloudIotClient *client)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
int rc = 0;
|
||||
|
||||
Log_i("attempt to reconnect...");
|
||||
|
||||
if (get_client_conn_state(client)) {
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_MQTT_ALREADY_CONNECTED);
|
||||
}
|
||||
|
||||
rc = qcloud_iot_mqtt_connect(client, RECONNECT);
|
||||
|
||||
if (!get_client_conn_state(client)) {
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
#if 0 // clean session is 0, user don't need resubscribe only if mqtt server error.
|
||||
if (!client->options.clean_session) {
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_MQTT_RECONNECTED);
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = qcloud_iot_mqtt_resubscribe(client);
|
||||
if (rc) {
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_MQTT_RECONNECTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disconnect MQTT server.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_disconnect(QcloudIotClient *client)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
int rc, packet_len = 0;
|
||||
|
||||
if (!get_client_conn_state(client)) {
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
HAL_MutexLock(client->lock_write_buf);
|
||||
packet_len = mqtt_disconnect_packet_serialize(client->write_buf, client->write_buf_size);
|
||||
if (packet_len > 0) {
|
||||
rc = send_mqtt_packet(client, packet_len);
|
||||
} else {
|
||||
rc = packet_len == MQTT_ERR_SHORT_BUFFER ? QCLOUD_ERR_BUF_TOO_SHORT : QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
HAL_MutexUnlock(client->lock_write_buf);
|
||||
if (rc) {
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
client->network_stack.disconnect(&(client->network_stack));
|
||||
set_client_conn_state(client, NOTCONNECTED);
|
||||
client->was_manually_disconnected = 1;
|
||||
|
||||
Log_i("mqtt disconnect!");
|
||||
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serialize and send pingreq packet.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] try_times if failed, retry times
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_pingreq(QcloudIotClient *client, int try_times)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
int rc, packet_len, i = 0;
|
||||
|
||||
HAL_MutexLock(client->lock_write_buf);
|
||||
packet_len = mqtt_pingreq_packet_serialize(client->write_buf, client->write_buf_size);
|
||||
if (packet_len > 0) {
|
||||
do {
|
||||
rc = send_mqtt_packet(client, packet_len);
|
||||
} while (rc && (i++ < try_times));
|
||||
} else {
|
||||
rc = packet_len == MQTT_ERR_SHORT_BUFFER ? QCLOUD_ERR_BUF_TOO_SHORT : QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
HAL_MutexUnlock(client->lock_write_buf);
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
@@ -0,0 +1,397 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file mqtt_client_publish.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-28
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "mqtt_client.h"
|
||||
|
||||
/**
|
||||
* @brief Push pub info to list for republish.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @param[in] packet_len packet len of publish packet
|
||||
* @param[in] packet_id packet id
|
||||
* @param[out] node list node
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _push_pub_info_to_list(QcloudIotClient *client, int packet_len, uint16_t packet_id, void **node)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
void * list = client->list_pub_wait_ack;
|
||||
QcloudIotPubInfo *repub_info = NULL;
|
||||
|
||||
// construct republish info
|
||||
repub_info = (QcloudIotPubInfo *)HAL_Malloc(sizeof(QcloudIotPubInfo) + packet_len);
|
||||
if (!repub_info) {
|
||||
Log_e("memory malloc failed!");
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
repub_info->buf = (uint8_t *)repub_info + sizeof(QcloudIotPubInfo);
|
||||
repub_info->len = packet_len;
|
||||
repub_info->packet_id = packet_id;
|
||||
memcpy(repub_info->buf, client->write_buf, packet_len); // save the whole packet
|
||||
HAL_Timer_CountdownMs(&repub_info->pub_start_time, client->command_timeout_ms);
|
||||
|
||||
// push republish info to list
|
||||
*node = utils_list_push(list, repub_info);
|
||||
if (!*node) {
|
||||
HAL_Free(repub_info);
|
||||
Log_e("list push failed! Check the list len!");
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check pub wait list timeout.
|
||||
*
|
||||
* @param[in,out] list pointer to pub wait ack list.
|
||||
* @param[in] node pointer to list node
|
||||
* @param[in] val pointer to value, @see QcloudIotPubInfo
|
||||
* @param[in] usr_data @see QcloudIotClient
|
||||
* @return @see UtilsListResult
|
||||
*/
|
||||
static UtilsListResult _pub_wait_list_process_check_timeout(void *list, void *node, void *val, void *usr_data)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
|
||||
MQTTEventMsg msg;
|
||||
QcloudIotPubInfo *repub_info = (QcloudIotPubInfo *)val;
|
||||
QcloudIotClient * client = (QcloudIotClient *)usr_data;
|
||||
|
||||
// check the request if timeout or not
|
||||
if (HAL_Timer_Remain(&repub_info->pub_start_time) > 0) {
|
||||
IOT_FUNC_EXIT_RC(LIST_TRAVERSE_CONTINUE);
|
||||
}
|
||||
|
||||
// notify timeout event
|
||||
if (client->event_handle.h_fp) {
|
||||
msg.event_type = MQTT_EVENT_PUBLISH_TIMEOUT;
|
||||
msg.msg = (void *)(uintptr_t)repub_info->packet_id;
|
||||
client->event_handle.h_fp(client, client->event_handle.context, &msg);
|
||||
}
|
||||
utils_list_remove(list, node);
|
||||
IOT_FUNC_EXIT_RC(LIST_TRAVERSE_CONTINUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove info from pub wait list.
|
||||
*
|
||||
* @param[in,out] list pointer to pub wait ack list.
|
||||
* @param[in] node pointer to list node
|
||||
* @param[in] val pointer to value, @see QcloudIotPubInfo
|
||||
* @param[in] usr_data pointer to packet id
|
||||
* @return @see UtilsListResult
|
||||
*/
|
||||
static UtilsListResult _pub_wait_list_process_remove_info(void *list, void *node, void *val, void *usr_data)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
|
||||
QcloudIotPubInfo *repub_info = (QcloudIotPubInfo *)val;
|
||||
if (repub_info->packet_id == *((uint16_t *)usr_data)) {
|
||||
utils_list_remove(list, node);
|
||||
IOT_FUNC_EXIT_RC(LIST_TRAVERSE_BREAK);
|
||||
}
|
||||
IOT_FUNC_EXIT_RC(LIST_TRAVERSE_CONTINUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove node signed with packet id from publish ACK wait list.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @param[in] packet_id packet id
|
||||
*/
|
||||
static void _remove_pub_info_from_list(QcloudIotClient *client, uint16_t packet_id)
|
||||
{
|
||||
utils_list_process(client->list_pub_wait_ack, LIST_HEAD, _pub_wait_list_process_remove_info, &packet_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if topic match
|
||||
*
|
||||
* @param[in] topic_filter topic name filter, wildcard is supported
|
||||
* @param[in] topic_name topic name, no wildcard
|
||||
* @param[in] topic_name_len length of topic name
|
||||
* @return 1 for matched, 0 for no matched
|
||||
*
|
||||
* @note assume topic filter and name is in correct format;
|
||||
* # can only be at end;
|
||||
* + and # can only be next to separator.
|
||||
*/
|
||||
static uint8_t _is_topic_matched(char *topic_filter, const char *topic_name, uint16_t topic_name_len)
|
||||
{
|
||||
char *curf = topic_filter;
|
||||
char *curn = (char *)topic_name;
|
||||
char *curn_end = curn + topic_name_len;
|
||||
|
||||
while (*curf && curn < curn_end) {
|
||||
if (*curn == '/' && *curf != '/')
|
||||
break;
|
||||
if (*curf != '+' && *curf != '#' && *curf != *curn)
|
||||
break;
|
||||
if (*curf == '+') { // skip until we meet the next separator, or end of string
|
||||
char *nextpos = curn + 1;
|
||||
while (nextpos < curn_end && *nextpos != '/') nextpos = ++curn + 1;
|
||||
} else if (*curf == '#')
|
||||
curn = curn_end - 1; // skip until end of string
|
||||
curf++;
|
||||
curn++;
|
||||
};
|
||||
|
||||
return (curn == curn_end) && (*curf == '\0');
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief deliver the message to user callback
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] message message to deliver, @see MQTTMessage
|
||||
*/
|
||||
static void _deliver_message(QcloudIotClient *client, MQTTMessage *message)
|
||||
{
|
||||
int i;
|
||||
|
||||
MQTTEventMsg msg;
|
||||
|
||||
HAL_MutexLock(client->lock_generic);
|
||||
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
|
||||
if (client->sub_handles[i].topic_filter &&
|
||||
_is_topic_matched((char *)client->sub_handles[i].topic_filter, message->topic_name, message->topic_len)) {
|
||||
if (client->sub_handles[i].params.on_message_handler) {
|
||||
HAL_MutexUnlock(client->lock_generic);
|
||||
// if found, then handle it, then return
|
||||
client->sub_handles[i].params.on_message_handler(client, message,
|
||||
client->sub_handles[i].params.user_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
HAL_MutexUnlock(client->lock_generic);
|
||||
|
||||
/* Message handler not found for topic */
|
||||
/* May be we do not care change FAILURE use SUCCESS*/
|
||||
Log_d("no matching any topic, call default handle function");
|
||||
|
||||
if (client->event_handle.h_fp) {
|
||||
msg.event_type = MQTT_EVENT_PUBLISH_RECEIVED;
|
||||
msg.msg = message;
|
||||
client->event_handle.h_fp(client, client->event_handle.context, &msg);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MQTT_RMDUP_MSG_ENABLED
|
||||
|
||||
/**
|
||||
* @brief Check if packet id repeat.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] packet_id packet_id
|
||||
* @return < 0 for failed.
|
||||
*/
|
||||
static int _get_packet_id_repeat_buf(QcloudIotClient *client, uint16_t packet_id)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < MQTT_MAX_REPEAT_BUF_LEN; ++i) {
|
||||
if (packet_id == client->repeat_packet_id_buf[i]) {
|
||||
return packet_id;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add packet id, if buf full, then overwrite.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] packet_id packet_id
|
||||
*/
|
||||
static void _add_packet_id_to_repeat_buf(QcloudIotClient *client, uint16_t packet_id)
|
||||
{
|
||||
if (_get_packet_id_repeat_buf(client, packet_id) >= 0) {
|
||||
return;
|
||||
}
|
||||
client->repeat_packet_id_buf[client->current_packet_id_cnt++] = packet_id;
|
||||
client->current_packet_id_cnt %= MQTT_MAX_REPEAT_BUF_LEN;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Serialize and send publish packet.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @param[in] topic_name topic to publish
|
||||
* @param[in] params publish params
|
||||
* @return >=0 for packet id, < 0 for failed @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_publish(QcloudIotClient *client, const char *topic_name, const PublishParams *params)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
int rc, packet_len;
|
||||
MQTTPublishFlags flags;
|
||||
void * node = NULL;
|
||||
uint16_t packet_id = 0;
|
||||
|
||||
if (params->qos > QOS0) {
|
||||
packet_id = get_next_packet_id(client);
|
||||
}
|
||||
|
||||
Log_d("publish qos=%d|packet_id=%d|topic_name=%s|payload=%s", params->qos, packet_id, topic_name,
|
||||
STRING_PTR_PRINT_SANITY_CHECK((char *)params->payload));
|
||||
|
||||
flags.dup = params->dup;
|
||||
flags.qos = params->qos;
|
||||
flags.retain = params->retain;
|
||||
|
||||
// serialize packet
|
||||
HAL_MutexLock(client->lock_write_buf);
|
||||
packet_len = mqtt_publish_packet_serialize(client->write_buf, client->write_buf_size, &flags, packet_id, topic_name,
|
||||
params->payload, params->payload_len);
|
||||
if (packet_len < 0) {
|
||||
HAL_MutexUnlock(client->lock_write_buf);
|
||||
rc = packet_len == MQTT_ERR_SHORT_BUFFER ? QCLOUD_ERR_BUF_TOO_SHORT : QCLOUD_ERR_FAILURE;
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
if (params->qos > QOS0) {
|
||||
rc = _push_pub_info_to_list(client, packet_len, packet_id, &node);
|
||||
if (rc) {
|
||||
Log_e("push publish info failed!");
|
||||
HAL_MutexUnlock(client->lock_write_buf);
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
}
|
||||
|
||||
// send the publish packet
|
||||
rc = send_mqtt_packet(client, packet_len);
|
||||
HAL_MutexUnlock(client->lock_write_buf);
|
||||
if (rc) {
|
||||
if (params->qos > QOS0) {
|
||||
utils_list_remove(client->list_pub_wait_ack, node);
|
||||
}
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
IOT_FUNC_EXIT_RC(packet_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deserialize publish packet and deliver_message.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @param[in] params publish params
|
||||
* @return >=0 for packet id, < 0 for failed @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_handle_publish(QcloudIotClient *client)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
int rc, packet_len = 0;
|
||||
MQTTPublishFlags flags;
|
||||
MQTTMessage msg;
|
||||
|
||||
rc = mqtt_publish_packet_deserialize(client->read_buf, client->read_buf_size, &flags, &msg.packet_id,
|
||||
&msg.topic_name, &msg.topic_len, &msg.payload, &msg.payload_len);
|
||||
if (rc) {
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
msg.qos = flags.qos;
|
||||
|
||||
if (QOS0 == msg.qos) {
|
||||
// No further processing required for QOS0
|
||||
_deliver_message(client, &msg);
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
|
||||
}
|
||||
|
||||
// only Qos 1 is support
|
||||
#ifdef MQTT_RMDUP_MSG_ENABLED
|
||||
// check if packet_id has been received before
|
||||
if (_get_packet_id_repeat_buf(client, msg.packet_id) < 0)
|
||||
#endif
|
||||
{
|
||||
// deliver to msg callback
|
||||
_deliver_message(client, &msg);
|
||||
}
|
||||
#ifdef MQTT_RMDUP_MSG_ENABLED
|
||||
// just add packet id
|
||||
_add_packet_id_to_repeat_buf(client, msg.packet_id);
|
||||
#endif
|
||||
|
||||
// reply with puback
|
||||
HAL_MutexLock(client->lock_write_buf);
|
||||
packet_len = mqtt_puback_packet_serialize(client->write_buf, client->write_buf_size, msg.packet_id);
|
||||
if (packet_len > 0) {
|
||||
rc = send_mqtt_packet(client, packet_len);
|
||||
} else {
|
||||
rc = packet_len == MQTT_ERR_SHORT_BUFFER ? QCLOUD_ERR_BUF_TOO_SHORT : QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
HAL_MutexUnlock(client->lock_write_buf);
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deserialize puback packet.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @return 0 for success.
|
||||
*/
|
||||
int qcloud_iot_mqtt_handle_puback(QcloudIotClient *client)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
|
||||
int rc;
|
||||
uint16_t packet_id;
|
||||
MQTTEventMsg msg;
|
||||
|
||||
rc = mqtt_puback_packet_deserialize(client->read_buf, client->read_buf_size, &packet_id);
|
||||
if (rc) {
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
_remove_pub_info_from_list(client, packet_id);
|
||||
|
||||
/* notify this event to user callback */
|
||||
if (client->event_handle.h_fp) {
|
||||
msg.event_type = MQTT_EVENT_PUBLISH_SUCCESS;
|
||||
msg.msg = (void *)(uintptr_t)packet_id;
|
||||
client->event_handle.h_fp(client, client->event_handle.context, &msg);
|
||||
}
|
||||
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process puback waiting timout.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
*/
|
||||
void qcloud_iot_mqtt_check_pub_timeout(QcloudIotClient *client)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
utils_list_process(client->list_pub_wait_ack, LIST_HEAD, _pub_wait_list_process_check_timeout, client);
|
||||
IOT_FUNC_EXIT;
|
||||
}
|
@@ -0,0 +1,643 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file mqtt_client_subscribe.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-28
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "mqtt_client.h"
|
||||
|
||||
/**
|
||||
* @brief Context of subscribe
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t packet_id;
|
||||
SubTopicHandle handler;
|
||||
} QcloudIotSubConext;
|
||||
|
||||
/**
|
||||
* @brief Free topic_filter and user_data
|
||||
*
|
||||
* @param[in] handler subtopic handle
|
||||
*/
|
||||
static void _clear_sub_handle(SubTopicHandle *handler)
|
||||
{
|
||||
if (handler->topic_filter) {
|
||||
HAL_Free(handler->topic_filter);
|
||||
handler->topic_filter = NULL;
|
||||
}
|
||||
|
||||
if (handler->params.user_data && handler->params.user_data_free) {
|
||||
handler->params.user_data_free(handler->params.user_data);
|
||||
handler->params.user_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Push node to subscribe(unsubscribe) ACK wait list.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @param[in] packet_len packet len of publish packet
|
||||
* @param[in] packet_id packet id
|
||||
* @param[in] type mqtt packet type SUBSCRIBE or UNSUBSCRIBE
|
||||
* @param[in] handler subtopic handle
|
||||
* @param[out] node node to push to list
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _push_sub_info_to_list(QcloudIotClient *client, int packet_len, uint16_t packet_id, MQTTPacketType type,
|
||||
const SubTopicHandle *handler, void **node)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
void *list = client->list_sub_wait_ack;
|
||||
QcloudIotSubInfo *sub_info = NULL;
|
||||
|
||||
sub_info = (QcloudIotSubInfo *)HAL_Malloc(sizeof(QcloudIotSubInfo) + packet_len);
|
||||
if (!sub_info) {
|
||||
Log_e("memory malloc failed!");
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
sub_info->buf = (uint8_t *)sub_info + sizeof(QcloudIotSubInfo);
|
||||
sub_info->len = packet_len;
|
||||
sub_info->type = type;
|
||||
sub_info->packet_id = packet_id;
|
||||
sub_info->handler = *handler;
|
||||
memcpy(sub_info->buf, client->write_buf, packet_len);
|
||||
HAL_Timer_CountdownMs(&sub_info->sub_start_time, client->command_timeout_ms);
|
||||
|
||||
*node = utils_list_push(list, sub_info);
|
||||
if (!*node) {
|
||||
HAL_Free(sub_info);
|
||||
Log_e("list push failed! Check the list len!");
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check pub wait list timeout.
|
||||
*
|
||||
* @param[in,out] list pointer to pub wait ack list.
|
||||
* @param[in] node pointer to list node
|
||||
* @param[in] val pointer to value, @see QcloudIotPubInfo
|
||||
* @param[in] usr_data @see QcloudIotClient
|
||||
* @return @see UtilsListResult
|
||||
*/
|
||||
static UtilsListResult _sub_wait_list_process_pop_info(void *list, void *node, void *val, void *usr_data)
|
||||
{
|
||||
QcloudIotSubConext *sub_context = (QcloudIotSubConext *)usr_data;
|
||||
QcloudIotSubInfo *sub_info = (QcloudIotSubInfo *)val;
|
||||
|
||||
if (sub_info->packet_id == sub_context->packet_id) {
|
||||
memcpy(&sub_context->handler, &sub_info->handler, sizeof(SubTopicHandle));
|
||||
utils_list_remove(list, node);
|
||||
return LIST_TRAVERSE_BREAK;
|
||||
}
|
||||
return LIST_TRAVERSE_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check sub wait list timeout.
|
||||
*
|
||||
* @param[in,out] list pointer to sub wait ack list.
|
||||
* @param[in] node pointer to list node
|
||||
* @param[in] val pointer to value, @see QcloudIotSubInfo
|
||||
* @param[in] usr_data @see QcloudIotClient
|
||||
* @return @see UtilsListResult
|
||||
*/
|
||||
static UtilsListResult _sub_wait_list_process_check_timeout(void *list, void *node, void *val, void *usr_data)
|
||||
{
|
||||
MQTTEventMsg msg;
|
||||
QcloudIotSubInfo *sub_info = (QcloudIotSubInfo *)val;
|
||||
QcloudIotClient *client = (QcloudIotClient *)usr_data;
|
||||
|
||||
// check the request if timeout or not
|
||||
if (HAL_Timer_Remain(&sub_info->sub_start_time) > 0) {
|
||||
return LIST_TRAVERSE_CONTINUE;
|
||||
}
|
||||
|
||||
// notify timeout event
|
||||
if (client->event_handle.h_fp) {
|
||||
msg.event_type = SUBSCRIBE == sub_info->type ? MQTT_EVENT_SUBSCRIBE_TIMEOUT : MQTT_EVENT_UNSUBSCRIBE_TIMEOUT;
|
||||
msg.msg = (void *)(uintptr_t)sub_info->packet_id;
|
||||
if (sub_info->handler.params.on_sub_event_handler) {
|
||||
sub_info->handler.params.on_sub_event_handler(client, MQTT_EVENT_SUBSCRIBE_TIMEOUT,
|
||||
sub_info->handler.params.user_data);
|
||||
}
|
||||
client->event_handle.h_fp(client, client->event_handle.context, &msg);
|
||||
}
|
||||
|
||||
_clear_sub_handle(&sub_info->handler);
|
||||
utils_list_remove(list, node);
|
||||
return LIST_TRAVERSE_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear sub wait list.
|
||||
*
|
||||
* @param[in,out] list pointer to sub wait list.
|
||||
* @param[in] node pointer to list node
|
||||
* @param[in] val pointer to value, @see QcloudIotSubInfo
|
||||
* @param[in] usr_data null
|
||||
* @return @see UtilsListResult
|
||||
*/
|
||||
static UtilsListResult _sub_wait_list_process_clear(void *list, void *node, void *val, void *usr_data)
|
||||
{
|
||||
QcloudIotSubInfo *sub_info = (QcloudIotSubInfo *)val;
|
||||
if (sub_info->type == UNSUBSCRIBE) {
|
||||
_clear_sub_handle(&sub_info->handler);
|
||||
}
|
||||
utils_list_remove(list, node);
|
||||
return LIST_TRAVERSE_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pop node signed with packet id from subscribe ACK wait list, and return the sub handler
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @param[in] packet_id packet id
|
||||
* @param[out] sub_handle @see SubTopicHandle
|
||||
*/
|
||||
static void _pop_sub_info_from_list(QcloudIotClient *client, uint16_t packet_id, SubTopicHandle *sub_handle)
|
||||
{
|
||||
QcloudIotSubConext sub_context = {.packet_id = packet_id};
|
||||
utils_list_process(client->list_sub_wait_ack, LIST_HEAD, _sub_wait_list_process_pop_info, &sub_context);
|
||||
memcpy(sub_handle, &sub_context.handler, sizeof(SubTopicHandle));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove sub handle when unsubscribe.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @param[in] topic_filter topic to remove
|
||||
* @return true topic exist
|
||||
* @return false topic no exist
|
||||
*/
|
||||
static bool _remove_sub_handle_from_array(QcloudIotClient *client, const char *topic_filter)
|
||||
{
|
||||
int i;
|
||||
bool topic_exists = false;
|
||||
|
||||
// remove from message handler array
|
||||
HAL_MutexLock(client->lock_generic);
|
||||
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
|
||||
if ((client->sub_handles[i].topic_filter && !strcmp(client->sub_handles[i].topic_filter, topic_filter)) ||
|
||||
strstr(topic_filter, "/#") || strstr(topic_filter, "/+")) {
|
||||
// notify this event to topic subscriber
|
||||
if (client->sub_handles[i].params.on_sub_event_handler) {
|
||||
client->sub_handles[i].params.on_sub_event_handler(client, MQTT_EVENT_UNSUBSCRIBE,
|
||||
client->sub_handles[i].params.user_data);
|
||||
}
|
||||
_clear_sub_handle(&client->sub_handles[i]);
|
||||
// we don't want to break here, if the same topic is registered*with 2 callbacks.Unlikely scenario
|
||||
topic_exists = true;
|
||||
}
|
||||
}
|
||||
HAL_MutexUnlock(client->lock_generic);
|
||||
return topic_exists;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add sub handle when subscribe.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @param[in] sub_handle sub_handle to be add to array
|
||||
* @return true topic exist
|
||||
* @return false topic no exist
|
||||
*/
|
||||
static int _add_sub_handle_to_array(QcloudIotClient *client, const SubTopicHandle *sub_handle)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
int i, i_free = -1;
|
||||
|
||||
HAL_MutexLock(client->lock_generic);
|
||||
|
||||
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
|
||||
if (client->sub_handles[i].topic_filter) {
|
||||
if (!strcmp(client->sub_handles[i].topic_filter, sub_handle->topic_filter)) {
|
||||
i_free = i;
|
||||
// free the memory before
|
||||
_clear_sub_handle(&client->sub_handles[i]);
|
||||
Log_w("Identical topic found: %s", sub_handle->topic_filter);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
i_free = i_free == -1 ? i : i_free;
|
||||
}
|
||||
}
|
||||
|
||||
if (-1 == i_free) {
|
||||
Log_e("NO more @sub_handles space!");
|
||||
HAL_MutexUnlock(client->lock_generic);
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
client->sub_handles[i_free] = *sub_handle;
|
||||
HAL_MutexUnlock(client->lock_generic);
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set sub handle status.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @param[in] topic_filter topic to set status
|
||||
* @param[in] status @see SubStatus
|
||||
*/
|
||||
static void _set_sub_handle_status_to_array(QcloudIotClient *client, const char *topic_filter, SubStatus status)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
int i;
|
||||
|
||||
HAL_MutexLock(client->lock_generic);
|
||||
|
||||
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
|
||||
if (client->sub_handles[i].topic_filter) {
|
||||
if (!strcmp(client->sub_handles[i].topic_filter, topic_filter)) {
|
||||
client->sub_handles[i].status = status;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
HAL_MutexUnlock(client->lock_generic);
|
||||
IOT_FUNC_EXIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serialize and send subscribe packet.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] topic_filter topic to subscribe
|
||||
* @param[in] params subscribe params
|
||||
* @return >=0 for packet id, < 0 for failed @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_subscribe(QcloudIotClient *client, const char *topic_filter, const SubscribeParams *params)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
int rc, packet_len, qos = params->qos;
|
||||
uint16_t packet_id;
|
||||
char *topic_filter_stored;
|
||||
void *node = NULL;
|
||||
SubTopicHandle sub_handle;
|
||||
|
||||
// topic filter should be valid in the whole sub life
|
||||
topic_filter_stored = HAL_Malloc(strlen(topic_filter) + 1);
|
||||
if (!topic_filter_stored) {
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MALLOC);
|
||||
}
|
||||
strncpy(topic_filter_stored, topic_filter, strlen(topic_filter) + 1);
|
||||
|
||||
sub_handle.topic_filter = topic_filter_stored;
|
||||
sub_handle.params = *params;
|
||||
sub_handle.status = client->default_subscribe ? SUB_ACK_RECEIVED : SUB_ACK_NOT_RECEIVED;
|
||||
|
||||
// add sub handle first to process
|
||||
rc = _add_sub_handle_to_array(client, &sub_handle);
|
||||
if (rc) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (client->default_subscribe) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
packet_id = get_next_packet_id(client);
|
||||
Log_d("subscribe topic_name=%s|packet_id=%d", topic_filter_stored, packet_id);
|
||||
// serialize packet
|
||||
HAL_MutexLock(client->lock_write_buf);
|
||||
packet_len = mqtt_subscribe_packet_serialize(client->write_buf, client->write_buf_size, packet_id, 1,
|
||||
&topic_filter_stored, &qos);
|
||||
if (packet_len < 0) {
|
||||
HAL_MutexUnlock(client->lock_write_buf);
|
||||
rc = packet_len == MQTT_ERR_SHORT_BUFFER ? QCLOUD_ERR_BUF_TOO_SHORT : QCLOUD_ERR_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// add node into sub ack wait list
|
||||
rc = _push_sub_info_to_list(client, packet_len, packet_id, SUBSCRIBE, &sub_handle, &node);
|
||||
if (rc) {
|
||||
HAL_MutexUnlock(client->lock_write_buf);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// send packet
|
||||
rc = send_mqtt_packet(client, packet_len);
|
||||
HAL_MutexUnlock(client->lock_write_buf);
|
||||
if (rc) {
|
||||
utils_list_remove(client->list_sub_wait_ack, node);
|
||||
goto exit;
|
||||
}
|
||||
IOT_FUNC_EXIT_RC(packet_id);
|
||||
exit:
|
||||
_remove_sub_handle_from_array(client, topic_filter_stored);
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deserialize suback packet and return sub result.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_handle_suback(QcloudIotClient *client)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
int rc, count = 0;
|
||||
uint16_t packet_id = 0;
|
||||
int granted_qos;
|
||||
MQTTEventMsg msg = {MQTT_EVENT_SUBSCRIBE_SUCCESS, NULL};
|
||||
SubTopicHandle sub_handle = {0};
|
||||
MQTTEventType event_type = MQTT_EVENT_SUBSCRIBE_SUCCESS;
|
||||
|
||||
rc = mqtt_suback_packet_deserialize(client->read_buf, client->read_buf_size, 1, &count, &packet_id, &granted_qos);
|
||||
if (rc) {
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
msg.msg = (void *)(uintptr_t)packet_id;
|
||||
|
||||
// pop sub info and get sub handle
|
||||
_pop_sub_info_from_list(client, packet_id, &sub_handle);
|
||||
if (!sub_handle.topic_filter) {
|
||||
Log_w("can't get sub handle from list!");
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS); // in case of resubscribe when reconnect
|
||||
}
|
||||
|
||||
// check return code in SUBACK packet: 0x00(QOS0, SUCCESS),0x01(QOS1, SUCCESS),0x02(QOS2,SUCCESS),0x80(Failure)
|
||||
if (0x80 == granted_qos) {
|
||||
msg.event_type = MQTT_EVENT_SUBSCRIBE_NACK;
|
||||
event_type = MQTT_EVENT_SUBSCRIBE_NACK;
|
||||
Log_e("MQTT SUBSCRIBE failed, packet_id: %u topic: %s", packet_id, sub_handle.topic_filter);
|
||||
_remove_sub_handle_from_array(client, sub_handle.topic_filter);
|
||||
rc = QCLOUD_ERR_MQTT_SUB;
|
||||
}
|
||||
|
||||
if (MQTT_EVENT_SUBSCRIBE_SUCCESS == msg.event_type) {
|
||||
_set_sub_handle_status_to_array(client, sub_handle.topic_filter, SUB_ACK_RECEIVED);
|
||||
}
|
||||
|
||||
// notify this event to user callback
|
||||
if (client->event_handle.h_fp) {
|
||||
client->event_handle.h_fp(client, client->event_handle.context, &msg);
|
||||
}
|
||||
|
||||
// notify this event to topic subscriber
|
||||
if (sub_handle.params.on_sub_event_handler) {
|
||||
sub_handle.params.on_sub_event_handler(client, event_type, sub_handle.params.user_data);
|
||||
}
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serialize and send unsubscribe packet.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] topic_filter topic to unsubscribe
|
||||
* @return >=0 packet id, < 0 for failed @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_unsubscribe(QcloudIotClient *client, const char *topic_filter)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
int rc, packet_len;
|
||||
uint16_t packet_id;
|
||||
|
||||
void *node = NULL;
|
||||
SubTopicHandle sub_handle;
|
||||
memset(&sub_handle, 0, sizeof(SubTopicHandle));
|
||||
|
||||
// remove from sub handle
|
||||
if (!_remove_sub_handle_from_array(client, topic_filter)) {
|
||||
Log_w("subscription does not exists: %s", topic_filter);
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_UNSUB_FAIL);
|
||||
}
|
||||
|
||||
// topic filter should be valid in the whole sub life
|
||||
char *topic_filter_stored = HAL_Malloc(strlen(topic_filter) + 1);
|
||||
if (!topic_filter_stored) {
|
||||
Log_e("malloc failed");
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
strncpy(topic_filter_stored, topic_filter, strlen(topic_filter) + 1);
|
||||
sub_handle.topic_filter = topic_filter_stored;
|
||||
|
||||
packet_id = get_next_packet_id(client);
|
||||
Log_d("unsubscribe topic_name=%s|packet_id=%d", topic_filter_stored, packet_id);
|
||||
|
||||
HAL_MutexLock(client->lock_write_buf);
|
||||
packet_len = mqtt_unsubscribe_packet_serialize(client->write_buf, client->write_buf_size, packet_id, 1,
|
||||
&topic_filter_stored);
|
||||
if (packet_len < 0) {
|
||||
HAL_MutexUnlock(client->lock_write_buf);
|
||||
rc = packet_len == MQTT_ERR_SHORT_BUFFER ? QCLOUD_ERR_BUF_TOO_SHORT : QCLOUD_ERR_FAILURE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// add node into sub ack wait list
|
||||
rc = _push_sub_info_to_list(client, packet_len, packet_id, UNSUBSCRIBE, &sub_handle, &node);
|
||||
if (rc) {
|
||||
Log_e("push unsubscribe info failed!");
|
||||
HAL_MutexUnlock(client->lock_write_buf);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* send the unsubscribe packet */
|
||||
rc = send_mqtt_packet(client, packet_len);
|
||||
HAL_MutexUnlock(client->lock_write_buf);
|
||||
if (rc) {
|
||||
utils_list_remove(client->list_sub_wait_ack, node);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
IOT_FUNC_EXIT_RC(packet_id);
|
||||
exit:
|
||||
_clear_sub_handle(&sub_handle);
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deserialize unsuback packet and remove from list.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_handle_unsuback(QcloudIotClient *client)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
int rc;
|
||||
uint16_t packet_id = 0;
|
||||
SubTopicHandle sub_handle = {0};
|
||||
MQTTEventMsg msg;
|
||||
|
||||
rc = mqtt_unsuback_packet_deserialize(client->read_buf, client->read_buf_size, &packet_id);
|
||||
if (rc) {
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
_pop_sub_info_from_list(client, packet_id, &sub_handle);
|
||||
_clear_sub_handle(&sub_handle);
|
||||
|
||||
if (client->event_handle.h_fp) {
|
||||
msg.event_type = MQTT_EVENT_UNSUBSCRIBE_SUCCESS;
|
||||
msg.msg = (void *)(uintptr_t)packet_id;
|
||||
client->event_handle.h_fp(client, client->event_handle.context, &msg);
|
||||
}
|
||||
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process suback waiting timeout.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
*/
|
||||
void qcloud_iot_mqtt_check_sub_timeout(QcloudIotClient *client)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
utils_list_process(client->list_sub_wait_ack, LIST_HEAD, _sub_wait_list_process_check_timeout, client);
|
||||
IOT_FUNC_EXIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resubscribe topic when reconnect.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_resubscribe(QcloudIotClient *client)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
int rc, packet_len, qos, itr = 0;
|
||||
uint16_t packet_id;
|
||||
SubTopicHandle *sub_handle;
|
||||
|
||||
for (itr = 0; itr < MAX_MESSAGE_HANDLERS; itr++) {
|
||||
sub_handle = &client->sub_handles[itr];
|
||||
if (!sub_handle->topic_filter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
packet_id = get_next_packet_id(client);
|
||||
Log_d("subscribe topic_name=%s|packet_id=%d", sub_handle->topic_filter, packet_id);
|
||||
|
||||
HAL_MutexLock(client->lock_write_buf);
|
||||
qos = sub_handle->params.qos;
|
||||
packet_len = mqtt_subscribe_packet_serialize(client->write_buf, client->write_buf_size, packet_id, 1,
|
||||
&sub_handle->topic_filter, &qos);
|
||||
if (packet_len < 0) {
|
||||
HAL_MutexUnlock(client->lock_write_buf);
|
||||
continue;
|
||||
}
|
||||
|
||||
// send packet
|
||||
rc = send_mqtt_packet(client, packet_len);
|
||||
HAL_MutexUnlock(client->lock_write_buf);
|
||||
if (rc) {
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
}
|
||||
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return if topic is sub ready.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] topic_filter topic filter
|
||||
* @return true for ready
|
||||
* @return false for not ready
|
||||
*/
|
||||
bool qcloud_iot_mqtt_is_sub_ready(QcloudIotClient *client, const char *topic_filter)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
int i = 0;
|
||||
HAL_MutexLock(client->lock_generic);
|
||||
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
|
||||
if ((client->sub_handles[i].topic_filter && !strcmp(client->sub_handles[i].topic_filter, topic_filter)) ||
|
||||
strstr(topic_filter, "/#") || strstr(topic_filter, "/+")) {
|
||||
HAL_MutexUnlock(client->lock_generic);
|
||||
return client->sub_handles[i].status == SUB_ACK_RECEIVED;
|
||||
}
|
||||
}
|
||||
HAL_MutexUnlock(client->lock_generic);
|
||||
IOT_FUNC_EXIT_RC(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get usr data, usr should handle lock/unlock usrdata itself in callback and caller.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] topic_filter topic filter
|
||||
* @return NULL or user data
|
||||
*/
|
||||
void *qcloud_iot_mqtt_get_subscribe_usr_data(QcloudIotClient *client, const char *topic_filter)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
int i = 0;
|
||||
HAL_MutexLock(client->lock_generic);
|
||||
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
|
||||
if ((client->sub_handles[i].topic_filter && !strcmp(client->sub_handles[i].topic_filter, topic_filter)) ||
|
||||
strstr(topic_filter, "/#") || strstr(topic_filter, "/+")) {
|
||||
HAL_MutexUnlock(client->lock_generic);
|
||||
return client->sub_handles[i].params.user_data;
|
||||
}
|
||||
}
|
||||
HAL_MutexUnlock(client->lock_generic);
|
||||
IOT_FUNC_EXIT_RC(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear sub handle array.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
*/
|
||||
void qcloud_iot_mqtt_sub_handle_array_clear(QcloudIotClient *client)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
int i;
|
||||
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
|
||||
/* notify this event to topic subscriber */
|
||||
if (client->sub_handles[i].topic_filter && client->sub_handles[i].params.on_sub_event_handler) {
|
||||
client->sub_handles[i].params.on_sub_event_handler(client, MQTT_EVENT_CLIENT_DESTROY,
|
||||
client->sub_handles[i].params.user_data);
|
||||
}
|
||||
_clear_sub_handle(&client->sub_handles[i]);
|
||||
}
|
||||
|
||||
IOT_FUNC_EXIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear suback wait list and clear sub handle.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
*/
|
||||
void qcloud_iot_mqtt_suback_wait_list_clear(QcloudIotClient *client)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
utils_list_process(client->list_sub_wait_ack, LIST_HEAD, _sub_wait_list_process_clear, client);
|
||||
IOT_FUNC_EXIT;
|
||||
}
|
@@ -0,0 +1,554 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file mqtt_client_yield.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-05-28
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "mqtt_client.h"
|
||||
|
||||
/**
|
||||
* @brief Remain waiting time after MQTT data is received (unit: ms)
|
||||
*
|
||||
*/
|
||||
#define QCLOUD_IOT_MQTT_MAX_REMAIN_WAIT_MS (100)
|
||||
|
||||
/**
|
||||
* @brief Read one byte from network for mqtt packet header except remaining length.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @param[in] timeout_ms timeout to read
|
||||
* @param[out] packet_type packet type to get
|
||||
* @param[in,out] pptr pointer to buf pointer
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _read_packet_header(QcloudIotClient *client, uint32_t timeout_ms, uint8_t *packet_type, uint8_t **pptr)
|
||||
{
|
||||
int rc = 0;
|
||||
size_t read_len = 0;
|
||||
|
||||
// refresh timeout
|
||||
timeout_ms = timeout_ms <= 0 ? 1 : timeout_ms;
|
||||
|
||||
rc = client->network_stack.read(&(client->network_stack), *pptr, 1, timeout_ms, &read_len);
|
||||
if (rc == QCLOUD_ERR_SSL_NOTHING_TO_READ || rc == QCLOUD_ERR_TCP_NOTHING_TO_READ) {
|
||||
return QCLOUD_ERR_MQTT_NOTHING_TO_READ;
|
||||
}
|
||||
|
||||
// get packet type
|
||||
*packet_type = (**pptr & 0xf0) >> 4;
|
||||
(*pptr)++;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read 1~4 bytes from network for mqtt packet header remaining length.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @param[in] timeout_ms timeout to read
|
||||
* @param[out] plen read len
|
||||
* @param[in,out] pptr pointer to buf pointer
|
||||
* @return int
|
||||
*/
|
||||
static int _read_packet_remaining_len(QcloudIotClient *client, uint32_t timeout_ms, uint32_t *plen, uint8_t **pptr)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
|
||||
uint8_t *buf = *pptr;
|
||||
uint8_t c = 0;
|
||||
uint32_t multiplier = 1;
|
||||
uint32_t count = 0;
|
||||
uint32_t len = 0;
|
||||
size_t rlen = 0;
|
||||
|
||||
// refresh timeout
|
||||
timeout_ms = timeout_ms <= 0 ? 1 : timeout_ms;
|
||||
timeout_ms += QCLOUD_IOT_MQTT_MAX_REMAIN_WAIT_MS;
|
||||
|
||||
do {
|
||||
if (++count > MAX_NO_OF_REMAINING_LENGTH_BYTES) {
|
||||
/* bad data */
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_PACKET_READ)
|
||||
}
|
||||
|
||||
if (client->network_stack.read(&(client->network_stack), buf, 1, timeout_ms, &rlen)) {
|
||||
/* The value argument is the important value. len is just used temporarily
|
||||
* and never used by the calling function for anything else */
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
|
||||
}
|
||||
|
||||
c = *buf++;
|
||||
len += (c & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
} while (c & 128);
|
||||
|
||||
if (plen) {
|
||||
*plen = len;
|
||||
}
|
||||
|
||||
*pptr += count;
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Discard the packet for mqtt read buf not enough.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @param[in] timeout_ms timeout to read
|
||||
* @param[in] rem_len remaining length to read
|
||||
*/
|
||||
static void _discard_packet_for_short_buf(QcloudIotClient *client, uint32_t timeout_ms, uint32_t rem_len)
|
||||
{
|
||||
int rc;
|
||||
size_t bytes_to_be_read, total_bytes_read = 0;
|
||||
size_t read_len;
|
||||
|
||||
timeout_ms = timeout_ms <= 0 ? 1 : timeout_ms;
|
||||
timeout_ms += QCLOUD_IOT_MQTT_MAX_REMAIN_WAIT_MS;
|
||||
|
||||
bytes_to_be_read = client->read_buf_size;
|
||||
do {
|
||||
rc = client->network_stack.read(&(client->network_stack), client->read_buf, bytes_to_be_read, timeout_ms,
|
||||
&read_len);
|
||||
if (rc) {
|
||||
break;
|
||||
}
|
||||
|
||||
total_bytes_read += read_len;
|
||||
bytes_to_be_read =
|
||||
(rem_len - total_bytes_read) >= client->read_buf_size ? client->read_buf_size : rem_len - total_bytes_read;
|
||||
} while (total_bytes_read < rem_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read rem_len bytes from network for mqtt packet payload.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @param[in] timeout_ms timeout to read
|
||||
* @param[in] rem_len length to read
|
||||
* @param[out] buf buffer to save payload
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _read_packet_payload(QcloudIotClient *client, uint32_t timeout_ms, uint32_t rem_len, uint8_t *buf)
|
||||
{
|
||||
int rc;
|
||||
size_t read_len = 0;
|
||||
|
||||
timeout_ms = timeout_ms <= 0 ? 1 : timeout_ms;
|
||||
timeout_ms += QCLOUD_IOT_MQTT_MAX_REMAIN_WAIT_MS;
|
||||
|
||||
rc = client->network_stack.read(&(client->network_stack), buf, rem_len, timeout_ms, &read_len);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (read_len != rem_len) {
|
||||
#ifdef AUTH_WITH_NO_TLS
|
||||
return QCLOUD_ERR_SSL_READ_TIMEOUT;
|
||||
#else
|
||||
return QCLOUD_ERR_TCP_READ_TIMEOUT;
|
||||
#endif
|
||||
}
|
||||
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read MQTT packet from network stack
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @param[in,out] timer timeout timer
|
||||
* @param[out] packet_type MQTT packet type
|
||||
* @return @see IotReturnCode
|
||||
*
|
||||
* @note
|
||||
* 1. read 1st byte in fixed header and check if valid
|
||||
* 2. read the remaining length
|
||||
* 3. read payload according to remaining length
|
||||
*/
|
||||
static int _read_mqtt_packet(QcloudIotClient *client, Timer *timer, uint8_t *packet_type)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
int rc = 0;
|
||||
uint32_t rem_len = 0, packet_len = 0;
|
||||
uint8_t *packet_read_buf = client->read_buf;
|
||||
|
||||
// 1. read 1st byte in fixed header and check if valid
|
||||
rc = _read_packet_header(client, HAL_Timer_Remain(timer), packet_type, &packet_read_buf);
|
||||
if (rc) {
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
// 2. read the remaining length
|
||||
rc = _read_packet_remaining_len(client, HAL_Timer_Remain(timer), &rem_len, &packet_read_buf);
|
||||
if (rc) {
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
// if read buffer is not enough to read the remaining length, discard the packet
|
||||
packet_len = packet_read_buf - client->read_buf + rem_len;
|
||||
if (packet_len >= client->read_buf_size) {
|
||||
_discard_packet_for_short_buf(client, HAL_Timer_Remain(timer), rem_len);
|
||||
Log_e("MQTT Recv buffer not enough: %lu < %d", client->read_buf_size, rem_len);
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
|
||||
}
|
||||
|
||||
// 3. read payload according to remaining length
|
||||
if (rem_len > 0) {
|
||||
rc = _read_packet_payload(client, HAL_Timer_Remain(timer), rem_len, packet_read_buf);
|
||||
}
|
||||
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset ping counter & ping timer.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
*/
|
||||
static void _handle_pingresp_packet(QcloudIotClient *client)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
|
||||
HAL_MutexLock(client->lock_generic);
|
||||
client->is_ping_outstanding = 0;
|
||||
HAL_Timer_Countdown(&client->ping_timer, client->options.keep_alive_interval);
|
||||
HAL_MutexUnlock(client->lock_generic);
|
||||
|
||||
IOT_FUNC_EXIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read a mqtt packet from network and handle it.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt_client
|
||||
* @param[in] timer timer for operation
|
||||
* @param[out] packet_type packet type of packet read
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _cycle_for_read(QcloudIotClient *client, Timer *timer, uint8_t *packet_type)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
|
||||
int rc;
|
||||
|
||||
/* read the socket, see what work is due */
|
||||
rc = _read_mqtt_packet(client, timer, packet_type);
|
||||
if (QCLOUD_ERR_MQTT_NOTHING_TO_READ == rc) {
|
||||
/* Nothing to read, not a cycle failure */
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
switch (*packet_type) {
|
||||
case CONNACK:
|
||||
case PUBREC:
|
||||
case PUBREL:
|
||||
case PUBCOMP:
|
||||
case PINGRESP:
|
||||
break;
|
||||
case PUBACK:
|
||||
rc = qcloud_iot_mqtt_handle_puback(client);
|
||||
break;
|
||||
case SUBACK:
|
||||
rc = qcloud_iot_mqtt_handle_suback(client);
|
||||
break;
|
||||
case UNSUBACK:
|
||||
rc = qcloud_iot_mqtt_handle_unsuback(client);
|
||||
break;
|
||||
case PUBLISH:
|
||||
rc = qcloud_iot_mqtt_handle_publish(client);
|
||||
break;
|
||||
default:
|
||||
// Either unknown packet type or failure occurred should not happen
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_RX_MESSAGE_INVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (*packet_type) {
|
||||
// Recv below msgs are all considered as PING OK
|
||||
case PUBACK:
|
||||
case SUBACK:
|
||||
case UNSUBACK:
|
||||
case PINGRESP:
|
||||
_handle_pingresp_packet(client);
|
||||
break;
|
||||
|
||||
// Recv downlink pub means link is OK but we still need to send PING request
|
||||
case PUBLISH:
|
||||
HAL_MutexLock(client->lock_generic);
|
||||
client->is_ping_outstanding = 0;
|
||||
HAL_MutexUnlock(client->lock_generic);
|
||||
break;
|
||||
}
|
||||
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set reconnect wait interval if auto conect is enable.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
*/
|
||||
static void _set_reconnect_wait_interval(QcloudIotClient *client)
|
||||
{
|
||||
client->counter_network_disconnected++;
|
||||
if (client->auto_connect_enable) {
|
||||
srand(HAL_Timer_CurrentSec());
|
||||
// range: 1000 - 2000 ms, in 10ms unit
|
||||
client->current_reconnect_wait_interval = (rand() % 100 + 100) * 10;
|
||||
HAL_Timer_CountdownMs(&(client->reconnect_delay_timer), client->current_reconnect_wait_interval);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle disconnect if connection is error.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
*/
|
||||
static void _handle_disconnect(QcloudIotClient *client)
|
||||
{
|
||||
int rc;
|
||||
MQTTEventMsg msg;
|
||||
|
||||
if (!get_client_conn_state(client)) {
|
||||
_set_reconnect_wait_interval(client);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = qcloud_iot_mqtt_disconnect(client);
|
||||
// disconnect network stack by force
|
||||
if (rc) {
|
||||
client->network_stack.disconnect(&(client->network_stack));
|
||||
set_client_conn_state(client, NOTCONNECTED);
|
||||
}
|
||||
|
||||
Log_e("disconnect MQTT for some reasons..");
|
||||
|
||||
// notify event
|
||||
if (client->event_handle.h_fp) {
|
||||
msg.event_type = MQTT_EVENT_DISCONNECT;
|
||||
msg.msg = NULL;
|
||||
client->event_handle.h_fp(client, client->event_handle.context, &msg);
|
||||
}
|
||||
|
||||
// exceptional disconnection
|
||||
client->was_manually_disconnected = 0;
|
||||
_set_reconnect_wait_interval(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle reconnect.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _handle_reconnect(QcloudIotClient *client)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
|
||||
int rc = QCLOUD_RET_MQTT_RECONNECTED;
|
||||
|
||||
MQTTEventMsg msg;
|
||||
|
||||
// reconnect control by delay timer (increase interval exponentially )
|
||||
if (!HAL_Timer_Expired(&(client->reconnect_delay_timer))) {
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT);
|
||||
}
|
||||
|
||||
rc = qcloud_iot_mqtt_attempt_reconnect(client);
|
||||
if (QCLOUD_RET_MQTT_RECONNECTED == rc) {
|
||||
Log_e("attempt to reconnect success.");
|
||||
// notify event
|
||||
if (client->event_handle.h_fp) {
|
||||
msg.event_type = MQTT_EVENT_RECONNECT;
|
||||
msg.msg = NULL;
|
||||
client->event_handle.h_fp(client, client->event_handle.context, &msg);
|
||||
}
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
Log_e("attempt to reconnect failed, errCode: %d", rc);
|
||||
|
||||
client->current_reconnect_wait_interval *= 2;
|
||||
|
||||
if (MAX_RECONNECT_WAIT_INTERVAL < client->current_reconnect_wait_interval) {
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_RECONNECT_TIMEOUT);
|
||||
}
|
||||
|
||||
HAL_Timer_CountdownMs(&(client->reconnect_delay_timer), client->current_reconnect_wait_interval);
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle MQTT keep alive (hearbeat with server).
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _mqtt_keep_alive(QcloudIotClient *client)
|
||||
{
|
||||
#define MQTT_PING_RETRY_TIMES 2
|
||||
#define MQTT_PING_SEND_RETRY_TIMES 3
|
||||
|
||||
IOT_FUNC_ENTRY;
|
||||
int rc = 0;
|
||||
|
||||
if (0 == client->options.keep_alive_interval) {
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
|
||||
}
|
||||
|
||||
if (!HAL_Timer_Expired(&client->ping_timer)) {
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
|
||||
}
|
||||
|
||||
if (client->is_ping_outstanding >= MQTT_PING_RETRY_TIMES) {
|
||||
// reaching here means we haven't received any MQTT packet for a long time (keep_alive_interval)
|
||||
Log_e("Fail to recv MQTT msg. Something wrong with the connection.");
|
||||
_handle_disconnect(client);
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
// there is no ping outstanding - send one
|
||||
rc = qcloud_iot_mqtt_pingreq(client, MQTT_PING_SEND_RETRY_TIMES);
|
||||
if (rc) {
|
||||
// if sending a PING fails, propably the connection is not OK and we decide to disconnect and begin reconnection
|
||||
// attempts
|
||||
Log_e("Fail to send PING request. Something wrong with the connection.");
|
||||
_handle_disconnect(client);
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
// start a timer to wait for PINGRESP from server
|
||||
HAL_MutexLock(client->lock_generic);
|
||||
client->is_ping_outstanding++;
|
||||
HAL_Timer_CountdownMs(&client->ping_timer, client->command_timeout_ms);
|
||||
HAL_MutexUnlock(client->lock_generic);
|
||||
Log_d("PING request %u has been sent...", client->is_ping_outstanding);
|
||||
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check connection and keep alive state, read/handle MQTT message in synchronized way.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] timeout_ms timeout value (unit: ms) for this operation
|
||||
*
|
||||
* @return QCLOUD_RET_SUCCESS when success, QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT when try reconnecting, others @see
|
||||
* IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_yield(QcloudIotClient *client, uint32_t timeout_ms)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
|
||||
int rc = QCLOUD_RET_SUCCESS;
|
||||
uint8_t packet_type;
|
||||
Timer timer;
|
||||
|
||||
// 1. check if manually disconnect
|
||||
if (!get_client_conn_state(client) && client->was_manually_disconnected == 1) {
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_RET_MQTT_MANUALLY_DISCONNECTED);
|
||||
}
|
||||
|
||||
// 2. check connection state and if auto reconnect is enabled
|
||||
if (!get_client_conn_state(client) && client->auto_connect_enable == 0) {
|
||||
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
|
||||
}
|
||||
|
||||
// 3. main loop for packet reading/handling and keep alive maintainance
|
||||
HAL_Timer_CountdownMs(&timer, timeout_ms);
|
||||
|
||||
while (!HAL_Timer_Expired(&timer)) {
|
||||
// handle reconnect
|
||||
if (!get_client_conn_state(client)) {
|
||||
if (client->current_reconnect_wait_interval > MAX_RECONNECT_WAIT_INTERVAL) {
|
||||
rc = QCLOUD_ERR_MQTT_RECONNECT_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
rc = _handle_reconnect(client);
|
||||
continue;
|
||||
}
|
||||
|
||||
// read and handle packet
|
||||
rc = _cycle_for_read(client, &timer, &packet_type);
|
||||
switch (rc) {
|
||||
case QCLOUD_RET_SUCCESS:
|
||||
// check list of wait publish ACK to remove node that is ACKED or timeout
|
||||
qcloud_iot_mqtt_check_pub_timeout(client);
|
||||
// check list of wait subscribe(or unsubscribe) ACK to remove node that is ACKED or timeout
|
||||
qcloud_iot_mqtt_check_sub_timeout(client);
|
||||
|
||||
rc = _mqtt_keep_alive(client);
|
||||
if (rc) {
|
||||
IOT_FUNC_EXIT_RC(client->auto_connect_enable ? QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT : rc);
|
||||
}
|
||||
break;
|
||||
case QCLOUD_ERR_SSL_READ_TIMEOUT:
|
||||
case QCLOUD_ERR_SSL_READ:
|
||||
case QCLOUD_ERR_TCP_PEER_SHUTDOWN:
|
||||
case QCLOUD_ERR_TCP_READ_TIMEOUT:
|
||||
case QCLOUD_ERR_TCP_READ_FAIL:
|
||||
Log_e("network read failed, rc: %d. MQTT Disconnect.", rc);
|
||||
_handle_disconnect(client);
|
||||
IOT_FUNC_EXIT_RC(client->auto_connect_enable ? QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT
|
||||
: QCLOUD_ERR_MQTT_NO_CONN);
|
||||
break;
|
||||
default: // others, just return
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
}
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wait read specific mqtt packet, such as connack.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] packet_type packet type except to read
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int qcloud_iot_mqtt_wait_for_read(QcloudIotClient *client, uint8_t packet_type)
|
||||
{
|
||||
IOT_FUNC_ENTRY;
|
||||
|
||||
int rc;
|
||||
uint8_t read_packet_type = 0;
|
||||
Timer timer;
|
||||
HAL_Timer_CountdownMs(&timer, client->command_timeout_ms);
|
||||
|
||||
do {
|
||||
if (HAL_Timer_Expired(&timer)) {
|
||||
rc = QCLOUD_ERR_MQTT_REQUEST_TIMEOUT;
|
||||
break;
|
||||
}
|
||||
rc = _cycle_for_read(client, &timer, &read_packet_type);
|
||||
} while (QCLOUD_RET_SUCCESS == rc && read_packet_type != packet_type);
|
||||
|
||||
IOT_FUNC_EXIT_RC(rc);
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 120
|
||||
DerivePointerAlignment: true
|
||||
PointerAlignment: Left
|
||||
SortIncludes: true
|
||||
IncludeBlocks: Preserve
|
||||
IndentPPDirectives: AfterHash
|
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file test_mqtt_client.cc
|
||||
* @brief unittest for mqtt client
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-07-07
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-07-07 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>support tls test
|
||||
* <tr><td>2021-07-12 <td>1.1 <td>fancyxu <td>fix connect twice in 5s error
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "mqtt_client_test.h"
|
||||
|
||||
namespace mqtt_client_unittest {
|
||||
|
||||
void MqttClientTest::SetUp() {
|
||||
LogHandleFunc func = {0};
|
||||
func.log_malloc = HAL_Malloc;
|
||||
func.log_free = HAL_Free;
|
||||
func.log_get_current_time_str = HAL_Timer_Current;
|
||||
func.log_printf = HAL_Printf;
|
||||
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
|
||||
|
||||
ASSERT_EQ(HAL_GetDevInfo(reinterpret_cast<void *>(&device_info)), 0);
|
||||
|
||||
MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
|
||||
init_params.device_info = &device_info;
|
||||
init_params.command_timeout = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT;
|
||||
init_params.keep_alive_interval_ms = QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL;
|
||||
init_params.auto_connect_enable = 1;
|
||||
init_params.event_handle.h_fp = NULL;
|
||||
init_params.event_handle.context = NULL;
|
||||
|
||||
HAL_SleepMs(5000); // for iot hub can not connect twice in 5 s
|
||||
|
||||
client = IOT_MQTT_Construct(&init_params);
|
||||
ASSERT_NE(client, nullptr);
|
||||
}
|
||||
|
||||
void MqttClientTest::TearDown() {
|
||||
IOT_MQTT_Destroy(&client);
|
||||
utils_log_deinit();
|
||||
}
|
||||
|
||||
} // namespace mqtt_client_unittest
|
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file mqtt_client_test.h
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-07-18
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_MQTT_CLIENT_TEST_MQTT_CLIENT_TEST_H_
|
||||
#define IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_MQTT_CLIENT_TEST_MQTT_CLIENT_TEST_H_
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "qcloud_iot_common.h"
|
||||
|
||||
namespace mqtt_client_unittest {
|
||||
|
||||
/**
|
||||
* @brief test fixture of mqtt client
|
||||
*
|
||||
*/
|
||||
class MqttClientTest : public testing::Test {
|
||||
protected:
|
||||
void SetUp() override;
|
||||
|
||||
void TearDown() override;
|
||||
|
||||
void *client = NULL;
|
||||
DeviceInfo device_info;
|
||||
};
|
||||
|
||||
} // namespace mqtt_client_unittest
|
||||
|
||||
#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_MQTT_CLIENT_TEST_MQTT_CLIENT_TEST_H_
|
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file test_mqtt_client.cc
|
||||
* @brief unittest for mqtt client
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-07-07
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-07-07 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>support tls test
|
||||
* <tr><td>2021-07-12 <td>1.1 <td>fancyxu <td>fix connect twice in 5s error
|
||||
* <tr><td>2021-07-18 <td>1.2 <td>fancyxu <td>remove MqttClientTest for common use
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "mqtt_client_test.h"
|
||||
|
||||
namespace mqtt_client_unittest {
|
||||
|
||||
/**
|
||||
* @brief Test subscribe.
|
||||
*
|
||||
*/
|
||||
TEST_F(MqttClientTest, subscribe) {
|
||||
int wait_cnt;
|
||||
char topic_name[MAX_SIZE_OF_CLOUD_TOPIC] = {0};
|
||||
HAL_Snprintf(topic_name, sizeof(topic_name), "%s/%s/data", device_info.product_id, device_info.device_name);
|
||||
|
||||
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
|
||||
|
||||
/**
|
||||
* @brief QOS0
|
||||
*
|
||||
*/
|
||||
wait_cnt = 10;
|
||||
sub_params.qos = QOS0;
|
||||
ASSERT_GE(IOT_MQTT_Subscribe(client, topic_name, &sub_params), 0);
|
||||
while (!IOT_MQTT_IsSubReady(client, topic_name) && (wait_cnt > 0)) {
|
||||
ASSERT_EQ(IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT), 0);
|
||||
wait_cnt--;
|
||||
}
|
||||
ASSERT_NE(wait_cnt, 0);
|
||||
|
||||
ASSERT_GE(IOT_MQTT_Unsubscribe(client, topic_name), 0);
|
||||
ASSERT_EQ(IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT), 0);
|
||||
|
||||
/**
|
||||
* @brief QOS1
|
||||
*
|
||||
*/
|
||||
wait_cnt = 10;
|
||||
sub_params.qos = QOS1;
|
||||
ASSERT_GE(IOT_MQTT_Subscribe(client, topic_name, &sub_params), 0);
|
||||
while (!IOT_MQTT_IsSubReady(client, topic_name) && (wait_cnt > 0)) {
|
||||
ASSERT_EQ(IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT), 0);
|
||||
wait_cnt--;
|
||||
}
|
||||
ASSERT_NE(wait_cnt, 0);
|
||||
|
||||
ASSERT_GE(IOT_MQTT_Unsubscribe(client, topic_name), 0);
|
||||
ASSERT_EQ(IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT), 0);
|
||||
|
||||
/**
|
||||
* @brief sub sync
|
||||
*
|
||||
*/
|
||||
ASSERT_GE(IOT_MQTT_SubscribeSync(client, topic_name, &sub_params), 0);
|
||||
ASSERT_GE(IOT_MQTT_Unsubscribe(client, topic_name), 0);
|
||||
ASSERT_EQ(IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test publish.
|
||||
*
|
||||
*/
|
||||
TEST_F(MqttClientTest, publish) {
|
||||
char topic_name[MAX_SIZE_OF_CLOUD_TOPIC] = {0};
|
||||
HAL_Snprintf(topic_name, sizeof(topic_name), "%s/%s/data", device_info.product_id, device_info.device_name);
|
||||
|
||||
char topic_content[] = "{\"action\": \"publish_test\", \"count\": \"0\"}";
|
||||
PublishParams pub_params = DEFAULT_PUB_PARAMS;
|
||||
|
||||
pub_params.payload = topic_content;
|
||||
pub_params.payload_len = strlen(topic_content);
|
||||
/**
|
||||
* @brief QOS0
|
||||
*
|
||||
*/
|
||||
pub_params.qos = QOS0;
|
||||
ASSERT_GE(IOT_MQTT_Publish(client, topic_name, &pub_params), 0);
|
||||
|
||||
/**
|
||||
* @brief QOS1
|
||||
*
|
||||
*/
|
||||
pub_params.qos = QOS1;
|
||||
ASSERT_GE(IOT_MQTT_Publish(client, topic_name, &pub_params), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test clean session.
|
||||
*
|
||||
*/
|
||||
TEST_F(MqttClientTest, clean_session) {
|
||||
IOT_MQTT_Destroy(&client);
|
||||
|
||||
HAL_SleepMs(5000); // for iot hub can not connect twice in 5 s
|
||||
|
||||
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
|
||||
|
||||
char topic_name[MAX_SIZE_OF_CLOUD_TOPIC] = {0};
|
||||
ASSERT_EQ(HAL_GetDevInfo(reinterpret_cast<void *>(&device_info)), 0);
|
||||
HAL_Snprintf(topic_name, sizeof(topic_name), "%s/%s/data", device_info.product_id, device_info.device_name);
|
||||
|
||||
MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
|
||||
init_params.device_info = &device_info;
|
||||
init_params.clean_session = 0;
|
||||
|
||||
client = IOT_MQTT_Construct(&init_params);
|
||||
ASSERT_NE(client, nullptr);
|
||||
ASSERT_GE(IOT_MQTT_SubscribeSync(client, topic_name, &sub_params), 0);
|
||||
IOT_MQTT_Destroy(&client);
|
||||
|
||||
HAL_SleepMs(5000);
|
||||
|
||||
init_params.clean_session = 0;
|
||||
init_params.connect_when_construct = 0;
|
||||
init_params.default_subscribe = 1;
|
||||
|
||||
client = IOT_MQTT_Construct(&init_params);
|
||||
ASSERT_NE(client, nullptr);
|
||||
ASSERT_GE(IOT_MQTT_SubscribeSync(client, topic_name, &sub_params), 0);
|
||||
ASSERT_EQ(IOT_MQTT_Connect(client), 0);
|
||||
}
|
||||
|
||||
} // namespace mqtt_client_unittest
|
@@ -0,0 +1,11 @@
|
||||
file(GLOB src_ota_mqtt ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
|
||||
set(src_services ${src_services} ${src_ota_mqtt} PARENT_SCOPE)
|
||||
|
||||
file(GLOB src_ota_mqtt_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/ota_mqtt_sample.c)
|
||||
add_executable(ota_mqtt_sample ${src_ota_mqtt_sample})
|
||||
target_link_libraries(ota_mqtt_sample ${libsdk})
|
||||
|
||||
if( ${CONFIG_IOT_TEST} STREQUAL "ON")
|
||||
file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
|
||||
set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
|
||||
endif()
|
@@ -0,0 +1,266 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file broadcast_sample.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-07-18
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "qcloud_iot_common.h"
|
||||
|
||||
#include "utils_log.h"
|
||||
|
||||
/**
|
||||
* @brief MQTT event callback, @see MQTTEventHandleFun
|
||||
*
|
||||
* @param[in] client pointer to mqtt client
|
||||
* @param[in] handle_context context
|
||||
* @param[in] msg msg
|
||||
*/
|
||||
static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
|
||||
{
|
||||
MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
|
||||
uintptr_t packet_id = (uintptr_t)msg->msg;
|
||||
|
||||
switch (msg->event_type) {
|
||||
case MQTT_EVENT_UNDEF:
|
||||
Log_i("undefined event occur.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DISCONNECT:
|
||||
Log_i("MQTT disconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_RECONNECT:
|
||||
Log_i("MQTT reconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_RECEIVED:
|
||||
Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
|
||||
mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
|
||||
mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
|
||||
break;
|
||||
case MQTT_EVENT_SUBSCRIBE_SUCCESS:
|
||||
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
|
||||
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_NACK:
|
||||
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
|
||||
Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
|
||||
Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_NACK:
|
||||
Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_SUCCESS:
|
||||
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_TIMEOUT:
|
||||
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_NACK:
|
||||
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
default:
|
||||
Log_i("Should NOT arrive here.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Setup MQTT construct parameters.
|
||||
*
|
||||
* @param[in,out] initParams @see MQTTInitParams
|
||||
* @param[in] device_info @see DeviceInfo
|
||||
*/
|
||||
static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
|
||||
{
|
||||
init_params->device_info = device_info;
|
||||
init_params->command_timeout = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT;
|
||||
init_params->keep_alive_interval_ms = QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL;
|
||||
init_params->auto_connect_enable = 1;
|
||||
init_params->event_handle.h_fp = _mqtt_event_handler;
|
||||
init_params->event_handle.context = NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// OTA function
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
int _firmware_download(void *client, char *buf, int buf_len, UtilsJsonValue url, UtilsJsonValue md5sum,
|
||||
uint32_t file_size, const char *version)
|
||||
{
|
||||
Log_i("downloading firmware:url=%.*s|md5sum=%.*s|file_size=%u", url.value_len, url.value, md5sum.value_len,
|
||||
md5sum.value, file_size);
|
||||
IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_DOWNLOADING, 0, version);
|
||||
IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_DOWNLOADING, 50, version);
|
||||
IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_DOWNLOADING, 100, version);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _firmware_burn(void *client, char *buf, int buf_len, const char *version)
|
||||
{
|
||||
IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_UPGRADE_BEGIN, 0, version);
|
||||
IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_UPGRADE_SUCCESS, 0, version);
|
||||
IOT_OTA_ReportVersion(client, buf, buf_len, version);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// OTA callback
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void _update_firmware_callback(UtilsJsonValue version, UtilsJsonValue url, UtilsJsonValue md5sum, uint32_t file_size,
|
||||
void *usr_data)
|
||||
{
|
||||
char firmware_version[64] = {0};
|
||||
char buf[256];
|
||||
int buf_len = sizeof(buf);
|
||||
|
||||
Log_i("recv firmware: version=%.*s|url=%.*s|md5sum=%.*s|file_size=%u", version.value_len, version.value,
|
||||
url.value_len, url.value, md5sum.value_len, md5sum.value, file_size);
|
||||
strncpy(firmware_version, version.value, version.value_len);
|
||||
|
||||
int rc = _firmware_download(usr_data, buf, buf_len, url, md5sum, file_size, firmware_version);
|
||||
if (rc) {
|
||||
IOT_OTA_ReportProgress(usr_data, buf, buf_len, IOT_OTA_REPORT_TYPE_UPGRADE_FAIL, 0, firmware_version);
|
||||
return;
|
||||
}
|
||||
_firmware_burn(usr_data, buf, buf_len, firmware_version);
|
||||
}
|
||||
|
||||
void _report_version_reply_callback(int result_code, void *usr_data)
|
||||
{
|
||||
Log_i("recv code=%d", result_code);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Main
|
||||
// ----------------------------------------------------------------------------
|
||||
static int sg_main_exit = 0;
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void _main_exit(int sig)
|
||||
{
|
||||
Log_e("demo exit by signal:%d\n", sig);
|
||||
sg_main_exit = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#ifdef __linux__
|
||||
signal(SIGINT, _main_exit);
|
||||
#endif
|
||||
|
||||
int rc;
|
||||
|
||||
char buf[1024];
|
||||
|
||||
// init log level
|
||||
LogHandleFunc func = {0};
|
||||
func.log_malloc = HAL_Malloc;
|
||||
func.log_free = HAL_Free;
|
||||
func.log_get_current_time_str = HAL_Timer_Current;
|
||||
func.log_printf = HAL_Printf;
|
||||
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
|
||||
|
||||
DeviceInfo device_info;
|
||||
rc = HAL_GetDevInfo((void *)&device_info);
|
||||
if (rc) {
|
||||
Log_e("get device info failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// init connection
|
||||
MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
|
||||
_setup_connect_init_params(&init_params, &device_info);
|
||||
|
||||
// create MQTT client and connect with server
|
||||
void *client = IOT_MQTT_Construct(&init_params);
|
||||
if (client) {
|
||||
Log_i("Cloud Device Construct Success");
|
||||
} else {
|
||||
Log_e("MQTT Construct failed!");
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
IotOTAUpdateCallback ota_callback = {
|
||||
.update_firmware_callback = _update_firmware_callback,
|
||||
.report_version_reply_callback = _report_version_reply_callback,
|
||||
};
|
||||
rc = IOT_OTA_Init(client, ota_callback, client);
|
||||
if (rc) {
|
||||
Log_e("OTA init failed!, rc=%d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = IOT_OTA_ReportVersion(client, buf, sizeof(buf), QCLOUD_IOT_DEVICE_SDK_VERSION);
|
||||
if (rc) {
|
||||
Log_e("OTA report version failed!, rc=%d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
do {
|
||||
rc = IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT);
|
||||
switch (rc) {
|
||||
case QCLOUD_RET_SUCCESS:
|
||||
break;
|
||||
case QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT:
|
||||
continue;
|
||||
case QCLOUD_RET_MQTT_RECONNECTED:
|
||||
break;
|
||||
default:
|
||||
Log_e("Exit loop caused of errCode:%d", rc);
|
||||
goto exit;
|
||||
}
|
||||
} while (!sg_main_exit);
|
||||
exit:
|
||||
IOT_OTA_Deinit(client);
|
||||
rc = IOT_MQTT_Destroy(&client);
|
||||
utils_log_deinit();
|
||||
return rc;
|
||||
}
|
@@ -0,0 +1,310 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file ota_mqtt.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-10-18
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-10-18 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "qcloud_iot_ota.h"
|
||||
|
||||
/**
|
||||
* @brief Context of OTA update topic, callback and user data.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
IotOTAUpdateCallback callback;
|
||||
void * usr_data;
|
||||
} OTAUpdateContext;
|
||||
|
||||
/**
|
||||
* @brief OTA update type.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
OTA_UPDATE_TYPE_REPORT_VERSION_RSP = 0,
|
||||
OTA_UPDATE_TYPE_UPDATE_FIRMWARE,
|
||||
} OTAUpdateType;
|
||||
|
||||
/**
|
||||
* @brief Parse payload and callback.
|
||||
*
|
||||
* @param[in] type @see OTAUpdateType
|
||||
* @param[in] message message from cloud
|
||||
* @param[in] callback callback for user
|
||||
* @param[in,out] usr_data user data used in callback
|
||||
*/
|
||||
static void _parse_update_payload_and_callback(OTAUpdateType type, const MQTTMessage *message,
|
||||
const IotOTAUpdateCallback *callback, void *usr_data)
|
||||
{
|
||||
int rc, result_code;
|
||||
uint32_t file_size = 0;
|
||||
UtilsJsonValue version, md5sum, url, value_file_size, value_result_code;
|
||||
|
||||
// callback
|
||||
switch (type) {
|
||||
case OTA_UPDATE_TYPE_REPORT_VERSION_RSP:
|
||||
if (!callback->report_version_reply_callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
rc = utils_json_value_get("result_code", strlen("result_code"), message->payload_str, message->payload_len,
|
||||
&value_result_code);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
rc = utils_json_value_data_get(value_result_code, UTILS_JSON_VALUE_TYPE_INT32, &result_code);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
callback->report_version_reply_callback(result_code, usr_data);
|
||||
break;
|
||||
case OTA_UPDATE_TYPE_UPDATE_FIRMWARE:
|
||||
if (!callback->update_firmware_callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
rc = utils_json_value_get("version", strlen("version"), message->payload_str, message->payload_len,
|
||||
&version);
|
||||
rc |= utils_json_value_get("url", strlen("url"), message->payload_str, message->payload_len, &url);
|
||||
rc |= utils_json_value_get("md5sum", strlen("md5sum"), message->payload_str, message->payload_len, &md5sum);
|
||||
rc |= utils_json_value_get("file_size", strlen("file_size"), message->payload_str, message->payload_len,
|
||||
&value_file_size);
|
||||
rc |= utils_json_value_data_get(value_file_size, UTILS_JSON_VALUE_TYPE_UINT32, &file_size);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
callback->update_firmware_callback(version, url, md5sum, file_size, usr_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
error:
|
||||
Log_e("invalid format of payload!");
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Mqtt message callback for property topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] message message from topic
|
||||
* @param[in,out] usr_data pointer to @see OTAUpdateContext
|
||||
*/
|
||||
static void _ota_mqtt_message_callback(void *client, const MQTTMessage *message, void *usr_data)
|
||||
{
|
||||
const char *ota_update_str[] = {
|
||||
"report_version_rsp", // OTA_UPDATE_TYPE_REPORT_VERSION_RSP
|
||||
"update_firmware", // OTA_UPDATE_TYPE_UPDATE_FIRMWARE
|
||||
};
|
||||
|
||||
int rc, i = 0;
|
||||
|
||||
OTAUpdateContext *ota_update_context = (OTAUpdateContext *)usr_data;
|
||||
UtilsJsonValue update_type;
|
||||
|
||||
Log_d("receive ota message:%.*s", message->payload_len, message->payload_str);
|
||||
|
||||
rc = utils_json_value_get("type", strlen("type"), message->payload_str, message->payload_len, &update_type);
|
||||
if (rc) {
|
||||
Log_e("invalid ota message!");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = OTA_UPDATE_TYPE_REPORT_VERSION_RSP; i <= OTA_UPDATE_TYPE_UPDATE_FIRMWARE; i++) {
|
||||
if (!strncmp(update_type.value, ota_update_str[i], update_type.value_len)) {
|
||||
Log_d("callback ota message!");
|
||||
_parse_update_payload_and_callback(i, message, &ota_update_context->callback, ota_update_context->usr_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if topic already subscribed, if not then subscribe.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param callback @see IotOTAUpdateCallback
|
||||
* @param usr_data usr data in callback
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _ota_mqtt_update_topic_check_and_sub(void *client, IotOTAUpdateCallback callback, void *usr_data)
|
||||
{
|
||||
int rc = 0;
|
||||
char ota_result_topic[MAX_SIZE_OF_CLOUD_TOPIC];
|
||||
|
||||
HAL_Snprintf(ota_result_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$ota/update/%s/%s",
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
|
||||
|
||||
OTAUpdateContext *ota_update_context = (OTAUpdateContext *)HAL_Malloc(sizeof(OTAUpdateContext));
|
||||
if (!ota_update_context) {
|
||||
return QCLOUD_ERR_MALLOC;
|
||||
}
|
||||
ota_update_context->callback = callback;
|
||||
ota_update_context->usr_data = usr_data;
|
||||
|
||||
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
|
||||
sub_params.on_message_handler = _ota_mqtt_message_callback;
|
||||
sub_params.qos = QOS1;
|
||||
sub_params.user_data = ota_update_context;
|
||||
sub_params.user_data_free = HAL_Free;
|
||||
|
||||
rc = IOT_MQTT_SubscribeSync(client, ota_result_topic, &sub_params);
|
||||
if (rc) {
|
||||
HAL_Free(ota_update_context);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
static int _ota_mqtt_update_topic_unsubscribe(void *client)
|
||||
{
|
||||
char ota_result_topic[MAX_SIZE_OF_CLOUD_TOPIC];
|
||||
HAL_Snprintf(ota_result_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$ota/update/%s/%s",
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
|
||||
return IOT_MQTT_Unsubscribe(client, ota_result_topic);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish to OTA report topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] qos @see QoS
|
||||
* @param[in] payload payload of mqtt packet
|
||||
* @param[in] payload_len payload len
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
static int _ota_mqtt_publish(void *client, QoS qos, const char *payload, int payload_len)
|
||||
{
|
||||
char ota_report_topic[MAX_SIZE_OF_CLOUD_TOPIC];
|
||||
HAL_Snprintf(ota_report_topic, sizeof(ota_report_topic), "$ota/report/%s/%s",
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
|
||||
|
||||
PublishParams pub_params = DEFAULT_PUB_PARAMS;
|
||||
pub_params.qos = qos;
|
||||
pub_params.payload = (void *)payload;
|
||||
pub_params.payload_len = payload_len;
|
||||
return IOT_MQTT_Publish(client, ota_report_topic, &pub_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief OTA init, subscribe update topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] callback @see IotOTAUpdateCallback
|
||||
* @param[in] usr_data usr data used in callback
|
||||
* @return 0 for success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int IOT_OTA_Init(void *client, IotOTAUpdateCallback callback, void *usr_data)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
return _ota_mqtt_update_topic_check_and_sub(client, callback, usr_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief OTA deinit, unsubscribe update topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
*/
|
||||
void IOT_OTA_Deinit(void *client)
|
||||
{
|
||||
POINTER_SANITY_CHECK_RTN(client);
|
||||
_ota_mqtt_update_topic_unsubscribe(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Report upgrade progress.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[out] buf publish message buffer
|
||||
* @param[in] buf_len buffer len
|
||||
* @param[in] report_type @see IotOTAReportType
|
||||
* @param[in] progress progress using in IOT_OTA_REPORT_TYPE_DOWNLOADING
|
||||
* @param[in] version update firmware version
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int IOT_OTA_ReportProgress(void *client, char *buf, int buf_len, IotOTAReportType report_type, int progress,
|
||||
const char *version)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
NUMBERIC_SANITY_CHECK(buf_len, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(version, QCLOUD_ERR_INVAL);
|
||||
|
||||
/**
|
||||
* @brief order @see IotOTAReportType
|
||||
*
|
||||
*/
|
||||
const char *ota_state[] = {"downloading", "burning", "done", "fail", "fail", "fail", "fail", "fail"};
|
||||
int result_code[] = {0, 0, 0, -1, -2, -3, -4, -5};
|
||||
const char *result_msg[] = {"", "", "", "timeout", "file not exit", "auth fail", "md5 not match", "upgrade fail"};
|
||||
|
||||
int len;
|
||||
switch (report_type) {
|
||||
case IOT_OTA_REPORT_TYPE_DOWNLOADING:
|
||||
len = HAL_Snprintf(buf, buf_len,
|
||||
"{\"type\":\"report_progress\",\"report\":{\"progress\":{\"state\":\"%s\",\"percent\":"
|
||||
"\"%d\",\"result_code\":\"%d\",\"result_msg\":\"\"},\"version\":\"%s\"}}",
|
||||
ota_state[report_type], progress, result_code[report_type], version);
|
||||
break;
|
||||
case IOT_OTA_REPORT_TYPE_UPGRADE_BEGIN:
|
||||
case IOT_OTA_REPORT_TYPE_UPGRADE_SUCCESS:
|
||||
case IOT_OTA_REPORT_TYPE_DOWNLOAD_TIMEOUT:
|
||||
case IOT_OTA_REPORT_TYPE_FILE_NOT_EXIST:
|
||||
case IOT_OTA_REPORT_TYPE_AUTH_FAIL:
|
||||
case IOT_OTA_REPORT_TYPE_MD5_NOT_MATCH:
|
||||
case IOT_OTA_REPORT_TYPE_UPGRADE_FAIL:
|
||||
len = HAL_Snprintf(buf, buf_len,
|
||||
"{\"type\":\"report_progress\",\"report\":{\"progress\":{\"state\":\"%s\",\"result_"
|
||||
"code\":\"%d\",\"result_msg\":\"%s\"},\"version\":\"%s\"}}",
|
||||
ota_state[report_type], result_code[report_type], result_msg[report_type], version);
|
||||
break;
|
||||
default:
|
||||
return QCLOUD_ERR_INVAL;
|
||||
}
|
||||
return _ota_mqtt_publish(client, QOS0, buf, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Report current firmware version.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[out] buf publish message buffer
|
||||
* @param[in] buf_len buffer len
|
||||
* @param[in] version current firmware version
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int IOT_OTA_ReportVersion(void *client, char *buf, int buf_len, const char *version)
|
||||
{
|
||||
int len = HAL_Snprintf(buf, buf_len, "{\"type\":\"report_version\",\"report\":{\"version\":\"%s\"}}",
|
||||
STRING_PTR_PRINT_SANITY_CHECK(version));
|
||||
return _ota_mqtt_publish(client, QOS0, buf, len);
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 120
|
||||
DerivePointerAlignment: true
|
||||
PointerAlignment: Left
|
||||
SortIncludes: true
|
||||
IncludeBlocks: Preserve
|
||||
IndentPPDirectives: AfterHash
|
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file test_ota_mqtt.cc
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-10-19
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-10-19 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "mqtt_client_test.h"
|
||||
#include "qcloud_iot_common.h"
|
||||
|
||||
namespace mqtt_client_unittest {
|
||||
|
||||
void _update_firmware_callback(UtilsJsonValue version, UtilsJsonValue url, UtilsJsonValue md5sum, uint32_t file_size,
|
||||
void *usr_data) {
|
||||
Log_i("recv firmware: version=%.*s|url=%.*s|md5sum=%.*s|file_size=%u", version.value_len, version.value,
|
||||
url.value_len, url.value, md5sum.value_len, md5sum.value, file_size);
|
||||
}
|
||||
|
||||
void _report_version_reply_callback(int result_code, void *usr_data) { Log_i("recv code=%d", result_code); }
|
||||
|
||||
/**
|
||||
* @brief Test ota mqtt.
|
||||
*
|
||||
*/
|
||||
TEST_F(MqttClientTest, ota_mqtt) {
|
||||
char buf[256];
|
||||
int buf_len = sizeof(buf);
|
||||
const char *version = "1.0.0";
|
||||
IotOTAUpdateCallback ota_callback = {
|
||||
.update_firmware_callback = _update_firmware_callback,
|
||||
.report_version_reply_callback = _report_version_reply_callback,
|
||||
};
|
||||
ASSERT_EQ(IOT_OTA_Init(client, ota_callback, client), 0);
|
||||
ASSERT_GE(IOT_OTA_ReportVersion(client, buf, buf_len, version), 0);
|
||||
ASSERT_GE(IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_DOWNLOADING, 0, version), 0);
|
||||
ASSERT_GE(IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_DOWNLOADING, 100, version), 0);
|
||||
ASSERT_GE(IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_UPGRADE_BEGIN, 0, version), 0);
|
||||
ASSERT_GE(IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_UPGRADE_SUCCESS, 0, version), 0);
|
||||
ASSERT_GE(IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_DOWNLOAD_TIMEOUT, 0, version), 0);
|
||||
ASSERT_GE(IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_FILE_NOT_EXIST, 0, version), 0);
|
||||
ASSERT_GE(IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_AUTH_FAIL, 0, version), 0);
|
||||
ASSERT_GE(IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_MD5_NOT_MATCH, 0, version), 0);
|
||||
ASSERT_GE(IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_UPGRADE_FAIL, 0, version), 0);
|
||||
IOT_OTA_Deinit(client);
|
||||
}
|
||||
|
||||
} // namespace mqtt_client_unittest
|
@@ -0,0 +1,11 @@
|
||||
file(GLOB src_system_mqtt ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
|
||||
set(src_services ${src_services} ${src_system_mqtt} PARENT_SCOPE)
|
||||
|
||||
file(GLOB src_system_mqtt_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/system_mqtt_sample.c)
|
||||
add_executable(system_mqtt_sample ${src_system_mqtt_sample})
|
||||
target_link_libraries(system_mqtt_sample ${libsdk})
|
||||
|
||||
if( ${CONFIG_IOT_TEST} STREQUAL "ON")
|
||||
file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
|
||||
set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
|
||||
endif()
|
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file broadcast_sample.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-07-18
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2022-01-26 <td>1.1 <td>hubert <td>add serverip
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "qcloud_iot_common.h"
|
||||
|
||||
#include "utils_log.h"
|
||||
|
||||
/**
|
||||
* @brief MQTT event callback, @see MQTTEventHandleFun
|
||||
*
|
||||
* @param[in] client pointer to mqtt client
|
||||
* @param[in] handle_context context
|
||||
* @param[in] msg msg
|
||||
*/
|
||||
static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
|
||||
{
|
||||
MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
|
||||
uintptr_t packet_id = (uintptr_t)msg->msg;
|
||||
|
||||
switch (msg->event_type) {
|
||||
case MQTT_EVENT_UNDEF:
|
||||
Log_i("undefined event occur.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DISCONNECT:
|
||||
Log_i("MQTT disconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_RECONNECT:
|
||||
Log_i("MQTT reconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_RECEIVED:
|
||||
Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
|
||||
mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
|
||||
mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
|
||||
break;
|
||||
case MQTT_EVENT_SUBSCRIBE_SUCCESS:
|
||||
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
|
||||
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_NACK:
|
||||
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
|
||||
Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
|
||||
Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_NACK:
|
||||
Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_SUCCESS:
|
||||
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_TIMEOUT:
|
||||
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_NACK:
|
||||
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
default:
|
||||
Log_i("Should NOT arrive here.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Setup MQTT construct parameters.
|
||||
*
|
||||
* @param[in,out] initParams @see MQTTInitParams
|
||||
* @param[in] device_info @see DeviceInfo
|
||||
*/
|
||||
static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
|
||||
{
|
||||
init_params->device_info = device_info;
|
||||
init_params->command_timeout = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT;
|
||||
init_params->keep_alive_interval_ms = QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL;
|
||||
init_params->auto_connect_enable = 1;
|
||||
init_params->event_handle.h_fp = _mqtt_event_handler;
|
||||
init_params->event_handle.context = NULL;
|
||||
init_params->host = QCLOUD_IOT_MQTT_DIRECT_DOMAIN;
|
||||
init_params->backup_host = "your_backup_host_domain.com";
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Main
|
||||
// ----------------------------------------------------------------------------
|
||||
static int sg_main_exit = 0;
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void _main_exit(int sig)
|
||||
{
|
||||
Log_e("demo exit by signal:%d\n", sig);
|
||||
sg_main_exit = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#ifdef __linux__
|
||||
signal(SIGINT, _main_exit);
|
||||
#endif
|
||||
|
||||
int rc;
|
||||
|
||||
// init log level
|
||||
LogHandleFunc func = {0};
|
||||
|
||||
func.log_malloc = HAL_Malloc;
|
||||
func.log_free = HAL_Free;
|
||||
func.log_get_current_time_str = HAL_Timer_Current;
|
||||
func.log_printf = HAL_Printf;
|
||||
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
|
||||
|
||||
DeviceInfo device_info;
|
||||
|
||||
rc = HAL_GetDevInfo((void *)&device_info);
|
||||
if (rc) {
|
||||
Log_e("get device info failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// init connection
|
||||
MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
|
||||
_setup_connect_init_params(&init_params, &device_info);
|
||||
|
||||
// create MQTT client and connect with server
|
||||
void *client = IOT_MQTT_Construct(&init_params);
|
||||
if (client) {
|
||||
Log_i("Cloud Device Construct Success");
|
||||
} else {
|
||||
Log_e("MQTT Construct failed!");
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
uint32_t time_stamp = 0;
|
||||
|
||||
rc = IOT_Sys_GetTime(client, &time_stamp);
|
||||
if (rc) {
|
||||
Log_e("Get system time failed!, rc=%d", rc);
|
||||
} else {
|
||||
Log_d("Get system time success, time %d", time_stamp);
|
||||
}
|
||||
|
||||
rc = IOT_Sys_SyncNTPTime(client);
|
||||
if (rc) {
|
||||
Log_e("Get system ntp time failed!, rc=%d", rc);
|
||||
}
|
||||
|
||||
char server_ip[256] = {0};
|
||||
rc = IOT_Sys_GetServerIp(client, server_ip);
|
||||
if (rc) {
|
||||
Log_e("Get system server ip failed!, rc=%d", rc);
|
||||
} else {
|
||||
Log_i("Get system server ip list success. server ip: %s", server_ip);
|
||||
}
|
||||
|
||||
rc = IOT_MQTT_Destroy(&client);
|
||||
|
||||
utils_log_deinit();
|
||||
return rc;
|
||||
}
|
@@ -0,0 +1,354 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file system_mqtt.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-07-24
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-07-24 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2022-01-26 <td>1.1 <td>hubert <td>add serverip
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "qcloud_iot_system.h"
|
||||
|
||||
#define MAX_SYSTEM_INFO_SIZE 256
|
||||
|
||||
#define IP_MAX_NUM 10
|
||||
#define ONE_IP_MAX_LEN 24
|
||||
#define IP_LIST_MAX_LEN (IP_MAX_NUM * ONE_IP_MAX_LEN)
|
||||
|
||||
#define SYS_MQTT_GET_RESOURCE_TIME "{\"type\":\"get\",\"resource\":[\"time\"]}"
|
||||
#define SYS_MQTT_GET_RESOURCE_SERVERIP "{\"type\":\"get\",\"resource\":[\"serverip\"]}"
|
||||
|
||||
typedef enum {
|
||||
RESOURCE_TIME = 0,
|
||||
RESOURCE_IP,
|
||||
} SysResourceType;
|
||||
|
||||
/**
|
||||
* @brief data structure for system time service
|
||||
*/
|
||||
typedef struct {
|
||||
bool result_recv_ok;
|
||||
// time
|
||||
struct {
|
||||
uint32_t time;
|
||||
uint64_t ntptime1;
|
||||
uint64_t ntptime2;
|
||||
} time;
|
||||
// server ip
|
||||
struct {
|
||||
char ip_list[IP_LIST_MAX_LEN];
|
||||
int ip_len;
|
||||
} server_ip;
|
||||
SysResourceType wait_type;
|
||||
uint64_t result_recv_time;
|
||||
} SystemResultInfo;
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] message message from topic
|
||||
* @param[in,out] usr_data pointer to @see SystemResultInfo
|
||||
*/
|
||||
static void _system_mqtt_message_callback(void *client, const MQTTMessage *message, void *usr_data)
|
||||
{
|
||||
int rc = 0;
|
||||
SystemResultInfo *result = (SystemResultInfo *)usr_data;
|
||||
UtilsJsonValue value;
|
||||
|
||||
Log_d("Receive system result message:%.*s", message->payload_len, message->payload_str);
|
||||
if (strstr(message->payload_str, "\"time\"")) {
|
||||
// get time
|
||||
rc = utils_json_value_get("time", strlen("time"), message->payload_str, message->payload_len, &value);
|
||||
if (rc) {
|
||||
return;
|
||||
}
|
||||
rc = utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_UINT32, &result->time.time);
|
||||
if (rc) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get ntptime1
|
||||
rc = utils_json_value_get("ntptime1", strlen("ntptime1"), message->payload_str, message->payload_len, &value);
|
||||
if (rc) {
|
||||
return;
|
||||
}
|
||||
rc = utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_UINT64, &result->time.ntptime1);
|
||||
if (rc) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get ntptime2
|
||||
rc = utils_json_value_get("ntptime2", strlen("ntptime2"), message->payload_str, message->payload_len, &value);
|
||||
if (rc) {
|
||||
return;
|
||||
}
|
||||
rc = utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_UINT64, &result->time.ntptime2);
|
||||
if (rc) {
|
||||
result->time.ntptime1 = result->time.time * 1000;
|
||||
result->time.ntptime2 = result->time.time * 1000;
|
||||
}
|
||||
}
|
||||
if (strstr(message->payload_str, "\"serverip\"")) {
|
||||
// get serverip
|
||||
rc = utils_json_value_get("serverip", strlen("serverip"), message->payload_str, message->payload_len, &value);
|
||||
if (rc) {
|
||||
return;
|
||||
}
|
||||
result->server_ip.ip_len = value.value_len > IP_LIST_MAX_LEN ? IP_LIST_MAX_LEN : value.value_len;
|
||||
memset(result->server_ip.ip_list, 0, IP_LIST_MAX_LEN);
|
||||
strncpy(result->server_ip.ip_list, value.value, result->server_ip.ip_len);
|
||||
}
|
||||
|
||||
result->result_recv_time = HAL_Timer_CurrentMs();
|
||||
result->result_recv_ok = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check and subscribe system result topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _system_mqtt_result_topic_check_and_sub(void *client, const char *topic)
|
||||
{
|
||||
int rc = 0;
|
||||
// 1. check topic is sub ready
|
||||
if (IOT_MQTT_IsSubReady(client, topic)) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
// 2. subscribe
|
||||
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
|
||||
SystemResultInfo *system_info = (SystemResultInfo *)HAL_Malloc(sizeof(SystemResultInfo));
|
||||
if (!system_info) {
|
||||
return QCLOUD_ERR_MALLOC;
|
||||
}
|
||||
|
||||
system_info->result_recv_ok = false;
|
||||
|
||||
sub_params.on_message_handler = _system_mqtt_message_callback;
|
||||
sub_params.qos = QOS1;
|
||||
sub_params.user_data = system_info;
|
||||
sub_params.user_data_free = HAL_Free;
|
||||
|
||||
rc = IOT_MQTT_SubscribeSync(client, topic, &sub_params);
|
||||
if (rc) {
|
||||
HAL_Free(system_info);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish get time message to system topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] topic system result topic
|
||||
* @return >= 0 for success
|
||||
*/
|
||||
static int _system_mqtt_get_resource_publish(void *client, const char *topic, SysResourceType type)
|
||||
{
|
||||
char pub_topic_name[MAX_SIZE_OF_CLOUD_TOPIC];
|
||||
HAL_Snprintf(pub_topic_name, sizeof(pub_topic_name), "$sys/operation/%s/%s",
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
|
||||
|
||||
SystemResultInfo *result = IOT_MQTT_GetSubUsrData(client, topic);
|
||||
if (!result) {
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
result->wait_type = type;
|
||||
result->result_recv_ok = false;
|
||||
|
||||
PublishParams pub_params = DEFAULT_PUB_PARAMS;
|
||||
pub_params.qos = QOS0;
|
||||
switch (type) {
|
||||
case RESOURCE_TIME:
|
||||
pub_params.payload = SYS_MQTT_GET_RESOURCE_TIME;
|
||||
pub_params.payload_len = strlen(SYS_MQTT_GET_RESOURCE_TIME);
|
||||
break;
|
||||
case RESOURCE_IP:
|
||||
pub_params.payload = SYS_MQTT_GET_RESOURCE_SERVERIP;
|
||||
pub_params.payload_len = strlen(SYS_MQTT_GET_RESOURCE_SERVERIP);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return IOT_MQTT_Publish(client, pub_topic_name, &pub_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wait system result, timeout @see QCLOUD_IOT_MQTT_WAIT_ACK_TIMEOUT
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] topic system result topic
|
||||
* @param[out] time time from system result topic
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
static int _system_mqtt_result_wait(void *client, const char *topic, void *user_data)
|
||||
{
|
||||
int rc = 0;
|
||||
Timer wait_result_timer;
|
||||
SystemResultInfo *result;
|
||||
|
||||
HAL_Timer_CountdownMs(&wait_result_timer, QCLOUD_IOT_MQTT_WAIT_ACK_TIMEOUT);
|
||||
|
||||
result = IOT_MQTT_GetSubUsrData(client, topic);
|
||||
if (!result) {
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
result->result_recv_ok = false;
|
||||
while (!rc && !HAL_Timer_Expired(&wait_result_timer)) {
|
||||
rc = IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT);
|
||||
if (result->result_recv_ok) {
|
||||
switch (result->wait_type) {
|
||||
case RESOURCE_TIME:
|
||||
*((uint32_t *)user_data) = result->time.time;
|
||||
break;
|
||||
case RESOURCE_IP:
|
||||
strncpy(user_data, result->server_ip.ip_list, result->server_ip.ip_len);
|
||||
result->server_ip.ip_len = 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
}
|
||||
return QCLOUD_ERR_MQTT_REQUEST_TIMEOUT;
|
||||
}
|
||||
|
||||
static int _system_mqtt_get_resource(void *client, SysResourceType type, void *user_data)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(user_data, QCLOUD_ERR_INVAL);
|
||||
|
||||
int rc = 0;
|
||||
char system_result_topic[MAX_SIZE_OF_CLOUD_TOPIC];
|
||||
|
||||
HAL_Snprintf(system_result_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$sys/operation/result/%s/%s",
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
|
||||
|
||||
rc = _system_mqtt_result_topic_check_and_sub(client, system_result_topic);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = _system_mqtt_get_resource_publish(client, system_result_topic, type);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return _system_mqtt_result_wait(client, system_result_topic, user_data);
|
||||
}
|
||||
/**
|
||||
* @brief Get time from system result topic
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[out] time time from system result topic
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int IOT_Sys_GetTime(void *client, uint32_t *time)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(time, QCLOUD_ERR_INVAL);
|
||||
return _system_mqtt_get_resource(client, RESOURCE_TIME, time);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get ntp time from system result topic and set to system.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int IOT_Sys_SyncNTPTime(void *client)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
|
||||
int rc = 0;
|
||||
uint32_t time_get = 0;
|
||||
uint64_t local_publish_before, local_ntptime = 0;
|
||||
|
||||
SystemResultInfo *result = NULL;
|
||||
|
||||
char system_result_topic[MAX_SIZE_OF_CLOUD_TOPIC];
|
||||
|
||||
HAL_Snprintf(system_result_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$sys/operation/result/%s/%s",
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
|
||||
|
||||
rc = _system_mqtt_result_topic_check_and_sub(client, system_result_topic);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
// prepare for publish
|
||||
result = IOT_MQTT_GetSubUsrData(client, system_result_topic);
|
||||
if (!result) {
|
||||
return -1;
|
||||
}
|
||||
local_publish_before = HAL_Timer_CurrentMs();
|
||||
|
||||
// publish and wait
|
||||
rc = _system_mqtt_get_resource_publish(client, system_result_topic, RESOURCE_TIME);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = _system_mqtt_result_wait(client, system_result_topic, &time_get);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
local_ntptime =
|
||||
(result->time.ntptime2 + result->time.ntptime1 + result->result_recv_time - local_publish_before) / 2;
|
||||
|
||||
rc = HAL_Timer_SetSystimeSec(time_get);
|
||||
if (rc) {
|
||||
Log_e("set systime sec failed, timestamp %d sec, please check permission or other ret:%d", time_get, rc);
|
||||
} else {
|
||||
Log_i("set systime sec success, timestamp %d sec", time_get);
|
||||
}
|
||||
|
||||
rc = HAL_Timer_SetSystimeMs(local_ntptime);
|
||||
if (rc) {
|
||||
Log_e("set systime ms failed, timestamp %lld, please check permission or other ret :%d", local_ntptime, rc);
|
||||
} else {
|
||||
Log_i("set systime ms success, timestamp %lld ms", local_ntptime);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get serverip from system result topic
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[out] server_ip serverip from system result topic
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int IOT_Sys_GetServerIp(void *client, char *server_ip)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(server_ip, QCLOUD_ERR_INVAL);
|
||||
|
||||
return _system_mqtt_get_resource(client, RESOURCE_IP, server_ip);
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 120
|
||||
DerivePointerAlignment: true
|
||||
PointerAlignment: Left
|
||||
SortIncludes: true
|
||||
IncludeBlocks: Preserve
|
||||
IndentPPDirectives: AfterHash
|
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file test_broadcast.cc
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-07-18
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
|
||||
* <tr><td>2022-01-26 <td>1.1 <td>hubert <td>add serverip test
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "mqtt_client_test.h"
|
||||
#include "qcloud_iot_common.h"
|
||||
|
||||
namespace mqtt_client_unittest {
|
||||
|
||||
/**
|
||||
* @brief Test system mqtt.
|
||||
*
|
||||
*/
|
||||
TEST_F(MqttClientTest, system_mqtt) {
|
||||
uint32_t time_stamp = 0;
|
||||
char server_ip[256] = {0};
|
||||
ASSERT_EQ(IOT_Sys_GetTime(client, &time_stamp), 0);
|
||||
ASSERT_EQ(IOT_Sys_SyncNTPTime(client), 0);
|
||||
ASSERT_EQ(IOT_Sys_GetServerIp(client, server_ip), 0);
|
||||
}
|
||||
|
||||
} // namespace mqtt_client_unittest
|
@@ -0,0 +1,14 @@
|
||||
file(GLOB src_system_mqtt ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
|
||||
set(inc_system_mqtt ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
|
||||
|
||||
set(src_services ${src_services} ${src_system_mqtt} PARENT_SCOPE)
|
||||
set(inc_services ${inc_services} ${inc_system_mqtt} PARENT_SCOPE)
|
||||
|
||||
file(GLOB src_data_template_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/data_template_sample.c)
|
||||
add_executable(data_template_sample ${src_data_template_sample})
|
||||
target_link_libraries(data_template_sample ${libsdk})
|
||||
|
||||
if( ${CONFIG_IOT_TEST} STREQUAL "ON")
|
||||
file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
|
||||
set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
|
||||
endif()
|
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file data_template.h
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-08-23
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-08-23 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_EXPLORER_DATA_TEMPLATE_INC_DATA_TEMPLATE_H_
|
||||
#define IOT_HUB_DEVICE_C_SDK_SERVICES_EXPLORER_DATA_TEMPLATE_INC_DATA_TEMPLATE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "qcloud_iot_common.h"
|
||||
#include "qcloud_iot_data_template.h"
|
||||
|
||||
/**************************************************************************************
|
||||
* common
|
||||
**************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Type of data template(property/event/action).
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
DATA_TEMPLATE_TYPE_PROPERTY = 0,
|
||||
DATA_TEMPLATE_TYPE_EVENT,
|
||||
DATA_TEMPLATE_TYPE_ACTION,
|
||||
} DataTemplateType;
|
||||
|
||||
/**
|
||||
* @brief Context of data template, callback and user data.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
union {
|
||||
PropertyMessageCallback property_callback;
|
||||
EventMessageCallback event_callback;
|
||||
ActionMessageCallback action_callback;
|
||||
};
|
||||
void *usr_data;
|
||||
} DataTemplateContext;
|
||||
|
||||
/**
|
||||
* @brief Check if topic already subscribed, if not then subscribe.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] type @see DataTemplateType
|
||||
* @param[in] on_message_handler message handle of topic
|
||||
* @param[in] context @see DataTemplateContext
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int data_template_topic_check_and_sub(void *client, DataTemplateType type, OnMessageHandler on_message_handler,
|
||||
DataTemplateContext context);
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe data template topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] type @see DataTemplateType
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int data_template_topic_unsubscribe(void *client, DataTemplateType type);
|
||||
|
||||
/**
|
||||
* @brief Publish to data template topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] type @see DataTemplateType
|
||||
* @param[in] qos @see QoS
|
||||
* @param[in] payload payload of mqtt packet
|
||||
* @param[in] payload_len payload len
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int data_template_publish(void *client, DataTemplateType type, QoS qos, const char *payload, int payload_len);
|
||||
|
||||
/**************************************************************************************
|
||||
* property
|
||||
**************************************************************************************/
|
||||
|
||||
typedef enum {
|
||||
PROPERTY_UP_METHOD_TYPE_REPORT = 0,
|
||||
PROPERTY_UP_METHOD_TYPE_REPORT_INFO,
|
||||
PROPERTY_UP_METHOD_TYPE_GET_STATUS,
|
||||
PROPERTY_UP_METHOD_TYPE_CLEAR_CONTROL,
|
||||
PROPERTY_UP_METHOD_TYPE_CONTROL_REPLY,
|
||||
} PropertyUpMethodType;
|
||||
|
||||
typedef union {
|
||||
const char *json;
|
||||
int code;
|
||||
struct {
|
||||
int code;
|
||||
UtilsJsonValue client_token;
|
||||
} control_reply;
|
||||
} PropertyPublishParams;
|
||||
|
||||
/**
|
||||
* @brief Mqtt message callback for property topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] message message from topic
|
||||
* @param[in,out] usr_data pointer to @see DataTemplateContext
|
||||
*/
|
||||
void data_template_property_message_handler(void *client, const MQTTMessage *message, void *usr_data);
|
||||
|
||||
/**
|
||||
* @brief Publish message to property topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] publish_type @see PropertyUpMethodType
|
||||
* @param[out] buf publish message buffer
|
||||
* @param[in] buf_len buffer len
|
||||
* @param[in] params @see PropertyPublishParams
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int data_template_property_publish(void *client, PropertyUpMethodType publish_type, char *buf, int buf_len,
|
||||
PropertyPublishParams params);
|
||||
|
||||
/**************************************************************************************
|
||||
* event
|
||||
**************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Mqtt message callback for event topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] message message from topic
|
||||
* @param[in,out] usr_data pointer to @see DataTemplateContext
|
||||
*/
|
||||
void data_template_event_message_handler(void *client, const MQTTMessage *message, void *usr_data);
|
||||
|
||||
/**
|
||||
* @brief Publish message to event topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[out] buf publish message buffer
|
||||
* @param[in] buf_len buffer len
|
||||
* @param[in] data @see IotDataTemplateEventData
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int data_template_event_reply_publish(void *client, char *buf, int buf_len, IotDataTemplateEventData data);
|
||||
|
||||
/**************************************************************************************
|
||||
* action
|
||||
**************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Mqtt message callback for action topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] message message from topic
|
||||
* @param[in,out] usr_data pointer to @see DataTemplateContext
|
||||
*/
|
||||
void data_template_action_message_handler(void *client, const MQTTMessage *message, void *usr_data);
|
||||
|
||||
/**
|
||||
* @brief Publish message to event topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[out] buf publish message buffer
|
||||
* @param[in] buf_len buffer len
|
||||
* @param[in] reply @see IotDataTemplateActionReply
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int data_template_action_reply_publish(void *client, char *buf, int buf_len, IotDataTemplateActionReply reply);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_EXPLORER_DATA_TEMPLATE_INC_DATA_TEMPLATE_H_
|
@@ -0,0 +1,314 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file data_template_sample.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-09-27
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-09-27 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "qcloud_iot_common.h"
|
||||
#include "qcloud_iot_explorer.h"
|
||||
|
||||
#include "utils_log.h"
|
||||
|
||||
/**
|
||||
* @brief MQTT event callback, @see MQTTEventHandleFun
|
||||
*
|
||||
* @param[in] client pointer to mqtt client
|
||||
* @param[in] handle_context context
|
||||
* @param[in] msg msg
|
||||
*/
|
||||
static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
|
||||
{
|
||||
MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
|
||||
uintptr_t packet_id = (uintptr_t)msg->msg;
|
||||
|
||||
switch (msg->event_type) {
|
||||
case MQTT_EVENT_UNDEF:
|
||||
Log_i("undefined event occur.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DISCONNECT:
|
||||
Log_i("MQTT disconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_RECONNECT:
|
||||
Log_i("MQTT reconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_RECEIVED:
|
||||
Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
|
||||
mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
|
||||
mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_SUCCESS:
|
||||
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
|
||||
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_NACK:
|
||||
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
|
||||
Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
|
||||
Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_NACK:
|
||||
Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_SUCCESS:
|
||||
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_TIMEOUT:
|
||||
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_NACK:
|
||||
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
default:
|
||||
Log_i("Should NOT arrive here.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Setup MQTT construct parameters.
|
||||
*
|
||||
* @param[in,out] initParams @see MQTTInitParams
|
||||
* @param[in] device_info @see DeviceInfo
|
||||
*/
|
||||
static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
|
||||
{
|
||||
init_params->device_info = device_info;
|
||||
init_params->command_timeout = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT;
|
||||
init_params->keep_alive_interval_ms = QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL;
|
||||
init_params->auto_connect_enable = 1;
|
||||
init_params->event_handle.h_fp = _mqtt_event_handler;
|
||||
init_params->event_handle.context = NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Data template callback
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static void _method_control_callback(UtilsJsonValue client_token, UtilsJsonValue params, void *usr_data)
|
||||
{
|
||||
char buf[256];
|
||||
Log_i("recv msg[%.*s]: params=%.*s", client_token.value_len, client_token.value, params.value_len, params.value);
|
||||
IOT_DataTemplate_PropertyControlReply(usr_data, buf, sizeof(buf), 0, client_token);
|
||||
}
|
||||
|
||||
static void _method_report_reply_callback(UtilsJsonValue client_token, int code, void *usr_data)
|
||||
{
|
||||
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
|
||||
}
|
||||
|
||||
static void _method_get_status_reply_callback(UtilsJsonValue client_token, int code, UtilsJsonValue reported,
|
||||
UtilsJsonValue control, void *usr_data)
|
||||
{
|
||||
char buf[256];
|
||||
Log_i("recv msg[%.*s]: code=%d|reported=%.*s|control=%.*s", client_token.value_len, client_token.value, code,
|
||||
reported.value_len, STRING_PTR_PRINT_SANITY_CHECK(reported.value), control.value_len,
|
||||
STRING_PTR_PRINT_SANITY_CHECK(control.value));
|
||||
IOT_DataTemplate_PropertyClearControl(usr_data, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static void _method_report_info_reply_callback(UtilsJsonValue client_token, int code, void *usr_data)
|
||||
{
|
||||
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
|
||||
}
|
||||
|
||||
static void _method_clear_control_reply_callback(UtilsJsonValue client_token, int code, void *usr_data)
|
||||
{
|
||||
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
|
||||
}
|
||||
|
||||
static void _method_event_reply_callback(UtilsJsonValue client_token, int code, void *usr_data)
|
||||
{
|
||||
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
|
||||
}
|
||||
|
||||
static void _method_action_callback(UtilsJsonValue client_token, UtilsJsonValue action_id, UtilsJsonValue params,
|
||||
void *usr_data)
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
Log_i("recv msg[%.*s]: action_id=%.*s|params=%.*s", client_token.value_len, client_token.value, action_id.value_len,
|
||||
action_id.value, params.value_len, params.value);
|
||||
IotDataTemplateActionReply reply = {
|
||||
.code = 0,
|
||||
.client_token = client_token,
|
||||
.response = "{\"err_code\":0}",
|
||||
};
|
||||
IOT_DataTemplate_ActionReply(usr_data, buf, sizeof(buf), reply);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Data template upstream
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static void _cycle_report(void *client)
|
||||
{
|
||||
char buf[256];
|
||||
const char *report_property = "{\"power_switch\":0}";
|
||||
|
||||
IotDataTemplateEventData event_data = {
|
||||
.event_id = "status_report",
|
||||
.type = IOT_DATA_TEMPLATE_EVENT_TYPE_INFO,
|
||||
.params = "{\"status\":0,\"message\":\"ok\"}",
|
||||
|
||||
};
|
||||
|
||||
static Timer sg_cycle_report_timer;
|
||||
if (HAL_Timer_Expired(&sg_cycle_report_timer)) {
|
||||
IOT_DataTemplate_PropertyReport(client, buf, sizeof(buf), report_property);
|
||||
IOT_DataTemplate_EventPost(client, buf, sizeof(buf), event_data);
|
||||
HAL_Timer_Countdown(&sg_cycle_report_timer, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Main
|
||||
// ----------------------------------------------------------------------------
|
||||
static int sg_main_exit = 0;
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void _main_exit(int sig)
|
||||
{
|
||||
Log_e("demo exit by signal:%d\n", sig);
|
||||
sg_main_exit = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#ifdef __linux__
|
||||
signal(SIGINT, _main_exit);
|
||||
#endif
|
||||
|
||||
int rc;
|
||||
|
||||
char buf[1024];
|
||||
|
||||
// init log level
|
||||
LogHandleFunc func = {0};
|
||||
|
||||
func.log_malloc = HAL_Malloc;
|
||||
func.log_free = HAL_Free;
|
||||
func.log_get_current_time_str = HAL_Timer_Current;
|
||||
func.log_printf = HAL_Printf;
|
||||
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
|
||||
|
||||
DeviceInfo device_info;
|
||||
|
||||
rc = HAL_GetDevInfo((void *)&device_info);
|
||||
if (rc) {
|
||||
Log_e("get device info failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// init connection
|
||||
MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
|
||||
_setup_connect_init_params(&init_params, &device_info);
|
||||
|
||||
// create MQTT client and connect with server
|
||||
void *client = IOT_MQTT_Construct(&init_params);
|
||||
if (client) {
|
||||
Log_i("Cloud Device Construct Success");
|
||||
} else {
|
||||
Log_e("MQTT Construct failed!");
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
// subscribe normal topics and wait result
|
||||
IotDataTemplateCallback callback = {
|
||||
.property_callback = {.method_control_callback = _method_control_callback,
|
||||
.method_clear_control_reply_callback = _method_clear_control_reply_callback,
|
||||
.method_get_status_reply_callback = _method_get_status_reply_callback,
|
||||
.method_report_info_reply_callback = _method_report_info_reply_callback,
|
||||
.method_report_reply_callback = _method_report_reply_callback},
|
||||
.event_callback = {.method_event_reply_callback = _method_event_reply_callback},
|
||||
.action_callback = {.method_action_callback = _method_action_callback},
|
||||
};
|
||||
|
||||
rc = IOT_DataTemplate_Init(client, callback, client);
|
||||
if (rc) {
|
||||
Log_e("Client Subscribe Topic Failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
const char *report_info =
|
||||
"{\"module_hardinfo\":\"ESP8266\",\"module_softinfo\":\"V1.0\", \"fw_ver\":\"4.0.0\", "
|
||||
"\"imei\":\"11-22-33-44\",\"lat\":\"22.546015\",\"lon\":\"113.941125\",\"mac\":\"11:22:33:44:55:66\",\"device_"
|
||||
"label\":{\"append_"
|
||||
"info\":\"your self defined info\"}}";
|
||||
|
||||
IOT_DataTemplate_PropertyReportInfo(client, buf, sizeof(buf), report_info);
|
||||
IOT_DataTemplate_PropertyGetStatus(client, buf, sizeof(buf));
|
||||
|
||||
do {
|
||||
rc = IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT);
|
||||
switch (rc) {
|
||||
case QCLOUD_RET_SUCCESS:
|
||||
break;
|
||||
case QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT:
|
||||
continue;
|
||||
case QCLOUD_RET_MQTT_RECONNECTED:
|
||||
IOT_DataTemplate_PropertyGetStatus(client, buf, sizeof(buf));
|
||||
break;
|
||||
default:
|
||||
Log_e("Exit loop caused of errCode:%d", rc);
|
||||
goto exit;
|
||||
}
|
||||
_cycle_report(client);
|
||||
} while (!sg_main_exit);
|
||||
|
||||
exit:
|
||||
IOT_DataTemplate_Deinit(client);
|
||||
rc |= IOT_MQTT_Destroy(&client);
|
||||
utils_log_deinit();
|
||||
return rc;
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file data_template_action.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-09-26
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-09-26 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "data_template.h"
|
||||
|
||||
/**
|
||||
* @brief Mqtt message callback for action topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] message message from topic
|
||||
* @param[in,out] usr_data pointer to @see DataTemplateContext
|
||||
*/
|
||||
void data_template_action_message_handler(void *client, const MQTTMessage *message, void *usr_data)
|
||||
{
|
||||
DataTemplateContext *data_template_context = (DataTemplateContext *)usr_data;
|
||||
|
||||
int rc = 0;
|
||||
UtilsJsonValue method, client_token, action_id, params;
|
||||
|
||||
Log_d("receive action message:%.*s", message->payload_len, message->payload_str);
|
||||
|
||||
rc = utils_json_value_get("method", strlen("method"), message->payload_str, message->payload_len, &method);
|
||||
if (rc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strncmp(method.value, "action", method.value_len)) {
|
||||
if (data_template_context->action_callback.method_action_callback) {
|
||||
rc = utils_json_value_get("clientToken", strlen("clientToken"), message->payload_str, message->payload_len,
|
||||
&client_token);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = utils_json_value_get("actionId", strlen("actionId"), message->payload_str, message->payload_len,
|
||||
&action_id);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = utils_json_value_get("params", strlen("params"), message->payload_str, message->payload_len, ¶ms);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
data_template_context->action_callback.method_action_callback(client_token, action_id, params,
|
||||
data_template_context->usr_data);
|
||||
}
|
||||
}
|
||||
return;
|
||||
error:
|
||||
Log_e("invalid format of payload!");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish message to event topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[out] buf publish message buffer
|
||||
* @param[in] buf_len buffer len
|
||||
* @param[in] reply @see IotDataTemplateActionReply
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int data_template_action_reply_publish(void *client, char *buf, int buf_len, IotDataTemplateActionReply reply)
|
||||
{
|
||||
int len =
|
||||
HAL_Snprintf(buf, buf_len, "{\"method\":\"action_reply\",\"clientToken\":\"%.*s\",\"code\":%d,\"response\":%s}",
|
||||
reply.client_token.value_len, reply.client_token.value, reply.code, reply.response);
|
||||
return data_template_publish(client, DATA_TEMPLATE_TYPE_ACTION, QOS0, buf, len);
|
||||
}
|
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file data_template.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-08-22
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-08-22 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "qcloud_iot_data_template.h"
|
||||
|
||||
#include "data_template.h"
|
||||
|
||||
/**
|
||||
* @brief Check and subscribe data template topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] callback @see IotDataTemplateCallback
|
||||
* @param[in] usr_data usr data used in callback
|
||||
* @return 0 for success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int IOT_DataTemplate_Init(void *client, IotDataTemplateCallback callback, void *usr_data)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
int rc = 0;
|
||||
|
||||
DataTemplateContext data_tempale_context;
|
||||
data_tempale_context.usr_data = usr_data;
|
||||
|
||||
data_tempale_context.property_callback = callback.property_callback;
|
||||
rc |= data_template_topic_check_and_sub(client, DATA_TEMPLATE_TYPE_PROPERTY, data_template_property_message_handler,
|
||||
data_tempale_context);
|
||||
|
||||
// if (callback.event_callback.method_event_reply_callback) {
|
||||
// data_tempale_context.event_callback = callback.event_callback;
|
||||
// rc |= data_template_topic_check_and_sub(client, DATA_TEMPLATE_TYPE_EVENT, data_template_event_message_handler,
|
||||
// data_tempale_context);
|
||||
// }
|
||||
|
||||
// if (callback.action_callback.method_action_callback) {
|
||||
// data_tempale_context.action_callback = callback.action_callback;
|
||||
// rc |= data_template_topic_check_and_sub(client, DATA_TEMPLATE_TYPE_ACTION, data_template_action_message_handler,
|
||||
// data_tempale_context);
|
||||
// }
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe data template topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
*/
|
||||
void IOT_DataTemplate_Deinit(void *client)
|
||||
{
|
||||
POINTER_SANITY_CHECK_RTN(client);
|
||||
|
||||
data_template_topic_unsubscribe(client, DATA_TEMPLATE_TYPE_PROPERTY);
|
||||
data_template_topic_unsubscribe(client, DATA_TEMPLATE_TYPE_EVENT);
|
||||
data_template_topic_unsubscribe(client, DATA_TEMPLATE_TYPE_ACTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Report property.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] buf buffer for message
|
||||
* @param[in] buf_len buffer length
|
||||
* @param[in] params params constructed with property
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int IOT_DataTemplate_PropertyReport(void *client, char *buf, int buf_len, const char *params)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
|
||||
PropertyPublishParams publish_params = {.json = params};
|
||||
return data_template_property_publish(client, PROPERTY_UP_METHOD_TYPE_REPORT, buf, buf_len, publish_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get control message offline.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] buf buffer for message
|
||||
* @param[in] buf_len buffer length
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int IOT_DataTemplate_PropertyGetStatus(void *client, char *buf, int buf_len)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
|
||||
PropertyPublishParams publish_params = {0};
|
||||
return data_template_property_publish(client, PROPERTY_UP_METHOD_TYPE_GET_STATUS, buf, buf_len, publish_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Report device info.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] buf buffer for message
|
||||
* @param[in] buf_len buffer length
|
||||
* @param[in] params params constructed with device info
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int IOT_DataTemplate_PropertyReportInfo(void *client, char *buf, int buf_len, const char *params)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
|
||||
PropertyPublishParams publish_params = {.json = params};
|
||||
return data_template_property_publish(client, PROPERTY_UP_METHOD_TYPE_REPORT_INFO, buf, buf_len, publish_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear control message offline.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] buf buffer for message
|
||||
* @param[in] buf_len buffer length
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int IOT_DataTemplate_PropertyClearControl(void *client, char *buf, int buf_len)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
|
||||
PropertyPublishParams publish_params = {0};
|
||||
return data_template_property_publish(client, PROPERTY_UP_METHOD_TYPE_CLEAR_CONTROL, buf, buf_len, publish_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reply control message.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] buf buffer for message
|
||||
* @param[in] buf_len buffer length
|
||||
* @param[in] code 0 for success
|
||||
* @param[in] client_token client token of control message
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int IOT_DataTemplate_PropertyControlReply(void *client, char *buf, int buf_len, int code, UtilsJsonValue client_token)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
|
||||
PropertyPublishParams publish_params = {.control_reply.code = code, .control_reply.client_token = client_token};
|
||||
return data_template_property_publish(client, PROPERTY_UP_METHOD_TYPE_CONTROL_REPLY, buf, buf_len, publish_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Post event.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] buf buffer for message
|
||||
* @param[in] buf_len buffer length
|
||||
* @param[in] data @see IotDataTemplateEventData
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int IOT_DataTemplate_EventPost(void *client, char *buf, int buf_len, IotDataTemplateEventData data)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
|
||||
return data_template_event_reply_publish(client, buf, buf_len, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reply action message.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] buf buffer for message
|
||||
* @param[in] buf_len buffer length
|
||||
* @param[in] reply @see IotDataTemplateActionReply
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int IOT_DataTemplate_ActionReply(void *client, char *buf, int buf_len, IotDataTemplateActionReply reply)
|
||||
{
|
||||
return data_template_action_reply_publish(client, buf, buf_len, reply);
|
||||
}
|
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file data_template_event.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-09-26
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-09-26 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "data_template.h"
|
||||
|
||||
/**
|
||||
* @brief Mqtt message callback for event topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] message message from topic
|
||||
* @param[in,out] usr_data pointer to @see DataTemplateContext
|
||||
*/
|
||||
void data_template_event_message_handler(void *client, const MQTTMessage *message, void *usr_data)
|
||||
{
|
||||
DataTemplateContext *data_template_context = (DataTemplateContext *)usr_data;
|
||||
|
||||
int rc, code = 0;
|
||||
UtilsJsonValue method, client_token, value_code;
|
||||
|
||||
Log_d("receive event message:%.*s", message->payload_len, message->payload_str);
|
||||
|
||||
rc = utils_json_value_get("method", strlen("method"), message->payload_str, message->payload_len, &method);
|
||||
if (rc) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strncmp(method.value, "event_reply", method.value_len)) {
|
||||
if (data_template_context->event_callback.method_event_reply_callback) {
|
||||
rc = utils_json_value_get("clientToken", strlen("clientToken"), message->payload_str, message->payload_len,
|
||||
&client_token);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = utils_json_value_get("code", strlen("code"), message->payload_str, message->payload_len, &value_code);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
rc = utils_json_value_data_get(value_code, UTILS_JSON_VALUE_TYPE_INT32, &code);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
data_template_context->event_callback.method_event_reply_callback(client_token, code,
|
||||
data_template_context->usr_data);
|
||||
}
|
||||
}
|
||||
return;
|
||||
error:
|
||||
Log_e("invalid format of payload!");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish message to event topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[out] buf publish message buffer
|
||||
* @param[in] buf_len buffer len
|
||||
* @param[in] data @see IotDataTemplateEventData
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int data_template_event_reply_publish(void *client, char *buf, int buf_len, IotDataTemplateEventData data)
|
||||
{
|
||||
const char *event_type[] = {
|
||||
"info", // IOT_DATA_TEMPLATE_EVENT_TYPE_INFO
|
||||
"alert", // IOT_DATA_TEMPLATE_EVENT_TYPE_ALERT
|
||||
"fault", // IOT_DATA_TEMPLATE_EVENT_TYPE_FAULT
|
||||
};
|
||||
|
||||
static uint32_t token_num = 0;
|
||||
|
||||
int len = HAL_Snprintf(
|
||||
buf, buf_len,
|
||||
"{\"method\":\"event_post\",\"clientToken\":\"event-%u\",\"eventId\":\"%s\",\"type\":\"%s\",\"params\":%s}",
|
||||
token_num++, data.event_id, event_type[data.type], data.params);
|
||||
return data_template_publish(client, DATA_TEMPLATE_TYPE_EVENT, QOS0, buf, len);
|
||||
}
|
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file data_template_mqtt.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-10-18
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-10-18 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "qcloud_iot_data_template.h"
|
||||
|
||||
#include "data_template.h"
|
||||
|
||||
/**
|
||||
* @brief Direction(upstream/downstream) for topic.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
DATA_TEMPLATE_DIRECTION_UP = 0,
|
||||
DATA_TEMPLATE_DIRECTION_DOWN,
|
||||
} DataTemplateDirection;
|
||||
|
||||
/**
|
||||
* @brief Generate topic string.
|
||||
*
|
||||
* @param[out] buf buffer for topic name
|
||||
* @param[in] buf_len buffer length
|
||||
* @param[in] direction @see DataTemplateDirection
|
||||
* @param[in] type @see DataTemplateType
|
||||
* @param[in] product_id product id of device
|
||||
* @param[in] device_name device name of device
|
||||
* @return > 0 for length of topic name, others for fail.
|
||||
*/
|
||||
static int _data_template_topic_generate(char *buf, int buf_len, DataTemplateDirection direction, DataTemplateType type,
|
||||
const char *product_id, const char *device_name)
|
||||
{
|
||||
const char *topic_method[] = {"property", "event", "action"};
|
||||
// $thing/down/property/C283SMY3W3/test1
|
||||
return HAL_Snprintf(buf, buf_len, "$thing/%s/%s/%s/%s", direction ? "down" : "up", topic_method[type],
|
||||
STRING_PTR_PRINT_SANITY_CHECK(product_id), STRING_PTR_PRINT_SANITY_CHECK(device_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if topic already subscribed, if not then subscribe.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] type @see DataTemplateType
|
||||
* @param[in] on_message_handler message handle of topic
|
||||
* @param[in] context @see DataTemplateContext
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int data_template_topic_check_and_sub(void *client, DataTemplateType type, OnMessageHandler on_message_handler,
|
||||
DataTemplateContext context)
|
||||
{
|
||||
char data_template_topic[MAX_SIZE_OF_CLOUD_TOPIC];
|
||||
_data_template_topic_generate(data_template_topic, MAX_SIZE_OF_CLOUD_TOPIC, DATA_TEMPLATE_DIRECTION_DOWN, type,
|
||||
IOT_MQTT_GetDeviceInfo(client)->product_id,
|
||||
IOT_MQTT_GetDeviceInfo(client)->device_name);
|
||||
|
||||
int rc = 0;
|
||||
|
||||
DataTemplateContext *data_template_context = HAL_Malloc(sizeof(DataTemplateContext));
|
||||
if (!data_template_context) {
|
||||
return QCLOUD_ERR_MALLOC;
|
||||
}
|
||||
*data_template_context = context;
|
||||
|
||||
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
|
||||
sub_params.on_message_handler = on_message_handler;
|
||||
sub_params.qos = QOS1;
|
||||
sub_params.user_data = data_template_context;
|
||||
sub_params.user_data_free = HAL_Free;
|
||||
|
||||
rc = IOT_MQTT_SubscribeSync(client, data_template_topic, &sub_params);
|
||||
if (rc) {
|
||||
Log_e("subscribe topic %s failed!", data_template_topic);
|
||||
HAL_Free(data_template_context);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe data template topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] type @see DataTemplateType
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int data_template_topic_unsubscribe(void *client, DataTemplateType type)
|
||||
{
|
||||
char data_template_topic[MAX_SIZE_OF_CLOUD_TOPIC];
|
||||
_data_template_topic_generate(data_template_topic, MAX_SIZE_OF_CLOUD_TOPIC, DATA_TEMPLATE_DIRECTION_DOWN, type,
|
||||
IOT_MQTT_GetDeviceInfo(client)->product_id,
|
||||
IOT_MQTT_GetDeviceInfo(client)->device_name);
|
||||
return IOT_MQTT_Unsubscribe(client, data_template_topic);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish to data template topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] type @see DataTemplateType
|
||||
* @param[in] qos @see QoS
|
||||
* @param[in] payload payload of mqtt packet
|
||||
* @param[in] payload_len payload len
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int data_template_publish(void *client, DataTemplateType type, QoS qos, const char *payload, int payload_len)
|
||||
{
|
||||
NUMBERIC_SANITY_CHECK(payload_len, QCLOUD_ERR_BUF_TOO_SHORT);
|
||||
|
||||
char data_template_topic[MAX_SIZE_OF_CLOUD_TOPIC];
|
||||
_data_template_topic_generate(data_template_topic, MAX_SIZE_OF_CLOUD_TOPIC, DATA_TEMPLATE_DIRECTION_UP, type,
|
||||
IOT_MQTT_GetDeviceInfo(client)->product_id,
|
||||
IOT_MQTT_GetDeviceInfo(client)->device_name);
|
||||
|
||||
PublishParams pub_params = DEFAULT_PUB_PARAMS;
|
||||
pub_params.qos = qos;
|
||||
pub_params.payload = (void *)payload;
|
||||
pub_params.payload_len = payload_len;
|
||||
return IOT_MQTT_Publish(client, data_template_topic, &pub_params);
|
||||
}
|
@@ -0,0 +1,214 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file data_template_property.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-08-23
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-08-23 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "qcloud_iot_data_template.h"
|
||||
#include "data_template.h"
|
||||
|
||||
/**
|
||||
* @brief Down method type.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
PROPERTY_DOWN_METHOD_TYPE_CONTROL = 0,
|
||||
PROPERTY_DOWN_METHOD_TYPE_REPORT_REPLY,
|
||||
PROPERTY_DOWN_METHOD_TYPE_GET_STATUS_REPLY,
|
||||
PROPERTY_DOWN_METHOD_TYPE_REPORT_INFO_REPLY,
|
||||
PROPERTY_DOWN_METHOD_TYPE_CLEAR_CONTROL_REPLY,
|
||||
} PropertyDownMethodType;
|
||||
|
||||
/**
|
||||
* @brief Parse payload and callback.
|
||||
*
|
||||
* @param[in] type @see PropertyDownMethodType
|
||||
* @param[in] message message from cloud
|
||||
* @param[in] callback callback for user
|
||||
* @param[in,out] usr_data user data used in callback
|
||||
*/
|
||||
static void _parse_method_payload_and_callback(PropertyDownMethodType type, const MQTTMessage *message,
|
||||
const PropertyMessageCallback *callback, void *usr_data)
|
||||
{
|
||||
int rc = 0, code;
|
||||
UtilsJsonValue client_token, value_code, params, reported, control;
|
||||
|
||||
// get client token
|
||||
rc = utils_json_value_get("clientToken", strlen("clientToken"), message->payload_str, message->payload_len,
|
||||
&client_token);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
// get code
|
||||
if (PROPERTY_DOWN_METHOD_TYPE_REPORT_REPLY == type || PROPERTY_DOWN_METHOD_TYPE_GET_STATUS_REPLY == type ||
|
||||
PROPERTY_DOWN_METHOD_TYPE_REPORT_INFO_REPLY == type || PROPERTY_DOWN_METHOD_TYPE_CLEAR_CONTROL_REPLY == type) {
|
||||
rc = utils_json_value_get("code", strlen("code"), message->payload_str, message->payload_len, &value_code);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = utils_json_value_data_get(value_code, UTILS_JSON_VALUE_TYPE_INT32, &code);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
// callback
|
||||
switch (type) {
|
||||
case PROPERTY_DOWN_METHOD_TYPE_CONTROL:
|
||||
if (callback->method_control_callback) {
|
||||
rc = utils_json_value_get("params", strlen("params"), message->payload_str, message->payload_len,
|
||||
¶ms);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
callback->method_control_callback(client_token, params, usr_data);
|
||||
}
|
||||
break;
|
||||
case PROPERTY_DOWN_METHOD_TYPE_REPORT_REPLY:
|
||||
if (callback->method_report_reply_callback) {
|
||||
callback->method_report_reply_callback(client_token, code, usr_data);
|
||||
}
|
||||
break;
|
||||
case PROPERTY_DOWN_METHOD_TYPE_GET_STATUS_REPLY:
|
||||
if (callback->method_get_status_reply_callback) {
|
||||
reported.value = NULL;
|
||||
reported.value_len = 0;
|
||||
control.value = NULL;
|
||||
control.value_len = 0;
|
||||
rc = utils_json_value_get("data.reported", strlen("data.reported"), message->payload_str,
|
||||
message->payload_len, &reported);
|
||||
rc &= utils_json_value_get("data.control", strlen("data.control"), message->payload_str,
|
||||
message->payload_len, &control);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
callback->method_get_status_reply_callback(client_token, code, reported, control, usr_data);
|
||||
}
|
||||
break;
|
||||
case PROPERTY_DOWN_METHOD_TYPE_REPORT_INFO_REPLY:
|
||||
if (callback->method_report_info_reply_callback) {
|
||||
callback->method_report_info_reply_callback(client_token, code, usr_data);
|
||||
}
|
||||
break;
|
||||
case PROPERTY_DOWN_METHOD_TYPE_CLEAR_CONTROL_REPLY:
|
||||
if (callback->method_clear_control_reply_callback) {
|
||||
callback->method_clear_control_reply_callback(client_token, code, usr_data);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
error:
|
||||
Log_e("invalid format of payload!");
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Mqtt message callback for property topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] message message from topic
|
||||
* @param[in,out] usr_data pointer to @see DataTemplateContext
|
||||
*/
|
||||
void data_template_property_message_handler(void *client, const MQTTMessage *message, void *usr_data)
|
||||
{
|
||||
const char *property_down_method_str[] = {
|
||||
"control", // PROPERTY_DOWN_METHOD_TYPE_CONTROL
|
||||
"report_reply", // PROPERTY_DOWN_METHOD_TYPE_REPORT_REPLY
|
||||
"get_status_reply", // PROPERTY_DOWN_METHOD_TYPE_GET_STATUS_REPLY
|
||||
"report_info_reply", // PROPERTY_DOWN_METHOD_TYPE_REPORT_INFO_REPLY
|
||||
"clear_control_reply", // PROPERTY_DOWN_METHOD_TYPE_CLEAR_CONTROL_REPLY
|
||||
};
|
||||
|
||||
int rc, i = 0;
|
||||
|
||||
DataTemplateContext *data_template_context = (DataTemplateContext *)usr_data;
|
||||
UtilsJsonValue method;
|
||||
|
||||
Log_d("receive property message:%.*s", message->payload_len, message->payload_str);
|
||||
|
||||
rc = utils_json_value_get("method", strlen("method"), message->payload_str, message->payload_len, &method);
|
||||
if (rc) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = PROPERTY_DOWN_METHOD_TYPE_CONTROL; i <= PROPERTY_DOWN_METHOD_TYPE_CLEAR_CONTROL_REPLY; i++) {
|
||||
if (!strncmp(method.value, property_down_method_str[i], method.value_len)) {
|
||||
_parse_method_payload_and_callback(i, message, &data_template_context->property_callback,
|
||||
data_template_context->usr_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish message to property topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] publish_type @see PropertyUpMethodType
|
||||
* @param[out] buf publish message buffer
|
||||
* @param[in] buf_len buffer len
|
||||
* @param[in] params @see PropertyPublishParams
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int data_template_property_publish(void *client, PropertyUpMethodType publish_type, char *buf, int buf_len,
|
||||
PropertyPublishParams params)
|
||||
{
|
||||
static uint32_t token_num = 0;
|
||||
|
||||
int len = 0;
|
||||
|
||||
switch (publish_type) {
|
||||
case PROPERTY_UP_METHOD_TYPE_REPORT:
|
||||
len = HAL_Snprintf(buf, buf_len, "{\"method\":\"report\",\"clientToken\":\"property-%u\",\"params\":%s}",
|
||||
token_num++, params.json);
|
||||
break;
|
||||
case PROPERTY_UP_METHOD_TYPE_REPORT_INFO:
|
||||
len =
|
||||
HAL_Snprintf(buf, buf_len, "{\"method\":\"report_info\",\"clientToken\":\"property-%u\",\"params\":%s}",
|
||||
token_num++, params.json);
|
||||
break;
|
||||
case PROPERTY_UP_METHOD_TYPE_GET_STATUS:
|
||||
len =
|
||||
HAL_Snprintf(buf, buf_len, "{\"method\":\"get_status\",\"clientToken\":\"property-%u\"}", token_num++);
|
||||
break;
|
||||
case PROPERTY_UP_METHOD_TYPE_CLEAR_CONTROL:
|
||||
len = HAL_Snprintf(buf, buf_len, "{\"method\":\"clear_control\",\"clientToken\":\"property-%u\"}",
|
||||
token_num++);
|
||||
break;
|
||||
case PROPERTY_UP_METHOD_TYPE_CONTROL_REPLY:
|
||||
len = HAL_Snprintf(buf, buf_len, "{\"method\":\"control_reply\",\"clientToken\":\"%.*s\",\"code\":%d}",
|
||||
params.control_reply.client_token.value_len, params.control_reply.client_token.value,
|
||||
params.control_reply.code);
|
||||
break;
|
||||
default:
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
return data_template_publish(client, DATA_TEMPLATE_TYPE_PROPERTY, QOS0, buf, len);
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 120
|
||||
DerivePointerAlignment: true
|
||||
PointerAlignment: Left
|
||||
SortIncludes: true
|
||||
IncludeBlocks: Preserve
|
||||
IndentPPDirectives: AfterHash
|
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file test_data_template.cc
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-09-29
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-09-29 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "mqtt_client_test.h"
|
||||
#include "qcloud_iot_common.h"
|
||||
#include "qcloud_iot_explorer.h"
|
||||
|
||||
namespace mqtt_client_unittest {
|
||||
|
||||
static void _method_control_callback(UtilsJsonValue client_token, UtilsJsonValue params, void *usr_data) {
|
||||
char buf[256];
|
||||
Log_i("recv msg[%.*s]: params=%.*s", client_token.value_len, client_token.value, params.value_len, params.value);
|
||||
IOT_DataTemplate_PropertyControlReply(usr_data, buf, sizeof(buf), 0, client_token);
|
||||
}
|
||||
|
||||
static void _method_report_reply_callback(UtilsJsonValue client_token, int code, void *usr_data) {
|
||||
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
|
||||
}
|
||||
|
||||
static void _method_get_status_reply_callback(UtilsJsonValue client_token, int code, UtilsJsonValue reported,
|
||||
UtilsJsonValue control, void *usr_data) {
|
||||
char buf[256];
|
||||
Log_i("recv msg[%.*s]: code=%d|reported=%.*s|control=%.*s", client_token.value_len, client_token.value, code,
|
||||
reported.value_len, STRING_PTR_PRINT_SANITY_CHECK(reported.value), control.value_len,
|
||||
STRING_PTR_PRINT_SANITY_CHECK(control.value));
|
||||
IOT_DataTemplate_PropertyClearControl(usr_data, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static void _method_report_info_reply_callback(UtilsJsonValue client_token, int code, void *usr_data) {
|
||||
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
|
||||
}
|
||||
|
||||
static void _method_clear_control_reply_callback(UtilsJsonValue client_token, int code, void *usr_data) {
|
||||
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
|
||||
}
|
||||
|
||||
static void _method_event_reply_callback(UtilsJsonValue client_token, int code, void *usr_data) {
|
||||
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
|
||||
}
|
||||
|
||||
static void _method_action_callback(UtilsJsonValue client_token, UtilsJsonValue action_id, UtilsJsonValue params,
|
||||
void *usr_data) {
|
||||
char buf[256];
|
||||
|
||||
Log_i("recv msg[%.*s]: action_id=%.*s|params=%.*s", client_token.value_len, client_token.value, action_id.value_len,
|
||||
action_id.value, params.value_len, params.value);
|
||||
|
||||
IotDataTemplateActionReply reply = {
|
||||
.client_token = client_token,
|
||||
.code = 0,
|
||||
.response = "{\"err_code\":0}",
|
||||
};
|
||||
|
||||
ASSERT_GE(IOT_DataTemplate_ActionReply(usr_data, buf, sizeof(buf), reply), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test data template property.
|
||||
*
|
||||
*/
|
||||
TEST_F(MqttClientTest, data_template_property) {
|
||||
char buf[1024];
|
||||
|
||||
IotDataTemplateCallback callback = {
|
||||
.property_callback = {.method_control_callback = _method_control_callback,
|
||||
.method_report_reply_callback = _method_report_reply_callback,
|
||||
.method_get_status_reply_callback = _method_get_status_reply_callback,
|
||||
.method_report_info_reply_callback = _method_report_info_reply_callback,
|
||||
.method_clear_control_reply_callback = _method_clear_control_reply_callback},
|
||||
.event_callback = {.method_event_reply_callback = NULL},
|
||||
.action_callback = {.method_action_callback = NULL},
|
||||
};
|
||||
|
||||
const char *report_info =
|
||||
"{\"module_hardinfo\":\"ESP8266\",\"module_softinfo\":\"V1.0\", \"fw_ver\":\"4.0.0\", "
|
||||
"\"imei\":\"11-22-33-44\",\"lat\":\"22.546015\",\"lon\":\"113.941125\",\"mac\":\"11:22:33:44:55:66\",\"device_"
|
||||
"label\":{\"append_"
|
||||
"info\":\"your self defined info\"}}";
|
||||
|
||||
ASSERT_EQ(IOT_DataTemplate_Init(client, callback, client), 0);
|
||||
ASSERT_EQ(IOT_DataTemplate_PropertyReportInfo(client, buf, sizeof(buf), report_info), 0);
|
||||
ASSERT_EQ(IOT_DataTemplate_PropertyGetStatus(client, buf, sizeof(buf)), 0);
|
||||
IOT_DataTemplate_Deinit(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test data template event.
|
||||
*
|
||||
*/
|
||||
TEST_F(MqttClientTest, data_template_event) {
|
||||
char buf[1024];
|
||||
IotDataTemplateCallback callback = {
|
||||
.property_callback = {0},
|
||||
.event_callback = {.method_event_reply_callback = _method_event_reply_callback},
|
||||
.action_callback = {.method_action_callback = _method_action_callback},
|
||||
};
|
||||
|
||||
const char *report_property = "{\"power_switch\":0}";
|
||||
|
||||
IotDataTemplateEventData event_data = {
|
||||
.event_id = "status_report",
|
||||
.type = IOT_DATA_TEMPLATE_EVENT_TYPE_INFO,
|
||||
.params = "{\"status\":0,\"message\":\"ok\"}",
|
||||
};
|
||||
|
||||
ASSERT_EQ(IOT_DataTemplate_Init(client, callback, client), 0);
|
||||
ASSERT_GE(IOT_DataTemplate_EventPost(client, buf, sizeof(buf), event_data), 0);
|
||||
IOT_DataTemplate_Deinit(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test data template action.
|
||||
*
|
||||
*/
|
||||
TEST_F(MqttClientTest, data_template_action) {
|
||||
char buf[1024];
|
||||
IotDataTemplateCallback callback = {
|
||||
.property_callback = {0},
|
||||
.event_callback = {.method_event_reply_callback = NULL},
|
||||
.action_callback = {.method_action_callback = _method_action_callback},
|
||||
};
|
||||
|
||||
ASSERT_EQ(IOT_DataTemplate_Init(client, callback, client), 0);
|
||||
IOT_DataTemplate_Deinit(client);
|
||||
}
|
||||
|
||||
} // namespace mqtt_client_unittest
|
@@ -0,0 +1,17 @@
|
||||
file(GLOB src_service_mqtt
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/*.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/file_manage/*.c
|
||||
)
|
||||
set(inc_service_mqtt ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
|
||||
|
||||
set(src_services ${src_services} ${src_service_mqtt} PARENT_SCOPE)
|
||||
set(inc_services ${inc_services} ${inc_service_mqtt} PARENT_SCOPE)
|
||||
|
||||
file(GLOB src_file_manage_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/file_manage_sample.c)
|
||||
add_executable(file_manage_sample ${src_file_manage_sample})
|
||||
target_link_libraries(file_manage_sample ${libsdk})
|
||||
|
||||
if( ${CONFIG_IOT_TEST} STREQUAL "ON")
|
||||
file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
|
||||
set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
|
||||
endif()
|
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file service_mqtt.h
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2022-01-11
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2022-01-11 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_EXPLORER_SERVICE_MQTT_INC_SERVICE_MQTT_H_
|
||||
#define IOT_HUB_DEVICE_C_SDK_SERVICES_EXPLORER_SERVICE_MQTT_INC_SERVICE_MQTT_H_
|
||||
|
||||
#include "qcloud_iot_common.h"
|
||||
#include "utils_list.h"
|
||||
|
||||
/**
|
||||
* @brief Service type, only file manage supportted now.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
SERVICE_TYPE_FILE_MANAGE = 0,
|
||||
} ServiceType;
|
||||
|
||||
/**
|
||||
* @brief Register to server list.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
ServiceType type;
|
||||
const char ** method_list;
|
||||
int method_num;
|
||||
OnMessageHandler message_handle;
|
||||
void * usr_data;
|
||||
void (*user_data_free)(void *);
|
||||
} ServiceRegisterParams;
|
||||
|
||||
/**
|
||||
* @brief Subscribe service mqtt.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int service_mqtt_init(void *client);
|
||||
|
||||
/**
|
||||
* @brief If no service in the service list, then unsubscribe.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
*/
|
||||
void service_mqtt_deinit(void *client);
|
||||
|
||||
/**
|
||||
* @brief Publish to service mqtt topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] qos @see QoS
|
||||
* @param[in] payload payload of mqtt packet
|
||||
* @param[in] payload_len payload len
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int service_mqtt_publish(void *client, QoS qos, const char *payload, int payload_len);
|
||||
|
||||
/**
|
||||
* @brief Register server handler to server list.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] params @see ServiceRegisterParams
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int service_mqtt_service_register(void *client, const ServiceRegisterParams *params);
|
||||
|
||||
/**
|
||||
* @brief Unregister server handler from server list.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] type @see ServiceType
|
||||
*/
|
||||
void service_mqtt_service_unregister(void *client, ServiceType type);
|
||||
|
||||
#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_EXPLORER_SERVICE_MQTT_INC_SERVICE_MQTT_H_
|
@@ -0,0 +1,349 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file broadcast_sample.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-07-18
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "qcloud_iot_explorer.h"
|
||||
|
||||
#include "utils_log.h"
|
||||
|
||||
/**
|
||||
* @brief MQTT event callback, @see MQTTEventHandleFun
|
||||
*
|
||||
* @param[in] client pointer to mqtt client
|
||||
* @param[in] handle_context context
|
||||
* @param[in] msg msg
|
||||
*/
|
||||
static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
|
||||
{
|
||||
MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
|
||||
uintptr_t packet_id = (uintptr_t)msg->msg;
|
||||
|
||||
switch (msg->event_type) {
|
||||
case MQTT_EVENT_UNDEF:
|
||||
Log_i("undefined event occur.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DISCONNECT:
|
||||
Log_i("MQTT disconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_RECONNECT:
|
||||
Log_i("MQTT reconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_RECEIVED:
|
||||
Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
|
||||
mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
|
||||
mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
|
||||
break;
|
||||
case MQTT_EVENT_SUBSCRIBE_SUCCESS:
|
||||
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
|
||||
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_NACK:
|
||||
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
|
||||
Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
|
||||
Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_NACK:
|
||||
Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_SUCCESS:
|
||||
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_TIMEOUT:
|
||||
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_NACK:
|
||||
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
default:
|
||||
Log_i("Should NOT arrive here.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Setup MQTT construct parameters.
|
||||
*
|
||||
* @param[in,out] initParams @see MQTTInitParams
|
||||
* @param[in] device_info @see DeviceInfo
|
||||
*/
|
||||
static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
|
||||
{
|
||||
init_params->device_info = device_info;
|
||||
init_params->command_timeout = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT;
|
||||
init_params->keep_alive_interval_ms = QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL;
|
||||
init_params->auto_connect_enable = 1;
|
||||
init_params->event_handle.h_fp = _mqtt_event_handler;
|
||||
init_params->event_handle.context = NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// file list function
|
||||
// ----------------------------------------------------------------------------
|
||||
static IotFileManageFileInfo sg_file_list[5];
|
||||
|
||||
static void _file_info_init(IotFileManageFileInfo *file_info, UtilsJsonValue file_name, UtilsJsonValue file_type,
|
||||
UtilsJsonValue version)
|
||||
{
|
||||
strncpy(file_info->file_name, file_name.value, file_name.value_len);
|
||||
strncpy(file_info->file_version, version.value, version.value_len);
|
||||
file_info->file_type = IOT_FileManage_GetFileType(file_type.value, file_type.value_len);
|
||||
}
|
||||
|
||||
static void _add_file_info_to_list(IotFileManageFileInfo *file_info)
|
||||
{
|
||||
int i_free = -1;
|
||||
for (int i = 0; i < sizeof(sg_file_list) / sizeof(sg_file_list[0]); i++) {
|
||||
if (!strcmp(sg_file_list[i].file_name, file_info->file_name)) {
|
||||
memcpy(sg_file_list + i, file_info, sizeof(IotFileManageFileInfo));
|
||||
return;
|
||||
}
|
||||
|
||||
if (sg_file_list[i].file_name[0] == '\0' && i_free == -1) {
|
||||
i_free = i;
|
||||
}
|
||||
}
|
||||
if (i_free != -1) {
|
||||
memcpy(sg_file_list + i_free, file_info, sizeof(IotFileManageFileInfo));
|
||||
}
|
||||
}
|
||||
|
||||
static void _del_file_info_to_list(IotFileManageFileInfo *file_info)
|
||||
{
|
||||
for (int i = 0; i < sizeof(sg_file_list) / sizeof(sg_file_list[0]); i++) {
|
||||
if (!strcmp(sg_file_list[i].file_name, file_info->file_name)) {
|
||||
sg_file_list[i].file_name[0] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// file function
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static int _file_download(void *client, char *buf, int buf_len, UtilsJsonValue url, UtilsJsonValue md5sum,
|
||||
uint32_t file_size, IotFileManageFileInfo *file_info)
|
||||
{
|
||||
Log_i("downloading file:url=%.*s|md5sum=%.*s|file_size=%u", url.value_len, url.value, md5sum.value_len,
|
||||
md5sum.value, file_size);
|
||||
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOADING, 0, file_info->file_name,
|
||||
file_info->file_version);
|
||||
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOADING, 50, file_info->file_name,
|
||||
file_info->file_version);
|
||||
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOADING, 100, file_info->file_name,
|
||||
file_info->file_version);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _file_upgrade(void *client, char *buf, int buf_len, IotFileManageFileInfo *file_info)
|
||||
{
|
||||
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_BEGIN, 0, file_info->file_name,
|
||||
file_info->file_version);
|
||||
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_SUCCESS, 0, file_info->file_name,
|
||||
file_info->file_version);
|
||||
_add_file_info_to_list(file_info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _file_del(void *client, char *buf, int buf_len, IotFileManageFileInfo *file_info)
|
||||
{
|
||||
_del_file_info_to_list(file_info);
|
||||
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_DEL_SUCCESS, 0, file_info->file_name,
|
||||
file_info->file_version);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// OTA callback
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static void _file_manage_update_file_callback(UtilsJsonValue file_name, UtilsJsonValue file_type,
|
||||
UtilsJsonValue version, UtilsJsonValue url, UtilsJsonValue md5sum,
|
||||
uint32_t file_size, void *usr_data)
|
||||
{
|
||||
IotFileManageFileInfo file_info = {0};
|
||||
|
||||
char buf[256];
|
||||
int buf_len = sizeof(buf);
|
||||
|
||||
Log_i("recv file: file_name=%.*s|type=%.*s|version=%.*s|url=%.*s|md5sum=%.*s|file_size=%u", file_name.value_len,
|
||||
file_name.value, file_type.value_len, file_type.value, version.value_len, version.value, url.value_len,
|
||||
url.value, md5sum.value_len, md5sum.value, file_size);
|
||||
|
||||
_file_info_init(&file_info, file_name, file_type, version);
|
||||
|
||||
int rc = _file_download(usr_data, buf, buf_len, url, md5sum, file_size, &file_info);
|
||||
if (rc) {
|
||||
IOT_FileManage_Report(usr_data, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_FAIL, 0, file_info.file_name,
|
||||
file_info.file_version);
|
||||
return;
|
||||
}
|
||||
_file_upgrade(usr_data, buf, buf_len, &file_info);
|
||||
IOT_FileManage_ReportFileList(usr_data, buf, buf_len, sg_file_list, sizeof(sg_file_list) / sizeof(sg_file_list[0]));
|
||||
}
|
||||
|
||||
static void _file_manage_del_file_callback(UtilsJsonValue file_name, UtilsJsonValue file_type, UtilsJsonValue version,
|
||||
void *usr_data)
|
||||
{
|
||||
char buf[256];
|
||||
int buf_len = sizeof(buf);
|
||||
|
||||
IotFileManageFileInfo file_info = {0};
|
||||
_file_info_init(&file_info, file_name, file_type, version);
|
||||
_file_del(usr_data, buf, buf_len, &file_info);
|
||||
IOT_FileManage_ReportFileList(usr_data, buf, buf_len, sg_file_list, sizeof(sg_file_list) / sizeof(sg_file_list[0]));
|
||||
}
|
||||
|
||||
static void _file_manage_report_file_version_reponse_callback(UtilsJsonValue file_list, int result_code, void *usr_data)
|
||||
{
|
||||
Log_i("recv file_list=%.*s|result_code=%d", file_list.value_len, file_list.value, result_code);
|
||||
}
|
||||
|
||||
static void _file_manage_request_file_url_response_callback(UtilsJsonValue url, UtilsJsonValue file_token,
|
||||
int result_code, void *usr_data)
|
||||
{
|
||||
Log_i("recv response: url=%.*s|file_token=%.*s|result_code=%d", url.value_len, url.value, file_token.value_len,
|
||||
file_token.value, result_code);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Main
|
||||
// ----------------------------------------------------------------------------
|
||||
static int sg_main_exit = 0;
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void _main_exit(int sig)
|
||||
{
|
||||
Log_e("demo exit by signal:%d\n", sig);
|
||||
sg_main_exit = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#ifdef __linux__
|
||||
signal(SIGINT, _main_exit);
|
||||
#endif
|
||||
|
||||
int rc;
|
||||
|
||||
char buf[1024];
|
||||
|
||||
// init log level
|
||||
LogHandleFunc func = {0};
|
||||
|
||||
func.log_malloc = HAL_Malloc;
|
||||
func.log_free = HAL_Free;
|
||||
func.log_get_current_time_str = HAL_Timer_Current;
|
||||
func.log_printf = HAL_Printf;
|
||||
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
|
||||
|
||||
DeviceInfo device_info;
|
||||
rc = HAL_GetDevInfo((void *)&device_info);
|
||||
if (rc) {
|
||||
Log_e("get device info failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// init connection
|
||||
MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
|
||||
_setup_connect_init_params(&init_params, &device_info);
|
||||
|
||||
// create MQTT client and connect with server
|
||||
void *client = IOT_MQTT_Construct(&init_params);
|
||||
if (client) {
|
||||
Log_i("Cloud Device Construct Success");
|
||||
} else {
|
||||
Log_e("MQTT Construct failed!");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
IotFileManageCallback file_manage_callback = {
|
||||
.update_file_callback = _file_manage_update_file_callback,
|
||||
.del_file_callback = _file_manage_del_file_callback,
|
||||
.report_file_version_reponse_callback = _file_manage_report_file_version_reponse_callback,
|
||||
.request_file_url_response_callback = _file_manage_request_file_url_response_callback,
|
||||
};
|
||||
rc = IOT_FileManage_Init(client, file_manage_callback, client);
|
||||
if (rc) {
|
||||
Log_e("OTA init failed!, rc=%d", rc);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = IOT_FileManage_ReportFileList(client, buf, sizeof(buf), sg_file_list,
|
||||
sizeof(sg_file_list) / sizeof(sg_file_list[0]));
|
||||
if (rc) {
|
||||
Log_e("OTA report version failed!, rc=%d", rc);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
do {
|
||||
rc = IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT);
|
||||
switch (rc) {
|
||||
case QCLOUD_RET_SUCCESS:
|
||||
break;
|
||||
case QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT:
|
||||
continue;
|
||||
case QCLOUD_RET_MQTT_RECONNECTED:
|
||||
break;
|
||||
default:
|
||||
Log_e("Exit loop caused of errCode:%d", rc);
|
||||
goto exit;
|
||||
}
|
||||
} while (!sg_main_exit);
|
||||
exit:
|
||||
IOT_FileManage_Deinit(client);
|
||||
rc = IOT_MQTT_Destroy(&client);
|
||||
utils_log_deinit();
|
||||
return rc;
|
||||
}
|
@@ -0,0 +1,435 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file file_manage.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2022-01-11
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2022-01-11 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "qcloud_iot_file_manage.h"
|
||||
|
||||
#include "service_mqtt.h"
|
||||
|
||||
/**
|
||||
* @brief Context of file manage, callback and user data.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
IotFileManageCallback callback;
|
||||
void *usr_data;
|
||||
} FileManageContext;
|
||||
|
||||
/**
|
||||
* @brief File manage down stream message type.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
FILE_MANAGE_DOWN_MESSAGE_TYPE_UPDATE = 0,
|
||||
FILE_MANAGE_DOWN_MESSAGE_TYPE_DEL,
|
||||
FILE_MANAGE_DOWN_MESSAGE_TYPE_REPORT_VERSION_RESPONSE,
|
||||
FILE_MANAGE_DOWN_MESSAGE_TYPE_POST_REQUEST_RESPONSE,
|
||||
} FileManageDownMessageType;
|
||||
|
||||
/**
|
||||
* @brief Method string of down stream message. Order @see FileManageDownMessageType.
|
||||
*
|
||||
*/
|
||||
static const char *sg_file_manage_method_str[] = {
|
||||
"update_resource", // FILE_MANAGE_DOWN_MESSAGE_TYPE_UPDATE
|
||||
"del_resource", // FILE_MANAGE_DOWN_MESSAGE_TYPE_DEL
|
||||
"report_version_rsp", // FILE_MANAGE_DOWN_MESSAGE_TYPE_REPORT_VERSION_RESPONSE
|
||||
"request_url_resp", // FILE_MANAGE_DOWN_MESSAGE_TYPE_POST_REQUEST_RESPONSE
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Method string of down stream message. Order @see IotFileManageFileType.
|
||||
*
|
||||
*/
|
||||
static const char *sg_file_manage_file_type_str[] = {
|
||||
"FILE", // IOT_FILE_MANAGE_FILE_TYPE_FILE
|
||||
"AUDIO", // IOT_FILE_MANAGE_FILE_TYPE_AUDIO
|
||||
"VOICE", // IOT_FILE_MANAGE_FILE_TYPE_VOICE
|
||||
"VIDEO", // IOT_FILE_MANAGE_FILE_TYPE_VIDEO
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Handle file mange down stream message.
|
||||
*
|
||||
* @param[in] type @see FileManageDownMessageType
|
||||
* @param[in] message @see MQTTMessage
|
||||
* @param[in] callback @see IotFileManageCallback
|
||||
* @param[in,out] usr_data user define in IOT_FileManage_Init
|
||||
*/
|
||||
static void _parse_update_payload_and_callback(FileManageDownMessageType type, const MQTTMessage *message,
|
||||
const IotFileManageCallback *callback, void *usr_data)
|
||||
{
|
||||
int rc, result_code;
|
||||
uint32_t file_size = 0;
|
||||
UtilsJsonValue file_name, file_type, version, md5sum, url, value_result_code, file_token, file_list = {0};
|
||||
|
||||
// callback
|
||||
switch (type) {
|
||||
case FILE_MANAGE_DOWN_MESSAGE_TYPE_UPDATE:
|
||||
if (!callback->update_file_callback) {
|
||||
return;
|
||||
}
|
||||
rc = utils_json_value_get("resource_name", sizeof("resource_name") - 1, message->payload_str,
|
||||
message->payload_len, &file_name);
|
||||
rc |= utils_json_value_get("resource_type", sizeof("resource_type") - 1, message->payload_str,
|
||||
message->payload_len, &file_type);
|
||||
rc |= utils_json_value_get("version", sizeof("version") - 1, message->payload_str, message->payload_len,
|
||||
&version);
|
||||
rc |= utils_json_value_get("url", sizeof("url") - 1, message->payload_str, message->payload_len, &url);
|
||||
rc |= utils_json_value_get("md5sum", sizeof("md5sum") - 1, message->payload_str, message->payload_len,
|
||||
&md5sum);
|
||||
rc |= utils_json_get_uint32("file_size", sizeof("file_size") - 1, message->payload_str,
|
||||
message->payload_len, &file_size);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
callback->update_file_callback(file_name, file_type, version, url, md5sum, file_size, usr_data);
|
||||
break;
|
||||
case FILE_MANAGE_DOWN_MESSAGE_TYPE_DEL:
|
||||
if (!callback->del_file_callback) {
|
||||
return;
|
||||
}
|
||||
rc = utils_json_value_get("resource_name", sizeof("resource_name") - 1, message->payload_str,
|
||||
message->payload_len, &file_name);
|
||||
rc |= utils_json_value_get("resource_type", sizeof("resource_type") - 1, message->payload_str,
|
||||
message->payload_len, &file_type);
|
||||
rc |= utils_json_value_get("version", sizeof("version") - 1, message->payload_str, message->payload_len,
|
||||
&version);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
callback->del_file_callback(file_name, file_type, version, usr_data);
|
||||
break;
|
||||
case FILE_MANAGE_DOWN_MESSAGE_TYPE_REPORT_VERSION_RESPONSE:
|
||||
if (!callback->report_file_version_reponse_callback) {
|
||||
return;
|
||||
}
|
||||
// maybe no resource
|
||||
utils_json_value_get("resource_list", sizeof("resource_list") - 1, message->payload_str,
|
||||
message->payload_len, &file_list);
|
||||
rc = utils_json_value_get("result_code", sizeof("result_code") - 1, message->payload_str,
|
||||
message->payload_len, &value_result_code);
|
||||
rc |= utils_json_value_data_get(value_result_code, UTILS_JSON_VALUE_TYPE_INT32, &result_code);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
callback->report_file_version_reponse_callback(file_list, result_code, usr_data);
|
||||
break;
|
||||
case FILE_MANAGE_DOWN_MESSAGE_TYPE_POST_REQUEST_RESPONSE:
|
||||
if (!callback->request_file_url_response_callback) {
|
||||
return;
|
||||
}
|
||||
rc = utils_json_value_get("resource_url", sizeof("resource_url") - 1, message->payload_str,
|
||||
message->payload_len, &url);
|
||||
rc |= utils_json_value_get("resource_token", sizeof("resource_token") - 1, message->payload_str,
|
||||
message->payload_len, &file_token);
|
||||
rc |= utils_json_value_get("result_code", sizeof("result_code") - 1, message->payload_str,
|
||||
message->payload_len, &value_result_code);
|
||||
rc |= utils_json_value_data_get(value_result_code, UTILS_JSON_VALUE_TYPE_INT32, &result_code);
|
||||
if (rc) {
|
||||
goto error;
|
||||
}
|
||||
callback->request_file_url_response_callback(file_list, file_token, result_code, usr_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
error:
|
||||
Log_e("invalid format of payload!");
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Mqtt message callback for file manage.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] message message from topic
|
||||
* @param[in,out] usr_data pointer to @see FileManageContext
|
||||
*/
|
||||
static void _file_manage_message_callback(void *client, const MQTTMessage *message, void *usr_data)
|
||||
{
|
||||
const char *file_manage_method_str[] = {
|
||||
"update_resource", // FILE_MANAGE_DOWN_MESSAGE_TYPE_UPDATE
|
||||
"del_resource", // FILE_MANAGE_DOWN_MESSAGE_TYPE_DEL
|
||||
"report_version_rsp", // FILE_MANAGE_DOWN_MESSAGE_TYPE_REPORT_VERSION_RESPONSE
|
||||
"request_url_resp", // FILE_MANAGE_DOWN_MESSAGE_TYPE_POST_REQUEST_RESPONSE
|
||||
};
|
||||
|
||||
int rc, i = 0;
|
||||
|
||||
FileManageContext *file_manage_context = (FileManageContext *)usr_data;
|
||||
UtilsJsonValue method;
|
||||
|
||||
Log_d("receive file manage message:%.*s", message->payload_len, message->payload_str);
|
||||
|
||||
rc = utils_json_value_get("method", sizeof("method") - 1, message->payload_str, message->payload_len, &method);
|
||||
if (rc) {
|
||||
Log_e("invalid file manage message!");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = FILE_MANAGE_DOWN_MESSAGE_TYPE_UPDATE; i <= FILE_MANAGE_DOWN_MESSAGE_TYPE_POST_REQUEST_RESPONSE; i++) {
|
||||
if (!strncmp(method.value, file_manage_method_str[i], method.value_len)) {
|
||||
Log_d("callback file manage message!");
|
||||
_parse_update_payload_and_callback(i, message, &file_manage_context->callback,
|
||||
file_manage_context->usr_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief File manage init, register handler to server list.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] callback @see IotFileManageCallback
|
||||
* @param[in] usr_data usr data used in callback
|
||||
* @return 0 for success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int IOT_FileManage_Init(void *client, IotFileManageCallback callback, void *usr_data)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
|
||||
int rc = service_mqtt_init(client);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
FileManageContext *file_manage_context = (FileManageContext *)HAL_Malloc(sizeof(FileManageContext));
|
||||
if (!file_manage_context) {
|
||||
return QCLOUD_ERR_MALLOC;
|
||||
}
|
||||
file_manage_context->callback = callback;
|
||||
file_manage_context->usr_data = usr_data;
|
||||
|
||||
ServiceRegisterParams params = {
|
||||
.type = SERVICE_TYPE_FILE_MANAGE,
|
||||
.method_list = sg_file_manage_method_str,
|
||||
.method_num = sizeof(sg_file_manage_method_str) / sizeof(sg_file_manage_method_str[0]),
|
||||
.message_handle = _file_manage_message_callback,
|
||||
.usr_data = file_manage_context,
|
||||
.user_data_free = HAL_Free,
|
||||
};
|
||||
|
||||
rc = service_mqtt_service_register(client, ¶ms);
|
||||
if (rc) {
|
||||
HAL_Free(file_manage_context);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief File manage deinit, unregister handler from server list.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
*/
|
||||
void IOT_FileManage_Deinit(void *client)
|
||||
{
|
||||
POINTER_SANITY_CHECK_RTN(client);
|
||||
|
||||
service_mqtt_service_unregister(client, SERVICE_TYPE_FILE_MANAGE);
|
||||
service_mqtt_deinit(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Report file manage message.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[out] buf publish message buffer
|
||||
* @param[in] buf_len buffer len
|
||||
* @param[in] report_type @see IotFileManageReportType
|
||||
* @param[in] progress progress using in IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOADING
|
||||
* @param[in] file_name_or_token token using in post event;file name using in other event
|
||||
* @param[in] version file version
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int IOT_FileManage_Report(void *client, char *buf, int buf_len, IotFileManageReportType report_type, int progress,
|
||||
const char *file_name_or_token, const char *version)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
NUMBERIC_SANITY_CHECK(buf_len, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(file_name_or_token, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(version, QCLOUD_ERR_INVAL);
|
||||
|
||||
/**
|
||||
* @brief order @see IotOTAReportType
|
||||
*
|
||||
*/
|
||||
const char *state_string[] = {
|
||||
"downloading", "burning", "done", "fail", "fail", "fail", "fail",
|
||||
"fail", "fail", "done", "fail", "done", "fail",
|
||||
};
|
||||
|
||||
int result_code[] = {0, 0, 0, -1, -2, -3, -4, -5, -6, 0, -7, 0, -8};
|
||||
|
||||
const char *result_msg[] = {
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"timeout",
|
||||
"file not exit",
|
||||
"auth fail",
|
||||
"md5 not match",
|
||||
"upgrade fail",
|
||||
"space not enough",
|
||||
"",
|
||||
"del fail",
|
||||
"",
|
||||
"post fail",
|
||||
};
|
||||
|
||||
int len;
|
||||
switch (report_type) {
|
||||
case IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOADING:
|
||||
len = HAL_Snprintf(
|
||||
buf, buf_len,
|
||||
"{\"method\":\"report_progress\",\"report\":{\"progress\":{\"resource_name\":\"%s\",\"state\":\"%s\","
|
||||
"\"percent\":\"%d\",\"result_code\":\"%d\",\"result_msg\":\"\"},\"version\":\"%s\"}}",
|
||||
file_name_or_token, state_string[report_type], progress, result_code[report_type], version);
|
||||
break;
|
||||
case IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_BEGIN:
|
||||
case IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_SUCCESS:
|
||||
case IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOAD_TIMEOUT:
|
||||
case IOT_FILE_MANAGE_REPORT_TYPE_FILE_NOT_EXIST:
|
||||
case IOT_FILE_MANAGE_REPORT_TYPE_AUTH_FAIL:
|
||||
case IOT_FILE_MANAGE_REPORT_TYPE_MD5_NOT_MATCH:
|
||||
case IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_FAIL:
|
||||
case IOT_FILE_MANAGE_REPORT_TYPE_SPACE_NOT_ENOUGH:
|
||||
len = HAL_Snprintf(buf, buf_len,
|
||||
"{\"method\":\"report_result\",\"report\":{\"progress\":{\"resource_name\":\"%s\","
|
||||
"\"state\":\"%s\",\"result_code\":\"%d\",\"result_msg\":\"%s\"},\"version\":\"%s\"}}",
|
||||
file_name_or_token, state_string[report_type], result_code[report_type],
|
||||
result_msg[report_type], version);
|
||||
break;
|
||||
case IOT_FILE_MANAGE_REPORT_TYPE_DEL_SUCCESS:
|
||||
case IOT_FILE_MANAGE_REPORT_TYPE_DEL_FAIL:
|
||||
len = HAL_Snprintf(buf, buf_len,
|
||||
"{\"method\":\"del_result\",\"report\":{\"progress\":{\"resource_name\":\"%s\","
|
||||
"\"state\":\"%s\",\"result_code\":\"%d\", \"result_msg\":\"%s\"}, \"version\":\"%s\"}}",
|
||||
file_name_or_token, state_string[report_type], result_code[report_type],
|
||||
result_msg[report_type], version);
|
||||
break;
|
||||
case IOT_FILE_MANAGE_REPORT_TYPE_POST_SUCCESS:
|
||||
case IOT_FILE_MANAGE_REPORT_TYPE_POST_FAIL:
|
||||
len = HAL_Snprintf(buf, buf_len,
|
||||
"{\"method\":\"report_post_result\",\"report\":{\"progress\":{\"resource_token\":\"%s\","
|
||||
"\"state\":\"%s\",\"result_code\":\"%d\", \"result_msg\":\"%s\"}}}",
|
||||
file_name_or_token, state_string[report_type], result_code[report_type],
|
||||
result_msg[report_type]);
|
||||
break;
|
||||
default:
|
||||
return QCLOUD_ERR_INVAL;
|
||||
}
|
||||
return service_mqtt_publish(client, QOS0, buf, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Report file list to server mqtt topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[out] buf publish message buffer
|
||||
* @param[in] buf_len buffer len
|
||||
* @param[in] file_list file list of @see IotFileManageFileInfo; file name which is "\0" means invalid.
|
||||
* @param[in] max_num max num of file list.
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int IOT_FileManage_ReportFileList(void *client, char *buf, int buf_len, const IotFileManageFileInfo file_list[],
|
||||
int max_num)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
NUMBERIC_SANITY_CHECK(buf_len, QCLOUD_ERR_INVAL);
|
||||
|
||||
int len = HAL_Snprintf(buf, buf_len, "{\"method\":\"report_version\",\"report\":{\"resource_list\":[");
|
||||
for (int i = 0; i < max_num; i++) {
|
||||
if (file_list[i].file_type == IOT_FILE_MANAGE_FILE_TYPE_UNKOWN) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file_list[i].file_name[0] != '\0') {
|
||||
len += HAL_Snprintf(buf + len, buf_len - len,
|
||||
"{\"resource_name\":\"%s\",\"version\":\"%s\",\"resource_type\":\"%s\"},",
|
||||
file_list[i].file_name, file_list[i].file_version,
|
||||
sg_file_manage_file_type_str[file_list[i].file_type]);
|
||||
}
|
||||
}
|
||||
if (buf[len - 1] != '[') {
|
||||
len--; // remove the last ','
|
||||
}
|
||||
len += HAL_Snprintf(buf + len, buf_len - len, "]}}");
|
||||
return service_mqtt_publish(client, QOS0, buf, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Request url to upload.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[out] buf publish message buffer
|
||||
* @param[in] buf_len buffer len
|
||||
* @param[in] file_info file to upload, @see IotFileManageFileInfo
|
||||
* @param[in] request_id user defined, to keep unique.
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int IOT_FileManage_PostRequest(void *client, char *buf, int buf_len, const IotFileManageFileInfo *file_info,
|
||||
int request_id)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
|
||||
NUMBERIC_SANITY_CHECK(buf_len, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(file_info, QCLOUD_ERR_INVAL);
|
||||
NUMBERIC_SANITY_CHECK(file_info->file_type, QCLOUD_ERR_INVAL);
|
||||
|
||||
int len = HAL_Snprintf(buf, buf_len,
|
||||
"{\"method\":\"request_url\",\"request_id\":\"%d\","
|
||||
"\"report\":{\"resource_name\":\"%s\",\"version\":\"%s\",\"resource_type\":\"%s\"}}",
|
||||
request_id, file_info->file_name, file_info->file_version,
|
||||
sg_file_manage_file_type_str[file_info->file_type]);
|
||||
return service_mqtt_publish(client, QOS0, buf, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get file type according to string. @see sg_file_manage_file_type_str.
|
||||
*
|
||||
* @param[in] file_type file type string
|
||||
* @param[in] len string length
|
||||
* @return @see IotFileManageFileType
|
||||
*/
|
||||
IotFileManageFileType IOT_FileManage_GetFileType(const char *file_type, int len)
|
||||
{
|
||||
for (int i = 0; i < sizeof(sg_file_manage_file_type_str) / sizeof(sg_file_manage_file_type_str[0]); i++) {
|
||||
if (len != strlen(sg_file_manage_file_type_str[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strncmp(file_type, sg_file_manage_file_type_str[i], len)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return IOT_FILE_MANAGE_FILE_TYPE_UNKOWN;
|
||||
}
|
@@ -0,0 +1,364 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file sevice_mqtt.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2022-01-11
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2022-01-11 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "service_mqtt.h"
|
||||
|
||||
/**
|
||||
* @brief Direction(upstream/downstream) for topic.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
SERVICE_TOPIC_DIRECTION_UP = 0,
|
||||
SERVICE_TOPIC_DIRECTION_DOWN,
|
||||
} ServiceTopicDirection;
|
||||
|
||||
/**
|
||||
* @brief Context of service mqtt, callback and user data.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
void *service_list;
|
||||
void *usr_data;
|
||||
} ServiceMqttContext;
|
||||
|
||||
/**
|
||||
* @brief Service list conext.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
const char *method;
|
||||
int method_len;
|
||||
const MQTTMessage *message;
|
||||
} ServiceListContext;
|
||||
|
||||
/**
|
||||
* @brief Generate topic string.
|
||||
*
|
||||
* @param[out] buf buffer for topic name
|
||||
* @param[in] buf_len buffer length
|
||||
* @param[in] direction @see ServiceTopicDirection
|
||||
* @param[in] product_id product id of device
|
||||
* @param[in] device_name device name of device
|
||||
* @return > 0 for length of topic name, others for fail.
|
||||
*/
|
||||
static int _service_mqtt_topic_generate(char *buf, int buf_len, ServiceTopicDirection direction, const char *product_id,
|
||||
const char *device_name)
|
||||
{
|
||||
return HAL_Snprintf(buf, buf_len, "$thing/%s/service/%s/%s", direction ? "down" : "up",
|
||||
STRING_PTR_PRINT_SANITY_CHECK(product_id), STRING_PTR_PRINT_SANITY_CHECK(device_name));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// server mqtt context
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Malloc service context and service list.
|
||||
*
|
||||
* @return @see ServiceMqttContext
|
||||
*/
|
||||
static ServiceMqttContext *_service_mqtt_context_malloc(void)
|
||||
{
|
||||
ServiceMqttContext *context = (ServiceMqttContext *)HAL_Malloc(sizeof(ServiceMqttContext));
|
||||
if (!context) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UtilsListFunc func = {
|
||||
.list_malloc = HAL_Malloc,
|
||||
.list_free = HAL_Free,
|
||||
.list_lock_init = HAL_MutexCreate,
|
||||
.list_lock = HAL_MutexLock,
|
||||
.list_unlock = HAL_MutexUnlock,
|
||||
.list_lock_deinit = HAL_MutexDestroy,
|
||||
};
|
||||
context->service_list = utils_list_create(func, 10);
|
||||
if (!context->service_list) {
|
||||
HAL_Free(context);
|
||||
context = NULL;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Free service context and service list.
|
||||
*
|
||||
* @param usr_data @see ServiceMqttContext
|
||||
*/
|
||||
static void _service_mqtt_context_free(void *usr_data)
|
||||
{
|
||||
ServiceMqttContext *context = (ServiceMqttContext *)usr_data;
|
||||
utils_list_destroy(context->service_list);
|
||||
HAL_Free(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get service mqtt context from client.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see ServiceMqttContext
|
||||
*/
|
||||
static ServiceMqttContext *_service_mqtt_context_get(void *client)
|
||||
{
|
||||
char service_mqtt_topic[MAX_SIZE_OF_CLOUD_TOPIC];
|
||||
_service_mqtt_topic_generate(service_mqtt_topic, MAX_SIZE_OF_CLOUD_TOPIC, SERVICE_TOPIC_DIRECTION_DOWN,
|
||||
IOT_MQTT_GetDeviceInfo(client)->product_id,
|
||||
IOT_MQTT_GetDeviceInfo(client)->device_name);
|
||||
return (ServiceMqttContext *)IOT_MQTT_GetSubUsrData(client, service_mqtt_topic);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// server list
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Unregister server from server list.
|
||||
*
|
||||
* @param[in] list pointer to server list
|
||||
* @param[in] node pointer tot server list node
|
||||
* @param[in] val @see ServiceRegisterParams
|
||||
* @param[in] usr_data @see ServiceType
|
||||
* @return @see UtilsListResult
|
||||
*/
|
||||
static UtilsListResult _server_list_unregister_callback(void *list, void *node, void *val, void *usr_data)
|
||||
{
|
||||
ServiceRegisterParams *params = (ServiceRegisterParams *)val;
|
||||
|
||||
ServiceType type = *(ServiceType *)usr_data;
|
||||
if (params->type == type) {
|
||||
if (params->user_data_free) {
|
||||
params->user_data_free(params->usr_data);
|
||||
}
|
||||
utils_list_remove(list, node);
|
||||
return LIST_TRAVERSE_BREAK;
|
||||
}
|
||||
return LIST_TRAVERSE_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback server handler from server list.
|
||||
*
|
||||
* @param[in] list pointer to server list
|
||||
* @param[in] node pointer tot server list node
|
||||
* @param[in] val @see ServiceRegisterParams
|
||||
* @param[in] usr_data @see ServiceListContext
|
||||
* @return @see UtilsListResult
|
||||
*/
|
||||
static UtilsListResult _server_list_process_message_callback(void *list, void *node, void *val, void *usr_data)
|
||||
{
|
||||
ServiceRegisterParams *params = (ServiceRegisterParams *)val;
|
||||
ServiceListContext *context = (ServiceListContext *)usr_data;
|
||||
|
||||
for (int i = 0; i < params->method_num; i++) {
|
||||
int len = strlen(params->method_list[i]);
|
||||
if (len == context->method_len && !strncmp(context->method, params->method_list[i], len)) {
|
||||
params->message_handle(usr_data, context->message, params->usr_data);
|
||||
return LIST_TRAVERSE_BREAK;
|
||||
}
|
||||
}
|
||||
return LIST_TRAVERSE_CONTINUE;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// mqtt
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Handle server mqtt topic down stream message.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] message @see MQTTMessage
|
||||
* @param[in] usr_data @see ServiceMqttContext
|
||||
*/
|
||||
static void _service_mqtt_message_handle(void *client, const MQTTMessage *message, void *usr_data)
|
||||
{
|
||||
Log_d("receive service message:%.*s", message->payload_len, message->payload_str);
|
||||
|
||||
UtilsJsonValue method;
|
||||
|
||||
int rc = utils_json_value_get("method", sizeof("method") - 1, message->payload_str, message->payload_len, &method);
|
||||
if (rc) {
|
||||
return;
|
||||
}
|
||||
|
||||
// process list
|
||||
ServiceListContext server_list_context = {
|
||||
.method = method.value,
|
||||
.method_len = method.value_len,
|
||||
.message = message,
|
||||
};
|
||||
void *list = ((ServiceMqttContext *)usr_data)->service_list;
|
||||
utils_list_process(list, LIST_HEAD, _server_list_process_message_callback, &server_list_context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if topic already subscribed, if not then subscribe.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int _service_mqtt_topic_check_and_sub(void *client)
|
||||
{
|
||||
char service_mqtt_topic[MAX_SIZE_OF_CLOUD_TOPIC];
|
||||
_service_mqtt_topic_generate(service_mqtt_topic, MAX_SIZE_OF_CLOUD_TOPIC, SERVICE_TOPIC_DIRECTION_DOWN,
|
||||
IOT_MQTT_GetDeviceInfo(client)->product_id,
|
||||
IOT_MQTT_GetDeviceInfo(client)->device_name);
|
||||
|
||||
int rc = 0;
|
||||
|
||||
ServiceMqttContext *context = _service_mqtt_context_malloc();
|
||||
if (!context) {
|
||||
return QCLOUD_ERR_MALLOC;
|
||||
}
|
||||
|
||||
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
|
||||
sub_params.on_message_handler = _service_mqtt_message_handle;
|
||||
sub_params.qos = QOS1;
|
||||
sub_params.user_data = context;
|
||||
sub_params.user_data_free = _service_mqtt_context_free;
|
||||
|
||||
rc = IOT_MQTT_SubscribeSync(client, service_mqtt_topic, &sub_params);
|
||||
if (rc) {
|
||||
_service_mqtt_context_free(context);
|
||||
Log_e("subscribe topic %s failed!", service_mqtt_topic);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe service mqtt topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int _service_mqtt_unsubscribe(void *client)
|
||||
{
|
||||
char service_mqtt_topic[MAX_SIZE_OF_CLOUD_TOPIC];
|
||||
_service_mqtt_topic_generate(service_mqtt_topic, MAX_SIZE_OF_CLOUD_TOPIC, SERVICE_TOPIC_DIRECTION_DOWN,
|
||||
IOT_MQTT_GetDeviceInfo(client)->product_id,
|
||||
IOT_MQTT_GetDeviceInfo(client)->device_name);
|
||||
return IOT_MQTT_Unsubscribe(client, service_mqtt_topic);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// api
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Subscribe service mqtt.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int service_mqtt_init(void *client)
|
||||
{
|
||||
return _service_mqtt_topic_check_and_sub(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief If no service in the service list, then unsubscribe.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
*/
|
||||
void service_mqtt_deinit(void *client)
|
||||
{
|
||||
ServiceMqttContext *context = _service_mqtt_context_get(client);
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!utils_list_len_get(context->service_list)) {
|
||||
_service_mqtt_unsubscribe(client);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Publish to service mqtt topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] qos @see QoS
|
||||
* @param[in] payload payload of mqtt packet
|
||||
* @param[in] payload_len payload len
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int service_mqtt_publish(void *client, QoS qos, const char *payload, int payload_len)
|
||||
{
|
||||
char service_mqtt_topic[MAX_SIZE_OF_CLOUD_TOPIC];
|
||||
_service_mqtt_topic_generate(service_mqtt_topic, MAX_SIZE_OF_CLOUD_TOPIC, SERVICE_TOPIC_DIRECTION_UP,
|
||||
IOT_MQTT_GetDeviceInfo(client)->product_id,
|
||||
IOT_MQTT_GetDeviceInfo(client)->device_name);
|
||||
|
||||
PublishParams pub_params = DEFAULT_PUB_PARAMS;
|
||||
pub_params.qos = qos;
|
||||
pub_params.payload = (void *)payload;
|
||||
pub_params.payload_len = payload_len;
|
||||
return IOT_MQTT_Publish(client, service_mqtt_topic, &pub_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register server handler to server list.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] params @see ServiceRegisterParams
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int service_mqtt_service_register(void *client, const ServiceRegisterParams *params)
|
||||
{
|
||||
ServiceMqttContext *context = _service_mqtt_context_get(client);
|
||||
if (!context) {
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
ServiceRegisterParams *register_params = (ServiceRegisterParams *)HAL_Malloc(sizeof(ServiceRegisterParams));
|
||||
if (!register_params) {
|
||||
return QCLOUD_ERR_MALLOC;
|
||||
}
|
||||
memcpy(register_params, params, sizeof(ServiceRegisterParams));
|
||||
|
||||
void *node = utils_list_push(context->service_list, register_params);
|
||||
if (!node) {
|
||||
HAL_Free(register_params);
|
||||
return QCLOUD_ERR_MALLOC;
|
||||
}
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregister server handler from server list.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] type @see ServiceType
|
||||
*/
|
||||
void service_mqtt_service_unregister(void *client, ServiceType type)
|
||||
{
|
||||
ServiceMqttContext *context = _service_mqtt_context_get(client);
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
utils_list_process(context->service_list, LIST_HEAD, _server_list_unregister_callback, &type);
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 120
|
||||
DerivePointerAlignment: true
|
||||
PointerAlignment: Left
|
||||
SortIncludes: true
|
||||
IncludeBlocks: Preserve
|
||||
IndentPPDirectives: AfterHash
|
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file test_ota_mqtt.cc
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2022-01-18
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2022-01-18 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "mqtt_client_test.h"
|
||||
#include "qcloud_iot_explorer.h"
|
||||
|
||||
namespace mqtt_client_unittest {
|
||||
|
||||
static void _file_manage_update_file_callback(UtilsJsonValue file_name, UtilsJsonValue file_type,
|
||||
UtilsJsonValue version, UtilsJsonValue url, UtilsJsonValue md5sum,
|
||||
uint32_t file_size, void *usr_data) {
|
||||
Log_i("recv update file: file_name=%.*s|type=%.*s|version=%.*s|url=%.*s|md5sum=%.*s|file_size=%u",
|
||||
file_name.value_len, file_name.value, file_type.value_len, file_type.value, version.value_len, version.value,
|
||||
url.value_len, url.value, md5sum.value_len, md5sum.value, file_size);
|
||||
}
|
||||
|
||||
static void _file_manage_del_file_callback(UtilsJsonValue file_name, UtilsJsonValue file_type, UtilsJsonValue version,
|
||||
void *usr_data) {
|
||||
Log_i("recv del file: file_name=%.*s|type=%.*s|version=%.*s|", file_name.value_len, file_name.value,
|
||||
file_type.value_len, file_type.value, version.value_len, version.value);
|
||||
}
|
||||
|
||||
static void _file_manage_report_file_version_reponse_callback(UtilsJsonValue file_list, int result_code,
|
||||
void *usr_data) {
|
||||
Log_i("recv file version: file_list=%.*s|result_code=%d", file_list, result_code);
|
||||
}
|
||||
|
||||
static void _file_manage_request_file_url_response_callback(UtilsJsonValue url, UtilsJsonValue file_token,
|
||||
int result_code, void *usr_data) {
|
||||
Log_i("recv request response: url=%.*s|file_token=%.*s|result_code=%d", url.value_len, url.value,
|
||||
file_token.value_len, file_token.value, result_code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test file manage.
|
||||
*
|
||||
*/
|
||||
TEST_F(MqttClientTest, file_manage) {
|
||||
char buf[256];
|
||||
int buf_len = sizeof(buf);
|
||||
|
||||
IotFileManageFileInfo file_info = {0};
|
||||
|
||||
const char *file_name = "test.txt";
|
||||
const char *version = "1.0.0";
|
||||
|
||||
strncpy(file_info.file_name, file_name, strlen(file_name));
|
||||
strncpy(file_info.file_version, version, strlen(version));
|
||||
file_info.file_type = IOT_FILE_MANAGE_FILE_TYPE_FILE;
|
||||
|
||||
IotFileManageCallback file_manage_callback = {
|
||||
.update_file_callback = _file_manage_update_file_callback,
|
||||
.del_file_callback = _file_manage_del_file_callback,
|
||||
.report_file_version_reponse_callback = _file_manage_report_file_version_reponse_callback,
|
||||
.request_file_url_response_callback = _file_manage_request_file_url_response_callback,
|
||||
};
|
||||
|
||||
ASSERT_EQ(IOT_FileManage_Init(client, file_manage_callback, client), 0);
|
||||
ASSERT_GE(IOT_FileManage_ReportFileList(client, buf, buf_len, NULL, 0), 0);
|
||||
ASSERT_GE(IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOADING, 0, file_name, version),
|
||||
0);
|
||||
ASSERT_GE(
|
||||
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_BEGIN, 100, file_name, version),
|
||||
0);
|
||||
ASSERT_GE(
|
||||
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_SUCCESS, 0, file_name, version),
|
||||
0);
|
||||
ASSERT_GE(
|
||||
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOAD_TIMEOUT, 0, file_name, version),
|
||||
0);
|
||||
ASSERT_GE(
|
||||
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_FILE_NOT_EXIST, 0, file_name, version),
|
||||
0);
|
||||
ASSERT_GE(IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_AUTH_FAIL, 0, file_name, version),
|
||||
0);
|
||||
ASSERT_GE(
|
||||
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_MD5_NOT_MATCH, 0, file_name, version), 0);
|
||||
ASSERT_GE(
|
||||
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_FAIL, 0, file_name, version), 0);
|
||||
ASSERT_GE(
|
||||
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_SPACE_NOT_ENOUGH, 0, file_name, version),
|
||||
0);
|
||||
ASSERT_GE(IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_DEL_SUCCESS, 0, file_name, version),
|
||||
0);
|
||||
ASSERT_GE(IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_DEL_FAIL, 0, file_name, version),
|
||||
0);
|
||||
ASSERT_GE(IOT_FileManage_PostRequest(client, buf, buf_len, &file_info, 0), 0);
|
||||
ASSERT_EQ(IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT), 0);
|
||||
ASSERT_GE(
|
||||
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_POST_SUCCESS, 0, file_name, version), 0);
|
||||
ASSERT_GE(IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_POST_FAIL, 0, file_name, version),
|
||||
0);
|
||||
|
||||
ASSERT_EQ(IOT_FileManage_GetFileType("FILE", sizeof("FILE") - 1), IOT_FILE_MANAGE_FILE_TYPE_FILE);
|
||||
ASSERT_EQ(IOT_FileManage_GetFileType("AUDIO", sizeof("AUDIO") - 1), IOT_FILE_MANAGE_FILE_TYPE_AUDIO);
|
||||
ASSERT_EQ(IOT_FileManage_GetFileType("VOICE", sizeof("VOICE") - 1), IOT_FILE_MANAGE_FILE_TYPE_VOICE);
|
||||
ASSERT_EQ(IOT_FileManage_GetFileType("VIDEO", sizeof("VIDEO") - 1), IOT_FILE_MANAGE_FILE_TYPE_VIDEO);
|
||||
ASSERT_EQ(IOT_FileManage_GetFileType("TEXT", sizeof("TEXT") - 1), IOT_FILE_MANAGE_FILE_TYPE_UNKOWN);
|
||||
|
||||
IOT_FileManage_Deinit(client);
|
||||
}
|
||||
|
||||
} // namespace mqtt_client_unittest
|
@@ -0,0 +1,14 @@
|
||||
file(GLOB src_broadcast ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
|
||||
set(inc_broadcast ${CMAKE_CURRENT_SOURCE_DIR}/inc)
|
||||
|
||||
set(src_services ${src_services} ${src_broadcast} PARENT_SCOPE)
|
||||
set(inc_services ${inc_services} ${inc_broadcast} PARENT_SCOPE)
|
||||
|
||||
file(GLOB src_broadcast_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/broadcast_sample.c)
|
||||
add_executable(broadcast_sample ${src_broadcast_sample})
|
||||
target_link_libraries(broadcast_sample ${libsdk})
|
||||
|
||||
if( ${CONFIG_IOT_TEST} STREQUAL "ON")
|
||||
file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
|
||||
set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
|
||||
endif()
|
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file broadcast_sample.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-07-18
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "qcloud_iot_common.h"
|
||||
#include "qcloud_iot_hub.h"
|
||||
|
||||
#include "utils_log.h"
|
||||
|
||||
/**
|
||||
* @brief MQTT event callback, @see MQTTEventHandleFun
|
||||
*
|
||||
* @param[in] client pointer to mqtt client
|
||||
* @param[in] handle_context context
|
||||
* @param[in] msg msg
|
||||
*/
|
||||
static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
|
||||
{
|
||||
MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
|
||||
uintptr_t packet_id = (uintptr_t)msg->msg;
|
||||
|
||||
switch (msg->event_type) {
|
||||
case MQTT_EVENT_UNDEF:
|
||||
Log_i("undefined event occur.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DISCONNECT:
|
||||
Log_i("MQTT disconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_RECONNECT:
|
||||
Log_i("MQTT reconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_RECEIVED:
|
||||
Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
|
||||
mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
|
||||
mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
|
||||
break;
|
||||
case MQTT_EVENT_SUBSCRIBE_SUCCESS:
|
||||
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
|
||||
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_NACK:
|
||||
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
|
||||
Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
|
||||
Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_NACK:
|
||||
Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_SUCCESS:
|
||||
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_TIMEOUT:
|
||||
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_NACK:
|
||||
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
default:
|
||||
Log_i("Should NOT arrive here.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Setup MQTT construct parameters.
|
||||
*
|
||||
* @param[in,out] initParams @see MQTTInitParams
|
||||
* @param[in] device_info @see DeviceInfo
|
||||
*/
|
||||
static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
|
||||
{
|
||||
init_params->device_info = device_info;
|
||||
init_params->command_timeout = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT;
|
||||
init_params->keep_alive_interval_ms = QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL;
|
||||
init_params->auto_connect_enable = 1;
|
||||
init_params->event_handle.h_fp = _mqtt_event_handler;
|
||||
init_params->event_handle.context = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback when MQTT msg arrives @see OnMessageHandler
|
||||
*
|
||||
* @param[in, out] client pointer to mqtt client
|
||||
* @param[in] message publish message from server
|
||||
* @param[in] usr_data user data of SubscribeParams, @see SubscribeParams
|
||||
*/
|
||||
static void _on_broadcast_arrived_callback(void *client, const char *msg, int msg_len, void *usr_data)
|
||||
{
|
||||
Log_i("Receive broadcast message:%.*s, usr data:%d", msg_len, STRING_PTR_PRINT_SANITY_CHECK(msg), *(int *)usr_data);
|
||||
(*(int *)usr_data)++;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Main
|
||||
// ----------------------------------------------------------------------------
|
||||
static int sg_main_exit = 0;
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void _main_exit(int sig)
|
||||
{
|
||||
Log_e("demo exit by signal:%d\n", sig);
|
||||
sg_main_exit = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#ifdef __linux__
|
||||
signal(SIGINT, _main_exit);
|
||||
#endif
|
||||
|
||||
int rc;
|
||||
|
||||
// init log level
|
||||
LogHandleFunc func = {0};
|
||||
|
||||
func.log_malloc = HAL_Malloc;
|
||||
func.log_free = HAL_Free;
|
||||
func.log_get_current_time_str = HAL_Timer_Current;
|
||||
func.log_printf = HAL_Printf;
|
||||
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
|
||||
|
||||
DeviceInfo device_info;
|
||||
|
||||
rc = HAL_GetDevInfo((void *)&device_info);
|
||||
if (rc) {
|
||||
Log_e("get device info failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// init connection
|
||||
MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
|
||||
_setup_connect_init_params(&init_params, &device_info);
|
||||
|
||||
// create MQTT client and connect with server
|
||||
void *client = IOT_MQTT_Construct(&init_params);
|
||||
if (client) {
|
||||
Log_i("Cloud Device Construct Success");
|
||||
} else {
|
||||
Log_e("MQTT Construct failed!");
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
// subscribe normal topics and wait result
|
||||
static int test_usr_data = 0;
|
||||
|
||||
rc = IOT_Broadcast_Init(client, _on_broadcast_arrived_callback, &test_usr_data);
|
||||
if (rc) {
|
||||
Log_e("Client Subscribe Topic Failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
do {
|
||||
rc = IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT);
|
||||
if (rc == QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT) {
|
||||
HAL_SleepMs(1000);
|
||||
continue;
|
||||
} else if (rc != QCLOUD_RET_SUCCESS && rc != QCLOUD_RET_MQTT_RECONNECTED) {
|
||||
Log_e("exit with error: %d", rc);
|
||||
break;
|
||||
}
|
||||
} while (!sg_main_exit);
|
||||
|
||||
rc = IOT_Broadcast_Deinit(client);
|
||||
rc |= IOT_MQTT_Destroy(&client);
|
||||
|
||||
utils_log_deinit();
|
||||
return rc;
|
||||
}
|
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file broadcast.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-07-18
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "qcloud_iot_broadcast.h"
|
||||
|
||||
/**
|
||||
* @brief Context of broadcast message.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
OnBroadcastArrivedCallback callback; /**< callback to handle message */
|
||||
void * usr_data; /**< usr data using in callback */
|
||||
} QcloudIotBroadcastContext;
|
||||
|
||||
/**
|
||||
* @brief Callback for broadcast topic message
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] message broadcast message @see MQTTMessage
|
||||
* @param[in,out] context @see QcloudIotBroadcastContext
|
||||
*/
|
||||
static void _broadcast_message_cb(void *client, const MQTTMessage *message, void *context)
|
||||
{
|
||||
QcloudIotBroadcastContext *broadcast_context = (QcloudIotBroadcastContext *)context;
|
||||
|
||||
Log_d("topic=%.*s", message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(message->topic_name));
|
||||
Log_i("len=%u, topic_msg=%.*s", message->payload_len, message->payload_len,
|
||||
STRING_PTR_PRINT_SANITY_CHECK(message->payload_str));
|
||||
|
||||
if (broadcast_context->callback) {
|
||||
broadcast_context->callback(client, message->payload_str, message->payload_len, broadcast_context->usr_data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Subscribe broadcast topic with callback.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] callback callback to handle message
|
||||
* @param[in] usr_data usr data using in callback
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int IOT_Broadcast_Init(void *client, OnBroadcastArrivedCallback callback, void *usr_data)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
POINTER_SANITY_CHECK(callback, QCLOUD_ERR_INVAL);
|
||||
|
||||
int rc = 0;
|
||||
char broadcast_topic[MAX_SIZE_OF_CLOUD_TOPIC];
|
||||
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
|
||||
|
||||
/**
|
||||
* @brief Using static for only one broardcast topic can be subscribed to.
|
||||
*
|
||||
*/
|
||||
QcloudIotBroadcastContext *broadcast_context =
|
||||
(QcloudIotBroadcastContext *)HAL_Malloc(sizeof(QcloudIotBroadcastContext));
|
||||
if (!broadcast_context) {
|
||||
return QCLOUD_ERR_MALLOC;
|
||||
}
|
||||
|
||||
broadcast_context->callback = callback;
|
||||
broadcast_context->usr_data = usr_data;
|
||||
|
||||
HAL_Snprintf(broadcast_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$broadcast/rxd/%s/%s",
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
|
||||
|
||||
sub_params.on_message_handler = _broadcast_message_cb;
|
||||
sub_params.qos = QOS1;
|
||||
sub_params.user_data = broadcast_context;
|
||||
sub_params.user_data_free = HAL_Free;
|
||||
|
||||
rc = IOT_MQTT_SubscribeSync(client, broadcast_topic, &sub_params);
|
||||
if (rc) {
|
||||
HAL_Free(broadcast_context);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unsubscribe broadcast topic.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return @see IotReturnCode
|
||||
*/
|
||||
int IOT_Broadcast_Deinit(void *client)
|
||||
{
|
||||
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
|
||||
char broadcast_topic[MAX_SIZE_OF_CLOUD_TOPIC];
|
||||
HAL_Snprintf(broadcast_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$broadcast/rxd/%s/%s",
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
|
||||
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
|
||||
return IOT_MQTT_Unsubscribe(client, broadcast_topic) > 0 ? QCLOUD_RET_SUCCESS : QCLOUD_ERR_FAILURE;
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 120
|
||||
DerivePointerAlignment: true
|
||||
PointerAlignment: Left
|
||||
SortIncludes: true
|
||||
IncludeBlocks: Preserve
|
||||
IndentPPDirectives: AfterHash
|
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @file test_broadcast.cc
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-07-18
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "mqtt_client_test.h"
|
||||
#include "qcloud_iot_common.h"
|
||||
#include "qcloud_iot_hub.h"
|
||||
|
||||
namespace mqtt_client_unittest {
|
||||
|
||||
/**
|
||||
* @brief Callback when MQTT msg arrives @see OnMessageHandler
|
||||
*
|
||||
* @param[in, out] client pointer to mqtt client
|
||||
* @param[in] message publish message from server
|
||||
* @param[in] usr_data user data of SubscribeParams, @see SubscribeParams
|
||||
*/
|
||||
void on_broadcast_arrived_callback(void *client, const char *msg, int msg_len, void *usr_data) {
|
||||
Log_i("Receive broadcast message:%.*s, usr data:%d", msg_len, STRING_PTR_PRINT_SANITY_CHECK(msg),
|
||||
*(reinterpret_cast<int *>(usr_data)));
|
||||
*(reinterpret_cast<int *>(usr_data)) += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test broadcast.
|
||||
*
|
||||
*/
|
||||
TEST_F(MqttClientTest, broadcast) {
|
||||
int usr_data = 0;
|
||||
ASSERT_EQ(IOT_Broadcast_Init(client, on_broadcast_arrived_callback, &usr_data), 0);
|
||||
ASSERT_EQ(IOT_Broadcast_Deinit(client), 0);
|
||||
}
|
||||
|
||||
} // namespace mqtt_client_unittest
|
Reference in New Issue
Block a user