feat: 移植腾讯云物联网开发平台 C SDK

This commit is contained in:
fancyxu
2022-07-01 11:06:09 +08:00
parent 2be1169b0b
commit 0acc079ed6
195 changed files with 36646 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
BasedOnStyle: Google
BreakBeforeBraces: Linux
AlignConsecutiveDeclarations: true
AlignConsecutiveMacros: true
AlignConsecutiveAssignments: true
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: false
SortIncludes: false
IndentWidth: 4
ColumnLimit: 120

View File

@@ -0,0 +1,11 @@
source:
auto_generate_source:
# 自动生成代码文件的正则表达式若无统一标识格式可以指定具体目录样例可参考test_source举例
filepath_regex: [
".*/build/.*",
".*/output/.*",
]
third_party_source:
filepath_regex: [
".*/3rd/mbedtls/mbedtls/.*",
]

View File

@@ -0,0 +1,7 @@
/.vscode/
/build/
/output/
/tmp/
build.yml
app_ota_fw.bin
break_point.dat

View File

@@ -0,0 +1,4 @@
[submodule "3rd/mbedtls/mbedtls"]
path = 3rd/mbedtls/mbedtls
url = https://github.com/ARMmbed/mbedtls.git
branch = mbedtls-2.16

View File

@@ -0,0 +1,18 @@
option(USE_STATIC_MBEDTLS_LIBRARY "Build mbed TLS static library." ON)
option(USE_SHARED_MBEDTLS_LIBRARY "Build mbed TLS shared library." OFF)
option(LINK_WITH_PTHREAD "Explicitly link mbed TLS library to pthread." OFF)
file(GLOB src_mbedtls
${CMAKE_CURRENT_SOURCE_DIR}/mbedtls/library/*.c
${CMAKE_CURRENT_SOURCE_DIR}/port/src/*.c
)
if(${CONFIG_EXTRACT_SRC} STREQUAL "ON")
file(GLOB inc_mbedtls ${CMAKE_CURRENT_SOURCE_DIR}/mbedtls/include/mbedtls/*.h)
file(GLOB inc_port ${CMAKE_CURRENT_SOURCE_DIR}/port/inc/*h)
file(COPY ${src_mbedtls} DESTINATION ${PROJECT_SOURCE_DIR}/output/sdk/src/3rd/mbedtls)
file(COPY ${inc_mbedtls} DESTINATION ${PROJECT_SOURCE_DIR}/output/sdk/inc/3rd/mbedtls)
file(COPY ${inc_port} DESTINATION ${PROJECT_SOURCE_DIR}/output/sdk/inc/3rd)
endif()
add_library(mbedtls STATIC ${src_mbedtls})

View File

@@ -0,0 +1,119 @@
/**
* @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 qcloud_iot_tls_client.h
* @brief header file for tls client
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-12
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-12 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_3RD_MBEDTLS_PORT_INC_QCLOUD_IOT_TLS_CLIENT_H_
#define IOT_HUB_DEVICE_C_SDK_3RD_MBEDTLS_PORT_INC_QCLOUD_IOT_TLS_CLIENT_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "qcloud_iot_platform.h"
#ifndef MAX_SIZE_OF_CLIENT_ID
#define MAX_SIZE_OF_CLIENT_ID (80)
#endif
/**
* @brief Define structure for TLS connection parameters
*
*/
typedef struct {
const char *ca_crt;
uint16_t ca_crt_len;
#ifdef AUTH_MODE_CERT
/**
* Device with certificate
*/
const char *cert_file; // public certificate file
const char *key_file; // pravite certificate file
#else
/**
* Device with PSK
*/
const char *psk; // PSK string
const char *psk_id; // PSK ID
#endif
size_t psk_length; // PSK length
unsigned int timeout_ms; // SSL handshake timeout in millisecond
} SSLConnectParams;
typedef SSLConnectParams TLSConnectParams;
/**
* @brief Tls setup and sharkhand
*
* @param[in] connect_params connect params of tls
* @param[in] host server host
* @param[in] port server port
* @return tls handle, 0 for fail
*/
uintptr_t qcloud_iot_tls_client_connect(const TLSConnectParams *connect_params, const char *host, const char *port);
/**
* @brief Disconect and free
*
* @param[in,out] handle tls handle
*/
void qcloud_iot_tls_client_disconnect(uintptr_t handle);
/**
* @brief Write msg with tls
*
* @param[in,out] handle tls handle
* @param[in] msg msg to write
* @param[in] total_len number of bytes to write
* @param[in] timeout_ms timeout millsecond
* @param[out] written_len number of bytes writtern
* @return @see IotReturnCode
*/
int qcloud_iot_tls_client_write(uintptr_t handle, unsigned char *msg, size_t total_len, uint32_t timeout_ms,
size_t *written_len);
/**
* @brief Read msg with tls
*
* @param[in,out] handle tls handle
* @param[out] msg msg buffer
* @param[in] total_len buffer len
* @param[in] timeout_ms timeout millsecond
* @param[out] read_len number of bytes read
* @return @see IotReturnCode
*/
int qcloud_iot_tls_client_read(uintptr_t handle, unsigned char *msg, size_t total_len, uint32_t timeout_ms,
size_t *read_len);
#if defined(__cplusplus)
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_3RD_MBEDTLS_PORT_INC_QCLOUD_IOT_TLS_CLIENT_H_

View File

@@ -0,0 +1,81 @@
/**
* @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 qcloud_iot_tls_psk_config.h
* @brief set config for tls psk
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-12
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-12 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_3RD_MBEDTLS_PORT_INC_QCLOUD_IOT_TLS_PSK_CONFIG_H
#define IOT_HUB_DEVICE_C_SDK_3RD_MBEDTLS_PORT_INC_QCLOUD_IOT_TLS_PSK_CONFIG_H
#if defined(__cplusplus)
extern "C" {
#endif
/* System support */
#define MBEDTLS_HAVE_ASM
#define MBEDTLS_HAVE_TIME
#define MBEDTLS_TIMING_ALT
// #define MBEDTLS_ENTROPY_HARDWARE_ALT
// #define MBEDTLS_NO_PLATFORM_ENTROPY
/* mbed TLS feature support */
#define MBEDTLS_CIPHER_MODE_CBC
#define MBEDTLS_CIPHER_PADDING_PKCS7
#define MBEDTLS_CIPHER_PADDING_ONE_AND_ZEROS
#define MBEDTLS_CIPHER_PADDING_ZEROS_AND_LEN
#define MBEDTLS_CIPHER_PADDING_ZEROS
#define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
/* mbed TLS modules */
#define MBEDTLS_AES_C
#define MBEDTLS_MD_C
#define MBEDTLS_MD5_C
#define MBEDTLS_PLATFORM_C
#define MBEDTLS_CIPHER_C
#define MBEDTLS_SHA1_C
#define MBEDTLS_SHA256_C
#define MBEDTLS_BASE64_C
#define MBEDTLS_CTR_DRBG_C
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_SSL_PROTO_TLS1_2
#define MBEDTLS_SSL_ENCRYPT_THEN_MAC
#define MBEDTLS_SSL_EXTENDED_MASTER_SECRET
#define MBEDTLS_SSL_SESSION_TICKETS
#define MBEDTLS_SSL_CLI_C
#define MBEDTLS_SSL_TLS_C
#define MBEDTLS_SSL_MAX_CONTENT_LEN 3584
#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA, MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA
#define MBEDTLS_TLS_DEFAULT_ALLOW_SHA1_IN_KEY_EXCHANGE
/* For testing with compat.sh */
#include "mbedtls/check_config.h"
#if defined(__cplusplus)
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_3RD_MBEDTLS_PORT_INC_QCLOUD_IOT_TLS_PSK_CONFIG_H

View File

@@ -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 qcloud_iot_net_socket.c
* @brief implements mbedtls net socket api for tls client
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-09
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-09 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "qcloud_iot_platform.h"
#include "mbedtls/net_sockets.h"
/**
* @brief Convert return code from IotReturnCode to mbedtls error code
*
* @param[in] iot_return_code @see IotReturnCode
* @return mbedtls error code
*/
static int _return_code_convert(int iot_return_code)
{
switch (iot_return_code) {
case QCLOUD_ERR_TCP_UNKNOWN_HOST:
return MBEDTLS_ERR_NET_UNKNOWN_HOST;
case QCLOUD_ERR_TCP_SOCKET_FAILED:
return MBEDTLS_ERR_NET_SOCKET_FAILED;
case QCLOUD_ERR_TCP_CONNECT:
return MBEDTLS_ERR_NET_CONNECT_FAILED;
case QCLOUD_ERR_TCP_WRITE_TIMEOUT:
return MBEDTLS_ERR_SSL_WANT_WRITE;
case QCLOUD_ERR_TCP_WRITE_FAIL:
return MBEDTLS_ERR_NET_SEND_FAILED;
case QCLOUD_ERR_TCP_PEER_SHUTDOWN:
return MBEDTLS_ERR_NET_CONN_RESET;
case QCLOUD_ERR_TCP_READ_TIMEOUT:
return MBEDTLS_ERR_SSL_TIMEOUT;
case QCLOUD_ERR_TCP_NOTHING_TO_READ:
return MBEDTLS_ERR_SSL_WANT_READ;
case QCLOUD_ERR_TCP_READ_FAIL:
return MBEDTLS_ERR_NET_RECV_FAILED;
default:
return -1;
}
}
/**
* @brief Init net context
*
* @param[in,out] ctx mbedtls net context handle
*/
void mbedtls_net_init(mbedtls_net_context *ctx)
{
ctx->fd = -1;
}
/**
* @brief Initiate a TCP connection with host:port and the given protocol
*
* @param[in,out] ctx mbedtls net context handle
* @param[in] host host to connect
* @param[in] port port to connect
* @param[in] proto no use for always tcp
* @return fd > 0 for success, others for mbedtls error code
*/
int mbedtls_net_connect(mbedtls_net_context *ctx, const char *host, const char *port, int proto)
{
ctx->fd = HAL_TCP_Connect(host, port);
return ctx->fd < 0 ? _return_code_convert(ctx->fd) : ctx->fd;
}
/**
* @brief Read at most 'len' characters
*
* @param[in] ctx mbedtls net context handle
* @param[out] buf data buffer
* @param[in] len data buffer len
* @return > 0 for data received bytes, others for mbedtls error code
*/
int mbedtls_net_recv(void *ctx, unsigned char *buf, size_t len)
{
int rc;
size_t rlen;
int fd = ((mbedtls_net_context *)ctx)->fd;
rc = HAL_TCP_Read(fd, buf, len, INT_MAX, &rlen);
return rc ? _return_code_convert(rc) : rlen;
}
/**
* @brief Read at most 'len' characters, blocking for at most 'timeout' ms
*
* @param[in] ctx mbedtls net context handle
* @param[out] buf data buffer
* @param[in] len data buffer len
* @param[in] timeout read timeout
* @return > 0 for data received bytes, others for mbedtls error code
*/
int mbedtls_net_recv_timeout(void *ctx, unsigned char *buf, size_t len, uint32_t timeout)
{
int rc;
size_t rlen;
int fd = ((mbedtls_net_context *)ctx)->fd;
rc = HAL_TCP_Read(fd, buf, len, timeout, &rlen);
return rc ? _return_code_convert(rc) : rlen;
}
/**
* @brief Write at most 'len' characters
*
* @param[in] ctx mbedtls net context handle
* @param[in] buf data buffer
* @param[in] len data buffer len
* @return > 0 for data writtern bytes, others for mbedtls error code
*/
int mbedtls_net_send(void *ctx, const unsigned char *buf, size_t len)
{
int rc;
size_t wlen;
int fd = ((mbedtls_net_context *)ctx)->fd;
rc = HAL_TCP_Write(fd, buf, len, INT_MAX, &wlen);
return rc ? _return_code_convert(rc) : wlen;
}
/**
* @brief Gracefully close the connection
*
* @param ctx mbedtls net context handle
*/
void mbedtls_net_free(mbedtls_net_context *ctx)
{
HAL_TCP_Disconnect(ctx->fd);
}

View File

@@ -0,0 +1,395 @@
/**
* @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 qcloud_iot_tls_client.c
* @brief implements tls client with mbedtls
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-12
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-12 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "qcloud_iot_tls_client.h"
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/error.h"
#include "mbedtls/debug.h"
#ifdef AUTH_MODE_KEY
/**
* @brief Only tls psk is supportted when using psk
*
*/
static const int ciphersuites[] = {MBEDTLS_TLS_PSK_WITH_AES_128_CBC_SHA, MBEDTLS_TLS_PSK_WITH_AES_256_CBC_SHA, 0};
#endif
/**
* @brief Data structure for mbedtls SSL connection
*
*/
typedef struct {
mbedtls_net_context socket_fd;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ssl_context ssl;
mbedtls_ssl_config ssl_conf;
#ifdef AUTH_MODE_CERT
mbedtls_x509_crt ca_cert;
mbedtls_x509_crt client_cert;
#endif
mbedtls_pk_context private_key;
} TLSHandle;
#ifdef MBEDTLS_DEBUG_C
#define DEBUG_LEVEL 0
static void _ssl_debug(void *ctx, int level, const char *file, int line, const char *str)
{
Log_i("[mbedTLS]:[%s]:[%d]: %s\r\n", STRING_PTR_PRINT_SANITY_CHECK(file), line, STRING_PTR_PRINT_SANITY_CHECK(str));
}
#endif
/**
* @brief mbedtls SSL client init
*
* 1. call a series of mbedtls init functions
* 2. init and set seed for random functions
* 3. load CA file, cert files or PSK
*
* @param[in,out] tls_handle mbedtls TLS handle
* @param[in] connect_params device info for TLS connection
* @return @see IotReturnCode
*/
static int _mbedtls_tls_client_init(TLSHandle *tls_handle, const TLSConnectParams *connect_params)
{
int rc;
mbedtls_net_init(&tls_handle->socket_fd);
mbedtls_ssl_init(&tls_handle->ssl);
mbedtls_ssl_config_init(&tls_handle->ssl_conf);
mbedtls_ctr_drbg_init(&tls_handle->ctr_drbg);
mbedtls_entropy_init(&tls_handle->entropy);
#ifdef AUTH_MODE_CERT
mbedtls_x509_crt_init(&tls_handle->ca_cert);
mbedtls_x509_crt_init(&tls_handle->client_cert);
mbedtls_pk_init(&tls_handle->private_key);
#endif
#ifdef MBEDTLS_DEBUG_C
mbedtls_debug_set_threshold(DEBUG_LEVEL);
mbedtls_ssl_conf_dbg(&tls_handle->ssl_conf, _ssl_debug, NULL);
#endif
rc = mbedtls_ctr_drbg_seed(&tls_handle->ctr_drbg, mbedtls_entropy_func, &tls_handle->entropy, NULL, 0);
if (rc) {
Log_e("mbedtls_ctr_drbg_seed failed returned 0x%04x", -rc);
return QCLOUD_ERR_SSL_INIT;
}
#ifdef AUTH_MODE_CERT
if (!connect_params->cert_file || !connect_params->key_file || !connect_params->ca_crt) {
Log_d("cert_file/key_file/ca is empty!|cert_file=%s|key_file=%s|ca=%s",
STRING_PTR_PRINT_SANITY_CHECK(connect_params->cert_file),
STRING_PTR_PRINT_SANITY_CHECK(connect_params->key_file),
STRING_PTR_PRINT_SANITY_CHECK(connect_params->ca_crt));
return QCLOUD_ERR_SSL_CERT;
}
rc = mbedtls_x509_crt_parse(&tls_handle->ca_cert, (const unsigned char *)connect_params->ca_crt,
(connect_params->ca_crt_len + 1));
if (rc) {
Log_e("parse ca crt failed returned 0x%04x", -rc);
return QCLOUD_ERR_SSL_CERT;
}
rc = mbedtls_x509_crt_parse_file(&tls_handle->client_cert, connect_params->cert_file);
if (rc) {
Log_e("load client cert file failed returned 0x%04x", -rc);
return QCLOUD_ERR_SSL_CERT;
}
rc = mbedtls_pk_parse_keyfile(&tls_handle->private_key, connect_params->key_file, "");
if (rc) {
Log_e("load client key file failed returned 0x%04x", -rc);
return QCLOUD_ERR_SSL_CERT;
}
#else
if (!connect_params->psk || !connect_params->psk_id) {
Log_d("psk/psk_id is empty!");
return QCLOUD_ERR_SSL_INIT;
}
rc = mbedtls_ssl_conf_psk(&tls_handle->ssl_conf, (unsigned char *)connect_params->psk, connect_params->psk_length,
(const unsigned char *)connect_params->psk_id, strlen(connect_params->psk_id));
if (rc) {
Log_e("mbedtls_ssl_conf_psk fail 0x%04x", -rc);
return rc;
}
#endif
return QCLOUD_RET_SUCCESS;
}
/**
* @brief Free memory/resources allocated by mbedtls
*
* @param[in,out] tls_handle @see TLSHandle
*/
static void _mbedtls_tls_client_free(TLSHandle *tls_handle)
{
mbedtls_net_free(&(tls_handle->socket_fd));
#ifdef AUTH_MODE_CERT
mbedtls_x509_crt_free(&tls_handle->client_cert);
mbedtls_x509_crt_free(&tls_handle->ca_cert);
mbedtls_pk_free(&tls_handle->private_key);
#endif
mbedtls_ssl_free(&tls_handle->ssl);
mbedtls_ssl_config_free(&tls_handle->ssl_conf);
mbedtls_ctr_drbg_free(&tls_handle->ctr_drbg);
mbedtls_entropy_free(&tls_handle->entropy);
HAL_Free(tls_handle);
}
#ifdef AUTH_MODE_CERT
/**
* @brief verify server certificate
*
* mbedtls has provided similar function mbedtls_x509_crt_verify_with_profile
*/
int _mbedtls_tls_client_certificate_verify(void *hostname, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
{
return *flags;
}
#endif
/**
* @brief Tls setup and sharkhand
*
* @param[in] connect_params connect params of tls
* @param[in] host server host
* @param[in] port server port
* @return tls handle, 0 for fail
*/
uintptr_t qcloud_iot_tls_client_connect(const TLSConnectParams *connect_params, const char *host, const char *port)
{
int rc = 0;
TLSHandle *tls_handle = (TLSHandle *)HAL_Malloc(sizeof(TLSHandle));
if (!tls_handle) {
return 0;
}
rc = _mbedtls_tls_client_init(tls_handle, connect_params);
if (rc) {
goto error;
}
Log_d("Setting up the SSL/TLS structure...");
rc = mbedtls_ssl_config_defaults(&tls_handle->ssl_conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT);
if (rc) {
Log_e("mbedtls_ssl_config_defaults failed returned 0x%04x", -rc);
goto error;
}
mbedtls_ssl_conf_rng(&tls_handle->ssl_conf, mbedtls_ctr_drbg_random, &tls_handle->ctr_drbg);
#ifdef AUTH_MODE_CERT
mbedtls_ssl_conf_verify(&(tls_handle->ssl_conf), _mbedtls_tls_client_certificate_verify, (void *)host);
mbedtls_ssl_conf_authmode(&(tls_handle->ssl_conf), MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_ca_chain(&tls_handle->ssl_conf, &tls_handle->ca_cert, NULL);
rc = mbedtls_ssl_conf_own_cert(&tls_handle->ssl_conf, &tls_handle->client_cert, &tls_handle->private_key);
if (rc) {
Log_e("mbedtls_ssl_conf_own_cert failed returned 0x%04x", -rc);
goto error;
}
#endif
mbedtls_ssl_conf_read_timeout(&tls_handle->ssl_conf, connect_params->timeout_ms);
rc = mbedtls_ssl_setup(&tls_handle->ssl, &tls_handle->ssl_conf);
if (rc) {
Log_e("mbedtls_ssl_setup failed returned 0x%04x", -rc);
goto error;
}
#ifdef AUTH_MODE_CERT
// Set the hostname to check against the received server certificate and sni
rc = mbedtls_ssl_set_hostname(&tls_handle->ssl, host);
if (rc) {
Log_e("mbedtls_ssl_set_hostname failed returned 0x%04x", -rc);
goto error;
}
#else
// ciphersuites selection for PSK device
if (connect_params->psk) {
mbedtls_ssl_conf_ciphersuites(&(tls_handle->ssl_conf), ciphersuites);
}
#endif
mbedtls_ssl_set_bio(&tls_handle->ssl, &tls_handle->socket_fd, mbedtls_net_send, mbedtls_net_recv,
mbedtls_net_recv_timeout);
Log_d("Performing the SSL/TLS handshake...");
Log_d("Connecting to /%s/%s...", STRING_PTR_PRINT_SANITY_CHECK(host), STRING_PTR_PRINT_SANITY_CHECK(port));
rc = mbedtls_net_connect(&tls_handle->socket_fd, host, port, MBEDTLS_NET_PROTO_TCP);
if (rc < 0) {
goto error;
}
do {
rc = mbedtls_ssl_handshake(&tls_handle->ssl);
if (rc && rc != MBEDTLS_ERR_SSL_WANT_READ && rc != MBEDTLS_ERR_SSL_WANT_WRITE) {
Log_e("mbedtls_ssl_handshake failed returned 0x%04x", -rc);
#ifdef AUTH_MODE_CERT
if (rc == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) {
Log_e("Unable to verify the server's certificate");
}
#endif
goto error;
}
} while (rc);
rc = mbedtls_ssl_get_verify_result(&(tls_handle->ssl));
if (rc) {
Log_e("mbedtls_ssl_get_verify_result failed returned 0x%04x", -rc);
goto error;
}
mbedtls_ssl_conf_read_timeout(&tls_handle->ssl_conf, 200);
Log_d("connected with /%s/%s...", STRING_PTR_PRINT_SANITY_CHECK(host), port);
return (uintptr_t)tls_handle;
error:
_mbedtls_tls_client_free(tls_handle);
return 0;
}
/**
* @brief Disconect and free
*
* @param[in,out] handle tls handle
*/
void qcloud_iot_tls_client_disconnect(uintptr_t handle)
{
int rc = 0;
TLSHandle *tls_handle = (TLSHandle *)handle;
if (!tls_handle) {
Log_d("handle is NULL");
return;
}
do {
rc = mbedtls_ssl_close_notify(&tls_handle->ssl);
} while (rc == MBEDTLS_ERR_SSL_WANT_READ || rc == MBEDTLS_ERR_SSL_WANT_WRITE);
_mbedtls_tls_client_free(tls_handle);
}
/**
* @brief Write msg with tls
*
* @param[in,out] handle tls handle
* @param[in] msg msg to write
* @param[in] total_len number of bytes to write
* @param[in] timeout_ms timeout millsecond
* @param[out] written_len number of bytes writtern
* @return @see IotReturnCode
*/
int qcloud_iot_tls_client_write(uintptr_t handle, unsigned char *msg, size_t total_len, uint32_t timeout_ms,
size_t *written_len)
{
QcloudIotTimer timer;
size_t written_so_far;
int write_rc = 0;
TLSHandle *tls_handle = (TLSHandle *)handle;
IOT_Timer_CountdownMs(&timer, (unsigned int)timeout_ms);
for (written_so_far = 0; written_so_far < total_len && !IOT_Timer_Expired(&timer); written_so_far += write_rc) {
do {
write_rc = mbedtls_ssl_write(&tls_handle->ssl, msg + written_so_far, total_len - written_so_far);
if (write_rc < 0 && write_rc != MBEDTLS_ERR_SSL_WANT_READ && write_rc != MBEDTLS_ERR_SSL_WANT_WRITE) {
Log_e("HAL_TLS_write failed 0x%04x", -write_rc);
return QCLOUD_ERR_SSL_WRITE;
}
if (IOT_Timer_Expired(&timer)) {
break;
}
} while (write_rc <= 0);
}
*written_len = written_so_far;
if (IOT_Timer_Expired(&timer) && written_so_far != total_len) {
return QCLOUD_ERR_SSL_WRITE_TIMEOUT;
}
return QCLOUD_RET_SUCCESS;
}
/**
* @brief Read msg with tls
*
* @param[in,out] handle tls handle
* @param[out] msg msg buffer
* @param[in] total_len buffer len
* @param[in] timeout_ms timeout millsecond
* @param[out] read_len number of bytes read
* @return @see IotReturnCode
*/
int qcloud_iot_tls_client_read(uintptr_t handle, unsigned char *msg, size_t total_len, uint32_t timeout_ms,
size_t *read_len)
{
QcloudIotTimer timer;
int read_rc;
TLSHandle *tls_handle = (TLSHandle *)handle;
IOT_Timer_CountdownMs(&timer, timeout_ms);
*read_len = 0;
do {
read_rc = mbedtls_ssl_read(&tls_handle->ssl, msg + *read_len, total_len - *read_len);
if (read_rc <= 0 && read_rc != MBEDTLS_ERR_SSL_WANT_WRITE && read_rc != MBEDTLS_ERR_SSL_WANT_READ &&
read_rc != MBEDTLS_ERR_SSL_TIMEOUT) {
Log_e("cloud_iot_network_tls_read failed: 0x%04x", -read_rc);
return QCLOUD_ERR_SSL_READ;
}
*read_len += read_rc > 0 ? read_rc : 0;
if (IOT_Timer_Expired(&timer)) {
break;
}
} while (*read_len < total_len);
if (*read_len > 0) {
return QCLOUD_RET_SUCCESS;
}
return *read_len == 0 ? QCLOUD_ERR_SSL_NOTHING_TO_READ : QCLOUD_ERR_SSL_READ_TIMEOUT;
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,14 @@
# 项目信息
project(QCloud_IoT_SDK C CXX)
# CMake 最低版本号要求
cmake_minimum_required(VERSION 3.5)
# 设置根目录地址
set(IOT_SDK_SOURCE_DIR ${PROJECT_SOURCE_DIR})
# 编译工具链
include(${PROJECT_SOURCE_DIR}/config/toolchains/tencentos_tiny_none.cmake)
# 工程配置
include(${PROJECT_SOURCE_DIR}/config/settings/iot_explorer_at_module.cmake)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
Tencent is pleased to support the open source community by making IoT Hub available.
Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
If you have downloaded a copy of the IoT Hub binary from Tencent, please note that the IoT Hub binary is licensed under the MIT License.
If you have downloaded a copy of the IoT Hub source code from Tencent, please note that IoT Hub source code is licensed under the MIT License,
except for the third-party components listed below which are subject to different license terms. Your integration of IoT Hub into your own projects may require compliance with the MIT License,
as well as the other licenses applicable to the third-party components included within IoT Hub.
A copy of the MIT License is included in this file.
--------------------------------------------------------------------
Terms of the MIT License:
--------------------------------------------------------------------
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,174 @@
# 腾讯云物联网设备端 C-SDK
腾讯云物联网设备端 C-SDK 依靠安全且性能强大的数据通道,为物联网领域开发人员提供设备端快速接入云端,并和云端进行双向通信的能力。
本SDK仅适用于物联网通信平台IoT Hub对于物联网开发平台请访问 [IoT Explorer C-SDK](https://github.com/tencentyun/qcloud-iot-explorer-sdk-embedded-c)
## 一、SDK 目录结构简介
| 名称 | 说明 |
| ------------------ | ------------------------------------------------------------ |
| CMakeLists.txt | cmake编译描述文件 |
| cmake_build.sh | Linux下使用cmake的编译脚本 |
| docs | 文档目录 |
| 3rd | 第三方软件包组件如mbedtls |
| config | SDK功能配置以及编译工具链配置 |
| include | 提供给用户使用的外部头文件 |
| platform | 平台相关的源码文件目前提供了针对不同OS(Linux/Windows/FreeRTOS/nonOS)TLS(mbedtls)以及AT模组下的实现 |
| common | SDK通用组件库 |
| services | SDK提供的服务源码以模块独立存在 |
- `config/settings`可选择相应的配置脚本以根据需要裁剪SDK
- `config/toolchains`可配置相应的编译工具链,以及选择平台
## 二、SDK 各模块介绍
| 模块名 | 所在目录 | 功能 |
| ------------ | ------ | ------ |
| mqtt_packet | common/mqtt_packet | 通用组件实现MQTT控制包的序列化和反序列化 |
| utils | common/utils | 通用组件,实现列表、日志等通用操作 |
| cryptology | common/cryptology | 通用组件实现SDK所需密码学算法 |
| mqtt_client | common/mqtt_packet | 服务组件实现MQTT客户端 |
## 三、快速体验
### 1. 安装环境
以下以ubuntu为例子安装`cmake``gcc`
```bash
apt-get install cmake gcc g++
```
### 2. 创建设备
参考[设备接入准备](https://cloud.tencent.com/document/product/634/14442#null)创建**密钥认证**设备,获取设备信息用作连接平台。
- 产品ID
![product_id](https://main.qcloudimg.com/raw/a746d4e9455f045b5e65ba870f269e7d.png)
- 设备名和设备密钥
![device_name&psk](https://main.qcloudimg.com/raw/e73b71b952fbb84a6d924882547921fa.png)
### 3. 修改设备信息
编辑`platform/os/Linux/HAL_Device_linux.c`,修改设备信息:
```c
static char sg_product_id[MAX_SIZE_OF_PRODUCT_ID + 1] = "YOUR_PRODUCT_ID";
static char sg_device_name[MAX_SIZE_OF_DEVICE_NAME + 1] = "YOUR_DEVICE_NAME";
static char sg_device_secret[MAX_SIZE_OF_DEVICE_SECRET + 1] = "YOUR_IOT_PSK";
```
### 4. 编译运行
运行根目录下`./cmake_build.sh`进行编译,然后运行`./output/bin/mqtt_sample`即可快速体验。
## 四、代码贡献
请仔细阅读[SDK代码规范说明](./docs/SDK代码规范说明.md)和[SDK代码格式说明](./docs/SDK代码格式说明)。
`vscode`编辑器请安装以下插件,并配置:
- `C/C++`支持C语言语法和`clang-format`
- `Code Spell Checker`:检查单词拼写,以及单词纠正
- `Doxygen Documentation Generator`:在`settings.json`中添加以下内容,以支持`doxygen`
```json
"doxdocgen.generic.authorEmail": "fancyxu@tencent.com",
"doxdocgen.generic.authorName": "fancyxu",
"doxdocgen.generic.authorTag": "@author {author} ({email})",
"doxdocgen.file.versionTag": "@version 1.0",
"doxdocgen.file.copyrightTag": [
"@copyright",
"",
"Tencent is pleased to support the open source community by making IoT Hub available.",
"Copyright(C) 2018 - {year} 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.",
],
"doxdocgen.file.customTag": [
"@par Change Log:",
"<table>",
"<tr><th>Date <th>Version <th>Author <th>Description",
"<tr><td>{date} <td>1.0 <td>{author} <td>first commit",
"</table>",
],
"doxdocgen.file.fileOrder": [
"copyright",
"empty",
"file",
"brief",
"author",
"version",
"date",
"empty",
"custom"
]
```
## 五、自动化文档生成
### 1. 安装 doxygen
```bash
apt-get install doxygen graphviz
```
### 2. 生成 doxygen
```bash
doxygen Doxyfile
```
### 3. 浏览
打开`output/html/index.html`,即可浏览项目中的接口和变量信息。
## 六、单元测试
### 1. 安装 google test
```bash
apt-get install googletest gcovr
cd /usr/src/googletest
mkdir -p build
cd build
cmake .. && make && make install
```
### 2. 安装并运行 mosquitto
```bash
apt-get install mosquitto
mosquitto -c ./config/mosquitto/mosquitto.conf
```
### 3. 打开测试选项
```cmake
set(CONFIG_IOT_TEST ON)
```
### 4. 运行单元测试
```bash
./output/bin/iot_sdk_test
```
## 学习参考
1. [腾讯云物联网设备端学习系列](https://cloud.tencent.com/developer/article/1789776)
2. [googletest](https://github.com/google/googletest)
3. [git 基本技能学习](https://github.com/xyfancy/GitBasicSkills)
4. [mbedtls](https://github.com/ARMmbed/mbedtls)

View File

@@ -0,0 +1,3 @@
file(GLOB src_app ${CMAKE_CURRENT_SOURCE_DIR}/*.c)
add_executable(app_data_template ${src_app})
target_link_libraries(app_data_template ${libsdk})

View File

@@ -0,0 +1,352 @@
/**
* @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 "data_template_config.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->event_handle.h_fp = _mqtt_event_handler;
}
// ----------------------------------------------------------------------------
// Data template callback
// ----------------------------------------------------------------------------
static void _handle_property_callback(void *client, int is_get_status)
{
for (UsrPropertyIndex i = USR_PROPERTY_INDEX_POWER_SWITCH; i <= USR_PROPERTY_INDEX_POWER; i++) {
if (usr_data_template_property_status_get(i)) {
DataTemplatePropertyValue value;
switch (i) {
case USR_PROPERTY_INDEX_POWER_SWITCH:
case USR_PROPERTY_INDEX_COLOR:
case USR_PROPERTY_INDEX_BRIGHTNESS: // read only, just for example
case USR_PROPERTY_INDEX_POWER: // read only, just for example
value = usr_data_template_property_value_get(i);
Log_d("recv %s:%d", usr_data_template_property_key_get(i), value.value_int);
break;
case USR_PROPERTY_INDEX_NAME: // read only, just for example
value = usr_data_template_property_value_get(i);
Log_d("recv %s:%s", usr_data_template_property_key_get(i), value.value_string);
break;
case USR_PROPERTY_INDEX_POSITION: // read only, just for example
for (UsrPropertyPositionIndex j = USR_PROPERTY_POSITION_INDEX_LONGITUDE;
j <= USR_PROPERTY_POSITION_INDEX_LATITUDE; j++) {
value = usr_data_template_property_struct_value_get(i, j);
Log_d("recv %s:%d", usr_data_template_property_struct_key_get(i, j), value.value_int);
}
break;
}
usr_data_template_property_status_reset(i);
}
}
}
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);
usr_data_template_property_parse(params);
_handle_property_callback(usr_data, 0);
}
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));
usr_data_template_property_parse(control);
IOT_DataTemplate_PropertyClearControl(usr_data, buf, sizeof(buf));
_handle_property_callback(usr_data, 1);
}
static void _method_action_callback(UtilsJsonValue client_token, UtilsJsonValue action_id, UtilsJsonValue params,
void *usr_data)
{
UsrActionIndex index;
int rc;
DataTemplatePropertyValue value_time, value_color, value_total_time;
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);
rc = usr_data_template_action_parse(action_id, params, &index);
if (rc) {
return;
}
switch (index) {
case USR_ACTION_INDEX_LIGHT_BLINK:
value_time = usr_data_template_action_input_value_get(USR_ACTION_INDEX_LIGHT_BLINK,
USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME);
value_color = usr_data_template_action_input_value_get(USR_ACTION_INDEX_LIGHT_BLINK,
USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR);
value_total_time = usr_data_template_action_input_value_get(USR_ACTION_INDEX_LIGHT_BLINK,
USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME);
Log_i("light[%d] blink %d every %d s ", value_color.value_enum, value_time.value_int,
value_total_time.value_int);
usr_data_template_action_reply(usr_data, buf, sizeof(buf), index, client_token, 0, "{\"err_code\":0}");
break;
default:
break;
}
}
// ----------------------------------------------------------------------------
// Data template upstream
// ----------------------------------------------------------------------------
/**
* @brief Report status.
*
* @param[in,out] client pointer to mqtt client
*/
static void _cycle_report(void *client)
{
char buf[256];
static QcloudIotTimer sg_cycle_report_timer;
if (IOT_Timer_Expired(&sg_cycle_report_timer)) {
usr_data_template_event_post(client, buf, sizeof(buf), USR_EVENT_INDEX_STATUS_REPORT,
"{\"status\":0,\"message\":\"ok\"}");
IOT_Timer_Countdown(&sg_cycle_report_timer, 60);
}
}
/**
* @brief Init usr data template and data.
*
*/
static void _usr_init(void)
{
usr_data_template_init();
DataTemplatePropertyValue value;
value.value_int = 0;
usr_data_template_property_value_set(USR_PROPERTY_INDEX_POWER_SWITCH, value);
value.value_enum = 0;
usr_data_template_property_value_set(USR_PROPERTY_INDEX_COLOR, value);
value.value_int = 10;
usr_data_template_property_value_set(USR_PROPERTY_INDEX_BRIGHTNESS, value);
value.value_string = "light";
usr_data_template_property_value_set(USR_PROPERTY_INDEX_NAME, value);
value.value_int = 30;
usr_data_template_property_struct_value_set(USR_PROPERTY_INDEX_POSITION, USR_PROPERTY_POSITION_INDEX_LONGITUDE,
value);
value.value_int = 30;
usr_data_template_property_struct_value_set(USR_PROPERTY_INDEX_POSITION, USR_PROPERTY_POSITION_INDEX_LATITUDE,
value);
value.value_string = "high";
usr_data_template_property_value_set(USR_PROPERTY_INDEX_POWER, value);
}
// ----------------------------------------------------------------------------
// 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", 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 = DEFAULT_LOG_HANDLE_FUNCS;
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;
}
_usr_init();
// 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 = DEFAULT_DATA_TEMPLATE_CALLBACK;
callback.property_callback.method_control_callback = _method_control_callback;
callback.property_callback.method_get_status_reply_callback = _method_get_status_reply_callback;
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;
}
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);
usr_data_template_property_report(client, buf, sizeof(buf));
} while (!sg_main_exit);
exit:
IOT_DataTemplate_Deinit(client);
rc |= IOT_MQTT_Destroy(&client);
utils_log_deinit();
return rc;
}

View File

@@ -0,0 +1,410 @@
/**
* @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_config.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-09
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-09 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "data_template_config.h"
/**
* @brief Set property value.
*
* @param[in,out] property pointer to property
* @param[in] value value to set, should match with property type
* @return 0 for success.
*/
static int _set_property_value(DataTemplateProperty* property, UtilsJsonValue value)
{
int i, rc = 0;
switch (property->type) {
case DATA_TEMPLATE_TYPE_INT:
case DATA_TEMPLATE_TYPE_ENUM:
case DATA_TEMPLATE_TYPE_BOOL:
return utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_INT32, &property->value.value_int);
case DATA_TEMPLATE_TYPE_TIME:
return utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_UINT32, &property->value.value_time);
case DATA_TEMPLATE_TYPE_STRING:
case DATA_TEMPLATE_TYPE_STRING_ENUM:
if (!property->value.value_string) { // no need copy
return 0;
}
strncpy(property->value.value_string, value.value, value.value_len);
property->value.value_string[value.value_len] = '\0';
return 0;
case DATA_TEMPLATE_TYPE_FLOAT:
return utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_FLOAT, &property->value.value_float);
case DATA_TEMPLATE_TYPE_STRUCT:
for (i = 0; i < property->value.value_struct.count; i++) {
rc |= _set_property_value(property->value.value_struct.property + i, value);
}
return rc;
case DATA_TEMPLATE_TYPE_ARRAY:
Log_e("array type is not supportted yet!");
return -1;
default:
Log_e("unkown type!");
return -1;
}
}
/**
* @brief Get property node in json.
*
* @param[out] json_buf buffer to put node
* @param[in] buf_len buffer length
* @param[in] property property to get node
* @return 0 for success.
*/
static int _get_property_node(char* json_buf, int buf_len, const DataTemplateProperty* property)
{
int len, i;
switch (property->type) {
case DATA_TEMPLATE_TYPE_INT:
case DATA_TEMPLATE_TYPE_ENUM:
case DATA_TEMPLATE_TYPE_BOOL:
return HAL_Snprintf(json_buf, buf_len, "\"%s\":%d", property->key, property->value.value_int);
case DATA_TEMPLATE_TYPE_TIME:
return HAL_Snprintf(json_buf, buf_len, "\"%s\":%u", property->key, property->value.value_time);
case DATA_TEMPLATE_TYPE_STRING:
case DATA_TEMPLATE_TYPE_STRING_ENUM:
if (!property->value.value_string) {
return 0;
}
return HAL_Snprintf(json_buf, buf_len, "\"%s\":\"%s\"", property->key, property->value.value_string);
case DATA_TEMPLATE_TYPE_FLOAT:
return HAL_Snprintf(json_buf, buf_len, "\"%s\":%f", property->key, property->value.value_float);
case DATA_TEMPLATE_TYPE_STRUCT:
len = HAL_Snprintf(json_buf, buf_len, "\"%s\":{", property->key);
for (i = 0; i < property->value.value_struct.count; i++) {
len += _get_property_node(json_buf + len, buf_len - len, property->value.value_struct.property + i);
json_buf[len++] = ',';
}
json_buf[--len] = '}';
return len + 1;
case DATA_TEMPLATE_TYPE_ARRAY:
Log_e("array type is not supportted yet!");
return 0;
default:
Log_e("unkown type!");
return 0;
}
}
/**
* @brief Parse property array.
*
* @param[in] json_buf json string to parse
* @param[in] buf_len json len
* @param[in,out] properties pointer to property array
* @param[in] property_count count of property
*/
static void _parse_property_array(const char* json_buf, int buf_len, DataTemplateProperty* properties,
int property_count)
{
DataTemplateProperty* property;
UtilsJsonValue value;
for (int i = 0; i < property_count; i++) {
property = &properties[i];
if (!property->is_rw) { // read only property should not be processed
continue;
}
if (!utils_json_value_get(property->key, strlen(property->key), json_buf, buf_len, &value)) {
property->is_change = property->need_report = !_set_property_value(property, value);
}
}
}
#include "data_template_config_src_c.include"
/**************************************************************************************
* API
**************************************************************************************/
/**
* @brief Init user data template(property/event/action).
*
*/
void usr_data_template_init(void)
{
_init_data_template_property();
_init_data_template_event();
_init_data_template_action();
}
/**
* @brief Get property value.
*
* @param[in] index @see UsrPropertyIndex
* @return @see DataTemplatePropertyValue
*/
DataTemplatePropertyValue usr_data_template_property_value_get(UsrPropertyIndex index)
{
return sg_usr_data_template_property[index].value;
}
/**
* @brief Set property value.
*
* @param[in] index @see UsrPropertyIndex
* @param[in] value @see DataTemplatePropertyValue, @note value should match property type.
*/
void usr_data_template_property_value_set(UsrPropertyIndex index, DataTemplatePropertyValue value)
{
if (sg_usr_data_template_property[index].type == DATA_TEMPLATE_TYPE_STRING ||
sg_usr_data_template_property[index].type == DATA_TEMPLATE_TYPE_STRING_ENUM) {
size_t value_len = strlen(value.value_string);
strncpy(sg_usr_data_template_property[index].value.value_string, value.value_string, value_len);
sg_usr_data_template_property[index].value.value_string[value_len] = '\0';
} else {
sg_usr_data_template_property[index].value = value;
}
sg_usr_data_template_property[index].need_report = 1;
}
/**
* @brief Get property(struct) value.
*
* @param[in] struct_index @see UsrPropertyIndex, @note DATA_TEMPLATE_TYPE_STRUCT is required here.
* @param[in] property_index depends on which struct
* @return @see DataTemplatePropertyValue
*/
DataTemplatePropertyValue usr_data_template_property_struct_value_get(UsrPropertyIndex struct_index, int property_index)
{
return sg_usr_data_template_property[struct_index].value.value_struct.property[property_index].value;
}
/**
* @brief Set property(struct) value.
*
* @param[in] struct_index @see UsrPropertyIndex, @note DATA_TEMPLATE_TYPE_STRUCT is required here.
* @param[in] property_index depends on which struct
* @param[in] value @see DataTemplatePropertyValue, @note value should match property type.
*/
void usr_data_template_property_struct_value_set(UsrPropertyIndex struct_index, int property_index,
DataTemplatePropertyValue value)
{
if (sg_usr_data_template_property[struct_index].value.value_struct.property[property_index].type ==
DATA_TEMPLATE_TYPE_STRING ||
sg_usr_data_template_property[struct_index].value.value_struct.property[property_index].type ==
DATA_TEMPLATE_TYPE_STRING_ENUM) {
size_t value_len = strlen(value.value_string);
char* dst_str =
sg_usr_data_template_property[struct_index].value.value_struct.property[property_index].value.value_string;
strncpy(dst_str, value.value_string, value_len);
dst_str[value_len] = '\0';
} else {
sg_usr_data_template_property[struct_index].value.value_struct.property[property_index].value = value;
}
sg_usr_data_template_property[struct_index].need_report = 1;
}
/**
* @brief Parse control message and set property value.
*
* @param[in] params params filed of control message
*/
void usr_data_template_property_parse(UtilsJsonValue params)
{
_parse_property_array(params.value, params.value_len, sg_usr_data_template_property, TOTAL_USR_PROPERTY_COUNT);
}
/**
* @brief Get property status.
*
* @param[in] index @see UsrPropertyIndex
* @return if property is changed
*/
int usr_data_template_property_status_get(UsrPropertyIndex index)
{
return sg_usr_data_template_property[index].is_change;
}
/**
* @brief Reset property status.
*
* @param[in] index @see UsrPropertyIndex
*/
void usr_data_template_property_status_reset(UsrPropertyIndex index)
{
sg_usr_data_template_property[index].is_change = 0;
}
/**
* @brief Get property type.
*
* @param[in] index @see UsrPropertyIndex
* @return @see DataTemplatePropertyType
*/
DataTemplatePropertyType usr_data_template_property_type_get(UsrPropertyIndex index)
{
return sg_usr_data_template_property[index].type;
}
/**
* @brief Get property key.
*
* @param[in] index @see UsrPropertyIndex
* @return key string
*/
const char* usr_data_template_property_key_get(UsrPropertyIndex index)
{
return sg_usr_data_template_property[index].key;
}
/**
* @brief Get property(struct) key.
*
* @param[in] struct_index @see UsrPropertyIndex, @note DATA_TEMPLATE_TYPE_STRUCT is required here.
* @param[in] property_index depends on which struct
* @return key string
*/
const char* usr_data_template_property_struct_key_get(UsrPropertyIndex struct_index, int property_index)
{
return sg_usr_data_template_property[struct_index].value.value_struct.property[property_index].key;
}
/**
* @brief Report all the properties needed report.
*
* @param[in,out] client pointer to mqtt client
* @param[in] buf buffer to report
* @param[in] buf_len buffer length
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int usr_data_template_property_report(void* client, char* buf, int buf_len)
{
char params[1024];
memset(params, 0, sizeof(params));
params[0] = '{';
int offset, param_offset = 1;
for (int i = 0; i < TOTAL_USR_PROPERTY_COUNT; i++) {
DataTemplateProperty* property = &sg_usr_data_template_property[i];
if (property->need_report) {
offset = _get_property_node(params + param_offset, sizeof(params) - param_offset, property);
if (offset) {
param_offset += offset;
params[param_offset++] = ',';
}
property->need_report = 0;
}
}
params[--param_offset] = '}';
if (param_offset) {
return IOT_DataTemplate_PropertyReport(client, buf, buf_len, params);
}
return QCLOUD_RET_SUCCESS;
}
/**
* @brief Post event.
*
* @param[in,out] client pointer to mqtt client
* @param[in] buf buffer to report
* @param[in] buf_len buffer length
* @param[in] id @see UsrEventIndex
* @param[in] params user should construct params according to event.
*/
void usr_data_template_event_post(void* client, char* buf, int buf_len, UsrEventIndex id, const char* params)
{
if (params) {
sg_usr_data_template_event[id].params = params;
}
IOT_DataTemplate_EventPost(client, buf, buf_len, sg_usr_data_template_event[id]);
}
/**
* @brief Parse action message and set action input params.
*
* @param[in] action_id action id
* @param[in] params params of action
* @param[out] index @see UsrActionIndex
* @return 0 for success, QCLOUD_ERR_JSON_PARSE for invalid json.
*/
int usr_data_template_action_parse(UtilsJsonValue action_id, UtilsJsonValue params, UsrActionIndex* index)
{
DataTemplateProperty* property;
int input_property_count;
for (int i = 0; i < TOTAL_USR_ACTION_COUNT; i++) {
if (!strncmp(action_id.value, sg_usr_data_template_action[i].action_id, action_id.value_len)) {
property = sg_usr_data_template_action[i].input_struct.value_struct.property;
input_property_count = sg_usr_data_template_action[i].input_struct.value_struct.count;
// 1. reset need report
for (int j = 0; j < input_property_count; j++) {
property[j].need_report = 0;
}
// 2. parse
_parse_property_array(params.value, params.value_len, property, input_property_count);
// 3. check all the input params is set
for (int j = 0; j < input_property_count; j++) {
if (!property[j].need_report) {
return QCLOUD_ERR_JSON_PARSE;
}
}
*index = i;
return QCLOUD_RET_SUCCESS;
}
}
return QCLOUD_ERR_JSON_PARSE;
}
/**
* @brief Get input value, should call after usr_data_template_action_parse
*
* @param[in] index index get from usr_data_template_action_parse
* @param[in] property_index property index of action input params
* @return @see DataTemplatePropertyValue
*/
DataTemplatePropertyValue usr_data_template_action_input_value_get(UsrActionIndex index, int property_index)
{
return sg_usr_data_template_action[index].input_struct.value_struct.property[property_index].value;
}
/**
* @brief Reply action, should call after parse action message.
*
* @param[in,out] client pointer to mqtt client
* @param[in] buf buffer to report
* @param[in] buf_len buffer length
* @param[in] index @see UsrActionIndex
* @param[in] client_token client token of action message
* @param[in] code result code for action, 0 for success
* @param[in] response action output params, user should construct params according to action
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int usr_data_template_action_reply(void* client, char* buf, int buf_len, UsrActionIndex index,
UtilsJsonValue client_token, int code, const char* response)
{
sg_usr_data_template_action[index].reply.code = code;
sg_usr_data_template_action[index].reply.client_token = client_token;
sg_usr_data_template_action[index].reply.response = response;
return IOT_DataTemplate_ActionReply(client, buf, buf_len, sg_usr_data_template_action[index].reply);
}

View File

@@ -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 data_template_config.h
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-09
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-09 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_APP_DATA_TEMPLATE_DATA_TEMPLATE_CONFIG_H_
#define IOT_HUB_DEVICE_C_SDK_APP_DATA_TEMPLATE_DATA_TEMPLATE_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "qcloud_iot_common.h"
#include "qcloud_iot_data_template.h"
/**
* @brief Type of property.
*
*/
typedef enum {
// basic type
DATA_TEMPLATE_TYPE_INT,
DATA_TEMPLATE_TYPE_ENUM,
DATA_TEMPLATE_TYPE_STRING_ENUM,
DATA_TEMPLATE_TYPE_FLOAT,
DATA_TEMPLATE_TYPE_BOOL,
DATA_TEMPLATE_TYPE_STRING,
DATA_TEMPLATE_TYPE_TIME,
// construct by basic type
DATA_TEMPLATE_TYPE_STRUCT,
DATA_TEMPLATE_TYPE_ARRAY,
} DataTemplatePropertyType;
/**
* @brief Declaration.
*
*/
typedef struct DataTemplateProperty DataTemplateProperty;
typedef union DataTemplatePropertyValue DataTemplatePropertyValue;
/**
* @brief Property value definition.
*
*/
union DataTemplatePropertyValue {
int value_int;
int value_enum;
char* value_string_enum;
float value_float;
int value_bool;
char* value_string;
uint32_t value_time;
struct {
DataTemplateProperty* property;
int count;
} value_struct;
DataTemplatePropertyValue* value_arrary; /**< not supportted yet */
};
/**
* @brief Property definition.
*
*/
struct DataTemplateProperty {
DataTemplatePropertyType type;
const char* key;
DataTemplatePropertyValue value;
int need_report;
int is_change;
int is_rw;
};
/**
* @brief Event definition.
*
*/
#define DataTemplateEvent IotDataTemplateEventData
/**
* @brief Action definition.
*
*/
typedef struct {
const char* action_id;
DataTemplatePropertyValue input_struct;
IotDataTemplateActionReply reply;
} DataTemplateAction;
#include "data_template_config_header.include"
/**************************************************************************************
* api for user data template
**************************************************************************************/
/**
* @brief Init user data template(property/event/action).
*
*/
void usr_data_template_init(void);
/**
* @brief Get property value.
*
* @param[in] index @see UsrPropertyIndex
* @return @see DataTemplatePropertyValue
*/
DataTemplatePropertyValue usr_data_template_property_value_get(UsrPropertyIndex index);
/**
* @brief Set property value.
*
* @param[in] index @see UsrPropertyIndex
* @param[in] value @see DataTemplatePropertyValue, @note value should match property type.
*/
void usr_data_template_property_value_set(UsrPropertyIndex index, DataTemplatePropertyValue value);
/**
* @brief Get property(struct) value.
*
* @param[in] struct_index @see UsrPropertyIndex, @note DATA_TEMPLATE_TYPE_STRUCT is required here.
* @param[in] property_index depends on which struct
* @return @see DataTemplatePropertyValue
*/
DataTemplatePropertyValue usr_data_template_property_struct_value_get(UsrPropertyIndex struct_index,
int property_index);
/**
* @brief Set property(struct) value.
*
* @param[in] struct_index @see UsrPropertyIndex, @note DATA_TEMPLATE_TYPE_STRUCT is required here.
* @param[in] property_index depends on which struct
* @param[in] value @see DataTemplatePropertyValue, @note value should match property type.
*/
void usr_data_template_property_struct_value_set(UsrPropertyIndex struct_index, int property_index,
DataTemplatePropertyValue value);
/**
* @brief Parse control message and set property value.
*
* @param[in] params params filed of control message
*/
void usr_data_template_property_parse(UtilsJsonValue params);
/**
* @brief Get property status.
*
* @param[in] index @see UsrPropertyIndex
* @return need_report
*/
int usr_data_template_property_status_get(UsrPropertyIndex index);
/**
* @brief Reset property status.
*
* @param[in] index @see UsrPropertyIndex
*/
void usr_data_template_property_status_reset(UsrPropertyIndex index);
/**
* @brief Get property type.
*
* @param[in] index @see UsrPropertyIndex
* @return @see DataTemplatePropertyType
*/
DataTemplatePropertyType usr_data_template_property_type_get(UsrPropertyIndex index);
/**
* @brief Get property key.
*
* @param[in] index @see UsrPropertyIndex
* @return key string
*/
const char* usr_data_template_property_key_get(UsrPropertyIndex index);
/**
* @brief Get property(struct) key.
*
* @param[in] struct_index @see UsrPropertyIndex, @note DATA_TEMPLATE_TYPE_STRUCT is required here.
* @param[in] property_index depends on which struct
* @return key string
*/
const char* usr_data_template_property_struct_key_get(UsrPropertyIndex struct_index, int property_index);
/**
* @brief Report all the properties needed report.
*
* @param[in,out] client pointer to mqtt client
* @param[in] buf buffer to report
* @param[in] buf_len buffer length
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int usr_data_template_property_report(void* client, char* buf, int buf_len);
/**
* @brief Post event.
*
* @param[in,out] client pointer to mqtt client
* @param[in] buf buffer to report
* @param[in] buf_len buffer length
* @param[in] id @see UsrEventIndex
* @param[in] params user should construct params according to event.
*/
void usr_data_template_event_post(void* client, char* buf, int buf_len, UsrEventIndex id, const char* params);
/**
* @brief Parse action message and set action input params.
*
* @param[in] action_id action id
* @param[in] params params of action
* @param[out] index @see UsrActionIndex
* @return 0 for success, QCLOUD_ERR_JSON_PARSE for invalid json.
*/
int usr_data_template_action_parse(UtilsJsonValue action_id, UtilsJsonValue params, UsrActionIndex* index);
/**
* @brief Get input value, should call after usr_data_template_action_parse
*
* @param[in] index index get from usr_data_template_action_parse
* @param[in] property_index property index of action input params
* @return @see DataTemplatePropertyValue
*/
DataTemplatePropertyValue usr_data_template_action_input_value_get(UsrActionIndex index, int property_index);
/**
* @brief Reply action, should call after parse action message.
*
* @param[in,out] client pointer to mqtt client
* @param[in] buf buffer to report
* @param[in] buf_len buffer length
* @param[in] index @see UsrActionIndex
* @param[in] client_token client token of action message
* @param[in] code result code for action, 0 for success
* @param[in] response action output params, user should construct params according to action
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int usr_data_template_action_reply(void* client, char* buf, int buf_len, UsrActionIndex index,
UtilsJsonValue client_token, int code, const char* response);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_APP_DATA_TEMPLATE_DATA_TEMPLATE_CONFIG_H_

View File

@@ -0,0 +1,49 @@
/*----------------- property index enum start -------------------*/
typedef enum {
USR_PROPERTY_INDEX_POWER_SWITCH = 0,
USR_PROPERTY_INDEX_COLOR,
USR_PROPERTY_INDEX_BRIGHTNESS,
USR_PROPERTY_INDEX_NAME,
USR_PROPERTY_INDEX_POSITION,
USR_PROPERTY_INDEX_POWER,
} UsrPropertyIndex;
/*----------------- property index enum end -------------------*/
/*----------------- position property index enum start -------------------*/
typedef enum {
USR_PROPERTY_POSITION_INDEX_LONGITUDE = 0,
USR_PROPERTY_POSITION_INDEX_LATITUDE,
} UsrPropertyPositionIndex;
/*----------------- position property index enum end -------------------*/
/*----------------- event index enum start -------------------*/
typedef enum {
USR_EVENT_INDEX_STATUS_REPORT = 0,
USR_EVENT_INDEX_LOW_VOLTAGE,
USR_EVENT_INDEX_HARDWARE_FAULT,
} UsrEventIndex;
/*----------------- event index enum end -------------------*/
/*----------------- action index enum start -------------------*/
typedef enum {
USR_ACTION_INDEX_LIGHT_BLINK = 0,
} UsrActionIndex;
/*----------------- action index enum end -------------------*/
/*----------------- action light_blink input index enum start -------------------*/
typedef enum {
USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME = 0,
USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR,
USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME,
} UsrActionLightblinkInputIndex;
/*----------------- action light_blink input index enum end -------------------*/

View File

@@ -0,0 +1,160 @@
// ----------------------------------------------------------------------------
// user property
// ----------------------------------------------------------------------------/
#define TOTAL_USR_PROPERTY_STRUCT_POSITION_COUNT 2
static DataTemplateProperty sg_usr_property_position[TOTAL_USR_PROPERTY_STRUCT_POSITION_COUNT];
static void _init_data_template_property_position(void)
{
sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LONGITUDE].value.value_int = 1;
sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LONGITUDE].key = "longitude";
sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LONGITUDE].type = DATA_TEMPLATE_TYPE_INT;
sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LONGITUDE].is_rw = 0;
sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LATITUDE].value.value_int = 1;
sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LATITUDE].key = "latitude";
sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LATITUDE].type = DATA_TEMPLATE_TYPE_INT;
sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LATITUDE].is_rw = 0;
}
#define TOTAL_USR_PROPERTY_COUNT 6
static DataTemplateProperty sg_usr_data_template_property[TOTAL_USR_PROPERTY_COUNT];
static void _init_data_template_property(void)
{
sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER_SWITCH].value.value_bool = 0;
sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER_SWITCH].key = "power_switch";
sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER_SWITCH].type = DATA_TEMPLATE_TYPE_BOOL;
sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER_SWITCH].need_report = 1;
sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER_SWITCH].is_rw = 1;
sg_usr_data_template_property[USR_PROPERTY_INDEX_COLOR].value.value_enum = 0;
sg_usr_data_template_property[USR_PROPERTY_INDEX_COLOR].key = "color";
sg_usr_data_template_property[USR_PROPERTY_INDEX_COLOR].type = DATA_TEMPLATE_TYPE_ENUM;
sg_usr_data_template_property[USR_PROPERTY_INDEX_COLOR].need_report = 1;
sg_usr_data_template_property[USR_PROPERTY_INDEX_COLOR].is_rw = 1;
sg_usr_data_template_property[USR_PROPERTY_INDEX_BRIGHTNESS].value.value_int = 1;
sg_usr_data_template_property[USR_PROPERTY_INDEX_BRIGHTNESS].key = "brightness";
sg_usr_data_template_property[USR_PROPERTY_INDEX_BRIGHTNESS].type = DATA_TEMPLATE_TYPE_INT;
sg_usr_data_template_property[USR_PROPERTY_INDEX_BRIGHTNESS].need_report = 1;
sg_usr_data_template_property[USR_PROPERTY_INDEX_BRIGHTNESS].is_rw = 1;
static char sg_usr_property_name[64 + 1] = " ";
sg_usr_data_template_property[USR_PROPERTY_INDEX_NAME].value.value_string = sg_usr_property_name;
sg_usr_data_template_property[USR_PROPERTY_INDEX_NAME].key = "name";
sg_usr_data_template_property[USR_PROPERTY_INDEX_NAME].type = DATA_TEMPLATE_TYPE_STRING;
sg_usr_data_template_property[USR_PROPERTY_INDEX_NAME].need_report = 1;
sg_usr_data_template_property[USR_PROPERTY_INDEX_NAME].is_rw = 1;
_init_data_template_property_position();
sg_usr_data_template_property[USR_PROPERTY_INDEX_POSITION].value.value_struct.property = sg_usr_property_position;
sg_usr_data_template_property[USR_PROPERTY_INDEX_POSITION].value.value_struct.count =
TOTAL_USR_PROPERTY_STRUCT_POSITION_COUNT;
sg_usr_data_template_property[USR_PROPERTY_INDEX_POSITION].key = "position";
sg_usr_data_template_property[USR_PROPERTY_INDEX_POSITION].type = DATA_TEMPLATE_TYPE_STRUCT;
sg_usr_data_template_property[USR_PROPERTY_INDEX_POSITION].need_report = 1;
sg_usr_data_template_property[USR_PROPERTY_INDEX_POSITION].is_rw = 0;
static char sg_usr_property_power[6 + 1] = "high";
sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER].value.value_string_enum = sg_usr_property_power;
sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER].key = "power";
sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER].type = DATA_TEMPLATE_TYPE_STRING_ENUM;
sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER].need_report = 1;
sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER].is_rw = 1;
}
// ----------------------------------------------------------------------------
// user event
// ----------------------------------------------------------------------------
/**
* @brief Sample of event status_report post params.
*
*/
static const char* sg_usr_event_status_report_params = "{\"status\":0,\"message\":\" \"}";
/**
* @brief Sample of event low_voltage post params.
*
*/
static const char* sg_usr_event_low_voltage_params = "{\"voltage\":1}";
/**
* @brief Sample of event hardware_fault post params.
*
*/
static const char* sg_usr_event_hardware_fault_params = "{\"name\":\" \",\"error_code\":1}";
#define TOTAL_USR_EVENT_COUNT 3
static DataTemplateEvent sg_usr_data_template_event[TOTAL_USR_EVENT_COUNT];
static void _init_data_template_event(void)
{
sg_usr_data_template_event[USR_EVENT_INDEX_STATUS_REPORT].event_id = "status_report";
sg_usr_data_template_event[USR_EVENT_INDEX_STATUS_REPORT].type = IOT_DATA_TEMPLATE_EVENT_TYPE_INFO;
sg_usr_data_template_event[USR_EVENT_INDEX_STATUS_REPORT].params = sg_usr_event_status_report_params;
sg_usr_data_template_event[USR_EVENT_INDEX_LOW_VOLTAGE].event_id = "low_voltage";
sg_usr_data_template_event[USR_EVENT_INDEX_LOW_VOLTAGE].type = IOT_DATA_TEMPLATE_EVENT_TYPE_ALERT;
sg_usr_data_template_event[USR_EVENT_INDEX_LOW_VOLTAGE].params = sg_usr_event_low_voltage_params;
sg_usr_data_template_event[USR_EVENT_INDEX_HARDWARE_FAULT].event_id = "hardware_fault";
sg_usr_data_template_event[USR_EVENT_INDEX_HARDWARE_FAULT].type = IOT_DATA_TEMPLATE_EVENT_TYPE_FAULT;
sg_usr_data_template_event[USR_EVENT_INDEX_HARDWARE_FAULT].params = sg_usr_event_hardware_fault_params;
}
// ----------------------------------------------------------------------------
// user action
// ----------------------------------------------------------------------------
#define TOTAL_USR_ACTION_LIGHT_BLINK_INPUT_PARAMS_COUNT 3
static DataTemplateProperty sg_usr_action_light_blink_input[TOTAL_USR_ACTION_LIGHT_BLINK_INPUT_PARAMS_COUNT];
static void _init_data_template_action_light_blink_input(void)
{
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME].value.value_int = 0;
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME].key = "time";
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME].type = DATA_TEMPLATE_TYPE_INT;
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME].is_rw = 1;
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR].value.value_bool = 0;
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR].key = "color";
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR].type = DATA_TEMPLATE_TYPE_BOOL;
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR].is_rw = 1;
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME].value.value_int = 0;
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME].key = "total_time";
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME].type = DATA_TEMPLATE_TYPE_INT;
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME].is_rw = 1;
}
/**
* @brief Sample of action light_blink reply.
*
*/
static IotDataTemplateActionReply sg_usr_action_light_blink_reply = {
.code = 0,
.client_token = {.value = "test_light_blink", .value_len = sizeof("test_light_blink")},
.response = "{\"err_code\":0}",
};
#define TOTAL_USR_ACTION_COUNT 1
static DataTemplateAction sg_usr_data_template_action[TOTAL_USR_ACTION_COUNT];
static void _init_data_template_action(void)
{
_init_data_template_action_light_blink_input();
sg_usr_data_template_action[USR_ACTION_INDEX_LIGHT_BLINK].action_id = "light_blink";
sg_usr_data_template_action[USR_ACTION_INDEX_LIGHT_BLINK].input_struct.value_struct.property =
sg_usr_action_light_blink_input;
sg_usr_data_template_action[USR_ACTION_INDEX_LIGHT_BLINK].input_struct.value_struct.count =
TOTAL_USR_ACTION_LIGHT_BLINK_INPUT_PARAMS_COUNT;
sg_usr_data_template_action[USR_ACTION_INDEX_LIGHT_BLINK].reply = sg_usr_action_light_blink_reply;
}

View File

@@ -0,0 +1,3 @@
file(GLOB src_app ${CMAKE_CURRENT_SOURCE_DIR}/*.c)
add_executable(app_ota ${src_app})
target_link_libraries(app_ota ${libsdk})

View File

@@ -0,0 +1,238 @@
/**
* @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 "ota_downloader.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(mqtt_message->payload_str));
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->event_handle.h_fp = _mqtt_event_handler;
}
// ----------------------------------------------------------------------------
// OTA callback
// ----------------------------------------------------------------------------
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);
// only one firmware one time is supportted now
OTAFirmwareInfo firmware_info;
memset(&firmware_info, 0, sizeof(OTAFirmwareInfo));
strncpy(firmware_info.version, version.value, version.value_len);
strncpy(firmware_info.md5sum, md5sum.value, md5sum.value_len);
firmware_info.file_size = file_size;
ota_downloader_info_set(&firmware_info, url.value, url.value_len);
}
// ----------------------------------------------------------------------------
// 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 = DEFAULT_LOG_HANDLE_FUNCS;
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;
}
// OTA init
IotOTAUpdateCallback ota_callback = {
.update_firmware_callback = _update_firmware_callback,
.report_version_reply_callback = NULL,
};
rc = IOT_OTA_Init(client, ota_callback, NULL);
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;
}
rc = ota_downloader_init(client);
if (rc) {
Log_e("OTA downloader init failed!, rc=%d", rc);
return rc;
}
do {
ota_downloader_process();
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:
ota_downloader_deinit();
IOT_OTA_Deinit(client);
rc = IOT_MQTT_Destroy(&client);
utils_log_deinit();
return rc;
}

View File

@@ -0,0 +1,409 @@
/**
* @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_downloader.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-20
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-20 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "ota_downloader.h"
#include "utils_log.h"
#include "utils_md5.h"
#define OTA_HTTP_TIMEOUT_MS 5000
#define OTA_HTTP_BUF_SIZE 1024
#define MAX_SIZE_OF_DOWNLOAD_URL 512
/**
* @brief Break point info.
*
*/
typedef struct {
OTAFirmwareInfo file_id;
uint32_t downloaded_size;
} OTADownloadInfo;
/**
* @brief OTA downloader handle.
*
*/
typedef struct {
void* cos_download;
void* mqtt_client;
void* downloader;
OTAFirmwareInfo download_now;
OTADownloadInfo break_point;
char download_url[MAX_SIZE_OF_DOWNLOAD_URL];
uint8_t download_buff[OTA_HTTP_BUF_SIZE];
uint32_t download_size;
IotMd5Context download_md5_ctx;
OTADownloaderStatus status;
} OTADownloaderHandle;
/**
* @brief Handle.
*
*/
static OTADownloaderHandle sg_ota_downloader_handle = {0};
// ----------------------------------------------------------------------------
// Downloader function
// ----------------------------------------------------------------------------
// break point function
/**
* @brief Read break point from file.
*
* @param[in,out] usr_data @see OTADownloaderHandle
* @return 0 for success
*/
static int _ota_break_point_init(void* usr_data)
{
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
sg_ota_downloader_handle.cos_download = NULL;
utils_md5_reset(&handle->download_md5_ctx);
memset(&handle->break_point, 0, sizeof(handle->break_point));
size_t len =
HAL_File_Read(OTA_BREAK_POINT_FILE_PATH, (uint8_t*)&handle->break_point, sizeof(handle->break_point), 0);
if (len != sizeof(handle->break_point)) {
Log_w("open break point file fail!");
}
return 0;
}
/**
* @brief Memset break point.
*
* @param[in,out] usr_data @see OTADownloaderHandle
*/
static void _ota_break_point_deinit(void* usr_data)
{
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
memset(&handle->break_point, 0, sizeof(handle->break_point));
}
/**
* @brief Set break point using download now info.
*
* @param[in,out] usr_data @see OTADownloaderHandle
* @return 0 for success
*/
static int _ota_break_point_set(void* usr_data)
{
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
// read break point info from flash
memset(&handle->break_point, 0, sizeof(handle->break_point));
memcpy(&handle->break_point.file_id, &handle->download_now, sizeof(handle->break_point.file_id));
return 0;
}
/**
* @brief Update break point and save.
*
* @param[in,out] usr_data @see OTADownloaderHandle
* @return 0 for success
*/
static int _ota_break_point_save(void* usr_data)
{
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
// update md5 sum & download_size
handle->break_point.downloaded_size += handle->download_size;
utils_md5_update(&handle->download_md5_ctx, handle->download_buff, handle->download_size);
// report progress
char buf[256];
int buf_len = sizeof(buf);
IOT_OTA_ReportProgress(handle->mqtt_client, buf, buf_len, IOT_OTA_REPORT_TYPE_DOWNLOADING,
handle->break_point.downloaded_size * 100 / handle->download_now.file_size,
handle->break_point.file_id.version);
// write to local only write downloaded size is ok
size_t write_size =
HAL_File_Write(OTA_BREAK_POINT_FILE_PATH, (uint8_t*)&handle->break_point, sizeof(OTADownloadInfo), 0);
return write_size != sizeof(OTADownloadInfo);
}
/**
* @brief Check if break point matches download now info.
*
* @param[in,out] usr_data @see OTADownloaderHandle
* @return 0 for success
*/
static int _ota_break_point_check(void* usr_data)
{
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
// version should be the same, download size should not bigger than file size
Log_d("download now:%s,%d,%s", handle->download_now.version, handle->download_now.file_size,
handle->download_now.md5sum);
Log_d("break point:%s,%d,%d,%s", handle->break_point.file_id.version, handle->break_point.file_id.file_size,
handle->break_point.downloaded_size, handle->break_point.file_id.md5sum);
return strncmp(handle->break_point.file_id.version, handle->download_now.version, MAX_SIZE_OF_FW_VERSION) ||
handle->break_point.file_id.file_size != handle->download_now.file_size ||
handle->break_point.downloaded_size > handle->download_now.file_size ||
strncmp(handle->break_point.file_id.md5sum, handle->download_now.md5sum, 32);
}
/**
* @brief Calculate md5 sum according break point.
*
* @param[in,out] usr_data @see OTADownloaderHandle
* @return 0 for success
*/
static int _ota_break_point_restore(void* usr_data)
{
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
// update md5 according downloaded data
size_t rlen, total_read = 0, size = 0;
size = handle->break_point.downloaded_size;
while (size > 0) {
rlen = (size > OTA_HTTP_BUF_SIZE) ? OTA_HTTP_BUF_SIZE : size;
if (!HAL_File_Read(OTA_FILE_PATH, handle->download_buff, rlen, total_read)) {
Log_e("read data failed");
handle->break_point.downloaded_size = 0;
break;
}
utils_md5_update(&handle->download_md5_ctx, handle->download_buff, rlen);
size -= rlen;
total_read += rlen;
}
return 0;
}
// data download function
/**
* @brief Init cos downloader.
*
* @param[in,out] usr_data @see OTADownloaderHandle
* @return 0 for success
*/
static int _ota_data_download_init(void* usr_data)
{
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
IotCosDownloadParams params = {
.url = handle->download_url,
.offset = handle->break_point.downloaded_size,
.file_size = handle->break_point.file_id.file_size,
.is_fragmentation = false,
.is_https_enabled = false,
};
handle->cos_download = IOT_COS_DownloadInit(&params);
return handle->cos_download ? 0 : -1;
}
/**
* @brief Deinit cos downloader.
*
* @param[in,out] usr_data @see OTADownloaderHandle
*/
static void _ota_data_download_deinit(void* usr_data)
{
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
IOT_COS_DownloadDeinit(handle->cos_download);
}
/**
* @brief Check if download finished.
*
* @param[in,out] usr_data @see OTADownloaderHandle
* @return 0 for success
*/
static int _ota_data_download_is_over(void* usr_data)
{
// check download is over
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
return handle->break_point.downloaded_size == handle->download_now.file_size;
}
/**
* @brief Download from cos server.
*
* @param[in,out] usr_data @see OTADownloaderHandle
* @return 0 for success
*/
static int _ota_data_download_recv(void* usr_data)
{
// download data using http
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
// TODO: https download
handle->download_size =
IOT_COS_DownloadFetch(handle->cos_download, handle->download_buff, OTA_HTTP_BUF_SIZE, OTA_HTTP_TIMEOUT_MS);
return handle->download_size;
}
/**
* @brief Sava firmware to file.
*
* @param[in,out] usr_data @see OTADownloaderHandle
* @return 0 for success
*/
static int _ota_data_download_save(void* usr_data)
{
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
return handle->download_size > 0 ? HAL_File_Write(OTA_FILE_PATH, handle->download_buff, handle->download_size,
handle->break_point.downloaded_size) != handle->download_size
: 0;
}
/**
* @brief Process download result.
*
* @param[in,out] usr_data @see OTADownloaderHandle
* @param[in] status status when finish download, @see UtilsDownloaderStatus
* @return 0 for success
*/
static int _ota_data_download_finish(void* usr_data, UtilsDownloaderStatus status)
{
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
int rc = 0;
char buf[256];
int buf_len = sizeof(buf);
switch (status) {
case UTILS_DOWNLOADER_STATUS_SUCCESS:
utils_md5_finish(&handle->download_md5_ctx);
// do something when ota finish;
int valid = !utils_md5_compare(&handle->download_md5_ctx, handle->download_now.md5sum);
if (!valid) {
memset(&handle->break_point, 0, sizeof(handle->break_point));
HAL_File_Write(OTA_BREAK_POINT_FILE_PATH, (uint8_t*)&handle->break_point, sizeof(handle->break_point),
0);
}
rc = valid ? IOT_OTA_ReportProgress(handle->mqtt_client, buf, buf_len, IOT_OTA_REPORT_TYPE_UPGRADE_SUCCESS,
0, handle->download_now.version)
: IOT_OTA_ReportProgress(handle->mqtt_client, buf, buf_len, IOT_OTA_REPORT_TYPE_MD5_NOT_MATCH, 0,
handle->download_now.version);
break;
case UTILS_DOWNLOADER_STATUS_NETWORK_FAILED:
rc = IOT_OTA_ReportProgress(handle->mqtt_client, buf, buf_len, IOT_OTA_REPORT_TYPE_DOWNLOAD_TIMEOUT, 0,
handle->download_now.version);
break;
case UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED:
case UTILS_DOWNLOADER_STATUS_DATA_DOWNLOAD_FAILED:
rc = IOT_OTA_ReportProgress(handle->mqtt_client, buf, buf_len, IOT_OTA_REPORT_TYPE_UPGRADE_FAIL, 0,
handle->download_now.version);
break;
default:
break;
}
handle->status = OTA_DOWNLOADER_STATUS_FINISHED;
return rc;
}
// ----------------------------------------------------------------------------
// API
// ----------------------------------------------------------------------------
/**
* @brief Init ota downloader.
*
* @param[in,out] client pointer to mqtt client
* @return 0 for success.
*/
int ota_downloader_init(void* client)
{
// downloader init
UtilsDownloaderFunction ota_callback = {
.downloader_malloc = HAL_Malloc,
.downloader_free = HAL_Free,
// break point
.break_point_init = _ota_break_point_init,
.break_point_deinit = _ota_break_point_deinit,
.break_point_set = _ota_break_point_set,
.break_point_save = _ota_break_point_save,
.break_point_check = _ota_break_point_check,
.break_point_restore = _ota_break_point_restore,
// data download
.data_download_init = _ota_data_download_init,
.data_download_deinit = _ota_data_download_deinit,
.data_download_is_over = _ota_data_download_is_over,
.data_download_recv = _ota_data_download_recv,
.data_download_save = _ota_data_download_save,
.data_download_finish = _ota_data_download_finish,
};
sg_ota_downloader_handle.downloader = utils_downloader_init(ota_callback, &sg_ota_downloader_handle);
if (!sg_ota_downloader_handle.downloader) {
Log_e("initialize downloaded failed");
return -1;
}
sg_ota_downloader_handle.mqtt_client = client;
sg_ota_downloader_handle.status = OTA_DOWNLOADER_STATUS_INITTED;
return 0;
}
/**
* @brief Set download info of ota firmware.
*
* @param[in] firmware_info pointer to firmware info
* @param[in] url url of cos download
* @param[in] url_len download length
*/
void ota_downloader_info_set(OTAFirmwareInfo* firmware_info, const char* url, int url_len)
{
if (OTA_DOWNLOADER_STATUS_DOWNLOADING != sg_ota_downloader_handle.status) {
memcpy(&sg_ota_downloader_handle.download_now, firmware_info, sizeof(OTAFirmwareInfo));
strncpy(sg_ota_downloader_handle.download_url, url, url_len);
sg_ota_downloader_handle.download_url[url_len] = '\0';
sg_ota_downloader_handle.status = OTA_DOWNLOADER_STATUS_DOWNLOADING;
}
}
/**
* @brief Process ota download.
*
* @return @see OTADownloaderStatus
*/
OTADownloaderStatus ota_downloader_process(void)
{
if (OTA_DOWNLOADER_STATUS_DOWNLOADING == sg_ota_downloader_handle.status) {
utils_downloader_process(sg_ota_downloader_handle.downloader);
}
return sg_ota_downloader_handle.status;
}
/**
* @brief Deinit ota downloader.
*
*/
void ota_downloader_deinit(void)
{
utils_downloader_deinit(sg_ota_downloader_handle.downloader);
memset(&sg_ota_downloader_handle, 0, sizeof(sg_ota_downloader_handle));
}

View File

@@ -0,0 +1,105 @@
/**
* @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_downloader.h
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-20
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-20 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_APP_OTA_OTA_DOWNLOADER_H_
#define IOT_HUB_DEVICE_C_SDK_APP_OTA_OTA_DOWNLOADER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "qcloud_iot_common.h"
#include "utils_downloader.h"
#define OTA_FILE_PATH "./app_ota_fw.bin"
#define OTA_BREAK_POINT_FILE_PATH "./break_point.dat"
/**
* @brief Firmware version size.
*
*/
#define MAX_SIZE_OF_FW_VERSION 32
/**
* @brief OTA firmware info.
*
*/
typedef struct {
char version[MAX_SIZE_OF_FW_VERSION + 4];
uint32_t file_size;
char md5sum[33];
} OTAFirmwareInfo;
/**
* @brief Downloader status.
*
*/
typedef enum {
OTA_DOWNLOADER_STATUS_NO_INITTED = 0,
OTA_DOWNLOADER_STATUS_INITTED,
OTA_DOWNLOADER_STATUS_DOWNLOADING,
OTA_DOWNLOADER_STATUS_FINISHED,
} OTADownloaderStatus;
/**
* @brief Init ota downloader.
*
* @param[in,out] client pointer to mqtt client
* @return 0 for success.
*/
int ota_downloader_init(void* client);
/**
* @brief Set download info of ota firmware.
*
* @param[in] firmware_info pointer to firmware info
* @param[in] url url of cos download
* @param[in] url_len download length
*/
void ota_downloader_info_set(OTAFirmwareInfo* firmware_info, const char* url, int url_len);
/**
* @brief Process ota download.
*
* @return @see OTADownloaderStatus
*/
OTADownloaderStatus ota_downloader_process(void);
/**
* @brief Deinit ota downloader.
*
*/
void ota_downloader_deinit(void);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_APP_OTA_OTA_DOWNLOADER_H_

View File

@@ -0,0 +1,7 @@
rm -rf build
git submodule init
git submodule update
mkdir -p build
cd build
cmake ..
make all -j16

View File

@@ -0,0 +1,10 @@
file(GLOB src ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(inc ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
set(src_common ${src_common} ${src} PARENT_SCOPE)
set(inc_common ${inc_common} ${inc} PARENT_SCOPE)
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()

View File

@@ -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 utils_base64.h
* @brief utils base64 header file
* @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
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_BASE64_H_
#define IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_BASE64_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
/**
* @brief Encode a buffer into base64 format.
*
* @param[out] dst destination buffer
* @param[in] dlen size of the destination buffer
* @param[out] olen number of bytes written
* @param[in] src source buffer
* @param[in] slen amount of data to be encoded
* @return 0 if successful, or MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL.
* *olen is always updated to reflect the amount
* of data that has (or would have) been written.
* If that length cannot be represented, then no data is
* written to the buffer and *olen is set to the maximum
* length representable as a size_t.
*
* @note Call this function with dlen = 0 to obtain the
* required buffer size in *olen
*/
int utils_base64encode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen);
/**
* @brief Decode a base64-formatted buffer.
*
* @param[out] dst destination buffer (can be NULL for checking size)
* @param[in] dlen size of the destination buffer
* @param[out] olen number of bytes written
* @param[in] src source buffer
* @param[in] slen amount of data to be decoded
* @return 0 if successful, MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL, or
* MBEDTLS_ERR_BASE64_INVALID_CHARACTER if the input data is
* not correct. *olen is always updated to reflect the amount
* of data that has (or would have) been written.
*
* @note Call this function with *dst = NULL or dlen = 0 to obtain
* the required buffer size in *olen
*/
int utils_base64decode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_BASE64_H_

View File

@@ -0,0 +1,74 @@
/**
* @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 utils_hmac.h
* @brief header file for utils_hmac
* @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
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_HMAC_H_
#define IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_HMAC_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <string.h>
#include "utils_sha1.h"
/**
* @brief Sha1 digest size.
*
*/
#define SHA1_DIGEST_SIZE 20
/**
* @brief Get digest of hmac-sha1.
*
* @param[in] msg message to hmac-sha1
* @param[in] msg_len message len
* @param[in] key key using in hmac-sha1
* @param[in] key_len key len
* @param[out] digest digest to calculate
* @return 0 for success
*/
int utils_hmac_sha1(const char *msg, int msg_len, const uint8_t *key, int key_len, char *digest);
/**
* @brief Get digest hex of hmac-sha1. Output hexstr format.
*
* @param[in] msg message to hmac-sha1
* @param[in] msg_len message len
* @param[in] key key using in hmac-sha1
* @param[in] key_len key len
* @param[out] digest digest to calculate
* @return 0 for success
*/
int utils_hmac_sha1_hex(const char *msg, int msg_len, const uint8_t *key, int key_len, char *digest);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_HMAC_H_

View File

@@ -0,0 +1,94 @@
/**
* @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 utils_md5.h
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-20
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-20 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_MD5_H_
#define IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_MD5_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "qcloud_iot_config.h"
#ifdef AUTH_WITH_NO_TLS
typedef struct {
uint32_t total[2]; /**< number of bytes processed */
uint32_t state[4]; /**< intermediate digest state */
uint8_t buffer[64]; /**< data block being processed */
char md5sum[33]; /**< md5sum result in hex string */
} IotMd5Context;
#else
#include "mbedtls/md5.h"
typedef struct {
mbedtls_md5_context ctx;
char md5sum[33]; /**< md5sum result in hex string */
} IotMd5Context;
#endif
/**
* @brief Reset MD5 context.
*
* @param[in,out] ctx MD5 context
*/
void utils_md5_reset(IotMd5Context *ctx);
/**
* @brief MD5 update.
*
* @param[in,out] ctx MD5 context
* @param[in] input input data
* @param[in] ilen data length
*/
void utils_md5_update(IotMd5Context *ctx, const uint8_t *input, size_t ilen);
/**
* @brief Finish MD5 calculation, result will store in md5sum.
*
* @param[in,out] ctx MD5 context
*/
void utils_md5_finish(IotMd5Context *ctx);
/**
* @brief Compare md5sum with context.
*
* @param[in,out] ctx MD5 context
* @param[in] md5sum md5sum to compare
* @return 0 for the same
*/
int utils_md5_compare(IotMd5Context *ctx, const char md5sum[33]);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_MD5_H_

View File

@@ -0,0 +1,119 @@
/**
* @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 utils_sha1.h
* @brief header file for utils-sha1
* @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 IotSha1Context
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_SHA1_H_
#define IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_SHA1_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
/**
* @brief SHA-1 context structure.
*
*/
typedef struct {
uint32_t total[2]; /**< number of bytes processed */
uint32_t state[5]; /**< intermediate digest state */
unsigned char buffer[64]; /**< data block being processed */
} IotSha1Context;
/**
* @brief Initialize SHA-1 context.
*
* @param[in,out] ctx SHA-1 context to be initialized
*/
void utils_sha1_init(IotSha1Context *ctx);
/**
* @brief Clear SHA-1 context.
*
* @param[in,out] ctx SHA-1 context to be cleared
*/
void utils_sha1_free(IotSha1Context *ctx);
/**
* @brief Clone (the state of) a SHA-1 context.
*
* @param[out] dst The destination context
* @param[in] src The context to be cloned
*/
void utils_sha1_clone(IotSha1Context *dst, const IotSha1Context *src);
/**
* @brief SHA-1 context setup
*
* @param[in,out] ctx context to be initialized
*/
void utils_sha1_starts(IotSha1Context *ctx);
/**
* @brief SHA-1 process buffer.
*
* @param[in,out] ctx SHA-1 context
* @param[in] input buffer holding the data
* @param[in] ilen length of the input data
*/
void utils_sha1_update(IotSha1Context *ctx, const unsigned char *input, size_t ilen);
/**
* @brief SHA-1 final digest
*
* @param[in,out] ctx SHA-1 context
* @param[out] output SHA-1 checksum result
*/
void utils_sha1_finish(IotSha1Context *ctx, unsigned char output[20]);
/**
* @brief Output = SHA-1( input buffer )
*
* @param[in] input buffer holding the data
* @param[in] ilen length of the input data
* @param[out] output SHA-1 checksum result
*/
void utils_sha1(const unsigned char *input, size_t ilen, unsigned char output[20]);
/**
* @brief Output = SHA-1( input buffer )
*
* @param[in] input buffer holding the data
* @param[in] ilen length of the input data
* @param[out] output SHA-1 checksum result hex
*/
void utils_sha1_hex(const unsigned char *input, size_t ilen, unsigned char output_hex[40]);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_CRYPTOLOGY_INC_UTILS_SHA1_H_

View File

@@ -0,0 +1,240 @@
/**
* @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 utils_base64.c
* @brief base64 encdoe & decode. Reference mbedtls/base64.c.
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-27
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-27 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "utils_base64.h"
static const unsigned char base64_enc_map[64] = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
static const unsigned char base64_dec_map[128] = {
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 62,
127, 127, 127, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 127, 127, 127, 64, 127, 127, 127, 0,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 127, 127, 127, 127, 127, 127, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 127, 127, 127, 127, 127};
#define BASE64_SIZE_T_MAX ((size_t)-1) /* SIZE_T_MAX is not standard */
/**
* @brief Check if src buf and dst buf.
*
* @param[in] dst destination buffer
* @param[in] dlen size of the destination buffer
* @param[in] src source buffer
* @param[in] slen amount of data to be encoded
* @return 0 for valid, -1 for invalid
*/
static int _utils_base64_check_valid(unsigned char *dst, size_t dlen, const unsigned char *src, size_t slen)
{
size_t i, n;
uint32_t j, x;
/* First pass: check for validity and get output length */
for (i = n = j = 0; i < slen; i++) {
/* Skip spaces before checking for EOL */
x = 0;
while (i < slen && src[i] == ' ') {
++i;
++x;
}
/* Spaces at end of buffer are OK */
if (i == slen)
break;
if ((slen - i) >= 2 && src[i] == '\r' && src[i + 1] == '\n')
continue;
if (src[i] == '\n')
continue;
/* Space inside a line is an error */
if (x != 0)
return -1;
if (src[i] == '=' && ++j > 2)
return -1;
if (src[i] > 127 || base64_dec_map[src[i]] == 127)
return -1;
if (base64_dec_map[src[i]] < 64 && j != 0)
return -1;
n++;
}
// if no useful data, means error
if (n == 0) {
return -1;
}
/* The following expression is to calculate the following formula without
* risk of integer overflow in n:
* n = ( ( n * 6 ) + 7 ) >> 3;
*/
n = (6 * (n >> 3)) + ((6 * (n & 0x7) + 7) >> 3);
n -= j;
if (!dst || dlen < n) {
return -1;
}
return 0;
}
/**
* @brief Encode a buffer into base64 format.
*
* @param[out] dst destination buffer
* @param[in] dlen size of the destination buffer
* @param[out] olen number of bytes written
* @param[in] src source buffer
* @param[in] slen amount of data to be encoded
* @return 0 if successful, or MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL.
* *olen is always updated to reflect the amount
* of data that has (or would have) been written.
* If that length cannot be represented, then no data is
* written to the buffer and *olen is set to the maximum
* length representable as a size_t.
*
* @note Call this function with dlen = 0 to obtain the
* required buffer size in *olen
*/
int utils_base64encode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen)
{
size_t i, n;
unsigned char *p;
if (slen == 0) {
*olen = 0;
return 0;
}
n = slen / 3 + (slen % 3 != 0);
if (n > (BASE64_SIZE_T_MAX - 1) / 4) {
*olen = BASE64_SIZE_T_MAX;
return -1;
}
n *= 4;
if ((dlen < n + 1) || (NULL == dst)) {
*olen = n + 1;
return -1;
}
n = (slen / 3) * 3;
int C1, C2, C3;
for (i = 0, p = dst; i < n; i += 3) {
C1 = *src++;
C2 = *src++;
C3 = *src++;
*p++ = base64_enc_map[(C1 >> 2) & 0x3F];
*p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F];
*p++ = base64_enc_map[(((C2 & 15) << 2) + (C3 >> 6)) & 0x3F];
*p++ = base64_enc_map[C3 & 0x3F];
}
if (i < slen) {
C1 = *src++;
C2 = ((i + 1) < slen) ? *src++ : 0;
*p++ = base64_enc_map[(C1 >> 2) & 0x3F];
*p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F];
if ((i + 1) < slen)
*p++ = base64_enc_map[((C2 & 15) << 2) & 0x3F];
else
*p++ = '=';
*p++ = '=';
}
*olen = p - dst;
*p = 0;
return 0;
}
/**
* @brief Decode a base64-formatted buffer.
*
* @param[out] dst destination buffer (can be NULL for checking size)
* @param[in] dlen size of the destination buffer
* @param[out] olen number of bytes written
* @param[in] src source buffer
* @param[in] slen amount of data to be decoded
* @return 0 if successful, MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL, or
* MBEDTLS_ERR_BASE64_INVALID_CHARACTER if the input data is
* not correct. *olen is always updated to reflect the amount
* of data that has (or would have) been written.
*
* @note Call this function with *dst = NULL or dlen = 0 to obtain
* the required buffer size in *olen
*/
int utils_base64decode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen)
{
size_t i, n;
uint32_t j, x;
unsigned char *p;
/* First pass: check for validity and get output length */
if (_utils_base64_check_valid(dst, dlen, src, slen)) {
*olen = 0;
return -1;
}
for (i = slen, j = 3, n = x = 0, p = dst; i > 0; i--, src++) {
if (*src == '\r' || *src == '\n' || *src == ' ')
continue;
j -= (base64_dec_map[*src] == 64);
x = (x << 6) | (base64_dec_map[*src] & 0x3F);
if (++n == 4) {
n = 0;
if (j > 0)
*p++ = (unsigned char)(x >> 16);
if (j > 1)
*p++ = (unsigned char)(x >> 8);
if (j > 2)
*p++ = (unsigned char)(x);
}
}
*olen = p - dst;
return 0;
}

View File

@@ -0,0 +1,144 @@
/**
* @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 utils_hmac.c
* @brief Support hmac
* @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 IotSha1Context
* </table>
*/
#include "utils_hmac.h"
/**
* @brief inner/outer padding size
*
*/
#define KEY_IO_PAD_SIZE 64
/**
* @brief sha1 digest size
*
*/
#define SHA1_DIGEST_SIZE 20
/**
* @brief Binary half byte to hex char
*
* @param[in] hb half byte
* @return hex char(0~f)
*/
static char _hb2hex(uint8_t hb)
{
hb = hb & 0xF;
return (char)(hb < 10 ? '0' + hb : hb - 10 + 'a');
}
static int _utils_hmac_sha1_process(const char *msg, int msg_len, const uint8_t *key, int key_len, unsigned char *out)
{
IotSha1Context context;
unsigned char k_ipad[KEY_IO_PAD_SIZE]; /* inner padding - key XORd with ipad */
unsigned char k_opad[KEY_IO_PAD_SIZE]; /* outer padding - key XORd with opad */
int i;
/* start out by storing key in pads */
memset(k_ipad, 0, sizeof(k_ipad));
memset(k_opad, 0, sizeof(k_opad));
memcpy(k_ipad, key, key_len);
memcpy(k_opad, key, key_len);
/* XOR key with ipad and opad values */
for (i = 0; i < KEY_IO_PAD_SIZE; i++) {
k_ipad[i] ^= 0x36;
k_opad[i] ^= 0x5c;
}
/* perform inner SHA */
utils_sha1_init(&context); /* init context for 1st pass */
utils_sha1_starts(&context); /* setup context for 1st pass */
utils_sha1_update(&context, k_ipad, KEY_IO_PAD_SIZE); /* start with inner pad */
utils_sha1_update(&context, (unsigned char *)msg, msg_len); /* then text of datagram */
utils_sha1_finish(&context, out); /* finish up 1st pass */
/* perform outer SHA */
utils_sha1_init(&context); /* init context for 2nd pass */
utils_sha1_starts(&context); /* setup context for 2nd pass */
utils_sha1_update(&context, k_opad, KEY_IO_PAD_SIZE); /* start with outer pad */
utils_sha1_update(&context, out, SHA1_DIGEST_SIZE); /* then results of 1st hash */
utils_sha1_finish(&context, out); /* finish up 2nd pass */
return 0;
}
/**
* @brief Get digest of hmac-sha1.
*
* @param[in] msg message to hmac-sha1
* @param[in] msg_len message len
* @param[in] key key using in hmac-sha1
* @param[in] key_len key len
* @param[out] digest digest to calculate
* @return 0 for success
*/
int utils_hmac_sha1(const char *msg, int msg_len, const uint8_t *key, int key_len, char *digest)
{
unsigned char out[SHA1_DIGEST_SIZE] = {0};
if (!msg || !digest || !key) {
return -1;
}
if (key_len > KEY_IO_PAD_SIZE) {
return -1;
}
_utils_hmac_sha1_process(msg, msg_len, key, key_len, out);
memcpy(digest, out, SHA1_DIGEST_SIZE);
return 0;
}
/**
* @brief Get digest hex of hmac-sha1.
*
* @param[in] msg message to hmac-sha1
* @param[in] msg_len message len
* @param[in] key key using in hmac-sha1
* @param[in] key_len key len
* @param[out] digest digest to calculate
* @return 0 for success
*/
int utils_hmac_sha1_hex(const char *msg, int msg_len, const uint8_t *key, int key_len, char *digest)
{
unsigned char out[SHA1_DIGEST_SIZE] = {0};
if (!msg || !digest || !key) {
return -1;
}
if (key_len > KEY_IO_PAD_SIZE) {
return -1;
}
_utils_hmac_sha1_process(msg, msg_len, key, key_len, out);
for (int i = 0; i < SHA1_DIGEST_SIZE; ++i) {
digest[i * 2] = _hb2hex(out[i] >> 4);
digest[i * 2 + 1] = _hb2hex(out[i]);
}
return 0;
}

View File

@@ -0,0 +1,409 @@
/**
* @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 utils_md5.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 "utils_md5.h"
/**
* @brief Binary half byte to hex char
*
* @param[in] hb half byte
* @return hex char(0~f)
*/
static char _hb2hex(uint8_t hb)
{
hb = hb & 0xF;
return (char)(hb < 10 ? '0' + hb : hb - 10 + 'a');
}
/**
* @brief Lower md5sum.
*
* @param[out] md5_lower md5 sum lower
* @param[in] md5 md5 sum
*/
static void _lower(char md5_lower[33], const char md5[33])
{
int i = 0;
for (i = 0; i < 32; i++) {
md5_lower[i] = md5[i];
if (md5[i] >= 'A' && md5[i] <= 'F') {
md5_lower[i] += ('a' - 'A');
}
}
md5_lower[32] = '\0';
}
#ifdef AUTH_WITH_NO_TLS
/**
* @brief 32-bit integer manipulation macros (little endian)
*
*/
#define IOT_MD5_GET_UINT32_LE(n, b, i) \
{ \
(n) = ((uint32_t)(b)[(i)]) | ((uint32_t)(b)[(i) + 1] << 8) | ((uint32_t)(b)[(i) + 2] << 16) | \
((uint32_t)(b)[(i) + 3] << 24); \
}
#define IOT_MD5_PUT_UINT32_LE(n, b, i) \
{ \
(b)[(i)] = (uint8_t)(((n)) & 0xFF); \
(b)[(i) + 1] = (uint8_t)(((n) >> 8) & 0xFF); \
(b)[(i) + 2] = (uint8_t)(((n) >> 16) & 0xFF); \
(b)[(i) + 3] = (uint8_t)(((n) >> 24) & 0xFF); \
}
/**
* @brief Padding for md5.
*
*/
static const uint8_t sg_iot_md5_padding[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
/**
* @brief Initialize md5 context.
*
* @param[in,out] ctx MD5 context
*/
static void _utils_md5_init(IotMd5Context *ctx)
{
memset(ctx, 0, sizeof(IotMd5Context));
}
/**
* @brief Start md5 calculate.
*
* @param[in,out] ctx MD5 context
*/
static void _utils_md5_start(IotMd5Context *ctx)
{
ctx->total[0] = 0;
ctx->total[1] = 0;
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xEFCDAB89;
ctx->state[2] = 0x98BADCFE;
ctx->state[3] = 0x10325476;
}
/**
* @brief Calculate md5.
*
* @param[in,out] ctx MD5 context
* @param[in] data data to calculate
*/
static void _utils_md5_process(IotMd5Context *ctx, const uint8_t data[64])
{
uint32_t X[16], A, B, C, D;
IOT_MD5_GET_UINT32_LE(X[0], data, 0);
IOT_MD5_GET_UINT32_LE(X[1], data, 4);
IOT_MD5_GET_UINT32_LE(X[2], data, 8);
IOT_MD5_GET_UINT32_LE(X[3], data, 12);
IOT_MD5_GET_UINT32_LE(X[4], data, 16);
IOT_MD5_GET_UINT32_LE(X[5], data, 20);
IOT_MD5_GET_UINT32_LE(X[6], data, 24);
IOT_MD5_GET_UINT32_LE(X[7], data, 28);
IOT_MD5_GET_UINT32_LE(X[8], data, 32);
IOT_MD5_GET_UINT32_LE(X[9], data, 36);
IOT_MD5_GET_UINT32_LE(X[10], data, 40);
IOT_MD5_GET_UINT32_LE(X[11], data, 44);
IOT_MD5_GET_UINT32_LE(X[12], data, 48);
IOT_MD5_GET_UINT32_LE(X[13], data, 52);
IOT_MD5_GET_UINT32_LE(X[14], data, 56);
IOT_MD5_GET_UINT32_LE(X[15], data, 60);
#define S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
#define P(a, b, c, d, k, s, t) \
{ \
a += F(b, c, d) + X[k] + t; \
a = S(a, s) + b; \
}
A = ctx->state[0];
B = ctx->state[1];
C = ctx->state[2];
D = ctx->state[3];
#define F(x, y, z) (z ^ (x & (y ^ z)))
P(A, B, C, D, 0, 7, 0xD76AA478);
P(D, A, B, C, 1, 12, 0xE8C7B756);
P(C, D, A, B, 2, 17, 0x242070DB);
P(B, C, D, A, 3, 22, 0xC1BDCEEE);
P(A, B, C, D, 4, 7, 0xF57C0FAF);
P(D, A, B, C, 5, 12, 0x4787C62A);
P(C, D, A, B, 6, 17, 0xA8304613);
P(B, C, D, A, 7, 22, 0xFD469501);
P(A, B, C, D, 8, 7, 0x698098D8);
P(D, A, B, C, 9, 12, 0x8B44F7AF);
P(C, D, A, B, 10, 17, 0xFFFF5BB1);
P(B, C, D, A, 11, 22, 0x895CD7BE);
P(A, B, C, D, 12, 7, 0x6B901122);
P(D, A, B, C, 13, 12, 0xFD987193);
P(C, D, A, B, 14, 17, 0xA679438E);
P(B, C, D, A, 15, 22, 0x49B40821);
#undef F
#define F(x, y, z) (y ^ (z & (x ^ y)))
P(A, B, C, D, 1, 5, 0xF61E2562);
P(D, A, B, C, 6, 9, 0xC040B340);
P(C, D, A, B, 11, 14, 0x265E5A51);
P(B, C, D, A, 0, 20, 0xE9B6C7AA);
P(A, B, C, D, 5, 5, 0xD62F105D);
P(D, A, B, C, 10, 9, 0x02441453);
P(C, D, A, B, 15, 14, 0xD8A1E681);
P(B, C, D, A, 4, 20, 0xE7D3FBC8);
P(A, B, C, D, 9, 5, 0x21E1CDE6);
P(D, A, B, C, 14, 9, 0xC33707D6);
P(C, D, A, B, 3, 14, 0xF4D50D87);
P(B, C, D, A, 8, 20, 0x455A14ED);
P(A, B, C, D, 13, 5, 0xA9E3E905);
P(D, A, B, C, 2, 9, 0xFCEFA3F8);
P(C, D, A, B, 7, 14, 0x676F02D9);
P(B, C, D, A, 12, 20, 0x8D2A4C8A);
#undef F
#define F(x, y, z) (x ^ y ^ z)
P(A, B, C, D, 5, 4, 0xFFFA3942);
P(D, A, B, C, 8, 11, 0x8771F681);
P(C, D, A, B, 11, 16, 0x6D9D6122);
P(B, C, D, A, 14, 23, 0xFDE5380C);
P(A, B, C, D, 1, 4, 0xA4BEEA44);
P(D, A, B, C, 4, 11, 0x4BDECFA9);
P(C, D, A, B, 7, 16, 0xF6BB4B60);
P(B, C, D, A, 10, 23, 0xBEBFBC70);
P(A, B, C, D, 13, 4, 0x289B7EC6);
P(D, A, B, C, 0, 11, 0xEAA127FA);
P(C, D, A, B, 3, 16, 0xD4EF3085);
P(B, C, D, A, 6, 23, 0x04881D05);
P(A, B, C, D, 9, 4, 0xD9D4D039);
P(D, A, B, C, 12, 11, 0xE6DB99E5);
P(C, D, A, B, 15, 16, 0x1FA27CF8);
P(B, C, D, A, 2, 23, 0xC4AC5665);
#undef F
#define F(x, y, z) (y ^ (x | ~z))
P(A, B, C, D, 0, 6, 0xF4292244);
P(D, A, B, C, 7, 10, 0x432AFF97);
P(C, D, A, B, 14, 15, 0xAB9423A7);
P(B, C, D, A, 5, 21, 0xFC93A039);
P(A, B, C, D, 12, 6, 0x655B59C3);
P(D, A, B, C, 3, 10, 0x8F0CCC92);
P(C, D, A, B, 10, 15, 0xFFEFF47D);
P(B, C, D, A, 1, 21, 0x85845DD1);
P(A, B, C, D, 8, 6, 0x6FA87E4F);
P(D, A, B, C, 15, 10, 0xFE2CE6E0);
P(C, D, A, B, 6, 15, 0xA3014314);
P(B, C, D, A, 13, 21, 0x4E0811A1);
P(A, B, C, D, 4, 6, 0xF7537E82);
P(D, A, B, C, 11, 10, 0xBD3AF235);
P(C, D, A, B, 2, 15, 0x2AD7D2BB);
P(B, C, D, A, 9, 21, 0xEB86D391);
#undef F
ctx->state[0] += A;
ctx->state[1] += B;
ctx->state[2] += C;
ctx->state[3] += D;
}
/**
* @brief Reset MD5 context.
*
* @param[in,out] ctx MD5 context
*/
void utils_md5_reset(IotMd5Context *ctx)
{
memset(ctx, 0, sizeof(IotMd5Context));
_utils_md5_init(ctx);
_utils_md5_start(ctx);
}
/**
* @brief MD5 update.
*
* @param[in,out] ctx MD5 context
* @param[in] input input data
* @param[in] ilen data length
*/
void utils_md5_update(IotMd5Context *ctx, const uint8_t *input, size_t ilen)
{
size_t fill;
uint32_t left;
if (ilen == 0) {
return;
}
left = ctx->total[0] & 0x3F;
fill = 64 - left;
ctx->total[0] += (uint32_t)ilen;
ctx->total[0] &= 0xFFFFFFFF;
if (ctx->total[0] < (uint32_t)ilen) {
ctx->total[1]++;
}
if (left && ilen >= fill) {
memcpy((void *)(ctx->buffer + left), input, fill);
_utils_md5_process(ctx, ctx->buffer);
input += fill;
ilen -= fill;
left = 0;
}
while (ilen >= 64) {
_utils_md5_process(ctx, input);
input += 64;
ilen -= 64;
}
if (ilen > 0) {
memcpy((void *)(ctx->buffer + left), input, ilen);
}
}
/**
* @brief Finish MD5 calculation, result will store in md5sum.
*
* @param[in,out] ctx MD5 context
*/
void utils_md5_finish(IotMd5Context *ctx)
{
int i;
uint32_t last, padn;
uint32_t high, low;
uint8_t msglen[8];
uint8_t output_tmp[16];
high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
low = (ctx->total[0] << 3);
IOT_MD5_PUT_UINT32_LE(low, msglen, 0);
IOT_MD5_PUT_UINT32_LE(high, msglen, 4);
last = ctx->total[0] & 0x3F;
padn = (last < 56) ? (56 - last) : (120 - last);
utils_md5_update(ctx, sg_iot_md5_padding, padn);
utils_md5_update(ctx, msglen, 8);
IOT_MD5_PUT_UINT32_LE(ctx->state[0], output_tmp, 0);
IOT_MD5_PUT_UINT32_LE(ctx->state[1], output_tmp, 4);
IOT_MD5_PUT_UINT32_LE(ctx->state[2], output_tmp, 8);
IOT_MD5_PUT_UINT32_LE(ctx->state[3], output_tmp, 12);
for (i = 0; i < 16; ++i) {
ctx->md5sum[i * 2] = _hb2hex(output_tmp[i] >> 4);
ctx->md5sum[i * 2 + 1] = _hb2hex(output_tmp[i]);
}
ctx->md5sum[32] = '\0';
}
/**
* @brief Compare md5sum with context.
*
* @param[in,out] ctx MD5 context
* @param[in] md5sum md5sum to compare
* @return 0 for the same
*/
int utils_md5_compare(IotMd5Context *ctx, const char md5sum[33])
{
char md5sum_lower[33];
_lower(md5sum_lower, md5sum);
return strncmp(ctx->md5sum, md5sum_lower, 32);
}
#else
/**
* @brief Reset MD5 context.
*
* @param[in,out] ctx MD5 context
*/
void utils_md5_reset(IotMd5Context *ctx)
{
memset(ctx, 0, sizeof(IotMd5Context));
mbedtls_md5_init(&ctx->ctx);
mbedtls_md5_starts(&ctx->ctx);
}
/**
* @brief MD5 update.
*
* @param[in,out] ctx MD5 context
* @param[in] input input data
* @param[in] ilen data length
*/
void utils_md5_update(IotMd5Context *ctx, const uint8_t *input, size_t ilen)
{
mbedtls_md5_update(&ctx->ctx, input, ilen);
}
/**
* @brief Finish MD5 calculation, result will store in md5sum.
*
* @param[in,out] ctx MD5 context
*/
void utils_md5_finish(IotMd5Context *ctx)
{
int i;
uint8_t output_tmp[16];
mbedtls_md5_finish(&ctx->ctx, output_tmp);
for (i = 0; i < 16; ++i) {
ctx->md5sum[i * 2] = _hb2hex(output_tmp[i] >> 4);
ctx->md5sum[i * 2 + 1] = _hb2hex(output_tmp[i]);
}
ctx->md5sum[32] = '\0';
}
/**
* @brief Compare md5sum with context.
*
* @param[in,out] ctx MD5 context
* @param[in] md5sum md5sum to compare
* @return 0 for the same
*/
int utils_md5_compare(IotMd5Context *ctx, const char md5sum[33])
{
char md5sum_lower[33];
_lower(md5sum_lower, md5sum);
return strncmp(ctx->md5sum, md5sum_lower, 32);
}
#endif

View File

@@ -0,0 +1,408 @@
/**
* @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 utils_sha1.c
* @brief SHA-1 operation, reference mbedtls
* @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 IotSha1Context
* </table>
*/
#include "utils_sha1.h"
#ifndef IOT_SHA1_GET_UINT32_BE
/**
* @brief Get 32-bit integer manipulation macros (big endian)
*
*/
#define IOT_SHA1_GET_UINT32_BE(n, b, i) \
{ \
(n) = ((uint32_t)(b)[(i)] << 24) | ((uint32_t)(b)[(i) + 1] << 16) | ((uint32_t)(b)[(i) + 2] << 8) | \
((uint32_t)(b)[(i) + 3]); \
}
#endif
#ifndef IOT_SHA1_PUT_UINT32_BE
/**
* @brief Put 32-bit integer manipulation macros (big endian)
*
*/
#define IOT_SHA1_PUT_UINT32_BE(n, b, i) \
{ \
(b)[(i)] = (unsigned char)((n) >> 24); \
(b)[(i) + 1] = (unsigned char)((n) >> 16); \
(b)[(i) + 2] = (unsigned char)((n) >> 8); \
(b)[(i) + 3] = (unsigned char)((n)); \
}
#endif
/**
* @brief Binary half byte to hex char
*
* @param[in] hb half byte
* @return hex char(0~f)
*/
static char _hb2hex(uint8_t hb)
{
hb = hb & 0xF;
return (char)(hb < 10 ? '0' + hb : hb - 10 + 'a');
}
/**
* @brief Implementation that should never be optimized out by the compiler.
*
* @param[in] v pointer to ctx
* @param[in] n sizeof ctx
*/
static void utils_sha1_zeroize(void *v, size_t n)
{
volatile unsigned char *p = v;
while (n--) {
*p++ = 0;
}
}
/**
* @brief Initialize SHA-1 context.
*
* @param[in,out] ctx SHA-1 context to be initialized
*/
void utils_sha1_init(IotSha1Context *ctx)
{
memset(ctx, 0, sizeof(IotSha1Context));
}
/**
* @brief Clear SHA-1 context.
*
* @param[in,out] ctx SHA-1 context to be cleared
*/
void utils_sha1_free(IotSha1Context *ctx)
{
if (!ctx) {
return;
}
utils_sha1_zeroize(ctx, sizeof(IotSha1Context));
}
/**
* @brief Clone (the state of) a SHA-1 context.
*
* @param[out] dst The destination context
* @param[in] src The context to be cloned
*/
void utils_sha1_clone(IotSha1Context *dst, const IotSha1Context *src)
{
*dst = *src;
}
/**
* @brief SHA-1 context setup
*
* @param[in,out] ctx context to be initialized
*/
void utils_sha1_starts(IotSha1Context *ctx)
{
ctx->total[0] = 0;
ctx->total[1] = 0;
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xEFCDAB89;
ctx->state[2] = 0x98BADCFE;
ctx->state[3] = 0x10325476;
ctx->state[4] = 0xC3D2E1F0;
}
/**
* @brief SHA-1 process
*
* @param[in,out] ctx pointer to ctx
* @param[in] data data to be calculated
*/
void utils_sha1_process(IotSha1Context *ctx, const unsigned char data[64])
{
uint32_t temp, W[16], A, B, C, D, E;
IOT_SHA1_GET_UINT32_BE(W[0], data, 0);
IOT_SHA1_GET_UINT32_BE(W[1], data, 4);
IOT_SHA1_GET_UINT32_BE(W[2], data, 8);
IOT_SHA1_GET_UINT32_BE(W[3], data, 12);
IOT_SHA1_GET_UINT32_BE(W[4], data, 16);
IOT_SHA1_GET_UINT32_BE(W[5], data, 20);
IOT_SHA1_GET_UINT32_BE(W[6], data, 24);
IOT_SHA1_GET_UINT32_BE(W[7], data, 28);
IOT_SHA1_GET_UINT32_BE(W[8], data, 32);
IOT_SHA1_GET_UINT32_BE(W[9], data, 36);
IOT_SHA1_GET_UINT32_BE(W[10], data, 40);
IOT_SHA1_GET_UINT32_BE(W[11], data, 44);
IOT_SHA1_GET_UINT32_BE(W[12], data, 48);
IOT_SHA1_GET_UINT32_BE(W[13], data, 52);
IOT_SHA1_GET_UINT32_BE(W[14], data, 56);
IOT_SHA1_GET_UINT32_BE(W[15], data, 60);
#define S(x, n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
#define R(t) \
(temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ W[(t - 14) & 0x0F] ^ W[t & 0x0F], (W[t & 0x0F] = S(temp, 1)))
#define P(a, b, c, d, e, x) \
{ \
e += S(a, 5) + F(b, c, d) + K + x; \
b = S(b, 30); \
}
A = ctx->state[0];
B = ctx->state[1];
C = ctx->state[2];
D = ctx->state[3];
E = ctx->state[4];
#define F(x, y, z) (z ^ (x & (y ^ z)))
#define K 0x5A827999
P(A, B, C, D, E, W[0]);
P(E, A, B, C, D, W[1]);
P(D, E, A, B, C, W[2]);
P(C, D, E, A, B, W[3]);
P(B, C, D, E, A, W[4]);
P(A, B, C, D, E, W[5]);
P(E, A, B, C, D, W[6]);
P(D, E, A, B, C, W[7]);
P(C, D, E, A, B, W[8]);
P(B, C, D, E, A, W[9]);
P(A, B, C, D, E, W[10]);
P(E, A, B, C, D, W[11]);
P(D, E, A, B, C, W[12]);
P(C, D, E, A, B, W[13]);
P(B, C, D, E, A, W[14]);
P(A, B, C, D, E, W[15]);
P(E, A, B, C, D, R(16));
P(D, E, A, B, C, R(17));
P(C, D, E, A, B, R(18));
P(B, C, D, E, A, R(19));
#undef K
#undef F
#define F(x, y, z) (x ^ y ^ z)
#define K 0x6ED9EBA1
P(A, B, C, D, E, R(20));
P(E, A, B, C, D, R(21));
P(D, E, A, B, C, R(22));
P(C, D, E, A, B, R(23));
P(B, C, D, E, A, R(24));
P(A, B, C, D, E, R(25));
P(E, A, B, C, D, R(26));
P(D, E, A, B, C, R(27));
P(C, D, E, A, B, R(28));
P(B, C, D, E, A, R(29));
P(A, B, C, D, E, R(30));
P(E, A, B, C, D, R(31));
P(D, E, A, B, C, R(32));
P(C, D, E, A, B, R(33));
P(B, C, D, E, A, R(34));
P(A, B, C, D, E, R(35));
P(E, A, B, C, D, R(36));
P(D, E, A, B, C, R(37));
P(C, D, E, A, B, R(38));
P(B, C, D, E, A, R(39));
#undef K
#undef F
#define F(x, y, z) ((x & y) | (z & (x | y)))
#define K 0x8F1BBCDC
P(A, B, C, D, E, R(40));
P(E, A, B, C, D, R(41));
P(D, E, A, B, C, R(42));
P(C, D, E, A, B, R(43));
P(B, C, D, E, A, R(44));
P(A, B, C, D, E, R(45));
P(E, A, B, C, D, R(46));
P(D, E, A, B, C, R(47));
P(C, D, E, A, B, R(48));
P(B, C, D, E, A, R(49));
P(A, B, C, D, E, R(50));
P(E, A, B, C, D, R(51));
P(D, E, A, B, C, R(52));
P(C, D, E, A, B, R(53));
P(B, C, D, E, A, R(54));
P(A, B, C, D, E, R(55));
P(E, A, B, C, D, R(56));
P(D, E, A, B, C, R(57));
P(C, D, E, A, B, R(58));
P(B, C, D, E, A, R(59));
#undef K
#undef F
#define F(x, y, z) (x ^ y ^ z)
#define K 0xCA62C1D6
P(A, B, C, D, E, R(60));
P(E, A, B, C, D, R(61));
P(D, E, A, B, C, R(62));
P(C, D, E, A, B, R(63));
P(B, C, D, E, A, R(64));
P(A, B, C, D, E, R(65));
P(E, A, B, C, D, R(66));
P(D, E, A, B, C, R(67));
P(C, D, E, A, B, R(68));
P(B, C, D, E, A, R(69));
P(A, B, C, D, E, R(70));
P(E, A, B, C, D, R(71));
P(D, E, A, B, C, R(72));
P(C, D, E, A, B, R(73));
P(B, C, D, E, A, R(74));
P(A, B, C, D, E, R(75));
P(E, A, B, C, D, R(76));
P(D, E, A, B, C, R(77));
P(C, D, E, A, B, R(78));
P(B, C, D, E, A, R(79));
#undef K
#undef F
ctx->state[0] += A;
ctx->state[1] += B;
ctx->state[2] += C;
ctx->state[3] += D;
ctx->state[4] += E;
}
/**
* @brief SHA-1 process buffer.
*
* @param[in,out] ctx SHA-1 context
* @param[in] input buffer holding the data
* @param[in] ilen length of the input data
*/
void utils_sha1_update(IotSha1Context *ctx, const unsigned char *input, size_t ilen)
{
size_t fill;
uint32_t left;
if (ilen == 0) {
return;
}
left = ctx->total[0] & 0x3F;
fill = 64 - left;
ctx->total[0] += (uint32_t)ilen;
ctx->total[0] &= 0xFFFFFFFF;
if (ctx->total[0] < (uint32_t)ilen) {
ctx->total[1]++;
}
if (left && ilen >= fill) {
memcpy((void *)(ctx->buffer + left), input, fill);
utils_sha1_process(ctx, ctx->buffer);
input += fill;
ilen -= fill;
left = 0;
}
while (ilen >= 64) {
utils_sha1_process(ctx, input);
input += 64;
ilen -= 64;
}
if (ilen > 0) {
memcpy((void *)(ctx->buffer + left), input, ilen);
}
}
static const unsigned char iot_sha1_padding[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/**
* @brief SHA-1 final digest
*
* @param[in,out] ctx SHA-1 context
* @param[out] output SHA-1 checksum result
*/
void utils_sha1_finish(IotSha1Context *ctx, unsigned char output[20])
{
uint32_t last, padn;
uint32_t high, low;
unsigned char msglen[8];
high = (ctx->total[0] >> 29) | (ctx->total[1] << 3);
low = (ctx->total[0] << 3);
IOT_SHA1_PUT_UINT32_BE(high, msglen, 0);
IOT_SHA1_PUT_UINT32_BE(low, msglen, 4);
last = ctx->total[0] & 0x3F;
padn = (last < 56) ? (56 - last) : (120 - last);
utils_sha1_update(ctx, iot_sha1_padding, padn);
utils_sha1_update(ctx, msglen, 8);
IOT_SHA1_PUT_UINT32_BE(ctx->state[0], output, 0);
IOT_SHA1_PUT_UINT32_BE(ctx->state[1], output, 4);
IOT_SHA1_PUT_UINT32_BE(ctx->state[2], output, 8);
IOT_SHA1_PUT_UINT32_BE(ctx->state[3], output, 12);
IOT_SHA1_PUT_UINT32_BE(ctx->state[4], output, 16);
}
/**
* @brief Output = SHA-1( input buffer )
*
* @param[in] input buffer holding the data
* @param[in] ilen length of the input data
* @param[out] output SHA-1 checksum result
*/
void utils_sha1(const unsigned char *input, size_t ilen, unsigned char output[20])
{
IotSha1Context ctx;
utils_sha1_init(&ctx);
utils_sha1_starts(&ctx);
utils_sha1_update(&ctx, input, ilen);
utils_sha1_finish(&ctx, output);
utils_sha1_free(&ctx);
}
/**
* @brief Output = SHA-1( input buffer )
*
* @param[in] input buffer holding the data
* @param[in] ilen length of the input data
* @param[out] output SHA-1 checksum result hex
*/
void utils_sha1_hex(const unsigned char *input, size_t ilen, unsigned char output_hex[40])
{
unsigned char out[20 + 1] = {0};
utils_sha1(input, ilen, out);
for (int i = 0; i < 20; ++i) {
output_hex[i * 2] = _hb2hex(out[i] >> 4);
output_hex[i * 2 + 1] = _hb2hex(out[i]);
}
}

View File

@@ -0,0 +1,8 @@
Language: Cpp
BasedOnStyle: Google
ColumnLimit: 120
DerivePointerAlignment: true
PointerAlignment: Left
SortIncludes: true
IncludeBlocks: Preserve
IndentPPDirectives: AfterHash

View File

@@ -0,0 +1,179 @@
/**
* @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_cryptology.cc
* @brief test cryptology
* @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>fix code standard of IotSha1Context
* </table>
*/
#include <iostream>
#include <string>
#include "gtest/gtest.h"
#include "utils_base64.h"
#include "utils_hmac.h"
#include "utils_md5.h"
#include "utils_sha1.h"
namespace cryptology_unittest {
/**
* @brief Test base64.
*
*/
TEST(CryptologyTest, base64) {
const uint8_t base64_test_dec[64] = {0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD, 0xBF, 0x17, 0xD9, 0xA2, 0xC4,
0x17, 0x1A, 0x01, 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09, 0x0C, 0xB6,
0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13, 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA,
0x31, 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38, 0x00, 0x43, 0xE9, 0x54,
0x97, 0xAF, 0x50, 0x4B, 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97};
const uint8_t base64_test_enc[] =
"JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK"
"swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw==";
size_t len;
uint8_t buffer[128];
ASSERT_EQ(utils_base64encode(buffer, sizeof(buffer), &len, base64_test_dec, sizeof(base64_test_dec)), 0);
ASSERT_EQ(memcmp(base64_test_enc, buffer, len), 0);
ASSERT_EQ(utils_base64decode(buffer, sizeof(buffer), &len, base64_test_enc, sizeof(base64_test_enc) - 1), 0);
ASSERT_EQ(memcmp(base64_test_dec, buffer, len), 0);
}
/**
* @brief Test hmac.
*
*/
TEST(CryptologyTest, hmac) {
/**
* @brief HMAC-SHA1
*
*/
const char test_buf[] = "Here is a test for hmac-sha1!";
const uint8_t key[] = "0123456789123456";
const char result[] = "614b650ffefff7862c1bc7fdd9de4e472a8184c4";
char buf[128] = {0};
ASSERT_EQ(utils_hmac_sha1_hex(test_buf, strlen(test_buf), key, sizeof(key), buf), 0);
ASSERT_EQ(memcmp(buf, result, sizeof(result)), 0);
}
/**
* @brief Test sha1.
*
*/
TEST(CryptologyTest, sha1) {
/*
* FIPS-180-1 test vectors
*/
const uint8_t sha1_test_buf[3][57] = {{"abc"}, {"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"}, {""}};
const int sha1_test_buflen[3] = {3, 56, 1000};
const uint8_t sha1_test_sum[3][20] = {
{
0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E,
0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D,
},
{
0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE,
0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1,
},
{
0x34, 0xAA, 0x97, 0x3C, 0xD4, 0xC4, 0xDA, 0xA4, 0xF6, 0x1E,
0xEB, 0x2B, 0xDB, 0xAD, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6F,
},
};
int i, j, buflen, rc = 0;
uint8_t buf[1024];
uint8_t sha1sum[20];
IotSha1Context ctx;
utils_sha1_init(&ctx);
for (i = 0; i < 3; i++) {
utils_sha1_starts(&ctx);
if (i == 2) {
memset(buf, 'a', buflen = 1000);
for (j = 0; j < 1000; j++) {
utils_sha1_update(&ctx, buf, buflen);
}
} else {
utils_sha1_update(&ctx, sha1_test_buf[i], sha1_test_buflen[i]);
}
utils_sha1_finish(&ctx, sha1sum);
ASSERT_EQ(memcmp(sha1sum, sha1_test_sum[i], 20), 0);
}
utils_sha1_free(&ctx);
utils_sha1(sha1_test_buf[0], 3, sha1sum);
ASSERT_EQ(memcmp(sha1sum, sha1_test_sum[0], 20), 0);
}
/**
* @brief Test md5.
*
*/
TEST(CryptologyTest, md5) {
/*
* RFC 1321 test vectors
*/
const uint8_t md5_test_buf[7][81] = {
{""},
{"a"},
{"abc"},
{"message digest"},
{"abcdefghijklmnopqrstuvwxyz"},
{"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"},
{"12345678901234567890123456789012345678901234567890123456789012345678901234567890"},
};
const size_t md5_test_buflen[7] = {0, 1, 3, 14, 26, 62, 80};
const char md5_test_sum[7][33] = {
{"D41D8CD98F00B204E9800998ECF8427E"}, {"0CC175B9C0F1B6A831C399E269772661"}, {"900150983CD24FB0D6963F7D28E17F72"},
{"F96B697D7CB7938D525A2F31AAF161D0"}, {"C3FCD3D76192E4007DFB496CCA67E13B"}, {"D174AB98D277D9F5A5611C2C9F419D9F"},
{"57EDF4A22BE3C955AC49DA2E2107B67A"},
};
/*
* Checkup routine
*/
IotMd5Context md5_ctx;
for (int i = 0; i < 7; i++) {
utils_md5_reset(&md5_ctx);
utils_md5_update(&md5_ctx, md5_test_buf[i], md5_test_buflen[i]);
utils_md5_finish(&md5_ctx);
ASSERT_EQ(utils_md5_compare(&md5_ctx, md5_test_sum[i]), 0);
}
}
} // namespace cryptology_unittest

View File

@@ -0,0 +1,10 @@
file(GLOB src ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(inc ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
set(src_common ${src_common} ${src} PARENT_SCOPE)
set(inc_common ${inc_common} ${inc} PARENT_SCOPE)
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()

View File

@@ -0,0 +1,335 @@
/**
* @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_packet.h
* @brief header file for mqtt packet
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-24
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-24 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_MQTT_PACKET_INC_MQTT_PACKET_H_
#define IOT_HUB_DEVICE_C_SDK_COMMON_MQTT_PACKET_INC_MQTT_PACKET_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <string.h>
/**
* @brief header 1 byte + remaining length 1~4 byte(s).
*
*/
#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
#define MAX_MQTT_FIXED_HEADER_LEN (1 + MAX_NO_OF_REMAINING_LENGTH_BYTES)
#define MIN_MQTT_FIXED_HEADER_LEN (1 + 1)
/**
* @brief Check if short buffer.
*
*/
#define SHORT_BUFFER_CHECK(buf_len, len) \
if (buf_len < len) { \
return MQTT_ERR_SHORT_BUFFER; \
}
/**
* @brief Error code for mqtt packet。
*
*/
typedef enum {
MQTT_RET_PACKET_OK = 0,
MQTT_ERR_INVALID_PACKET_TYPE = -1,
MQTT_ERR_SHORT_BUFFER = -2,
MQTT_ERR_VERSION_UNSUPPORTED = -3,
MQTT_ERR_SUB_COUNT_EXCEED = -4,
} MQTTPacketErrCode;
/**
* @brief MQTT packet type.
*
*/
typedef enum {
CONNECT = 1,
CONNACK,
PUBLISH,
PUBACK,
PUBREC,
PUBREL,
PUBCOMP,
SUBSCRIBE,
SUBACK,
UNSUBSCRIBE,
UNSUBACK,
PINGREQ,
PINGRESP,
DISCONNECT
} MQTTPacketType;
/**
* @brief Defines the MQTT "Last Will and Testament" (LWT) settings for the connect packet.
*
*/
typedef struct {
const char* topic_name; /**< The LWT topic to which the LWT message will be published */
const char* message; /**< The LWT payload */
uint8_t retained; /**< The retained flag for the LWT message */
uint8_t qos; /**< The quality of service setting for the LWT message */
} MQTTPacketWillOptions;
/**
* @brief MQTT packet connect option.
*
*/
typedef struct {
uint8_t mqtt_version; /**< Version of MQTT to be used. 3 = 3.1 4 = 3.1.1 */
const char* client_id;
uint16_t keep_alive_interval;
uint8_t clean_session;
uint8_t will_flag;
MQTTPacketWillOptions will;
char* username;
char* password;
} MQTTPacketConnectOption;
/**
* @brief Bitfields for the MQTT header byte.
*
*/
typedef union {
uint8_t byte; /**< the whole byte */
struct {
uint8_t retain : 1; /**< retained flag bit */
uint8_t qos : 2; /**< QoS value, 0, 1 or 2 */
uint8_t dup : 1; /**< DUP flag bit */
uint8_t type : 4; /**< message type nibble */
} bits;
} MQTTHeader;
/**
* @brief Connect flags byte.
*
*/
typedef union {
uint8_t all; /**< all connect flags */
struct {
uint8_t : 1; /**< unused */
uint8_t clean_session : 1; /**< cleansession flag */
uint8_t will : 1; /**< will flag */
uint8_t will_qos : 2; /**< will QoS value */
uint8_t will_retain : 1; /**< will retain setting */
uint8_t password : 1; /**< 3.1 password */
uint8_t username : 1; /**< 3.1 user name */
} bits;
} MQTTConnectFlags;
/**
* @brief connack flags byte.
*
*/
typedef union {
unsigned char all; /**< all connack flags */
struct {
unsigned int sessionpresent : 1; /**< session present flag */
unsigned int reserved : 7; /**< unused */
} bits;
} MQTTConnackFlags;
/**
* @brief Flags for publish.
*
*/
typedef struct {
uint8_t dup;
uint8_t qos;
uint8_t retain;
} MQTTPublishFlags;
/**
* @brief Connect return code.
*
*/
typedef enum {
CONNACK_CONNECTION_ACCEPTED = 0, /**< connection accepted */
CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR = 1, /**< connection refused: unaccepted protocol version */
CONNACK_IDENTIFIER_REJECTED_ERROR = 2, /**< connection refused: identifier rejected */
CONNACK_SERVER_UNAVAILABLE_ERROR = 3, /**< connection refused: server unavailable */
CONNACK_BAD_USERDATA_ERROR = 4, /**< connection refused: bad user name or password */
CONNACK_NOT_AUTHORIZED_ERROR = 5 /**< connection refused: not authorized */
} MQTTConnackReturnCodes;
/**
* @brief Serialize the connect options into the buffer. See 3.1.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[in] options the options to be used to build the connect packet
* @return serialized length, or error if <= 0
*/
int mqtt_connect_packet_serialize(uint8_t* buf, int buf_len, const MQTTPacketConnectOption* options);
/**
* @brief Serialize a disconnect packet into the supplied buffer, ready for writing to a socket. See 3.14.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer, to avoid overruns
* @return serialized length, or error if <= 0
*/
int mqtt_disconnect_packet_serialize(uint8_t* buf, int buf_len);
/**
* @brief Serialize a disconnect packet into the supplied buffer, ready for writing to a socket. See 3.12.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer, to avoid overruns
* @return serialized length, or error if <= 0
*/
int mqtt_pingreq_packet_serialize(uint8_t* buf, int buf_len);
/**
* @brief Serialize the supplied publish data into the supplied buffer, ready for sending. See 3.3.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[in] flags the MQTT dup, qos, retained flag
* @param[in] packet_id integer - the MQTT packet identifier
* @param[in] topic_name char * - the MQTT topic in the publish
* @param[in] payload byte buffer - the MQTT publish payload
* @param[in] payload_len integer - the length of the MQTT payload
* @return serialized length, or error if <= 0
*/
int mqtt_publish_packet_serialize(uint8_t* buf, int buf_len, const MQTTPublishFlags* flags, uint16_t packet_id,
const char* topic_name, const uint8_t* payload, int payload_len);
/**
* @brief Serialize a puback packet into the supplied buffer. See 3.4.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[in] packet_id integer - the MQTT packet identifier
* @return serialized length, or error if <= 0
*/
int mqtt_puback_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id);
/**
* @brief Serialize the supplied subscribe data into the supplied buffer, ready for sending. See 3.8.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied bufferr
* @param[in] packet_id integer - the MQTT packet identifier
* @param[in] count - number of members in the topicFilters and reqQos arrays
* @param[in] topic_filters - array of topic filter names
* @param[in] requested_qos - array of requested QoS
* @return serialized length, or error if <= 0
*/
int mqtt_subscribe_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id, uint32_t count,
char* topic_filters[], const int requested_qos[]);
/**
* @brief Serialize the supplied unsubscribe data into the supplied buffer, ready for sending. See 3.10.
*
* @param[out] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[in] packet_id integer - the MQTT packet identifier
* @param[in] count - number of members in the topicFilters array
* @param[in] topic_filters - array of topic filter names
* @return serialized length, or error if <= 0
*/
int mqtt_unsubscribe_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id, int count, char* topic_filters[]);
/**
* @brief Deserialize the supplied (wire) buffer into connack data. See 3.2.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[out] session_present the session present flag returned (only for MQTT 3.1.1)
* @param[out] connack_rc returned integer value of the connack return code
* @return @see MQTTPacketErrCode
*/
int mqtt_connack_packet_deserialize(uint8_t* buf, int buf_len, uint8_t* session_present, uint8_t* connack_rc);
/**
* @brief Deserialize the supplied (wire) buffer into pingresp data. See 3.13.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @return @see MQTTPacketErrCode
*/
int mqtt_pingresp_packet_deserialize(uint8_t* buf, int buf_len);
/**
* @brief Deserialize the supplied (wire) buffer into publish data. See 3.3.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[out] flags the MQTT dup, qos, retained flag
* @param[out] packet_id returned integer - the MQTT packet identifier
* @param[out] topic_name returned string - the MQTT topic in the publish
* @param[out] topic_len returned integer - the length of the MQTT topic
* @param[out] payload returned byte buffer - the MQTT publish payload
* @param[out] payload_len returned integer - the length of the MQTT payload
* @return @see MQTTPacketErrCode
*/
int mqtt_publish_packet_deserialize(uint8_t* buf, int buf_len, MQTTPublishFlags* flags, uint16_t* packet_id,
char** topic_name, int* topic_len, uint8_t** payload, int* payload_len);
/**
* @brief Deserialize the supplied (wire) buffer into an ack. See 3.4.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[out] packet_id returned integer - the MQTT packet identifier
* @return @see MQTTPacketErrCode
*/
int mqtt_puback_packet_deserialize(uint8_t* buf, int buf_len, uint16_t* packet_id);
/**
* @brief Deserialize the supplied (wire) buffer into suback data. See 3.9.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[in] maxcount - the maximum number of members allowed in the grantedQoSs array
* @param[out] count returned integer - number of members in the grantedQoSs array
* @param[out] packet_id returned integer - the MQTT packet identifier
* @param[out] granted_qos returned array of integers - the granted qualities of service
* @return @see MQTTPacketErrCode
*/
int mqtt_suback_packet_deserialize(uint8_t* buf, int buf_len, int maxcount, int* count, uint16_t* packet_id,
int granted_qos[]);
/**
* @brief Deserialize the supplied (wire) buffer into unsuback data. See 3.11.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[out] packet_id returned integer - the MQTT packet identifier
* @return @see MQTTPacketErrCode
*/
int mqtt_unsuback_packet_deserialize(uint8_t* buf, int buf_len, uint16_t* packet_id);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_MQTT_PACKET_INC_MQTT_PACKET_H_

View File

@@ -0,0 +1,364 @@
/**
* @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_packet_deserialize.c
* @brief implements mqtt packet deserialize. Reference paho.mqtt.embedded-c &
* http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.pdf
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-21
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-21 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "mqtt_packet.h"
/**
* @brief Reads one character from the input buffer. See 1.5.1.
*
* @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
* @return the character read
*/
static inline char _read_char(uint8_t** pptr)
{
char c = **pptr;
(*pptr)++;
return c;
}
/**
* @brief Calculates an integer from two bytes read from the input buffer. See 1.5.2.
*
* @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
* @return the integer value calculated
*/
static inline uint16_t _read_int(uint8_t** pptr)
{
uint8_t* ptr = *pptr;
int len = 256 * (*ptr) + (*(ptr + 1));
*pptr += 2;
return (uint16_t)len;
}
/**
* @brief Read a UTF-8 encoded string pointer from the input buffer. See 1.5.3.
*
* @param[out] pstring the pointer of the C string to read.
* @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
* @return the length of string
*/
static inline int _read_string(char** pstring, uint8_t** pptr)
{
int str_len = _read_int(pptr);
if (str_len > 0) {
*pstring = (char*)*pptr;
*pptr += str_len;
}
return str_len;
}
/**
* @brief Check the mqtt type and get publish flags. See 2.2.
*
* @param[in] packet_type type of mqtt control packet, See 2.2.1
* @param[in] pflags pointer to the flags of mqtt control packet fixed header. See 2.2.2
* @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
* @return @see MQTTPacketErrCode
*/
static int _mqtt_packet_type_check(MQTTPacketType packet_type, MQTTPublishFlags* pflags, uint8_t** pptr)
{
MQTTHeader header = {0};
header.byte = _read_char(pptr);
switch (header.bits.type) {
case PUBLISH:
pflags->dup = header.bits.dup;
pflags->qos = header.bits.qos;
pflags->retain = header.bits.retain;
break;
case SUBSCRIBE:
case UNSUBSCRIBE:
if ((header.byte & 0xf0) != 0x10) {
return MQTT_ERR_INVALID_PACKET_TYPE;
}
break;
default:
break;
}
return packet_type != header.bits.type ? MQTT_ERR_INVALID_PACKET_TYPE : MQTT_RET_PACKET_OK;
}
/**
* @brief Deserialize remaining length. See 2.2.3.
*
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[in] plen the pointer to the remaining length
* @param[in,out] pptr pointer to the input buffer - incremented by the number of bytes used & returned
* @return @see MQTTPacketErrCode
*/
static int _mqtt_remaining_length_deserialize(int buf_len, int* plen, uint8_t** pptr)
{
uint8_t* buf = *pptr;
uint8_t c = 0;
uint32_t multiplier = 1;
uint32_t count = 0;
uint32_t len = 0;
do {
if (++count > MAX_NO_OF_REMAINING_LENGTH_BYTES) {
return MQTT_ERR_INVALID_PACKET_TYPE;
}
c = *buf++;
len += (c & 127) * multiplier;
multiplier *= 128;
SHORT_BUFFER_CHECK(buf_len, count + 1 + len);
} while (c & 128);
if (plen) {
*plen = len;
}
*pptr += count;
return MQTT_RET_PACKET_OK;
}
/**
* @brief Deserialize the supplied (wire) buffer into connack data. See 3.2.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[out] session_present the session present flag returned (only for MQTT 3.1.1)
* @param[out] connack_rc returned integer value of the connack return code
* @return @see MQTTPacketErrCode
*/
int mqtt_connack_packet_deserialize(uint8_t* buf, int buf_len, uint8_t* session_present, uint8_t* connack_rc)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
uint8_t* ptr = buf;
int rc = 0;
MQTTConnackFlags flags = {0};
rc = _mqtt_packet_type_check(CONNACK, NULL, &ptr);
if (rc) {
goto exit;
}
rc = _mqtt_remaining_length_deserialize(buf_len, NULL, &ptr);
if (rc < 0) {
goto exit;
}
flags.all = _read_char(&ptr);
*session_present = flags.bits.sessionpresent;
*connack_rc = _read_char(&ptr);
exit:
return rc;
}
/**
* @brief Deserialize the supplied (wire) buffer into pingresp data. See 3.13.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @return @see MQTTPacketErrCode
*/
int mqtt_pingresp_packet_deserialize(uint8_t* buf, int buf_len)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
uint8_t* ptr = buf;
int rc = 0;
rc = _mqtt_packet_type_check(PINGRESP, NULL, &ptr);
if (rc) {
goto exit;
}
rc = _mqtt_remaining_length_deserialize(buf_len, NULL, &ptr);
if (rc < 0) {
goto exit;
}
exit:
return rc;
}
/**
* @brief Deserialize the supplied (wire) buffer into publish data. See 3.3.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[out] flags the MQTT dup, qos, retained flag
* @param[out] packet_id returned integer - the MQTT packet identifier
* @param[out] topic_name returned string - the MQTT topic in the publish
* @param[out] topic_len returned integer - the length of the MQTT topic
* @param[out] payload returned byte buffer - the MQTT publish payload
* @param[out] payload_len returned integer - the length of the MQTT payload
* @return @see MQTTPacketErrCode
*/
int mqtt_publish_packet_deserialize(uint8_t* buf, int buf_len, MQTTPublishFlags* flags, uint16_t* packet_id,
char** topic_name, int* topic_len, uint8_t** payload, int* payload_len)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
uint8_t* ptr = buf;
uint8_t* ptr_remain = NULL;
int rc = 0;
int len = 0;
rc = _mqtt_packet_type_check(PUBLISH, flags, &ptr);
if (rc) {
goto exit;
}
rc = _mqtt_remaining_length_deserialize(buf_len, &len, &ptr);
if (rc) {
goto exit;
}
ptr_remain = ptr;
rc = _read_string(topic_name, &ptr);
if (rc <= 0) {
rc = MQTT_ERR_INVALID_PACKET_TYPE;
goto exit;
}
*topic_len = rc;
if (flags->qos > 0) {
*packet_id = _read_int(&ptr);
}
*payload_len = len - (ptr - ptr_remain);
*payload = ptr;
rc = MQTT_RET_PACKET_OK;
exit:
return rc;
}
/**
* @brief Deserialize the supplied (wire) buffer into an ack. See 3.4.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[out] packet_id returned integer - the MQTT packet identifier
* @return @see MQTTPacketErrCode
*/
int mqtt_puback_packet_deserialize(uint8_t* buf, int buf_len, uint16_t* packet_id)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
uint8_t* ptr = buf;
int rc = 0;
rc = _mqtt_packet_type_check(PUBACK, 0, &ptr);
if (rc) {
goto exit;
}
rc = _mqtt_remaining_length_deserialize(buf_len, NULL, &ptr);
if (rc) {
goto exit;
}
*packet_id = _read_int(&ptr);
exit:
return rc;
}
/**
* @brief Deserialize the supplied (wire) buffer into suback data. See 3.9.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[in] maxcount - the maximum number of members allowed in the grantedQoSs array
* @param[out] count returned integer - number of members in the grantedQoSs array
* @param[out] packet_id returned integer - the MQTT packet identifier
* @param[out] granted_qos returned array of integers - the granted qualities of service
* @return @see MQTTPacketErrCode
*/
int mqtt_suback_packet_deserialize(uint8_t* buf, int buf_len, int maxcount, int* count, uint16_t* packet_id,
int granted_qos[])
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
uint8_t* ptr = buf;
int rc = 0;
int len = 0;
rc = _mqtt_packet_type_check(SUBACK, NULL, &ptr);
if (rc) {
goto exit;
}
rc = _mqtt_remaining_length_deserialize(buf_len, &len, &ptr);
if (rc) {
goto exit;
}
*packet_id = _read_int(&ptr);
len -= 2;
*count = 0;
while (len--) {
if (*count > maxcount) {
rc = MQTT_ERR_SUB_COUNT_EXCEED;
goto exit;
}
granted_qos[(*count)++] = _read_char(&ptr);
}
exit:
return rc;
}
/**
* @brief Deserialize the supplied (wire) buffer into unsuback data. See 3.11.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[out] packet_id returned integer - the MQTT packet identifier
* @return @see MQTTPacketErrCode
*/
int mqtt_unsuback_packet_deserialize(uint8_t* buf, int buf_len, uint16_t* packet_id)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
uint8_t* ptr = buf;
int rc = 0;
rc = _mqtt_packet_type_check(UNSUBACK, NULL, &ptr);
if (rc) {
goto exit;
}
rc = _mqtt_remaining_length_deserialize(buf_len, NULL, &ptr);
if (rc) {
goto exit;
}
*packet_id = _read_int(&ptr);
exit:
return rc;
}

View File

@@ -0,0 +1,458 @@
/**
* @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_packet_serialize.c
* @brief implements mqtt packet serialize. Reference paho.mqtt.embedded-c &
* http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.pdf
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-21
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-21 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "mqtt_packet.h"
/**
* @brief Writes one character to an output buffer. See 1.5.1.
*
* @param[in] c the character to write
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
*/
static inline void _write_char(char c, uint8_t** pptr)
{
**pptr = c;
(*pptr)++;
}
/**
* @brief Writes an integer as 2 bytes to an output buffer. See 1.5.2.
*
* @param[in] value the integer to write.
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned.
*/
static inline void _write_int(int value, uint8_t** pptr)
{
**pptr = (uint8_t)(value / 256);
(*pptr)++;
**pptr = (uint8_t)(value % 256);
(*pptr)++;
}
/**
* @brief Writes a UTF-8 encoded string to an output buffer. Converts C string to
* length-delimited. See 1.5.3.
*
* @param[in] string the C string to write
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
*/
static inline void _write_string(const char* string, uint8_t** pptr)
{
int len = strlen(string);
_write_int(len, pptr);
memcpy(*pptr, string, len);
*pptr += len;
}
/**
* @brief Serialize mqtt fixed header except remaining length. See 2.2.
*
* @param[in] packet_type type of mqtt control packet, See 2.2.1 MQTT Control Packet type
* @param[in] pflags pointer to the flags of mqtt control packet fixed header. See 2.2.2 Flags
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
*/
static void _mqtt_header_serialize(MQTTPacketType packet_type, const MQTTPublishFlags* pflags, uint8_t** pptr)
{
MQTTHeader header = {0};
switch (packet_type) {
case PUBLISH:
header.bits.dup = pflags->dup;
header.bits.qos = pflags->qos;
header.bits.retain = pflags->retain;
break;
case SUBSCRIBE:
case UNSUBSCRIBE:
header.byte = 0x02;
break;
default:
break;
}
header.bits.type |= packet_type;
_write_char(header.byte, pptr); /* write header */
}
/**
* @brief Serialize remaining length. See 2.2.3.
*
* @param[in] length the length to be encoded to remaining length
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
*/
static void _mqtt_remaining_length_serialize(int length, uint8_t** pptr)
{
char d;
do {
d = length % 128;
length /= 128;
// if there are more digits to encode, set the top bit of this digit
if (length > 0) {
d |= 0x80;
}
_write_char(d, pptr);
} while (length > 0);
}
/**
* @brief Serialize the remaining length of the MQTT connect packet that would be produced using the
* supplied connect options. See 3.1.1.
*
* @param[in] options the options to be used to build the connect packet
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
*/
static int _mqtt_connect_remaining_length_serialize(const MQTTPacketConnectOption* options, int buf_len, uint8_t** pptr)
{
if (options->mqtt_version != 3 && options->mqtt_version != 4) {
return MQTT_ERR_VERSION_UNSUPPORTED;
}
int len = 0;
len = options->mqtt_version == 3 ? 12 : 10;
len += strlen(options->client_id) + 2;
if (options->will_flag) {
len += strlen(options->will.topic_name) + 2 + strlen(options->will.message) + 2;
}
if (options->username) {
len += strlen(options->username) + 2;
}
if (options->password) {
len += strlen(options->password) + 2;
}
SHORT_BUFFER_CHECK(buf_len, len);
_mqtt_remaining_length_serialize(len, pptr);
return MQTT_RET_PACKET_OK;
}
/**
* @brief Serialize the mqtt connect packet variable header. See 3.1.2.
*
* @param[in] options the options to be used to build the connect packet
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
*/
static int _mqtt_connect_variable_header_serialize(const MQTTPacketConnectOption* options, uint8_t** pptr)
{
if (options->password && !options->username) {
return MQTT_ERR_INVALID_PACKET_TYPE;
}
// 3.1.2.1 protocol name & 3.1.2.2 protocol level
switch (options->mqtt_version) {
case 3:
_write_string("MQIsdp", pptr);
_write_char((char)3, pptr);
break;
case 4:
_write_string("MQTT", pptr);
_write_char((char)4, pptr);
break;
}
// 3.1.2.3 Connect Flags
MQTTConnectFlags flags = {
.bits.clean_session = options->clean_session,
.bits.will = options->will_flag,
.bits.will_qos = options->will_flag ? options->will.qos : 0,
.bits.will_retain = options->will_flag ? options->will.retained : 0,
.bits.username = options->username ? 1 : 0,
.bits.password = options->password ? 1 : 0,
};
_write_char(flags.all, pptr);
return MQTT_RET_PACKET_OK;
}
/**
* @brief Serialize the remaining length of the MQTT publish packet that would be produced using the supplied
* parameters. See 3.3.2 & 3.3.3.
*
* @param[in] qos the MQTT QoS of the publish (packet id is omitted for QoS 0)
* @param[in] topic_name the topic name to be used in the publish
* @param[in] payload_len the length of the payload to be sent
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
*/
static int _mqtt_publish_remaining_length_serialize(uint8_t qos, const char* topic_name, int payload_len, int buf_len,
uint8_t** pptr)
{
int len = 0;
len += 2 + strlen(topic_name) + payload_len;
if (qos > 0) {
len += 2; /* packet id */
}
SHORT_BUFFER_CHECK(buf_len, len + MAX_MQTT_FIXED_HEADER_LEN);
_mqtt_remaining_length_serialize(len, pptr);
return MQTT_RET_PACKET_OK;
}
/**
* @brief Serialize the length of the MQTT subscribe/unsubscribe packet that would be produced using the
* supplied parameters. See 3.8.1 & 3.10.1.
*
* @param[in] count the number of topic filter strings in topicFilters
* @param[in] topic_filters the array of topic filter strings to be used in the publish
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[out] pptr pointer to the output buffer - incremented by the number of bytes used & returned
*/
static int _mqtt_sub_unsub_remaining_length_serialize(uint32_t count, char* topic_filters[], int buf_len,
uint8_t** pptr, uint8_t is_sub)
{
int i = 0;
int len = 2; /* packet id */
for (i = 0; i < count; ++i) {
len += 2 + strlen(topic_filters[i]) + is_sub; /* length + topic */
}
SHORT_BUFFER_CHECK(buf_len, len + MAX_MQTT_FIXED_HEADER_LEN);
_mqtt_remaining_length_serialize(len, pptr);
return MQTT_RET_PACKET_OK;
}
/**
* @brief Serialize the connect options into the buffer. See 3.1.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[in] options the options to be used to build the connect packet
* @return serialized length, or error if <= 0
*/
int mqtt_connect_packet_serialize(uint8_t* buf, int buf_len, const MQTTPacketConnectOption* options)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
int rc;
uint8_t* ptr = buf;
// 3.1.1 Fixed header
_mqtt_header_serialize(CONNECT, NULL, &ptr);
rc = _mqtt_connect_remaining_length_serialize(options, buf_len, &ptr);
if (rc) {
return rc;
}
// 3.1.2 Variable header
rc = _mqtt_connect_variable_header_serialize(options, &ptr);
if (rc) {
return rc;
}
// 3.1.3 Payload
_write_int(options->keep_alive_interval, &ptr);
_write_string(options->client_id, &ptr);
if (options->will_flag) {
_write_string(options->will.topic_name, &ptr);
_write_string(options->will.message, &ptr);
}
if (options->username) {
_write_string(options->username, &ptr);
}
if (options->password) {
_write_string(options->password, &ptr);
}
return ptr - buf;
}
/**
* @brief Serialize a disconnect packet into the supplied buffer, ready for writing to a socket. See 3.14.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer, to avoid overruns
* @return serialized length, or error if <= 0
*/
int mqtt_disconnect_packet_serialize(uint8_t* buf, int buf_len)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
uint8_t* ptr = buf;
_mqtt_header_serialize(DISCONNECT, 0, &ptr);
_mqtt_remaining_length_serialize(0, &ptr);
return ptr - buf;
}
/**
* @brief Serialize a disconnect packet into the supplied buffer, ready for writing to a socket. See 3.12.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer, to avoid overruns
* @return serialized length, or error if <= 0
*/
int mqtt_pingreq_packet_serialize(uint8_t* buf, int buf_len)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
uint8_t* ptr = buf;
_mqtt_header_serialize(PINGREQ, 0, &ptr);
_mqtt_remaining_length_serialize(0, &ptr);
return ptr - buf;
}
/**
* @brief Serialize the supplied publish data into the supplied buffer, ready for sending. See 3.3.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[in] flags the MQTT dup, qos, retained flag
* @param[in] packet_id integer - the MQTT packet identifier
* @param[in] topic_name char * - the MQTT topic in the publish
* @param[in] payload byte buffer - the MQTT publish payload
* @param[in] payload_len integer - the length of the MQTT payload
* @return serialized length, or error if <= 0
*/
int mqtt_publish_packet_serialize(uint8_t* buf, int buf_len, const MQTTPublishFlags* flags, uint16_t packet_id,
const char* topic_name, const uint8_t* payload, int payload_len)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
int rc;
uint8_t* ptr = buf;
_mqtt_header_serialize(PUBLISH, flags, &ptr);
rc = _mqtt_publish_remaining_length_serialize(flags->qos, topic_name, payload_len, buf_len, &ptr);
if (rc) {
return rc;
}
_write_string(topic_name, &ptr);
if (flags->qos > 0) {
_write_int(packet_id, &ptr);
}
memcpy(ptr, payload, payload_len);
ptr += payload_len;
return ptr - buf;
}
/**
* @brief Serialize a puback packet into the supplied buffer. See 3.4.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied buffer
* @param[in] packet_id integer - the MQTT packet identifier
* @return serialized length, or error if <= 0
*/
int mqtt_puback_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id)
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN + 2);
uint8_t* ptr = buf;
_mqtt_header_serialize(PUBACK, 0, &ptr);
_mqtt_remaining_length_serialize(2, &ptr);
_write_int(packet_id, &ptr);
return ptr - buf;
}
/**
* @brief Serialize the supplied subscribe data into the supplied buffer, ready for sending. See 3.8.
*
* @param[out] buf the buffer into which the packet will be serialized
* @param[in] buf_len the length in bytes of the supplied bufferr
* @param[in] packet_id integer - the MQTT packet identifier
* @param[in] count - number of members in the topicFilters and reqQos arrays
* @param[in] topic_filters - array of topic filter names
* @param[in] requested_qos - array of requested QoS
* @return serialized length, or error if <= 0
*/
int mqtt_subscribe_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id, uint32_t count,
char* topic_filters[], const int requested_qos[])
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
int rc, i = 0;
uint8_t* ptr = buf;
_mqtt_header_serialize(SUBSCRIBE, 0, &ptr);
rc = _mqtt_sub_unsub_remaining_length_serialize(count, topic_filters, buf_len, &ptr, 1);
if (rc) {
return rc;
}
_write_int(packet_id, &ptr);
for (i = 0; i < count; ++i) {
_write_string(topic_filters[i], &ptr);
_write_char(requested_qos[i], &ptr);
}
return ptr - buf;
}
/**
* @brief Serialize the supplied unsubscribe data into the supplied buffer, ready for sending. See 3.10.
*
* @param[out] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[in] packet_id integer - the MQTT packet identifier
* @param[in] count - number of members in the topicFilters array
* @param[in] topic_filters - array of topic filter names
* @return serialized length, or error if <= 0
*/
int mqtt_unsubscribe_packet_serialize(uint8_t* buf, int buf_len, uint16_t packet_id, int count, char* topic_filters[])
{
SHORT_BUFFER_CHECK(buf_len, MIN_MQTT_FIXED_HEADER_LEN);
int rc, i = 0;
uint8_t* ptr = buf;
_mqtt_header_serialize(UNSUBSCRIBE, 0, &ptr);
rc = _mqtt_sub_unsub_remaining_length_serialize(count, topic_filters, buf_len, &ptr, 0);
if (rc) {
return rc;
}
_write_int(packet_id, &ptr);
for (i = 0; i < count; ++i) {
_write_string(topic_filters[i], &ptr);
}
return ptr - buf;
}

View File

@@ -0,0 +1,8 @@
Language: Cpp
BasedOnStyle: Google
ColumnLimit: 120
DerivePointerAlignment: true
PointerAlignment: Left
SortIncludes: true
IncludeBlocks: Preserve
IndentPPDirectives: AfterHash

View File

@@ -0,0 +1,307 @@
/**
* @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_packet.cc
* @brief unit test for mqtt packet
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-06-01
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-06-01 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include <iostream>
#include <string>
#include "gtest/gtest.h"
#include "mqtt_packet.h"
namespace mqtt_packet_unittest {
/**
* @brief Test connect packet.
*
*/
TEST(MQTTPacketTest, CONNECT) {
uint8_t test_packet[] = {
0x10, 0x77, 0x00, 0x04, 0x4d, 0x51, 0x54, 0x54, 0x04, 0xc2, 0x00, 0xf0, 0x00, 0x0e, 0x4a, 0x4e, 0x4c, 0x52,
0x57, 0x4f, 0x33, 0x54, 0x35, 0x39, 0x74, 0x65, 0x73, 0x74, 0x00, 0x28, 0x4a, 0x4e, 0x4c, 0x52, 0x57, 0x4f,
0x33, 0x54, 0x35, 0x39, 0x74, 0x65, 0x73, 0x74, 0x3b, 0x32, 0x31, 0x30, 0x31, 0x30, 0x34, 0x30, 0x36, 0x3b,
0x49, 0x41, 0x5a, 0x5a, 0x4a, 0x3b, 0x32, 0x31, 0x34, 0x37, 0x34, 0x38, 0x33, 0x36, 0x34, 0x37, 0x00, 0x31,
0x66, 0x62, 0x63, 0x39, 0x65, 0x34, 0x36, 0x33, 0x61, 0x32, 0x30, 0x64, 0x66, 0x30, 0x34, 0x35, 0x65, 0x62,
0x66, 0x34, 0x38, 0x34, 0x35, 0x61, 0x35, 0x39, 0x62, 0x32, 0x61, 0x38, 0x35, 0x66, 0x65, 0x62, 0x34, 0x31,
0x34, 0x31, 0x30, 0x30, 0x3b, 0x68, 0x6d, 0x61, 0x63, 0x73, 0x68, 0x61, 0x31,
};
uint8_t packet_buf[2048];
std::string user_name = "JNLRWO3T59test;21010406;IAZZJ;2147483647";
std::string password = "fbc9e463a20df045ebf4845a59b2a85feb414100;hmacsha1";
MQTTPacketConnectOption options = {
.mqtt_version = 4,
.client_id = "JNLRWO3T59test",
.keep_alive_interval = 240,
.clean_session = 1,
.will_flag = 0,
.will = {0},
.username = reinterpret_cast<char *>(const_cast<char *>(user_name.c_str())),
.password = reinterpret_cast<char *>(const_cast<char *>(password.c_str())),
};
ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, sizeof(packet_buf), &options), sizeof(test_packet));
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, 1, &options), MQTT_ERR_SHORT_BUFFER);
options.username = NULL;
ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, sizeof(packet_buf), &options), MQTT_ERR_INVALID_PACKET_TYPE);
options.password = NULL;
ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, sizeof(packet_buf), &options),
sizeof(test_packet) - user_name.length() - password.length() - 4);
options.mqtt_version = 5;
ASSERT_EQ(mqtt_connect_packet_serialize(packet_buf, sizeof(packet_buf), &options), MQTT_ERR_VERSION_UNSUPPORTED);
}
/**
* @brief Test connack packet.
*
*/
TEST(MQTTPacketTest, CONNACK) {
uint8_t test_packet[] = {0x20, 0x02, 0x00, 0x00};
uint8_t session_present, connack_rc;
for (uint8_t i = CONNACK_CONNECTION_ACCEPTED; i <= CONNACK_NOT_AUTHORIZED_ERROR; i++) {
test_packet[3] = i;
ASSERT_EQ(mqtt_connack_packet_deserialize(test_packet, sizeof(test_packet), &session_present, &connack_rc),
MQTT_RET_PACKET_OK);
ASSERT_EQ(connack_rc, i);
}
ASSERT_EQ(mqtt_connack_packet_deserialize(test_packet, 2, &session_present, &connack_rc), MQTT_ERR_SHORT_BUFFER);
test_packet[0] = SUBACK;
ASSERT_EQ(mqtt_connack_packet_deserialize(test_packet, sizeof(test_packet), &session_present, &connack_rc),
MQTT_ERR_INVALID_PACKET_TYPE);
}
/**
* @brief Test publish packet.
*
*/
TEST(MQTTPacketTest, PUBLISH) {
uint8_t test_packet[] = {
0x32, 0x40, 0x00, 0x14, 0x4a, 0x4e, 0x4c, 0x52, 0x57, 0x4f, 0x33, 0x54, 0x35, 0x39, 0x2f, 0x74, 0x65,
0x73, 0x74, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x70, 0xaf, 0x7b, 0x22, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x22, 0x3a, 0x20, 0x22, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x22,
0x2c, 0x20, 0x22, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x30, 0x22, 0x7d,
};
uint8_t packet_buf[128];
/**
* @brief QoS1
*
*/
char serialize_topic_name[] = "JNLRWO3T59/test/data";
int serialize_topic_len = strlen(serialize_topic_name);
uint16_t serialize_packet_id = 28847;
uint8_t serialize_payload[] = "{\"action\": \"publish_test\", \"count\": \"0\"}";
int serialize_payload_len = strlen((const char *)serialize_payload);
MQTTPublishFlags serialize_flags = {0, 1, 0};
ASSERT_EQ(mqtt_publish_packet_serialize(packet_buf, sizeof(packet_buf), &serialize_flags, serialize_packet_id,
serialize_topic_name, serialize_payload, serialize_payload_len),
sizeof(test_packet));
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
char *deserialize_topic_name;
int deserialize_topic_len;
uint16_t deserialize_packet_id = 0;
uint8_t *deserialize_payload;
int deserialize_payload_len;
MQTTPublishFlags deserialize_flags;
ASSERT_EQ(mqtt_publish_packet_deserialize(test_packet, sizeof(test_packet), &deserialize_flags,
&deserialize_packet_id, &deserialize_topic_name, &deserialize_topic_len,
&deserialize_payload, &deserialize_payload_len),
0);
ASSERT_EQ(deserialize_packet_id, serialize_packet_id);
ASSERT_EQ(deserialize_topic_len, serialize_topic_len);
ASSERT_EQ(deserialize_payload_len, serialize_payload_len);
ASSERT_EQ(memcmp(&deserialize_flags, &serialize_flags, sizeof(deserialize_flags)), 0);
ASSERT_EQ(memcmp(deserialize_topic_name, serialize_topic_name, deserialize_topic_len), 0);
ASSERT_EQ(memcmp(deserialize_payload, serialize_payload, deserialize_payload_len), 0);
/**
* @brief QoS0
*
*/
serialize_flags.qos = 0;
deserialize_packet_id = 0;
ASSERT_EQ(
mqtt_publish_packet_serialize(packet_buf, sizeof(packet_buf), &serialize_flags, serialize_packet_id,
serialize_topic_name, serialize_payload, strlen((const char *)serialize_payload)),
sizeof(test_packet) - 2);
ASSERT_EQ(mqtt_publish_packet_deserialize(packet_buf, sizeof(packet_buf), &deserialize_flags, &deserialize_packet_id,
&deserialize_topic_name, &deserialize_topic_len, &deserialize_payload,
&deserialize_payload_len),
MQTT_RET_PACKET_OK);
ASSERT_NE(deserialize_packet_id, serialize_packet_id);
/**
* @brief bad packet
*
*/
packet_buf[0] = PUBACK;
ASSERT_EQ(mqtt_publish_packet_deserialize(packet_buf, sizeof(packet_buf), &deserialize_flags, &deserialize_packet_id,
&deserialize_topic_name, &deserialize_topic_len, &deserialize_payload,
&deserialize_payload_len),
MQTT_ERR_INVALID_PACKET_TYPE);
}
/**
* @brief Test puback packet.
*
*/
TEST(MQTTPacketTest, PUBACK) {
uint8_t test_packet[] = {0x40, 0x02, 0x70, 0xaf};
uint8_t packet_buf[128];
uint16_t serialize_packet_id = 28847;
ASSERT_EQ(mqtt_puback_packet_serialize(packet_buf, sizeof(packet_buf), serialize_packet_id), 4);
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
uint16_t deserialize_packet_id;
ASSERT_EQ(mqtt_puback_packet_deserialize(test_packet, sizeof(test_packet), &deserialize_packet_id),
MQTT_RET_PACKET_OK);
ASSERT_EQ(deserialize_packet_id, serialize_packet_id);
}
/**
* @brief Test subscribe packet.
*
*/
TEST(MQTTPacketTest, SUBSCRIBE) {
uint8_t test_packet[] = {
0x82, 0x19, 0x70, 0xae, 0x00, 0x14, 0x4a, 0x4e, 0x4c, 0x52, 0x57, 0x4f, 0x33, 0x54,
0x35, 0x39, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x00,
};
uint8_t packet_buf[128];
std::string topic = "JNLRWO3T59/test/data";
uint16_t serialize_packet_id = 28846;
char *serialize_topic_name = reinterpret_cast<char *>(const_cast<char *>(topic.c_str()));
int serialize_qos = 0;
ASSERT_EQ(mqtt_subscribe_packet_serialize(packet_buf, sizeof(packet_buf), serialize_packet_id, 1,
&serialize_topic_name, &serialize_qos),
sizeof(test_packet));
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
}
/**
* @brief Test suback packet.
*
*/
TEST(MQTTPacketTest, SUBACK) {
uint8_t test_packet[] = {0x90, 0x03, 0x70, 0xae, 0x00};
int sub_count = 0;
uint16_t packet_id;
int granted_qos[1];
ASSERT_EQ(mqtt_suback_packet_deserialize(test_packet, sizeof(test_packet), 1, &sub_count, &packet_id, granted_qos),
MQTT_RET_PACKET_OK);
ASSERT_EQ(sub_count, 1);
ASSERT_EQ(packet_id, 28846);
ASSERT_EQ(granted_qos[0], 0);
}
/**
* @brief Test unsubscribe packet.
*
*/
TEST(MQTTPacketTest, UNSUBSCRIBE) {
uint8_t test_packet[] = {
0xa2, 0x18, 0x35, 0x74, 0x00, 0x14, 0x4a, 0x4e, 0x4c, 0x52, 0x57, 0x4f, 0x33,
0x54, 0x35, 0x39, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x64, 0x61, 0x74, 0x61,
};
uint8_t packet_buf[128];
uint16_t packet_id = 13684;
std::string topic = "JNLRWO3T59/test/data";
char *topic_name = reinterpret_cast<char *>(const_cast<char *>(topic.c_str()));
ASSERT_EQ(mqtt_unsubscribe_packet_serialize(packet_buf, sizeof(packet_buf), packet_id, 1, &topic_name),
sizeof(test_packet));
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
}
/**
* @brief Test connack packet.
*
*/
TEST(MQTTPacketTest, UNSUBACK) {
uint8_t test_packet[] = {0xb0, 0x02, 0x92, 0x6b};
uint16_t packet_id;
ASSERT_EQ(mqtt_unsuback_packet_deserialize(test_packet, sizeof(test_packet), &packet_id), MQTT_RET_PACKET_OK);
ASSERT_EQ(packet_id, 37483);
}
/**
* @brief Test pingreq packet.
*
*/
TEST(MQTTPacketTest, PINGREQ) {
uint8_t test_packet[] = {0xc0, 0x00};
uint8_t packet_buf[128];
ASSERT_EQ(mqtt_pingreq_packet_serialize(packet_buf, sizeof(packet_buf)), sizeof(test_packet));
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
}
/**
* @brief Test pingresp packet.
*
*/
TEST(MQTTPacketTest, PINGRESP) {
uint8_t test_packet[] = {0xd0, 0x00};
ASSERT_EQ(mqtt_pingresp_packet_deserialize(test_packet, sizeof(test_packet)), MQTT_RET_PACKET_OK);
}
/**
* @brief Test disconnect packet.
*
*/
TEST(MQTTPacketTest, DISCONNECT) {
uint8_t test_packet[] = {0xe0, 0x00};
uint8_t packet_buf[128];
ASSERT_EQ(mqtt_disconnect_packet_serialize(packet_buf, sizeof(packet_buf)), sizeof(test_packet));
ASSERT_EQ(memcmp(packet_buf, test_packet, sizeof(test_packet)), 0);
}
} // namespace mqtt_packet_unittest

View File

@@ -0,0 +1,10 @@
file(GLOB src ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(inc ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
set(src_common ${src_common} ${src} PARENT_SCOPE)
set(inc_common ${inc_common} ${inc} PARENT_SCOPE)
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()

View File

@@ -0,0 +1,104 @@
/**
* @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 utils_downloader.h
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-20
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-20 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_DOWNLOADER_H_
#define IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_DOWNLOADER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
/**
* @brief Downloader status.
*
*/
typedef enum {
UTILS_DOWNLOADER_STATUS_SUCCESS = 0,
UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED,
UTILS_DOWNLOADER_STATUS_DATA_DOWNLOAD_FAILED,
UTILS_DOWNLOADER_STATUS_NETWORK_FAILED
} UtilsDownloaderStatus;
/**
* @brief Downloader function.
*
*/
typedef struct {
// memory
void* (*downloader_malloc)(size_t len); /**< user malloc */
void (*downloader_free)(void* val); /**< user free */
// break point
int (*break_point_init)(void* usr_data); /**< init break point, read from flash or file */
void (*break_point_deinit)(void* usr_data); /**< deinit break point */
int (*break_point_set)(void* usr_data); /**< set break point structure */
int (*break_point_save)(void* usr_data); /**< save break point in flash or file */
int (*break_point_check)(void* usr_data); /**< check break point valid */
int (*break_point_restore)(void* usr_data); /**< restore break point */
// data download
int (*data_download_init)(void* usr_data); /**< init data download, such as http connect */
void (*data_download_deinit)(void* usr_data); /**< deinit data download, such as http disconnect */
int (*data_download_is_over)(void* usr_data); /**< check if download finish */
int (*data_download_recv)(void* usr_data); /**< recv data, such as http recv */
int (*data_download_save)(void* usr_data); /**< save data, such as write firmware to flash */
int (*data_download_finish)(void* usr_data, UtilsDownloaderStatus status); /**< process result */
} UtilsDownloaderFunction;
/**
* @brief Init downloader.
*
* @param[in] func download function should implement
* @param[in] usr_data user data using in function
* @return pointer to downloader
*/
void* utils_downloader_init(UtilsDownloaderFunction func, void* usr_data);
/**
* @brief Process download using function.
*
* @param[in,out] handle pointer to downloader
* @return -1 for fail, others see data_download_finish
*/
int utils_downloader_process(void* handle);
/**
* @brief Deinit downloader.
*
* @param[in,out] handle pointer to downloader
*/
void utils_downloader_deinit(void* handle);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_DOWNLOADER_H_

View File

@@ -0,0 +1,146 @@
/**
* @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 utils_json.h
* @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>2021-07-29 <td>1.1 <td>fancyxu <td>fix bug and add utils_json_value_data_get
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_JSON_H_
#define IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_JSON_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
/**
* @brief Json value type
*
*/
typedef enum {
UTILS_JSON_VALUE_TYPE_INT32 = 0,
UTILS_JSON_VALUE_TYPE_INT64,
UTILS_JSON_VALUE_TYPE_UINT32,
UTILS_JSON_VALUE_TYPE_UINT64,
UTILS_JSON_VALUE_TYPE_FLOAT,
UTILS_JSON_VALUE_TYPE_DOUBLE,
UTILS_JSON_VALUE_TYPE_BOOLEAN,
} UtilsJsonValueType;
/**
* @brief Json array result
*
*/
typedef enum {
UTILS_JSON_ARRAY_ITER_CONTINUE,
UTILS_JSON_ARRAY_ITER_STOP,
} UtilsJsonArrayIterResult;
/**
* @brief Json value
*
*/
typedef struct {
const char *value;
int value_len;
} UtilsJsonValue;
/**
* @brief Get value from json string. Not strict, just for iot scene, we suppose all the string is valid json.
*
* @param[in] key key in json, support nesting with '.'
* @param[in] key_len key len
* @param[in] src json string
* @param[in] src_len src length
* @param[out] value value
* @return 0 for success
*/
int utils_json_value_get(const char *key, int key_len, const char *src, int src_len, UtilsJsonValue *value);
/**
* @brief Get data of value with type.
*
* @param[in] value @see UtilsJsonValue
* @param[in] type value type, string can use value directly @see UtilsJsonValueType
* @param[out] data data pointer, user should match the type
* @return 0 for success
*/
int utils_json_value_data_get(UtilsJsonValue value, UtilsJsonValueType type, void *data);
/**
* @brief Return unsigned int value of key in json.
*
* @param[in] key key in json, support nesting with '.'
* @param[in] key_len key len
* @param[in] src json string
* @param[in] src_len src length
* @param[out] data data value
* @return 0 for success
*/
int utils_json_get_uint32(const char *key, int key_len, const char *src, int src_len, uint32_t *data);
/**
* @brief Return int value of key in json.
*
* @param[in] key key in json, support nesting with '.'
* @param[in] key_len key len
* @param[in] src json string
* @param[in] src_len src length
* @param[out] data data value
* @return 0 for success
*/
int utils_json_get_int32(const char *key, int key_len, const char *src, int src_len, int32_t *data);
/**
* @brief Remove '\\' in json string.
*
* @param[in,out] src string to transfer
* @param[in] src_len string len
* @return length after transfer
*/
int utils_json_strip_transfer(char *src, int src_len);
/**
* @brief Parse array object, assume array json is legal, src should be like "[12, 456]", this function will split array
* according to array_elem_type, obj_cb will be called for each elements.
*
* @param[in] src array string
* @param[in] src_len length of src
* @param[in] obj_cb callback to deal with array element
* @param[in] arg argument passed to the obj_cb
*/
void utils_json_array_parse(const char *src, int src_len, UtilsJsonArrayIterResult (*obj_cb)(const char *, int, void *),
void *arg);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_JSON_H_

View File

@@ -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 utils_list.h
* @brief header file for utils list
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-25
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-25 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LIST_H_
#define IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LIST_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <string.h>
/**
* @brief ListNode iterator direction.
*
*/
typedef enum {
LIST_HEAD,
LIST_TAIL,
} UtilsListDirection;
/**
* @brief ListNode process result of OnNodeProcessHandle.
*
*/
typedef enum {
LIST_TRAVERSE_CONTINUE,
LIST_TRAVERSE_BREAK,
} UtilsListResult;
/**
* @brief Utils list function.
*
*/
typedef struct {
void *(*list_malloc)(size_t len);
void (*list_free)(void *val);
void *(*list_lock_init)(void);
void (*list_lock)(void *lock);
void (*list_unlock)(void *lock);
void (*list_lock_deinit)(void *lock);
} UtilsListFunc;
/**
* @brief Default list func
*
*/
#define DEFAULT_LIST_FUNCS \
{ \
HAL_Malloc, HAL_Free, HAL_MutexCreate, HAL_MutexLock, HAL_MutexUnlock, HAL_MutexDestroy \
}
/**
* @brief Node process handle called by utils_list_process.
*
*/
typedef UtilsListResult (*OnNodeProcessHandle)(void *list, void *node, void *val, void *usr_data);
/**
* @brief Create list with max len, return NULL if fail.
*
* @param[in] func function needed by list
* @param[in] max_len max_len of list
* @return pointer to list, NULL for failed
*/
void *utils_list_create(UtilsListFunc func, int max_len);
/**
* @brief Destroy list.
*
* @param[in] list pointer to list
*/
void utils_list_destroy(void *list);
/**
* @brief Clear the list
*
* @param list
*/
void utils_list_clear(void *list);
/**
* @brief Get list len.
*
* @param[in] list pointer to list
* @return len of list
*/
int utils_list_len_get(void *list);
/**
* @brief Push the node to list tail, return NULL if node invalid.
*
* @param[in] list pointer to list
* @param[in] val value needed to push to list
* @return pointer to node, NULL for failed
*/
void *utils_list_push(void *list, void *val);
/**
* @brief Pop the val from list head, return NULL if list empty.
*
* @param[in] list pointer to list
* @return val in the head node
*/
void *utils_list_pop(void *list);
/**
* @brief Delete the node in list and release the resource.
*
* @param[in] list pointer to list
* @param[in] node pointer to node needed remove
*/
void utils_list_remove(void *list, void *node);
/**
* @brief Process list using handle function.
*
* @param[in] list pointer to list
* @param[in] direction direction to traverse @see UtilsListDirection
* @param[in] handle process function @see OnNodeProcessHandle
* @param[in,out] usr_data usr data to pass to OnNodeProcessHandle
*/
void utils_list_process(void *list, UtilsListDirection direction, OnNodeProcessHandle handle, void *usr_data);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LIST_H_

View File

@@ -0,0 +1,131 @@
/**
* @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 utils_log.h
* @brief header file for utils log
* @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
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LOG_H_
#define IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LOG_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <string.h>
/**
* @brief SDK log print/upload level.
*
*/
typedef enum {
LOG_LEVEL_DISABLE = 0, /**< disable log print/upload */
LOG_LEVEL_ERROR = 1, /**< error log level */
LOG_LEVEL_WARN = 2, /**< warning log level */
LOG_LEVEL_INFO = 3, /**< info log level */
LOG_LEVEL_DEBUG = 4, /**< debug log level */
} LogLevel;
/**
* @brief User's self defined log handler callback.
*
*/
typedef struct {
void *(*log_malloc)(size_t len);
void (*log_free)(void *val);
void (*log_handle)(const char *message);
void (*log_upload)(LogLevel log_level, const char *message);
void (*log_printf)(const char *fmt, ...);
char *(*log_get_current_time_str)(void);
void *(*log_mutex_create)(void);
void (*log_mutex_lock)(void *mutex);
void (*log_mutex_unlock)(void *mutex);
void (*log_mutex_destroy)(void *mutex);
} LogHandleFunc;
/**
* @brief Default log func
*
*/
#define DEFAULT_LOG_HANDLE_FUNCS \
{ \
HAL_Malloc, HAL_Free, NULL, NULL, HAL_Printf, HAL_Timer_Current, HAL_MutexCreate, HAL_MutexLock, \
HAL_MutexUnlock, HAL_MutexDestroy \
}
/**
* @brief Init log with func, log level, max log size.
*
* @param[in] func function should be implement for utils log
* @param[in] log_level @see LogLevel
* @param[in] max_log_size max size of log to print
* @return 0 for success
*/
int utils_log_init(LogHandleFunc func, LogLevel log_level, int max_log_size);
/**
* @brief Deinit log.
*
*/
void utils_log_deinit(void);
/**
* @brief Set log level.
*
* @param log_level @see LogLevel
*/
void utils_log_set_level(LogLevel log_level);
/**
* @brief Get log level.
*
* @return @see LogLevel
*/
LogLevel utils_log_get_level(void);
/**
* @brief Generate log if level higher than set.
*
* @param[in] file file path
* @param[in] func function where generate log
* @param[in] line line of source file where genertate log
* @param[in] level @see LogLevel
* @param[in] fmt format of log content
*/
void utils_log_gen(const char *file, const char *func, const int line, const int level, const char *fmt, ...);
// Simple APIs for log generation in different level
#define Log_d(fmt, ...) utils_log_gen(__FILE__, __FUNCTION__, __LINE__, LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__)
#define Log_i(fmt, ...) utils_log_gen(__FILE__, __FUNCTION__, __LINE__, LOG_LEVEL_INFO, fmt, ##__VA_ARGS__)
#define Log_w(fmt, ...) utils_log_gen(__FILE__, __FUNCTION__, __LINE__, LOG_LEVEL_WARN, fmt, ##__VA_ARGS__)
#define Log_e(fmt, ...) utils_log_gen(__FILE__, __FUNCTION__, __LINE__, LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__)
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_COMMON_UTILS_INC_UTILS_LOG_H_

View File

@@ -0,0 +1,142 @@
/**
* @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 utils_downloader.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 "utils_downloader.h"
/**
* @brief Downloader.
*
*/
typedef struct {
UtilsDownloaderFunction func;
void* usr_data;
} UtilsDownloader;
/**
* @brief Init downloader.
*
* @param[in] func download function should implement
* @param[in] usr_data user data using in function
* @return pointer to downloader
*/
void* utils_downloader_init(UtilsDownloaderFunction func, void* usr_data)
{
if (!func.downloader_malloc) {
return NULL;
}
UtilsDownloader* handle = func.downloader_malloc(sizeof(UtilsDownloader));
if (!handle) {
return NULL;
}
handle->func = func;
handle->usr_data = usr_data;
return handle;
}
/**
* @brief Process download using function.
*
* @param[in,out] handle pointer to downloader
* @return -1 for fail, others see data_download_finish
*/
int utils_downloader_process(void* handle)
{
int rc = -1;
UtilsDownloader* downloader = handle;
UtilsDownloaderStatus status = UTILS_DOWNLOADER_STATUS_SUCCESS;
if (!handle) {
return -1;
}
rc = downloader->func.break_point_init(downloader->usr_data);
if (rc) {
status = UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED;
goto exit;
}
// if check ok, restore from break point, otherwise set break point
rc = downloader->func.break_point_check(downloader->usr_data)
? downloader->func.break_point_set(downloader->usr_data)
: downloader->func.break_point_restore(downloader->usr_data);
if (rc) {
status = UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED;
goto exit;
}
rc = downloader->func.data_download_init(downloader->usr_data);
if (rc) {
status = UTILS_DOWNLOADER_STATUS_DATA_DOWNLOAD_FAILED;
goto exit;
}
while (!downloader->func.data_download_is_over(downloader->usr_data)) {
rc = downloader->func.data_download_recv(downloader->usr_data);
if (rc < 0) {
status = UTILS_DOWNLOADER_STATUS_NETWORK_FAILED;
goto exit;
}
rc = downloader->func.data_download_save(downloader->usr_data);
if (rc) {
status = UTILS_DOWNLOADER_STATUS_DATA_DOWNLOAD_FAILED;
goto exit;
}
rc = downloader->func.break_point_save(downloader->usr_data);
if (rc) {
status = UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED;
goto exit;
}
}
exit:
rc = downloader->func.data_download_finish(downloader->usr_data, status);
downloader->func.break_point_deinit(downloader->usr_data);
downloader->func.data_download_deinit(downloader->usr_data);
return rc;
}
/**
* @brief Deinit downloader.
*
* @param[in,out] handle pointer to downloader
*/
void utils_downloader_deinit(void* handle)
{
UtilsDownloader* downloader = handle;
if (!handle) {
return;
}
downloader->func.downloader_free(handle);
}

View File

@@ -0,0 +1,684 @@
/**
* @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 utils_json.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-25
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-25 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-29 <td>1.1 <td>fancyxu <td>fix bug and add utils_json_value_data_get
* </table>
*/
#include "utils_json.h"
/**
* @brief Delimiter of json
*
*/
typedef enum {
JSON_DELIMITER_ARRAY_BEGIN = '[',
JSON_DELIMITER_ARRAY_END = ']',
JSON_DELIMITER_OBJECT_BEGIN = '{',
JSON_DELIMITER_OBJECT_END = '}',
JSON_DELIMITER_KEY = '"',
JSON_DELIMITER_VALUE = ':',
JSON_DELIMITER_ELEMENT_END = ',',
JSON_DELIMITER_TYPE_STRING = '"',
JSON_DELIMITER_TYPE_BOOLEAN_TRUE_UPPER = 'T',
JSON_DELIMITER_TYPE_BOOLEAN_TRUE_LOWER = 't',
JSON_DELIMITER_TYPE_BOOLEAN_FALSE_UPPER = 'F',
JSON_DELIMITER_TYPE_BOOLEAN_FALSE_LOWER = 'f',
JSON_DELIMITER_TYPE_NULL_UPPER = 'N',
JSON_DELIMITER_TYPE_NULL_LOWER = 'n',
JSON_DELIMITER_SPACE = ' ',
JSON_DELIMITER_NONE = 0,
} JsonDelimiter;
/**
* @brief Element type support
*
*/
typedef enum {
JSON_ELEMENT_ARRAY = 0,
JSON_ELEMENT_OBJECT,
JSON_ELEMENT_VALUE_STRING,
JSON_ELEMENT_VALUE_NUMBER,
JSON_ELEMENT_VALUE_BOOLEAN,
JSON_ELEMENT_VALUE_NULL,
JSON_ELEMENT_KEY,
} JsonElementType;
/**
* @brief Json Element
*
*/
typedef struct {
JsonElementType type;
char *pos_begin;
char *pos_end;
int element_len;
} JsonElement;
#define json_key JsonElement
#define json_value JsonElement
/**
* @brief Is delimiter begin or end matched
*
*/
typedef struct {
int (*is_begin_matched)(char *str);
int (*is_end_matched)(char *str, int *offset);
} IsDelimiterMatched;
/**
* @brief For array type, when meets '[', means depth++
*
* @param[in] str pointer to char
* @return true for matched
*/
static int _is_array_begin_matched(char *str)
{
return *str == JSON_DELIMITER_ARRAY_BEGIN;
}
/**
* @brief For array type, when meets ']', means depth--
*
* @param[in] str pointer to char
* @param[out] offset offset for pointer to move
* @return true for matched
*/
static int _is_array_end_matched(char *str, int *offset)
{
return *str == JSON_DELIMITER_ARRAY_END;
}
/**
* @brief For object type, when meets '{', means depth++
*
* @param[in] str pointer to char
* @return true for matched
*/
static int _is_object_begin_matched(char *str)
{
return *str == JSON_DELIMITER_OBJECT_BEGIN;
}
/**
* @brief For object type, when meets '}', means depth--
*
* @param[in] str pointer to char
* @param[out] offset offset for pointer to move
* @return true for matched
*/
static int _is_object_end_matched(char *str, int *offset)
{
return *str == JSON_DELIMITER_OBJECT_END;
}
/**
* @brief For string type, when meets '"', but not "\"", means the end
*
* @param[in] str pointer to char
* @param[out] offset offset for pointer to move
* @return true for matched
*/
static int _is_string_end_matched(char *str, int *offset)
{
return *str == '"' && *(str - 1) != '\\';
}
/**
* @brief For number type, when meets not '0~9', '.' or 'f' for float, means the end
*
* @param[in] str pointer to char
* @param[out] offset offset for pointer to move
* @return true for matched
*/
static int _is_number_end_matched(char *str, int *offset)
{
return (*str < '0' || *str > '9') && *str != '.' && *str != 'f' && *str != '-';
}
/**
* @brief For boolen type, when meets "true", "True", "false" or "False", means the end
*
* @param[in] str pointer to char
* @param[out] offset offset for pointer to move, -1 means error
* @return true for matched
*/
static int _is_boolean_end_matched(char *str, int *offset)
{
*offset = !strncmp(str, "true", 4) || !strncmp(str, "TRUE", 4) ? 4 : *offset;
*offset = !strncmp(str, "false", 5) || !strncmp(str, "FALSE", 5) ? 5 : *offset;
if (*offset == 0) {
*offset = -1; // -1 means error
}
return 1;
}
/**
* @brief For boolen type, when meets "null", "NULL", means the end
*
* @param[in] str pointer to char
* @param[out] offset offset for pointer to move, -1 means error
* @return true for matched
*/
static int _is_null_end_matched(char *str, int *offset)
{
*offset = !strncmp(str, "null", 4) || !strncmp(str, "NULL", 4) ? 4 : 0;
if (*offset == 0) {
*offset = -1; // -1 means error
}
return 1;
}
/**
* @brief Table of matched function of elements
*
*/
static IsDelimiterMatched sg_element_matched_func[] = {
{_is_array_begin_matched, _is_array_end_matched}, // JSON_ELEMENT_ARRAY
{_is_object_begin_matched, _is_object_end_matched}, // JSON_ELEMENT_OBJECT
{NULL, _is_string_end_matched}, // JSON_ELEMENT_VALUE_STRING
{NULL, _is_number_end_matched}, // JSON_ELEMENT_VALUE_NUMBER
{NULL, _is_boolean_end_matched}, // JSON_ELEMENT_VALUE_BOOLEAN
{NULL, _is_null_end_matched}, // JSON_ELEMENT_VALUE_NULL
{NULL, NULL}, // JSON_ELEMENT_KEY
};
/**
* @brief Increase pos and decrease remain len
*
* @param[in,out] pos current pos
* @param[in,out] len current len
*/
static void _increase_pos(char **pos, int *len)
{
(*pos)++;
(*len)--;
};
/**
* @brief Find delimiter and return pos
*
* @param[in] ch delimiter character
* @param[in] spilt_char if 0 then spilt nothing but delimiter
* @param[in] str input string
* @param[in,out] remain_len remaining length
* @return NULL for failed
*/
static char *_find_json_delimiter(JsonDelimiter ch, char spilt_char, const char *str, int *remain_len)
{
char *pos = NULL;
char *src = (char *)str;
while ((*remain_len) > 0 && src && *src) {
if ((!spilt_char && *src != ch) || (spilt_char && *src == spilt_char) || (*src == '\n')) {
_increase_pos(&src, remain_len);
continue;
}
pos = (*src == ch) ? src : 0;
break;
}
return pos;
}
/**
* @brief Find element end
*
* @param[in] is_begin_matched using in nesting
* @param[in] is_end_matched match end
* @param[in] str input string
* @param[in,out] remain_len remaining length
* @return NULL for failed
*/
static char *_find_json_value_element_end(int (*is_begin_matched)(char *), int (*is_end_matched)(char *, int *),
char *str, int *remain_len)
{
int offset = 0;
int depth = 0;
char *pos = NULL;
while (str && *str) {
offset = 0;
if (*remain_len <= 0) {
return NULL;
}
if (is_begin_matched) {
if (is_begin_matched(str)) {
depth++;
}
}
if (is_end_matched(str, &offset)) {
if (!depth) {
if (offset < 0) {
break; // failed
}
pos = str + offset;
(*remain_len) -= offset;
break;
}
if (depth > 0) {
depth--;
}
}
str++;
(*remain_len)--;
}
return pos;
}
/**
* @brief Get json key from string
*
* @param[in] node_begin string begin
* @param[in,out] remain_len remaining length
* @param[out] key key node
* @return 0 for success
*/
static int _get_json_key(char *node_begin, int *remain_len, json_key *key)
{
char *key_begin = _find_json_delimiter(JSON_DELIMITER_KEY, JSON_DELIMITER_SPACE, node_begin, remain_len);
if (!key_begin) {
return -1;
}
_increase_pos(&key_begin, remain_len);
char *key_end = _find_json_delimiter(JSON_DELIMITER_KEY, JSON_DELIMITER_NONE, key_begin, remain_len);
if (!key_end) {
return -1;
}
key->type = JSON_ELEMENT_KEY;
key->pos_begin = key_begin;
key->pos_end = key_end;
key->element_len = key_end - key_begin;
return 0;
}
/**
* @brief Get json value from key end
*
* @param[in] key_end end of key
* @param[in,out] remain_len remaining length
* @param[out] value value node
* @return 0 for success
*/
static int _get_json_value(char *key_end, int *remain_len, json_value *value)
{
char *value_begin = _find_json_delimiter(JSON_DELIMITER_VALUE, JSON_DELIMITER_SPACE, key_end, remain_len);
if (!value_begin) {
return -1;
}
_increase_pos(&value_begin, remain_len);
// filter all the space
while (*value_begin == ' ') {
value_begin++;
(*remain_len)--;
if (*remain_len <= 0) {
return -1;
}
};
JsonElementType type;
char *value_end = NULL;
switch (*value_begin) {
case JSON_DELIMITER_ARRAY_BEGIN:
// should + 1 for escape first [
_increase_pos(&value_begin, remain_len);
type = JSON_ELEMENT_ARRAY;
break;
case JSON_DELIMITER_OBJECT_BEGIN:
// should + 1 for escape first {
_increase_pos(&value_begin, remain_len);
type = JSON_ELEMENT_OBJECT;
break;
case JSON_DELIMITER_TYPE_STRING:
// should + 1 for escape first "
_increase_pos(&value_begin, remain_len);
type = JSON_ELEMENT_VALUE_STRING;
break;
case JSON_DELIMITER_TYPE_BOOLEAN_TRUE_UPPER:
case JSON_DELIMITER_TYPE_BOOLEAN_TRUE_LOWER:
case JSON_DELIMITER_TYPE_BOOLEAN_FALSE_UPPER:
case JSON_DELIMITER_TYPE_BOOLEAN_FALSE_LOWER:
type = JSON_ELEMENT_VALUE_BOOLEAN;
break;
case JSON_DELIMITER_TYPE_NULL_UPPER:
case JSON_DELIMITER_TYPE_NULL_LOWER:
type = JSON_ELEMENT_VALUE_NULL;
break;
default:
type = JSON_ELEMENT_VALUE_NUMBER;
break;
}
value_end = _find_json_value_element_end(sg_element_matched_func[type].is_begin_matched,
sg_element_matched_func[type].is_end_matched, value_begin, remain_len);
if (!value_end) {
return -1;
}
int is_object_or_array = (type == JSON_ELEMENT_ARRAY || type == JSON_ELEMENT_OBJECT);
int is_object_or_array_or_string = (is_object_or_array || type == JSON_ELEMENT_VALUE_STRING);
value->type = type;
value->pos_begin = value_begin - is_object_or_array; // include '[' or '{'
value->element_len = value_end - value->pos_begin + is_object_or_array; // include ']' or '}'
value->pos_end = value_end + is_object_or_array_or_string; // filter '"' , ']' or '}'
return 0;
}
/**
* @brief Get value by key from json string
*
* @param[in] str json string
* @param[in] str_len strign length
* @param[in] key key
* @param[in] key_len key length
* @param[out] value value
* @return 0 for success
*/
int json_get_value_by_key(const char *str, int str_len, const char *key, int key_len, json_value *value)
{
int rc = 0;
char *object_begin = NULL;
json_key key_found = {JSON_ELEMENT_KEY, 0, 0, 0};
object_begin = _find_json_delimiter(JSON_DELIMITER_OBJECT_BEGIN, JSON_DELIMITER_SPACE, (char *)str, &str_len);
if (!object_begin) {
return -1;
}
_increase_pos(&object_begin, &str_len);
do {
rc = _get_json_key(object_begin, &str_len, &key_found);
if (rc) {
break;
}
_increase_pos(&key_found.pos_end, &str_len);
rc = _get_json_value(key_found.pos_end, &str_len, value);
if (rc) {
break;
}
// NULL of object begin can be ok for reach the end of str
object_begin = _find_json_delimiter(JSON_DELIMITER_ELEMENT_END, JSON_DELIMITER_SPACE, value->pos_end, &str_len);
if (object_begin) {
_increase_pos(&object_begin, &str_len);
}
} while (key_len != key_found.element_len || strncmp(key, key_found.pos_begin, key_found.element_len));
return rc;
}
/**
* @brief Get value from json string. Not strict, just for iot scene, we suppose all the string is valid json.
*
* @param[in] key key in json, support nesting with '.'
* @param[in] key_len key len
* @param[in] src json string
* @param[in] src_len src length
* @param[out] value value
* @return 0 for success
*/
int utils_json_value_get(const char *key, int key_len, const char *src, int src_len, UtilsJsonValue *value)
{
int rc = 0;
char *delim = NULL;
char *src_iter = (char *)src;
int src_iter_len = src_len;
char *key_iter = (char *)key;
char *key_next = NULL;
int key_next_len = 0;
json_value value_tmp;
// key can be separated by '.', such as: outer_key.(.......).inner_key
while ((delim = strchr(key_iter, '.')) != NULL) {
key_next = key_iter;
key_next_len = delim - key_iter;
rc = json_get_value_by_key(src_iter, src_iter_len, key_next, key_next_len, &value_tmp);
if (rc) {
return rc;
}
src_iter = value_tmp.pos_begin;
src_iter_len = value_tmp.element_len;
key_iter = delim + 1;
}
// found inner key and get value
rc = json_get_value_by_key(src_iter, src_iter_len, key_iter, key_len + key - key_iter, &value_tmp);
if (rc) {
return rc;
}
value->value = value_tmp.pos_begin;
value->value_len = value_tmp.element_len;
return 0;
}
/**
* @brief Get data of value with type.
*
* @param[in] value @see UtilsJsonValue
* @param[in] type value type, string can use value directly. @see UtilsJsonValueType
* @param[out] data data pointer, user should
* @return 0 for success
*/
int utils_json_value_data_get(UtilsJsonValue value, UtilsJsonValueType type, void *data)
{
char value_tmp[32] = {0};
if (value.value_len > sizeof(value_tmp) || !value.value_len) {
return -1;
}
strncpy(value_tmp, value.value, value.value_len);
switch (type) {
case UTILS_JSON_VALUE_TYPE_INT32:
return !(sscanf(value_tmp, "%" SCNi32, (int32_t *)data) == 1);
case UTILS_JSON_VALUE_TYPE_INT64:
return !(sscanf(value_tmp, "%" SCNi64, (int64_t *)data) == 1);
case UTILS_JSON_VALUE_TYPE_UINT32:
return !(sscanf(value_tmp, "%" SCNu32, (uint32_t *)data) == 1);
case UTILS_JSON_VALUE_TYPE_UINT64:
return !(sscanf(value_tmp, "%" SCNu64, (uint64_t *)data) == 1);
case UTILS_JSON_VALUE_TYPE_FLOAT:
return !(sscanf(value_tmp, "%f", (float *)data) == 1);
case UTILS_JSON_VALUE_TYPE_DOUBLE:
return !(sscanf(value_tmp, "%lf", (double *)data) == 1);
case UTILS_JSON_VALUE_TYPE_BOOLEAN:
*(int *)data = strcmp(value_tmp, "false");
return 0;
default:
break;
}
return -1;
}
/**
* @brief Return unsigned int value of key in json.
*
* @param[in] key key in json, support nesting with '.'
* @param[in] key_len key len
* @param[in] src json string
* @param[in] src_len src length
* @param[out] data data value
* @return 0 for success
*/
int utils_json_get_uint32(const char *key, int key_len, const char *src, int src_len, uint32_t *data)
{
int rc;
UtilsJsonValue tmp;
rc = utils_json_value_get(key, key_len, src, src_len, &tmp);
if (rc) {
return -1;
}
rc = utils_json_value_data_get(tmp, UTILS_JSON_VALUE_TYPE_UINT32, data);
if (rc) {
return -1;
}
return 0;
}
/**
* @brief Return int value of key in json.
*
* @param[in] key key in json, support nesting with '.'
* @param[in] key_len key len
* @param[in] src json string
* @param[in] src_len src length
* @param[out] data data value
* @return 0 for success
*/
int utils_json_get_int32(const char *key, int key_len, const char *src, int src_len, int32_t *data)
{
int rc;
UtilsJsonValue tmp;
rc = utils_json_value_get(key, key_len, src, src_len, &tmp);
if (rc) {
return -1;
}
rc = utils_json_value_data_get(tmp, UTILS_JSON_VALUE_TYPE_INT32, data);
if (rc) {
return -1;
}
return 0;
}
/**
* @brief Remove '\\' in json string.
*
* @param[in,out] src string to transfer
* @param[in] src_len string len
* @return length after transfer
*/
int utils_json_strip_transfer(char *src, int src_len)
{
char *src_tmp = src;
char *end = src + src_len;
while (src_tmp < end) {
if (*src_tmp == '\\') {
memmove(src_tmp, src_tmp + 1, end - src_tmp);
end--;
}
src_tmp++;
}
return end - src;
}
/**
* @brief Parse array object, assume array json is legal, src should be like "[12, 456]", this function will split array
* according to array_elem_type, obj_cb will be called for each elements.
*
* @param[in] src array string
* @param[in] src_len length of src
* @param[in] obj_cb callback to deal with array element
* @param[in] arg argument passed to the obj_cb
*/
void utils_json_array_parse(const char *src, int src_len, UtilsJsonArrayIterResult (*obj_cb)(const char *, int, void *),
void *arg)
{
#define SKIP_SPACE(str) while (*str == ' ' && (str = str + 1))
if (!obj_cb) {
return;
}
const char *start = src + 1, *end = src + 1;
char left_delimiter, right_delimiter;
SKIP_SPACE(start);
switch (*start) {
case JSON_DELIMITER_ARRAY_BEGIN:
left_delimiter = JSON_DELIMITER_ARRAY_BEGIN;
right_delimiter = JSON_DELIMITER_ARRAY_END;
break;
case JSON_DELIMITER_OBJECT_BEGIN:
left_delimiter = JSON_DELIMITER_OBJECT_BEGIN;
right_delimiter = JSON_DELIMITER_OBJECT_END;
break;
case JSON_DELIMITER_TYPE_STRING:
left_delimiter = JSON_DELIMITER_TYPE_STRING;
right_delimiter = JSON_DELIMITER_TYPE_STRING;
break;
default:
left_delimiter = 0;
right_delimiter = JSON_DELIMITER_ELEMENT_END;
break;
}
int remain_length = 0;
while (end && end <= (src + src_len)) {
SKIP_SPACE(start);
if (left_delimiter) {
remain_length = src + src_len - start;
start = _find_json_delimiter(left_delimiter, JSON_DELIMITER_NONE, start, &remain_length);
if (!start) {
break;
}
if (JSON_DELIMITER_TYPE_STRING == left_delimiter) {
start++;
}
}
remain_length = src + src_len - start;
end = _find_json_delimiter(right_delimiter, JSON_DELIMITER_NONE, start, &remain_length);
if (!end) {
if (!left_delimiter) {
obj_cb(start, src + src_len - start - 1, arg);
}
break;
}
if (JSON_DELIMITER_ARRAY_END == right_delimiter || JSON_DELIMITER_OBJECT_END == right_delimiter) {
end++;
}
if (UTILS_JSON_ARRAY_ITER_STOP == obj_cb(start, end - start, arg)) {
return;
}
start = end + 1;
}
}

View File

@@ -0,0 +1,338 @@
/**
* @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 utils_list.c
* @brief utils list operation
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-25
*
* @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
* </table>
*/
#include "utils_list.h"
/**
* @brief Define list node.
*
*/
typedef struct ListNode {
struct ListNode *prev;
struct ListNode *next;
void *val;
} ListNode;
/**
* @brief Double Linked List.
*
*/
typedef struct {
UtilsListFunc func;
ListNode *head;
ListNode *tail;
void *lock;
int len;
int max_len;
} List;
/**
* @brief List iterator.
*
*/
typedef struct {
List *list;
ListNode *next;
UtilsListDirection direction;
} ListIterator;
/**
* @brief Lock list.
*
* @param[in] list pointer to list
*/
static inline void _list_lock(List *list)
{
if (list->lock) {
list->func.list_lock(list->lock);
}
}
/**
* @brief Unlock list.
*
* @param[in] list pointer to list
*/
static inline void _list_unlock(List *list)
{
if (list->lock) {
list->func.list_unlock(list->lock);
}
}
/**
* @brief Delete the node in list and release the resource.
*
* @param[in] list pointer to list
* @param[in] node pointer to node needed remove
*/
static void _list_remove(void *list, void *node)
{
List *self = (List *)list;
ListNode *list_node = (ListNode *)node;
list_node->prev ? (list_node->prev->next = list_node->next) : (self->head = list_node->next);
list_node->next ? (list_node->next->prev = list_node->prev) : (self->tail = list_node->prev);
self->func.list_free(list_node->val);
self->func.list_free(list_node);
if (self->len) {
--self->len;
}
}
/**
* @brief Create list with max len, return NULL if fail.
*
* @param[in] func function needed by list
* @param[in] max_len max_len of list
* @return pointer to list, NULL for failed
*/
void *utils_list_create(UtilsListFunc func, int max_len)
{
List *self;
if (max_len <= 0) {
return NULL;
}
self = (List *)func.list_malloc(sizeof(List));
if (!self) {
return NULL;
}
memset(self, 0, sizeof(List));
if (func.list_lock_init) {
self->lock = func.list_lock_init();
if (!self->lock) {
func.list_free(self);
return NULL;
}
}
self->func = func;
self->max_len = max_len;
return self;
}
/**
* @brief Destroy list.
*
* @param[in] list pointer to list
*/
void utils_list_destroy(void *list)
{
utils_list_clear(list);
List *self = (List *)list;
if (self->lock) {
self->func.list_lock_deinit(self->lock);
}
self->func.list_free(self);
}
/**
* @brief Clear the list.
*
* @param[in] list pointer to list
*/
void utils_list_clear(void *list)
{
if (!list) {
return;
}
List *self = (List *)list;
_list_lock(self);
ListNode *next;
ListNode *curr = self->head;
while (self->len--) {
next = curr->next;
self->func.list_free(curr->val);
self->func.list_free(curr);
curr = next;
}
_list_unlock(self);
}
/**
* @brief Get list len.
*
* @param[in] list pointer to list
* @return len of list
*/
int utils_list_len_get(void *list)
{
List *self = (List *)list;
return self->len;
}
/**
* @brief Push the node to list tail, return NULL if node invalid.
*
* @param[in] list pointer to list
* @param[in] val value needed to push to list
* @return pointer to node, NULL for failed
*/
void *utils_list_push(void *list, void *val)
{
List *self = (List *)list;
_list_lock(self);
if (!val || self->len >= self->max_len) {
_list_unlock(self);
return NULL;
}
ListNode *node;
node = self->func.list_malloc(sizeof(ListNode));
if (!node) {
_list_unlock(self);
return NULL;
}
node->prev = NULL;
node->next = NULL;
node->val = val;
if (self->len) {
node->prev = self->tail;
node->next = NULL;
self->tail->next = node;
self->tail = node;
} else {
self->head = self->tail = node;
node->prev = node->next = NULL;
}
++self->len;
_list_unlock(self);
return node;
}
/**
* @brief Pop the value from list head, return NULL if list empty.
*
* @param[in] list pointer to list
* @return value in the head node
*/
void *utils_list_pop(void *list)
{
List *self = (List *)list;
ListNode *node = NULL;
_list_lock(self);
if (!self->len) {
_list_unlock(self);
return NULL;
}
node = self->head;
if (--self->len) {
(self->head = node->next)->prev = NULL;
} else {
self->head = self->tail = NULL;
}
node->next = node->prev = NULL;
_list_unlock(self);
void *val = node->val;
self->func.list_free(node);
return val;
}
/**
* @brief Delete the node in list and release the resource.
*
* @param[in] list pointer to list
* @param[in] node pointer to node needed remove
*/
void utils_list_remove(void *list, void *node)
{
List *self = (List *)list;
_list_lock(self);
_list_remove(self, node);
_list_unlock(self);
}
/**
* @brief Process list using handle function.
*
* @param[in] list pointer to list
* @param[in] direction direction to traverse
* @param[in] handle process function @see OnNodeProcessHandle
* @param[in,out] usr_data usr data to pass to OnNodeProcessHandle
*/
void utils_list_process(void *list, UtilsListDirection direction, OnNodeProcessHandle handle, void *usr_data)
{
int rc;
ListNode *node = NULL;
List *self = (List *)list;
_list_lock(self);
if (!utils_list_len_get(list)) {
_list_unlock(self);
return;
}
ListIterator iterator = {
.direction = direction,
.list = self,
.next = direction == LIST_HEAD ? self->head : self->tail,
};
// traverse list to process
while ((node = iterator.next) != NULL) {
iterator.next = iterator.direction == LIST_HEAD ? node->next : node->prev;
if (!node->val) {
_list_remove(list, node);
continue;
}
// process node and val
if (handle) {
rc = handle(list, node, node->val, usr_data);
if (rc) {
break;
}
}
}
_list_unlock(list);
}

View File

@@ -0,0 +1,178 @@
/**
* @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 utils_log.c
* @brief different level log generator
* @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
* </table>
*/
#include "utils_log.h"
static const char *LEVEL_STR[] = {"DIS", "ERR", "WRN", "INF", "DBG"};
static LogHandleFunc sg_log_handle_func;
static char *sg_log_buffer;
static int sg_log_max_size;
static void *sg_log_mutex;
static LogLevel sg_log_print_level = LOG_LEVEL_DEBUG;
extern LogLevel sg_log_upload_level;
/**
* @brief Get file name form path.
*
* @param[in] path file path
* @return file name
*/
static const char *_get_filename(const char *path)
{
#ifdef WIN32
char ch = '\\';
#else
char ch = '/';
#endif
const char *q = strrchr(path, ch);
if (!q) {
q = path;
} else {
q++;
}
return q;
}
/**
* @brief Init log with func, log level, max log size.
*
* @param[in] func function should be implement for utils log
* @param[in] log_level @see LogLevel
* @param[in] max_log_size max size of log to print
* @return 0 for success
*/
int utils_log_init(LogHandleFunc func, LogLevel log_level, int max_log_size)
{
sg_log_handle_func = func;
sg_log_print_level = log_level;
sg_log_max_size = max_log_size;
if (func.log_mutex_create) {
sg_log_mutex = func.log_mutex_create();
if (!sg_log_mutex) {
return -1;
}
}
if (func.log_malloc) {
sg_log_buffer = func.log_malloc(max_log_size);
if (!sg_log_buffer) {
if (sg_log_mutex) {
func.log_mutex_destroy(sg_log_mutex);
}
}
}
return !sg_log_buffer;
}
/**
* @brief Deinit log.
*
*/
void utils_log_deinit(void)
{
sg_log_handle_func.log_free(sg_log_buffer);
if (sg_log_mutex) {
sg_log_handle_func.log_mutex_destroy(sg_log_mutex);
}
sg_log_mutex = NULL;
sg_log_buffer = NULL;
}
/**
* @brief Set log level.
*
* @param log_level @see LogLevel
*/
void utils_log_set_level(LogLevel log_level)
{
sg_log_print_level = log_level;
}
/**
* @brief Get log level.
*
* @return @see LogLevel
*/
LogLevel utils_log_get_level(void)
{
return sg_log_print_level;
}
/**
* @brief Generate log if level higher than set.
*
* @param[in] file file path
* @param[in] func function where generate log
* @param[in] line line of source file where genertate log
* @param[in] level @see LogLevel
* @param[in] fmt format of log content
*/
void utils_log_gen(const char *file, const char *func, const int line, const int level, const char *fmt, ...)
{
if (level > sg_log_print_level) {
return;
}
if (sg_log_mutex) {
sg_log_handle_func.log_mutex_lock(sg_log_mutex);
}
/* format log content */
const char *file_name = _get_filename(file);
char *o = sg_log_buffer;
memset(sg_log_buffer, 0, sg_log_max_size);
o += snprintf(sg_log_buffer, sg_log_max_size, "%s|%s|%s|%s(%d): ", LEVEL_STR[level],
sg_log_handle_func.log_get_current_time_str(), file_name, func, line);
va_list ap;
va_start(ap, fmt);
vsnprintf(o, sg_log_max_size - 2 - strlen(sg_log_buffer), fmt, ap);
va_end(ap);
strncat(sg_log_buffer, "\r\n", sg_log_max_size - strlen(sg_log_buffer) - 1);
if (level <= sg_log_print_level) {
if (sg_log_handle_func.log_handle) {
sg_log_handle_func.log_handle(sg_log_buffer);
}
sg_log_handle_func.log_printf("%s", sg_log_buffer);
}
/* append to upload buffer */
if (sg_log_handle_func.log_upload) {
sg_log_handle_func.log_upload(level, sg_log_buffer);
}
if (sg_log_mutex) {
sg_log_handle_func.log_mutex_unlock(sg_log_mutex);
}
return;
}

View File

@@ -0,0 +1,8 @@
Language: Cpp
BasedOnStyle: Google
ColumnLimit: 120
DerivePointerAlignment: true
PointerAlignment: Left
SortIncludes: true
IncludeBlocks: Preserve
IndentPPDirectives: AfterHash

View File

@@ -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 test_utils.cc
* @brief unittest for utils
* @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-27 <td>1.1 <td>fancyxu <td>support utils json
* <tr><td>2021-07-29 <td>1.1 <td>fancyxu <td>rename HAL_Timer and add utils json test
* </table>
*/
#include <iostream>
#include <string>
#include "gtest/gtest.h"
#include "qcloud_iot_platform.h"
#include "utils_json.h"
#include "utils_list.h"
#include "utils_log.h"
namespace utils_unittest {
/**
* @brief test fixture of utils list
*
*/
class UtilsListTest : public testing::Test {
protected:
void SetUp() override {
UtilsListFunc func = DEFAULT_LIST_FUNCS;
self_list = utils_list_create(func, 10);
ASSERT_NE(self_list, nullptr);
for (int i = 0; i < 10;) {
int *val = reinterpret_cast<int *>(HAL_Malloc(sizeof(int)));
*val = i++;
ASSERT_NE(utils_list_push(self_list, reinterpret_cast<void *>(val)), nullptr);
ASSERT_EQ(utils_list_len_get(self_list), i);
}
}
void TearDown() override { utils_list_destroy(self_list); }
void *self_list;
};
/**
* @brief Test list.
*
*/
TEST_F(UtilsListTest, list) {
ASSERT_EQ(utils_list_push(self_list, reinterpret_cast<void *>(1)), nullptr);
for (int i = 0; i < 10; i++) {
ASSERT_EQ(utils_list_len_get(self_list), 10 - i);
int *val = reinterpret_cast<int *>(utils_list_pop(self_list));
ASSERT_EQ(*val, i);
HAL_Free(val);
}
}
/**
* @brief Test remove list node
*
* @param[in,out] list pointer to list
* @param[in,out] node pointer to node
* @param[in,out] val pointer to val
* @param[in,out] usr_data pointer to usr data
* @return @see UtilsListResult
*/
static UtilsListResult list_process_remove(void *list, void *node, void *val, void *usr_data) {
static int i = 0;
if (*reinterpret_cast<int *>(val) != i++) {
return LIST_TRAVERSE_BREAK;
}
utils_list_remove(list, node);
return LIST_TRAVERSE_CONTINUE;
}
/**
* @brief Test list process.
*
*/
TEST_F(UtilsListTest, list_process) {
utils_list_process(self_list, LIST_HEAD, list_process_remove, NULL);
ASSERT_EQ(utils_list_len_get(self_list), 0);
}
/**
* @brief Test log.
*
*/
TEST(UtilsLogTest, log) {
LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
ASSERT_EQ(utils_log_init(func, LOG_LEVEL_DEBUG, 2048), 0);
Log_d("Here is a debug level log test!");
Log_i("Here is a info level log test!");
Log_w("Here is a warning level log test!");
Log_e("Here is a error level log test!");
utils_log_deinit();
}
/**
* @brief Test json.
*
*/
TEST(UtilsJsonTest, json) {
char test_json[] =
"{\"str_test\":\"test\",\"int_test\":100,\"float_test\":1.210f,\"bool_test\":true,"
"\"bool_false_test\":false,\"null_test\":null}";
UtilsJsonValue value;
int data_int = 0;
ASSERT_EQ(utils_json_value_get("int_test", strlen("int_test"), test_json, strlen(test_json), &value), 0);
ASSERT_EQ(utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_INT32, &data_int), 0);
ASSERT_EQ(data_int, 100);
float data_float = 0.0;
ASSERT_EQ(utils_json_value_get("float_test", strlen("float_test"), test_json, strlen(test_json), &value), 0);
ASSERT_EQ(utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_FLOAT, &data_float), 0);
ASSERT_EQ(data_float, 1.210f);
ASSERT_EQ(utils_json_value_get("bool_test", strlen("bool_test"), test_json, strlen(test_json), &value), 0);
ASSERT_EQ(strncmp(value.value, "true", value.value_len), 0);
ASSERT_EQ(utils_json_value_get("bool_false_test", strlen("bool_false_test"), test_json, strlen(test_json), &value),
0);
ASSERT_EQ(strncmp(value.value, "false", value.value_len), 0);
ASSERT_EQ(utils_json_value_get("null_test", strlen("null_test"), test_json, strlen(test_json), &value), 0);
ASSERT_EQ(strncmp(value.value, "null", value.value_len), 0);
ASSERT_EQ(utils_json_value_get("str_test", strlen("str_test"), test_json, strlen(test_json), &value), 0);
ASSERT_EQ(strncmp(value.value, "test", value.value_len), 0);
char test_json_depth[] = "{\"depth_test\": {\"test\":\"test1\"}}";
ASSERT_EQ(utils_json_value_get("depth_test.test", strlen("depth_test.test"), test_json_depth, strlen(test_json_depth),
&value),
0);
ASSERT_EQ(strncmp(value.value, "test1", value.value_len), 0);
char test_json_before_strip[] =
"{\\\"str_test\":\\\"test\\\",\\\"int_test\\\":100,\\\"float_test\\\":1.210f,\\\"bool_test\\\":true,"
"\\\"bool_false_test\\\":false,\\\"null_test\\\":null}";
ASSERT_EQ(utils_json_strip_transfer(test_json_before_strip, strlen(test_json_before_strip)), strlen(test_json));
ASSERT_EQ(strncmp(test_json_before_strip, test_json, strlen(test_json_before_strip)), 0);
char test_json_break[] = "{\n\"str_test\":\"test\"\n}";
ASSERT_EQ(utils_json_value_get("str_test", strlen("str_test"), test_json_break, strlen(test_json_break), &value), 0);
ASSERT_EQ(strncmp(value.value, "test", value.value_len), 0);
}
/**
* @brief Test json array.
*
*/
static const char *sg_dst_value[12];
static UtilsJsonArrayIterResult _array_handle(const char *value, int value_len, void *arg) {
static int i = 0;
EXPECT_EQ(0, strncmp(sg_dst_value[i++], value, value_len));
return UTILS_JSON_ARRAY_ITER_CONTINUE;
}
TEST(UtilsJsonTest, json_array) {
int i = 0;
char test_array_string[] = "[\"test0\",\"test1\"]";
sg_dst_value[i++] = "test0";
sg_dst_value[i++] = "test1";
char test_array_int[] = "[1,2]";
sg_dst_value[i++] = "1";
sg_dst_value[i++] = "2";
char test_array_float[] = "[1.20f,1.21f]";
sg_dst_value[i++] = "1.20f";
sg_dst_value[i++] = "1.21f";
char test_array_bool[] = "[false,true]";
sg_dst_value[i++] = "false";
sg_dst_value[i++] = "true";
char test_array_null[] = "[null,null]";
sg_dst_value[i++] = "null";
sg_dst_value[i++] = "null";
char test_array_obj[] = "[{\"test\":0},{\"test\":1}]";
sg_dst_value[i++] = "{\"test\":0}";
sg_dst_value[i++] = "{\"test\":1}";
utils_json_array_parse(test_array_string, sizeof(test_array_string) - 1, _array_handle, NULL);
utils_json_array_parse(test_array_int, sizeof(test_array_int) - 1, _array_handle, NULL);
utils_json_array_parse(test_array_float, sizeof(test_array_float) - 1, _array_handle, NULL);
utils_json_array_parse(test_array_bool, sizeof(test_array_bool) - 1, _array_handle, NULL);
utils_json_array_parse(test_array_null, sizeof(test_array_null) - 1, _array_handle, NULL);
utils_json_array_parse(test_array_obj, sizeof(test_array_obj) - 1, _array_handle, NULL);
}
} // namespace utils_unittest

View File

@@ -0,0 +1,687 @@
# Copyright (c) 2012 - 2017, Lars Bilke
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# CHANGES:
#
# 2012-01-31, Lars Bilke
# - Enable Code Coverage
#
# 2013-09-17, Joakim Söderberg
# - Added support for Clang.
# - Some additional usage instructions.
#
# 2016-02-03, Lars Bilke
# - Refactored functions to use named parameters
#
# 2017-06-02, Lars Bilke
# - Merged with modified version from github.com/ufz/ogs
#
# 2019-05-06, Anatolii Kurotych
# - Remove unnecessary --coverage flag
#
# 2019-12-13, FeRD (Frank Dana)
# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor
# of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments.
# - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY
# - All setup functions: accept BASE_DIRECTORY, EXCLUDE list
# - Set lcov basedir with -b argument
# - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be
# overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().)
# - Delete output dir, .info file on 'make clean'
# - Remove Python detection, since version mismatches will break gcovr
# - Minor cleanup (lowercase function names, update examples...)
#
# 2019-12-19, FeRD (Frank Dana)
# - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets
#
# 2020-01-19, Bob Apthorpe
# - Added gfortran support
#
# 2020-02-17, FeRD (Frank Dana)
# - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters
# in EXCLUDEs, and remove manual escaping from gcovr targets
#
# 2021-01-19, Robin Mueller
# - Add CODE_COVERAGE_VERBOSE option which will allow to print out commands which are run
# - Added the option for users to set the GCOVR_ADDITIONAL_ARGS variable to supply additional
# flags to the gcovr command
#
# 2020-05-04, Mihchael Davis
# - Add -fprofile-abs-path to make gcno files contain absolute paths
# - Fix BASE_DIRECTORY not working when defined
# - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines
#
# 2021-05-10, Martin Stump
# - Check if the generator is multi-config before warning about non-Debug builds
#
# USAGE:
#
# 1. Copy this file into your cmake modules path.
#
# 2. Add the following line to your CMakeLists.txt (best inside an if-condition
# using a CMake option() to enable it just optionally):
# include(CodeCoverage)
#
# 3. Append necessary compiler flags:
# append_coverage_compiler_flags()
#
# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og
#
# 4. If you need to exclude additional directories from the report, specify them
# using full paths in the COVERAGE_EXCLUDES variable before calling
# setup_target_for_coverage_*().
# Example:
# set(COVERAGE_EXCLUDES
# '${PROJECT_SOURCE_DIR}/src/dir1/*'
# '/path/to/my/src/dir2/*')
# Or, use the EXCLUDE argument to setup_target_for_coverage_*().
# Example:
# setup_target_for_coverage_lcov(
# NAME coverage
# EXECUTABLE testrunner
# EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*")
#
# 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set
# relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR)
# Example:
# set(COVERAGE_EXCLUDES "dir1/*")
# setup_target_for_coverage_gcovr_html(
# NAME coverage
# EXECUTABLE testrunner
# BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src"
# EXCLUDE "dir2/*")
#
# 5. Use the functions described below to create a custom make target which
# runs your test executable and produces a code coverage report.
#
# 6. Build a Debug build:
# cmake -DCMAKE_BUILD_TYPE=Debug ..
# make
# make my_coverage_target
#
include(CMakeParseArguments)
option(CODE_COVERAGE_VERBOSE "Verbose information" FALSE)
# Check prereqs
find_program( GCOV_PATH gcov )
find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl)
find_program( FASTCOV_PATH NAMES fastcov fastcov.py )
find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat )
find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
find_program( CPPFILT_PATH NAMES c++filt )
if(NOT GCOV_PATH)
message(FATAL_ERROR "gcov not found! Aborting...")
endif() # NOT GCOV_PATH
get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
list(GET LANGUAGES 0 LANG)
if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3)
message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
endif()
elseif(NOT CMAKE_COMPILER_IS_GNUCXX)
if("${CMAKE_Fortran_COMPILER_ID}" MATCHES "[Ff]lang")
# Do nothing; exit conditional without error if true
elseif("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU")
# Do nothing; exit conditional without error if true
else()
message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
endif()
endif()
set(COVERAGE_COMPILER_FLAGS "-g -fprofile-arcs -ftest-coverage"
CACHE INTERNAL "")
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-fprofile-abs-path HAVE_fprofile_abs_path)
if(HAVE_fprofile_abs_path)
set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
endif()
endif()
set(CMAKE_Fortran_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the Fortran compiler during coverage builds."
FORCE )
set(CMAKE_CXX_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the C++ compiler during coverage builds."
FORCE )
set(CMAKE_C_FLAGS_COVERAGE
${COVERAGE_COMPILER_FLAGS}
CACHE STRING "Flags used by the C compiler during coverage builds."
FORCE )
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used for linking binaries during coverage builds."
FORCE )
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
FORCE )
mark_as_advanced(
CMAKE_Fortran_FLAGS_COVERAGE
CMAKE_CXX_FLAGS_COVERAGE
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG))
message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
link_libraries(gcov)
endif()
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_lcov(
# NAME testrunner_coverage # New target name
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES testrunner # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
# # to BASE_DIRECTORY, with CMake 3.4+)
# NO_DEMANGLE # Don't demangle C++ symbols
# # even if c++filt is found
# )
function(setup_target_for_coverage_lcov)
set(options NO_DEMANGLE)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT LCOV_PATH)
message(FATAL_ERROR "lcov not found! Aborting...")
endif() # NOT LCOV_PATH
if(NOT GENHTML_PATH)
message(FATAL_ERROR "genhtml not found! Aborting...")
endif() # NOT GENHTML_PATH
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(DEFINED Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (CMake 3.4+: Also compute absolute paths)
set(LCOV_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES})
if(CMAKE_VERSION VERSION_GREATER 3.4)
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
endif()
list(APPEND LCOV_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES LCOV_EXCLUDES)
# Conditional arguments
if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
set(GENHTML_EXTRA_ARGS "--demangle-cpp")
endif()
# Setting up commands which will be run to generate coverage data.
# Cleanup lcov
set(LCOV_CLEAN_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory .
-b ${BASEDIR} --zerocounters
)
# Create baseline to make sure untouched files show up in the report
set(LCOV_BASELINE_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b
${BASEDIR} -o ${Coverage_NAME}.base
)
# Run tests
set(LCOV_EXEC_TESTS_CMD
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
)
# Capturing lcov counters and generating report
set(LCOV_CAPTURE_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b
${BASEDIR} --capture --output-file ${Coverage_NAME}.capture
)
# add baseline counters
set(LCOV_BASELINE_COUNT_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base
-a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total
)
# filter collected data to final coverage report
set(LCOV_FILTER_CMD
${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove
${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info
)
# Generate HTML output
set(LCOV_GEN_HTML_CMD
${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o
${Coverage_NAME} ${Coverage_NAME}.info
)
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Executed command report")
message(STATUS "Command to clean up lcov: ")
string(REPLACE ";" " " LCOV_CLEAN_CMD_SPACED "${LCOV_CLEAN_CMD}")
message(STATUS "${LCOV_CLEAN_CMD_SPACED}")
message(STATUS "Command to create baseline: ")
string(REPLACE ";" " " LCOV_BASELINE_CMD_SPACED "${LCOV_BASELINE_CMD}")
message(STATUS "${LCOV_BASELINE_CMD_SPACED}")
message(STATUS "Command to run the tests: ")
string(REPLACE ";" " " LCOV_EXEC_TESTS_CMD_SPACED "${LCOV_EXEC_TESTS_CMD}")
message(STATUS "${LCOV_EXEC_TESTS_CMD_SPACED}")
message(STATUS "Command to capture counters and generate report: ")
string(REPLACE ";" " " LCOV_CAPTURE_CMD_SPACED "${LCOV_CAPTURE_CMD}")
message(STATUS "${LCOV_CAPTURE_CMD_SPACED}")
message(STATUS "Command to add baseline counters: ")
string(REPLACE ";" " " LCOV_BASELINE_COUNT_CMD_SPACED "${LCOV_BASELINE_COUNT_CMD}")
message(STATUS "${LCOV_BASELINE_COUNT_CMD_SPACED}")
message(STATUS "Command to filter collected data: ")
string(REPLACE ";" " " LCOV_FILTER_CMD_SPACED "${LCOV_FILTER_CMD}")
message(STATUS "${LCOV_FILTER_CMD_SPACED}")
message(STATUS "Command to generate lcov HTML output: ")
string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}")
message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}")
endif()
# Setup target
add_custom_target(${Coverage_NAME}
COMMAND ${LCOV_CLEAN_CMD}
COMMAND ${LCOV_BASELINE_CMD}
COMMAND ${LCOV_EXEC_TESTS_CMD}
COMMAND ${LCOV_CAPTURE_CMD}
COMMAND ${LCOV_BASELINE_COUNT_CMD}
COMMAND ${LCOV_FILTER_CMD}
COMMAND ${LCOV_GEN_HTML_CMD}
# Set output files as GENERATED (will be removed on 'make clean')
BYPRODUCTS
${Coverage_NAME}.base
${Coverage_NAME}.capture
${Coverage_NAME}.total
${Coverage_NAME}.info
${Coverage_NAME}/index.html
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
)
# Show where to find the lcov info report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info."
)
# Show info where to find the report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
)
endfunction() # setup_target_for_coverage_lcov
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_gcovr_xml(
# NAME ctest_coverage # New target name
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES executable_target # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
# # to BASE_DIRECTORY, with CMake 3.4+)
# )
# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
# GCVOR command.
function(setup_target_for_coverage_gcovr_xml)
set(options NONE)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT GCOVR_PATH)
message(FATAL_ERROR "gcovr not found! Aborting...")
endif() # NOT GCOVR_PATH
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(DEFINED Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (CMake 3.4+: Also compute absolute paths)
set(GCOVR_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
if(CMAKE_VERSION VERSION_GREATER 3.4)
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
endif()
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
# Combine excludes to several -e arguments
set(GCOVR_EXCLUDE_ARGS "")
foreach(EXCLUDE ${GCOVR_EXCLUDES})
list(APPEND GCOVR_EXCLUDE_ARGS "-e")
list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
endforeach()
# Set up commands which will be run to generate coverage data
# Run tests
set(GCOVR_XML_EXEC_TESTS_CMD
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
)
# Running gcovr
set(GCOVR_XML_CMD
${GCOVR_PATH} --xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} ${GCOVR_EXCLUDE_ARGS}
--object-directory=${PROJECT_BINARY_DIR} -o ${Coverage_NAME}.xml
)
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Executed command report")
message(STATUS "Command to run tests: ")
string(REPLACE ";" " " GCOVR_XML_EXEC_TESTS_CMD_SPACED "${GCOVR_XML_EXEC_TESTS_CMD}")
message(STATUS "${GCOVR_XML_EXEC_TESTS_CMD_SPACED}")
message(STATUS "Command to generate gcovr XML coverage data: ")
string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}")
message(STATUS "${GCOVR_XML_CMD_SPACED}")
endif()
add_custom_target(${Coverage_NAME}
COMMAND ${GCOVR_XML_EXEC_TESTS_CMD}
COMMAND ${GCOVR_XML_CMD}
BYPRODUCTS ${Coverage_NAME}.xml
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Running gcovr to produce Cobertura code coverage report."
)
# Show info where to find the report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml."
)
endfunction() # setup_target_for_coverage_gcovr_xml
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_gcovr_html(
# NAME ctest_coverage # New target name
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES executable_target # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
# # to BASE_DIRECTORY, with CMake 3.4+)
# )
# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
# GCVOR command.
function(setup_target_for_coverage_gcovr_html)
set(options NONE)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT GCOVR_PATH)
message(FATAL_ERROR "gcovr not found! Aborting...")
endif() # NOT GCOVR_PATH
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(DEFINED Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (CMake 3.4+: Also compute absolute paths)
set(GCOVR_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
if(CMAKE_VERSION VERSION_GREATER 3.4)
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
endif()
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
# Combine excludes to several -e arguments
set(GCOVR_EXCLUDE_ARGS "")
foreach(EXCLUDE ${GCOVR_EXCLUDES})
list(APPEND GCOVR_EXCLUDE_ARGS "-e")
list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
endforeach()
# Set up commands which will be run to generate coverage data
# Run tests
set(GCOVR_HTML_EXEC_TESTS_CMD
${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}
)
# Create folder
set(GCOVR_HTML_FOLDER_CMD
${CMAKE_COMMAND} -E make_directory ${EXECUTABLE_OUTPUT_PATH}/${Coverage_NAME}
)
# Running gcovr
set(GCOVR_HTML_CMD
${GCOVR_PATH} --html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS}
${GCOVR_EXCLUDE_ARGS} --object-directory=${EXECUTABLE_OUTPUT_PATH}
-o ${Coverage_NAME}/index.html
)
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Executed command report")
message(STATUS "Command to run tests: ")
string(REPLACE ";" " " GCOVR_HTML_EXEC_TESTS_CMD_SPACED "${GCOVR_HTML_EXEC_TESTS_CMD}")
message(STATUS "${GCOVR_HTML_EXEC_TESTS_CMD_SPACED}")
message(STATUS "Command to create a folder: ")
string(REPLACE ";" " " GCOVR_HTML_FOLDER_CMD_SPACED "${GCOVR_HTML_FOLDER_CMD}")
message(STATUS "${GCOVR_HTML_FOLDER_CMD_SPACED}")
message(STATUS "Command to generate gcovr HTML coverage data: ")
string(REPLACE ";" " " GCOVR_HTML_CMD_SPACED "${GCOVR_HTML_CMD}")
message(STATUS "${GCOVR_HTML_CMD_SPACED}")
endif()
add_custom_target(${Coverage_NAME} ALL
COMMAND ${GCOVR_HTML_EXEC_TESTS_CMD}
COMMAND ${GCOVR_HTML_FOLDER_CMD}
COMMAND ${GCOVR_HTML_CMD}
BYPRODUCTS ${EXECUTABLE_OUTPUT_PATH}/${Coverage_NAME}/index.html # report directory
WORKING_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Running gcovr to produce HTML code coverage report."
)
# Show info where to find the report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ;
COMMENT "Open ${EXECUTABLE_OUTPUT_PATH}/${Coverage_NAME}/index.html in your browser to view the coverage report."
)
endfunction() # setup_target_for_coverage_gcovr_html
# Defines a target for running and collection code coverage information
# Builds dependencies, runs the given executable and outputs reports.
# NOTE! The executable should always have a ZERO as exit code otherwise
# the coverage generation will not complete.
#
# setup_target_for_coverage_fastcov(
# NAME testrunner_coverage # New target name
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES testrunner # Dependencies to build first
# BASE_DIRECTORY "../" # Base directory for report
# # (defaults to PROJECT_SOURCE_DIR)
# EXCLUDE "src/dir1/" "src/dir2/" # Patterns to exclude.
# NO_DEMANGLE # Don't demangle C++ symbols
# # even if c++filt is found
# SKIP_HTML # Don't create html report
# )
function(setup_target_for_coverage_fastcov)
set(options NO_DEMANGLE SKIP_HTML)
set(oneValueArgs BASE_DIRECTORY NAME)
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES FASTCOV_ARGS GENHTML_ARGS)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT FASTCOV_PATH)
message(FATAL_ERROR "fastcov not found! Aborting...")
endif()
if(NOT GENHTML_PATH)
message(FATAL_ERROR "genhtml not found! Aborting...")
endif()
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
if(Coverage_BASE_DIRECTORY)
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
else()
set(BASEDIR ${PROJECT_SOURCE_DIR})
endif()
# Collect excludes (Patterns, not paths, for fastcov)
set(FASTCOV_EXCLUDES "")
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_FASTCOV_EXCLUDES})
list(APPEND FASTCOV_EXCLUDES "${EXCLUDE}")
endforeach()
list(REMOVE_DUPLICATES FASTCOV_EXCLUDES)
# Conditional arguments
if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
set(GENHTML_EXTRA_ARGS "--demangle-cpp")
endif()
# Set up commands which will be run to generate coverage data
set(FASTCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS})
set(FASTCOV_CAPTURE_CMD ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
--search-directory ${BASEDIR}
--process-gcno
--lcov
--output ${Coverage_NAME}.info
--exclude ${FASTCOV_EXCLUDES}
--exclude ${FASTCOV_EXCLUDES}
)
if(Coverage_SKIP_HTML)
set(FASTCOV_HTML_CMD ";")
else()
set(FASTCOV_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS}
-o ${Coverage_NAME} ${Coverage_NAME}.info
)
endif()
if(CODE_COVERAGE_VERBOSE)
message(STATUS "Code coverage commands for target ${Coverage_NAME} (fastcov):")
message(" Running tests:")
string(REPLACE ";" " " FASTCOV_EXEC_TESTS_CMD_SPACED "${FASTCOV_EXEC_TESTS_CMD}")
message(" ${FASTCOV_EXEC_TESTS_CMD_SPACED}")
message(" Capturing fastcov counters and generating report:")
string(REPLACE ";" " " FASTCOV_CAPTURE_CMD_SPACED "${FASTCOV_CAPTURE_CMD}")
message(" ${FASTCOV_CAPTURE_CMD_SPACED}")
if(NOT Coverage_SKIP_HTML)
message(" Generating HTML report: ")
string(REPLACE ";" " " FASTCOV_HTML_CMD_SPACED "${FASTCOV_HTML_CMD}")
message(" ${FASTCOV_HTML_CMD_SPACED}")
endif()
endif()
# Setup target
add_custom_target(${Coverage_NAME}
# Cleanup fastcov
COMMAND ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH}
--search-directory ${BASEDIR}
--zerocounters
COMMAND ${FASTCOV_EXEC_TESTS_CMD}
COMMAND ${FASTCOV_CAPTURE_CMD}
COMMAND ${FASTCOV_HTML_CMD}
# Set output files as GENERATED (will be removed on 'make clean')
BYPRODUCTS
${Coverage_NAME}.info
${Coverage_NAME}/index.html # report directory
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
VERBATIM # Protect arguments to commands
COMMENT "Resetting code coverage counters to zero. Processing code coverage counters and generating report."
)
set(INFO_MSG "fastcov code coverage info report saved in ${Coverage_NAME}.info.")
if(NOT Coverage_SKIP_HTML)
string(APPEND INFO_MSG " Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report.")
endif()
# Show where to find the fastcov info report
add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo ${INFO_MSG}
)
endfunction() # setup_target_for_coverage_fastcov
function(append_coverage_compiler_flags)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
endfunction() # append_coverage_compiler_flags

View File

@@ -0,0 +1,874 @@
# Config file for mosquitto
#
# See mosquitto.conf(5) for more information.
#
# Default values are shown, uncomment to change.
#
# Use the # character to indicate a comment, but only if it is the
# very first character on the line.
# =================================================================
# General configuration
# =================================================================
# Use per listener security settings.
#
# It is recommended this option be set before any other options.
#
# If this option is set to true, then all authentication and access control
# options are controlled on a per listener basis. The following options are
# affected:
#
# password_file acl_file psk_file auth_plugin auth_opt_* allow_anonymous
# auto_id_prefix allow_zero_length_clientid
#
# Note that if set to true, then a durable client (i.e. with clean session set
# to false) that has disconnected will use the ACL settings defined for the
# listener that it was most recently connected to.
#
# The default behaviour is for this to be set to false, which maintains the
# setting behaviour from previous versions of mosquitto.
#per_listener_settings false
# This option controls whether a client is allowed to connect with a zero
# length client id or not. This option only affects clients using MQTT v3.1.1
# and later. If set to false, clients connecting with a zero length client id
# are disconnected. If set to true, clients will be allocated a client id by
# the broker. This means it is only useful for clients with clean session set
# to true.
#allow_zero_length_clientid true
# If allow_zero_length_clientid is true, this option allows you to set a prefix
# to automatically generated client ids to aid visibility in logs.
# Defaults to 'auto-'
#auto_id_prefix auto-
# This option affects the scenario when a client subscribes to a topic that has
# retained messages. It is possible that the client that published the retained
# message to the topic had access at the time they published, but that access
# has been subsequently removed. If check_retain_source is set to true, the
# default, the source of a retained message will be checked for access rights
# before it is republished. When set to false, no check will be made and the
# retained message will always be published. This affects all listeners.
#check_retain_source true
# QoS 1 and 2 messages will be allowed inflight per client until this limit
# is exceeded. Defaults to 0. (No maximum)
# See also max_inflight_messages
#max_inflight_bytes 0
# The maximum number of QoS 1 and 2 messages currently inflight per
# client.
# This includes messages that are partway through handshakes and
# those that are being retried. Defaults to 20. Set to 0 for no
# maximum. Setting to 1 will guarantee in-order delivery of QoS 1
# and 2 messages.
#max_inflight_messages 20
# For MQTT v5 clients, it is possible to have the server send a "server
# keepalive" value that will override the keepalive value set by the client.
# This is intended to be used as a mechanism to say that the server will
# disconnect the client earlier than it anticipated, and that the client should
# use the new keepalive value. The max_keepalive option allows you to specify
# that clients may only connect with keepalive less than or equal to this
# value, otherwise they will be sent a server keepalive telling them to use
# max_keepalive. This only applies to MQTT v5 clients. The maximum value
# allowable is 65535. Do not set below 10.
#max_keepalive 65535
# For MQTT v5 clients, it is possible to have the server send a "maximum packet
# size" value that will instruct the client it will not accept MQTT packets
# with size greater than max_packet_size bytes. This applies to the full MQTT
# packet, not just the payload. Setting this option to a positive value will
# set the maximum packet size to that number of bytes. If a client sends a
# packet which is larger than this value, it will be disconnected. This applies
# to all clients regardless of the protocol version they are using, but v3.1.1
# and earlier clients will of course not have received the maximum packet size
# information. Defaults to no limit. Setting below 20 bytes is forbidden
# because it is likely to interfere with ordinary client operation, even with
# very small payloads.
#max_packet_size 0
# QoS 1 and 2 messages above those currently in-flight will be queued per
# client until this limit is exceeded. Defaults to 0. (No maximum)
# See also max_queued_messages.
# If both max_queued_messages and max_queued_bytes are specified, packets will
# be queued until the first limit is reached.
#max_queued_bytes 0
# Set the maximum QoS supported. Clients publishing at a QoS higher than
# specified here will be disconnected.
#max_qos 2
# The maximum number of QoS 1 and 2 messages to hold in a queue per client
# above those that are currently in-flight. Defaults to 1000. Set
# to 0 for no maximum (not recommended).
# See also queue_qos0_messages.
# See also max_queued_bytes.
#max_queued_messages 1000
#
# This option sets the maximum number of heap memory bytes that the broker will
# allocate, and hence sets a hard limit on memory use by the broker. Memory
# requests that exceed this value will be denied. The effect will vary
# depending on what has been denied. If an incoming message is being processed,
# then the message will be dropped and the publishing client will be
# disconnected. If an outgoing message is being sent, then the individual
# message will be dropped and the receiving client will be disconnected.
# Defaults to no limit.
#memory_limit 0
# This option sets the maximum publish payload size that the broker will allow.
# Received messages that exceed this size will not be accepted by the broker.
# The default value is 0, which means that all valid MQTT messages are
# accepted. MQTT imposes a maximum payload size of 268435455 bytes.
#message_size_limit 0
# This option allows persistent clients (those with clean session set to false)
# to be removed if they do not reconnect within a certain time frame.
#
# This is a non-standard option in MQTT V3.1 but allowed in MQTT v3.1.1.
#
# Badly designed clients may set clean session to false whilst using a randomly
# generated client id. This leads to persistent clients that will never
# reconnect. This option allows these clients to be removed.
#
# The expiration period should be an integer followed by one of h d w m y for
# hour, day, week, month and year respectively. For example
#
# persistent_client_expiration 2m
# persistent_client_expiration 14d
# persistent_client_expiration 1y
#
# The default if not set is to never expire persistent clients.
#persistent_client_expiration
# Write process id to a file. Default is a blank string which means
# a pid file shouldn't be written.
# This should be set to /var/run/mosquitto/mosquitto.pid if mosquitto is
# being run automatically on boot with an init script and
# start-stop-daemon or similar.
#pid_file
# Set to true to queue messages with QoS 0 when a persistent client is
# disconnected. These messages are included in the limit imposed by
# max_queued_messages and max_queued_bytes
# Defaults to false.
# This is a non-standard option for the MQTT v3.1 spec but is allowed in
# v3.1.1.
#queue_qos0_messages false
# Set to false to disable retained message support. If a client publishes a
# message with the retain bit set, it will be disconnected if this is set to
# false.
#retain_available true
# Disable Nagle's algorithm on client sockets. This has the effect of reducing
# latency of individual messages at the potential cost of increasing the number
# of packets being sent.
#set_tcp_nodelay false
# Time in seconds between updates of the $SYS tree.
# Set to 0 to disable the publishing of the $SYS tree.
#sys_interval 10
# The MQTT specification requires that the QoS of a message delivered to a
# subscriber is never upgraded to match the QoS of the subscription. Enabling
# this option changes this behaviour. If upgrade_outgoing_qos is set true,
# messages sent to a subscriber will always match the QoS of its subscription.
# This is a non-standard option explicitly disallowed by the spec.
#upgrade_outgoing_qos false
# When run as root, drop privileges to this user and its primary
# group.
# Set to root to stay as root, but this is not recommended.
# If set to "mosquitto", or left unset, and the "mosquitto" user does not exist
# then it will drop privileges to the "nobody" user instead.
# If run as a non-root user, this setting has no effect.
# Note that on Windows this has no effect and so mosquitto should be started by
# the user you wish it to run as.
#user mosquitto
# =================================================================
# Listeners
# =================================================================
# Listen on a port/ip address combination. By using this variable
# multiple times, mosquitto can listen on more than one port. If
# this variable is used and neither bind_address nor port given,
# then the default listener will not be started.
# The port number to listen on must be given. Optionally, an ip
# address or host name may be supplied as a second argument. In
# this case, mosquitto will attempt to bind the listener to that
# address and so restrict access to the associated network and
# interface. By default, mosquitto will listen on all interfaces.
# Note that for a websockets listener it is not possible to bind to a host
# name.
#
# On systems that support Unix Domain Sockets, it is also possible
# to create a # Unix socket rather than opening a TCP socket. In
# this case, the port number should be set to 0 and a unix socket
# path must be provided, e.g.
# listener 0 /tmp/mosquitto.sock
#
# listener port-number [ip address/host name/unix socket path]
#listener
# By default, a listener will attempt to listen on all supported IP protocol
# versions. If you do not have an IPv4 or IPv6 interface you may wish to
# disable support for either of those protocol versions. In particular, note
# that due to the limitations of the websockets library, it will only ever
# attempt to open IPv6 sockets if IPv6 support is compiled in, and so will fail
# if IPv6 is not available.
#
# Set to `ipv4` to force the listener to only use IPv4, or set to `ipv6` to
# force the listener to only use IPv6. If you want support for both IPv4 and
# IPv6, then do not use the socket_domain option.
#
#socket_domain
# Bind the listener to a specific interface. This is similar to
# the [ip address/host name] part of the listener definition, but is useful
# when an interface has multiple addresses or the address may change. If used
# with the [ip address/host name] part of the listener definition, then the
# bind_interface option will take priority.
# Not available on Windows.
#
# Example: bind_interface eth0
#bind_interface
# When a listener is using the websockets protocol, it is possible to serve
# http data as well. Set http_dir to a directory which contains the files you
# wish to serve. If this option is not specified, then no normal http
# connections will be possible.
#http_dir
# The maximum number of client connections to allow. This is
# a per listener setting.
# Default is -1, which means unlimited connections.
# Note that other process limits mean that unlimited connections
# are not really possible. Typically the default maximum number of
# connections possible is around 1024.
#max_connections -1
# The listener can be restricted to operating within a topic hierarchy using
# the mount_point option. This is achieved be prefixing the mount_point string
# to all topics for any clients connected to this listener. This prefixing only
# happens internally to the broker; the client will not see the prefix.
#mount_point
# Choose the protocol to use when listening.
# This can be either mqtt or websockets.
# Certificate based TLS may be used with websockets, except that only the
# cafile, certfile, keyfile, ciphers, and ciphers_tls13 options are supported.
#protocol mqtt
# Set use_username_as_clientid to true to replace the clientid that a client
# connected with with its username. This allows authentication to be tied to
# the clientid, which means that it is possible to prevent one client
# disconnecting another by using the same clientid.
# If a client connects with no username it will be disconnected as not
# authorised when this option is set to true.
# Do not use in conjunction with clientid_prefixes.
# See also use_identity_as_username.
#use_username_as_clientid
# Change the websockets headers size. This is a global option, it is not
# possible to set per listener. This option sets the size of the buffer used in
# the libwebsockets library when reading HTTP headers. If you are passing large
# header data such as cookies then you may need to increase this value. If left
# unset, or set to 0, then the default of 1024 bytes will be used.
#websockets_headers_size
# -----------------------------------------------------------------
# Certificate based SSL/TLS support
# -----------------------------------------------------------------
# The following options can be used to enable certificate based SSL/TLS support
# for this listener. Note that the recommended port for MQTT over TLS is 8883,
# but this must be set manually.
#
# See also the mosquitto-tls man page and the "Pre-shared-key based SSL/TLS
# support" section. Only one of certificate or PSK encryption support can be
# enabled for any listener.
# Both of certfile and keyfile must be defined to enable certificate based
# TLS encryption.
# Path to the PEM encoded server certificate.
#certfile
# Path to the PEM encoded keyfile.
#keyfile
# If you wish to control which encryption ciphers are used, use the ciphers
# option. The list of available ciphers can be optained using the "openssl
# ciphers" command and should be provided in the same format as the output of
# that command. This applies to TLS 1.2 and earlier versions only. Use
# ciphers_tls1.3 for TLS v1.3.
#ciphers
# Choose which TLS v1.3 ciphersuites are used for this listener.
# Defaults to "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256"
#ciphers_tls1.3
# If you have require_certificate set to true, you can create a certificate
# revocation list file to revoke access to particular client certificates. If
# you have done this, use crlfile to point to the PEM encoded revocation file.
#crlfile
# To allow the use of ephemeral DH key exchange, which provides forward
# security, the listener must load DH parameters. This can be specified with
# the dhparamfile option. The dhparamfile can be generated with the command
# e.g. "openssl dhparam -out dhparam.pem 2048"
#dhparamfile
# By default an TLS enabled listener will operate in a similar fashion to a
# https enabled web server, in that the server has a certificate signed by a CA
# and the client will verify that it is a trusted certificate. The overall aim
# is encryption of the network traffic. By setting require_certificate to true,
# the client must provide a valid certificate in order for the network
# connection to proceed. This allows access to the broker to be controlled
# outside of the mechanisms provided by MQTT.
#require_certificate false
# cafile and capath define methods of accessing the PEM encoded
# Certificate Authority certificates that will be considered trusted when
# checking incoming client certificates.
# cafile defines the path to a file containing the CA certificates.
# capath defines a directory that will be searched for files
# containing the CA certificates. For capath to work correctly, the
# certificate files must have ".crt" as the file ending and you must run
# "openssl rehash <path to capath>" each time you add/remove a certificate.
#cafile
#capath
# If require_certificate is true, you may set use_identity_as_username to true
# to use the CN value from the client certificate as a username. If this is
# true, the password_file option will not be used for this listener.
#use_identity_as_username false
# -----------------------------------------------------------------
# Pre-shared-key based SSL/TLS support
# -----------------------------------------------------------------
# The following options can be used to enable PSK based SSL/TLS support for
# this listener. Note that the recommended port for MQTT over TLS is 8883, but
# this must be set manually.
#
# See also the mosquitto-tls man page and the "Certificate based SSL/TLS
# support" section. Only one of certificate or PSK encryption support can be
# enabled for any listener.
# The psk_hint option enables pre-shared-key support for this listener and also
# acts as an identifier for this listener. The hint is sent to clients and may
# be used locally to aid authentication. The hint is a free form string that
# doesn't have much meaning in itself, so feel free to be creative.
# If this option is provided, see psk_file to define the pre-shared keys to be
# used or create a security plugin to handle them.
#psk_hint
# When using PSK, the encryption ciphers used will be chosen from the list of
# available PSK ciphers. If you want to control which ciphers are available,
# use the "ciphers" option. The list of available ciphers can be optained
# using the "openssl ciphers" command and should be provided in the same format
# as the output of that command.
#ciphers
# Set use_identity_as_username to have the psk identity sent by the client used
# as its username. Authentication will be carried out using the PSK rather than
# the MQTT username/password and so password_file will not be used for this
# listener.
#use_identity_as_username false
# =================================================================
# Persistence
# =================================================================
# If persistence is enabled, save the in-memory database to disk
# every autosave_interval seconds. If set to 0, the persistence
# database will only be written when mosquitto exits. See also
# autosave_on_changes.
# Note that writing of the persistence database can be forced by
# sending mosquitto a SIGUSR1 signal.
#autosave_interval 1800
# If true, mosquitto will count the number of subscription changes, retained
# messages received and queued messages and if the total exceeds
# autosave_interval then the in-memory database will be saved to disk.
# If false, mosquitto will save the in-memory database to disk by treating
# autosave_interval as a time in seconds.
#autosave_on_changes false
# Save persistent message data to disk (true/false).
# This saves information about all messages, including
# subscriptions, currently in-flight messages and retained
# messages.
# retained_persistence is a synonym for this option.
#persistence false
# The filename to use for the persistent database, not including
# the path.
#persistence_file mosquitto.db
# Location for persistent database.
# Default is an empty string (current directory).
# Set to e.g. /var/lib/mosquitto if running as a proper service on Linux or
# similar.
#persistence_location
# =================================================================
# Logging
# =================================================================
# Places to log to. Use multiple log_dest lines for multiple
# logging destinations.
# Possible destinations are: stdout stderr syslog topic file dlt
#
# stdout and stderr log to the console on the named output.
#
# syslog uses the userspace syslog facility which usually ends up
# in /var/log/messages or similar.
#
# topic logs to the broker topic '$SYS/broker/log/<severity>',
# where severity is one of D, E, W, N, I, M which are debug, error,
# warning, notice, information and message. Message type severity is used by
# the subscribe/unsubscribe log_types and publishes log messages to
# $SYS/broker/log/M/susbcribe or $SYS/broker/log/M/unsubscribe.
#
# The file destination requires an additional parameter which is the file to be
# logged to, e.g. "log_dest file /var/log/mosquitto.log". The file will be
# closed and reopened when the broker receives a HUP signal. Only a single file
# destination may be configured.
#
# The dlt destination is for the automotive `Diagnostic Log and Trace` tool.
# This requires that Mosquitto has been compiled with DLT support.
#
# Note that if the broker is running as a Windows service it will default to
# "log_dest none" and neither stdout nor stderr logging is available.
# Use "log_dest none" if you wish to disable logging.
#log_dest stderr
# Types of messages to log. Use multiple log_type lines for logging
# multiple types of messages.
# Possible types are: debug, error, warning, notice, information,
# none, subscribe, unsubscribe, websockets, all.
# Note that debug type messages are for decoding the incoming/outgoing
# network packets. They are not logged in "topics".
#log_type error
#log_type warning
#log_type notice
#log_type information
log_type all
# If set to true, client connection and disconnection messages will be included
# in the log.
connection_messages true
# If using syslog logging (not on Windows), messages will be logged to the
# "daemon" facility by default. Use the log_facility option to choose which of
# local0 to local7 to log to instead. The option value should be an integer
# value, e.g. "log_facility 5" to use local5.
#log_facility
# If set to true, add a timestamp value to each log message.
log_timestamp true
# Set the format of the log timestamp. If left unset, this is the number of
# seconds since the Unix epoch.
# This is a free text string which will be passed to the strftime function. To
# get an ISO 8601 datetime, for example:
# log_timestamp_format %Y-%m-%dT%H:%M:%S
#log_timestamp_format
# Change the websockets logging level. This is a global option, it is not
# possible to set per listener. This is an integer that is interpreted by
# libwebsockets as a bit mask for its lws_log_levels enum. See the
# libwebsockets documentation for more details. "log_type websockets" must also
# be enabled.
#websockets_log_level 0
# =================================================================
# Security
# =================================================================
# If set, only clients that have a matching prefix on their
# clientid will be allowed to connect to the broker. By default,
# all clients may connect.
# For example, setting "secure-" here would mean a client "secure-
# client" could connect but another with clientid "mqtt" couldn't.
#clientid_prefixes
# Boolean value that determines whether clients that connect
# without providing a username are allowed to connect. If set to
# false then a password file should be created (see the
# password_file option) to control authenticated client access.
#
# Defaults to false, unless there are no listeners defined in the configuration
# file, in which case it is set to true, but connections are only allowed from
# the local machine.
allow_anonymous true
# -----------------------------------------------------------------
# Default authentication and topic access control
# -----------------------------------------------------------------
# Control access to the broker using a password file. This file can be
# generated using the mosquitto_passwd utility. If TLS support is not compiled
# into mosquitto (it is recommended that TLS support should be included) then
# plain text passwords are used, in which case the file should be a text file
# with lines in the format:
# username:password
# The password (and colon) may be omitted if desired, although this
# offers very little in the way of security.
#
# See the TLS client require_certificate and use_identity_as_username options
# for alternative authentication options. If an auth_plugin is used as well as
# password_file, the auth_plugin check will be made first.
#password_file
# Access may also be controlled using a pre-shared-key file. This requires
# TLS-PSK support and a listener configured to use it. The file should be text
# lines in the format:
# identity:key
# The key should be in hexadecimal format without a leading "0x".
# If an auth_plugin is used as well, the auth_plugin check will be made first.
#psk_file
# Control access to topics on the broker using an access control list
# file. If this parameter is defined then only the topics listed will
# have access.
# If the first character of a line of the ACL file is a # it is treated as a
# comment.
# Topic access is added with lines of the format:
#
# topic [read|write|readwrite|deny] <topic>
#
# The access type is controlled using "read", "write", "readwrite" or "deny".
# This parameter is optional (unless <topic> contains a space character) - if
# not given then the access is read/write. <topic> can contain the + or #
# wildcards as in subscriptions.
#
# The "deny" option can used to explicity deny access to a topic that would
# otherwise be granted by a broader read/write/readwrite statement. Any "deny"
# topics are handled before topics that grant read/write access.
#
# The first set of topics are applied to anonymous clients, assuming
# allow_anonymous is true. User specific topic ACLs are added after a
# user line as follows:
#
# user <username>
#
# The username referred to here is the same as in password_file. It is
# not the clientid.
#
#
# If is also possible to define ACLs based on pattern substitution within the
# topic. The patterns available for substition are:
#
# %c to match the client id of the client
# %u to match the username of the client
#
# The substitution pattern must be the only text for that level of hierarchy.
#
# The form is the same as for the topic keyword, but using pattern as the
# keyword.
# Pattern ACLs apply to all users even if the "user" keyword has previously
# been given.
#
# If using bridges with usernames and ACLs, connection messages can be allowed
# with the following pattern:
# pattern write $SYS/broker/connection/%c/state
#
# pattern [read|write|readwrite] <topic>
#
# Example:
#
# pattern write sensor/%u/data
#
# If an auth_plugin is used as well as acl_file, the auth_plugin check will be
# made first.
#acl_file
# -----------------------------------------------------------------
# External authentication and topic access plugin options
# -----------------------------------------------------------------
# External authentication and access control can be supported with the
# auth_plugin option. This is a path to a loadable plugin. See also the
# auth_opt_* options described below.
#
# The auth_plugin option can be specified multiple times to load multiple
# plugins. The plugins will be processed in the order that they are specified
# here. If the auth_plugin option is specified alongside either of
# password_file or acl_file then the plugin checks will be made first.
#
#auth_plugin
# If the auth_plugin option above is used, define options to pass to the
# plugin here as described by the plugin instructions. All options named
# using the format auth_opt_* will be passed to the plugin, for example:
#
# auth_opt_db_host
# auth_opt_db_port
# auth_opt_db_username
# auth_opt_db_password
# =================================================================
# Bridges
# =================================================================
# A bridge is a way of connecting multiple MQTT brokers together.
# Create a new bridge using the "connection" option as described below. Set
# options for the bridges using the remaining parameters. You must specify the
# address and at least one topic to subscribe to.
#
# Each connection must have a unique name.
#
# The address line may have multiple host address and ports specified. See
# below in the round_robin description for more details on bridge behaviour if
# multiple addresses are used. Note that if you use an IPv6 address, then you
# are required to specify a port.
#
# The direction that the topic will be shared can be chosen by
# specifying out, in or both, where the default value is out.
# The QoS level of the bridged communication can be specified with the next
# topic option. The default QoS level is 0, to change the QoS the topic
# direction must also be given.
#
# The local and remote prefix options allow a topic to be remapped when it is
# bridged to/from the remote broker. This provides the ability to place a topic
# tree in an appropriate location.
#
# For more details see the mosquitto.conf man page.
#
# Multiple topics can be specified per connection, but be careful
# not to create any loops.
#
# If you are using bridges with cleansession set to false (the default), then
# you may get unexpected behaviour from incoming topics if you change what
# topics you are subscribing to. This is because the remote broker keeps the
# subscription for the old topic. If you have this problem, connect your bridge
# with cleansession set to true, then reconnect with cleansession set to false
# as normal.
#connection <name>
#address <host>[:<port>] [<host>[:<port>]]
#topic <topic> [[[out | in | both] qos-level] local-prefix remote-prefix]
# If you need to have the bridge connect over a particular network interface,
# use bridge_bind_address to tell the bridge which local IP address the socket
# should bind to, e.g. `bridge_bind_address 192.168.1.10`
#bridge_bind_address
# If a bridge has topics that have "out" direction, the default behaviour is to
# send an unsubscribe request to the remote broker on that topic. This means
# that changing a topic direction from "in" to "out" will not keep receiving
# incoming messages. Sending these unsubscribe requests is not always
# desirable, setting bridge_attempt_unsubscribe to false will disable sending
# the unsubscribe request.
#bridge_attempt_unsubscribe true
# Set the version of the MQTT protocol to use with for this bridge. Can be one
# of mqttv50, mqttv311 or mqttv31. Defaults to mqttv311.
#bridge_protocol_version mqttv311
# Set the clean session variable for this bridge.
# When set to true, when the bridge disconnects for any reason, all
# messages and subscriptions will be cleaned up on the remote
# broker. Note that with cleansession set to true, there may be a
# significant amount of retained messages sent when the bridge
# reconnects after losing its connection.
# When set to false, the subscriptions and messages are kept on the
# remote broker, and delivered when the bridge reconnects.
#cleansession false
# Set the amount of time a bridge using the lazy start type must be idle before
# it will be stopped. Defaults to 60 seconds.
#idle_timeout 60
# Set the keepalive interval for this bridge connection, in
# seconds.
#keepalive_interval 60
# Set the clientid to use on the local broker. If not defined, this defaults to
# 'local.<clientid>'. If you are bridging a broker to itself, it is important
# that local_clientid and clientid do not match.
#local_clientid
# If set to true, publish notification messages to the local and remote brokers
# giving information about the state of the bridge connection. Retained
# messages are published to the topic $SYS/broker/connection/<clientid>/state
# unless the notification_topic option is used.
# If the message is 1 then the connection is active, or 0 if the connection has
# failed.
# This uses the last will and testament feature.
#notifications true
# Choose the topic on which notification messages for this bridge are
# published. If not set, messages are published on the topic
# $SYS/broker/connection/<clientid>/state
#notification_topic
# Set the client id to use on the remote end of this bridge connection. If not
# defined, this defaults to 'name.hostname' where name is the connection name
# and hostname is the hostname of this computer.
# This replaces the old "clientid" option to avoid confusion. "clientid"
# remains valid for the time being.
#remote_clientid
# Set the password to use when connecting to a broker that requires
# authentication. This option is only used if remote_username is also set.
# This replaces the old "password" option to avoid confusion. "password"
# remains valid for the time being.
#remote_password
# Set the username to use when connecting to a broker that requires
# authentication.
# This replaces the old "username" option to avoid confusion. "username"
# remains valid for the time being.
#remote_username
# Set the amount of time a bridge using the automatic start type will wait
# until attempting to reconnect.
# This option can be configured to use a constant delay time in seconds, or to
# use a backoff mechanism based on "Decorrelated Jitter", which adds a degree
# of randomness to when the restart occurs.
#
# Set a constant timeout of 20 seconds:
# restart_timeout 20
#
# Set backoff with a base (start value) of 10 seconds and a cap (upper limit) of
# 60 seconds:
# restart_timeout 10 30
#
# Defaults to jitter with a base of 5 and cap of 30
#restart_timeout 5 30
# If the bridge has more than one address given in the address/addresses
# configuration, the round_robin option defines the behaviour of the bridge on
# a failure of the bridge connection. If round_robin is false, the default
# value, then the first address is treated as the main bridge connection. If
# the connection fails, the other secondary addresses will be attempted in
# turn. Whilst connected to a secondary bridge, the bridge will periodically
# attempt to reconnect to the main bridge until successful.
# If round_robin is true, then all addresses are treated as equals. If a
# connection fails, the next address will be tried and if successful will
# remain connected until it fails
#round_robin false
# Set the start type of the bridge. This controls how the bridge starts and
# can be one of three types: automatic, lazy and once. Note that RSMB provides
# a fourth start type "manual" which isn't currently supported by mosquitto.
#
# "automatic" is the default start type and means that the bridge connection
# will be started automatically when the broker starts and also restarted
# after a short delay (30 seconds) if the connection fails.
#
# Bridges using the "lazy" start type will be started automatically when the
# number of queued messages exceeds the number set with the "threshold"
# parameter. It will be stopped automatically after the time set by the
# "idle_timeout" parameter. Use this start type if you wish the connection to
# only be active when it is needed.
#
# A bridge using the "once" start type will be started automatically when the
# broker starts but will not be restarted if the connection fails.
#start_type automatic
# Set the number of messages that need to be queued for a bridge with lazy
# start type to be restarted. Defaults to 10 messages.
# Must be less than max_queued_messages.
#threshold 10
# If try_private is set to true, the bridge will attempt to indicate to the
# remote broker that it is a bridge not an ordinary client. If successful, this
# means that loop detection will be more effective and that retained messages
# will be propagated correctly. Not all brokers support this feature so it may
# be necessary to set try_private to false if your bridge does not connect
# properly.
#try_private true
# Some MQTT brokers do not allow retained messages. MQTT v5 gives a mechanism
# for brokers to tell clients that they do not support retained messages, but
# this is not possible for MQTT v3.1.1 or v3.1. If you need to bridge to a
# v3.1.1 or v3.1 broker that does not support retained messages, set the
# bridge_outgoing_retain option to false. This will remove the retain bit on
# all outgoing messages to that bridge, regardless of any other setting.
#bridge_outgoing_retain true
# If you wish to restrict the size of messages sent to a remote bridge, use the
# bridge_max_packet_size option. This sets the maximum number of bytes for
# the total message, including headers and payload.
# Note that MQTT v5 brokers may provide their own maximum-packet-size property.
# In this case, the smaller of the two limits will be used.
# Set to 0 for "unlimited".
#bridge_max_packet_size 0
# -----------------------------------------------------------------
# Certificate based SSL/TLS support
# -----------------------------------------------------------------
# Either bridge_cafile or bridge_capath must be defined to enable TLS support
# for this bridge.
# bridge_cafile defines the path to a file containing the
# Certificate Authority certificates that have signed the remote broker
# certificate.
# bridge_capath defines a directory that will be searched for files containing
# the CA certificates. For bridge_capath to work correctly, the certificate
# files must have ".crt" as the file ending and you must run "openssl rehash
# <path to capath>" each time you add/remove a certificate.
#bridge_cafile
#bridge_capath
# If the remote broker has more than one protocol available on its port, e.g.
# MQTT and WebSockets, then use bridge_alpn to configure which protocol is
# requested. Note that WebSockets support for bridges is not yet available.
#bridge_alpn
# When using certificate based encryption, bridge_insecure disables
# verification of the server hostname in the server certificate. This can be
# useful when testing initial server configurations, but makes it possible for
# a malicious third party to impersonate your server through DNS spoofing, for
# example. Use this option in testing only. If you need to resort to using this
# option in a production environment, your setup is at fault and there is no
# point using encryption.
#bridge_insecure false
# Path to the PEM encoded client certificate, if required by the remote broker.
#bridge_certfile
# Path to the PEM encoded client private key, if required by the remote broker.
#bridge_keyfile
# -----------------------------------------------------------------
# PSK based SSL/TLS support
# -----------------------------------------------------------------
# Pre-shared-key encryption provides an alternative to certificate based
# encryption. A bridge can be configured to use PSK with the bridge_identity
# and bridge_psk options. These are the client PSK identity, and pre-shared-key
# in hexadecimal format with no "0x". Only one of certificate and PSK based
# encryption can be used on one
# bridge at once.
#bridge_identity
#bridge_psk
# =================================================================
# External config files
# =================================================================
# External configuration files may be included by using the
# include_dir option. This defines a directory that will be searched
# for config files. All files that end in '.conf' will be loaded as
# a configuration file. It is best to have this as the last option
# in the main file. This option will only be processed from the main
# configuration file. The directory specified must not contain the
# main configuration file.
# Files within include_dir will be loaded sorted in case-sensitive
# alphabetical order, with capital letters ordered first. If this option is
# given multiple times, all of the files from the first instance will be
# processed before the next instance. See the man page for examples.
#include_dir

View File

@@ -0,0 +1,32 @@
# Copyright (C) 2022 Tencent Technologies Limited.
# All rights reserved.
#
set(target ql_qcloud_iot)
add_library(${target} STATIC)
set_target_properties(${target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${out_app_lib_dir})
target_compile_definitions(${target} PRIVATE OSI_LOG_TAG=LOG_TAG_QCLOUD_IOT)
set(PLATFORM "quectel")
set(IOT_SDK_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/iot_c_sdk")
include(${CMAKE_CURRENT_SOURCE_DIR}/config/iot_ql_sdk_setting.cmake)
target_include_directories(${target} PUBLIC
${IOT_SDK_SOURCE_DIR}/include/
${IOT_SDK_SOURCE_DIR}/include/common
${IOT_SDK_SOURCE_DIR}/include/config
${IOT_SDK_SOURCE_DIR}/include/services/common
${IOT_SDK_SOURCE_DIR}/include/services/hub
${IOT_SDK_SOURCE_DIR}/include/services/explorer
${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/mbedtls/include
${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/port/inc
${inc_mbedtls_port}
${inc_common}
${inc_platform}
${inc_services}
)
target_sources(${target} PRIVATE qcloud_iot_mqtt_demo.c ${src_mbedtls_port} ${src_common} ${src_platform} ${src_services})
relative_glob(srcs include/*.h src/*.c inc/*.h)
beautify_c_code(${target} ${srcs})

View File

@@ -0,0 +1,146 @@
###################### CONFIG #####################################
# 开启单元测试
set(CONFIG_IOT_TEST OFF)
# 开启示例编译
set(CONFIG_COMPILE_SAMPLE OFF)
# 打开IOT DEBUG
set(CONFIG_IOT_DEBUG OFF)
# 代码抽取ON表示根据配置抽取源码到ouput/sdk目录
set(CONFIG_EXTRACT_SRC OFF)
# 接入认证方式使用证书认证CERT使用密钥认证KEY
set(CONFIG_AUTH_MODE "KEY")
# 接入认证是否不使用TLS证书方式必须选择使用TLS密钥认证可选择不使用TLS
set(CONFIG_AUTH_WITH_NOTLS OFF)
# 使用用户自己的mbedtls库
set(CONFIG_USR_MBEDTLS ON)
# 是否打开代码中获取设备信息功能OFF时将从device_info.json中读取设备信息
set(CONFIG_DEBUG_DEV_INFO_USED ON)
# 是否使能多线程
set(CONFIG_MULTITHREAD_ENABLED ON)
# 使用JSON格式上报日志 默认使用text格式
set(CONFIG_LOG_UPLOAD_TYPE_JSON ON)
# 使用AES加密上报日志
set(CONFIG_LOG_UPLOAD_AES_ENCRYPT_POST OFF)
# 是否使用动态注册功能
set(CONFIG_DEV_DYN_REG_ENABLED ON)
option(IOT_DEBUG "Enable IOT_DEBUG" ${CONFIG_IOT_DEBUG})
option(DEBUG_DEV_INFO_USED "Enable DEBUG_DEV_INFO_USED" ${CONFIG_DEBUG_DEV_INFO_USED})
option(AUTH_WITH_NO_TLS "Enable AUTH_WITH_NO_TLS" ${CONFIG_AUTH_WITH_NOTLS})
option(LOG_UPLOAD_TYPE_JSON "Enable LOG_UPLOAD_DEBUG" ${CONFIG_LOG_UPLOAD_TYPE_JSON})
option(LOG_UPLOAD_AES_ENCRYPT_POST "Log upload with AES encrypt" ${CONFIG_LOG_UPLOAD_AES_ENCRYPT_POST})
option(DEV_DYN_REG_ENABLED "Enable DEV_DYN_REG_ENABLED" ${CONFIG_DEV_DYN_REG_ENABLED})
option(MULTITHREAD_ENABLED "Enable AUTH_WITH_NO_TLS" ${CONFIG_MULTITHREAD_ENABLED})
if(${CONFIG_AUTH_MODE} STREQUAL "KEY")
option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" ON)
option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" OFF)
elseif(${CONFIG_AUTH_MODE} STREQUAL "CERT" AND ${CONFIG_AUTH_WITH_NOTLS} STREQUAL "OFF")
option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" OFF)
option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" ON)
else()
message(FATAL_ERROR "INVAILD AUTH_MODE:${FEATURE_AUTH_MODE} WITH AUTH_WITH_NO_TLS:${FEATURE_AUTH_WITH_NOTLS}!")
endif()
configure_file (
"${IOT_SDK_SOURCE_DIR}/config/settings/qcloud_iot_config.h.in"
"${IOT_SDK_SOURCE_DIR}/include/config/qcloud_iot_config.h"
@ONLY
)
# export include
include_directories(
${IOT_SDK_SOURCE_DIR}/include/
${IOT_SDK_SOURCE_DIR}/include/common
${IOT_SDK_SOURCE_DIR}/include/config
${IOT_SDK_SOURCE_DIR}/include/services/common
${IOT_SDK_SOURCE_DIR}/include/services/hub
${IOT_SDK_SOURCE_DIR}/include/services/explorer
)
###################### PLATFORM MODULE #######################################
set(src_platform CACHE INTERNAL "")
set(inc_platform CACHE INTERNAL "")
add_subdirectory(${IOT_SDK_SOURCE_DIR}/platform)
# set include
include_directories(${inc_platform})
###################### COMMON MODULE #######################################
set(src_common CACHE INTERNAL "")
set(inc_common CACHE INTERNAL "")
# mqtt packet
add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/mqtt_packet)
# utils
add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/utils)
# cryptology
add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/cryptology)
# set include
include_directories(${inc_common})
###################### SERVICE MODULE ####################################
set(src_services CACHE INTERNAL "")
set(inc_services CACHE INTERNAL "")
# mqtt client (must include except dynamic register)
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/mqtt_client)
## MQTT ONLY
# 是否打开广播功能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/hub/broadcast)
# 是否使能获取iot后台时间功能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/system)
## HTTP ONLY
# 是否使能设备动态注册
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/dynreg)
## MQTT & HTTP
# 是否使能网关功能
#add_subdirectory()
# 是否使能OTA固件升级功能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/ota)
# 是否使能资源管理功能
#add_subdirectory()
# 是否使能HTTP请求腾讯云后台功能 如果开启了日志上报和动态注册 则必须要使能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/http_signed)
# 是否使能日志上报云端功能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/log_upload)
# set include
include_directories(${inc_services})
###################### MBEDTLS MODULE ####################################
if(${CONFIG_AUTH_WITH_NOTLS} STREQUAL "OFF" AND ${CONFIG_USR_MBEDTLS} STREQUAL "OFF")
add_definitions("-DMBEDTLS_CONFIG_FILE=\"qcloud_iot_tls_psk_config.h\"")
add_subdirectory(${IOT_SDK_SOURCE_DIR}/3rd/mbedtls)
set(src_mbedtls_port "")
set(int_mbedtls_port "")
else(${CONFIG_USR_MBEDTLS} STREQUAL "ON")
set(src_mbedtls_port ${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/port/src/qcloud_iot_tls_client.c)
set(int_mbedtls_port ${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/port/inc/)
endif()

View File

@@ -0,0 +1,278 @@
/**
* @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 mqtt_sample.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2022-03-08
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022-03-08 <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->event_handle.h_fp = _mqtt_event_handler;
}
/**
* @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
// ----------------------------------------------------------------------------
int qcloud_iot_mqtt_demo(int argc, char **argv)
{
int rc;
// init log level
LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
DeviceInfo device_info;
rc = HAL_GetDevInfo(&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 (1);
rc = _unsubscribe_topic(client, "data");
rc |= IOT_MQTT_Destroy(&client);
utils_log_deinit();
return rc;
}

View File

@@ -0,0 +1,57 @@
# 移远SDK适配
1. 将该目录的内容拷贝到`ql-application\qcloud-iot-c-sdk`并将sdk放置在`ql-application\qcloud-iot-c-sdk\iot_c_sdk`目录下
```c
CMakeLists.txt
config
iot_c_sdk
qcloud_iot_mqtt_demo.c
```
2. 修改相应的编译脚本
- `core_export.list` 添加相应的函数
```c
//md5
mbedtls_md5_init
mbedtls_md5_starts
mbedtls_md5_update
mbedtls_md5_finish
//
mbedtls_ctr_drbg_free
mbedtls_entropy_free
mbedtls_ctr_drbg_init
mbedtls_entropy_init
mbedtls_ctr_drbg_seed
mbedtls_entropy_func
mbedtls_ctr_drbg_random
```
- `ql-application/CMakeLists.txt`修改
```cmake
if(QL_APP_FEATURE_QCLOUD_IOT)
add_subdirectory_if_exist(qcloud-iot-c-sdk)
endif()
```
- `ql-application/init/ql_init.c`修改
```c
#ifdef QL_APP_FEATURE_QCLOUD_IOT
#include "qcloud_iot_common.h"
#include "qcloud_iot_hub.h"
#include "qcloud_iot_explorer.h"
#endif
```
```c
#ifdef QL_APP_FEATURE_QCLOUD_IOT
//your application entry
extern int qcloud_iot_mqtt_demo(int argc, char **argv);
qcloud_iot_mqtt_demo(0, NULL);
#endif
```

View File

@@ -0,0 +1,157 @@
# ##################### CONFIG #####################################
# 开启单元测试
set(CONFIG_IOT_TEST OFF)
# 开启示例编译
set(CONFIG_COMPILE_SAMPLE ON)
# 打开IOT DEBUG
set(CONFIG_IOT_DEBUG OFF)
# 代码抽取ON表示根据配置抽取源码到ouput/sdk目录
set(CONFIG_EXTRACT_SRC ON)
# 接入认证方式使用证书认证CERT使用密钥认证KEY
set(CONFIG_AUTH_MODE "KEY")
# 接入认证是否不使用TLS证书方式必须选择使用TLS密钥认证可选择不使用TLS
set(CONFIG_AUTH_WITH_NOTLS OFF)
# 是否打开代码中获取设备信息功能OFF时将从device_info.json中读取设备信息
set(CONFIG_DEBUG_DEV_INFO_USED ON)
# 是否使能多线程
set(CONFIG_MULTITHREAD_ENABLED OFF)
# 使用JSON格式上报日志 默认使用text格式
set(CONFIG_LOG_UPLOAD_TYPE_JSON OFF)
# 使用AES加密上报日志
set(CONFIG_LOG_UPLOAD_AES_ENCRYPT_POST OFF)
# 是否使用动态注册功能
set(CONFIG_DEV_DYN_REG_ENABLED ON)
# 是否打开获取iot后台时间功能
# Whether to access the feature of getting iot background time
set(FEATURE_SYSTEM_COMM_ENABLED ON)
option(IOT_DEBUG "Enable IOT_DEBUG" ${CONFIG_IOT_DEBUG})
option(DEBUG_DEV_INFO_USED "Enable DEBUG_DEV_INFO_USED" ${CONFIG_DEBUG_DEV_INFO_USED})
option(AUTH_WITH_NO_TLS "Enable AUTH_WITH_NO_TLS" ${CONFIG_AUTH_WITH_NOTLS})
option(LOG_UPLOAD_TYPE_JSON "Enable LOG_UPLOAD_DEBUG" ${CONFIG_LOG_UPLOAD_TYPE_JSON})
option(LOG_UPLOAD_AES_ENCRYPT_POST "Log upload with AES encrypt" ${CONFIG_LOG_UPLOAD_AES_ENCRYPT_POST})
option(SYSTEM_COMM "Enable SYSTEM_COMM" ${FEATURE_SYSTEM_COMM_ENABLED})
option(DEV_DYN_REG_ENABLED "Enable DEV_DYN_REG_ENABLED" ${CONFIG_DEV_DYN_REG_ENABLED})
if(${CONFIG_AUTH_MODE} STREQUAL "KEY")
option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" ON)
option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" OFF)
elseif(${CONFIG_AUTH_MODE} STREQUAL "CERT" AND ${CONFIG_AUTH_WITH_NOTLS} STREQUAL "OFF")
option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" OFF)
option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" ON)
else()
message(FATAL_ERROR "INVAILD AUTH_MODE:${FEATURE_AUTH_MODE} WITH AUTH_WITH_NO_TLS:${FEATURE_AUTH_WITH_NOTLS}!")
endif()
configure_file(
"${IOT_SDK_SOURCE_DIR}/config/settings/qcloud_iot_config.h.in"
"${IOT_SDK_SOURCE_DIR}/include/config/qcloud_iot_config.h"
@ONLY
)
# export include
include_directories(
${IOT_SDK_SOURCE_DIR}/include/
${IOT_SDK_SOURCE_DIR}/include/common
${IOT_SDK_SOURCE_DIR}/include/config
${IOT_SDK_SOURCE_DIR}/include/services/common
${IOT_SDK_SOURCE_DIR}/include/services/explorer
)
# set output path
set(LIBRARY_OUTPUT_PATH ${IOT_SDK_SOURCE_DIR}/output/libs)
set(EXECUTABLE_OUTPUT_PATH ${IOT_SDK_SOURCE_DIR}/output/bin)
# set link lib dir
link_directories(${LIBRARY_OUTPUT_PATH})
# set test src
if(${CONFIG_IOT_TEST} STREQUAL "ON")
set(src_test CACHE INTERNAL "")
set(inc_test CACHE INTERNAL "")
endif()
# ##################### PLATFORM MODULE #######################################
set(src_platform CACHE INTERNAL "")
set(inc_platform CACHE INTERNAL "")
add_subdirectory(${IOT_SDK_SOURCE_DIR}/platform)
include_directories(${inc_platform})
add_library(iot_platform STATIC ${src_platform})
# ##################### COMMON MODULE #######################################
set(src_common CACHE INTERNAL "")
set(inc_common CACHE INTERNAL "")
add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/mqtt_packet)
add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/utils)
add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/cryptology)
include_directories(${inc_common})
add_library(iot_common STATIC ${src_common})
# ##################### 3rd MODULE ####################################
# mbedtls
if(${CONFIG_AUTH_MODE} STREQUAL "KEY")
include_directories(
${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/mbedtls/include
${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/port/inc
)
add_definitions("-DMBEDTLS_CONFIG_FILE=\"qcloud_iot_tls_psk_config.h\"")
endif()
if(${CONFIG_AUTH_WITH_NOTLS} STREQUAL "OFF")
add_subdirectory(${IOT_SDK_SOURCE_DIR}/3rd/mbedtls)
set(libsdk ${libsdk} mbedtls)
endif()
# ##################### SERVICE MODULE ####################################
set(src_services CACHE INTERNAL "")
set(inc_services CACHE INTERNAL "")
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/mqtt_client)
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/http_client)
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/http_signed)
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/cos)
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/system)
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/log_upload)
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/dynreg)
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/ota)
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/gateway)
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/explorer/data_template)
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/explorer/service_mqtt)
include_directories(${inc_services})
add_library(iot_services STATIC ${src_services})
# ##################### APP ####################################
add_subdirectory(${IOT_SDK_SOURCE_DIR}/app/data_template)
add_subdirectory(${IOT_SDK_SOURCE_DIR}/app/ota)
# ##################### EXTRACT ####################################
if(${CONFIG_EXTRACT_SRC} STREQUAL "ON")
file(COPY ${src_platform} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
file(COPY ${inc_platform} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
file(COPY ${src_common} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
file(COPY ${inc_common} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
file(COPY ${src_services} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
file(COPY ${inc_services} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
file(GLOB inc_export
${IOT_SDK_SOURCE_DIR}/include/*.h
${IOT_SDK_SOURCE_DIR}/include/common/*.h
${IOT_SDK_SOURCE_DIR}/include/config/*.h
${IOT_SDK_SOURCE_DIR}/include/services/common/*.h
${IOT_SDK_SOURCE_DIR}/include/services/explorer/*.h
)
file(COPY ${inc_export} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc)
endif()

View File

@@ -0,0 +1,127 @@
# ##################### CONFIG #####################################
# 开启单元测试
set(CONFIG_IOT_TEST OFF)
# 开启示例编译
set(CONFIG_COMPILE_SAMPLE OFF)
# 打开IOT DEBUG
set(CONFIG_IOT_DEBUG OFF)
# 代码抽取ON表示根据配置抽取源码到ouput/sdk目录
set(CONFIG_EXTRACT_SRC ON)
# 接入认证方式使用证书认证CERT使用密钥认证KEY
set(CONFIG_AUTH_MODE "KEY")
# 接入认证是否不使用TLS证书方式必须选择使用TLS密钥认证可选择不使用TLS
set(CONFIG_AUTH_WITH_NOTLS ON)
# 是否打开代码中获取设备信息功能OFF时将从device_info.json中读取设备信息
set(CONFIG_DEBUG_DEV_INFO_USED ON)
# 是否使能多线程
set(CONFIG_MULTITHREAD_ENABLED ON)
# 是否使能AT模组
set(CONFIG_AT_MODULE_ENABLED ON)
option(IOT_DEBUG "Enable IOT_DEBUG" ${CONFIG_IOT_DEBUG})
option(DEBUG_DEV_INFO_USED "Enable DEBUG_DEV_INFO_USED" ${CONFIG_DEBUG_DEV_INFO_USED})
option(AUTH_WITH_NO_TLS "Enable AUTH_WITH_NO_TLS" ${CONFIG_AUTH_WITH_NOTLS})
option(MULTITHREAD_ENABLED "Enable AUTH_WITH_NO_TLS" ${CONFIG_MULTITHREAD_ENABLED})
option(AT_MODULE_ENABLE "Enable AUTH_WITH_NO_TLS" ${CONFIG_AT_MODULE_ENABLED})
if(${CONFIG_AUTH_MODE} STREQUAL "KEY")
option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" ON)
option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" OFF)
elseif(${CONFIG_AUTH_MODE} STREQUAL "CERT" AND ${CONFIG_AUTH_WITH_NOTLS} STREQUAL "OFF")
option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" OFF)
option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" ON)
else()
message(FATAL_ERROR "INVAILD AUTH_MODE:${FEATURE_AUTH_MODE} WITH AUTH_WITH_NO_TLS:${FEATURE_AUTH_WITH_NOTLS}!")
endif()
configure_file(
"${IOT_SDK_SOURCE_DIR}/config/settings/qcloud_iot_config.h.in"
"${IOT_SDK_SOURCE_DIR}/include/config/qcloud_iot_config.h"
@ONLY
)
# export include
include_directories(
${IOT_SDK_SOURCE_DIR}/include/
${IOT_SDK_SOURCE_DIR}/include/common
${IOT_SDK_SOURCE_DIR}/include/config
${IOT_SDK_SOURCE_DIR}/include/services/common
${IOT_SDK_SOURCE_DIR}/include/services/explorer
)
# set output path
set(LIBRARY_OUTPUT_PATH ${IOT_SDK_SOURCE_DIR}/output/libs)
set(EXECUTABLE_OUTPUT_PATH ${IOT_SDK_SOURCE_DIR}/output/bin)
# set link lib dir
link_directories(${LIBRARY_OUTPUT_PATH})
# set test src
if(${CONFIG_IOT_TEST} STREQUAL "ON")
set(src_test CACHE INTERNAL "")
set(inc_test CACHE INTERNAL "")
endif()
# ##################### PLATFORM MODULE #######################################
set(src_platform CACHE INTERNAL "")
set(inc_platform CACHE INTERNAL "")
add_subdirectory(${IOT_SDK_SOURCE_DIR}/platform)
# add_subdirectory(${IOT_SDK_SOURCE_DIR}/platform/at_client)
# ##################### COMMON MODULE #######################################
set(src_common CACHE INTERNAL "")
set(inc_common CACHE INTERNAL "")
# utils
add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/utils)
# cryptology
add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/cryptology)
# ##################### SERVICE MODULE ####################################
set(src_services CACHE INTERNAL "")
set(inc_services CACHE INTERNAL "")
# mqtt client
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/at_module)
# 是否使能获取iot后台时间功能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/system)
# 是否使能数据模板功能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/explorer/data_template)
# 是否使能网关功能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/gateway)
# ##################### EXTRACT ####################################
if(${CONFIG_EXTRACT_SRC} STREQUAL "ON")
file(COPY ${src_platform} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
file(COPY ${inc_platform} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
file(COPY ${src_common} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
file(COPY ${inc_common} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
file(COPY ${src_services} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
file(COPY ${inc_services} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
file(GLOB inc_export
${IOT_SDK_SOURCE_DIR}/include/*.h
${IOT_SDK_SOURCE_DIR}/include/common/*.h
${IOT_SDK_SOURCE_DIR}/include/config/*.h
${IOT_SDK_SOURCE_DIR}/include/services/common/*.h
${IOT_SDK_SOURCE_DIR}/include/services/explorer/*.h
)
file(COPY ${inc_export} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc)
endif()

View File

@@ -0,0 +1,213 @@
###################### CONFIG #####################################
# 开启单元测试
set(CONFIG_IOT_TEST OFF)
# 开启示例编译
set(CONFIG_COMPILE_SAMPLE ON)
# 打开IOT DEBUG
set(CONFIG_IOT_DEBUG OFF)
# 代码抽取ON表示根据配置抽取源码到ouput/sdk目录
set(CONFIG_EXTRACT_SRC OFF)
# 接入认证方式使用证书认证CERT使用密钥认证KEY
set(CONFIG_AUTH_MODE "KEY")
# 接入认证是否不使用TLS证书方式必须选择使用TLS密钥认证可选择不使用TLS
set(CONFIG_AUTH_WITH_NOTLS OFF)
# 是否打开代码中获取设备信息功能OFF时将从device_info.json中读取设备信息
set(CONFIG_DEBUG_DEV_INFO_USED ON)
# 是否使能多线程
set(CONFIG_MULTITHREAD_ENABLED ON)
# 使用JSON格式上报日志 默认使用text格式
set(CONFIG_LOG_UPLOAD_TYPE_JSON ON)
# 使用AES加密上报日志
set(CONFIG_LOG_UPLOAD_AES_ENCRYPT_POST OFF)
# 是否使用动态注册功能
set(CONFIG_DEV_DYN_REG_ENABLED ON)
option(IOT_DEBUG "Enable IOT_DEBUG" ${CONFIG_IOT_DEBUG})
option(DEBUG_DEV_INFO_USED "Enable DEBUG_DEV_INFO_USED" ${CONFIG_DEBUG_DEV_INFO_USED})
option(AUTH_WITH_NO_TLS "Enable AUTH_WITH_NO_TLS" ${CONFIG_AUTH_WITH_NOTLS})
option(LOG_UPLOAD_TYPE_JSON "Enable LOG_UPLOAD_DEBUG" ${CONFIG_LOG_UPLOAD_TYPE_JSON})
option(LOG_UPLOAD_AES_ENCRYPT_POST "Log upload with AES encrypt" ${CONFIG_LOG_UPLOAD_AES_ENCRYPT_POST})
option(DEV_DYN_REG_ENABLED "Enable DEV_DYN_REG_ENABLED" ${CONFIG_DEV_DYN_REG_ENABLED})
option(MULTITHREAD_ENABLED "Enable AUTH_WITH_NO_TLS" ${CONFIG_MULTITHREAD_ENABLED})
if(${CONFIG_AUTH_MODE} STREQUAL "KEY")
option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" ON)
option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" OFF)
elseif(${CONFIG_AUTH_MODE} STREQUAL "CERT" AND ${CONFIG_AUTH_WITH_NOTLS} STREQUAL "OFF")
option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" OFF)
option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" ON)
else()
message(FATAL_ERROR "INVAILD AUTH_MODE:${FEATURE_AUTH_MODE} WITH AUTH_WITH_NO_TLS:${FEATURE_AUTH_WITH_NOTLS}!")
endif()
configure_file (
"${IOT_SDK_SOURCE_DIR}/config/settings/qcloud_iot_config.h.in"
"${IOT_SDK_SOURCE_DIR}/include/config/qcloud_iot_config.h"
@ONLY
)
# export include
include_directories(
${IOT_SDK_SOURCE_DIR}/include/
${IOT_SDK_SOURCE_DIR}/include/common
${IOT_SDK_SOURCE_DIR}/include/config
${IOT_SDK_SOURCE_DIR}/include/services/common
${IOT_SDK_SOURCE_DIR}/include/services/explorer
)
# set output path
set(LIBRARY_OUTPUT_PATH ${IOT_SDK_SOURCE_DIR}/output/libs)
set(EXECUTABLE_OUTPUT_PATH ${IOT_SDK_SOURCE_DIR}/output/bin)
# set link lib dir
link_directories(${LIBRARY_OUTPUT_PATH})
# set test src
if(${CONFIG_IOT_TEST} STREQUAL "ON")
set(src_test CACHE INTERNAL "")
set(inc_test CACHE INTERNAL "")
endif()
###################### PLATFORM MODULE #######################################
set(src_platform CACHE INTERNAL "")
set(inc_platform CACHE INTERNAL "")
add_subdirectory(${IOT_SDK_SOURCE_DIR}/platform)
# set include
include_directories(${inc_platform})
# add library libiot_platform.a
add_library(iot_platform STATIC ${src_platform})
###################### COMMON MODULE #######################################
set(src_common CACHE INTERNAL "")
set(inc_common CACHE INTERNAL "")
# mqtt packet
add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/mqtt_packet)
# utils
add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/utils)
# cryptology
add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/cryptology)
# set include
include_directories(${inc_common})
# add library libiot_common.a
add_library(iot_common STATIC ${src_common})
###################### 3rd MODULE ####################################
# mbedtls
if(${CONFIG_AUTH_MODE} STREQUAL "KEY" )
include_directories(
${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/mbedtls/include
${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/port/inc
)
add_definitions("-DMBEDTLS_CONFIG_FILE=\"qcloud_iot_tls_psk_config.h\"")
endif()
if(${CONFIG_AUTH_WITH_NOTLS} STREQUAL "OFF")
# libmbedtls.a
add_subdirectory(${IOT_SDK_SOURCE_DIR}/3rd/mbedtls)
set(libsdk ${libsdk} mbedtls)
endif()
###################### SERVICE MODULE ####################################
set(src_services CACHE INTERNAL "")
set(inc_services CACHE INTERNAL "")
# mqtt client (must include except dynamic register)
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/mqtt_client)
# http client
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/http_client)
## MQTT ONLY
# 是否使能获取iot后台时间功能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/system)
# 是否使能OTA固件升级功能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/ota)
# gateway
add_subdirectory(${PROJECT_SOURCE_DIR}/services/common/gateway)
# 是否使能数据模板功能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/explorer/data_template)
# 是否系统服务
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/explorer/service_mqtt)
## HTTP ONLY
# 是否使能设备动态注册
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/dynreg)
# 是否使能COS下载功能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/cos)
# 是否使能HTTP请求腾讯云后台功能 如果开启了日志上报和动态注册 则必须要使能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/http_signed)
## HTTP & MQTT
# 是否使能日志上报云端功能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/log_upload)
# set include
include_directories(${inc_services})
# add library libiot_services.a
add_library(iot_services STATIC ${src_services})
###################### UINT TEST ####################################
if(${CONFIG_IOT_TEST} STREQUAL "ON")
include_directories(${inc_test})
find_package(GTest REQUIRED)
add_executable(iot_hub_sdk_test ${src_test})
target_link_libraries(iot_hub_sdk_test ${GTEST_BOTH_LIBRARIES} ${libsdk})
setup_target_for_coverage_gcovr_html(
NAME sdk_test_coverage
EXECUTABLE iot_hub_sdk_test
DEPENDENCIES iot_hub_sdk_test
)
endif()
###################### EXTRACT ####################################
if(${CONFIG_EXTRACT_SRC} STREQUAL "ON")
file(COPY ${src_platform} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
file(COPY ${inc_platform} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
file(COPY ${src_common} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
file(COPY ${inc_common} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
file(COPY ${src_services} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
file(COPY ${inc_services} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
file(GLOB inc_export
${IOT_SDK_SOURCE_DIR}/include/*.h
${IOT_SDK_SOURCE_DIR}/include/common/*.h
${IOT_SDK_SOURCE_DIR}/include/config/*.h
${IOT_SDK_SOURCE_DIR}/include/services/common/*.h
${IOT_SDK_SOURCE_DIR}/include/services/explorer/*.h
)
file(COPY ${inc_export} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc)
endif()

View File

@@ -0,0 +1,205 @@
###################### CONFIG #####################################
# 开启单元测试
set(CONFIG_IOT_TEST OFF)
# 开启示例编译
set(CONFIG_COMPILE_SAMPLE ON)
# 打开IOT DEBUG
set(CONFIG_IOT_DEBUG OFF)
# 代码抽取ON表示根据配置抽取源码到ouput/sdk目录
set(CONFIG_EXTRACT_SRC OFF)
# 接入认证方式使用证书认证CERT使用密钥认证KEY
set(CONFIG_AUTH_MODE "KEY")
# 接入认证是否不使用TLS证书方式必须选择使用TLS密钥认证可选择不使用TLS
set(CONFIG_AUTH_WITH_NOTLS OFF)
# 是否打开代码中获取设备信息功能OFF时将从device_info.json中读取设备信息
set(CONFIG_DEBUG_DEV_INFO_USED ON)
# 是否使能多线程
set(CONFIG_MULTITHREAD_ENABLED OFF)
# 使用JSON格式上报日志 默认使用text格式
set(CONFIG_LOG_UPLOAD_TYPE_JSON ON)
option(IOT_DEBUG "Enable IOT_DEBUG" ${CONFIG_IOT_DEBUG})
option(DEBUG_DEV_INFO_USED "Enable DEBUG_DEV_INFO_USED" ${CONFIG_DEBUG_DEV_INFO_USED})
option(AUTH_WITH_NO_TLS "Enable AUTH_WITH_NO_TLS" ${CONFIG_AUTH_WITH_NOTLS})
option(LOG_UPLOAD_TYPE_JSON "Enable LOG_UPLOAD_DEBUG" ${CONFIG_LOG_UPLOAD_TYPE_JSON})
if(${CONFIG_AUTH_MODE} STREQUAL "KEY")
option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" ON)
option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" OFF)
elseif(${CONFIG_AUTH_MODE} STREQUAL "CERT" AND ${CONFIG_AUTH_WITH_NOTLS} STREQUAL "OFF")
option(AUTH_MODE_KEY "Enable AUTH_MODE_KEY" OFF)
option(AUTH_MODE_CERT "Enable AUTH_MODE_CERT" ON)
else()
message(FATAL_ERROR "INVAILD AUTH_MODE:${FEATURE_AUTH_MODE} WITH AUTH_WITH_NO_TLS:${FEATURE_AUTH_WITH_NOTLS}!")
endif()
configure_file (
"${IOT_SDK_SOURCE_DIR}/config/settings/qcloud_iot_config.h.in"
"${IOT_SDK_SOURCE_DIR}/include/config/qcloud_iot_config.h"
@ONLY
)
# export include
include_directories(
${IOT_SDK_SOURCE_DIR}/include/
${IOT_SDK_SOURCE_DIR}/include/common
${IOT_SDK_SOURCE_DIR}/include/config
${IOT_SDK_SOURCE_DIR}/include/services/common
${IOT_SDK_SOURCE_DIR}/include/services/hub
)
# set output path
set(LIBRARY_OUTPUT_PATH ${IOT_SDK_SOURCE_DIR}/output/libs)
set(EXECUTABLE_OUTPUT_PATH ${IOT_SDK_SOURCE_DIR}/output/bin)
# set link lib dir
link_directories(${LIBRARY_OUTPUT_PATH})
# set test src
if(${CONFIG_IOT_TEST} STREQUAL "ON")
set(src_test CACHE INTERNAL "")
set(inc_test CACHE INTERNAL "")
endif()
###################### PLATFORM MODULE #######################################
set(src_platform CACHE INTERNAL "")
set(inc_platform CACHE INTERNAL "")
add_subdirectory(${IOT_SDK_SOURCE_DIR}/platform)
# set include
include_directories(${inc_platform})
# add library libiot_platform.a
add_library(iot_platform STATIC ${src_platform})
###################### COMMON MODULE #######################################
set(src_common CACHE INTERNAL "")
set(inc_common CACHE INTERNAL "")
# mqtt packet
add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/mqtt_packet)
# utils
add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/utils)
# cryptology
add_subdirectory(${IOT_SDK_SOURCE_DIR}/common/cryptology)
# set include
include_directories(${inc_common})
# add library libiot_common.a
add_library(iot_common STATIC ${src_common})
###################### SERVICE MODULE ####################################
set(src_services CACHE INTERNAL "")
set(inc_services CACHE INTERNAL "")
# mqtt client (must include except dynamic register)
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/mqtt_client)
## MQTT ONLY
# 是否打开广播功能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/hub/broadcast)
# 是否使能获取iot后台时间功能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/system)
# 是否打开RRPC功能
#add_subdirectory()
# 是否打开远程配置功能
#add_subdirectory()
# 是否打开设备影子的总开关
#add_subdirectory()
## HTTP ONLY
# 是否使能设备动态注册
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/dynreg)
## MQTT & HTTP
# 是否使能网关功能
#add_subdirectory()
# 是否使能OTA固件升级功能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/ota)
# 是否使能资源管理功能
#add_subdirectory()
# 是否使能HTTP请求腾讯云后台功能 如果开启了日志上报和动态注册 则必须要使能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/http_signed)
# 是否使能日志上报云端功能
add_subdirectory(${IOT_SDK_SOURCE_DIR}/services/common/log_upload)
# set include
include_directories(${inc_services})
# add library libiot_services.a
add_library(iot_services STATIC ${src_services})
###################### 3rd MODULE ####################################
# mbedtls
if(${CONFIG_AUTH_MODE} STREQUAL "KEY" )
include_directories(
${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/mbedtls/include
${IOT_SDK_SOURCE_DIR}/3rd/mbedtls/port/inc
)
add_definitions("-DMBEDTLS_CONFIG_FILE=\"qcloud_iot_tls_psk_config.h\"")
endif()
if(${CONFIG_AUTH_WITH_NOTLS} STREQUAL "OFF")
# libmbedtls.a
add_subdirectory(${IOT_SDK_SOURCE_DIR}/3rd/mbedtls)
set(libsdk ${libsdk} mbedtls)
endif()
###################### UINT TEST ####################################
if(${CONFIG_IOT_TEST} STREQUAL "ON")
include_directories(${inc_test})
find_package(GTest REQUIRED)
add_executable(iot_hub_sdk_test ${src_test})
target_link_libraries(iot_hub_sdk_test ${GTEST_BOTH_LIBRARIES} ${libsdk})
setup_target_for_coverage_gcovr_html(
NAME sdk_test_coverage
EXECUTABLE iot_hub_sdk_test
DEPENDENCIES iot_hub_sdk_test
)
endif()
###################### EXTRACT ####################################
if(${CONFIG_EXTRACT_SRC} STREQUAL "ON")
file(COPY ${src_platform} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
file(COPY ${inc_platform} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
file(COPY ${src_common} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
file(COPY ${inc_common} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
file(COPY ${src_services} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/src)
file(COPY ${inc_services} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc/internal)
file(GLOB inc_export
${IOT_SDK_SOURCE_DIR}/include/*.h
${IOT_SDK_SOURCE_DIR}/include/common/*.h
${IOT_SDK_SOURCE_DIR}/include/config/*.h
${IOT_SDK_SOURCE_DIR}/include/services/common/*.h
${IOT_SDK_SOURCE_DIR}/include/services/explorer/*.h
)
file(COPY ${inc_export} DESTINATION ${IOT_SDK_SOURCE_DIR}/output/sdk/inc)
endif()

View File

@@ -0,0 +1,55 @@
/**
* @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 qcloud_iot_config.h
* @brief sdk config define
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-06-01
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-06-01 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-12 <td>1.1 <td>fancyxu <td>rename AUTH_WITH_NOTLS to AUTH_WITH_NO_TLS
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_CONFIG_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
#cmakedefine AUTH_MODE_CERT
#cmakedefine AUTH_MODE_KEY
#cmakedefine AUTH_WITH_NO_TLS
#cmakedefine GATEWAY_ENABLED
#cmakedefine SYSTEM_COMM
#cmakedefine DEV_DYN_REG_ENABLED
#cmakedefine LOG_UPLOAD
#cmakedefine IOT_DEBUG
#cmakedefine DEBUG_DEV_INFO_USED
#cmakedefine AT_MODULE_ENABLE
#cmakedefine MULTITHREAD_ENABLED
#cmakedefine LOG_UPLOAD_TYPE_JSON
#cmakedefine LOG_UPLOAD_AES_ENCRYPT_POST
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_CONFIG_H_

View File

@@ -0,0 +1,4 @@
# 编译类型: release/debug
set(BUILD_TYPE "debug")
# 设置操作系统
set(PLATFORM "free_rtos")

View File

@@ -0,0 +1,24 @@
# 编译类型: release/debug
set(BUILD_TYPE "debug")
# 设置CMAKE使用编译工具及编译选项
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_C_COMPILER "/usr/bin/gcc")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Wall -pthread -fstack-protector-strong -Wl,-z,now -Wl,-z,noexecstack -fPIE -pie -ffunction-sections -fdata-sections")
set(LINK_FLAGS "${LINK_FLAGS} -Wl,--gc-sections")
if(${BUILD_TYPE} STREQUAL "debug")
set(CMAKE_BUILD_TYPE "Debug")
list(APPEND CMAKE_MODULE_PATH "${IOT_SDK_SOURCE_DIR}/config/gcovr")
include(code_coverage)
append_coverage_compiler_flags()
else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Os -D_FORTIFY_SOURCE=2") # 编译选项 -Wl,-Map,iot.map
endif()
set(PLATFORM "linux")
set(libsdk -Wl,--start-group ${libsdk} iot_common iot_services iot_platform pthread -Wl,--end-group)

View File

@@ -0,0 +1,4 @@
# 编译类型: release/debug
set(BUILD_TYPE "debug")
# 设置操作系统
set(PLATFORM "tencentos_tiny")

View File

@@ -0,0 +1,453 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
import json
import sys
import os
import argparse
import glob
from sys import version_info
if version_info.major == 3:
import importlib
importlib.reload(sys)
elif version_info.major == 2:
reload(sys)
sys.setdefaultencoding("utf-8")
try:
import simplejson as json
except:
import json
# {"version":"1.0","properties":[{"id":"light_switch","name":"电灯开关","desc":"控制电灯开灭","required":true,"mode":"rw","define":{"type":"bool","mapping":{"0":"关","1":"开"}}},{"id":"color","name":"颜色","desc":"灯光颜色","mode":"rw","define":{"type":"enum","mapping":{"0":"Red","1":"Green","2":"Blue"}}},{"id":"brightness","name":"颜色","desc":"灯光颜色","mode":"rw","define":{"type":"int","unit":"%","unitDesc":"亮度百分比","min":"0","max":"100"}},{"id":"name","name":"灯位置名称","desc":"灯位置名称:书房、客厅等","mode":"rw","required":true,"define":{"type":"string","min":"0","max":"64"}}]}
class iot_property:
"""Parse iot property json node and get property code.
Retrieves rows pertaining to the given keys from the Table instance
represented by table_handle. String keys will be UTF-8 encoded.
Args:
json_node:
json node include property
prefix:
property, action, event.
Raises:
ValueError: invalid node
"""
def __init__(self, json_node, prefix="property", node_define="define"):
self.prefix = prefix
self.id = json_node["id"]
self.type = json_node[node_define]["type"]
self.default_value = ""
self.struct_property_list = []
self.mode = 1 # rw mode
try:
# property type dict
PROPERTY_TYPE_DICT = {
# key type_id type_value_name
'bool' : ["DATA_TEMPLATE_TYPE_BOOL" , "value_bool" ],
'enum' : ["DATA_TEMPLATE_TYPE_ENUM" , "value_enum" ],
'float' : ["DATA_TEMPLATE_TYPE_FLOAT" , "value_float" ],
'int' : ["DATA_TEMPLATE_TYPE_INT" , "value_int" ],
'string' : ["DATA_TEMPLATE_TYPE_STRING" , "value_string" ],
'timestamp' : ["DATA_TEMPLATE_TYPE_TIME" , "value_time" ],
'struct' : ["DATA_TEMPLATE_TYPE_STRUCT" , "value_struct" ],
'stringenum': ["DATA_TEMPLATE_TYPE_STRING_ENUM" , "value_string_enum" ],
#'array' : ["DATA_TEMPLATE_TYPE_ARRAY" , "value_array" ], # TODO
}
self.type_id = PROPERTY_TYPE_DICT[self.type][0]
self.type_value_name = PROPERTY_TYPE_DICT[self.type][1]
# mode: rw or r
if "mode" in json_node:
self.mode = 1 if json_node["mode"] == "rw" else 0
# default value
if "mapping" in json_node[node_define]:
self.default_value = next(iter(json_node[node_define]["mapping"]))
elif "start" in json_node[node_define]:
self.default_value = json_node[node_define]["start"]
if self.type_id in ["DATA_TEMPLATE_TYPE_STRUCT"]:
self.default_value = self.get_property_global_var_name()
# string
if self.type_id is "DATA_TEMPLATE_TYPE_STRING":
self.max_length = json_node[node_define]["max"]
self.default_value = "\" \""
if self.type_id is "DATA_TEMPLATE_TYPE_STRING_ENUM":
self.max_length = 0
for enum in json_node[node_define]["mapping"]:
self.max_length = max(self.max_length, len(enum))
self.default_value = "\"{}\"".format(next(iter(json_node[node_define]["mapping"])))
# struct
if "specs" in json_node[node_define]:
for subnode in json_node[node_define]["specs"]:
self.struct_property_list.append(iot_property(subnode, self.prefix + '_' + self.id, "dataType"))
except KeyError:
ValueError('{} 字段 数据类型 type={} 取值非法有效值应为bool,enum,float,int,,string,timestamp,struct,stringenum'.format(
self.id, self.type))
def get_property_enum_index(self):
return "USR_{}_INDEX_{}".format(self.prefix.upper(), self.id.upper())
def get_property_global_var_name(self):
return "sg_usr_{}_{}".format( self.prefix, self.id)
def create_property_struct_index_enum(self):
main_code = ""
main_code += "/*----------------- {} {} index enum start -------------------*/\n\n".format(self.id, self.prefix)
main_code += "typedef enum {\n"
# 构造枚举索引
for property in self.struct_property_list:
if self.struct_property_list.index(property) is 0:
main_code += " {} = 0,\n".format(property.get_property_enum_index())
else:
main_code += " {},\n".format(property.get_property_enum_index())
main_code += "} "
main_code += "Usr{}{}Index;\n\n".format(self.prefix.capitalize(), self.id.capitalize())
main_code += "/*----------------- {} {} index enum end -------------------*/\n\n".format(self.id, self.prefix)
return main_code
def create_property_struct_definition(self):
main_code = ""
if self.type_id != "DATA_TEMPLATE_TYPE_STRUCT":
return main_code
# 定义结构体成员数目
main_code += "#define TOTAL_USR_{}_STRUCT_{}_COUNT {}\n\n".format(self.prefix.upper(), self.id.upper(), len(self.struct_property_list))
# 定义数据模版结构体
main_code += "static DataTemplateProperty {}[TOTAL_USR_{}_STRUCT_{}_COUNT];\n\n".format(self.get_property_global_var_name(), self.prefix.upper(), self.id.upper())
# 定义结构体类型和结构体变量
main_code += "static void _init_data_template_{}_{}(void)\n".format(self.prefix, self.id)
main_code += "{\n"
# 构造结构体数组
for property in self.struct_property_list:
static_property_struct_array_index="{}[{}]".format(self.get_property_global_var_name(), property.get_property_enum_index())
if property.type_id in ["DATA_TEMPLATE_TYPE_STRING", "DATA_TEMPLATE_TYPE_STRING_ENUM"]:
main_code += " static char {}[{}+1] = {};\n".format(property.get_property_global_var_name(), property.max_length, property.default_value)
main_code += " {}[{}].value.{} = {};\n".format(static_property_struct_array_index, property.type_value_name, property.get_property_global_var_name())
else:
main_code += " {}.value.{} = {};\n".format(static_property_struct_array_index, property.type_value_name, property.default_value)
main_code += " {}.key = \"{}\";\n".format(static_property_struct_array_index, property.id)
main_code += " {}.type = {};\n".format(static_property_struct_array_index, property.type_id)
main_code += " {}.is_rw = {};\n\n".format(static_property_struct_array_index, self.mode)
# 结尾
main_code += "}\n\n"
return main_code
class iot_event:
def __init__(self, json_node):
self.event_id = json_node["id"]
self.event_type = json_node["type"]
self.event_type_enum= "IOT_DATA_TEMPLATE_EVENT_TYPE_{}".format(self.event_type.upper())
self.event_properties = []
for property in json_node["params"]:
self.event_properties.append(iot_property(property,"event"))
def get_event_enum_index(self):
return "USR_EVENT_INDEX_{}".format(self.event_id.upper())
def create_event_params_global_var(self):
main_code = ""
main_code += "/**\n * @brief Sample of event {} post params.\n *\n */\n".format(self.event_id)
main_code += "static const char* sg_usr_event_{}_params = ".format(self.event_id)
main_code += "\"{"
for property in self.event_properties:
if self.event_properties.index(property) > 0:
main_code += ","
if property.type_id in ["DATA_TEMPLATE_TYPE_STRING", "DATA_TEMPLATE_TYPE_STRING_ENUM"]:
main_code += "\\\"{}\\\":{}".format(property.id, property.default_value.replace('\"','\\\"'))
else:
main_code += "\\\"{}\\\":{}".format(property.id, property.default_value)
main_code += "}\";\n\n"
return main_code
class iot_action:
def __init__(self, json_node):
self.action_id = json_node["id"]
self.action_input = []
for input in json_node["input"]:
self.action_input.append(iot_property(input, 'action_' + self.action_id + '_input'))
self.action_output = []
for output in json_node["output"]:
self.action_output.append(iot_property(output, 'action_' + self.action_id + '_output'))
def get_action_enum_index(self):
return "USR_ACTION_INDEX_{}".format(self.action_id.upper())
def get_action_input_index_enum(self) :
main_code = ""
main_code += "/*----------------- action {} input index enum start -------------------*/\n\n".format(self.action_id)
main_code += "typedef enum {\n"
for property in self.action_input:
if self.action_input.index(property) is 0:
main_code += " {} = 0,\n".format(property.get_property_enum_index())
else:
main_code += " {},\n".format(property.get_property_enum_index())
main_code += "} "
main_code += "UsrAction{}InputIndex;\n\n".format(self.action_id.capitalize().replace('_', ''))
main_code += "/*----------------- action {} input index enum end -------------------*/\n\n".format(self.action_id)
return main_code
def create_action_input_init(self):
static_usr_action_input_array_name = "sg_usr_action_{}_input".format(self.action_id)
main_code = ""
main_code += "#define TOTAL_USR_ACTION_{}_INPUT_PARAMS_COUNT {}\n\n".format(self.action_id.upper(), len(self.action_input))
main_code += "static DataTemplateProperty {}[TOTAL_USR_ACTION_{}_INPUT_PARAMS_COUNT];\n\n".format(static_usr_action_input_array_name, self.action_id.upper())
main_code += "static void _init_data_template_action_{}_input(void)\n".format(self.action_id)
main_code += "{\n"
input_struct = ""
for property in self.action_input:
if property.type_id is "DATA_TEMPLATE_TYPE_STRUCT":
input_struct += property.create_property_struct_definition()
if property.type_id in ["DATA_TEMPLATE_TYPE_STRING", "DATA_TEMPLATE_TYPE_STRING_ENUM"]:
main_code += " static char {}[{}+1]= {};\n".format(property.get_property_global_var_name(), property.max_length, property.default_value)
array_index = "{}[{}]".format(static_usr_action_input_array_name, property.get_property_enum_index())
main_code += " {}.value.{} = {};\n".format(array_index, property.type_value_name, property.default_value)
main_code += " {}.key = \"{}\";\n".format(array_index, property.id)
main_code += " {}.type = {};\n".format(array_index, property.type_id)
main_code += " {}.is_rw = {};\n\n".format(array_index, property.mode)
main_code += "}\n\n"
return main_code
def create_action_reply(self):
main_code = ""
input_struct = ""
main_code += "/**\n * @brief Sample of action {} reply.\n *\n */\n".format(self.action_id)
main_code += "static IotDataTemplateActionReply sg_usr_action_{}_reply = ".format(self.action_id)
main_code += "{\n"
main_code += " .code = 0,\n"
main_code += " .client_token = {"
main_code += ".value = \"test_{}\", .value_len = sizeof(\"test_{}\")".format(self.action_id, self.action_id)
main_code += "},\n"
main_code += " .response = \"{"
for property in self.action_output:
if self.action_output.index(property) > 0:
main_code += ","
if property.type_id == "DATA_TEMPLATE_TYPE_STRUCT":
input_struct += property.create_property_struct_definition()
main_code += "\\\"{}\\\":{}".format(property.id, property.default_value)
main_code += "}\",\n};\n\n"
# print(main_code)
return main_code
class iot_struct:
def __init__(self, model):
self.properties = []
self.events = []
self.actions = []
if "properties" in model:
for property in model["properties"]:
self.properties.append(iot_property(property))
if "events" in model:
for event in model["events"]:
self.events.append(iot_event(event))
if "actions" in model:
for action in model["actions"]:
self.actions.append(iot_action(action))
def __index_enum_create(self, list, prefix) :
property_struct_index_enum_str = ""
action_input_index_enum_str = ""
main_code = ""
main_code += "/*----------------- {} index enum start -------------------*/\n\n".format(prefix)
main_code += "typedef enum {\n"
enum_index = ""
for node in list:
if prefix is "property":
enum_index = node.get_property_enum_index()
if node.type_id is "DATA_TEMPLATE_TYPE_STRUCT":
property_struct_index_enum_str += node.create_property_struct_index_enum()
elif prefix is "action":
enum_index = node.get_action_enum_index()
action_input_index_enum_str += node.get_action_input_index_enum()
elif prefix is "event":
enum_index = node.get_event_enum_index()
if list.index(node) is 0:
main_code += " {} = 0,\n".format(enum_index)
else:
main_code += " {},\n".format(enum_index)
main_code += "} "
main_code += "Usr{}Index;\n\n".format(prefix.capitalize())
main_code += "/*----------------- {} index enum end -------------------*/\n\n".format(prefix)
main_code += property_struct_index_enum_str
main_code += action_input_index_enum_str
return main_code
def __property_data_initializer(self) :
main_code = ""
main_code += "// ----------------------------------------------------------------------------\n"
main_code += "// user property\n"
main_code += "// ----------------------------------------------------------------------------/\n\n"
property_code = ""
property_code += "#define TOTAL_USR_PROPERTY_COUNT {}\n\n".format(len(self.properties))
property_code += "static DataTemplateProperty sg_usr_data_template_property[TOTAL_USR_PROPERTY_COUNT];\n\n"
property_code += "static void _init_data_template_property(void)\n"
property_code += "{\n"
for property in self.properties:
if property.type_id is "DATA_TEMPLATE_TYPE_STRUCT":
main_code += property.create_property_struct_definition()
property_code += " _init_data_template_property_{}();\n".format(property.id)
if property.type_id in ["DATA_TEMPLATE_TYPE_STRING", "DATA_TEMPLATE_TYPE_STRING_ENUM"]:
property_code += " static char {}[{}+1] = {};\n".format(property.get_property_global_var_name(), property.max_length, property.default_value)
static_usr_property_array_name = "sg_usr_data_template_property[{}]".format(property.get_property_enum_index())
if property.type_id is "DATA_TEMPLATE_TYPE_STRUCT":
property_code += " {}.value.{}.property = {};\n".format(static_usr_property_array_name, property.type_value_name, property.default_value)
property_code += " {}.value.{}.count = TOTAL_USR_PROPERTY_STRUCT_{}_COUNT;\n".format(static_usr_property_array_name, property.type_value_name, property.id.upper())
elif property.type_id in ["DATA_TEMPLATE_TYPE_STRING", "DATA_TEMPLATE_TYPE_STRING_ENUM"]:
property_code+= " {}.value.{} = {};\n".format(static_usr_property_array_name, property.type_value_name, property.get_property_global_var_name())
else:
property_code += " {}.value.{} = {};\n".format(static_usr_property_array_name, property.type_value_name, property.default_value)
property_code += " {}.key = \"{}\";\n".format(static_usr_property_array_name, property.id)
property_code += " {}.type = {};\n".format(static_usr_property_array_name, property.type_id)
property_code += " {}.need_report = {};\n".format(static_usr_property_array_name, 1)
property_code += " {}.is_rw = {};\n\n".format(static_usr_property_array_name, property.mode)
property_code += "}\n\n"
main_code += property_code
return main_code
def __event_data_initializer(self) :
main_code = ""
main_code += "// ----------------------------------------------------------------------------\n"
main_code += "// user event\n"
main_code += "// ----------------------------------------------------------------------------\n\n"
event_code = ""
event_code += "#define TOTAL_USR_EVENT_COUNT {}\n\n".format(len(self.events))
event_code += "static DataTemplateEvent sg_usr_data_template_event[TOTAL_USR_EVENT_COUNT];\n\n"
event_code += "static void _init_data_template_event(void)\n"
event_code += "{\n"
for event in self.events:
main_code += event.create_event_params_global_var()
static_usr_event_array_name = "sg_usr_data_template_event[{}]".format(event.get_event_enum_index())
event_code += " {}.event_id = \"{}\";\n".format(static_usr_event_array_name, event.event_id)
event_code += " {}.type = {};\n".format(static_usr_event_array_name, event.event_type_enum)
event_code += " {}.params = sg_usr_event_{}_params;\n\n".format(static_usr_event_array_name, event.event_id)
event_code += "}\n\n"
main_code += event_code
# print(event_params)
return main_code
def __action_data_initializer(self):
main_code = ""
action_code = ""
main_code += "// ----------------------------------------------------------------------------\n"
main_code += "// user action\n"
main_code += "// ----------------------------------------------------------------------------\n\n"
action_code += "#define TOTAL_USR_ACTION_COUNT {}\n\n".format(len(self.actions))
action_code += "static DataTemplateAction sg_usr_data_template_action[TOTAL_USR_ACTION_COUNT];\n\n"
action_code += "static void _init_data_template_action(void)\n"
action_code += "{\n"
for action in self.actions:
main_code += action.create_action_input_init()
main_code += action.create_action_reply()
static_usr_action_input_array_name = "sg_usr_data_template_action[{}]".format(action.get_action_enum_index())
action_code += " _init_data_template_action_{}_input();\n".format(action.action_id)
action_code += " {}.action_id = \"{}\";\n".format(static_usr_action_input_array_name, action.action_id)
action_code += " {}.input_struct.value_struct.property = sg_usr_action_{}_input;\n".format(static_usr_action_input_array_name,action.action_id)
action_code += " {}.input_struct.value_struct.count = TOTAL_USR_ACTION_{}_INPUT_PARAMS_COUNT;\n".format(static_usr_action_input_array_name,action.action_id.upper())
action_code += " {}.reply = sg_usr_action_{}_reply;\n\n".format(static_usr_action_input_array_name, action.action_id)
action_code += "}\n\n"
main_code += action_code
return main_code
def gen_config_header(self):
main_code = ""
main_code += self.__index_enum_create(self.properties, "property")
main_code += self.__index_enum_create(self.events, "event")
main_code += self.__index_enum_create(self.actions, "action")
return main_code
def gen_config_src_c(self):
main_code = ""
main_code += self.__property_data_initializer()
main_code += self.__event_data_initializer()
main_code += self.__action_data_initializer()
return main_code
def main():
parser = argparse.ArgumentParser(description='Iot hub data_template and events config code generator.',
usage='use "./data_template_codegen.py -c xx/config.json" gen config code')
parser.add_argument('-c', '--config', dest='config', metavar='xxx.json', required=False, default='xxx.json',
help='copy the generated file (data_config.c and events_config.c) to data_template_sample dir '
'or your own code dir with data_template. '
'\nconfig file can be download from tencent iot-hub platfrom. https://console.cloud.tencent.com/iotcloud')
parser.add_argument('-d', '--dest', dest='dest', required=False, default='.',
help='Dest directory for generated code files, no / at the end.')
args = parser.parse_args()
config_path = args.config
if not os.path.exists(config_path):
print(u"错误:配置文件不存在,请重新指定数据模板配置文件路径,请参考用法 ./data_template_code_generate.py -c <dir>/data_template.json".format(config_path))
return 1
config_dir = os.path.dirname(config_path)
if config_dir:
config_dir += "/"
f = open(config_path, "r", encoding='utf-8')
try:
thing_model = json.load(f)
if 'properties' not in thing_model:
thing_model.properties = []
if 'events' not in thing_model:
thing_model.events = []
print(u"加载 {} 文件成功".format(config_path))
except ValueError as e:
print(u"错误:文件格式非法,请检查 {} 文件是否是 JSON 格式。".format(config_path))
return 1
try:
snippet = iot_struct(thing_model)
output_config_header_file_name = args.dest + "/data_template_config_header.include"
with open(output_config_header_file_name, "w") as file:
file.write("{}".format(snippet.gen_config_header()))
file.close()
print(u"文件 {} 生成成功".format(output_config_header_file_name))
output_config_src_c_file_name = args.dest + "/data_template_config_src_c.include"
with open(output_config_src_c_file_name, "w") as file:
file.write("{}".format(snippet.gen_config_src_c()))
file.close()
print(u"文件 {} 生成成功".format(output_config_src_c_file_name))
return 0
except ValueError as e:
print(e)
return 1
if __name__ == '__main__':
sys.exit(main())

View File

@@ -0,0 +1,258 @@
{
"version": "1.0",
"properties": [
{
"id": "power_switch",
"name": "电灯开关",
"desc": "控制电灯开灭",
"required": true,
"mode": "rw",
"define": {
"type": "bool",
"mapping": {
"0": "关",
"1": "开"
}
}
},
{
"id": "color",
"name": "颜色",
"desc": "灯光颜色",
"mode": "rw",
"define": {
"type": "enum",
"mapping": {
"0": "Red",
"1": "Green",
"2": "Blue"
}
}
},
{
"id": "brightness",
"name": "亮度",
"desc": "灯光亮度",
"mode": "rw",
"define": {
"type": "int",
"unit": "%",
"step": "1",
"min": "0",
"max": "100",
"start": "1"
}
},
{
"id": "name",
"name": "灯位置名称",
"desc": "灯位置名称:书房、客厅等",
"mode": "rw",
"required": false,
"define": {
"type": "string",
"min": "0",
"max": "64"
}
},
{
"id": "position",
"name": "灯位置坐标",
"desc": "",
"mode": "r",
"define": {
"type": "struct",
"specs": [
{
"id": "longitude",
"name": "经度",
"dataType": {
"type": "int",
"min": "-180",
"max": "180",
"start": "1",
"step": "1",
"unit": "度"
}
},
{
"id": "latitude",
"name": "纬度",
"dataType": {
"type": "int",
"min": "-90",
"max": "90",
"start": "1",
"step": "1",
"unit": "度"
}
}
]
},
"required": false
},
{
"id": "power",
"name": "功率",
"desc": "灯泡功率",
"mode": "rw",
"define": {
"type": "stringenum",
"mapping": {
"high": "High",
"medium": "Medium",
"low": "Low"
}
}
}
],
"events": [
{
"id": "status_report",
"name": "DeviceStatus",
"desc": "Report the device status",
"type": "info",
"required": false,
"params": [
{
"id": "status",
"name": "running_state",
"desc": "Report current device running state",
"define": {
"type": "bool",
"mapping": {
"0": "normal",
"1": "fault"
}
}
},
{
"id": "message",
"name": "Message",
"desc": "Some extra message",
"define": {
"type": "string",
"min": "0",
"max": "64"
}
}
]
},
{
"id": "low_voltage",
"name": "LowVoltage",
"desc": "Alert for device voltage is low",
"type": "alert",
"required": false,
"params": [
{
"id": "voltage",
"name": "Voltage",
"desc": "Current voltage",
"define": {
"type": "float",
"unit": "V",
"step": "1",
"min": "0.0",
"max": "24.0",
"start": "1"
}
}
]
},
{
"id": "hardware_fault",
"name": "Hardware_fault",
"desc": "Report hardware fault",
"type": "fault",
"required": false,
"params": [
{
"id": "name",
"name": "Name",
"desc": "Name like: memory,tf card, censors ...",
"define": {
"type": "string",
"min": "0",
"max": "64"
}
},
{
"id": "error_code",
"name": "Error_Code",
"desc": "Error code for fault",
"define": {
"type": "int",
"unit": "",
"step": "1",
"min": "0",
"max": "2000",
"start": "1"
}
}
]
}
],
"actions": [
{
"id": "light_blink",
"name": "light_blink",
"desc": "根据time和color实现灯的闪烁",
"input": [
{
"id": "time",
"name": "time",
"define": {
"type": "int",
"min": "0",
"max": "10",
"start": "0",
"step": "1",
"unit": "秒"
}
},
{
"id": "color",
"name": "color",
"define": {
"type": "bool",
"mapping": {
"0": "red",
"1": "green"
}
}
},
{
"id": "total_time",
"name": "total_time",
"define": {
"type": "int",
"min": "0",
"max": "100",
"start": "0",
"step": "1",
"unit": "秒"
}
}
],
"output": [
{
"id": "err_code",
"name": "code",
"define": {
"type": "enum",
"mapping": {
"0": "ok",
"1": "failed"
}
}
}
],
"required": false
}
],
"profile": {
"ProductId": "Q5NNWVC2Z8",
"CategoryId": "3"
}
}

View File

@@ -0,0 +1,148 @@
<!-- TOC -->
- [一、编写目的](#%E4%B8%80%E7%BC%96%E5%86%99%E7%9B%AE%E7%9A%84)
- [二、代码格式](#%E4%BA%8C%E4%BB%A3%E7%A0%81%E6%A0%BC%E5%BC%8F)
- [行长度](#%E8%A1%8C%E9%95%BF%E5%BA%A6)
- [缩进](#%E7%BC%A9%E8%BF%9B)
- [文件编码](#%E6%96%87%E4%BB%B6%E7%BC%96%E7%A0%81)
- [分行](#%E5%88%86%E8%A1%8C)
- [条件语句](#%E6%9D%A1%E4%BB%B6%E8%AF%AD%E5%8F%A5)
- [循环和开关语句](#%E5%BE%AA%E7%8E%AF%E5%92%8C%E5%BC%80%E5%85%B3%E8%AF%AD%E5%8F%A5)
- [指针](#%E6%8C%87%E9%92%88)
- [水平留白](#%E6%B0%B4%E5%B9%B3%E7%95%99%E7%99%BD)
- [垂直留白](#%E5%9E%82%E7%9B%B4%E7%95%99%E7%99%BD)
<!-- /TOC -->
## 一、编写目的
该文档用作提供 SDK 开发的代码格式说明,开发者提交代码前应仔细阅读。
本项目根目录提供了`.clang-format`格式文件,请优先使用`clang-format`进行代码格式统一。
如有冲突,请以`clang-format`的结果为准。
## 二、代码格式
### 行长度
最长120行
### 缩进
四个空格
### 文件编码
UTF-8 不能有BOM头。
### 分行
1.对齐式
```c
bool retval = DoSomething(averyveryveryverylongargument1,
argument2, argument3);
```
2.缩进式
```c
if (...) {
...
...
if (...) {
DoSomething(
argument1, argument2, // 4 空格缩进
argument3, argument4);
}
```
### 条件语句
```c
if (condition) {
DoSomething(); // 4 空格缩进.
}
```
### 循环和开关语句
- default不可达时用assert
- case使用大括号非强制要求
```c
switch (var) {
case 0: { // 4 空格缩进
... // 4 空格缩进
break;
}
case 1: {
...
break;
}
default: {
assert(false);
}
}
```
### 指针
```
char* ptr = NULL;
```
### 水平留白
行尾不留白
```c
void f(bool b) { // 左大括号前总是有空格
...
int i = 0; // 分号前不加空格
// 列表初始化中大括号内的空格是可选的,
// 如果加了空格,那么两边都要加上
int x[] = { 0 };
int x[] = {0};
if (b) { // if 条件语句和循环语句关键字后均有空格
} else { // else 前后有空格
}
while (test) {} // 圆括号内部不紧邻空格
switch (i) {
for (int i = 0; i < 5; ++i) {
// 循环和条件语句的圆括号内可以有空格,
// 但这种情况很少,要保持一致
switch ( i ) {
if ( test ) {
for ( int i = 0; i < 5; ++i ) {
// 循环里内分号后恒有空格,分号前可以加个空格
for ( ; i < 5 ; ++i) {
switch (i) {
case 1: // switch case 的冒号前无空格
...
case 2: break; // 如果冒号后有代码,加个空格
// 赋值运算符前后总是有空格
x = 0;
// 其它二元操作符前后也恒有空格,对于表达式的子式可以不加空格
// 圆括号内部没有紧邻空格
v = w * x + y / z;
v = w*x + y/z;
v = w * (x + z);
// 在参数和一元操作符之间不加空格
x = -5;
++x;
if (x && !y)
...
```
### 垂直留白
看情况,根据个人习惯,增强可读性即可

View File

@@ -0,0 +1,528 @@
<!-- TOC -->
- [编写目的](#%E7%BC%96%E5%86%99%E7%9B%AE%E7%9A%84)
- [代码规范描述](#%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83%E6%8F%8F%E8%BF%B0)
- [头文件](#%E5%A4%B4%E6%96%87%E4%BB%B6)
- [头文件保护](#%E5%A4%B4%E6%96%87%E4%BB%B6%E4%BF%9D%E6%8A%A4)
- [内联及结构体](#%E5%86%85%E8%81%94%E5%8F%8A%E7%BB%93%E6%9E%84%E4%BD%93)
- [前置声明](#%E5%89%8D%E7%BD%AE%E5%A3%B0%E6%98%8E)
- [内联函数](#%E5%86%85%E8%81%94%E5%87%BD%E6%95%B0)
- [头文件包含顺序](#%E5%A4%B4%E6%96%87%E4%BB%B6%E5%8C%85%E5%90%AB%E9%A1%BA%E5%BA%8F)
- [作用域](#%E4%BD%9C%E7%94%A8%E5%9F%9F)
- [静态变量](#%E9%9D%99%E6%80%81%E5%8F%98%E9%87%8F)
- [局部变量](#%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F)
- [函数](#%E5%87%BD%E6%95%B0)
- [输出参数](#%E8%BE%93%E5%87%BA%E5%8F%82%E6%95%B0)
- [简短函数](#%E7%AE%80%E7%9F%AD%E5%87%BD%E6%95%B0)
- [语言特性](#%E8%AF%AD%E8%A8%80%E7%89%B9%E6%80%A7)
- [const](#const)
- [整形](#%E6%95%B4%E5%BD%A2)
- [位可移植性](#%E4%BD%8D%E5%8F%AF%E7%A7%BB%E6%A4%8D%E6%80%A7)
- [预处理宏](#%E9%A2%84%E5%A4%84%E7%90%86%E5%AE%8F)
- [NULL和0](#null%E5%92%8C0)
- [sizeof](#sizeof)
- [别名](#%E5%88%AB%E5%90%8D)
- [动态分配](#%E5%8A%A8%E6%80%81%E5%88%86%E9%85%8D)
- [命名约定](#%E5%91%BD%E5%90%8D%E7%BA%A6%E5%AE%9A)
- [通用命名规则](#%E9%80%9A%E7%94%A8%E5%91%BD%E5%90%8D%E8%A7%84%E5%88%99)
- [文件命名](#%E6%96%87%E4%BB%B6%E5%91%BD%E5%90%8D)
- [类型命名](#%E7%B1%BB%E5%9E%8B%E5%91%BD%E5%90%8D)
- [变量命名](#%E5%8F%98%E9%87%8F%E5%91%BD%E5%90%8D)
- [常量命名、枚举型变量以及宏](#%E5%B8%B8%E9%87%8F%E5%91%BD%E5%90%8D%E6%9E%9A%E4%B8%BE%E5%9E%8B%E5%8F%98%E9%87%8F%E4%BB%A5%E5%8F%8A%E5%AE%8F)
- [函数命名](#%E5%87%BD%E6%95%B0%E5%91%BD%E5%90%8D)
- [注释](#%E6%B3%A8%E9%87%8A)
- [注释风格](#%E6%B3%A8%E9%87%8A%E9%A3%8E%E6%A0%BC)
- [注释示例](#%E6%B3%A8%E9%87%8A%E7%A4%BA%E4%BE%8B)
- [格式](#%E6%A0%BC%E5%BC%8F)
- [行长度](#%E8%A1%8C%E9%95%BF%E5%BA%A6)
- [非ASCII字符](#%E9%9D%9Eascii%E5%AD%97%E7%AC%A6)
- [空格](#%E7%A9%BA%E6%A0%BC)
- [函数声明与定义](#%E5%87%BD%E6%95%B0%E5%A3%B0%E6%98%8E%E4%B8%8E%E5%AE%9A%E4%B9%89)
- [浮点字面量](#%E6%B5%AE%E7%82%B9%E5%AD%97%E9%9D%A2%E9%87%8F)
- [函数调用](#%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8)
- [列表初始化](#%E5%88%97%E8%A1%A8%E5%88%9D%E5%A7%8B%E5%8C%96)
- [条件语句](#%E6%9D%A1%E4%BB%B6%E8%AF%AD%E5%8F%A5)
- [循环和开关选择语句](#%E5%BE%AA%E7%8E%AF%E5%92%8C%E5%BC%80%E5%85%B3%E9%80%89%E6%8B%A9%E8%AF%AD%E5%8F%A5)
- [指针和引用表达式](#%E6%8C%87%E9%92%88%E5%92%8C%E5%BC%95%E7%94%A8%E8%A1%A8%E8%BE%BE%E5%BC%8F)
- [布尔表达式](#%E5%B8%83%E5%B0%94%E8%A1%A8%E8%BE%BE%E5%BC%8F)
- [返回值](#%E8%BF%94%E5%9B%9E%E5%80%BC)
- [变量及数组初始化](#%E5%8F%98%E9%87%8F%E5%8F%8A%E6%95%B0%E7%BB%84%E5%88%9D%E5%A7%8B%E5%8C%96)
- [预处理指令](#%E9%A2%84%E5%A4%84%E7%90%86%E6%8C%87%E4%BB%A4)
- [水平留白](#%E6%B0%B4%E5%B9%B3%E7%95%99%E7%99%BD)
- [垂直留白](#%E5%9E%82%E7%9B%B4%E7%95%99%E7%99%BD)
<!-- /TOC -->
## 编写目的
该文档用作提供 SDK 开发的代码规范说明,开发者提交代码前应仔细阅读。
## 代码规范描述
### 头文件
#### 头文件保护
头文件应支持c++引用,并提供保护头,根据开源治理要求,本项目中保护头定义如下:
`IOT_HUB_DEVICE_C_SDK_<dir_path>_INC_<file_name>_`
比如 `mqtt_client.h`
```c
#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_MQTT_CLIENT_INC_MQTT_CLIENT_H_
#define IOT_HUB_DEVICE_C_SDK_SERVICES_MQTT_CLIENT_INC_MQTT_CLIENT_H_
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_MQTT_CLIENT_INC_MQTT_CLIENT_H_
```
#### 内联及结构体
头文件中若声明联函数及结构体,则必须定义,即如果对外提供结构体以及内联函数,请务必放到头文件中定义。
#### 前置声明
尽量避免前置声明,而采用包含头文件。
#### 内联函数
内联函数不超过10行。
C语言使用内联函数需要注意C99才开始支持并且一般需要编译器开了优化选项才支持这个跟C++不一样。
#### 头文件包含顺序
通常系统头文件会放到相关头文件中,以保证相关头文件的易用性。
```c
dir2/foo2.h
C
`.h`
`.h`
```
---
### 作用域
#### 静态变量
在源文件中定义一个不需要被外部引用的变量时,可以将它们声明为`static`。但是不要在头文件中这么做。
#### 局部变量
将函数变量尽可能置于最小作用域内,并在变量声明时进行初始化。
```c
int i = 1;
while (const char* p = strchr(str, '/')) str = p + 1;
```
---
### 函数
#### 输出参数
1. 推荐优先使用返回值作为函数输出。函数的参数列表排序为:输入参数在前,输出参数在后。
2. 输入参数,通常是值参或者是 const 引用,纯输出参数和输入兼输出参数则是非 const 指针。
3. 参数排序时,请将所有的纯输入参数置于所有输出参数之前。
以上从易读性角度考虑,不做强制要求,但是在函数的`doxygen`注释中需要通过`@param[in]``@param[out]``@param[in,out]`列出。
#### 简短函数
如果一个函数超过了 40 行,则可以思考下,能否在不破坏程序结构的前提之下,对函数进行拆分。
以上从易读性角度考虑,当`review`时超过3次无法看懂即需考虑进行拆分。
---
### 语言特性
#### const
在 API 里,只要合理,就应该使用 const
- 如果函数不会修改传你入的引用或指针类型参数,该参数应声明为 const
- 按值传入的函数参数const 对调用者没有影响,因此不建议在函数声明中使用
- 尽可能将方法声明为 const除非它们改变了对象的逻辑状态否则不能安全地并发调用它们。
#### 整形
1. 在 C 整型中, 只使用 `int`。在合适的情况下,推荐使用标准类型如 `size_t``ptrdiff_t`。大多数情况下,`int`即可覆盖。
2. `<stdint.h>`定义了 `int16_t``uint32_t``int64_t` 等整型,在需要确保整型大小时可以使用它们代替 short、unsigned long long 等。
#### 位可移植性
1. 请记住sizeof(void *) != sizeof(int)。如果需要指针大小的整数,请使用 `intptr_t`
2. 根据需要使用大括号初始化来创建 64 位常量
```c
int64_t my_value{0x123456789};
uint64_t my_mask{3ULL << 48};
```
#### 预处理宏
使用宏时要非常谨慎,尽量以内联函数,枚举和常量代替。往用宏展开性能关键的代码,现在可以用内联函数替代。用宏表示常量可被 const 或constexpr 变量代替。用宏 "缩写" 长变量名可被引用代替。
下面给出的用法模式可以避免使用宏带来的问题;如果你要用宏,尽可能遵守:
- 不要在 .h 文件中定义宏,常见的通用配置需要在头文件中定义,宏命名应清晰且易区分。
- 在马上要使用时才进行 #define,使用后要立即 #undef
- 不要只是对已经存在的宏使用 #undef,选择一个不会冲突的名称。
- 不要用 ## 处理函数,类和变量的名字。
#### NULL和0
整数用 0浮点数用 0.0,指针用 NULL字符用 '\0'。
#### sizeof
尽可能用 sizeof(varname) 代替 sizeof(type)。
#### 别名
不要把仅用于实现的别名放到公共 API 里;公共 API 里只放用户使用的别名。
在定义公共别名时,把新名字的意图写入文档,包括:是否保证始终和当前指向的类型相同,或是否打算使用更有限的兼容性。这让用户知道:是否可以将这些类型视为可替换类型,或者是否必须遵循更特定的规则,并且帮助实现部分保留别名更改的自由度。
#### 动态分配
如果必须使用动态分配,那么更倾向于将所有权保持在分配者手中。如果其他地方要使用这个对象,最好传递它的拷贝,或者传递一个不用改变所有权的指针或引用。
### 命名约定
#### 通用命名规则
函数命名、变量命名、文件命名要有描述性,少用缩写。
#### 文件命名
文件名要全部小写,可以包含下划线 "`_`" 或连字符 "`-`",依照项目的约定.如果没有约定,那么 "`_`" 更好。
不要使用已经存在于 /usr/include 下的文件名,如 db.h。
#### 类型命名
结构体、类型定义 (typedef)、枚举——均使用相同约定,即以大写字母开始,每个单词首字母均大写,不包含下划线。
```c
struct UrlTableProperties {
string name;
int num_entries;
};
// 枚举
enum UrlTableErrors { ...
```
#### 变量命名
变量 (包括函数参数) 和数据成员名一律小写,单词之间用下划线连接。其中全局变量中`static`修饰的以`sg`开头,非`static`修饰的以`g`开头
```c
static int sg_count = 0;
int g_count = 0;
```
#### 常量命名、枚举型变量以及宏
声明为const 的变量、枚举型变量或者宏,或在程序运行期间其值始终保持不变的,全部使用大写,单词用下划线`_`隔开。
```c
const MAX_COUNT = 10;
#define MAX_SIZE_OF_NUM 10
enum PinStateType {
PIN_OFF,
PIN_ON
};
```
#### 函数命名
内部函数命名均已小写形式,加下划线隔开
```c
const char *iot_ca_get(void);
```
对外API以及跨平台移植接口使用大小写混合用下划线隔开并以`IOT`或者`HAL`开头
```c
void *IOT_OTA_Init(const char *product_id, const char *device_name, void *ch_signal);
int HAL_AT_Uart_Send(void *data, uint32_t size)
```
---
### 注释
#### 注释风格
统一使用`doxegen`注释风格。
#### 注释示例
- 文件头:所有文件的开头应该包含以下内容:
```c
/**
* @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_packet_deserialize.c
* @brief implements mqtt packet deserialize. Reference paho.mqtt.embedded-c &
* http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.pdf
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-21
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-21 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
```
- 函数注释:参数的输入/输出必须指明
```c
/**
* @brief Deserialize the supplied (wire) buffer into publish data. See 3.3.
*
* @param[in] buf the raw buffer data, of the correct length determined by the remaining length field
* @param[in] buf_len the length in bytes of the data in the supplied buffer
* @param[out] flags the MQTT dup, qos, retained flag
* @param[out] packet_id returned integer - the MQTT packet identifier
* @param[out] topic_name returned string - the MQTT topic in the publish
* @param[out] topic_len returned integer - the length of the MQTT topic
* @param[out] payload returned byte buffer - the MQTT publish payload
* @param[out] payload_len returned integer - the length of the MQTT payload
* @return @see MQTTPacketErrCode
*/
```
- 结构体注释:结构体成员必须采用`/**< xxx */`才能显示在`doxygen`
```c
/**
* @brief Defines the MQTT "Last Will and Testament" (LWT) settings for the connect packet.
*
*/
typedef struct {
const char* topic_name; /**< The LWT topic to which the LWT message will be published */
const char* message; /**< The LWT payload */
uint8_t retained; /**< The retained flag for the LWT message */
uint8_t qos; /**< The quality of service setting for the LWT message */
} MQTTPacketWillOptions;
```
- 宏注释:对于需要注释的宏,需要在之前使用以下格式才能显示在`doxygen`
```
/**
* @brief header 1 byte + remaining length 1~4 byte(s).
*
*/
#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
```
---
### 格式
除了命名,格式请以`clang-format`格式化结果为准,可参考[SDK代码格式说明](./SDK代码格式说明.md)
#### 行长度
每一行代码最大字符数为 120。
#### 非ASCII字符
尽量不使用非 ASCII 字符,使用时必须使用 UTF-8 编码。
非 ASCII 字符(中文等)只能用于注释当中,含有中文注释的代码文件必须使用 UTF-8 编码保存不能有BOM头。
#### 空格
只使用空格,每次缩进 4 个空格。
不要在代码中使用制表符。你应该设置编辑器将制表符转为空格。
#### 函数声明与定义
返回类型和函数名在同一行,参数也尽量放在同一行,如果放不下就对形参分行,分行方式与函数调用一致。
- 使用好的参数名。
- 只有在参数未被使用或者其用途非常明显时,才能省略参数名。
- 如果返回类型和函数名在一行放不下,分行。
- 如果返回类型与函数声明或定义分行了,不要缩进。
- 左圆括号总是和函数名在同一行。
- 函数名和左圆括号间永远没有空格。
- 圆括号与参数间没有空格。
- 左大括号总在最后一个参数同一行的末尾处,不另起新行。
- 右大括号总是单独位于函数最后一行,或者与左大括号同一行。
- 右圆括号和左大括号间总是有一个空格。
- 所有形参应尽可能对齐。
- 缺省缩进为 4 个空格。
- 换行后的参数保持 4 个空格的缩进。
#### 浮点字面量
浮点字面常量都要带小数点小数点两边都要有数字即使它们使用指数表示法。如果所有浮点数都采用这种熟悉的形式可以提高可读性不会被误认为是整数指数符号的E/e不会被误认为十六进制数字。可以使用整数字面常量初始化浮点变量(假设变量类型可以精确地表示该整数),但是请注意,指数表示法中的数字绝不能用整数。
```c
float f = 1.0f;
float f2 = 1; // 也可以用整数字面常量初始化浮点数
long double ld = -0.5L;
double d = 1248.0e6;
```
#### 函数调用
允许3种格式在一行内写完函数调用在圆括号里对参数分行或者参数另起一行且缩进四格。
原则上,尽可能精简行数,比如把多个参数适当地放在同一行里。
函数调用遵循如下形式:
```c
bool retval = DoSomething(argument1, argument2, argument3);
```
如果同一行放不下,可断为多行,后面每一行都和第一个实参对齐,左圆括号后和右圆括号前不要留空格:
```c
bool retval = DoSomething(averyveryveryverylongargument1,
argument2, argument3);
```
参数也可以放在次行,缩进四格:
```c
if (...) {
...
...
if (...) {
DoSomething(
argument1, argument2, // 4 空格缩进
argument3, argument4);
}
```
除非影响到可读性,尽量把多个参数放在同一行,以减少函数调用所需的行数。有人认为把每个参数都独立成行,不仅更好读,而且方便编辑参数
#### 列表初始化
列表初始化格式,函数调用如何格式化,就如何格式化列表初始化。
#### 条件语句
建议不在圆括号内使用空格。关键字 if 和 else 另起一行。
在所有情况下if 和左圆括号间都要有个空格。右圆括号和左大括号之间也要有个空格
```c
if (condition) { // 好 - IF 和 { 都与空格紧邻
```
如果能增强可读性,简短的条件语句允许写在同一行。只有在语句简单并且没有使用 else 子句时允许使用:
```c
if (x == kFoo) return new Foo();
if (x == kBar) return new Bar();
```
通常,单行语句不需要使用大括号,如果用也没问题;复杂的条件或循环语句用大括号可读性会更好。也有一些项目要求 if必须总是使用大括号
```c
if (condition) {
DoSomething(); // 4 空格缩进.
}
```
#### 循环和开关选择语句
switch 语句中的 case 块可以使用大括号也可以不用,取决于个人选择。如果有不满足 case 条件的枚举值switch 应该总是包含一个 default匹配 (如果有输入值没有 case 去处理, 编译器将给出 warning)。如果default 永远执行不到,简单的使用 assert语句。
```c
switch (var) {
case 0: { // 2 空格缩进
... // 4 空格缩进
break;
}
case 1: {
...
break;
}
default: {
assert(false);
}
}
```
空循环体应使用 {} 或 continue而不是一个简单的分号。
#### 指针和引用表达式
句点或箭头前后不要有空格。 `(*, &)` 作为指针/地址操作符时之后不能有空格。`(*, &)`在用于声明指针变量或参数时,空格前置后置都可以。
#### 布尔表达式
如果一个布尔表达式超过标准行宽 `<line-length>`,断行方式要统一。比如,逻辑操作符要么都在行尾,要么都在行首。
#### 返回值
不要在 return 表达式里加上非必须的圆括号。
#### 变量及数组初始化
变量及数组初始化用 =() 和 {} 均可。
#### 预处理指令
预处理指令不要缩进,从行首开始。即使预处理指令位于缩进代码块中,指令也应从行首开始。
```c
// 好 - 指令从行首开始
if (lopsided_score) {
#if DISASTER_PENDING // 正确 - 从行首开始
DropEverything();
# if NOTIFY // 非必要 - # 后跟空格
NotifyClient();
# endif
#endif
BackToNormal();
}
```
#### 水平留白
水平留白的使用取决于代码的位置。永远不要在行尾添加没意义的留白。
#### 垂直留白
这不仅仅是规则而是原则问题: 不在万不得已,不要使用空行,尤其是:两个函数定义之间的空行不要超过 2 行,函数体首尾不要留空行,函数体中也不要随意添加空行。
基本原则是: 一屏显示的代码越多,程序的控制流越容易理解。当然,过于密集的代码块和过于疏松的代码块同样难看,这取决于你的判断,但通常是垂直留白越少越好。
下面的规则可以让加入的空行更有效:
- 函数体内开头或结尾的空行可读性微乎其微。
- 在多重 if-else 块里加空行或许有点可读性。

View File

@@ -0,0 +1,34 @@
<!-- TOC -->
- [简介](#%E7%AE%80%E4%BB%8B)
- [数据模板创建](#%E6%95%B0%E6%8D%AE%E6%A8%A1%E6%9D%BF%E5%88%9B%E5%BB%BA)
- [数据模板描述文件导出](#%E6%95%B0%E6%8D%AE%E6%A8%A1%E6%9D%BF%E6%8F%8F%E8%BF%B0%E6%96%87%E4%BB%B6%E5%AF%BC%E5%87%BA)
- [数据模板模板代码生成](#%E6%95%B0%E6%8D%AE%E6%A8%A1%E6%9D%BF%E6%A8%A1%E6%9D%BF%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90)
<!-- /TOC -->
## 简介
本文介绍基于物联开发平台 IoT Explorer 创建的数据模板如何生成模板代码。
## 数据模板创建
参阅[产品定义](https://cloud.tencent.com/document/product/1081/34739?!preview&!editLang=zh#.E6.95.B0.E6.8D.AE.E6.A8.A1.E6.9D.BF)创建数据模板
## 数据模板描述文件导出
数据模板描述文件是一个 JSON 格式的文件描述了产品定义的属性、事件及其他信息在平台导出此json文件如下图示:
![data_template_json](https://main.qcloudimg.com/raw/0951d7c3f540ca716442e08651a0efa5.jpg)
## 数据模板模板代码生成
将下载的json文件拷贝到tools目录执行`./data_template_codegen.py -c xx/example_config.json -d ../targetdir/`命令,则会根据json文件在target目录生成所定义产品的数据模板及事件的配置文件,
将这个生成的文件拷贝到`app/data_template`的目录下,
data_template_app.c 示例了智能灯的数据模板处理框架,可以基于此框架修改业务逻辑。
```bash
./data_template_codegen.py -c example_config.json
加载 .\example_config.json 文件成功
文件 ./data_template_config_header.include 生成成功
文件 ./data_template_config_src_c.include 生成成功
```

View File

@@ -0,0 +1,340 @@
<!-- TOC -->
- [简介](#%E7%AE%80%E4%BB%8B)
- [数据模板协议](#%E6%95%B0%E6%8D%AE%E6%A8%A1%E6%9D%BF%E5%8D%8F%E8%AE%AE)
- [概述](#%E6%A6%82%E8%BF%B0)
- [设备属性上报](#%E8%AE%BE%E5%A4%87%E5%B1%9E%E6%80%A7%E4%B8%8A%E6%8A%A5)
- [设备远程控制](#%E8%AE%BE%E5%A4%87%E8%BF%9C%E7%A8%8B%E6%8E%A7%E5%88%B6)
- [获取设备最新上报信息](#%E8%8E%B7%E5%8F%96%E8%AE%BE%E5%A4%87%E6%9C%80%E6%96%B0%E4%B8%8A%E6%8A%A5%E4%BF%A1%E6%81%AF)
- [设备事件上报](#%E8%AE%BE%E5%A4%87%E4%BA%8B%E4%BB%B6%E4%B8%8A%E6%8A%A5)
- [设备行为调用](#%E8%AE%BE%E5%A4%87%E8%A1%8C%E4%B8%BA%E8%B0%83%E7%94%A8)
<!-- /TOC -->
## 简介
用户创建完产品后即可定义数据模板基于导出数据模板json配置文件通过SDK提供的脚本工具可以自动生成数据模板配置代码设备调试阶段的在线调试会接收设备的上报数据并可在控制台下发控制指令到设备进行调试。本文档介绍数据模板协议。
## 数据模板协议
### 概述
物联网开发平台定义了一套通用的方法,实现设备的统一描述、统一控制,进而提供数据的流转和计算服务,实现不同设备的互联互通、数据的流转和融合,助力应用落地。
产品定义了数据模板以后,设备可以按照数据模板中的定义上报属性、事件,并可对设备下发远程控制指令,即对可写的设备属性进行修改。数据模板的管理详见 [产品定义](https://cloud.tencent.com/document/product/1081/34739)。数据模板协议包括了以下几部分。
- 设备属性上报:设备端将定义的属性根据设备端的业务逻辑向云端上报。
- 设备远程控制:从云端向设备端下发控制指令,即设置设备可写属性。
- 获取设备最新上报信息:获取设备最新的上报数据。
- 设备事件上报:设备可根据定义的数据模板中的事件,当事件被触发,则根据设备事件上报的协议上报告警、故障等事件信息。
- 设备行为:云端可以通过 RPC 的方式来要求设备执行某个动作行为,适用于应用需要实时获取设备的执行结果的场景。
### 设备属性上报
1.当设备需要向云端上报数据时,开发平台为设备设定了默认的 Topic
- 上行请求 Topic`$thing/up/property/{ProductID}/{DeviceName}`
- 上行响应 Topic`$thing/down/property/{ProductID}/{DeviceName}`
2.请求
- 设备端请求报文示例:
```json
{
"method":"report",
"clientToken":"123",
"timestamp":1212121221,
"params":{
"power_switch":1,
"color":1,
"brightness":32
}
}
```
- 参数说明:
|参数|类型|说明|
|---|---|---|
|method|String|`report`表示设备属性上报|
|clientToken|String|用于上下行消息配对标识|
|timestamp|Integer|属性上报的时间SDK默认省略|
|params|JSON|JSON 结构内为设备上报的属性值|
- sdk接口`IOT_DataTemplate_PropertyReport`
3.响应
- 云端返回设备端报文示例:
```json
{
"method":"report_reply",
"clientToken":"123",
"code":0,
"status":"some message where error"
}
```
- 响应参数说明:
|参数|类型|说明|
|---|---|---|
|method|String|`report_reply`表示云端接收设备上报后的响应报文|
|clientToken|String|用于上下行消息配对标识|
|code|Integer|`0`表示云端成功收到设备上报的属性|
|status|String|当code非0的时候, 提示错误信息|
- sdk回调`PropertyMessageCallback.method_control_callback`
### 设备远程控制
1.云端下发控制指令使用的 Topic
- 下发 Topic`$thing/down/property/{ProductID}/{DeviceName}`
- 响应 Topic`$thing/up/property/{ProductID}/{DeviceName}`
2.请求
- 远程控制请求消息格式:
```json
{
"method": "control",
"clientToken": "123",
"params": {
"power_switch": 1,
"color": 1,
"brightness": 66
}
}
```
- 请求参数说明:
|参数|类型|说明|
|---|---|---|
|method|String|control 表示云端向设备发起控制请求|
|clientToken|String|用于上下行消息配对标识|
|timestamp|Integer|属性上报的时间SDK默认省略|
|params|JSON|JSON 结构内为设备上报的属性值|
- sdk回调`PropertyMessageCallback.method_control_callback`
3.响应
- 设备响应远程控制请求消息格式:
```json
{
"method":"control_reply",
"clientToken":"123",
"code":0,
"status":"some message when error"
}
```
- 请求参数说明:
|参数|类型|说明|
|---|---|---|
|method|String|`control_reply`表示设备向云端下发的控制指令的请求响应|
|clientToken|String|用于上下行消息配对标识|
|code|Integer|0 表示设备成功接收到云端下发的控制指令|
|status|String|当code非0的时候, 提示错误信息|
- sdk接口`IOT_DataTemplate_PropertyControlReply`
### 获取设备最新上报信息
1.设备从云端接收最新消息使用的 Topic
- 请求 Topic`$thing/up/property/{ProductID}/{DeviceName}`
- 响应 Topic`$thing/down/property/{ProductID}/{DeviceName}`
2.请求
- 请求消息格式:
```json
{
"method": "get_status",
"clientToken": "123",
"type" : "control",
"showmeta": 0
}
```
- 请求参数说明:
|参数|类型|说明|
|---|---|---|
|method|String|`get_status`表示获取设备最新上报的信息|
|clientToken|String|消息ID回复的消息将会返回该数据用于请求响应消息的对比|
|type|String|表示获取什么类型的信息。`control`表示离线下发的消息;`report`表示设备上报的信息。SDK默认省略获取所有消息|
|showmeta|Integer|标识回复消息是否带`metadata`缺省为0表示不返回`metadata`主要是时间戳信息。SDK默认省略|
- sdk接口`IOT_DataTemplate_PropertyGetStatus`
3.响应
- 响应消息格式:
```json
{
"method": "get_status_reply",
"clientToken": "123",
"code": 0,
"type": "report",
"data": {
"reported": {
"power_switch": 1,
"color": 1,
"brightness": 66
}
}
}
```
- 响应参数
|参数|类型|说明|
|---|---|---|
|method|String|get_status_reply 表示获取设备最新上报信息的 reply 消息|
|clientToken|String|消息 ID回复的消息将会返回该数据用于请求响应消息的对比|
|code|Integer|0标识云端成功收到设备上报的属性|
|type|String|表示获取什么类型的信息。control 表示离线下发的消息report 表示设备上报的信息|
|data|JSON|返回具体设备上报的最新数据内容|
- sdk回调`PropertyMessageCallback.method_get_status_reply_callback`
### 设备事件上报
1.当设备需要向云端上报事件时,开发平台为设备设定了默认的 Topic
- 上行请求 Topic`$thing/up/event/<产品>/<设备>`
- 上行响应 Topic`$thing/down/event/<产品>/<设备>`
2.请求
- 设备端请求报文示例:
```json
{
"method":"event_post",
"clientToken":"123",
"version":"1.0",
"eventId":"PowerAlarm",
"type":"fatal",
"timestamp":1212121221,
"params":{
"Voltage":2.8,
"Percent":20
}
}
```
- 请求参数说明:
|参数|类型|说明|
|---|---|---|
|method|String|`event_post`表示事件上报|
|clientToken|String|消息 ID回复的消息将会返回该数据用于请求响应消息的对比|
|version|String|协议版本默认为1.0|
|eventId|String|事件的 Id在物模型事件中定义|
|type|String|事件类型,`info`:信息;`alert`:告警;`fault`:故障|
|timestamp|String|事件的参数,在物模型事件中定义|
|params|String|事件的参数,在物模型事件中定义|
- sdk接口`IOT_DataTemplate_EventPost`
3.响应
- 响应消息格式:
```json
{
"method": "event_reply",
"clientToken": "123",
"version": "1.0",
"code": 0,
"status": "some message where error",
"data": {}
}
```
- 响应参数:
|参数|类型|说明|
|---|---|---|
|method|String|`event_reply`表示是云端返回设备端的响应|
|clientToken|String|消息 ID回复的消息将会返回该数据用于请求响应消息的对比|
|version|String|协议版本默认为1.0|
|code|Integer|事件上报结果0表示成功|
|status|String|事件上报结果描述|
|data|JSON|事件上报返回的内容|
- sdk回调`EventMessageCallback.method_event_reply_callback`
### 设备行为调用
1.当应用通过云端向设备发起某个行为调用时,开发平台为设备行为的处理设定了默认的 Topic
- 应用调用设备行为 Topic`$thing/down/action/<产品>/<设备>`
- 设备响应行为执行结果 Topic`$thing/up/action/<产品>/<设备>`
2.请求
- 应用端发起设备行为调用报文示例:
```json
{
"method": "action",
"clientToken": "20a4ccfd-d308-11e9-86c6-5254008a4f10",
"actionId": "openDoor",
"timestamp": 1212121221,
"params": {
"userid": "323343"
}
}
```
- 请求参数说明:
|参数|类型|说明|
|---|---|---|
|method|String|`action`表示是调用设备的某个行为|
|clientToken|String|消息 ID回复的消息将会返回该数据用于请求响应消息的对比|
|actionId|String|`actionId`是数据模板中的行为标识符,由开发者自行根据设备的应用场景定义|
|timestamp|Integer|行为调用的当前时间SDK默认省略|
|params|String|行为的调用参数,在数据模板的行为中定义|
- sdk回调`ActionMessageCallback.method_action_callback`
3.响应
- 响应消息格式:
```json
{
"method": "action_reply",
"clientToken": "20a4ccfd-d308-11e9-86c6-5254008a4f10",
"code": 0,
"status": "some message where error",
"response": {
"Code": 0
}
}
```
- 响应参数:
|参数|类型|说明|
|---|---|---|
|method|String|`action_reply`表示是设备端执行完指定的行为向云端回复的响应|
|clientToken|String|消息 ID回复的消息将会返回该数据用于请求响应消息的对比|
|code|Integer|行为执行结果0表示成功|
|status|String|行为执行失败后的错误信息描述|
|response|String|设备行为中定义的返回参数,设备行为执行成功后,向云端返回执行结果|
- sdk接口`IOT_DataTemplate_ActionReply`

View File

@@ -0,0 +1,162 @@
<!-- TOC -->
- [数据模板业务逻辑开发发](#%E6%95%B0%E6%8D%AE%E6%A8%A1%E6%9D%BF%E4%B8%9A%E5%8A%A1%E9%80%BB%E8%BE%91%E5%BC%80%E5%8F%91%E5%8F%91)
- [下行业务逻辑实现](#%E4%B8%8B%E8%A1%8C%E4%B8%9A%E5%8A%A1%E9%80%BB%E8%BE%91%E5%AE%9E%E7%8E%B0)
- [上行业务逻辑实现现](#%E4%B8%8A%E8%A1%8C%E4%B8%9A%E5%8A%A1%E9%80%BB%E8%BE%91%E5%AE%9E%E7%8E%B0%E7%8E%B0)
<!-- /TOC -->
## 数据模板业务逻辑开发发
数据模板示例`data_template_app.c`已实现数据、事件收发及响应的通用处理框架。可以基于此示例开发业务逻辑,
上下行业务逻辑处理的入口函数分别为 `_usr_init``_method_control_callback``_cycle_report``_method_action_callback`
- 属性
属性即对设备能力的描述,譬如智能灯,通过 `开关``颜色``亮度`三个属性实现对智能灯的能力描述,通过对属性的修改即可实现对设备的控制。
- 事件
设备发生特定情况,譬如灯的开关状态发生了变化,上报事件。应用侧收到事件后按预设逻辑推送事件。
- 行为
控制设备执行特定的行为,并将执行的结果返回。行为与属性的区别,概念上行为是数据和方法的组合,行为有执行结果的返回。属性只有数据,修改属性后设备侧是否执行成功很难在属性本身体现。
### 下行业务逻辑实现
- 服务端下行的数据SDK已按数据模板协议完成了 json 数据的解析。
- 用户根据数据模板数据进行相应的业务逻辑处理,以下提供基于智能灯的示例,用户可以根据自己的物模型进行修改
```c
static void _handle_property_callback(void *client, int is_get_status)
{
for (UsrPropertyIndex i = USR_PROPERTY_INDEX_POWER_SWITCH; i <= USR_PROPERTY_INDEX_POWER; i++) {
if (usr_data_template_property_status_get(i)) { // 如果状态改变,说明收到了相应的属性
DataTemplatePropertyValue value;
switch (i) {
case USR_PROPERTY_INDEX_POWER_SWITCH:
case USR_PROPERTY_INDEX_COLOR:
case USR_PROPERTY_INDEX_BRIGHTNESS: // read only, just for example
case USR_PROPERTY_INDEX_POWER: // read only, just for example
value = usr_data_template_property_value_get(i); // 获取相应的属性值
Log_d("recv %s:%d", usr_data_template_property_key_get(i), value.value_int);
break;
case USR_PROPERTY_INDEX_NAME: // read only, just for example
value = usr_data_template_property_value_get(i);
Log_d("recv %s:%s", usr_data_template_property_key_get(i), value.value_string);
break;
case USR_PROPERTY_INDEX_POSITION: // read only, just for example
for (UsrPropertyPositionIndex j = USR_PROPERTY_POSITION_INDEX_LONGITUDE;
j <= USR_PROPERTY_POSITION_INDEX_LATITUDE; j++) {
value = usr_data_template_property_struct_value_get(i, j);
Log_d("recv %s:%d", usr_data_template_property_struct_key_get(i, j), value.value_int);
}
break;
default:
break;
}
usr_data_template_property_status_reset(i); //处理完需要重置属性状态
}
}
}
```
```c
static void _method_action_callback(UtilsJsonValue client_token, UtilsJsonValue action_id, UtilsJsonValue params,
void *usr_data)
{
UsrActionIndex index;
int rc;
DataTemplatePropertyValue value_time, value_color, value_total_time;
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);
// 数据模板行为 json 格式解析
rc = usr_data_template_action_parse(action_id, params, &index);
if (rc) {
return;
}
// 修改和添加下行行为数据的具体业务逻辑
switch (index) {
case USR_ACTION_INDEX_LIGHT_BLINK:
value_time = usr_data_template_action_input_value_get(USR_ACTION_INDEX_LIGHT_BLINK,
USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME);
value_color = usr_data_template_action_input_value_get(USR_ACTION_INDEX_LIGHT_BLINK,
USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR);
value_total_time = usr_data_template_action_input_value_get(USR_ACTION_INDEX_LIGHT_BLINK,
USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME);
Log_i("light[%d] blink %d every %d s ", value_color.value_enum, value_time.value_int,
value_total_time.value_int);
// 下行行为数据处理结果的回复
usr_data_template_action_reply(usr_data, buf, sizeof(buf), index, client_token, 0, "{\"err_code\":0}");
break;
default:
break;
}
}
```
### 上行业务逻辑实现现
- SDK提供了属性、事件、行为的上行接口用户可以按数据模板协议自行构造数据然后调用接口上报相应数据。
1. 属性上报接口`usr_data_template_property_report`
2. 事件上报接口`usr_data_template_event_post`
3. 行为回复接口`usr_data_template_action_reply`
- 数据模板示例 data_template_app.c 已提供了属性、事件、行为数据构造和上报的框架。
1.对于属性上报,调用修改对应属性参数的接口 `usr_data_template_property_value_set`,然后调用属性上报接口`usr_data_template_property_report`会将需要上报的属性信息上报。
```c
// 参考 _usr_init 函数,设备在业务运行过程中修改属性后需要调用以下对应函数修改对应的属性
// usr_data_template_property_report(client, buf, sizeof(buf)); 函数进行属性数据的上报
static void _usr_init(void)
{
usr_data_template_init();
DataTemplatePropertyValue value;
value.value_int = 0;
usr_data_template_property_value_set(USR_PROPERTY_INDEX_POWER_SWITCH, value);
value.value_enum = 0;
usr_data_template_property_value_set(USR_PROPERTY_INDEX_COLOR, value);
value.value_int = 10;
usr_data_template_property_value_set(USR_PROPERTY_INDEX_BRIGHTNESS, value);
value.value_string = "light";
usr_data_template_property_value_set(USR_PROPERTY_INDEX_NAME, value);
value.value_int = 30;
usr_data_template_property_struct_value_set(USR_PROPERTY_INDEX_POSITION, USR_PROPERTY_POSITION_INDEX_LONGITUDE,
value);
value.value_int = 30;
usr_data_template_property_struct_value_set(USR_PROPERTY_INDEX_POSITION, USR_PROPERTY_POSITION_INDEX_LATITUDE,
value);
value.value_string = "high";
usr_data_template_property_value_set(USR_PROPERTY_INDEX_POWER, value);
}
```
2.对于事件,在事件产生的地方,构造事件的 json 串并调用事件上报接口`usr_data_template_event_post`上报。
```c
static void _cycle_report(void *client)
{
char buf[256];
static QcloudIotTimer sg_cycle_report_timer;
if (IOT_Timer_Expired(&sg_cycle_report_timer)) {
// 添加和修改业务需要上报的事件数据
usr_data_template_event_post(client, buf, sizeof(buf), USR_EVENT_INDEX_STATUS_REPORT,
"{\"status\":0,\"message\":\"ok\"}");
IOT_Timer_Countdown(&sg_cycle_report_timer, 60);
}
}
```
3.对于行为,在行为的回调 `_method_action_callback` 中,添加业务逻辑执行对应的行为,根据行为执行结果,构造行为的输出参数,调用行为回复接口`usr_data_template_action_reply`上报行为执行结果。
```c
usr_data_template_action_reply(usr_data, buf, sizeof(buf), index, client_token, 0, "{\"err_code\":0}");
```

View File

@@ -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 qcloud_iot_debug.h
* @brief iot debug & upload debug
* @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
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_DEBUG_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_DEBUG_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "utils_log.h"
#include "qcloud_iot_config.h"
#include "qcloud_iot_platform.h"
#ifdef IOT_DEBUG
#define IOT_FUNC_ENTRY \
{ \
Log_d("FUNC_ENTRY: %s L#%d \n", __FUNCTION__, __LINE__); \
}
#define IOT_FUNC_EXIT \
{ \
Log_d("FUNC_EXIT: %s L#%d \n", __FUNCTION__, __LINE__); \
return; \
}
#define IOT_FUNC_EXIT_RC(x) \
{ \
Log_d("FUNC_EXIT: %s L#%d Return Code : %d \n", __FUNCTION__, __LINE__, x; \
return x; \
}
#else
#define IOT_FUNC_ENTRY
#define IOT_FUNC_EXIT \
{ \
return; \
}
#define IOT_FUNC_EXIT_RC(x) \
{ \
return x; \
}
#endif
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_DEBUG_H_

View File

@@ -0,0 +1,114 @@
/**
* @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 qcloud_iot_device.h
* @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
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_DEVICE_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_DEVICE_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "qcloud_iot_config.h"
/**
* @brief MAX size of client ID.
*
*/
#define MAX_SIZE_OF_CLIENT_ID (80)
/**
* @brief MAX size of product ID.
*
*/
#define MAX_SIZE_OF_PRODUCT_ID (10)
/**
* @brief MAX size of product secret.
*
*/
#define MAX_SIZE_OF_PRODUCT_SECRET (32)
/**
* @brief MAX size of device name.
*
*/
#define MAX_SIZE_OF_DEVICE_NAME (48)
/**
* @brief MAX size of device secret.
*
*/
#define MAX_SIZE_OF_DEVICE_SECRET (64)
/**
* @brief MAX size of device cert file name.
*
*/
#define MAX_SIZE_OF_DEVICE_CERT_FILE_NAME (128)
/**
* @brief MAX size of device key file name.
*
*/
#define MAX_SIZE_OF_DEVICE_SECRET_FILE_NAME (128)
/**
* @brief Max size of base64 encoded PSK = 64, after decode: 64/4*3 = 48.
*
*/
#define MAX_SIZE_OF_DECODE_PSK_LENGTH 48
/**
* @brief Device info needed to connect mqtt server.
*
*/
typedef struct {
char product_id[MAX_SIZE_OF_PRODUCT_ID + 1];
char device_name[MAX_SIZE_OF_DEVICE_NAME + 1];
char client_id[MAX_SIZE_OF_CLIENT_ID + 1];
#ifdef AUTH_MODE_CERT
char dev_cert_file_name[MAX_SIZE_OF_DEVICE_CERT_FILE_NAME + 1];
char dev_key_file_name[MAX_SIZE_OF_DEVICE_SECRET_FILE_NAME + 1];
#else
char device_secret[MAX_SIZE_OF_DEVICE_SECRET + 1];
uint8_t device_secret_decode[MAX_SIZE_OF_DECODE_PSK_LENGTH];
size_t device_secret_decode_len;
#endif
#ifdef DEV_DYN_REG_ENABLED
char product_secret[MAX_SIZE_OF_PRODUCT_SECRET + 1];
#endif
} DeviceInfo;
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_DEVICE_H_

View File

@@ -0,0 +1,97 @@
/**
* @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 qcloud_iot_error.h
* @brief error code of sdk
* @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>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_ERROR_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_ERROR_H_
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief IOT SDK return/error code.
* Enumeration of return code in QCloud IoT C-SDK.
* Values less than 0 are specific error codes
* Value of 0 is successful return
* Values greater than 0 are specific non-error return codes
*
*/
typedef enum {
QCLOUD_RET_SUCCESS = 0, /**< Successful return */
QCLOUD_ERR_FAILURE = -1, /**< Generic failure return */
QCLOUD_ERR_INVAL = -2, /**< Invalid parameter */
QCLOUD_ERR_DEV_INFO = -3, /**< Fail to get device info */
QCLOUD_ERR_MALLOC = -4, /**< Fail to malloc memory */
QCLOUD_RET_MQTT_ALREADY_CONNECTED = 4, /**< Already connected with MQTT server */
QCLOUD_RET_MQTT_CONNACK_CONNECTION_ACCEPTED = 3, /**< MQTT connection accepted by server */
QCLOUD_RET_MQTT_MANUALLY_DISCONNECTED = 2, /**< Manually disconnected with MQTT server */
QCLOUD_RET_MQTT_RECONNECTED = 1, /**< Reconnected with MQTT server successfully */
QCLOUD_ERR_MQTT_NO_CONN = -101, /**< Not connected with MQTT server */
QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT = -102, /**< Reconnecting with MQTT server */
QCLOUD_ERR_MQTT_RECONNECT_TIMEOUT = -103, /**< MQTT reconnect timeout */
QCLOUD_ERR_MQTT_SUB = -104, /**< MQTT topic subscription fail */
QCLOUD_ERR_MQTT_NOTHING_TO_READ = -105, /**< MQTT nothing to read */
QCLOUD_ERR_MQTT_PACKET_READ = -106, /**< Something wrong when reading MQTT packet */
QCLOUD_ERR_MQTT_REQUEST_TIMEOUT = -107, /**< MQTT request timeout */
QCLOUD_ERR_RX_MESSAGE_INVAL = -108, /**< MQTT received invalid msg */
QCLOUD_ERR_BUF_TOO_SHORT = -109, /**< MQTT recv buffer not enough */
QCLOUD_ERR_MQTT_QOS_NOT_SUPPORT = -110, /**< MQTT QoS level not supported */
QCLOUD_ERR_MQTT_UNSUB_FAIL = -111, /**< MQTT unsubscribe failed */
QCLOUD_ERR_MAX_TOPIC_LENGTH = -112, /**< Topic length oversize */
QCLOUD_ERR_HTTP = -201, /**< HTTP unknown error */
QCLOUD_ERR_HTTP_AUTH = -202, /**< HTTP auth failed */
QCLOUD_ERR_HTTP_NOT_FOUND = -203, /**< HTTP 404 */
QCLOUD_ERR_HTTP_TIMEOUT = -204, /**< HTTP timeout */
QCLOUD_ERR_HTTP_PARSE = -205, /**< HTTP URL parse failed */
QCLOUD_ERR_JSON_PARSE = -300, /**< JSON parsing error */
QCLOUD_ERR_TCP_SOCKET_FAILED = -401, /**< TLS TCP socket connect fail */
QCLOUD_ERR_TCP_UNKNOWN_HOST = -402, /**< TCP unknown host (DNS fail) */
QCLOUD_ERR_TCP_CONNECT = -403, /**< TCP/UDP socket connect fail */
QCLOUD_ERR_TCP_READ_TIMEOUT = -404, /**< TCP read timeout */
QCLOUD_ERR_TCP_WRITE_TIMEOUT = -405, /**< TCP write timeout */
QCLOUD_ERR_TCP_READ_FAIL = -406, /**< TCP read error */
QCLOUD_ERR_TCP_WRITE_FAIL = -407, /**< TCP write error */
QCLOUD_ERR_TCP_PEER_SHUTDOWN = -408, /**< TCP server close connection */
QCLOUD_ERR_TCP_NOTHING_TO_READ = -409, /**< TCP socket nothing to read */
QCLOUD_ERR_SSL_INIT = -501, /**< TLS/SSL init fail */
QCLOUD_ERR_SSL_CERT = -502, /**< TLS/SSL certificate issue */
QCLOUD_ERR_SSL_CONNECT = -503, /**< TLS/SSL connect fail */
QCLOUD_ERR_SSL_CONNECT_TIMEOUT = -504, /**< TLS/SSL connect timeout */
QCLOUD_ERR_SSL_WRITE_TIMEOUT = -505, /**< TLS/SSL write timeout */
QCLOUD_ERR_SSL_WRITE = -506, /**< TLS/SSL write error */
QCLOUD_ERR_SSL_READ_TIMEOUT = -507, /**< TLS/SSL read timeout */
QCLOUD_ERR_SSL_READ = -508, /**< TLS/SSL read error */
QCLOUD_ERR_SSL_NOTHING_TO_READ = -509, /**< TLS/SSL nothing to read */
} IotReturnCode;
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_ERROR_H_

View File

@@ -0,0 +1,100 @@
/**
* @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 qcloud_iot_params_check.h
* @brief check params
* @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
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_PARAMS_CHECK_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_PARAMS_CHECK_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include "utils_log.h"
#define NUMBERIC_SANITY_CHECK(num, err) \
do { \
if (0 > (num)) { \
Log_e("Invalid argument, numeric 0"); \
return (err); \
} \
} while (0)
#define NUMBERIC_SANITY_CHECK_RTN(num) \
do { \
if (0 > (num)) { \
Log_e("Invalid argument, numeric 0"); \
return; \
} \
} while (0)
#define POINTER_SANITY_CHECK(ptr, err) \
do { \
if (NULL == (ptr)) { \
Log_e("Invalid argument, %s = %p", #ptr, ptr); \
return (err); \
} \
} while (0)
#define POINTER_SANITY_CHECK_RTN(ptr) \
do { \
if (NULL == (ptr)) { \
Log_e("Invalid argument, %s = %p", #ptr, ptr); \
return; \
} \
} while (0)
#define STRING_PTR_SANITY_CHECK(ptr, err) \
do { \
if (NULL == (ptr)) { \
Log_e("Invalid argument, %s = %p", #ptr, (ptr)); \
return (err); \
} \
if (0 == strlen((ptr))) { \
Log_e("Invalid argument, %s = '%s'", #ptr, (ptr)); \
return (err); \
} \
} while (0)
#define STRING_PTR_SANITY_CHECK_RTN(ptr) \
do { \
if (NULL == (ptr)) { \
Log_e("Invalid argument, %s = %p", #ptr, (ptr)); \
return; \
} \
if (0 == strlen((ptr))) { \
Log_e("Invalid argument, %s = '%s'", #ptr, (ptr)); \
return; \
} \
} while (0)
#define STRING_PTR_PRINT_SANITY_CHECK(s) ((s) ? (s) : "null")
#if defined(__cplusplus)
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_PARAMS_CHECK_H_

View File

@@ -0,0 +1,451 @@
/**
* @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 qcloud_iot_platform.h
* @brief hal interface
* @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-09 <td>1.1 <td>fancyxu <td>support tls and change port to str format
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_PLATFORM_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_PLATFORM_H_
#if defined(__cplusplus)
extern "C" {
#endif
#include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "qcloud_iot_device.h"
#include "qcloud_iot_common.h"
/**********************************************************************
* QCloud IoT C-SDK Hardware Abstraction Layer
* Platform/OS/IP stack/SSL dependant functions
* Check platform folder for reference implementation
* Require porting when adapt SDK to new platform/OS
*********************************************************************/
/**************************************************************************************
* os
**************************************************************************************/
/**
* @brief Mutex create.
*
* @return pointer to mutex
*/
void *HAL_MutexCreate(void);
/**
* @brief Mutex destroy.
*
* @param[in,out] mutex pointer to mutex
*/
void HAL_MutexDestroy(void *mutex);
/**
* @brief Mutex lock.
*
* @param[in,out] mutex pointer to mutex
*/
void HAL_MutexLock(void *mutex);
/**
* @brief Mutex try lock.
*
* @param[in,out] mutex pointer to mutex
* @return 0 for success
*/
int HAL_MutexTryLock(void *mutex);
/**
* @brief Mutex unlock.
*
* @param[in,out] mutex pointer to mutex
*/
void HAL_MutexUnlock(void *mutex);
/**
* @brief Malloc from heap.
*
* @param[in] size size to malloc
* @return pointer to buffer, NULL for failed.
*/
void *HAL_Malloc(size_t size);
/**
* @brief Free buffer malloced by HAL_Malloc.
*
* @param[in] ptr
*/
void HAL_Free(void *ptr);
/**
* @brief Printf with format.
*
* @param[in] fmt format
*/
void HAL_Printf(const char *fmt, ...);
/**
* @brief Snprintf with format.
*
* @param[out] str buffer to save
* @param[in] len buffer len
* @param[in] fmt format
* @return length of formatted string, >0 for success.
*/
int HAL_Snprintf(char *str, const int len, const char *fmt, ...);
/**
* @brief Sleep for ms.
*
* @param[in] ms ms to sleep
*/
void HAL_SleepMs(uint32_t ms);
#ifdef MULTITHREAD_ENABLED
/**
* @brief Theard entry function.
*
*/
typedef void (*ThreadRunFunc)(void *arg);
/**
* @brief Thread priority.
*
*/
typedef enum {
THREAD_PRIORITY_HIGH,
THREAD_PRIORITY_MIDDLE,
THREAD_PRIORITY_LOW,
} ThreadPriority;
/**
* @brief Thread params to create.
*
*/
typedef struct {
char *thread_name; /**< thread name */
void* thread_id; /**< thread handle */
ThreadRunFunc thread_func; /**< thread entry function */
void *user_arg; /**< thread entry arg */
ThreadPriority priority; /**< thread priority */
void *stack_base; /**< thread stack base */
uint32_t stack_size; /**< thread stack size */
} ThreadParams;
/**
* @brief platform-dependant thread create function
*
* @param[in,out] params params to create thread @see ThreadParams
* @return @see IotReturnCode
*/
int HAL_ThreadCreate(ThreadParams *params);
/**
* @brief platform-dependent thread destroy function.
*
*/
void HAL_ThreadDestroy(void *thread_id);
/**
* @brief platform-dependent semaphore create function.
*
* @return pointer to semaphore
*/
void *HAL_SemaphoreCreate(void);
/**
* @brief platform-dependent semaphore destory function.
*
* @param[in] sem pointer to semaphore
*/
void HAL_SemaphoreDestroy(void *sem);
/**
* @brief platform-dependent semaphore post function.
*
* @param[in] sem pointer to semaphore
*/
void HAL_SemaphorePost(void *sem);
/**
* @brief platform-dependent semaphore wait function.
*
* @param[in] sem pointer to semaphore
* @param[in] timeout_ms wait timeout
* @return @see IotReturnCode
*/
int HAL_SemaphoreWait(void *sem, uint32_t timeout_ms);
/**
* @brief platform-dependent mail queue init function.
*
* @param[in] pool pool using in mail queue
* @param[in] mail_size mail size
* @param[in] mail_count mail count
* @return pointer to mail queue
*/
void *HAL_MailQueueInit(void *pool, size_t mail_size, int mail_count);
/**
* @brief platform-dependent mail queue deinit function.
*
* @param[in] mail_q pointer to mail queue
*/
void HAL_MailQueueDeinit(void *mail_q);
/**
* @brief platform-dependent mail queue send function.
*
* @param[in] mail_q pointer to mail queue
* @param[in] buf data buf
* @param[in] size data size
* @return 0 for success
*/
int HAL_MailQueueSend(void *mail_q, const void *buf, size_t size);
/**
* @brief platform-dependent mail queue send function.
*
* @param[in] mail_q pointer to mail queue
* @param[out] buf data buf
* @param[in] size data size
* @param[in] timeout_ms
* @return 0 for success
*/
int HAL_MailQueueRecv(void *mail_q, void *buf, size_t *size, uint32_t timeout_ms);
#endif
/**
* @brief Functions for saving file into NVS(files/FLASH)
* @param[in] filename file path name
* @param[in] buf source need write buffer
* @param[in] write_len length of file to write
* @return length of data save when success, or 0 for failure
*/
size_t HAL_File_Write(const char *filename, const void *buf, size_t write_len, size_t offset);
/**
* @brief Functions for reading file from NVS(files/FLASH)
* @param[in] filename file path name
* @param[in] buf destination log buffer
* @param[in] read_len length to read
* @return length of data read when success, or 0 for failure
*/
size_t HAL_File_Read(const char *filename, void *buf, size_t read_len, size_t offset);
/**
* @brief Functions for deleting file in NVS(files/FLASH).
* @param[in] filename file path name
* @return 0 when success
*/
int HAL_File_Del(const char *filename);
/**
* @brief Functions for reading the size of file in NVS(files/FLASH).
* @param[in] filename file path name
* @return 0 when nothing exist
*/
size_t HAL_File_GetSize(const char *filename);
/**************************************************************************************
* device info
**************************************************************************************/
/**
* @brief Save device info
*
* @param[in] device_info @see DeviceInfo
* @return @see IotReturnCode
*/
int HAL_SetDevInfo(DeviceInfo *device_info);
/**
* @brief Get device info
*
* @param[in] device_info @see DeviceInfo
* @return @see IotReturnCode
*/
int HAL_GetDevInfo(DeviceInfo *device_info);
/**************************************************************************************
* timer
**************************************************************************************/
/**
* @brief time format string
*
* @return time format string, such as "2021-05-31 15:58:46"
*/
char *HAL_Timer_Current(void);
/**
* @brief Get utc time ms timestamp.
*
* @return timestamp
*/
uint64_t HAL_Timer_CurrentMs(void);
/**
* @brief Set system time using second timestamp
*
* @param[in] timestamp_ms
* @return 0 for success
*/
int HAL_Timer_SetSystimeMs(uint64_t timestamp_ms);
/**************************************************************************************
* network tcp
**************************************************************************************/
/**
* @brief TCP connect in linux
*
* @param[in] host host to connect
* @param[out] port port to connect
* @return socket fd
*/
int HAL_TCP_Connect(const char *host, const char *port);
/**
* @brief TCP disconnect
*
* @param[in] fd socket fd
* @return 0 for success
*/
int HAL_TCP_Disconnect(int fd);
/**
* @brief TCP write
*
* @param[in] fd socket fd
* @param[in] buf buf to write
* @param[in] len buf len
* @param[in] timeout_ms timeout
* @param[out] written_len data written length
* @return @see IotReturnCode
*/
int HAL_TCP_Write(int fd, const uint8_t *data, uint32_t len, uint32_t timeout_ms, size_t *written_len);
/**
* @brief TCP read.
*
* @param[in] fd socket fd
* @param[out] buf buffer to save read data
* @param[in] len buffer len
* @param[in] timeout_ms timeout
* @param[out] read_len length of data read
* @return @see IotReturnCode
*/
int HAL_TCP_Read(int fd, uint8_t *data, uint32_t len, uint32_t timeout_ms, size_t *read_len);
/**************************************************************************************
* AT module
**************************************************************************************/
#ifdef AT_MODULE_ENABLE
/**
* @brief Urc handler.
*
*/
typedef void (*OnUrcHandler)(const char *data, size_t data_len);
/**
* @brief Init at module.
*
* @return 0 for success
*/
int HAL_Module_Init(void);
/**
* @brief Deinit at module.
*
*/
void HAL_Module_Deinit(void);
/**
* @brief Send at cmd to at module and wait for resp.
*
* @param[in] at_cmd at cmd
* @param[in] at_expect expect resp
* @param[in] timeout_ms wait timeout
* @return 0 for success
*/
int HAL_Module_SendAtCmdWaitResp(const char *at_cmd, const char *at_expect, uint32_t timeout_ms);
/**
* @brief Send at cmd and waif for data.
*
* @param[in] at_cmd at cmd
* @param[in] at_expect expect resp
* @param[out] recv_buf recv data buffer
* @param[out] recv_len recv data length
* @param[in] timeout_ms wait timeout
* @return 0 for success
*/
int HAL_Module_SendAtCmdWaitRespWithData(const char *at_cmd, const char *at_expect, void *recv_buf, uint32_t *recv_len,
uint32_t timeout_ms);
/**
* @brief Send date to at module.
*
* @param[in] data data to send
* @param[in] data_len data length
* @return 0 for success
*/
int HAL_Module_SendAtData(const void *data, int data_len);
/**
* @brief Set urc.
*
* @param[in] urc irc string
* @param[in] urc_handler urc handler
* @return 0 for success
*/
int HAL_Module_SetUrc(const char *urc, OnUrcHandler urc_handler);
/**
* @brief connect network
*
* @return int 0 for success
*/
int HAL_Module_ConnectNetwork(void);
#endif
#if defined(__cplusplus)
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_PLATFORM_H_

View File

@@ -0,0 +1,88 @@
/**
* @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 qcloud_iot_timer.h
* @brief timer interface
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2022-04-07
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-04-07 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_TIMER_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_TIMER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "qcloud_iot_platform.h"
/**
* @brief Define timer.
*
*/
#define QcloudIotTimer uint64_t
/**
* @brief Return if timer expired.
*
* @param[in] timer @see QcloudIotTimer
* @return true expired
* @return false no expired
*/
bool IOT_Timer_Expired(QcloudIotTimer *timer);
/**
* @brief Countdown ms.
*
* @param[in,out] timer @see QcloudIotTimer
* @param[in] timeout_ms ms to count down
*/
void IOT_Timer_CountdownMs(QcloudIotTimer *timer, uint32_t timeout_ms);
/**
* @brief Countdown second
*
* @param[in,out] timer @see QcloudIotTimer
* @param[in] timeout second to count down
*/
void IOT_Timer_Countdown(QcloudIotTimer *timer, uint32_t timeout);
/**
* @brief QcloudIotTimer remain ms.
*
* @param[in] timer @see QcloudIotTimer
* @return ms
*/
uint64_t IOT_Timer_Remain(QcloudIotTimer *timer);
/**
* @brief Get current utf timestamp of second
*
* @return timestamp
*/
uint64_t IOT_Timer_CurrentSec(void);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_COMMON_QCLOUD_IOT_TIMER_H_

View File

@@ -0,0 +1,55 @@
/**
* @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 qcloud_iot_config.h
* @brief sdk config define
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-06-01
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-06-01 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-12 <td>1.1 <td>fancyxu <td>rename AUTH_WITH_NOTLS to AUTH_WITH_NO_TLS
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_CONFIG_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_CONFIG_H_
#ifdef __cplusplus
extern "C" {
#endif
/* #undef AUTH_MODE_CERT */
#define AUTH_MODE_KEY
#define AUTH_WITH_NO_TLS
/* #undef GATEWAY_ENABLED */
/* #undef SYSTEM_COMM */
/* #undef DEV_DYN_REG_ENABLED */
/* #undef LOG_UPLOAD */
/* #undef IOT_DEBUG */
#define DEBUG_DEV_INFO_USED
#define AT_MODULE_ENABLE
#define MULTITHREAD_ENABLED
/* #undef LOG_UPLOAD_TYPE_JSON */
/* #undef LOG_UPLOAD_AES_ENCRYPT_POST */
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_CONFIG_H_

View File

@@ -0,0 +1,87 @@
/**
* @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 qcloud_iot_host.h
* @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-12 <td>1.1 <td>fancyxu <td>change port to str format
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_HOST_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_HOST_H_
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief IoT C-SDK APPID.
*
*/
#define QCLOUD_IOT_DEVICE_SDK_APPID "21010406"
#define QCLOUD_IOT_DEVICE_SDK_APPID_LEN (sizeof(QCLOUD_IOT_DEVICE_SDK_APPID) - 1)
/**
* @brief MQTT server domain.
*
*/
#define QCLOUD_IOT_MQTT_DIRECT_DOMAIN "iotcloud.tencentdevices.com"
#define MQTT_SERVER_PORT_TLS "8883"
#define MQTT_SERVER_PORT_NO_TLS "1883"
/**
* @brief Server domain for dynamic registering device.
*
*/
#define DYN_REG_SERVER_URL "ap-guangzhou.gateway.tencentdevices.com"
#define DYN_REG_URI_PATH "/device/register"
#define DYN_REG_SERVER_PORT "80"
#define DYN_REG_SERVER_PORT_TLS "443"
/**
* @brief URL for doing log upload.
*
*/
#define LOG_UPLOAD_SERVER_URL "ap-guangzhou.gateway.tencentdevices.com"
#define LOG_UPLOAD_URI_PATH "/device/reportlog"
#define LOG_UPLOAD_SERVER_PORT "80"
/**
* @brief (old)URL for doing log upload.
* Compatible with old versions, new users are recommended to use up
*
*/
#define LOG_UPLOAD_OLD_SERVER_URL "http://devicelog.iot.cloud.tencent.com/cgi-bin/report-log"
#define LOG_UPLOAD_OLD_SERVER_PORT "80"
/**
* @brief Max size of a host name.
*
*/
#define HOST_STR_LENGTH 64
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_HOST_H_

View File

@@ -0,0 +1,119 @@
/**
* @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 qcloud_iot_variables.h
* @brief variables can be set depend on user resource.
* @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
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_VARIABLES_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_VARIABLES_H_
#ifdef __cplusplus
extern "C" {
#endif
// Variables are dependant on user situation (network status/device memory/application context). Adjust the default
// value to meet your requirement
/**
* @brief default MQTT timeout value when connect/pub/sub (unit: ms)
*
*/
#define QCLOUD_IOT_MQTT_COMMAND_TIMEOUT (5 * 1000)
/**
* @brief default MQTT timeout value when wait server ack
*
*/
#define QCLOUD_IOT_MQTT_WAIT_ACK_TIMEOUT (5 * 1000)
/**
* @brief default MQTT keep alive interval (unit: s)
*
*/
#define QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL (240)
/**
* @brief default MQTT Tx buffer size, MAX: 16*1024
*
*/
#define QCLOUD_IOT_MQTT_TX_BUF_LEN (4096)
/**
* @brief default MQTT Rx buffer size, MAX: 16*1024
*
*/
#define QCLOUD_IOT_MQTT_RX_BUF_LEN (4096)
/**
* @brief default MQTT timeout value when yield
*
*/
#define QCLOUD_IOT_MQTT_YIELD_TIMEOUT (500)
/**
* @brief MAX MQTT reconnect interval (unit: ms)
*
*/
#define MAX_RECONNECT_WAIT_INTERVAL (60 * 1000)
/**
* @brief MAX valid time when connect to MQTT server. 0: always valid.
* Use this only if the device has accurate UTC time. Otherwise, set to 0.
*
*/
#define MAX_ACCESS_EXPIRE_TIMEOUT (0)
// Log upload related params, which will affect the size of device memory/disk consumption Log upload related params,
// which will affect the size of device memory/disk consumption
/**
* @brief size of buffer for log upload
*
*/
#define LOG_UPLOAD_BUFFER_SIZE 5000
/**
* @brief Max size of one http log upload. Should not larger than 5000
*
*/
#define MAX_HTTP_LOG_POST_SIZE 5000
/**
* @brief MAX size for saving log into NVS (files/FLASH) after upload fail
*
*/
#define MAX_LOG_SAVE_SIZE (3 * LOG_UPLOAD_BUFFER_SIZE)
/**
* @brief interval of log upload (unit: ms) Decrease this value if LOG_UPLOAD_BUFFER_SIZE is small
*
*/
#define LOG_UPLOAD_INTERVAL_MS 1800000
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_CONFIG_QCLOUD_IOT_VARIABLES_H_

View File

@@ -0,0 +1,70 @@
/**
* @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 qcloud_iot_common.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_INCLUDE_QCLOUD_IOT_COMMON_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_QCLOUD_IOT_COMMON_H_
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief IoT C-SDK version info
*
*/
#define QCLOUD_IOT_DEVICE_SDK_VERSION "4.0.0"
// common header file
#include "qcloud_iot_debug.h"
#include "qcloud_iot_device.h"
#include "qcloud_iot_error.h"
#include "qcloud_iot_params_check.h"
#include "qcloud_iot_platform.h"
#include "qcloud_iot_timer.h"
// config header file
#include "qcloud_iot_config.h"
#include "qcloud_iot_host.h"
#include "qcloud_iot_variables.h"
// service header file
#include "qcloud_iot_mqtt_client.h"
#include "qcloud_iot_system.h"
#include "qcloud_iot_ota.h"
#include "qcloud_iot_cos.h"
#include "qcloud_iot_http_client.h"
#include "qcloud_iot_log_upload.h"
#include "qcloud_iot_http_signed.h"
#include "qcloud_iot_dynreg.h"
#include "qcloud_iot_gateway.h"
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_QCLOUD_IOT_COMMON_H_

View File

@@ -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 qcloud_iot_explorer.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_INCLUDE_QCLOUD_IOT_EXPLORER_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_QCLOUD_IOT_EXPLORER_H_
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief IoT C-SDK version info
*
*/
#define QCLOUD_IOT_DEVICE_EXPLORER_SDK_VERSION "4.0.0"
// service header file
#include "qcloud_iot_data_template.h"
#include "qcloud_iot_file_manage.h"
#include "qcloud_iot_gateway_scene.h"
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_QCLOUD_IOT_EXPLORER_H_

View File

@@ -0,0 +1,50 @@
/**
* @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 qcloud_iot_hub.h
* @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-18 <td>1.1 <td>fancyxu <td>support broadcast
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_QCLOUD_IOT_HUB_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_QCLOUD_IOT_HUB_H_
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief IoT C-SDK version info
*
*/
#define QCLOUD_IOT_DEVICE_HUB_SDK_VERSION "4.0.0"
// service header file
#include "qcloud_iot_broadcast.h"
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_QCLOUD_IOT_HUB_H_

View File

@@ -0,0 +1,90 @@
/**
* @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 qcloud_iot_cos.h
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-26
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-26 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_COS_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_COS_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "qcloud_iot_common.h"
/**
* @brief Cos download params.
*
*/
typedef struct {
const char *url; /**< cos url, user space */
uint32_t offset; /**< download offset */
uint32_t file_size; /**< download file size */
int is_fragmentation; /**< http fragmentation support */
int is_https_enabled; /**< TODO:https support */
} IotCosDownloadParams;
/**
* @brief Init cos download handle.
*
* @param[in] params @see IotCosDownloadParams
* @return pointer to cos download handle
*/
void *IOT_COS_DownloadInit(IotCosDownloadParams *params);
/**
* @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);
/**
* @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);
/**
* @brief Deinit cos download.
*
* @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
*/
void IOT_COS_DownloadDeinit(void *handle);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_COS_H_

View File

@@ -0,0 +1,52 @@
/**
* @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 qcloud_iot_cos.h
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-26
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-26 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_DYNREG_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_DYNREG_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "qcloud_iot_common.h"
/**
* @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);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_DYNREG_H_

View File

@@ -0,0 +1,151 @@
/**
* @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 qcloud_iot_gateway.h
* @brief
* @author willssong (willssong@tencent.com)
* @version 1.0
* @date 2022-01-23
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022-01-23 <td>1.0 <td>willssong <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_GATEWAY_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_GATEWAY_H_
#include "qcloud_iot_common.h"
/**
* @brief Gateway reply result code.
* @ref https://cloud.tencent.com/document/product/1081/47442#.E9.94.99.E8.AF.AF.E7.A0.81
*
*/
typedef enum {
IOT_GATEWAY_RET_SUCCESS = 0,
IOT_GATEWAY_ERR_UNBIND = -1,
IOT_GATEWAY_ERR_FAIL = -2,
IOT_GATEWAY_ERR_PARAM = 801,
IOT_GATEWAY_ERR_SUBDEV_INVALID = 802,
IOT_GATEWAY_ERR_SIGN_FAIL = 803,
IOT_GATEWAY_ERR_SIGN_METHOD_UNSUPPORTED = 804,
IOT_GATEWAY_ERR_SIGN_EXPIRED = 805,
IOT_GATEWAY_ERR_SUBDEV_BIND_ALREADY = 806,
IOT_GATEWAY_ERR_SUBDEV_TYPE_NORMAL = 807,
IOT_GATEWAY_ERR_OPERATION_UNSUPPORTED = 808,
IOT_GATEWAY_ERR_BIND_REPEAT = 809,
IOT_GATEWAY_ERR_SUBDEV_UNSUPPORTED = 810
} IotGatewayResult;
/**
* @brief Callback of gateway.
*
*/
typedef struct {
void (*bind_unbind_reply_callback)(UtilsJsonValue sub_devices, bool is_bind, void *usr_data);
void (*unbind_all_callback)(void *usr_data);
void (*online_offline_reply_callback)(UtilsJsonValue sub_devices, bool is_online, void *usr_data);
void (*search_device_callback)(bool is_on, void *usr_data);
void (*describe_subdevices_reply_callback)(UtilsJsonValue sub_devices, void *usr_data);
void (*change_subdevices_status_callback)(UtilsJsonValue sub_devices, bool is_bind, void *usr_data);
} IotGatewayMessageCallback;
/**
* @brief Subscribe gateway topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] callback @see IotGatewayMessageCallback
* @param[in] usr_data usr data using in callback
* @return @see IotReturnCode
*/
int IOT_Gateway_Init(void *client, IotGatewayMessageCallback callback, void *usr_data);
/**
* @brief Unsubscribe gateway topic.
*
* @param[in,out] client pointer to mqtt client
*/
void IOT_Gateway_Deinit(void *client);
/**
* @brief Get gateway init usr data.
*
* @param [in,out] client pointer to mqtt client
* @return usr data or NULL
*/
void *IOT_Gateway_GetUsrData(void *client);
/**
* @brief Publish subdevice online/offline message. @ref https://cloud.tencent.com/document/product/1081/47442
*
* @param[in,out] client pointer to mqtt client
* @param[in] buf buffer to hold publish message
* @param[in] buf_len buffer length
* @param[in] sub_dev_list subdevice list
* @param[in] num num of subdevice, @note only one subdevice is supported now.
* @param[in] is_online 1: online; 0: offline
* @return @see packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_Gateway_SubOnOffLine(void *client, char *buf, int buf_len, const DeviceInfo *sub_dev_list[], int num,
bool is_online);
/**
* @brief Publish subdevice bind/unbind message. @ref https://cloud.tencent.com/document/product/1081/47441
*
* @param[in,out] client pointer to mqtt client
* @param[in] buf buffer to hold publish message
* @param[in] buf_len buffer length
* @param[in] sub_dev_list subdevice list
* @param[in] num num of subdevice, @note only one subdevice is supported now.
* @param[in] is_bind 1: bind; 0: unbind
* @return @see packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_Gateway_BindUnbind(void *client, char *buf, int buf_len, const DeviceInfo *sub_dev_list[], int num,
bool is_bind);
/**
* @brief Publish subdevice describe message.
* @ref https://cloud.tencent.com/document/product/1081/47441#.E6.9F.A5.E8.AF.A2.E6.8B.93.E6.89.91.E5.85.B3.E7.B3.BB
*
* @param[in,out] client pointer to mqtt client
* @return @see packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_Gateway_Describe(void *client);
/**
* @brief Publish search device reply message.
* @ref
* https://cloud.tencent.com/document/product/1081/47441#.E9.80.9A.E7.9F.A5.E7.BD.91.E5.85.B3.E5.BC.80.E5.90.AF.E6.90.9C.E7.B4.A2.E7.8A.B6.E6.80.81
*
* @param[in,out] client pointer to mqtt client
* @param[in] is_on 1: on; 0: off
* @param[in] result 0: success; 1: fail
* @return @see packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_Gateway_SearchDeviceReply(void *client, bool is_on, int result);
/**
* @brief Publish unbind all reply message.
*
* @param[in,out] client pointer to mqtt client
* @param[in] result 0: success; 1: fail
* @return @see packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_Gateway_UnbindAllReply(void *client, int result);
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_GATEWAY_H_

View File

@@ -0,0 +1,157 @@
/**
* @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 qcloud_iot_http_client.h
* @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>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_HTTP_CLIENT_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_HTTP_CLIENT_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "qcloud_iot_common.h"
/**
* @brief HTTP write socket timeout.
*
*/
#define HTTP_WRITE_TIMEOUT_MS 5000
/**
* @brief HTTPs read tls timeout.
*
*/
#define HTTPS_READ_TIMEOUT_MS 2000
/**
* @brief Connect params set by user.
*
*/
typedef struct {
const char *url; /**< http header */
const char *port; /**< content length(optional) */
const char *ca_crt; /**< content type(optional) */
} IotHTTPConnectParams;
/**
* @brief Http method.
*
*/
typedef enum {
IOT_HTTP_METHOD_GET = 0,
IOT_HTTP_METHOD_POST,
IOT_HTTP_METHOD_PUT,
IOT_HTTP_METHOD_DELETE,
IOT_HTTP_METHOD_HEAD,
} IotHTTPMethod;
/**
* @brief Request params set by user.
* @ref https://datatracker.ietf.org/doc/html/rfc7231
*
*/
typedef struct {
const char *url; /**< request url */
IotHTTPMethod method; /**< request method */
char *header; /**< http header */
int content_length; /**< content length(optional) */
char *content_type; /**< content type(optional) */
char *content; /**< content (optional) */
} IotHTTPRequestParams;
/**
* @brief Malloc http client.
*
* @return pointer to http client
*/
void *IOT_HTTP_Init(void);
/**
* @brief Free http client.
*
*/
void IOT_HTTP_Deinit(void *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);
/**
* @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);
/**
* @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);
/**
* @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);
/**
* @brief Check is recv finished.
*
* @param[in,out] client pointer to http client
* @return true for finished.
*/
int IOT_HTTP_IsRecvFinished(void *client);
/**
* @brief Disconnect http server.
*
* @param[in,out] client pointer to http client
*/
void IOT_HTTP_Disconnect(void *client);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_HTTP_CLIENT_H_

View File

@@ -0,0 +1,61 @@
/**
* @file qcloud_iot_http_signed.h
* @author {hubert} ({hubertxxu@tencent.com})
* @brief
* @version 1.0
* @date 2022-01-17
*
* @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-17 1.0 hubertxxu first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_HTTP_SIGNED_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_HTTP_SIGNED_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "qcloud_iot_common.h"
typedef struct {
const char *host;
const char *uri;
char *secretkey;
uint32_t recv_timeout_ms;
bool need_recv;
} HttpSignedParams;
/**
* @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 0 for success. others @see IotReturnCode
*/
int IOT_HTTP_SignedRequest(HttpSignedParams *params, const char *request_buf, size_t request_buf_len,
uint8_t *response_buf, int response_buf_len);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_HTTP_SIGNED_H_

View File

@@ -0,0 +1,128 @@
/**
* @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 qcloud_iot_log_upload.h
* @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>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_LOG_UPLOAD_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_LOG_UPLOAD_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "qcloud_iot_common.h"
#include "utils_json.h"
/**
* @brief User callback for saving/reading logs into/from NVS(files/FLASH) after upload fail/recover
*
*/
// callback for saving logs into NVS(files/FLASH) after upload fail
typedef size_t (*LogSaveFunc)(const char *filename, const void *buf, size_t write_len, size_t offset);
// callback for reading logs from NVS(files/FLASH) when upload ready
typedef size_t (*LogReadFunc)(const char *filename, void *buf, size_t read_len, size_t offset);
// callback for deleting logs in NVS(files/FLASH). return 0 when success
typedef int (*LogDelFunc)(const char *filename);
// callback for reading the size of logs in NVS(files/FLASH). return 0 when nothing exist
typedef size_t (*LogGetSizeFunc)(const char *filename);
/**
* @brief Data structure to init feature of log upload.
*
*/
typedef struct {
/* device info */
const char *product_id;
const char *device_name;
/* auth key, use device secret for PSK device and cert file path for cert device */
const char *sign_key;
/* http connect domain */
const char *host;
uint32_t log_buffer_size;
/* user callback saving/reading logs into/from NVS(files/FLASH) */
const char *save_log_filename;
LogSaveFunc save_func;
LogReadFunc read_func;
LogDelFunc del_func;
LogGetSizeFunc get_size_func;
} LogUploadInitParams;
/**
* @brief Default params.
*
*/
#define DEFAULT_LOG_UPLOAD_INIT_PARAMS \
{ \
NULL, NULL, NULL, NULL, LOG_UPLOAD_BUFFER_SIZE, NULL, NULL, NULL, NULL, NULL \
}
/**
* @brief Init log upload previously.
*
* @param[in] init_params @see LogUploadInitParams
* @return @see IotReturnCode
*/
int IOT_Log_Upload_InitPre(const LogUploadInitParams *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);
/**
* @brief Stop log upload add release resources.
*
* @return @see IotReturnCode
*/
int IOT_Log_Upload_Deinit(void);
/**
* @brief Append need report log to log upload buffer.
*
* @param[in] log_level @see LogLevel
* @param[in] log_content data of need to report
*/
void IOT_Log_Upload_AppendToUploadBuffer(LogLevel log_level, const char *log_content);
/**
* @brief Do log upload.
*
* @param[in] force_upload force upload when error
* @return @see IotReturnCode
*/
int IOT_Log_Upload(bool force_upload);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_LOG_UPLOAD_H_

View File

@@ -0,0 +1,339 @@
/**
* @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 qcloud_iot_mqtt_client.h
* @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
* <tr><td>2021-07-18 <td>1.1 <td>fancyxu <td>fix code standard of pClient
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_MQTT_CLIENT_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_MQTT_CLIENT_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "qcloud_iot_common.h"
/**
* @brief Max size of a topic name
*
*/
#define MAX_SIZE_OF_CLOUD_TOPIC ((MAX_SIZE_OF_DEVICE_NAME) + (MAX_SIZE_OF_PRODUCT_ID) + 64 + 6)
/**
* @brief MQTT event type
*
*/
typedef enum {
MQTT_EVENT_UNDEF = 0, /**< MQTT undefined event */
MQTT_EVENT_DISCONNECT = 1, /**< MQTT disconnect */
MQTT_EVENT_RECONNECT = 2, /**< MQTT reconnect */
MQTT_EVENT_SUBSCRIBE_SUCCESS = 3, /**< MQTT subscribe success */
MQTT_EVENT_SUBSCRIBE_TIMEOUT = 4, /**< MQTT subscribe timeout */
MQTT_EVENT_SUBSCRIBE_NACK = 5, /**< MQTT subscribe fail */
MQTT_EVENT_UNSUBSCRIBE_SUCCESS = 6, /**< MQTT unsubscribe success */
MQTT_EVENT_UNSUBSCRIBE_TIMEOUT = 7, /**< MQTT unsubscribe timeout */
MQTT_EVENT_UNSUBSCRIBE_NACK = 8, /**< MQTT unsubscribe fail */
MQTT_EVENT_PUBLISH_SUCCESS = 9, /**< MQTT publish success */
MQTT_EVENT_PUBLISH_TIMEOUT = 10, /**< MQTT publish timeout */
MQTT_EVENT_PUBLISH_NACK = 11, /**< MQTT publish fail */
MQTT_EVENT_PUBLISH_RECEIVED = 12, /**< MQTT received msg from server */
MQTT_EVENT_CLIENT_DESTROY = 13, /**< MQTT client destroy */
MQTT_EVENT_UNSUBSCRIBE = 14, /**< MQTT unsubscribe */
} MQTTEventType;
/**
* @brief MQTT event message
*
*/
typedef struct {
MQTTEventType event_type; /**< MQTT event type */
void *msg;
} MQTTEventMsg;
/**
* @brief Define MQTT callback when MQTT event happen
*
* @param[in,out] client pointer to mqtt client
* @param[in] context user callback context, @see MQTTEventHandler
* @param[in] msg the event message @see MQTTEventMsg
*/
typedef void (*MQTTEventHandleFun)(void *client, void *context, MQTTEventMsg *msg);
/* The structure of MQTT event handle */
/**
* @brief Define structure to handle mqtt event
*
*/
typedef struct {
MQTTEventHandleFun h_fp;
void *context;
} MQTTEventHandler;
/**
* @brief return next host ip
*/
typedef const char *(*MQTTGetNextHostIp)(void);
/**
* @brief The structure of MQTT init parameters
*
*/
typedef struct {
DeviceInfo *device_info; /**< device info */
const char *host; /**< host for user, null for default using QCLOUD_IOT_MQTT_DIRECT_DOMAIN */
const char *backup_host; /**< backup host for user if host not connect will try use this */
uint32_t command_timeout; /**< timeout value (unit: ms) for MQTT connect/pub/sub/yield */
uint32_t keep_alive_interval; /**< MQTT keep alive time interval in second */
uint8_t clean_session; /**< flag of clean session, 1 clean, 0 not clean */
uint8_t auto_connect_enable; /**< flag of auto reconnection, 1 is enable and recommended */
uint8_t connect_when_construct; /**< 1 is enable when no using pre-process before connect */
uint8_t default_subscribe; /**< 1 is enable when clean session is 0, no subscribe packet send, only add subhandle */
MQTTGetNextHostIp get_next_host_ip; /**< get host ip*/
MQTTEventHandler event_handle; /**< event callback */
} MQTTInitParams;
/**
* Default MQTT init parameters
*/
#define DEFAULT_MQTT_INIT_PARAMS \
{ \
NULL, NULL, NULL, QCLOUD_IOT_MQTT_COMMAND_TIMEOUT, QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL, 1, 1, 1, 0, NULL, \
{ \
0 \
} \
}
/**
* @brief MQTT Quality of Service level
*
*/
typedef enum {
QOS0 = 0, /**< At most once delivery */
QOS1 = 1, /**< At least once delivery, PUBACK is required */
QOS2 = 2 /**< Exactly once delivery. NOT supported currently */
} QoS;
/**
* @brief MQTT message parameter for pub/sub
*
*/
typedef struct {
QoS qos; // MQTT QoS level
uint8_t retain; // RETAIN flag
uint8_t dup; // DUP flag
uint16_t packet_id; // MQTT Id
char *topic_name; // MQTT topic
int topic_len; // topic length
union {
uint8_t *payload; // MQTT msg payload
char *payload_str; // MQTT msg payload string
};
int payload_len; // MQTT length of msg payload
} MQTTMessage;
/**
* @brief Params needed to publish expcept topic name(as a paramter of function).
*
*/
typedef struct {
QoS qos; // MQTT QoS level
uint8_t retain; // RETAIN flag
uint8_t dup; // DUP flag
void *payload; // MQTT msg payload
int payload_len; // MQTT length of msg payload
} PublishParams;
/**
* @brief Default MQTT publish params
*
*/
#define DEFAULT_PUB_PARAMS \
{ \
QOS0, 0, 0, NULL, 0 \
}
/**
* @brief Define MQTT SUBSCRIBE callback when message arrived
*
* @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
*/
typedef void (*OnMessageHandler)(void *client, const MQTTMessage *message, void *usr_data);
/**
* @brief Define MQTT SUBSCRIBE callback when event happened
*
* @param[in,out] client pointer to mqtt client
* @param[in] event_type @see MQTTEventType
* @param[in] usr_data user data of SubscribeParams, @see SubscribeParams
*
* @return none
*/
typedef void (*OnSubEventHandler)(void *client, MQTTEventType event_type, void *usr_data);
/**
* @brief Define structure to do MQTT subscription
*
*/
typedef struct {
QoS qos; /**< MQTT QoS level */
OnMessageHandler on_message_handler; /**< callback when message arrived */
OnSubEventHandler on_sub_event_handler; /**< callback when event happened */
void *user_data; /**< user context for callback */
void (*user_data_free)(void *); /**< user data free when sub handle remove */
} SubscribeParams;
/**
* Default MQTT subscribe parameters
*
*/
#define DEFAULT_SUB_PARAMS \
{ \
QOS0, NULL, NULL, NULL, NULL \
}
/**
* @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);
/**
* @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);
/**
* @brief Close connection and destroy MQTT client.
*
* @param client pointer to mqtt client pointer
* @return @see IotReturnCode
*/
int IOT_MQTT_Destroy(void **client);
/**
* @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);
/**
* @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);
/**
* @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);
/**
* @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);
/**
* @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);
/**
* @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);
/**
* @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);
/**
* @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);
/**
* @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);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_MQTT_CLIENT_H_

View File

@@ -0,0 +1,154 @@
/**
* @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 qcloud_iot_ota.h
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-11
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-11 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_OTA_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_OTA_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "qcloud_iot_common.h"
#include "utils_json.h"
#ifdef AT_MODULE_ENABLE
/**
* @brief Init ota && report mcu & at version.
*
* @param[in] version mcu version.
* @return 0 for success
*/
int IOT_OTA_Init(const char *version);
/**
* @brief Deinit ota.
*
* @return @see IotReturnCode
*/
void IOT_OTA_Deinit(void);
/**
* @brief Read fw info from at module.
*
* @param[out] version mcu fw version
* @param[out] fw_size mcu fw size
* @param[out] md5 mcu fw md5
* @param[in] timeout_ms timeout
* @return 0 for success
*/
int IOT_OTA_ReadFwInfo(char **version, uint32_t *fw_size, char **md5, uint32_t timeout_ms);
/**
* @brief Read fw data from at module.
*
* @param[out] fw_data fw data
* @param[out] fw_data_len fw data length
* @param[in] timeout_ms timeout
* @return 0 for success
*/
int IOT_OTA_ReadFWData(uint8_t *fw_data, uint32_t *fw_data_len, uint32_t timeout_ms);
#else
/**
* @brief OTA report type.
*
*/
typedef enum {
IOT_OTA_REPORT_TYPE_DOWNLOADING = 0,
IOT_OTA_REPORT_TYPE_UPGRADE_BEGIN,
IOT_OTA_REPORT_TYPE_UPGRADE_SUCCESS,
IOT_OTA_REPORT_TYPE_DOWNLOAD_TIMEOUT,
IOT_OTA_REPORT_TYPE_FILE_NOT_EXIST,
IOT_OTA_REPORT_TYPE_AUTH_FAIL,
IOT_OTA_REPORT_TYPE_MD5_NOT_MATCH,
IOT_OTA_REPORT_TYPE_UPGRADE_FAIL,
} IotOTAReportType;
/**
* @brief Callback of OTA.
*
*/
typedef struct {
void (*update_firmware_callback)(UtilsJsonValue version, UtilsJsonValue url, UtilsJsonValue md5sum,
uint32_t file_size, void *usr_data);
void (*report_version_reply_callback)(int result_code, void *usr_data);
} IotOTAUpdateCallback;
/**
* @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);
/**
* @brief OTA deinit, unsubscribe update topic.
*
* @param[in,out] client pointer to mqtt client
*/
void IOT_OTA_Deinit(void *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);
/**
* @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);
#endif
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_OTA_H_

View File

@@ -0,0 +1,73 @@
/**
* @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 qcloud_iot_system.h
* @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>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_SYSTEM_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_SYSTEM_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "qcloud_iot_common.h"
#include "utils_json.h"
/**
* @brief Get time from system topic
*
* @param[in,out] client pointer to mqtt client
* @param[out] time time from system topic
* @return @see IotReturnCode
*/
int IOT_Sys_GetTime(void *client, uint32_t *time);
/**
* @brief Get ntp time and set to system.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int IOT_Sys_SyncNTPTime(void *client);
/**
* @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);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_COMMON_QCLOUD_IOT_SYSTEM_H_

View File

@@ -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 qcloud_iot_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_INCLUDE_SERVICES_EXPLORER_QCLOUD_IOT_DATA_TEMPLATE_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_EXPLORER_QCLOUD_IOT_DATA_TEMPLATE_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "qcloud_iot_common.h"
/**
* @brief Callback of property.
*
*/
typedef struct {
void (*method_control_callback)(UtilsJsonValue client_token, UtilsJsonValue params, void *usr_data);
void (*method_report_reply_callback)(UtilsJsonValue client_token, int code, void *usr_data);
void (*method_get_status_reply_callback)(UtilsJsonValue client_token, int code, UtilsJsonValue reported,
UtilsJsonValue control, void *usr_data);
void (*method_report_info_reply_callback)(UtilsJsonValue client_token, int code, void *usr_data);
void (*method_clear_control_reply_callback)(UtilsJsonValue client_token, int code, void *usr_data);
} PropertyMessageCallback;
/**
* @brief Callback of event.
*
*/
typedef struct {
void (*method_event_reply_callback)(UtilsJsonValue client_token, int code, void *usr_data);
} EventMessageCallback;
/**
* @brief Callback of action.
*
*/
typedef struct {
void (*method_action_callback)(UtilsJsonValue client_token, UtilsJsonValue action_id, UtilsJsonValue params,
void *usr_data);
} ActionMessageCallback;
/**
* @brief Callback of data template(including property/event/action).
*
*/
typedef struct {
PropertyMessageCallback property_callback;
EventMessageCallback event_callback;
ActionMessageCallback action_callback;
} IotDataTemplateCallback;
#define DEFAULT_DATA_TEMPLATE_CALLBACK \
{ \
{NULL, NULL, NULL, NULL, NULL}, {NULL}, {NULL}, \
}
/**
* @brief Type of event.
*
*/
typedef enum {
IOT_DATA_TEMPLATE_EVENT_TYPE_INFO = 0,
IOT_DATA_TEMPLATE_EVENT_TYPE_ALERT,
IOT_DATA_TEMPLATE_EVENT_TYPE_FAULT,
} IotDataTemplateEventType;
/**
* @brief Post data.
*
*/
typedef struct {
const char * event_id; /**< event id defined in data template */
IotDataTemplateEventType type; /**< event type defined in data template */
const char * params; /**< property json defined in data template */
} IotDataTemplateEventData;
/**
* @brief Action reply.
*
*/
typedef struct {
UtilsJsonValue client_token;
int code;
const char * response; /**< property json defined in data template */
} IotDataTemplateActionReply;
/**
* @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);
/**
* @brief Unsubscribe data template topic.
*
* @param[in,out] client pointer to mqtt client
*/
void IOT_DataTemplate_Deinit(void *client);
/**
* @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);
/**
* @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);
/**
* @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);
/**
* @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);
/**
* @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);
/**
* @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);
/**
* @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);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_EXPLORER_QCLOUD_IOT_DATA_TEMPLATE_H_

View File

@@ -0,0 +1,179 @@
/**
* @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 qcloud_iot_file_manage.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_INCLUDE_SERVICES_EXPLORER_QCLOUD_IOT_FILE_MANAGE_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_EXPLORER_QCLOUD_IOT_FILE_MANAGE_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "qcloud_iot_common.h"
/**
* @brief Define of max size using in file manage.
*
*/
#define MAX_SIZE_OF_FILE_MANAGE_FILE_NAME 64
#define MAX_SIZE_OF_FILE_MANAGE_FILE_TYPE 10
#define MAX_SIZE_OF_FILE_MANAGE_FILE_VERSION 64
/**
* @brief FileManage report type.
*
*/
typedef enum {
// file update
IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOADING = 0,
IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_BEGIN,
IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_SUCCESS,
IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOAD_TIMEOUT,
IOT_FILE_MANAGE_REPORT_TYPE_FILE_NOT_EXIST,
IOT_FILE_MANAGE_REPORT_TYPE_AUTH_FAIL,
IOT_FILE_MANAGE_REPORT_TYPE_MD5_NOT_MATCH,
IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_FAIL,
IOT_FILE_MANAGE_REPORT_TYPE_SPACE_NOT_ENOUGH,
// file delete
IOT_FILE_MANAGE_REPORT_TYPE_DEL_SUCCESS,
IOT_FILE_MANAGE_REPORT_TYPE_DEL_FAIL,
// file post
IOT_FILE_MANAGE_REPORT_TYPE_POST_SUCCESS,
IOT_FILE_MANAGE_REPORT_TYPE_POST_FAIL,
} IotFileManageReportType;
/**
* @brief FileManage file type. @see sg_file_manage_file_type_str
*
*/
typedef enum {
IOT_FILE_MANAGE_FILE_TYPE_UNKOWN = -1,
IOT_FILE_MANAGE_FILE_TYPE_FILE = 0,
IOT_FILE_MANAGE_FILE_TYPE_AUDIO,
IOT_FILE_MANAGE_FILE_TYPE_VOICE,
IOT_FILE_MANAGE_FILE_TYPE_VIDEO,
} IotFileManageFileType;
/**
* @brief File info.
*
*/
typedef struct {
char file_name[MAX_SIZE_OF_FILE_MANAGE_FILE_NAME];
char file_version[MAX_SIZE_OF_FILE_MANAGE_FILE_VERSION];
IotFileManageFileType file_type;
} IotFileManageFileInfo;
/**
* @brief Callback of FileManage.
*
*/
typedef struct {
void (*update_file_callback)(UtilsJsonValue file_name, UtilsJsonValue file_type, UtilsJsonValue version,
UtilsJsonValue url, UtilsJsonValue md5sum, uint32_t file_size, void *usr_data);
void (*del_file_callback)(UtilsJsonValue file_name, UtilsJsonValue file_type, UtilsJsonValue version,
void *usr_data);
void (*report_file_version_reponse_callback)(UtilsJsonValue file_list, int result_code, void *usr_data);
void (*request_file_url_response_callback)(UtilsJsonValue url, UtilsJsonValue file_token, int result_code,
void *usr_data);
} IotFileManageCallback;
/**
* @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);
/**
* @brief File manage deinit, unregister handler from server list.
*
* @param[in,out] client pointer to mqtt client
*/
void IOT_FileManage_Deinit(void *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);
/**
* @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);
/**
* @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);
/**
* @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);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_EXPLORER_QCLOUD_IOT_FILE_MANAGE_H_

View File

@@ -0,0 +1,113 @@
/**
* @file qcloud_iot_gateway_scene.h
* @author {hubert} ({hubertxxu@tencent.com})
* @brief
* @version 1.0
* @date 2022-06-15
*
* @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-06-15 1.0 hubertxxu first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_EXPLORER_QCLOUD_IOT_GATEWAY_SCENE_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_EXPLORER_QCLOUD_IOT_GATEWAY_SCENE_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "qcloud_iot_common.h"
#define MAX_LENGTH_INNER_SCENE_ID (64)
#define MAX_LENGTH_INNER_SCENE_NAME (64)
#define MAX_LENGTH_INNER_SCENE_LIST (20)
/**
* @brief callback of gateway scene
*
*/
typedef struct {
int (*gateway_scene_handles_callback)(UtilsJsonValue scene_id, UtilsJsonValue scene_name,
UtilsJsonValue scene_update_time, UtilsJsonValue scene_handles,
void *usr_data);
int (*gateway_run_scene_callback)(UtilsJsonValue scene_id, void *usr_data);
int (*gateway_delete_scene_callback)(UtilsJsonValue scene_id, void *usr_data);
int (*gateway_reload_scene_reply_callback)(int result_code, UtilsJsonValue status, UtilsJsonValue scene_result,
void *usr_data);
} IoTGatewaySceneCallback;
/**
* @brief gateway scene inner list
*
*/
typedef struct {
char inner_scene_id[MAX_LENGTH_INNER_SCENE_ID];
char inner_scene_name[MAX_LENGTH_INNER_SCENE_NAME];
} IoTGatewaySceneInnerList;
/**
* @brief gateway scene init, register handler to server list.
*
* @param[in,out] client pointer to mqtt client
* @param[in] callback @see IoTGatewaySceneCallback
* @param[in] usr_data usr data used in callback
* @return 0 for success, or err code (<0) @see IotReturnCode
*/
int IOT_GatewayScene_Init(void *client, IoTGatewaySceneCallback callback, void *usr_data);
/**
* @brief Gateway scene deinit, unregister handler from server list.
*
* @param[in,out] client pointer to mqtt client
*/
void IOT_GatewayScene_Deinit(void *client);
/**
* @brief reload gateway scene from cloud.
*
* @param[in,out] client pointer to mqtt client
* @param[out] buf publish message buffer
* @param[in] buf_len buffer len
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_GatewayScene_Reload(void *client, char *buf, int buf_len);
/**
* @brief report gateway local scene
*
* @param[in,out] client pointer to mqtt client
* @param[out] buf publish message buffer
* @param[in] buf_len buffer len
* @param list local scene list
* @param list_count local scene list count
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_GatewayScene_ReportInnerList(void *client, char *buf, int buf_len, IoTGatewaySceneInnerList *list,
int list_count);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_EXPLORER_QCLOUD_IOT_GATEWAY_SCENE_H_

View File

@@ -0,0 +1,70 @@
/**
* @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 qcloud_iot_broadcast.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_INCLUDE_SERVICES_HUB_QCLOUD_IOT_BROADCAST_H_
#define IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_HUB_QCLOUD_IOT_BROADCAST_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "qcloud_iot_common.h"
/**
* @brief Callback when broadcast message arrived.
*
*/
typedef void (*OnBroadcastArrivedCallback)(void *client, const char *msg, int msg_len, void *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);
/**
* @brief Unsubscribe broadcast topic.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int IOT_Broadcast_Deinit(void *client);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_INCLUDE_SERVICES_HUB_QCLOUD_IOT_BROADCAST_H_

View File

@@ -0,0 +1,9 @@
file(GLOB src
${CMAKE_CURRENT_SOURCE_DIR}/network/src/*.c
${CMAKE_CURRENT_SOURCE_DIR}/timer/src/*.c
${CMAKE_CURRENT_SOURCE_DIR}/os/${PLATFORM}/*.c
)
set(inc ${CMAKE_CURRENT_SOURCE_DIR}/network/inc/)
set(src_platform ${src_platform} ${src} PARENT_SCOPE)
set(inc_platform ${inc_platform} ${inc} PARENT_SCOPE)

View File

@@ -0,0 +1,5 @@
file(GLOB src ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(inc ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
set(src_platform ${src_platform} ${src} PARENT_SCOPE)
set(inc_platform ${inc_platform} ${inc} PARENT_SCOPE)

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