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,5 @@
file(GLOB src_at_module ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(inc_at_module ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
set(src_services ${src_services} ${src_at_module} PARENT_SCOPE)
set(inc_services ${inc_services} ${inc_at_module} PARENT_SCOPE)

View File

@@ -0,0 +1,327 @@
/**
* @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 at_mqtt_client.h
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2022-04-19
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022-04-19 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_AT_MODULE_INC_AT_MODULE_H_
#define IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_AT_MODULE_INC_AT_MODULE_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "qcloud_iot_mqtt_client.h"
/**
* @brief Max number of topic subscribed
*
*/
#define MAX_MESSAGE_HANDLERS (10)
/**
* @brief Max number of ota fw version
*
*/
#define MAX_FW_VERSION_STRING (32)
/**
* @brief Max publish length
*
*/
#define MAX_PUB_SHORT_LEN 128
/**
* @brief Subscribe status of topic.
*
*/
typedef enum {
SUB_ACK_NOT_RECEIVED = 0,
SUB_ACK_RECEIVED = 1,
} SubStatus;
/**
* @brief data structure for topic subscription handle
*
*/
typedef struct {
char *topic_filter; /**< topic name, wildcard filter is supported */
SubStatus status; /**< status of sub handle */
SubscribeParams params; /**< params needed to subscribe */
} SubTopicHandle;
/**
* @brief
*
*/
typedef enum {
TLS_MODE_NO_TLS = 0,
TLS_MODE_TLS_PSK = 1,
TLS_MODE_TLS_CERT = 2,
} QcloudIotTlsMode;
/**
* @brief
*
*/
typedef enum {
MQTT_CONNECT_STATE_CONNECTED = 0,
MQTT_CONNECT_STATE_RECONNECTING,
MQTT_CONNECT_STATE_RECONNECTED,
MQTT_CONNECT_STATE_DISCONNECT,
} MQTTConnectState;
/**
* @brief MQTT QCloud IoT Client structure
*
*/
typedef struct {
DeviceInfo *device_info; /**< device info needed to connect server */
uint32_t command_timeout_ms; /**< MQTT command timeout, unit:ms */
MQTTEventHandler event_handle; /**< callback for MQTT event */
uint8_t default_subscribe; /**< no subscribe packet send, only add subhandle */
void *lock_generic; /**< mutex/lock for this client struture */
SubTopicHandle sub_handles[MAX_MESSAGE_HANDLERS]; /**< subscription handle array */
uint8_t read_buf[QCLOUD_IOT_MQTT_RX_BUF_LEN]; /**< MQTT read buffer */
size_t read_buf_size; /**< size of MQTT read buffer */
MQTTConnectState connect_state; /**< connect state */
// connect options using in module
QcloudIotTlsMode tls_mode; /**< 0:no tls; 1:tls+psk; 2:tls+cert */
uint8_t auto_connect_enable; /**< enable auto connection or not */
uint8_t clean_session; /**< clean session option */
uint16_t keep_alive_interval; /**< keepalive option */
} QcloudIotClient;
/**
* @brief Cls state.
*
*/
typedef enum {
IOT_OTA_CLS_STATE_NO_REPORT = 0,
IOT_OTA_CLS_STATE_REPORT_MCU,
IOT_OTA_CLS_STATE_REPORT_MODULE, /**< only support in at set 1.8.0 */
IOT_OTA_CLS_STATE_REPORT_BOTH /**< only support in at set 1.8.0 */
} QcloudIotOtaClsState;
/**
* @brief OTA fw info.
*
*/
typedef struct {
char version[MAX_FW_VERSION_STRING];
uint32_t fw_size;
uint32_t fw_max_size;
char md5[33];
void *get_fw_info_sem;
} QcloudIotOtaFwInfo;
// ----------------------------------------------------------------------------
// cmd
// ----------------------------------------------------------------------------
/**
* @brief Set device info.
*
* @param[in,out] client pointer to mqtt client
* @return 0 for success
*/
int module_device_info_set(const QcloudIotClient *client);
/**
* @brief Connect mqtt server.
*
* @param[in,out] client pointer to mqtt client
* @return 0 for success
*/
int module_mqtt_connect(const QcloudIotClient *client);
/**
* @brief Disconnect mqtt server.
*
* @param[in,out] client pointer to mqtt client
* @return 0 for success
*/
int module_mqtt_disconnect(const QcloudIotClient *client);
/**
* @brief Publish mqtt message.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_name topic name to publish
* @param[in] params params for publish
* @return 0 for success
*/
int module_mqtt_publish(const QcloudIotClient *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] qos max qos for subscribe
* @return 0 for success
*/
int module_mqtt_subscribe(const QcloudIotClient *client, const char *topic_filter, int qos);
/**
* @brief Unsubscribe mqtt topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter to unsubscribe
* @return 0 for success
*/
int module_mqtt_unsubscribe(const QcloudIotClient *client, const char *topic_filter);
/**
* @brief Get mqtt connect state
*
* @param[in,out] client pointer to mqtt client
* @return 0 for success
*/
int module_mqtt_get_connect_state(const QcloudIotClient *client);
/**
* @brief Set fw version and report.
*
* @param[in] cls_state 0: off;1: only report mcu version;2: only report module version;3: report all
* @param[in] version mcu fw version
* @return 0 for success
*/
int module_ota_set_fw_version(QcloudIotOtaClsState cls_state, const char *version);
/**
* @brief Get fw info.
*
* @return 0 for success
*/
int module_ota_get_fw_info(void);
/**
* @brief Read fw data.
*
* @param[out] fw_data_buf data buffer
* @param[out] fw_data_len data recv length
* @param[in] timeout read timeout
* @return 0 for success
*/
int module_ota_read_fw_data(void *fw_data_buf, uint32_t *fw_data_len, uint32_t timeout);
// ----------------------------------------------------------------------------
// urc
// ----------------------------------------------------------------------------
/**
* @brief Set mqtt urc to at module.
*
* @return 0 means successful.
*/
int module_set_mqtt_urc(void);
/**
* @brief Set ota urc to at module.
*
* @return 0 means successful.
*/
int module_set_ota_urc(void);
// ----------------------------------------------------------------------------
// mqtt
// ----------------------------------------------------------------------------
/**
* @brief Get pointer to mqtt client.
*
* @return pointer to mqtt client.
*/
QcloudIotClient *qcloud_iot_get_mqtt_client(void);
/**
* @brief Serialize and send subscribe packet.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic to subscribe
* @param[in] params subscribe params
* @return >=0 for packet id, < 0 for failed @see IotReturnCode
*/
int qcloud_iot_mqtt_subscribe(QcloudIotClient *client, const char *topic_filter, const SubscribeParams *params);
/**
* @brief Serialize and send unsubscribe packet.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic to unsubscribe
* @return >=0 packet id, < 0 for failed @see IotReturnCode
*/
int qcloud_iot_mqtt_unsubscribe(QcloudIotClient *client, const char *topic_filter);
/**
* @brief Return if topic is sub ready.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter
* @return true for ready
* @return false for not ready
*/
bool qcloud_iot_mqtt_is_sub_ready(QcloudIotClient *client, const char *topic_filter);
/**
* @brief Get usr data, usr should handle lock/unlock usrdata itself in callback and caller.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter
* @return NULL or user data
*/
void *qcloud_iot_mqtt_get_subscribe_usr_data(QcloudIotClient *client, const char *topic_filter);
/**
* @brief Clear sub handle array.
*
* @param[in,out] client pointer to mqtt client
*/
void qcloud_iot_mqtt_sub_handle_array_clear(QcloudIotClient *client);
/**
* @brief Deliver message to subscribe message handler.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_name topic name, no wildcard
* @param[in] payload publish packet payload
* @param[in] payload_len payload length
*/
void qcloud_iot_deliver_message(QcloudIotClient *client, char *topic_name, char *payload, int payload_len);
/**
* @brief Get ota info handle.
*
* @return @see QcloudIotOtaFwInfo
*/
QcloudIotOtaFwInfo *qcloud_iot_get_ota_info_handle(void);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_AT_MODULE_INC_AT_MODULE_H_

View File

@@ -0,0 +1,474 @@
/**
* @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 at_mqtt_cmd.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2022-04-19
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022-04-19 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "at_module.h"
/**
* @brief AT Command for Iot Hub
* 1. network_register:
* AT+TCREGNET=?
* AT+TCREGNET?
* AT+TCREGNET=<module_type>,<action>[,<ssid>,<pw>][,<apn>]
* 2. device_info:
* AT+TCDEVINFOSET=?
* AT+TCDEVINFOSET?
* AT+TCDEVINFOSET=<tlsmode>,<productId>,<devicename>[,<devicesecret>][,<certname>]
* 3. cert:
* AT+TCCERTADD=?
* AT+TCCERTADD?
* AT+TCCERTADD=<cert_name>,<cert_size>
* AT+TCCERTCHECK=?
* AT+TCCERTCHECK?
* AT+TCCERTCHECK=<cert_name>
* AT+TCCERTDEL=?
* AT+TCCERTDEL?
* AT+TCCERTDEL=<cert_name>
* 4. dynamic register:
* AT+TCPRDINFOSET=?
* AT+TCPRDINFOSET?
* AT+TCPRDINFOSET=<tls_mode>,<product_ID>,<product_secret>,<device_name>
* AT+TCDEVREG=?
* AT+TCDEVREG
* 5. module info
* AT+TCMODULE
* AT+TCRESTORE=?
* 6. mqtt
* AT+TCMQTTCONN=?
* AT+TCMQTTCONN?
* AT+TCMQTTCONN=<tlsmode>,<cmdtimeout>,<keepalive>,<clean_session>,<reconnect>
* AT+TCMQTTDISCONN=?
* AT+TCMQTTDISCONN
* AT+TCMQTTPUB=?
* AT+TCMQTTPUB=<topic>,<qos>,<message>
* AT+TCMQTTPUBL=?
* AT+TCMQTTPUBL= <topic>,<qos>,<msg_length>
* AT+TCMQTTSUB=?
* AT+TCMQTTSUB?
* AT+TCMQTTSUB=<topic>,<qos>
* AT+TCMQTTUNSUB=?
* AT+TCMQTTUNSUB?
* AT+TCMQTTUNSUB=<topic>
* AT+TCMQTTSTATE=?
* AT+TCMQTTSTATE?
* 7. ota
* AT+TCOTASET=?
* AT+TCOTASET?
* AT+TCOTASET=<ctlstate>,<fw_ver>
* AT+TCFWINFO=?
* AT+TCFWINFO?
* AT+TCREADFWDATA=?
* AT+TCREADFWDATA=<len>
*
*/
/**
* @brief AT module cmd.
*
*/
typedef enum {
MODULE_CMD_SET_DEVICE_INFO, // AT+TCDEVINFOSET=<tlsmode>,<productId>,<devicename>[,<devicesecret>][,<certname>]
MODULE_CMD_CONNECT, // AT+TCMQTTCONN=<tlsmode>,<cmdtimeout>,<keepalive>,<clean_session>,<reconnect>
MODULE_CMD_DISCONNECT, // AT+TCMQTTDISCONN
MODULE_CMD_PUBLISH, // AT+TCMQTTPUB=<topic>,<qos>,<message>
MODULE_CMD_PUBLISHL, // AT+TCMQTTPUBL=<topic>,<qos>,<msg_length>
MODULE_CMD_SUBSCRIBE, // AT+TCMQTTSUB=<topic>,<qos>
MODULE_CMD_UNSUBSCRIBE, // AT+TCMQTTUNSUB=<topic>
MODULE_CMD_GET_CONNECT_STATUS, // AT+TCMQTTSTATE?
MODULE_CMD_SET_OTA_VERSION, // AT+TCOTASET=<ctlstate>,<fw_ver>
MODULE_CMD_GET_OTA_FW_INFO, // AT+TCFWINFO?
MODULE_CMD_READ_OTA_FW_DATA // AT+TCREADFWDATA=<len>
} ModuleCmd;
/**
* @brief Publish cmd params.
*
*/
typedef struct {
const char *topic_name;
const PublishParams *params;
} ModuleCmdPublishParams;
/**
* @brief Subscribe cmd params.
*
*/
typedef struct {
const char *topic_filter;
int qos;
} ModuleCmdSubscribeParams;
/**
* @brief Unsubscribe cmd params.
*
*/
typedef struct {
const char *topic_filter;
} ModuleCmdUnsubscribeParams;
/**
* @brief Set ota version to report.
*
*/
typedef struct {
/**
* @brief Control module report
* 0: off
* 1: only report mcu version
* 2: only report module version
* 3: report all
*/
QcloudIotOtaClsState cls_state;
const char *version; /* ota version */
} ModuleCmdSetOTAVersionParams;
/**
* @brief Read fw data.
*
*/
typedef struct {
void *fw_data_buf;
uint32_t *fw_data_len;
uint32_t timeout;
} ModuleCmdReadOTFwDataParams;
/**
* @brief Module cmd params.
*
*/
typedef struct {
const QcloudIotClient *client;
union {
ModuleCmdPublishParams publish_params;
ModuleCmdSubscribeParams subscribe_params;
ModuleCmdUnsubscribeParams unsubscribe_params;
ModuleCmdSetOTAVersionParams set_ota_version_params;
ModuleCmdReadOTFwDataParams read_ota_fw_data_params;
};
} ModuleCmdParams;
/**
* @brief Transfer '\"' and ','.
*
* @param[in,out] payload payload to transfer, reuse memory
* @return length of payload
*/
static int _transfer_payload(char *payload, char **payload_transferred)
{
char *tmp, *src = payload;
int count = 0;
char c = '\0';
// 1. calculate '\"' and '.'
while ((c = *src++) != '\0') {
if (c == '\"' || c == ',') {
count++;
}
}
// 2. malloc for payload
int tmp_len = src - payload + count;
tmp = HAL_Malloc(tmp_len);
if (!tmp) {
return 0;
}
memset(tmp, 0, tmp_len);
// 3. replace
for (src = payload, count = 0; (c = *src) != '\0'; src++) {
if (c == '\"' || c == ',') {
tmp[count++] = '\\';
}
tmp[count++] = c;
}
tmp[count] = '\0';
*payload_transferred = tmp;
return count;
}
/**
* @brief Send at cmd to at module.
*
* @param[in] cmd @see ModuleCmd
* @param[in] params @see ModuleCmdParams
* @return 0 for success
*/
static int _module_send_cmd(ModuleCmd cmd, ModuleCmdParams *params)
{
#define DEFAULT_TIMEOUT_MS 5000
int rc, len = 0;
char at_cmd[1250] = {0};
char at_resp[32] = {0};
char *payload_transferred = NULL;
const QcloudIotClient *client = params->client;
switch (cmd) {
case MODULE_CMD_SET_DEVICE_INFO:
strncpy(at_resp, "+TCDEVINFOSET:OK", sizeof(at_resp));
len = HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCDEVINFOSET=%d,\"%s\",\"%s\",\"%s\"\r\n", client->tls_mode,
client->device_info->product_id, client->device_info->device_name,
client->device_info->device_secret);
break;
case MODULE_CMD_CONNECT:
strncpy(at_resp, "+TCMQTTCONN:OK", sizeof(at_resp));
len = HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCMQTTCONN=%d,%d,%d,%d,%d\r\n", client->tls_mode,
client->command_timeout_ms, client->keep_alive_interval, client->clean_session,
client->auto_connect_enable);
break;
case MODULE_CMD_DISCONNECT:
len = HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCMQTTDISCONN\r\n");
break;
case MODULE_CMD_PUBLISHL:
len = _transfer_payload(params->publish_params.params->payload, &payload_transferred);
if (!len) {
return QCLOUD_ERR_MALLOC;
}
strncpy(at_resp, ">", sizeof(at_resp));
HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCMQTTPUBL=\"%s\",%d,%d\r\n", params->publish_params.topic_name,
params->publish_params.params->qos, len);
rc = HAL_Module_SendAtCmdWaitResp(at_cmd, at_resp, client->command_timeout_ms);
if (rc) {
HAL_Free(payload_transferred);
return rc;
}
strncpy(at_resp, "+TCMQTTPUBL:OK", sizeof(at_resp));
rc = HAL_Module_SendAtData(payload_transferred, len);
HAL_Free(payload_transferred);
if (rc) {
return rc;
}
return HAL_Module_SendAtCmdWaitResp("\r\n", at_resp, client->command_timeout_ms);
case MODULE_CMD_PUBLISH:
strncpy(at_resp, "+TCMQTTPUB:OK", sizeof(at_resp));
len = _transfer_payload(params->publish_params.params->payload, &payload_transferred);
if (!len) {
return QCLOUD_ERR_MALLOC;
}
len = HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCMQTTPUB=\"%s\",%d,\"%s\"\r\n",
params->publish_params.topic_name, params->publish_params.params->qos,
payload_transferred);
HAL_Free(payload_transferred);
break;
case MODULE_CMD_SUBSCRIBE:
strncpy(at_resp, "+TCMQTTSUB:OK", sizeof(at_resp));
len = HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCMQTTSUB=\"%s\",%d\r\n",
params->subscribe_params.topic_filter, params->subscribe_params.qos);
break;
case MODULE_CMD_UNSUBSCRIBE:
strncpy(at_resp, "+TCMQTTUNSUB:OK", sizeof(at_resp));
len = HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCMQTTUNSUB=\"%s\"\r\n",
params->subscribe_params.topic_filter);
break;
case MODULE_CMD_GET_CONNECT_STATUS:
len = HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCMQTTSTATE?\r\n");
break;
case MODULE_CMD_SET_OTA_VERSION:
strncpy(at_resp, "+TCOTASET:OK", sizeof(at_resp));
len = HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCOTASET=%d,\"%s\"\r\n",
params->set_ota_version_params.cls_state, params->set_ota_version_params.version);
break;
case MODULE_CMD_GET_OTA_FW_INFO:
len = HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCFWINFO?\r\n");
break;
case MODULE_CMD_READ_OTA_FW_DATA:
strncpy(at_resp, "+TCREADFWDATA:%u,", sizeof(at_resp));
HAL_Snprintf(at_cmd, sizeof(at_cmd), "AT+TCREADFWDATA=%d\r\n",
*(params->read_ota_fw_data_params.fw_data_len));
return HAL_Module_SendAtCmdWaitRespWithData(at_cmd, at_resp, params->read_ota_fw_data_params.fw_data_buf,
params->read_ota_fw_data_params.fw_data_len,
params->read_ota_fw_data_params.timeout);
default:
return QCLOUD_ERR_INVAL;
}
return len ? HAL_Module_SendAtCmdWaitResp(at_cmd, at_resp[0] ? at_resp : NULL,
client ? client->command_timeout_ms : DEFAULT_TIMEOUT_MS)
: QCLOUD_ERR_BUF_TOO_SHORT;
}
// ----------------------------------------------------------------------------
// device info
// ----------------------------------------------------------------------------
/**
* @brief Set device info.
*
* @param[in,out] client pointer to mqtt client
* @return 0 for success
*/
int module_device_info_set(const QcloudIotClient *client)
{
ModuleCmdParams cmd_params = {0};
cmd_params.client = client;
return _module_send_cmd(MODULE_CMD_SET_DEVICE_INFO, &cmd_params);
}
// ----------------------------------------------------------------------------
// mqtt
// ----------------------------------------------------------------------------
/**
* @brief Connect mqtt server.
*
* @param[in,out] client pointer to mqtt client
* @return 0 for success
*/
int module_mqtt_connect(const QcloudIotClient *client)
{
ModuleCmdParams cmd_params = {0};
cmd_params.client = client;
return _module_send_cmd(MODULE_CMD_CONNECT, &cmd_params);
}
/**
* @brief Disconnect mqtt server.
*
* @param[in,out] client pointer to mqtt client
* @return 0 for success
*/
int module_mqtt_disconnect(const QcloudIotClient *client)
{
ModuleCmdParams cmd_params = {0};
cmd_params.client = client;
return _module_send_cmd(MODULE_CMD_DISCONNECT, &cmd_params);
}
/**
* @brief Publish mqtt message.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_name topic name to publish
* @param[in] params params for publish
* @return 0 for success
*/
int module_mqtt_publish(const QcloudIotClient *client, const char *topic_name, const PublishParams *params)
{
ModuleCmdParams cmd_params = {0};
cmd_params.client = client;
cmd_params.publish_params.params = params;
cmd_params.publish_params.topic_name = topic_name;
return params->payload_len > MAX_PUB_SHORT_LEN ? _module_send_cmd(MODULE_CMD_PUBLISHL, &cmd_params)
: _module_send_cmd(MODULE_CMD_PUBLISH, &cmd_params);
}
/**
* @brief Subscribe mqtt topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter to subscribe
* @param[in] qos max qos for subscribe
* @return 0 for success
*/
int module_mqtt_subscribe(const QcloudIotClient *client, const char *topic_filter, int qos)
{
ModuleCmdParams cmd_params = {0};
cmd_params.client = client;
cmd_params.subscribe_params.topic_filter = topic_filter;
cmd_params.subscribe_params.qos = qos;
return _module_send_cmd(MODULE_CMD_SUBSCRIBE, &cmd_params);
}
/**
* @brief Unsubscribe mqtt topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter to unsubscribe
* @return 0 for success
*/
int module_mqtt_unsubscribe(const QcloudIotClient *client, const char *topic_filter)
{
ModuleCmdParams cmd_params = {0};
cmd_params.client = client;
cmd_params.unsubscribe_params.topic_filter = topic_filter;
return _module_send_cmd(MODULE_CMD_UNSUBSCRIBE, &cmd_params);
}
/**
* @brief Get mqtt connect state
*
* @param[in,out] client pointer to mqtt client
* @return 0 for success
*/
int module_mqtt_get_connect_state(const QcloudIotClient *client)
{
ModuleCmdParams cmd_params = {0};
cmd_params.client = client;
return _module_send_cmd(MODULE_CMD_GET_CONNECT_STATUS, &cmd_params);
}
// ----------------------------------------------------------------------------
// ota
// ----------------------------------------------------------------------------
/**
* @brief Set fw version and report.
*
* @param[in] cls_state 0: off;1: only report mcu version;2: only report module version;3: report all
* @param[in] version mcu fw version
* @return 0 for success
*/
int module_ota_set_fw_version(QcloudIotOtaClsState cls_state, const char *version)
{
ModuleCmdParams cmd_params = {0};
cmd_params.client = NULL;
cmd_params.set_ota_version_params.cls_state = cls_state;
cmd_params.set_ota_version_params.version = version;
return _module_send_cmd(MODULE_CMD_SET_OTA_VERSION, &cmd_params);
}
/**
* @brief Get fw info.
*
* @return 0 for success
*/
int module_ota_get_fw_info(void)
{
ModuleCmdParams cmd_params = {0};
cmd_params.client = NULL;
return _module_send_cmd(MODULE_CMD_GET_OTA_FW_INFO, &cmd_params);
}
/**
* @brief Read fw data.
*
* @param[out] fw_data_buf data buffer
* @param[out] fw_data_len data recv length
* @param[in] timeout read timeout
* @return 0 for success
*/
int module_ota_read_fw_data(void *fw_data_buf, uint32_t *fw_data_len, uint32_t timeout)
{
ModuleCmdParams cmd_params = {0};
cmd_params.client = NULL;
cmd_params.read_ota_fw_data_params.fw_data_buf = fw_data_buf;
cmd_params.read_ota_fw_data_params.fw_data_len = fw_data_len;
cmd_params.read_ota_fw_data_params.timeout = timeout;
return _module_send_cmd(MODULE_CMD_READ_OTA_FW_DATA, &cmd_params);
}

View File

@@ -0,0 +1,407 @@
/**
* @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 at_module_mqtt_client.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2022-04-25
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022-04-25 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "at_module.h"
/**
* @brief Init mqtt client.
*
* @param[in,out] client pointer to mqtt client
* @param[in] params mqtt init params, @see MQTTInitParams
* @return @see IotReturnCode
*
* @note
* 1. init device info.
* 2. init params.
* 3. set mqtt urc to at module.
* 4. set device info to at module.
*/
static int _qcloud_iot_mqtt_client_init(QcloudIotClient *client, const MQTTInitParams *params)
{
memset(client, 0x0, sizeof(QcloudIotClient));
client->device_info = params->device_info;
client->command_timeout_ms = params->command_timeout;
client->event_handle = params->event_handle;
client->default_subscribe = params->default_subscribe;
client->connect_state = MQTT_CONNECT_STATE_DISCONNECT;
client->tls_mode = TLS_MODE_TLS_PSK;
client->auto_connect_enable = params->auto_connect_enable;
client->clean_session = params->clean_session;
client->keep_alive_interval = params->keep_alive_interval > 690 ? 690 : params->keep_alive_interval;
client->lock_generic = HAL_MutexCreate();
if (!client->lock_generic) {
return QCLOUD_ERR_FAILURE;
}
// 1. detect module && close echo
int rc = HAL_Module_Init();
if (rc) {
Log_e("module init fail %d", rc);
return rc;
}
// 2. set urc function
rc = module_set_mqtt_urc();
if (rc) {
Log_e("set mqtt urc fail %d", rc);
return rc;
}
// 3. set device info
rc = module_device_info_set(client);
if (rc) {
Log_e("module init fail %d", rc);
return rc;
}
// 4. connect network
return HAL_Module_ConnectNetwork();
}
/**
* @brief Deinit mqtt client.
*
* @param[in,out] client pointer to mqtt client
*/
static void _qcloud_iot_mqtt_client_deinit(QcloudIotClient *client)
{
qcloud_iot_mqtt_sub_handle_array_clear(client);
HAL_MutexDestroy(client->lock_generic);
HAL_Module_Deinit();
Log_i("release mqtt client resources");
}
// ----------------------------------------------------------------------------
// API
// ----------------------------------------------------------------------------
/**
* @brief Only support one mqtt client when using at module.
*
*/
static QcloudIotClient *sg_mqtt_client;
/**
* @brief Get pointer to mqtt client.
*
* @return pointer to mqtt client.
*/
QcloudIotClient *qcloud_iot_get_mqtt_client(void)
{
return sg_mqtt_client;
}
/**
* @brief Create MQTT client and connect to MQTT server. Only one client supportted.
*
* @param[in] params MQTT init parameters
* @return a valid MQTT client handle when success, or NULL otherwise
*/
void *IOT_MQTT_Construct(const MQTTInitParams *params)
{
POINTER_SANITY_CHECK(params, NULL);
POINTER_SANITY_CHECK(params->device_info, NULL);
STRING_PTR_SANITY_CHECK(params->device_info->product_id, NULL);
STRING_PTR_SANITY_CHECK(params->device_info->device_name, NULL);
#ifdef AUTH_MODE_CERT
STRING_PTR_SANITY_CHECK(params->device_info->dev_cert_file_name, NULL);
STRING_PTR_SANITY_CHECK(params->device_info->dev_key_file_name, NULL);
#else
STRING_PTR_SANITY_CHECK(params->device_info->device_secret, NULL);
#endif
if (sg_mqtt_client) {
Log_e("only support one mqtt client in at module!");
return NULL;
}
int rc = 0;
QcloudIotClient *client = NULL;
// create and init MQTTClient
client = (QcloudIotClient *)HAL_Malloc(sizeof(QcloudIotClient));
if (!client) {
Log_e("malloc MQTTClient failed");
return NULL;
}
rc = _qcloud_iot_mqtt_client_init(client, params);
if (rc) {
Log_e("mqtt init failed: %d", rc);
goto exit;
}
if (!params->connect_when_construct) {
return client;
}
rc = IOT_MQTT_Connect(client);
if (rc) {
goto exit;
}
client->connect_state = MQTT_CONNECT_STATE_CONNECTED;
Log_i("mqtt connect with success");
sg_mqtt_client = client;
return client;
exit:
_qcloud_iot_mqtt_client_deinit(client);
HAL_Free(client);
return NULL;
}
/**
* @brief Connect Mqtt server if not connect.
*
* @param[in,out] client pointer to mqtt client pointer, should using the pointer of IOT_MQTT_Construct return.
* @return @see IotReturnCode
*/
int IOT_MQTT_Connect(void *client)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
int rc = 0;
QcloudIotTimer connect_timer;
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
if (mqtt_client->connect_state == MQTT_CONNECT_STATE_DISCONNECT) {
IOT_Timer_CountdownMs(&connect_timer, MAX_RECONNECT_WAIT_INTERVAL);
do { // wait for wifi config
rc = module_mqtt_connect(client);
if (rc) {
Log_e("mqtt connect with failed: %d", rc);
HAL_SleepMs(QCLOUD_IOT_MQTT_WAIT_ACK_TIMEOUT);
}
if (IOT_Timer_Expired(&connect_timer)) {
return QCLOUD_ERR_FAILURE;
}
} while (rc);
}
return QCLOUD_RET_SUCCESS;
}
/**
* @brief Close connection and destroy MQTT client.
*
* @param[in,out] client pointer to mqtt client pointer, should using the pointer of IOT_MQTT_Construct return.
* @return @see IotReturnCode
*/
int IOT_MQTT_Destroy(void **client)
{
POINTER_SANITY_CHECK(*client, QCLOUD_ERR_INVAL);
QcloudIotClient *mqtt_client = (QcloudIotClient *)(*client);
int rc = module_mqtt_disconnect(mqtt_client);
_qcloud_iot_mqtt_client_deinit(mqtt_client);
HAL_Free(*client);
*client = sg_mqtt_client = NULL;
Log_i("mqtt release!");
return rc;
}
/**
* @brief Check connection state.
*
* @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, others @see IotReturnCode
*/
int IOT_MQTT_Yield(void *client, uint32_t timeout_ms)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
HAL_SleepMs(timeout_ms);
module_mqtt_get_connect_state(mqtt_client);
switch (mqtt_client->connect_state) {
case MQTT_CONNECT_STATE_CONNECTED:
return QCLOUD_RET_SUCCESS;
case MQTT_CONNECT_STATE_RECONNECTING:
return QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT;
case MQTT_CONNECT_STATE_RECONNECTED:
return QCLOUD_RET_MQTT_RECONNECTED;
case MQTT_CONNECT_STATE_DISCONNECT:
return QCLOUD_ERR_MQTT_RECONNECT_TIMEOUT;
}
return QCLOUD_RET_SUCCESS;
}
/**
* @brief Publish MQTT message.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_name topic to publish
* @param[in] params @see PublishParams
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_MQTT_Publish(void *client, const char *topic_name, const PublishParams *params)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(params, QCLOUD_ERR_INVAL);
STRING_PTR_SANITY_CHECK(topic_name, QCLOUD_ERR_INVAL);
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
if (strlen(topic_name) > MAX_SIZE_OF_CLOUD_TOPIC) {
return QCLOUD_ERR_MAX_TOPIC_LENGTH;
}
if (QOS2 == params->qos) {
Log_e("QoS2 is not supported currently");
return QCLOUD_ERR_MQTT_QOS_NOT_SUPPORT;
}
return module_mqtt_publish(mqtt_client, topic_name, params);
}
/**
* @brief Subscribe MQTT topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter to subscribe
* @param[in] params @see SubscribeParams
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_MQTT_Subscribe(void *client, const char *topic_filter, const SubscribeParams *params)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(params, QCLOUD_ERR_INVAL);
STRING_PTR_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
if (strlen(topic_filter) > MAX_SIZE_OF_CLOUD_TOPIC) {
return QCLOUD_ERR_MAX_TOPIC_LENGTH;
}
if (QOS2 == params->qos) {
Log_e("QoS2 is not supported currently");
return QCLOUD_ERR_MQTT_QOS_NOT_SUPPORT;
}
return qcloud_iot_mqtt_subscribe(mqtt_client, topic_filter, params);
}
/**
* @brief Unsubscribe MQTT topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter to unsubscribe
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_MQTT_Unsubscribe(void *client, const char *topic_filter)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
STRING_PTR_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
if (strlen(topic_filter) > MAX_SIZE_OF_CLOUD_TOPIC) {
return QCLOUD_ERR_MAX_TOPIC_LENGTH;
}
return qcloud_iot_mqtt_unsubscribe(mqtt_client, topic_filter);
}
/**
* @brief Check if MQTT topic has been subscribed or not
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter to subscribe
* @return true already subscribed
* @return false not ready
*/
bool IOT_MQTT_IsSubReady(void *client, const char *topic_filter)
{
POINTER_SANITY_CHECK(client, false);
STRING_PTR_SANITY_CHECK(topic_filter, false);
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
return qcloud_iot_mqtt_is_sub_ready(mqtt_client, topic_filter);
}
/**
* @brief Get user data in subscribe.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter to subscribe
* @return NULL or user data
*/
void *IOT_MQTT_GetSubUsrData(void *client, const char *topic_filter)
{
POINTER_SANITY_CHECK(client, NULL);
STRING_PTR_SANITY_CHECK(topic_filter, NULL);
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
return qcloud_iot_mqtt_get_subscribe_usr_data(mqtt_client, topic_filter);
}
/**
* @brief Subscribe and wait sub ready.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter to subscribe
* @param[in] params @see SubscribeParams
* @return @see IotReturnCode
*/
int IOT_MQTT_SubscribeSync(void *client, const char *topic_filter, const SubscribeParams *params)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(params, QCLOUD_ERR_INVAL);
STRING_PTR_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
return IOT_MQTT_Subscribe(client, topic_filter, 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)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
module_mqtt_get_connect_state(mqtt_client);
return mqtt_client->connect_state != MQTT_CONNECT_STATE_DISCONNECT;
}
/**
* @brief Get device info using to connect mqtt server.
*
* @param[in,out] client pointer to mqtt client
* @return @see DeviceInfo
*/
DeviceInfo *IOT_MQTT_GetDeviceInfo(void *client)
{
POINTER_SANITY_CHECK(client, NULL);
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
return mqtt_client->device_info;
}

View File

@@ -0,0 +1,114 @@
/**
* @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 at_module_mqtt_publish.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2022-04-25
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022-04-25 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "at_module.h"
/**
* @brief Check if topic match
*
* @param[in] topic_filter topic name filter, wildcard is supported
* @param[in] topic_name topic name, no wildcard
* @param[in] topic_name_len length of topic name
* @return 1 for matched, 0 for no matched
*
* @note assume topic filter and name is in correct format;
* # can only be at end;
* + and # can only be next to separator.
*/
static uint8_t _is_topic_matched(char *topic_filter, const char *topic_name, uint16_t topic_name_len)
{
char *curf = topic_filter;
char *curn = (char *)topic_name;
char *curn_end = curn + topic_name_len;
while (*curf && curn < curn_end) {
if (*curn == '/' && *curf != '/')
break;
if (*curf != '+' && *curf != '#' && *curf != *curn)
break;
if (*curf == '+') { // skip until we meet the next separator, or end of string
char *nextpos = curn + 1;
while (nextpos < curn_end && *nextpos != '/') nextpos = ++curn + 1;
} else if (*curf == '#')
curn = curn_end - 1; // skip until end of string
curf++;
curn++;
};
return (curn == curn_end) && (*curf == '\0');
}
/**
* @brief Deliver message to subscribe message handler.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_name topic name, no wildcard
* @param[in] payload publish packet payload
* @param[in] payload_len payload length
*/
void qcloud_iot_deliver_message(QcloudIotClient *client, char *topic_name, char *payload, int payload_len)
{
int i;
MQTTMessage message = {
.qos = QOS0, // no use
.retain = 0,
.dup = 0,
.packet_id = 0,
.topic_name = topic_name,
.topic_len = strlen(topic_name),
.payload_str = payload,
.payload_len = payload_len,
};
HAL_MutexLock(client->lock_generic);
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
if (client->sub_handles[i].topic_filter &&
_is_topic_matched(client->sub_handles[i].topic_filter, message.topic_name, message.topic_len)) {
if (client->sub_handles[i].params.on_message_handler) {
HAL_MutexUnlock(client->lock_generic);
// if found, then handle it, then return
client->sub_handles[i].params.on_message_handler(client, &message,
client->sub_handles[i].params.user_data);
return;
}
}
}
HAL_MutexUnlock(client->lock_generic);
/* Message handler not found for topic */
/* May be we do not care change FAILURE use SUCCESS*/
Log_d("no matching any topic, call default handle function");
MQTTEventMsg msg;
if (client->event_handle.h_fp) {
msg.event_type = MQTT_EVENT_PUBLISH_RECEIVED;
msg.msg = &message;
client->event_handle.h_fp(client, client->event_handle.context, &msg);
}
}

View File

@@ -0,0 +1,255 @@
/**
* @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 at_module_mqtt_subscribe.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2022-04-25
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022-04-25 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "at_module.h"
/**
* @brief Free topic_filter and user_data
*
* @param[in] handler subtopic handle
*/
static void _clear_sub_handle(SubTopicHandle *handler)
{
if (handler->topic_filter) {
HAL_Free(handler->topic_filter);
handler->topic_filter = NULL;
}
if (handler->params.user_data && handler->params.user_data_free) {
handler->params.user_data_free(handler->params.user_data);
handler->params.user_data = NULL;
}
}
/**
* @brief Remove sub handle when unsubscribe.
*
* @param[in,out] client pointer to mqtt_client
* @param[in] topic_filter topic to remove
* @return true topic exist
* @return false topic no exist
*/
static bool _remove_sub_handle_from_array(QcloudIotClient *client, const char *topic_filter)
{
int i;
bool topic_exists = false;
// remove from message handler array
HAL_MutexLock(client->lock_generic);
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
if ((client->sub_handles[i].topic_filter && !strcmp(client->sub_handles[i].topic_filter, topic_filter)) ||
strstr(topic_filter, "/#") || strstr(topic_filter, "/+")) {
// notify this event to topic subscriber
if (client->sub_handles[i].params.on_sub_event_handler) {
client->sub_handles[i].params.on_sub_event_handler(client, MQTT_EVENT_UNSUBSCRIBE,
client->sub_handles[i].params.user_data);
}
_clear_sub_handle(&client->sub_handles[i]);
// we don't want to break here, if the same topic is registered*with 2 callbacks.Unlikely scenario
topic_exists = true;
}
}
HAL_MutexUnlock(client->lock_generic);
return topic_exists;
}
/**
* @brief Add sub handle when subscribe.
*
* @param[in,out] client pointer to mqtt_client
* @param[in] sub_handle sub_handle to be add to array
* @return true topic exist
* @return false topic no exist
*/
static int _add_sub_handle_to_array(QcloudIotClient *client, const SubTopicHandle *sub_handle)
{
IOT_FUNC_ENTRY;
int i, i_free = -1;
HAL_MutexLock(client->lock_generic);
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
if (client->sub_handles[i].topic_filter) {
if (!strcmp(client->sub_handles[i].topic_filter, sub_handle->topic_filter)) {
i_free = i;
// free the memory before
_clear_sub_handle(&client->sub_handles[i]);
Log_w("Identical topic found: %s", sub_handle->topic_filter);
break;
}
} else {
i_free = i_free == -1 ? i : i_free;
}
}
if (-1 == i_free) {
Log_e("NO more @sub_handles space!");
HAL_MutexUnlock(client->lock_generic);
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
client->sub_handles[i_free] = *sub_handle;
HAL_MutexUnlock(client->lock_generic);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
/**
* @brief Serialize and send subscribe packet.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic to subscribe
* @param[in] params subscribe params
* @return >=0 for packet id, < 0 for failed @see IotReturnCode
*/
int qcloud_iot_mqtt_subscribe(QcloudIotClient *client, const char *topic_filter, const SubscribeParams *params)
{
IOT_FUNC_ENTRY;
int rc, qos = params->qos;
char *topic_filter_stored;
SubTopicHandle sub_handle;
// topic filter should be valid in the whole sub life
topic_filter_stored = HAL_Malloc(strlen(topic_filter) + 1);
if (!topic_filter_stored) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MALLOC);
}
strncpy(topic_filter_stored, topic_filter, strlen(topic_filter) + 1);
sub_handle.topic_filter = topic_filter_stored;
sub_handle.params = *params;
sub_handle.status = client->default_subscribe ? SUB_ACK_RECEIVED : SUB_ACK_NOT_RECEIVED;
// add sub handle first to process
rc = _add_sub_handle_to_array(client, &sub_handle);
if (rc) {
goto exit;
}
if (client->default_subscribe) {
return 0;
}
rc = module_mqtt_subscribe(client, topic_filter, qos);
if (!rc) {
sub_handle.status = SUB_ACK_RECEIVED;
}
IOT_FUNC_EXIT_RC(rc);
exit:
_remove_sub_handle_from_array(client, topic_filter_stored);
IOT_FUNC_EXIT_RC(rc);
}
/**
* @brief Serialize and send unsubscribe packet.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic to unsubscribe
* @return >=0 packet id, < 0 for failed @see IotReturnCode
*/
int qcloud_iot_mqtt_unsubscribe(QcloudIotClient *client, const char *topic_filter)
{
IOT_FUNC_ENTRY;
SubTopicHandle sub_handle;
memset(&sub_handle, 0, sizeof(SubTopicHandle));
// remove from sub handle
if (!_remove_sub_handle_from_array(client, topic_filter)) {
Log_w("subscription does not exists: %s", topic_filter);
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_UNSUB_FAIL);
}
IOT_FUNC_EXIT_RC(module_mqtt_unsubscribe(client, topic_filter));
}
/**
* @brief Return if topic is sub ready.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter
* @return true for ready
* @return false for not ready
*/
bool qcloud_iot_mqtt_is_sub_ready(QcloudIotClient *client, const char *topic_filter)
{
IOT_FUNC_ENTRY;
int i = 0;
HAL_MutexLock(client->lock_generic);
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
if ((client->sub_handles[i].topic_filter && !strcmp(client->sub_handles[i].topic_filter, topic_filter)) ||
strstr(topic_filter, "/#") || strstr(topic_filter, "/+")) {
HAL_MutexUnlock(client->lock_generic);
return client->sub_handles[i].status == SUB_ACK_RECEIVED;
}
}
HAL_MutexUnlock(client->lock_generic);
IOT_FUNC_EXIT_RC(false);
}
/**
* @brief Get usr data, usr should handle lock/unlock usrdata itself in callback and caller.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter
* @return NULL or user data
*/
void *qcloud_iot_mqtt_get_subscribe_usr_data(QcloudIotClient *client, const char *topic_filter)
{
IOT_FUNC_ENTRY;
int i = 0;
HAL_MutexLock(client->lock_generic);
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
if ((client->sub_handles[i].topic_filter && !strcmp(client->sub_handles[i].topic_filter, topic_filter)) ||
strstr(topic_filter, "/#") || strstr(topic_filter, "/+")) {
HAL_MutexUnlock(client->lock_generic);
return client->sub_handles[i].params.user_data;
}
}
HAL_MutexUnlock(client->lock_generic);
IOT_FUNC_EXIT_RC(NULL);
}
/**
* @brief Clear sub handle array.
*
* @param[in,out] client pointer to mqtt client
*/
void qcloud_iot_mqtt_sub_handle_array_clear(QcloudIotClient *client)
{
IOT_FUNC_ENTRY;
int i;
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
/* notify this event to topic subscriber */
if (client->sub_handles[i].topic_filter && client->sub_handles[i].params.on_sub_event_handler) {
client->sub_handles[i].params.on_sub_event_handler(client, MQTT_EVENT_CLIENT_DESTROY,
client->sub_handles[i].params.user_data);
}
_clear_sub_handle(&client->sub_handles[i]);
}
IOT_FUNC_EXIT;
}

View File

@@ -0,0 +1,116 @@
/**
* @file at_module_ota.c
* @author {hubert} ({hubertxxu@tencent.com})
* @brief
* @version 1.0
* @date 2022-06-07
*
* @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-07 1.0 hubertxxu first commit
* </table>
*/
#include "at_module.h"
/**
* @brief OTA info handle.
*
*/
static QcloudIotOtaFwInfo sg_iot_ota_info_handle;
/**
* @brief Get ota info handle.
*
* @return @see QcloudIotOtaFwInfo
*/
QcloudIotOtaFwInfo *qcloud_iot_get_ota_info_handle(void)
{
return &sg_iot_ota_info_handle;
}
/**
* @brief Init ota && report mcu & at version.
*
* @param[in] version mcu version.
* @return 0 for success
*/
int IOT_OTA_Init(const char *version)
{
sg_iot_ota_info_handle.get_fw_info_sem = HAL_SemaphoreCreate();
if (!sg_iot_ota_info_handle.get_fw_info_sem) {
Log_e("create sem fail.");
return QCLOUD_ERR_FAILURE;
}
module_set_ota_urc();
// TODO: get at module version and change cls state.
return module_ota_set_fw_version(IOT_OTA_CLS_STATE_REPORT_MCU, version);
}
/**
* @brief Deinit ota.
*
*/
void IOT_OTA_Deinit(void)
{
HAL_SemaphoreDestroy(sg_iot_ota_info_handle.get_fw_info_sem);
}
/**
* @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)
{
int rc = 0;
rc = module_ota_get_fw_info();
if (rc) {
return rc;
}
rc = HAL_SemaphoreWait(sg_iot_ota_info_handle.get_fw_info_sem, timeout_ms);
if (rc) {
*version = NULL;
*fw_size = 0;
*md5 = NULL;
return QCLOUD_ERR_FAILURE;
}
*version = sg_iot_ota_info_handle.version;
*fw_size = sg_iot_ota_info_handle.fw_size;
*md5 = sg_iot_ota_info_handle.md5;
return QCLOUD_RET_SUCCESS;
}
/**
* @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)
{
return module_ota_read_fw_data(fw_data, fw_data_len, timeout_ms);
}

View File

@@ -0,0 +1,230 @@
/**
* @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 at_module_urc.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2022-04-24
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022-04-24 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "at_module.h"
/**
* @brief URC for iot hub
* 1. urc for at command
* +TCMQTTRCVPUB
* +TCMQTTDISCON
* +TCMQTTRECONNECTING
* +TCMQTTRECONNECTED
* +TCOTASTATUS
* 2. urc cmd response
* +TCMQTTSTATE
* 3. urc for ota
* +TCREADFWDATA
* +TCFWINFO
* +TCOTASTATUS
*
*/
// ----------------------------------------------------------------------------
// mqtt urc handle
// ----------------------------------------------------------------------------
/**
* @brief Discconect by mqtt server.
*
* @param[in] data data recv from at module
* @param[in] data_len data len
*/
static void _urc_mqtt_disconnect_handle(const char *data, size_t data_len)
{
// +TCMQTTSTATE:-3
int error_code = 0;
QcloudIotClient *mqtt_client = qcloud_iot_get_mqtt_client();
if (!mqtt_client) {
return;
}
mqtt_client->connect_state = MQTT_CONNECT_STATE_DISCONNECT;
sscanf(data, "+TCMQTTDISCON,%d", &error_code);
Log_e("disconnect %d", error_code);
}
/**
* @brief Reconnecting.
*
* @param[in] data data recv from at module
* @param[in] data_len data len
*/
static void _urc_mqtt_reconnect_handle(const char *data, size_t data_len)
{
// +TCMQTTRECONNECTING
QcloudIotClient *mqtt_client = qcloud_iot_get_mqtt_client();
if (!mqtt_client) {
return;
}
mqtt_client->connect_state = MQTT_CONNECT_STATE_RECONNECTING;
}
/**
* @brief Reconnected.
*
* @param[in] data data recv from at module
* @param[in] data_len data len
*/
static void _urc_mqtt_reconnected_handle(const char *data, size_t data_len)
{
// +TCMQTTRECONNECTED
QcloudIotClient *mqtt_client = qcloud_iot_get_mqtt_client();
if (!mqtt_client) {
return;
}
mqtt_client->connect_state = MQTT_CONNECT_STATE_RECONNECTED;
}
/**
* @brief Connect state.
*
* @param[in] data data recv from at module
* @param[in] data_len data len
*/
static void _urc_mqtt_state_handle(const char *data, size_t data_len)
{
// +TCMQTTSTATE:1
int connect_state = 1;
QcloudIotClient *mqtt_client = qcloud_iot_get_mqtt_client();
if (!mqtt_client) {
return;
}
sscanf(data, "+TCMQTTSTATE:%d", &connect_state);
Log_d("mqtt state %d", connect_state);
if (connect_state) {
mqtt_client->connect_state = MQTT_CONNECT_STATE_CONNECTED;
}
}
/**
* @brief Recv publish packet.
*
* @param[in] data data recv from at module
* @param[in] data_len data len
*/
static void _urc_mqtt_publish_handle(const char *data, size_t data_len)
{
// +TCMQTTRCVPUB:"$thing/down/property/Q5NNWVC2Z8/test1",81,"{"method":"report_reply","clientToken":"property-69","code":0,"status":"success"}"
QcloudIotClient *mqtt_client = qcloud_iot_get_mqtt_client();
if (!mqtt_client) {
return;
}
char *head, *topic_name, *payload;
char *src = (char *)data;
src[data_len - 1] = '\0';
head = strtok((char *)data, "\"");
if (!head) {
return;
}
topic_name = strtok(NULL, "\"");
if (!topic_name) {
return;
}
strtok(NULL, "\"");
payload = strtok(NULL, "");
if (!payload) {
return;
}
qcloud_iot_deliver_message(mqtt_client, topic_name, payload, strlen(payload));
}
// ----------------------------------------------------------------------------
// ota urc handle
// ----------------------------------------------------------------------------
static void _delete_char(char *str, char ch)
{
char *p;
for (p = str; *p != '\0'; p++)
if (*p != ch)
*str++ = *p;
*str = '\0';
}
/**
* @brief Recv fw info.
*
* @param[in] data data recv from at module
* @param[in] data_len data len
*/
static void _urc_ota_fw_info_handle(const char *data, size_t data_len)
{
// +TCFWINFO:"1.2.0",17300,"a2aa3c261ebfc1322edafd37edb6b183",262144
QcloudIotOtaFwInfo *ota_info = qcloud_iot_get_ota_info_handle();
_delete_char((char *)data, '"');
sscanf((char *)data, "+TCFWINFO:%[^,],%u,%[^,],%u", ota_info->version, &(ota_info->fw_size), ota_info->md5,
&ota_info->fw_max_size);
HAL_SemaphorePost(ota_info->get_fw_info_sem);
}
static void _urc_ota_state_handle(const char *data, size_t data_len)
{
// Todo
}
// ----------------------------------------------------------------------------
// API
// ----------------------------------------------------------------------------
/**
* @brief Set mqtt urc to at module.
*
* @return 0 means successful.
*/
int module_set_mqtt_urc(void)
{
int rc;
rc = HAL_Module_SetUrc("+TCMQTTRCVPUB", _urc_mqtt_publish_handle);
rc |= HAL_Module_SetUrc("+TCMQTTDISCON", _urc_mqtt_disconnect_handle);
rc |= HAL_Module_SetUrc("+TCMQTTRECONNECTING", _urc_mqtt_reconnect_handle);
rc |= HAL_Module_SetUrc("+TCMQTTRECONNECTED", _urc_mqtt_reconnected_handle);
rc |= HAL_Module_SetUrc("+TCMQTTSTATE", _urc_mqtt_state_handle);
return rc;
}
/**
* @brief Set ota urc to at module.
*
* @return 0 means successful.
*/
int module_set_ota_urc(void)
{
int rc;
rc = HAL_Module_SetUrc("+TCFWINFO", _urc_ota_fw_info_handle);
rc |= HAL_Module_SetUrc("+TCOTASTATUS", _urc_ota_state_handle);
return rc;
}

View File

@@ -0,0 +1,8 @@
file(GLOB src_cos ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(src_services ${src_services} ${src_cos} PARENT_SCOPE)
if( ${CONFIG_COMPILE_SAMPLE} STREQUAL "ON")
file(GLOB src_cos_download_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/cos_download_sample.c)
add_executable(cos_download_sample ${src_cos_download_sample})
target_link_libraries(cos_download_sample ${libsdk})
endif()

View File

@@ -0,0 +1,93 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file cos_download_sample.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-27
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-27 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "qcloud_iot_cos.h"
#include "utils_log.h"
// ----------------------------------------------------------------------------
// Main
// ----------------------------------------------------------------------------
static int sg_main_exit = 0;
#ifdef __linux__
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
static void _main_exit(int sig)
{
Log_e("demo exit by signal:%d\n", sig);
sg_main_exit = 1;
}
#endif
int main(int argc, char **argv)
{
#ifdef __linux__
signal(SIGINT, _main_exit);
#endif
int rc = 0;
// init log level
LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
IotCosDownloadParams connect_params = {
.url = "http://localhost", // your cos url
.file_size = 0, // your cos file size
.offset = 0,
.is_fragmentation = false,
.is_https_enabled = false,
};
uint8_t buf[1024];
void *handle = IOT_COS_DownloadInit(&connect_params);
if (!handle) {
goto exit;
}
while (!IOT_COS_DownloadIsFinished(handle)) {
rc = IOT_COS_DownloadFetch(handle, buf, sizeof(buf), 5000);
if (rc < 0) {
break;
}
}
IOT_COS_DownloadDeinit(handle);
exit:
utils_log_deinit();
return rc;
}

View File

@@ -0,0 +1,261 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file cos_download.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-25
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-25 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "qcloud_iot_cos.h"
/**
* @brief COS request download header.
*
*/
#define HTTP_COS_DOWNLOAD_REQUEST_HEADER_LEN 256
/**
* @brief Download handle.
*
*/
typedef struct {
IotCosDownloadParams params;
void *http_client;
IotHTTPRequestParams http_request;
int download_size;
int is_first_run;
} HTTPCosDownloadHandle;
/**
* @brief Connect cos http server.
*
* @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
* @return 0 for success. others @see IotReturnCode
*/
static int _cos_download_connect(HTTPCosDownloadHandle *handle)
{
IotHTTPConnectParams connect_params = {
.url = handle->params.url,
.port = handle->params.is_https_enabled ? "443" : "80",
.ca_crt = NULL, // TODO: support cert
};
return IOT_HTTP_Connect(handle->http_client, &connect_params);
}
/**
* @brief Construct cos download request header.
*
* @param[out] header pointer to request header
* @param[in] is_fragmentation http fragmentation
* @param[in] begin_byte download begin byte
* @param[in] end_byte download end byte
* @return > 0 for header len, others fail
*/
static int _cos_download_request_header_construct(char *header, int is_fragmentation, int begin_byte, int end_byte)
{
int len = HAL_Snprintf(header, HTTP_COS_DOWNLOAD_REQUEST_HEADER_LEN,
"Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
"Accept-Encoding:gzip, deflate\r\nRange:bytes=%d-%d\r\n",
begin_byte, end_byte);
if (len <= 0) {
return QCLOUD_ERR_BUF_TOO_SHORT;
}
if (is_fragmentation) {
const char *keep_alive = "Connection:keep-alive\r\n";
int keep_alive_len = strlen(keep_alive);
if (HTTP_COS_DOWNLOAD_REQUEST_HEADER_LEN - len - 1 <= keep_alive_len) {
return QCLOUD_ERR_BUF_TOO_SHORT;
}
strncpy(header + len, keep_alive, keep_alive_len);
header[len + keep_alive_len] = '\0';
len += keep_alive_len;
}
return len;
}
/**
* @brief Request cos download.
*
* @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
* @param[in] max_len max_len for buffer to download in fragmentation
* @return 0 for success. others @see IotReturnCode
*/
static int _cos_download_request(HTTPCosDownloadHandle *handle, int max_len)
{
int rc, begin_byte, end_byte = 0;
begin_byte = handle->download_size;
#define min_http(x, y) (((x) < (y)) ? (x) : (y))
end_byte = handle->params.is_fragmentation
? begin_byte + min_http(max_len, handle->params.file_size - handle->download_size) - 1
: handle->params.file_size - 1;
#undef min_http
rc = _cos_download_request_header_construct(handle->http_request.header, handle->params.is_fragmentation,
begin_byte, end_byte);
if (rc <= 0) {
return rc;
}
handle->http_request.url = handle->params.url;
handle->http_request.method = IOT_HTTP_METHOD_GET;
handle->http_request.content_length = 0;
handle->http_request.content = handle->http_request.content_type = NULL;
return IOT_HTTP_Request(handle->http_client, &handle->http_request);
}
/**
* @brief HTTP recv data.
*
* @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
* @param[out] buf buf to store received data
* @param[in] buf_len buf_len
* @param[in] timeout_ms read socket timeout
* @return >= 0 for recv data len. others @see IotReturnCode
*/
static int _cos_download_recv_data(HTTPCosDownloadHandle *handle, uint8_t *buf, int buf_len, uint32_t timeout_ms)
{
int rc = IOT_HTTP_Recv(handle->http_client, buf, buf_len, timeout_ms);
if (rc > 0) {
handle->download_size += rc;
}
return rc;
}
/**************************************************************************************
* API
**************************************************************************************/
/**
* @brief Init cos download handle.
*
* @param[in] params @see IotCosDownloadParams
* @return pointer to cos download handle
*/
void *IOT_COS_DownloadInit(IotCosDownloadParams *params)
{
POINTER_SANITY_CHECK(params, NULL);
HTTPCosDownloadHandle *handle = HAL_Malloc(sizeof(HTTPCosDownloadHandle));
if (!handle) {
goto exit;
}
handle->http_client = IOT_HTTP_Init();
if (!handle->http_client) {
goto exit;
}
handle->http_request.header = HAL_Malloc(HTTP_COS_DOWNLOAD_REQUEST_HEADER_LEN);
if (!handle->http_request.header) {
goto exit;
}
handle->params = *params;
handle->download_size = params->offset;
if (_cos_download_connect(handle)) {
goto exit;
}
handle->is_first_run = 1;
return handle;
exit:
if (handle) {
HAL_Free(handle->http_request.header);
IOT_HTTP_Deinit(handle->http_client);
HAL_Free(handle);
}
return NULL;
}
/**
* @brief Fetch data from cos.
*
* @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
* @param[out] buf buffer to store data
* @param[in] buf_len buffer length
* @param timeout_ms timeout for fetching
* @return >= 0 for recv data len. others @see IotReturnCode
*/
int IOT_COS_DownloadFetch(void *handle, uint8_t *buf, uint32_t buf_len, uint32_t timeout_ms)
{
POINTER_SANITY_CHECK(handle, QCLOUD_ERR_INVAL);
int rc = 0;
HTTPCosDownloadHandle *download_handle = (HTTPCosDownloadHandle *)handle;
// download finish
if (IOT_COS_DownloadIsFinished(handle)) {
return 0;
}
if (download_handle->is_first_run) {
rc = _cos_download_request(download_handle, buf_len);
if (rc) {
Log_e("cos request failed %d", rc);
return rc;
}
download_handle->is_first_run = 0;
return _cos_download_recv_data(download_handle, buf, buf_len, timeout_ms);
}
if (download_handle->params.is_fragmentation && IOT_HTTP_IsRecvFinished(download_handle->http_client)) {
rc = _cos_download_request(download_handle, buf_len);
if (rc) {
Log_e("cos request failed %d", rc);
return rc;
}
}
return _cos_download_recv_data(download_handle, buf, buf_len, timeout_ms);
}
/**
* @brief Is download finished.
*
* @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
* @return true for finished
*/
int IOT_COS_DownloadIsFinished(void *handle)
{
POINTER_SANITY_CHECK(handle, QCLOUD_ERR_INVAL);
HTTPCosDownloadHandle *download_handle = (HTTPCosDownloadHandle *)handle;
return download_handle->download_size == download_handle->params.file_size;
}
/**
* @brief Deinit cos download.
*
* @param[in,out] handle pointer to cos download handle, @see HTTPCosDownloadHandle
*/
void IOT_COS_DownloadDeinit(void *handle)
{
POINTER_SANITY_CHECK_RTN(handle);
HTTPCosDownloadHandle *download_handle = (HTTPCosDownloadHandle *)handle;
IOT_HTTP_Disconnect(download_handle->http_client);
IOT_HTTP_Deinit(download_handle->http_client);
HAL_Free(download_handle->http_request.header);
HAL_Free(download_handle);
}

View File

@@ -0,0 +1,13 @@
file(GLOB src_dynreg ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(src_services ${src_services} ${src_dynreg} PARENT_SCOPE)
if( ${CONFIG_COMPILE_SAMPLE} STREQUAL "ON")
file(GLOB src_dynreg_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/dynreg_sample.c)
add_executable(dynreg_sample ${src_dynreg_sample})
target_link_libraries(dynreg_sample ${libsdk})
endif()
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,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 cos_download_sample.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-27
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-27 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "qcloud_iot_common.h"
#include "utils_log.h"
// ----------------------------------------------------------------------------
// Main
// ----------------------------------------------------------------------------
static int sg_main_exit = 0;
#ifdef __linux__
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
static void _main_exit(int sig)
{
Log_e("demo exit by signal:%d\n", sig);
sg_main_exit = 1;
}
#endif
int main(int argc, char **argv)
{
#ifdef __linux__
signal(SIGINT, _main_exit);
#endif
int rc = 0;
// init log level
LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
DeviceInfo device_info;
// 1. get device info
rc = HAL_GetDevInfo((void *)&device_info);
if (rc) {
Log_e("get device info failed: %d", rc);
return rc;
}
// 2. do dynreg
rc = IOT_DynReg_Device(&device_info);
if (rc) {
Log_e("dynreg error");
goto exit;
}
// 3. get device secret just save it. then you can do mqtt
HAL_SetDevInfo(&device_info);
Log_i("device secret : %s", device_info.device_secret);
exit:
utils_log_deinit();
return rc;
}

View File

@@ -0,0 +1,161 @@
/**
* @file dynreg.c
* @author {hubert} ({hubertxxu@tencent.com})
* @brief
* @version 1.0
* @date 2022-01-26
*
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @par Change Log:
* <table>
* Date Version Author Description
* 2022-01-26 1.0 hubertxxu first commit
* </table>
*/
#include "qcloud_iot_common.h"
#include "utils_json.h"
#include "utils_base64.h"
#include "mbedtls/aes.h"
#define DYN_RESPONSE_BUFF_LEN (256)
#define DECODE_BUFF_LEN (256)
#define UTILS_AES_BLOCK_LEN (16)
/**
* @brief aes cbc handle
*
* @return int 0 is success other is error.
*/
static int _aes_cbc(uint8_t *input_data, uint32_t input_data_len, uint8_t *output_data, uint32_t output_data_len,
DeviceInfo *device_info)
{
int i;
uint8_t iv[16] = {0};
char key[UTILS_AES_BLOCK_LEN + 1] = {0};
for (i = 0; i < 16; i++) {
iv[i] = '0';
}
strncpy(key, device_info->product_secret, UTILS_AES_BLOCK_LEN);
mbedtls_aes_context aes_ctx;
mbedtls_aes_init(&aes_ctx);
mbedtls_aes_setkey_dec(&aes_ctx, (const unsigned char *)key, 128);
if (mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, input_data_len, iv, (unsigned char *)input_data,
(unsigned char *)output_data)) {
return QCLOUD_ERR_FAILURE;
}
mbedtls_aes_free(&aes_ctx);
return QCLOUD_RET_SUCCESS;
}
/**
* @brief parse device register response buffer
*
* @param response_buf
* @param response_len
* @return int 0 success other error
*/
static int _parse_response_result(char *response_buf, int response_len, DeviceInfo *device_info)
{
int rc = 0;
UtilsJsonValue value;
char base64_decode_buf[DECODE_BUFF_LEN] = {0};
size_t olen = 0;
// 1. find payload
rc = utils_json_value_get("Response.Payload", strlen("Response.Payload"), response_buf, response_len, &value);
if (rc) {
Log_e("can not find payload. json : %s", response_buf);
goto exit;
}
// 2. base64 decode
rc = utils_base64decode((uint8_t *)base64_decode_buf, DECODE_BUFF_LEN, &olen, (const uint8_t *)value.value,
value.value_len);
if (rc) {
Log_e("base64 decode error.");
goto exit;
}
// 3. aes cbc
olen = olen + (16 - olen % 16);
rc = _aes_cbc((uint8_t *)base64_decode_buf, (uint32_t)olen, (uint8_t *)base64_decode_buf, DECODE_BUFF_LEN,
device_info);
if (rc) {
Log_e("aes cbc error. ");
goto exit;
}
// 4. find psk
uint32_t encryption_type = 0;
rc = utils_json_get_uint32("encryptionType", strlen("encryptionType"), base64_decode_buf, DECODE_BUFF_LEN,
&encryption_type);
if (rc) {
Log_e("parse encryptionType error. json : %s", base64_decode_buf);
goto exit;
}
rc = utils_json_value_get("psk", strlen("psk"), base64_decode_buf, DECODE_BUFF_LEN, &value);
if (rc) {
Log_e("parse psk error. json : %s", base64_decode_buf);
goto exit;
}
// 5. copy psk to device info
strncpy(device_info->device_secret, value.value, value.value_len);
exit:
return rc;
}
/**
* @brief dynreg device, get device secret or device cert file and private key file from iot platform
*
* @param[in] params @see DeviceInfo
* @return 0 is success other is failed
*/
int IOT_DynReg_Device(DeviceInfo *device_info)
{
int rc = 0;
POINTER_SANITY_CHECK(device_info, QCLOUD_ERR_INVAL);
if (strlen(device_info->product_secret) < UTILS_AES_BLOCK_LEN) {
Log_e("product key illegal");
rc = QCLOUD_ERR_FAILURE;
goto exit;
}
char dynreg_buf[DYN_RESPONSE_BUFF_LEN] = {0};
/* constructor dynreg http body */
int request_body_len =
HAL_Snprintf(dynreg_buf, DYN_RESPONSE_BUFF_LEN, "{\"ProductId\":\"%s\",\"DeviceName\":\"%s\"}",
device_info->product_id, device_info->device_name);
HttpSignedParams params = {
.host = DYN_REG_SERVER_URL,
.uri = DYN_REG_URI_PATH,
.need_recv = true,
.recv_timeout_ms = 2000,
.secretkey = device_info->product_secret,
};
rc = IOT_HTTP_SignedRequest(&params, dynreg_buf, request_body_len, (uint8_t *)dynreg_buf, DYN_RESPONSE_BUFF_LEN);
if (rc < 0) {
goto exit;
}
rc = _parse_response_result(dynreg_buf, rc, device_info);
exit:
return rc;
}

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,42 @@
/**
* @file test_dynreg.cc
* @author {hubert} ({hubertxxu@tencent.com})
* @brief
* @version 1.0
* @date 2022-01-26
*
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @par Change Log:
* <table>
* Date Version Author Description
* 2022-01-26 1.0 hubertxxu first commit
* </table>
*/
#include "gtest/gtest.h"
#include "mqtt_client_test.h"
#include "qcloud_iot_common.h"
namespace mqtt_client_unittest {
TEST_F(MqttClientTest, dynreg_test) {
DeviceInfo device_info;
ASSERT_EQ(HAL_GetDevInfo(&device_info), 0);
strncpy(device_info.device_name, "dynreg_test", 12);
EXPECT_EQ(IOT_DynReg_Device(&device_info), 0);
}
} // namespace mqtt_client_unittest

View File

@@ -0,0 +1,15 @@
file(GLOB src_gateway ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(src_services ${src_services} ${src_gateway} PARENT_SCOPE)
if( ${CONFIG_COMPILE_SAMPLE} STREQUAL "ON")
file(GLOB src_gateway_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/*.c)
add_executable(gateway_sample ${src_gateway_sample})
target_link_libraries(gateway_sample ${libsdk})
endif()
if( ${CONFIG_IOT_TEST} STREQUAL "ON")
file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
set(inc_gateway_test ${CMAKE_CURRENT_SOURCE_DIR}/test)
set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
set(inc_test ${inc_test} ${inc_gateway_test} PARENT_SCOPE)
endif()

View File

@@ -0,0 +1,312 @@
/**
* @file gateway_sample.c
* @author {hubert} ({hubertxxu@tencent.com})
* @brief
* @version 1.0
* @date 2022-05-25
*
* @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-05-25 1.0 hubertxxu first commit
* </table>
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "qcloud_iot_common.h"
#include "qcloud_iot_explorer.h"
#include "utils_log.h"
#include "gateway_subdev_handle.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
// ----------------------------------------------------------------------------
#if 0
static void _method_control_callback(UtilsJsonValue client_token, UtilsJsonValue params, void *usr_data)
{
char buf[256];
Log_i("recv msg[%.*s]: params=%.*s", client_token.value_len, client_token.value, params.value_len, params.value);
IOT_DataTemplate_PropertyControlReply(usr_data, buf, sizeof(buf), 0, client_token);
}
static void _method_report_reply_callback(UtilsJsonValue client_token, int code, void *usr_data)
{
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
}
static void _method_get_status_reply_callback(UtilsJsonValue client_token, int code, UtilsJsonValue reported,
UtilsJsonValue control, void *usr_data)
{
char buf[256];
Log_i("recv msg[%.*s]: code=%d|reported=%.*s|control=%.*s", client_token.value_len, client_token.value, code,
reported.value_len, STRING_PTR_PRINT_SANITY_CHECK(reported.value), control.value_len,
STRING_PTR_PRINT_SANITY_CHECK(control.value));
IOT_DataTemplate_PropertyClearControl(usr_data, buf, sizeof(buf));
}
static void _method_report_info_reply_callback(UtilsJsonValue client_token, int code, void *usr_data)
{
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
}
static void _method_clear_control_reply_callback(UtilsJsonValue client_token, int code, void *usr_data)
{
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
}
static void _method_event_reply_callback(UtilsJsonValue client_token, int code, void *usr_data)
{
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
}
static void _method_action_callback(UtilsJsonValue client_token, UtilsJsonValue action_id, UtilsJsonValue params,
void *usr_data)
{
char buf[256];
Log_i("recv msg[%.*s]: action_id=%.*s|params=%.*s", client_token.value_len, client_token.value, action_id.value_len,
action_id.value, params.value_len, params.value);
IotDataTemplateActionReply reply = {
.code = 0,
.client_token = client_token,
.response = "{\"err_code\":0}",
};
IOT_DataTemplate_ActionReply(usr_data, buf, sizeof(buf), reply);
}
// ----------------------------------------------------------------------------
// Data template upstream
// ----------------------------------------------------------------------------
static void _cycle_report(void *client)
{
char buf[256];
const char *report_property = "{\"power_switch\":0}";
IotDataTemplateEventData event_data = {
.event_id = "status_report",
.type = IOT_DATA_TEMPLATE_EVENT_TYPE_INFO,
.params = "{\"status\":0,\"message\":\"ok\"}",
};
static QcloudIotTimer sg_cycle_report_timer;
if (IOT_Timer_Expired(&sg_cycle_report_timer)) {
IOT_DataTemplate_PropertyReport(client, buf, sizeof(buf), report_property);
IOT_DataTemplate_EventPost(client, buf, sizeof(buf), event_data);
IOT_Timer_Countdown(&sg_cycle_report_timer, 500);
}
}
#endif
// ----------------------------------------------------------------------------
// 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;
}
#if 0
// subscribe normal topics and wait result
IotDataTemplateCallback callback = {
.property_callback = {.method_control_callback = _method_control_callback,
.method_clear_control_reply_callback = _method_clear_control_reply_callback,
.method_get_status_reply_callback = _method_get_status_reply_callback,
.method_report_info_reply_callback = _method_report_info_reply_callback,
.method_report_reply_callback = _method_report_reply_callback},
.event_callback = {.method_event_reply_callback = _method_event_reply_callback},
.action_callback = {.method_action_callback = _method_action_callback},
};
rc = IOT_DataTemplate_Init(client, callback, client);
if (rc) {
Log_e("Client Subscribe Topic Failed: %d", rc);
return rc;
}
const char *report_info =
"{\"module_hardinfo\":\"ESP8266\",\"module_softinfo\":\"V1.0\", \"fw_ver\":\"4.0.0\", "
"\"imei\":\"11-22-33-44\",\"lat\":\"22.546015\",\"lon\":\"113.941125\",\"mac\":\"11:22:33:44:55:66\",\"device_"
"label\":{\"append_"
"info\":\"your self defined info\"}}";
IOT_DataTemplate_PropertyReportInfo(client, buf, sizeof(buf), report_info);
IOT_DataTemplate_PropertyGetStatus(client, buf, sizeof(buf));
#endif
// gateway init
iot_gateway_init(client, 32);
do {
rc = IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT);
switch (rc) {
case QCLOUD_RET_SUCCESS:
break;
case QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT:
continue;
case QCLOUD_RET_MQTT_RECONNECTED:
IOT_DataTemplate_PropertyGetStatus(client, buf, sizeof(buf));
break;
default:
Log_e("Exit loop caused of errCode:%d", rc);
goto exit;
}
// _cycle_report(client);
} while (!sg_main_exit);
exit:
iot_gateway_deinit(client);
// IOT_DataTemplate_Deinit(client);
rc |= IOT_MQTT_Destroy(&client);
utils_log_deinit();
return rc;
}

View File

@@ -0,0 +1,514 @@
/**
* @file gateway_subdev_handle.c
* @author {hubert} ({hubertxxu@tencent.com})
* @brief
* @version 1.0
* @date 2022-05-25
*
* @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-05-25 1.0 hubertxxu first commit
* </table>
*/
#include "qcloud_iot_gateway.h"
#include "utils_list.h"
typedef struct {
void *subdev_list;
void *mqtt_client;
} GatewaySubDevHandle;
typedef enum {
GATEWAY_SUBDEV_OFFLINE = 0, // 离线
GATEWAY_SUBDEV_OFFLINEING = 1, // 发起离线
GATEWAY_SUBDEV_ONLINING = 2, // 发起上线
GATEWAY_SUBDEV_ONLINE = 3, // 在线
} GatewaySubDevOnlineStatus;
typedef enum {
GATEWAY_SUBDEV_UNBIND = 0,
GATEWAY_SUBDEV_UNBINDING = 2,
GATEWAY_SUBDEV_BINDING = 3,
GATEWAY_SUBDEV_BINDED = 4,
} GatewaySubDevBindStatus;
typedef struct {
DeviceInfo dev_info;
int online_status;
int bind_status;
bool is_sub_property;
void *mqtt_client;
} GatewaySubDev;
typedef struct {
GatewaySubDev *pair_sub;
const char *product_id;
const char *device_name;
} SubDevPair;
typedef enum {
DEV_ARRAY_HANDLE_DESCRIBE = 0,
DEV_ARRAY_HANDEL_ONLINE = 1,
DEV_ARRAY_HANDLE_OFFLINE = 2,
DEV_ARRAY_HANDLE_BIND = 3,
DEV_ARRAY_HANDLE_UNBIND = 4,
DEV_ARRAY_HANDLE_CHANGE_BIND = 5,
} DevArrayHandleType;
typedef struct {
GatewaySubDevHandle *gw_subdev_handle;
DevArrayHandleType type;
} DevArrayHandle;
static GatewaySubDevHandle *sg_gw_subdev_handle = NULL;
GatewaySubDev *iot_gateway_find_sub(GatewaySubDevHandle *sub_handle, const char *sub_pid, const char *sub_dname);
static UtilsListResult _find_subdev_by_name_str(void *list, void *node, void *val, void *usr_data);
// -----------------------------------------------------------------------
// sub device control && report
// ------------------------------------------------------------------------
int ext_subdev_control(const char *product_id, const char *device_name, int power_switch, int brightness)
{
Log_d("control device [%s/%s] power_switch : %d, brightness : %d", product_id, device_name, power_switch,
brightness);
return QCLOUD_RET_SUCCESS;
}
int ext_subdev_report(const char *product_id, const char *device_name, int power_switch, int brightness)
{
static int token_num = 0;
GatewaySubDevHandle *gw_subdev_handle = sg_gw_subdev_handle;
POINTER_SANITY_CHECK(gw_subdev_handle, QCLOUD_ERR_FAILURE);
POINTER_SANITY_CHECK(product_id, QCLOUD_ERR_FAILURE);
POINTER_SANITY_CHECK(device_name, QCLOUD_ERR_FAILURE);
GatewaySubDev *sub = iot_gateway_find_sub(gw_subdev_handle, product_id, device_name);
if (!sub || sub->bind_status != GATEWAY_SUBDEV_BINDED || sub->online_status != GATEWAY_SUBDEV_ONLINE) {
if (sub) {
Log_e("subdev [%s/%s] illegal, sub->bind_status : %d, sub->online_status : %d", sub->dev_info.product_id,
sub->dev_info.device_name, sub->bind_status, sub->online_status);
} else {
Log_e("subdev [%s/%s] illegal.", product_id, device_name);
}
return QCLOUD_ERR_FAILURE;
}
char property_topic[MAX_SIZE_OF_CLOUD_TOPIC];
char report_buf[256];
HAL_Snprintf(property_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$thing/up/property/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(sub->dev_info.product_id),
STRING_PTR_PRINT_SANITY_CHECK(sub->dev_info.device_name));
int len = HAL_Snprintf(report_buf, sizeof(report_buf),
"{\"method\":\"report\",\"clientToken\":\"property-%s-%u\",\"params\":{\"power_switch\":%d, "
"\"brightness\":%d}}",
device_name, token_num++, power_switch, brightness);
PublishParams pub_params = DEFAULT_PUB_PARAMS;
pub_params.qos = 0;
pub_params.payload = (void *)report_buf;
pub_params.payload_len = len;
return IOT_MQTT_Publish(gw_subdev_handle->mqtt_client, property_topic, &pub_params);
}
static void _data_template_property_message_handler(void *client, const MQTTMessage *message, void *usr_data)
{
int rc = 0;
GatewaySubDev *sub = (GatewaySubDev *)usr_data;
char buf[256];
Log_d("receive subdev [%s/%s] property message:%.*s", sub->dev_info.product_id, sub->dev_info.device_name,
message->payload_len, message->payload_str);
UtilsJsonValue client_token, method;
rc = utils_json_value_get("clientToken", strlen("clientToken"), (const char *)message->payload_str,
message->payload_len, &client_token);
if (rc) {
return;
}
rc = utils_json_value_get("method", strlen("method"), message->payload_str, message->payload_len, &method);
if (rc) {
return;
}
// only parse control
if (strncmp(method.value, "control", method.value_len)) {
return;
}
// parse params
int power_switch = -1, brightness = -1;
utils_json_get_int32("params.power_switch", strlen("params.power_switch"), message->payload_str,
message->payload_len, &power_switch);
utils_json_get_int32("params.brightness", strlen("params.brightness"), message->payload_str, message->payload_len,
&brightness);
rc = ext_subdev_control(sub->dev_info.product_id, sub->dev_info.device_name, power_switch, brightness);
char property_topic[MAX_SIZE_OF_CLOUD_TOPIC];
HAL_Snprintf(property_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$thing/up/property/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(sub->dev_info.product_id),
STRING_PTR_PRINT_SANITY_CHECK(sub->dev_info.device_name));
int len = HAL_Snprintf(buf, sizeof(buf), "{\"method\":\"control_reply\",\"clientToken\":\"%.*s\",\"code\":%d}",
client_token.value_len, client_token.value, rc);
PublishParams pub_params = DEFAULT_PUB_PARAMS;
pub_params.qos = 0;
pub_params.payload = (void *)buf;
pub_params.payload_len = len;
IOT_MQTT_Publish(client, property_topic, &pub_params);
ext_subdev_report(sub->dev_info.product_id, sub->dev_info.device_name, power_switch, brightness);
}
// ----------------------------------------------------
// subdev handle
// -----------------------------------------------------
static UtilsListResult _find_subdev_by_name_str(void *list, void *node, void *val, void *usr_data)
{
GatewaySubDev *sub = (GatewaySubDev *)val;
SubDevPair *march_sub = (SubDevPair *)usr_data;
if (!strncmp(sub->dev_info.product_id, march_sub->product_id, MAX_SIZE_OF_PRODUCT_ID) &&
!strncmp(sub->dev_info.device_name, march_sub->device_name, MAX_SIZE_OF_DEVICE_NAME)) {
march_sub->pair_sub = sub;
return LIST_TRAVERSE_BREAK;
}
return LIST_TRAVERSE_CONTINUE;
}
GatewaySubDev *iot_gateway_find_sub(GatewaySubDevHandle *sub_handle, const char *sub_pid, const char *sub_dname)
{
POINTER_SANITY_CHECK(sub_handle, NULL);
POINTER_SANITY_CHECK(sub_pid, NULL);
POINTER_SANITY_CHECK(sub_dname, NULL);
SubDevPair sub_info = {NULL, sub_pid, sub_dname};
utils_list_process(sub_handle->subdev_list, LIST_HEAD, _find_subdev_by_name_str, &sub_info);
return sub_info.pair_sub;
}
static UtilsListResult _remove_subdev_by_name_str(void *list, void *node, void *val, void *usr_data)
{
GatewaySubDev *sub = (GatewaySubDev *)val;
SubDevPair *march_sub = (SubDevPair *)usr_data;
if (!strncmp(sub->dev_info.product_id, march_sub->product_id, MAX_SIZE_OF_PRODUCT_ID) &&
!strncmp(sub->dev_info.device_name, march_sub->device_name, MAX_SIZE_OF_DEVICE_NAME)) {
march_sub->pair_sub = sub;
Log_w("remove sub[%s/%s]", sub->dev_info.product_id, sub->dev_info.device_name);
// 1. unsub property topic
char property_topic[MAX_SIZE_OF_CLOUD_TOPIC];
HAL_Snprintf(property_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$thing/down/property/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(sub->dev_info.product_id),
STRING_PTR_PRINT_SANITY_CHECK(sub->dev_info.device_name));
IOT_MQTT_Unsubscribe(sub->mqtt_client, property_topic);
// 2. offline?
// 3. remove
utils_list_remove(list, node);
return LIST_TRAVERSE_BREAK;
}
return LIST_TRAVERSE_CONTINUE;
}
void iot_gateway_remove_subdev_from_sublist(GatewaySubDevHandle *sub_handle, DeviceInfo *sub_dev_info)
{
SubDevPair pair_sub = {
.pair_sub = NULL, .device_name = sub_dev_info->device_name, .product_id = sub_dev_info->product_id};
utils_list_process(sub_handle->subdev_list, LIST_HEAD, _remove_subdev_by_name_str, &pair_sub);
}
int iot_gateway_add_sub_to_sublist(GatewaySubDevHandle *sub_handle, DeviceInfo *sub_dev_info, int bind_status)
{
GatewaySubDev *new_sub = iot_gateway_find_sub(sub_handle, sub_dev_info->product_id, sub_dev_info->device_name);
if (new_sub) {
new_sub->bind_status = bind_status;
Log_w("subdev[%s/%s] already exists.", sub_dev_info->product_id, sub_dev_info->device_name);
return QCLOUD_RET_SUCCESS;
}
new_sub = HAL_Malloc(sizeof(GatewaySubDev));
POINTER_SANITY_CHECK(new_sub, QCLOUD_ERR_MALLOC);
memset(new_sub, 0, sizeof(GatewaySubDev));
memcpy(&(new_sub->dev_info), sub_dev_info, sizeof(DeviceInfo));
void *node = utils_list_push(sub_handle->subdev_list, new_sub);
if (!node) {
HAL_Free(new_sub);
Log_e("add subdev[%s/%s] error.", sub_dev_info->product_id, sub_dev_info->device_name);
return QCLOUD_ERR_FAILURE;
}
new_sub->bind_status = bind_status;
new_sub->mqtt_client = sub_handle->mqtt_client;
Log_i("add subdev[%s/%s] in subdev list[total subdev : %d].", sub_dev_info->product_id, sub_dev_info->device_name,
utils_list_len_get(sub_handle->subdev_list));
return QCLOUD_RET_SUCCESS;
}
int iot_gateway_bind_subdev(DeviceInfo *sub_dev_info)
{
GatewaySubDevHandle *gw_subdev_handle = sg_gw_subdev_handle;
POINTER_SANITY_CHECK(sub_dev_info, QCLOUD_ERR_FAILURE);
POINTER_SANITY_CHECK(gw_subdev_handle, QCLOUD_ERR_FAILURE);
// 1. check if in subdev list
GatewaySubDev *sub = iot_gateway_find_sub(gw_subdev_handle, sub_dev_info->product_id, sub_dev_info->device_name);
if (sub && sub->bind_status == GATEWAY_SUBDEV_BINDED) {
return QCLOUD_RET_SUCCESS;
}
// 2. add to subdev list
iot_gateway_add_sub_to_sublist(gw_subdev_handle, sub_dev_info, GATEWAY_SUBDEV_BINDING);
// 3. sync cloud
char bind_buf[256];
const DeviceInfo *bind_dev_list[] = {sub_dev_info};
return IOT_Gateway_BindUnbind(gw_subdev_handle->mqtt_client, bind_buf, 256, bind_dev_list, 1, true);
}
int iot_gateway_subdev_on_or_offline(GatewaySubDevHandle *sub_handle, DeviceInfo *sub_dev_info, bool is_online)
{
char sub_dev_online_buffer[512];
const DeviceInfo *dev_online_list[] = {sub_dev_info};
// check device in dev list && binded
GatewaySubDev *sub_dev = iot_gateway_find_sub(sub_handle, sub_dev_info->product_id, sub_dev_info->device_name);
if (sub_dev && sub_dev->bind_status == GATEWAY_SUBDEV_BINDED && sub_dev->online_status != GATEWAY_SUBDEV_ONLINE) {
sub_dev->online_status = GATEWAY_SUBDEV_ONLINING;
return IOT_Gateway_SubOnOffLine(sub_handle->mqtt_client, sub_dev_online_buffer, 512, dev_online_list, 1,
is_online);
}
return QCLOUD_ERR_FAILURE;
}
int iot_gateway_subdev_subscribe_property(GatewaySubDevHandle *sub_handle, DeviceInfo *sub_dev_info)
{
POINTER_SANITY_CHECK(sub_handle, QCLOUD_ERR_FAILURE);
POINTER_SANITY_CHECK(sub_dev_info, QCLOUD_ERR_FAILURE);
int rc;
// find subdev && check binded && online
GatewaySubDev *sub = iot_gateway_find_sub(sub_handle, sub_dev_info->product_id, sub_dev_info->device_name);
POINTER_SANITY_CHECK(sub, QCLOUD_ERR_FAILURE);
if (sub->bind_status != GATEWAY_SUBDEV_BINDED || sub->online_status != GATEWAY_SUBDEV_ONLINE ||
sub->is_sub_property == true) {
Log_e("subdev [%s/%s] illegal. bind status : %d online status : %d is_sub_property : %s",
sub_dev_info->product_id, sub_dev_info->device_name, sub->bind_status, sub->online_status,
sub->is_sub_property ? "true" : "false");
return QCLOUD_ERR_FAILURE;
}
char property_topic[MAX_SIZE_OF_CLOUD_TOPIC];
HAL_Snprintf(property_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$thing/down/property/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(sub->dev_info.product_id),
STRING_PTR_PRINT_SANITY_CHECK(sub->dev_info.device_name));
// subscribe normal topics and wait result
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
sub_params.on_message_handler = _data_template_property_message_handler;
sub_params.qos = QOS1;
sub_params.user_data = sub;
sub_params.user_data_free = NULL;
rc = IOT_MQTT_SubscribeSync(sub_handle->mqtt_client, property_topic, &sub_params);
if (rc > 0) {
sub->is_sub_property = true;
}
return rc;
}
int qcloud_gateway_subdev_unsubscribe_property(GatewaySubDevHandle *sub_handle, DeviceInfo *sub_dev_info)
{
POINTER_SANITY_CHECK(sub_handle, QCLOUD_ERR_FAILURE);
POINTER_SANITY_CHECK(sub_dev_info, QCLOUD_ERR_FAILURE);
char property_topic[MAX_SIZE_OF_CLOUD_TOPIC];
HAL_Snprintf(property_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$thing/down/property/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(sub_dev_info->product_id),
STRING_PTR_PRINT_SANITY_CHECK(sub_dev_info->device_name));
return IOT_MQTT_Unsubscribe(sub_handle->mqtt_client, property_topic);
}
// ----------------------------------------------------
// gateway callback function
// -----------------------------------------------------
UtilsJsonArrayIterResult _parse_subdev_array_cb(const char *subdev, int subdev_len, void *usr_data)
{
int rc = 0;
UtilsJsonValue device_name;
UtilsJsonValue product_id;
utils_json_value_get("device_name", strlen("device_name"), subdev, subdev_len, &device_name);
utils_json_value_get("product_id", strlen("product_id"), subdev, subdev_len, &product_id);
DeviceInfo dev_info;
memset(&dev_info, 0, sizeof(DeviceInfo));
strncpy(dev_info.product_id, product_id.value, product_id.value_len);
strncpy(dev_info.device_name, device_name.value, device_name.value_len);
DevArrayHandle *dev_array_handle = (DevArrayHandle *)usr_data;
switch (dev_array_handle->type) {
case DEV_ARRAY_HANDLE_CHANGE_BIND:
case DEV_ARRAY_HANDLE_DESCRIBE:
// subdev has be bind in cloud. add this to subdev list
rc = iot_gateway_add_sub_to_sublist(dev_array_handle->gw_subdev_handle, &dev_info, GATEWAY_SUBDEV_BINDED);
if (!rc) {
iot_gateway_subdev_on_or_offline(dev_array_handle->gw_subdev_handle, &dev_info, true);
}
break;
case DEV_ARRAY_HANDEL_ONLINE: {
int result = -1;
GatewaySubDev *sub_dev =
iot_gateway_find_sub(dev_array_handle->gw_subdev_handle, dev_info.product_id, dev_info.device_name);
utils_json_get_int32("result", strlen("result"), subdev, subdev_len, &result);
if (sub_dev && 0 == result) {
sub_dev->online_status = GATEWAY_SUBDEV_ONLINE;
iot_gateway_subdev_subscribe_property(dev_array_handle->gw_subdev_handle, &dev_info);
} else if (sub_dev) {
sub_dev->online_status = result;
}
} break;
case DEV_ARRAY_HANDLE_BIND: {
int result = -1;
GatewaySubDev *sub_dev =
iot_gateway_find_sub(dev_array_handle->gw_subdev_handle, dev_info.product_id, dev_info.device_name);
if (!sub_dev) {
break;
}
utils_json_get_int32("result", strlen("result"), subdev, subdev_len, &result);
if (IOT_GATEWAY_RET_SUCCESS == result || IOT_GATEWAY_ERR_BIND_REPEAT == result) {
sub_dev->bind_status = GATEWAY_SUBDEV_BINDED;
iot_gateway_subdev_on_or_offline(dev_array_handle->gw_subdev_handle, &dev_info, true);
} else {
sub_dev->bind_status = result;
}
} break;
case DEV_ARRAY_HANDLE_UNBIND: {
iot_gateway_remove_subdev_from_sublist(dev_array_handle->gw_subdev_handle, &dev_info);
} break;
default:
break;
}
return UTILS_JSON_ARRAY_ITER_CONTINUE;
}
static void _gateway_bind_unbind_reply_callback(UtilsJsonValue sub_devices, bool is_bind, void *usr_data)
{
GatewaySubDevHandle *gw_subdev_handle = (GatewaySubDevHandle *)usr_data;
Log_d("bind status : %d %.*s", is_bind, sub_devices.value_len, sub_devices.value);
DevArrayHandle dev_array_handle = {.gw_subdev_handle = gw_subdev_handle,
.type = is_bind ? DEV_ARRAY_HANDLE_BIND : DEV_ARRAY_HANDLE_UNBIND};
utils_json_array_parse(sub_devices.value, sub_devices.value_len, _parse_subdev_array_cb, &dev_array_handle);
}
static void _gateway_unbind_all_callback(void *usr_data)
{
// GatewaySubDevHandle *gw_subdev_handle = (GatewaySubDevHandle *)usr_data;
Log_d("unbind all.");
}
static void _gateway_online_offline_reply_callback(UtilsJsonValue sub_devices, bool is_online, void *usr_data)
{
GatewaySubDevHandle *gw_subdev_handle = (GatewaySubDevHandle *)usr_data;
Log_d("online status : %d %.*s", is_online, sub_devices.value_len, sub_devices.value);
DevArrayHandle dev_array_handle = {.gw_subdev_handle = gw_subdev_handle,
.type = is_online ? DEV_ARRAY_HANDEL_ONLINE : DEV_ARRAY_HANDLE_OFFLINE};
utils_json_array_parse(sub_devices.value, sub_devices.value_len, _parse_subdev_array_cb, &dev_array_handle);
}
static void _gateway_search_device_callback(bool is_on, void *usr_data)
{
// GatewaySubDevHandle *gw_subdev_handle = (GatewaySubDevHandle *)usr_data;
Log_d("search : %d", is_on);
}
static void _gateway_describe_subdevices_reply_callback(UtilsJsonValue sub_devices, void *usr_data)
{
GatewaySubDevHandle *gw_subdev_handle = (GatewaySubDevHandle *)usr_data;
DevArrayHandle dev_array_handle = {.gw_subdev_handle = gw_subdev_handle, .type = DEV_ARRAY_HANDLE_DESCRIBE};
Log_d("describe %.*s", sub_devices.value_len, sub_devices.value);
utils_json_array_parse(sub_devices.value, sub_devices.value_len, _parse_subdev_array_cb, &dev_array_handle);
}
static void _gateway_change_subdevices_status_callback(UtilsJsonValue sub_devices, bool is_bind, void *usr_data)
{
GatewaySubDevHandle *gw_subdev_handle = (GatewaySubDevHandle *)usr_data;
Log_d("change sub devices is bind %d %.*s", is_bind, sub_devices.value_len, sub_devices.value);
DevArrayHandle dev_array_handle = {.gw_subdev_handle = gw_subdev_handle,
.type = is_bind ? DEV_ARRAY_HANDLE_CHANGE_BIND : DEV_ARRAY_HANDLE_UNBIND};
utils_json_array_parse(sub_devices.value, sub_devices.value_len, _parse_subdev_array_cb, &dev_array_handle);
}
// -----------------------------------------------------------------
// export api
// -----------------------------------------------------------------
int iot_gateway_init(void *client, int sub_dev_max)
{
int rc = 0;
POINTER_SANITY_CHECK(client, QCLOUD_ERR_FAILURE);
// 1. init gateway subdev handle
GatewaySubDevHandle *gw_subdev_handle = HAL_Malloc(sizeof(GatewaySubDevHandle));
POINTER_SANITY_CHECK(gw_subdev_handle, QCLOUD_ERR_MALLOC);
UtilsListFunc func = DEFAULT_LIST_FUNCS;
gw_subdev_handle->subdev_list = utils_list_create(func, sub_dev_max);
if (!gw_subdev_handle->subdev_list) {
goto _exit;
}
gw_subdev_handle->mqtt_client = client;
sg_gw_subdev_handle = gw_subdev_handle;
// 2. sub gateway topic
IotGatewayMessageCallback callback = {
.bind_unbind_reply_callback = _gateway_bind_unbind_reply_callback,
.change_subdevices_status_callback = _gateway_change_subdevices_status_callback,
.describe_subdevices_reply_callback = _gateway_describe_subdevices_reply_callback,
.online_offline_reply_callback = _gateway_online_offline_reply_callback,
.search_device_callback = _gateway_search_device_callback,
.unbind_all_callback = _gateway_unbind_all_callback,
};
rc = IOT_Gateway_Init(client, callback, gw_subdev_handle);
if (rc < 0) {
goto _exit;
}
// 3. sync subdev list from cloud
return IOT_Gateway_Describe(client);
_exit:
utils_list_destroy(gw_subdev_handle->subdev_list);
HAL_Free(gw_subdev_handle);
return QCLOUD_ERR_FAILURE;
}
int iot_gateway_deinit(void *client)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_FAILURE);
GatewaySubDevHandle *gw_subdev_handle = IOT_Gateway_GetUsrData(client);
IOT_Gateway_Deinit(client);
POINTER_SANITY_CHECK(gw_subdev_handle, QCLOUD_ERR_FAILURE);
utils_list_destroy(gw_subdev_handle->subdev_list);
HAL_Free(gw_subdev_handle);
return QCLOUD_RET_SUCCESS;
}

View File

@@ -0,0 +1,44 @@
/**
* @file gateway_subdev_handle.h
* @author {hubert} ({hubertxxu@tencent.com})
* @brief
* @version 1.0
* @date 2022-05-25
*
* @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-05-25 1.0 hubertxxu first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_GATEWAY_SAMPLE_GATEWAY_SUBDEV_HANDLE_H_
#define IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_GATEWAY_SAMPLE_GATEWAY_SUBDEV_HANDLE_H_
#ifdef __cplusplus
extern "C" {
#endif
int iot_gateway_init(void *client, int sub_dev_max);
int iot_gateway_deinit(void *client);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_GATEWAY_SAMPLE_GATEWAY_SUBDEV_HANDLE_H_

View File

@@ -0,0 +1,470 @@
/**
* @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 gateway_mqtt.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2022-05-10
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022-05-10 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "qcloud_iot_gateway.h"
#include "utils_hmac.h"
#include "utils_base64.h"
// ----------------------------------------------------------------------------
// downstream message
// ----------------------------------------------------------------------------
/**
* @brief Context of gateway callback and user data.
*
*/
typedef struct {
IotGatewayMessageCallback callback;
void *usr_data;
} GatewayContext;
/**
* @brief Gateway message type.
*
*/
typedef enum {
TYPE_BIND = 0,
TYPE_UNBIND,
TYPE_UNBIND_ALL,
TYPE_ONLINE,
TYPE_OFFLINE,
TYPE_SEARCH_DEVICES,
TYPE_DESCRIBE_SUB_DEVICES,
TYPE_CHANGE,
} GatewayMessageType;
/**
* @brief Get message type.
*
* @param[in] json_buf
* @param[in] buf_len
* @return < 0 for fail, others @see GatewayMessageType
*/
static int _parse_gateway_message_type(const char *json_buf, int buf_len)
{
UtilsJsonValue type;
// parse type
int rc = utils_json_value_get("type", sizeof("type") - 1, json_buf, buf_len, &type);
if (rc) {
Log_e("parse gateway msg type error");
return rc;
}
/**
* @brief Order @see GatewayMessageType
*
*/
const char *gateway_type_str[] = {
[TYPE_BIND] = "bind",
[TYPE_UNBIND] = "unbind",
[TYPE_UNBIND_ALL] = "unbind_all",
[TYPE_ONLINE] = "online",
[TYPE_OFFLINE] = "offline",
[TYPE_SEARCH_DEVICES] = "search_devices",
[TYPE_DESCRIBE_SUB_DEVICES] = "describe_sub_devices",
[TYPE_CHANGE] = "change",
};
for (int i = 0; i < sizeof(gateway_type_str) / sizeof(const char *); i++) {
if (!strncmp(type.value, gateway_type_str[i], type.value_len)) {
return i;
}
}
return QCLOUD_ERR_FAILURE;
}
static void _callback_gateway(GatewayMessageType msg_type, IotGatewayMessageCallback *callback,
UtilsJsonValue sub_devices, int status, void *usr_data)
{
switch (msg_type) {
case TYPE_BIND:
case TYPE_UNBIND:
if (callback->bind_unbind_reply_callback) {
callback->bind_unbind_reply_callback(sub_devices, msg_type == TYPE_BIND, usr_data);
}
break;
case TYPE_UNBIND_ALL:
if (callback->unbind_all_callback) {
callback->unbind_all_callback(usr_data);
}
break;
case TYPE_ONLINE:
case TYPE_OFFLINE:
if (callback->online_offline_reply_callback) {
callback->online_offline_reply_callback(sub_devices, msg_type == TYPE_ONLINE, usr_data);
}
break;
case TYPE_SEARCH_DEVICES:
if (callback->search_device_callback) {
callback->search_device_callback(status, usr_data);
}
break;
case TYPE_DESCRIBE_SUB_DEVICES:
if (callback->describe_subdevices_reply_callback) {
callback->describe_subdevices_reply_callback(sub_devices, usr_data);
}
break;
case TYPE_CHANGE:
// status : 0:unbind 1:bind
if (callback->change_subdevices_status_callback) {
callback->change_subdevices_status_callback(sub_devices, status, usr_data);
}
break;
default:
Log_e("unsupported now");
break;
}
}
/**
* @brief Gateway downstream messsage handle.
*
* @param[in] client pointer to mqtt client
* @param[in] message @see MQTTMessage
* @param[in] user_data
*/
static void _gateway_mqtt_message_handler(void *client, const MQTTMessage *message, void *user_data)
{
int rc = 0;
Log_d("receive gateway message %.*s", message->payload_len, message->payload_str);
int msg_type = _parse_gateway_message_type(message->payload_str, message->payload_len);
if (msg_type < 0) {
return;
}
GatewayContext *gateway_context = user_data;
Log_i("get type %d from server", msg_type);
UtilsJsonValue sub_devices;
int32_t status;
if (msg_type != TYPE_SEARCH_DEVICES && msg_type != TYPE_UNBIND_ALL) {
rc = utils_json_value_get("payload.devices", sizeof("payload.devices") - 1, message->payload_str,
message->payload_len, &sub_devices);
if (rc) {
Log_e("invalid gateway payload.");
return;
}
}
if (msg_type == TYPE_SEARCH_DEVICES || msg_type == TYPE_CHANGE) {
rc = utils_json_get_int32("payload.status", sizeof("payload.status") - 1, message->payload_str,
message->payload_len, &status);
if (rc) {
return;
}
}
_callback_gateway(msg_type, &gateway_context->callback, sub_devices, status, gateway_context->usr_data);
}
/**
* @brief Subscribe gateway result topic.
*
* @param[in] client pointer to mqtt client
* @param[in] callback @see IotGatewayMessageCallback
* @param[in] usr_data
* @return @see IotReturnCode
*/
static int _gateway_mqtt_subscribe(void *client, IotGatewayMessageCallback callback, void *usr_data)
{
GatewayContext *gateway_context = HAL_Malloc(sizeof(GatewayContext));
if (!gateway_context) {
return QCLOUD_ERR_MALLOC;
}
gateway_context->callback = callback;
gateway_context->usr_data = usr_data;
char gateway_topic[MAX_SIZE_OF_CLOUD_TOPIC];
HAL_Snprintf(gateway_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$gateway/operation/result/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
SubscribeParams sub_param = DEFAULT_SUB_PARAMS;
sub_param.on_message_handler = _gateway_mqtt_message_handler;
sub_param.user_data = gateway_context;
sub_param.user_data_free = HAL_Free;
int rc = IOT_MQTT_SubscribeSync(client, gateway_topic, &sub_param);
if (rc) {
HAL_Free(gateway_context);
}
return rc;
}
// ----------------------------------------------------------------------------
// server mqtt context
// ----------------------------------------------------------------------------
/**
* @brief Calculate bind signature
*
* @param[in] subdev_info subdev info
* @param[in] nonce random
* @param[in] timestamp timestamp
* @param[out] signature signature
* @return 0 for success
*/
static int _subdev_bind_hmac_sha1_cal(const DeviceInfo *subdev_info, int nonce, uint64_t timestamp, char *signature)
{
#define MAX_SIGN_BUF_SIZE (MAX_SIZE_OF_CLIENT_ID + 48)
size_t olen = 0;
char sign_buf[MAX_SIGN_BUF_SIZE] = {0};
int sign_buf_len;
char sign[SHA1_DIGEST_SIZE] = {0};
/* 1. sign fmt : ${product_id}${device_name};${random};${expiration_time} */
sign_buf_len = HAL_Snprintf(sign_buf, sizeof(sign_buf), "%s%s;%d;%llu", subdev_info->product_id,
subdev_info->device_name, nonce, timestamp);
/* 2. cal hmac sha1 */
utils_hmac_sha1(sign_buf, sign_buf_len, (const uint8_t *)subdev_info->device_secret,
strlen(subdev_info->device_secret), sign);
/* 3. base64 encode */
return utils_base64encode((uint8_t *)signature, 31, &olen, (const uint8_t *)sign, SHA1_DIGEST_SIZE);
}
/**
* @brief Publish gateway message.
*
* @param[in] client pointer to mqtt client
* @param[in] qos @see QoS
* @param[in] payload message payload
* @param[in] payload_len payload length
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
static int _gateway_mqtt_publish(void *client, QoS qos, const char *payload, int payload_len)
{
char gateway_topic[MAX_SIZE_OF_CLOUD_TOPIC];
HAL_Snprintf(gateway_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$gateway/operation/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
PublishParams pub_params = DEFAULT_PUB_PARAMS;
pub_params.qos = qos;
pub_params.payload = (void *)payload;
pub_params.payload_len = payload_len;
return IOT_MQTT_Publish(client, gateway_topic, &pub_params);
}
// ----------------------------------------------------------------------------
// API
// ----------------------------------------------------------------------------
/**
* @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)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
return _gateway_mqtt_subscribe(client, callback, usr_data);
}
/**
* @brief Unsubscribe gateway topic.
*
* @param[in,out] client pointer to mqtt client
*/
void IOT_Gateway_Deinit(void *client)
{
POINTER_SANITY_CHECK_RTN(client);
char gateway_topic[MAX_SIZE_OF_CLOUD_TOPIC];
HAL_Snprintf(gateway_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$gateway/operation/result/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
IOT_MQTT_Unsubscribe(client, gateway_topic);
}
/**
* @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)
{
POINTER_SANITY_CHECK(client, NULL);
char gateway_topic[MAX_SIZE_OF_CLOUD_TOPIC];
HAL_Snprintf(gateway_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$gateway/operation/result/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
GatewayContext *gateway_context = IOT_MQTT_GetSubUsrData(client, gateway_topic);
if (gateway_context && gateway_context->usr_data) {
return gateway_context->usr_data;
}
return NULL;
}
/**
* @brief Publish subdevice online/offline message.
*
* @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)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(sub_dev_list, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(*sub_dev_list, QCLOUD_ERR_INVAL);
NUMBERIC_SANITY_CHECK(num, QCLOUD_ERR_INVAL);
if (num > 1) {
Log_e("Only one subdevice is supported!");
return QCLOUD_ERR_INVAL;
}
int len;
len = HAL_Snprintf(buf, buf_len, "{\"type\":\"%s\",\"payload\":{\"devices\":[", is_online ? "online" : "offline");
for (int i = 0; i < num; i++) {
len += HAL_Snprintf(buf + len, buf_len - len, "{\"product_id\":\"%s\",\"device_name\":\"%s\"},",
sub_dev_list[i]->product_id, sub_dev_list[i]->device_name);
}
len += HAL_Snprintf(buf + len - 1, buf_len - len + 1, "]}}"); // -1 remove ','
return _gateway_mqtt_publish(client, QOS0, buf, len);
}
/**
* @brief Publish subdevice bind/unbind message.
*
* @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)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(sub_dev_list, QCLOUD_ERR_INVAL);
NUMBERIC_SANITY_CHECK(num, QCLOUD_ERR_INVAL);
if (num > 1) {
Log_e("Only one subdevice is supported!");
return QCLOUD_ERR_INVAL;
}
srand(IOT_Timer_CurrentSec());
int nonce = rand();
uint64_t timestamp = IOT_Timer_CurrentSec();
char signature[31] = {0};
int rc, len;
len = HAL_Snprintf(buf, buf_len, "{\"type\":\"%s\",\"payload\":{\"devices\":[", is_bind ? "bind" : "unbind");
for (int i = 0; i < num; i++) {
if (is_bind) {
rc = _subdev_bind_hmac_sha1_cal(sub_dev_list[i] + i, nonce, timestamp, signature);
if (rc) {
Log_e("calculate signature fail");
return QCLOUD_ERR_FAILURE;
}
len += HAL_Snprintf(buf + len, buf_len - len,
"{\"product_id\":\"%s\",\"device_name\":\"%s\",\"signature\":\"%s\",\"random\":%d,"
"\"timestamp\":%lld,\"signmethod\":\"hmacsha1\",\"authtype\":\"psk\"},",
sub_dev_list[i]->product_id, sub_dev_list[i]->device_name, signature, nonce, timestamp);
} else {
len += HAL_Snprintf(buf + len, buf_len - len, "{\"product_id\":\"%s\",\"device_name\":\"%s\"},",
sub_dev_list[i]->product_id, sub_dev_list[i]->device_name);
}
}
len += HAL_Snprintf(buf + len - 1, buf_len - len + 1, "]}}"); // -1 remove ','
return _gateway_mqtt_publish(client, QOS0, buf, len);
}
/**
* @brief Publish subdevice describe message.
*
* @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)
{
#define GATEWAY_UP_DESCRIBE "{\"type\":\"describe_sub_devices\"}"
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
return _gateway_mqtt_publish(client, QOS0, GATEWAY_UP_DESCRIBE, sizeof(GATEWAY_UP_DESCRIBE) - 1);
}
/**
* @brief Publish search device reply message.
*
* @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)
{
char buf[64];
int len = HAL_Snprintf(buf, sizeof(buf), "{\"type\":\"search_devices\",\"payload\":{\"status\":%d,\"result\":%d}}",
is_on, result);
return _gateway_mqtt_publish(client, QOS0, buf, len);
}
/**
* @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)
{
char buf[64];
int len = HAL_Snprintf(buf, sizeof(buf), "{\"type\":\"unbind_all\",\"payload\":{\"result\":%d}}", result);
return _gateway_mqtt_publish(client, QOS0, buf, len);
}
#if 0 // TODO: no need now for useless
int IOT_Gateway_ChangeReply(void *client, char *buf, int buf_len, DeviceInfo *sub_dev_list, int num, int status)
{
char buf[64];
int len = HAL_Snprintf(buf, sizeof(buf), "{\"type\":\"change\",\"payload\":{\"status\":%d,\"devices\":[", status);
for (int i = 0; i < num; i++) {
len += HAL_Snprintf(buf + len, buf_len - len, "{\"product_id\":\"%s\",\"device_name\":\"%s\"},",
sub_dev_list[i].product_id, sub_dev_list[i].device_name);
}
len += HAL_Snprintf(buf + len - 1, buf_len - len + 1, "]}}"); // -1 remove ','
return _gateway_mqtt_publish(client, QOS0, buf, len);
}
#endif

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,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 test_data_template.cc
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-09-29
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-09-29 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include <iostream>
#include <string>
#include "gtest/gtest.h"
#include "mqtt_client_test.h"
#include "qcloud_iot_common.h"
#include "qcloud_iot_explorer.h"
namespace mqtt_client_unittest {
static void _bind_unbind_reply_callback(UtilsJsonValue sub_devices, bool is_bind, void *usr_data) { Log_d("recv msg"); }
static void _unbind_all_callback(void *usr_data) { Log_d("recv msg"); }
static void _online_offline_reply_callback(UtilsJsonValue sub_devices, bool is_online, void *usr_data) {
Log_d("recv msg");
}
static void _search_device_callback(bool is_on, void *usr_data) { Log_d("recv msg"); }
static void _describe_subdevices_reply_callback(UtilsJsonValue sub_devices, void *usr_data) { Log_d("recv msg"); }
static void _change_subdevices_status_callback(UtilsJsonValue sub_devices, bool is_bind, void *usr_data) {
Log_d("recv msg");
}
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2 * !!(condition)]))
/**
* @brief Test data template action.
*
*/
TEST_F(MqttClientTest, gateway) {
char buf[1024];
IotGatewayMessageCallback callback = {
.bind_unbind_reply_callback = _bind_unbind_reply_callback,
.unbind_all_callback = _unbind_all_callback,
.online_offline_reply_callback = _online_offline_reply_callback,
.search_device_callback = _search_device_callback,
.describe_subdevices_reply_callback = _describe_subdevices_reply_callback,
.change_subdevices_status_callback = _change_subdevices_status_callback,
};
DeviceInfo sub_dev0;
HAL_GetDevInfo(&sub_dev0);
const DeviceInfo *subdev[1] = {0};
subdev[0] = &sub_dev0;
ASSERT_EQ(IOT_Gateway_Init(client, callback, client), 0);
ASSERT_GE(IOT_Gateway_BindUnbind(client, buf, sizeof(buf), subdev, sizeof(subdev) / sizeof(DeviceInfo *), true), 0);
IOT_MQTT_Yield(client, 100);
ASSERT_GE(IOT_Gateway_Describe(client), 0);
IOT_MQTT_Yield(client, 100);
ASSERT_GE(IOT_Gateway_SubOnOffLine(client, buf, sizeof(buf), subdev, sizeof(subdev) / sizeof(DeviceInfo *), 1), 0);
IOT_MQTT_Yield(client, 100);
ASSERT_GE(IOT_Gateway_SubOnOffLine(client, buf, sizeof(buf), subdev, sizeof(subdev) / sizeof(DeviceInfo *), 0), 0);
IOT_MQTT_Yield(client, 100);
ASSERT_GE(IOT_Gateway_BindUnbind(client, buf, sizeof(buf), subdev, sizeof(subdev) / sizeof(DeviceInfo *), false), 0);
IOT_MQTT_Yield(client, 100);
ASSERT_GE(IOT_Gateway_SearchDeviceReply(client, true, 0), 0);
ASSERT_GE(IOT_Gateway_UnbindAllReply(client, 0), 0);
IOT_Gateway_Deinit(client);
}
} // namespace mqtt_client_unittest

View File

@@ -0,0 +1,2 @@
file(GLOB src_http_client ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(src_services ${src_services} ${src_http_client} PARENT_SCOPE)

View File

@@ -0,0 +1,629 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file http_client.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-22
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-22 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "qcloud_iot_http_client.h"
#include "network_interface.h"
/**
* @brief Http data.
*
*/
typedef struct {
const char *data;
int data_len;
} HTTPData;
/**
* @brief Only focus on response code and content,
* @ref https://datatracker.ietf.org/doc/html/rfc7231
*
*/
typedef struct {
int status_code; /**< https://en.wikipedia.org/wiki/List_of_HTTP_status_codes */
int content_length; /**< content length */
uint8_t *content_buf; /**< content buffer */
int content_buf_len; /**< content buffer length */
int recv_len; /**< already recv length */
int need_recv_len; /**< need recv length */
int is_chunked; /**< support chunked */
} IotHTTPResponseData;
/**
* @brief HTTP client.
*
*/
typedef struct {
IotHTTPResponseData response;
IotNetwork network;
} IotHTTPClient;
/**************************************************************************************
* network
**************************************************************************************/
/**
* @brief Connect with host.
*
* @param[in,out] client pointer to http client. @see IotHTTPClient
* @param[in] host http host
* @param[in] port http port, 80(http)/443(https)
* @param[in] ca_crt https ca
* @return 0 for success. others @see IotReturnCode
*/
static int _http_client_connect(IotHTTPClient *client, const char *host, const char *port, const char *ca_crt)
{
int rc = 0;
client->network.type = IOT_NETWORK_TYPE_TCP;
#if !defined(AUTH_WITH_NO_TLS) && defined(AUTH_MODE_CERT)
client->network.ssl_connect_params.ca_crt = ca_crt;
client->network.ssl_connect_params.ca_crt_len = strlen(ca_crt);
client->network.ssl_connect_params.cert_file = NULL;
client->network.ssl_connect_params.key_file = NULL;
client->network.ssl_connect_params.timeout_ms = HTTPS_READ_TIMEOUT_MS;
#endif
client->network.host = host;
client->network.port = port;
rc = qcloud_iot_network_init(&(client->network));
return rc ? rc : client->network.connect(&(client->network));
}
/**
* @brief Disconnect with host.
*
* @param[in,out] client pointer to http client. @see IotHTTPClient
*/
static void _http_client_disconnect(IotHTTPClient *client)
{
return client->network.disconnect(&(client->network));
}
/**
* @brief Send data to http server.
*
* @param[in,out] client pointer to http client. @see IotHTTPClient
* @param[in] buf buffer tot send
* @param[in] len buffer length
* @return 0 for success. others @see IotReturnCode
*/
static int _http_client_send(IotHTTPClient *client, char *buf, uint32_t len)
{
size_t written_len;
return client->network.write(&(client->network), (uint8_t *)buf, len, HTTP_WRITE_TIMEOUT_MS, &written_len);
}
/**
* @brief Recv data from http server.
*
* @param[in,out] client pointer to http client. @see IotHTTPClient
* @param[out] buf
* @param[in] len
* @param[in] timeout_ms
* @return >0 for read data length, others @see IotReturnCode
*/
static int _http_client_recv(IotHTTPClient *client, uint8_t *buf, uint32_t len, uint32_t timeout_ms)
{
size_t read_len;
int rc = client->network.read(&client->network, buf, len, timeout_ms, &read_len);
switch (rc) {
case QCLOUD_ERR_TCP_NOTHING_TO_READ:
return QCLOUD_RET_SUCCESS;
default:
return rc ? rc : read_len;
}
}
/**************************************************************************************
* request
**************************************************************************************/
/**
* @brief Parse url and get host & path.
*
* @param[in] url url str
* @param[out] host pointer to host str and length. @see HTTPData
* @param[out] path pointer to path str and length. @see HTTPData
* @return 0 for success. others @see IotReturnCode
*/
static int _http_client_parse_url(const char *url, HTTPData *host, HTTPData *path)
{
// start from: http:// or https://
char *host_ptr = (char *)strstr(url, "://");
if (!host_ptr) {
return QCLOUD_ERR_HTTP_PARSE;
}
host->data = host_ptr + 3;
path->data = strchr(host->data, '/');
if (!path->data) {
host->data_len = url + strlen(url) - host->data;
path->data_len = 1;
path->data = "/";
return QCLOUD_RET_SUCCESS;
}
host->data_len = path->data - host->data;
char *fragment_ptr = strchr(host_ptr, '#');
path->data_len = fragment_ptr ? fragment_ptr - path->data : strlen(path->data);
return QCLOUD_RET_SUCCESS;
}
/**
* @brief Send http request line.
*
* @param[in,out] client pointer to http client. @see IotHTTPClient
* @param[in] params params needed to send request to http server, @see IotHTTPRequestParams
* @return int
*/
static int _http_client_send_request_line(IotHTTPClient *client, const IotHTTPRequestParams *params)
{
int len, rc, buf_len;
char *buf;
HTTPData host, path;
/**
* @brief order @see IotHTTPMethod
*
*/
const char *method_str[] = {
[IOT_HTTP_METHOD_GET] = "GET", [IOT_HTTP_METHOD_POST] = "POST", [IOT_HTTP_METHOD_PUT] = "PUT",
[IOT_HTTP_METHOD_DELETE] = "DELETE", [IOT_HTTP_METHOD_HEAD] = "HEAD",
};
rc = _http_client_parse_url(params->url, &host, &path);
if (rc) {
Log_e("http parse url failed %d", rc);
return rc;
}
buf_len = path.data_len + host.data_len + 128;
buf = HAL_Malloc(buf_len);
if (!buf) {
Log_e("http malloc request line failed %d", rc);
return QCLOUD_ERR_MALLOC;
}
len = HAL_Snprintf(buf, buf_len, "%s %.*s HTTP/1.1\r\nHost:%.*s\r\n", method_str[params->method], path.data_len,
path.data, host.data_len, host.data);
rc = _http_client_send(client, buf, len);
HAL_Free(buf);
return rc;
}
/**
* @brief Send http request content(optional).
*
* @param[in,out] client pointer to http client. @see IotHTTPClient
* @param[in] params params needed to send request to http server, @see IotHTTPRequestParams
* @return int
*/
static int _http_client_send_request_content(IotHTTPClient *client, const IotHTTPRequestParams *params)
{
int rc = 0, len, buf_len;
char *buf;
buf_len = params->content_type ? 32 + strlen(params->content_type) : 32;
buf = HAL_Malloc(buf_len);
if (!buf) {
Log_e("http malloc request line failed %d", rc);
return QCLOUD_ERR_MALLOC;
}
len = HAL_Snprintf(buf, buf_len, "Content-Length:%d\r\n", params->content_length);
rc = _http_client_send(client, buf, len);
if (rc) {
goto exit;
}
if (params->content_type) {
len = HAL_Snprintf(buf, buf_len, "Content-Type:%s\r\n", params->content_type);
rc = _http_client_send(client, buf, len);
if (rc) {
goto exit;
}
}
rc = _http_client_send(client, "\r\n", 2);
if (rc) {
goto exit;
}
HAL_Free(buf);
return _http_client_send(client, (char *)params->content, params->content_length);
exit:
HAL_Free(buf);
Log_e("send request content failed %d", rc);
return rc;
}
/**
* @brief Send http request to http server.
*
* @param[in,out] client pointer to http client. @see IotHTTPClient
* @param[in] params @see IotHTTPRequestParams
* @return 0 for success. others @see IotReturnCode
*/
static int _http_client_send_request(IotHTTPClient *client, const IotHTTPRequestParams *params)
{
int rc;
// 1. send request line
rc = _http_client_send_request_line(client, params);
if (rc) {
Log_e("http send request line failed, rc=%d", rc);
return rc;
}
// 2. send request header
if (params->header) {
rc = _http_client_send(client, (char *)params->header, strlen(params->header));
if (rc) {
Log_e("write failed %d", rc);
return rc;
}
}
if (!params->content || !params->content_length) { // no payload body
return _http_client_send(client, "\r\n", 2);
}
// 3. send payload body
return _http_client_send_request_content(client, params);
}
/**************************************************************************************
* response
**************************************************************************************/
/**
* @brief Recv data chunked.
*
* @param[in,out] client pointer to http client. @see IotHTTPClient
* @param[in] timeout_ms timeout for recv
* @return length of content data recv stored in content buf
*/
static int _http_client_chunked_recv(IotHTTPClient *client, uint32_t timeout_ms)
{
uint8_t *buf = client->response.content_buf;
int buf_len = client->response.content_buf_len;
IotHTTPResponseData *response = &client->response;
char *crlf_pointer = NULL;
char *find_from = (char *)buf;
int rc = 0, read_size = 0;
int read_size_flag = 1;
QcloudIotTimer read_timer;
IOT_Timer_CountdownMs(&read_timer, timeout_ms);
do {
while (1) {
// find \r\n from find_from, start with beginning
crlf_pointer = strstr(find_from, "\r\n");
if (crlf_pointer) {
break;
}
if (IOT_Timer_Expired(&read_timer)) {
return 0;
}
// try to read more to found \r\n
rc = _http_client_recv(client, buf + response->recv_len, buf_len - response->recv_len, 100);
if (rc < 0) {
return response->recv_len;
}
response->recv_len += rc;
}
// read size before \r\n
if (read_size_flag) {
*crlf_pointer = '\0';
read_size = strtoul(find_from, NULL, 16);
response->recv_len -= crlf_pointer - find_from + 2;
if (!read_size) { // last chunk
response->need_recv_len = 0;
return response->recv_len;
}
memmove(find_from, crlf_pointer + 2, buf + response->recv_len - (uint8_t *)find_from);
read_size_flag = false;
continue;
}
// remove /r/n and set find from
response->recv_len -= 2;
memmove(crlf_pointer, crlf_pointer + 2, buf + response->recv_len - (uint8_t *)crlf_pointer);
find_from += read_size;
read_size_flag = true;
} while (!IOT_Timer_Expired(&read_timer));
return 0;
}
/**
* @brief Recv data according content length.
*
* @param[in,out] client pointer to http client. @see IotHTTPClient
* @param[in] offset offset of content buf
* @param[in] timeout_ms timeout for recv
* @return length of content data recv stored in content buf
*/
static int _http_client_content_length_recv(IotHTTPClient *client, int offset, uint32_t timeout_ms)
{
int rc, len = 0;
uint8_t *buf = client->response.content_buf;
int buf_len = client->response.content_buf_len;
IotHTTPResponseData *response = &client->response;
if (response->need_recv_len <= 0) {
// return offset for data already received
return offset;
}
// need recv length may much longger than buffer length
len = response->need_recv_len > buf_len - offset ? buf_len - offset : response->need_recv_len;
rc = _http_client_recv(client, buf + offset, len, timeout_ms);
if (rc <= 0) {
return offset ? offset : rc;
}
response->recv_len += rc;
response->need_recv_len -= rc;
return rc + offset;
}
/**
* @brief Recv content data.
*
* @param[in,out] client pointer to http client. @see IotHTTPClient
* @param[in] offset offset of content buf
* @param[in] timeout_ms timeout for recv
* @return length of content data recv stored in content buf
*/
static int _http_client_recv_content(IotHTTPClient *client, int offset, uint32_t timeout_ms)
{
return client->response.is_chunked ? _http_client_chunked_recv(client, timeout_ms)
: _http_client_content_length_recv(client, offset, timeout_ms);
}
/**
* @brief Recv response from http server.
*
* @param[in,out] client pointer to http client. @see IotHTTPClient
* @param[in] timeout_ms timeout for recv
* @return 0 for success. others @see IotReturnCode
*/
static int _http_client_recv_response(IotHTTPClient *client, uint32_t timeout_ms)
{
QcloudIotTimer timer;
char *content_length, *body_end;
int rc, len = 0;
char *buf = (char *)client->response.content_buf;
int buf_len = client->response.content_buf_len;
IotHTTPResponseData *response = &client->response;
memset(buf, 0, buf_len);
IOT_Timer_CountdownMs(&timer, timeout_ms);
// 1. found body end
while (NULL == (body_end = strstr(buf, "\r\n\r\n"))) {
// timeout
if (IOT_Timer_Expired(&timer)) {
return QCLOUD_ERR_HTTP_TIMEOUT;
}
// timeout 100ms for header less than buff len
rc = _http_client_recv(client, (uint8_t *)buf + len, buf_len - len - 1, 100);
if (rc < 0) {
if (rc == QCLOUD_ERR_TCP_READ_TIMEOUT || rc == QCLOUD_ERR_SSL_READ_TIMEOUT) {
continue;
}
Log_e("read failed, rc %d", rc);
return rc;
}
len += rc;
}
body_end += 4; // \r\n\r\n
len = len - (body_end - buf); // body length
// 2. get response code
response->status_code = atoi(buf + 9);
switch (response->status_code) {
case 403:
return QCLOUD_ERR_HTTP_AUTH;
case 404:
return QCLOUD_ERR_HTTP_NOT_FOUND;
default:
if (response->status_code < 200 || response->status_code >= 400) {
Log_w("HTTP status code %d", response->status_code);
return QCLOUD_ERR_HTTP;
}
break;
}
// 3. parse header
// content length
content_length = strstr(buf, "Content-Length");
if (content_length) {
response->is_chunked = 0;
response->content_length = atoi(content_length + strlen("Content-Length: "));
response->need_recv_len = response->content_length - len;
goto recv_content;
}
// chunked
if (strstr(buf, "Transfer-Encoding: chunked")) {
response->is_chunked = 1;
response->need_recv_len = 1; // means always need recv
goto recv_content;
}
Log_e("Could not parse header");
response->content_length = -1;
return QCLOUD_ERR_HTTP;
// 4. remove data and receive body data
recv_content:
memmove(buf, body_end, len);
response->recv_len = len;
return _http_client_recv_content(client, len, IOT_Timer_Remain(&timer));
}
/**************************************************************************************
* API
**************************************************************************************/
/**
* @brief Malloc http client.
*
* @return pointer to http client
*/
void *IOT_HTTP_Init(void)
{
return HAL_Malloc(sizeof(IotHTTPClient));
}
/**
* @brief Free http client.
*
*/
void IOT_HTTP_Deinit(void *client)
{
HAL_Free(client);
}
/**
* @brief Connect http server.
*
* @param[in,out] client pointer to http client
* @param[in] params params needed to connect http server, @see IotHTTPConnectParams
* @return 0 for success. others @see IotReturnCode
*/
int IOT_HTTP_Connect(void *client, IotHTTPConnectParams *params)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
IotHTTPClient *http_client = (IotHTTPClient *)client;
memset(http_client, 0, sizeof(IotHTTPClient));
int rc;
// parse url
HTTPData host, path;
rc = _http_client_parse_url(params->url, &host, &path);
if (rc) {
Log_e("http parse url failed %d", rc);
return rc;
}
// copy host
char *host_str = HAL_Malloc(host.data_len + 1);
if (!host_str) {
return QCLOUD_ERR_MALLOC;
}
strncpy(host_str, host.data, host.data_len);
host_str[host.data_len] = '\0';
// http connect
rc = _http_client_connect(http_client, host_str, params->port, params->ca_crt);
HAL_Free(host_str);
return rc;
}
/**
* @brief Request http server.
*
* @param[in,out] client pointer to http client
* @param[in] params params needed to send request to http server, @see IotHTTPRequestParams
* @return 0 for success. others @see IotReturnCode
*/
int IOT_HTTP_Request(void *client, IotHTTPRequestParams *params)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
return _http_client_send_request((IotHTTPClient *)client, params);
}
/**
* @brief Send data to http server.
*
* @param[in,out] client pointer to http client
* @param[in] data data to send
* @param[out] data_len data len
* @return 0 for success. others @see IotReturnCode
*/
int IOT_HTTP_Send(void *client, uint8_t *data, int data_len)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
return _http_client_send((IotHTTPClient *)client, (char *)data, data_len);
}
/**
* @brief Recv data from http server.
*
* @param[in,out] client pointer to http client
* @param[out] buf buffer to store recv data
* @param[in] buf_len buffer len
* @param timeout_ms timeout for recv
* @return >= 0 for recv data len. others @see IotReturnCode
*/
int IOT_HTTP_Recv(void *client, uint8_t *buf, int buf_len, uint32_t timeout_ms)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
IotHTTPClient *http_client = (IotHTTPClient *)client;
http_client->response.content_buf = buf;
http_client->response.content_buf_len = buf_len;
return http_client->response.need_recv_len ? _http_client_recv_content(http_client, 0, timeout_ms)
: _http_client_recv_response(http_client, timeout_ms);
}
/**
* @brief Check is recv finished.
*
* @param[in,out] client pointer to http client
* @return true for finished.
*/
int IOT_HTTP_IsRecvFinished(void *client)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
IotHTTPClient *http_client = (IotHTTPClient *)client;
return http_client->response.need_recv_len == 0;
}
/**
* @brief Disconnect http server.
*
* @param[in,out] client pointer to http client
*/
void IOT_HTTP_Disconnect(void *client)
{
POINTER_SANITY_CHECK_RTN(client);
_http_client_disconnect((IotHTTPClient *)client);
}

View File

@@ -0,0 +1,2 @@
file(GLOB src_http_signed ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(src_services ${src_services} ${src_http_signed} PARENT_SCOPE)

View File

@@ -0,0 +1,279 @@
/**
* @file http_signed.c
* @author {hubert} ({hubertxxu@tencent.com})
* @brief
* @version 1.0
* @date 2022-01-21
*
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @par Change Log:
* <table>
* Date Version Author Description
* 2022-01-21 1.0 hubertxxu first commit
* </table>
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "qcloud_iot_http_signed.h"
#include "utils_hmac.h"
#include "utils_base64.h"
/**
* @brief http signed request header.
*
*/
#define HTTP_SIGNED_REQUEST_HEADER_LEN (1024)
/**
* @brief http signed request header signed length
*
*/
#define HTTP_SIGNED_STRING_BUFFER_LEN (256)
/**
* @brief http signed request url length
*
*/
#define HTTP_SIGNED_URL_LEN (128)
#define QCLOUD_SHA256_RESULT_LEN (32)
#define QCLOUD_SHA1_RESULT_LEN (20)
typedef struct {
HttpSignedParams params;
void *http_client;
IotHTTPRequestParams http_request;
char *sign_string;
char *url;
} HTTPSignedHandle;
/**
* @brief get rand
*
* @param[in, out] seed
* @return int
*/
static int _get_http_header_nonce(void)
{
srand(IOT_Timer_CurrentSec());
return rand();
}
/**
* @brief Connect http server.
*
* @param[in,out] handle pointer to http signed handle, @see HTTPSignedHandle
* @return 0 for success. others @see IotReturnCode
*/
static int _http_signed_connect(HTTPSignedHandle *handle)
{
IotHTTPConnectParams connect_params = {
.url = handle->url,
.port = "80",
.ca_crt = NULL, // TODO: support cert
};
return IOT_HTTP_Connect(handle->http_client, &connect_params);
}
/**
* @brief http signed header construct
*
* @param[in,out] handle pointer to http signed handle, @see HTTPSignedHandle
* @param request_body_buf
* @param request_body_buf_len
* @return 0 if success
*/
static void _http_signed_upload_header_construct(HTTPSignedHandle *handle, const char *request_body_buf,
int request_body_buf_len)
{
/**
* @brief http signed header format
*
*/
#define QCLOUD_HTTP_HEADER_FORMAT \
"Accept: %s*/*\r\n" \
"X-TC-Algorithm: %s\r\n" \
"X-TC-Timestamp: %d\r\n" \
"X-TC-Nonce: %d\r\n" \
"X-TC-Signature: %s\r\n"
#define QCLOUD_SUPPORT_HMACSHA1 "hmacsha1"
#define QCLOUD_SUPPORT_RSASHA256 "rsa-sha256"
#define QCLOUD_SUPPORT_ACCEPT_HEADER "application/json;"
char sign[QCLOUD_SHA1_RESULT_LEN] = {0};
char signout[QCLOUD_SHA1_RESULT_LEN * 2] = {0};
char request_buf_sha1[QCLOUD_SHA1_RESULT_LEN * 2 + 1] = {0};
size_t olen = 0;
int nonce = _get_http_header_nonce();
uint32_t timestamp = IOT_Timer_CurrentSec();
memset(handle->sign_string, 0, HTTP_SIGNED_STRING_BUFFER_LEN);
/* cal hmac sha1 */
utils_sha1_hex((const uint8_t *)request_body_buf, request_body_buf_len, (uint8_t *)request_buf_sha1);
/* create sign string */
HAL_Snprintf(handle->sign_string, HTTP_SIGNED_STRING_BUFFER_LEN, "%s\n%s\n%s\n%s\n%s\n%d\n%d\n%s", "POST",
handle->params.host, handle->params.uri, "", QCLOUD_SUPPORT_HMACSHA1, timestamp, nonce,
request_buf_sha1);
utils_hmac_sha1(handle->sign_string, strlen(handle->sign_string), (uint8_t *)handle->params.secretkey,
strlen(handle->params.secretkey), sign);
/* base64 encode */
utils_base64encode((uint8_t *)signout, QCLOUD_SHA1_RESULT_LEN * 2, &olen, (const uint8_t *)sign,
QCLOUD_SHA1_RESULT_LEN);
HAL_Snprintf(handle->http_request.header, HTTP_SIGNED_REQUEST_HEADER_LEN, QCLOUD_HTTP_HEADER_FORMAT,
QCLOUD_SUPPORT_ACCEPT_HEADER, QCLOUD_SUPPORT_HMACSHA1, timestamp, nonce, signout);
}
/**
* @brief http signed init.
*
* @param params @see HttpSignedParams
* @return pointer to http signed @see HTTPSignedHandle
*/
void *_http_signed_init(HttpSignedParams *params)
{
HTTPSignedHandle *handle = HAL_Malloc(sizeof(HTTPSignedHandle));
if (!handle) {
goto exit;
}
handle->http_client = IOT_HTTP_Init();
if (!handle->http_client) {
goto exit;
}
handle->http_request.header = HAL_Malloc(HTTP_SIGNED_REQUEST_HEADER_LEN);
if (!handle->http_request.header) {
goto exit;
}
handle->sign_string = HAL_Malloc(HTTP_SIGNED_STRING_BUFFER_LEN);
if (!handle->sign_string) {
goto exit;
}
handle->url = HAL_Malloc(HTTP_SIGNED_URL_LEN);
if (!handle->url) {
goto exit;
}
handle->params = *params;
memset(handle->url, 0, HTTP_SIGNED_URL_LEN);
HAL_Snprintf(handle->url, HTTP_SIGNED_URL_LEN, "%s://%s%s", "http", handle->params.host, handle->params.uri);
return handle;
exit:
if (handle) {
HAL_Free(handle->http_request.header);
HAL_Free(handle->sign_string);
HAL_Free(handle->url);
IOT_HTTP_Deinit(handle->http_client);
HAL_Free(handle);
}
return NULL;
}
/**
* @brief send to server
*
* @param[in] handle pointer to http signed @see HTTPSignedHandle
* @param[in] upload_buf need send data
* @param[in] upload_len send length
* @return int 0 for success. others @see IotReturnCode
*/
int _http_signed_upload(HTTPSignedHandle *handle, const char *upload_buf, size_t upload_len)
{
int rc = 0;
rc = _http_signed_connect(handle);
if (rc) {
return rc;
}
_http_signed_upload_header_construct(handle, upload_buf, upload_len);
handle->http_request.url = handle->url;
handle->http_request.method = IOT_HTTP_METHOD_POST;
handle->http_request.content_type = "application/json;charset=utf-8";
handle->http_request.content = (char *)upload_buf;
handle->http_request.content_length = upload_len;
rc = IOT_HTTP_Request(handle->http_client, &handle->http_request);
return rc;
}
/**
* @brief http signed deinit
*
* @param[in] handle pointer to http signed @see HTTPSignedHandle
*/
static void _http_signed_deinit(HTTPSignedHandle *signed_handle)
{
POINTER_SANITY_CHECK_RTN(signed_handle);
IOT_HTTP_Disconnect(signed_handle->http_client);
IOT_HTTP_Deinit(signed_handle->http_client);
HAL_Free(signed_handle->http_request.header);
HAL_Free(signed_handle->sign_string);
HAL_Free(signed_handle->url);
HAL_Free(signed_handle);
}
/**
* @brief post message and recv response
*
* @param params @see HttpSignedParams
* @param request_buf request buffer
* @param request_buf_len request buffer length
* @param response_buf response buffer if need recv
* @param response_buf_len response buffer length if need recv
* @return int if need recv return recv length else return 0 is success
*/
int IOT_HTTP_SignedRequest(HttpSignedParams *params, const char *request_buf, size_t request_buf_len,
uint8_t *response_buf, int response_buf_len)
{
int rc = 0;
POINTER_SANITY_CHECK(params, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(request_buf, QCLOUD_ERR_INVAL);
if (params->need_recv) {
POINTER_SANITY_CHECK(response_buf, QCLOUD_ERR_INVAL);
}
HTTPSignedHandle *handle = _http_signed_init(params);
if (!handle) {
Log_e("http signed init error.");
goto exit;
}
rc = _http_signed_upload(handle, request_buf, request_buf_len);
if (rc) {
Log_e("http signed upload error.");
goto exit;
}
if (params->need_recv) {
rc = IOT_HTTP_Recv(handle->http_client, response_buf, response_buf_len, params->recv_timeout_ms);
}
exit:
_http_signed_deinit(handle);
return rc;
}

View File

@@ -0,0 +1,16 @@
file(GLOB src_log_upload ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(inc_log_upload ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
set(src_services ${src_services} ${src_log_upload} PARENT_SCOPE)
set(inc_services ${inc_services} ${inc_log_upload} PARENT_SCOPE)
if( ${CONFIG_COMPILE_SAMPLE} STREQUAL "ON")
file(GLOB src_log_upload_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/log_upload_sample.c)
add_executable(log_upload_sample ${src_log_upload_sample})
target_link_libraries(log_upload_sample ${libsdk})
endif()
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,213 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file mqtt_client.h
* @brief mqtt client internel api
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-31
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-31 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_LOG_UPLOAD_INC_LOG_UPLOAD_H_
#define IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_LOG_UPLOAD_INC_LOG_UPLOAD_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "qcloud_iot_log_upload.h"
#include "utils_list.h"
#include "utils_base64.h"
#include "utils_hmac.h"
/**
* @brief To check log http server return msg or not.
*
*/
#define LOG_CHECK_HTTP_RET_CODE
/**
* @brief Enable log upload debug or not.
*
*/
// #define LOG_UPLOAD_DEBUG
/**
* @brief Http recv length.
*
*/
#define HTTP_RET_JSON_LENGTH 256
/**
* @brief Wait http recv time out.
*
*/
#define HTTP_WAIT_RET_TIMEOUT_MS 5000
/**
* @brief Do immediate log update if buffer is lower than this threshold
*
*/
#define LOG_LOW_BUFFER_THRESHOLD (LOG_UPLOAD_BUFFER_SIZE / 4)
/**
* @brief Sign key length.
*
*/
#define SIGN_KEY_SIZE (24)
#ifdef LOG_UPLOAD_DEBUG
#define UPLOAD_DBG(fmt, ...) HAL_Printf(">>LOG-DBG>>%s(%d): " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define UPLOAD_DBG(...)
#endif
#define UPLOAD_ERR(fmt, ...) HAL_Printf(">>LOG-ERR>>%s(%d): " fmt "\n", __FUNCTION__, __LINE__, ##__VA_ARGS__)
/**
* @brief Log upload buffer init.
*
* @param[in] init_params @see LogUploadInitParams
* @return void* if success return @see LogUploadBuffer
*/
void *log_upload_buffer_init(const LogUploadInitParams *init_params);
/**
* @brief Deinit log buffer handle.
*
* @param[in] upload_buffer_handle @see LogUploadBuffer
*/
void log_upload_buffer_deinit(void *upload_buffer_handle);
/**
* @brief Clear log upload buffer.
*
*/
void log_upload_buffer_clear(void);
/**
* @brief Get the log buffer write index object.
*
* @param[in] log_upload_buffer_handle @see LogUploadBuffer
* @return write index
*/
uint32_t log_upload_buffer_get_write_index(void *log_upload_buffer_handle);
/**
* @brief Get the log buffer head len object.
*
* @param[in] log_upload_buffer_handle @see LogUploadBuffer
* @return head length
*/
uint32_t log_upload_buffer_get_head_len(void *log_upload_buffer_handle);
/**
* @brief Get the log buffer size object.
*
* @return log buffer size
*/
uint32_t log_upload_buffer_get_size(void);
/**
* @brief Check log buffer is empty or not.
*
* @param[in] log_upload_buffer_handle @see LogUploadBuffer
* @return true empty
* @return false not empty
*/
bool log_upload_buffer_is_empty(void *log_upload_buffer_handle);
/**
* @brief When a new log appears during the reporting period, move the log to the specified position in the buffer.
*
* @param[in] log_upload_buffer_handle @see LogUploadBuffer
* @param[in] need_remove_size
*/
void log_upload_buffer_remove_log(void *log_upload_buffer_handle, uint32_t need_remove_size);
/**
* @brief Copy log buffer head.
*
* @param[out] dst Destination address
* @param[in] log_upload_buffer_handle @see LogUploadBuffer
*/
void log_upload_buffer_copy_header(char *dst, void *log_upload_buffer_handle);
/**
* @brief Get the log buffer object.
*
* @param[in] log_upload_buffer_handle @see LogUploadBuffer
* @return log buffer
*/
char *log_upload_buffer_get(void *log_upload_buffer_handle);
/**
* @brief Push data to log buffer.
*
* @param[in] log_content need push data
* @return if 0 success else QCLOUD_ERR_FAILURE
*/
int log_upload_buffer_append(const char *log_content);
/**
* @brief Post data to tencent cloud over http.
*
* @param[in] post_buf need post data buffer
* @param[in] post_size need post data buffer length
* @return @see IotReturnCode
*/
int post_one_http_to_server(void *log_upload_buffer_handle, char *post_buf, size_t post_size);
/**
* @brief Set log upload in comm err state.
*
* @param state true or false
*/
void log_upload_set_upload_log_in_comm_err(bool state);
/**
* @brief Change log upload level.
*
* @param[in] level @see LogLevel
*/
void log_upload_set_log_upload_level(LogLevel level);
/**
* @brief Get log upload level.
*
* @return @see LogLevel
*/
LogLevel log_upload_get_log_upload_level(void);
/**
* @brief Init log upload module.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int log_mqtt_init(void *client);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_LOG_UPLOAD_INC_LOG_UPLOAD_H_

View File

@@ -0,0 +1,211 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file broadcast_sample.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-18
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "qcloud_iot_common.h"
#include "utils_log.h"
/**
* @brief MQTT event callback, @see MQTTEventHandleFun
*
* @param[in] client pointer to mqtt client
* @param[in] handle_context context
* @param[in] msg msg
*/
static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
{
MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
uintptr_t packet_id = (uintptr_t)msg->msg;
switch (msg->event_type) {
case MQTT_EVENT_UNDEF:
Log_i("undefined event occur.");
break;
case MQTT_EVENT_DISCONNECT:
Log_i("MQTT disconnect.");
break;
case MQTT_EVENT_RECONNECT:
Log_i("MQTT reconnect.");
break;
case MQTT_EVENT_PUBLISH_RECEIVED:
Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
break;
case MQTT_EVENT_SUBSCRIBE_SUCCESS:
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_SUBSCRIBE_NACK:
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_NACK:
Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_SUCCESS:
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_TIMEOUT:
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_NACK:
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
break;
default:
Log_i("Should NOT arrive here.");
break;
}
}
/**
* @brief Setup MQTT construct parameters.
*
* @param[in,out] initParams @see MQTTInitParams
* @param[in] device_info @see DeviceInfo
*/
static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
{
init_params->device_info = device_info;
init_params->event_handle.h_fp = _mqtt_event_handler;
}
static void _setup_log_upload_init_params(LogUploadInitParams *init_params, DeviceInfo *device_info)
{
init_params->device_name = device_info->device_name;
init_params->product_id = device_info->product_id;
init_params->sign_key = device_info->device_secret;
init_params->log_buffer_size = LOG_UPLOAD_BUFFER_SIZE;
init_params->save_log_filename = "./tmp/upload-fail-save.log";
init_params->read_func = HAL_File_Read;
init_params->save_func = HAL_File_Write;
init_params->del_func = HAL_File_Del;
init_params->get_size_func = HAL_File_GetSize;
}
// ----------------------------------------------------------------------------
// Main
// ----------------------------------------------------------------------------
static int sg_main_exit = 0;
#ifdef __linux__
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
static void _main_exit(int sig)
{
Log_e("demo exit by signal:%d\n", sig);
sg_main_exit = 1;
}
#endif
int main(int argc, char **argv)
{
#ifdef __linux__
signal(SIGINT, _main_exit);
#endif
int rc;
int loop_cnt = 5;
// init log level
LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
func.log_upload = IOT_Log_Upload_AppendToUploadBuffer;
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
DeviceInfo device_info;
rc = HAL_GetDevInfo((void *)&device_info);
if (rc) {
Log_e("get device info failed: %d", rc);
return rc;
}
// init connection
MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
_setup_connect_init_params(&init_params, &device_info);
// init log upload
LogUploadInitParams log_upload_init_params = DEFAULT_LOG_UPLOAD_INIT_PARAMS;
_setup_log_upload_init_params(&log_upload_init_params, &device_info);
IOT_Log_Upload_InitPre(&log_upload_init_params);
// create MQTT client and connect with server
void *client = IOT_MQTT_Construct(&init_params);
if (client) {
Log_i("Cloud Device Construct Success");
rc = IOT_Log_Upload_Init(client);
if (rc) {
Log_e("Log upload init failed: %d", rc);
}
IOT_Log_Upload(true);
} else {
Log_e("MQTT Construct failed!");
return QCLOUD_ERR_FAILURE;
}
do {
Log_d("log upload test debug %d...", loop_cnt);
Log_i("log upload test info %d...", loop_cnt);
Log_w("log upload test waring %d...", loop_cnt);
Log_e("log upload test error %d...", loop_cnt);
rc = IOT_MQTT_Yield(client, 200);
IOT_Log_Upload(rc ? true : false);
} while (loop_cnt--);
// If you have to report some urgent messages, you can call this function
IOT_Log_Upload(true);
rc = IOT_MQTT_Destroy(&client);
IOT_Log_Upload_Deinit();
utils_log_deinit();
return rc;
}

View File

@@ -0,0 +1,735 @@
/**
* @file log_buffer.c
* @author {hubert} ({hubertxxu@tencent.com})
* @brief
* @version 1.0
* @date 2022-01-23
*
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @par Change Log:
* <table>
* Date Version Author Description
* 2022-01-23 1.0 hubertxxu first commit
* </table>
*/
#include "log_upload.h"
#include "utils_hmac.h"
typedef struct {
void *lock_buf;
char *log_buffer;
const char *host;
uint32_t log_buffer_size;
uint32_t log_buffer_head_len;
uint32_t write_index;
char sign_key[SIGN_KEY_SIZE + 1];
} LogUploadBuffer;
static LogUploadBuffer *sg_log_upload_buffer_handle = NULL;
// ----------------------------------------------------------------------------
// Common
// ----------------------------------------------------------------------------
/**
* @brief Reset log upload buffer.
*
* @param[in,out] log_upload_buffer_handle handle of log upload buffer
*/
static void _reset_log_buffer(LogUploadBuffer *log_upload_buffer_handle)
{
log_upload_buffer_handle->write_index = log_upload_buffer_handle->log_buffer_head_len;
memset(log_upload_buffer_handle->log_buffer + log_upload_buffer_handle->log_buffer_head_len, 0,
log_upload_buffer_handle->log_buffer_size - log_upload_buffer_handle->log_buffer_head_len);
}
/**
* @brief Init log buffer common logic.
*
* @param[in] init_params @see LogUploadInitParams
* @return pointer to log buffer
*/
static LogUploadBuffer *_log_upload_buffer_init_pre(const LogUploadInitParams *init_params)
{
LogUploadBuffer *handle = (LogUploadBuffer *)HAL_Malloc(sizeof(LogUploadBuffer));
if (!handle) {
UPLOAD_ERR("log upload buffer handle malloc error.");
goto err_exit;
}
memset(handle, 0, sizeof(LogUploadBuffer));
handle->lock_buf = HAL_MutexCreate();
if (!handle->lock_buf) {
UPLOAD_ERR("mutex create failed");
goto err_exit;
}
handle->log_buffer_size = init_params->log_buffer_size;
handle->log_buffer = HAL_Malloc(handle->log_buffer_size);
if (!handle->log_buffer) {
UPLOAD_ERR("malloc log buffer failed");
goto err_exit;
}
memset(handle->log_buffer, 0, handle->log_buffer_size);
/*init sign key*/
int key_len = strlen(init_params->sign_key);
if (!key_len) {
UPLOAD_ERR("invalid sign key length");
goto err_exit;
}
memcpy(handle->sign_key, init_params->sign_key, key_len > SIGN_KEY_SIZE ? SIGN_KEY_SIZE : key_len);
sg_log_upload_buffer_handle = handle;
return handle;
err_exit:
if (handle) {
HAL_MutexDestroy(handle->lock_buf);
HAL_Free(handle);
}
return NULL;
}
/**
* @brief Deinit log buffer handle.
*
* @param[in] upload_buffer_handle @see LogUploadBuffer
*/
void log_upload_buffer_deinit(void *upload_buffer_handle)
{
LogUploadBuffer *handle = (LogUploadBuffer *)upload_buffer_handle;
if (!handle) {
return;
}
HAL_MutexLock(handle->lock_buf);
_reset_log_buffer(handle);
HAL_Free(handle->log_buffer);
handle->log_buffer = NULL;
HAL_MutexUnlock(handle->lock_buf);
HAL_MutexDestroy(handle->lock_buf);
handle->lock_buf = NULL;
HAL_Free(handle);
sg_log_upload_buffer_handle = NULL;
}
/**
* @brief Get the log buffer write index object.
*
* @param[in] log_upload_buffer_handle @see LogUploadBuffer
* @return write index
*/
uint32_t log_upload_buffer_get_write_index(void *log_upload_buffer_handle)
{
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
HAL_MutexLock(handle->lock_buf);
uint32_t write_index = handle->write_index;
HAL_MutexUnlock(handle->lock_buf);
return write_index;
}
/**
* @brief Get the log buffer object.
*
* @param[in] log_upload_buffer_handle @see LogUploadBuffer
* @return log buffer
*/
char *log_upload_buffer_get(void *log_upload_buffer_handle)
{
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
return handle->log_buffer;
}
/**
* @brief Get the log buffer head len object.
*
* @param[in] log_upload_buffer_handle @see LogUploadBuffer
* @return head length
*/
uint32_t log_upload_buffer_get_head_len(void *log_upload_buffer_handle)
{
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
return handle->log_buffer_head_len;
}
/**
* @brief Check log buffer is empty or not.
*
* @param[in] log_upload_buffer_handle @see LogUploadBuffer
* @return true empty
* @return false not empty
*/
bool log_upload_buffer_is_empty(void *log_upload_buffer_handle)
{
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
return handle->write_index == handle->log_buffer_head_len;
}
/**
* @brief When a new log appears during the reporting period, move the log to the specified position in the buffer.
*
* @param[in] log_upload_buffer_handle @see LogUploadBuffer
* @param[in] need_remove_size
*/
void log_upload_buffer_remove_log(void *log_upload_buffer_handle, uint32_t need_remove_size)
{
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
if (need_remove_size > handle->write_index || need_remove_size > handle->log_buffer_size) {
return;
}
HAL_MutexLock(handle->lock_buf);
memmove(handle->log_buffer + handle->log_buffer_head_len, handle->log_buffer + need_remove_size,
handle->write_index - need_remove_size);
handle->write_index = handle->write_index - need_remove_size + handle->log_buffer_head_len;
memset(handle->log_buffer + handle->write_index, 0, handle->log_buffer_size - handle->write_index);
HAL_MutexUnlock(handle->lock_buf);
}
/**
* @brief Copy log buffer head.
*
* @param[out] dst Destination address
* @param[in] log_upload_buffer_handle @see LogUploadBuffer
*/
void log_upload_buffer_copy_header(char *dst, void *log_upload_buffer_handle)
{
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
memcpy(dst, handle->log_buffer, handle->log_buffer_head_len);
}
/**
* @brief Clear log upload buffer.
*
*/
void log_upload_buffer_clear(void)
{
if (!sg_log_upload_buffer_handle) {
UPLOAD_ERR("log upload buffer not init.");
return;
}
HAL_MutexLock(sg_log_upload_buffer_handle->lock_buf);
_reset_log_buffer(sg_log_upload_buffer_handle);
HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
}
/**
* @brief Get the log buffer size object.
*
* @return log buffer size
*/
uint32_t log_upload_buffer_get_size(void)
{
if (!sg_log_upload_buffer_handle) {
UPLOAD_ERR("log upload buffer not init.");
return 0;
}
return sg_log_upload_buffer_handle->log_buffer_size;
}
// ----------------------------------------------------------------------------
// New log fromat by json
// ----------------------------------------------------------------------------
#ifdef LOG_UPLOAD_TYPE_JSON
/**
* @brief Log upload buffer init.
*
* @param[in] init_params @see LogUploadInitParams
* @return void* if success return @see LogUploadBuffer
*/
void *log_upload_buffer_init(const LogUploadInitParams *init_params)
{
LogUploadBuffer *handle = _log_upload_buffer_init_pre(init_params);
if (!handle) {
return NULL;
}
handle->log_buffer_head_len = HAL_Snprintf(handle->log_buffer, handle->log_buffer_size,
"{\"DeviceName\":\"%s\",\"ProductId\":\"%s\",\"Message\":[",
init_params->device_name, init_params->product_id);
handle->write_index = handle->log_buffer_head_len;
handle->host = init_params->host;
_reset_log_buffer(handle);
return handle;
}
#ifdef LOG_CHECK_HTTP_RET_CODE
/**
* @brief Get http respose result.
*
* @param[in] json http response buffer
* @param[in] json_len http response buffer length
* @return true log report success
* @return false log report error
*/
static bool _get_json_ret_code(char *json, size_t json_len)
{
int rc = 0;
UtilsJsonValue resposse;
// get response
rc = utils_json_value_get("Response.Error", strlen("Response.Error"), json, json_len, &resposse);
if (rc) {
return true;
}
return false;
}
#endif
/**
* @brief When upload, append endchar in the upload buffer ending.
*
* @param[in,out] log_buf upload buffer
* @param[in] log_size upload buffer index
*/
static void _append_endchar_to_upload_buffer(char *log_buf, size_t log_size)
{
log_buf[log_size - 2] = ']';
log_buf[log_size - 1] = '}';
}
/**
* @brief When upload, remove endchar in the upload buffer ending.
*
* @param[in,out] log_buf upload buffer
* @param[in] log_size upload buffer index
*/
static void _remove_endchar_from_upload_buffer(char *log_buf, size_t log_size)
{
log_buf[log_size - 2] = ',';
log_buf[log_size - 1] = ' ';
}
/**
* @brief Post data to tencent cloud over http.
*
* @param[in] post_buf need post data buffer
* @param[in] post_size need post data buffer length
* @return @see IotReturnCode
*/
int post_one_http_to_server(void *log_upload_buffer_handle, char *post_buf, size_t post_size)
{
int rc = 0;
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
if (!post_size) {
return 0;
}
_append_endchar_to_upload_buffer(post_buf, post_size);
HttpSignedParams params = {
.host = handle->host ? handle->host : LOG_UPLOAD_SERVER_URL,
.uri = LOG_UPLOAD_URI_PATH,
.secretkey = handle->sign_key,
#ifdef LOG_CHECK_HTTP_RET_CODE
.need_recv = true,
.recv_timeout_ms = HTTP_WAIT_RET_TIMEOUT_MS,
#else
.need_recv = false,
.recv_timeout_ms = 0,
#endif
};
#ifdef LOG_CHECK_HTTP_RET_CODE
char buf[HTTP_RET_JSON_LENGTH] = {0};
rc = IOT_HTTP_SignedRequest(&params, post_buf, post_size, (uint8_t *)buf, HTTP_RET_JSON_LENGTH);
if (rc < 0) {
UPLOAD_ERR("IOT_HTTP_SignedRequest failed, rc = %d", rc);
goto exit;
}
if (rc == 0) {
UPLOAD_ERR("HTTP Recv length = %d", rc);
rc = QCLOUD_ERR_HTTP;
goto exit;
}
buf[rc - 1] = '\0'; // json_parse relies on a string
if (strlen(buf) > 0 && _get_json_ret_code(buf, rc)) {
UPLOAD_DBG("Log server return SUCCESS: %s", buf);
rc = QCLOUD_RET_SUCCESS;
} else {
UPLOAD_ERR("Log server return FAIL: %s", buf);
rc = QCLOUD_ERR_HTTP;
}
#else
rc = IOT_HTTP_SignedRequest(&params, post_buf, post_size, NULL, 0);
#endif
exit:
_remove_endchar_from_upload_buffer(post_buf, post_size);
UPLOAD_DBG("%ld data have be post to server. rc : %d", post_size, rc);
return rc;
}
/**
* @brief Push data to log buffer.
*
* @param[in] log_content need push data
* @return if 0 success else QCLOUD_ERR_FAILURE
*/
int log_upload_buffer_append(const char *log_content)
{
uint16_t log_size = strlen(log_content);
if (HAL_MutexTryLock(sg_log_upload_buffer_handle->lock_buf) != 0) {
UPLOAD_ERR("trylock buffer failed!");
return QCLOUD_ERR_FAILURE;
}
if ((sg_log_upload_buffer_handle->write_index + log_size + 5) > sg_log_upload_buffer_handle->log_buffer_size) {
HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
UPLOAD_ERR("log upload buffer is not enough!");
return QCLOUD_ERR_FAILURE;
}
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\"';
int index = 0;
while (index < (log_size - 2)) {
// replace may overflow buffer size
if ((sg_log_upload_buffer_handle->write_index + 5) > sg_log_upload_buffer_handle->log_buffer_size) {
break;
}
switch (log_content[index]) {
case '\"':
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\"';
break;
case '\\':
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
break;
case '/':
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '/';
break;
case '\b':
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 'b';
break;
case '\f':
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 'f';
break;
case '\n':
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 'n';
break;
case '\r':
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 'r';
break;
case '\t':
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\\';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = 't';
break;
default:
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] =
log_content[index];
}
index++;
}
/* replace \r\n to \", add as delimiter */
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = '\"';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = ',';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index++] = ' ';
HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
return QCLOUD_RET_SUCCESS;
}
#else
// ----------------------------------------------------------------------------
// Old log format by text
// ----------------------------------------------------------------------------
/* log post header format */
#define TIMESTAMP_SIZE (10)
#define SIGNATURE_SIZE (40)
#define CTRL_BYTES_SIZE (4)
// LOG_BUF_FIXED_HEADER_SIZE = 112
#define LOG_BUF_FIXED_HEADER_SIZE \
(SIGNATURE_SIZE + CTRL_BYTES_SIZE + MAX_SIZE_OF_PRODUCT_ID + MAX_SIZE_OF_DEVICE_NAME + TIMESTAMP_SIZE)
#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
#include "mbedtls/aes.h"
/**
* @brief Header of encrypt.
*
*/
static char sg_head_content[128] = {0};
/**
* @brief Log data encrypt by aes cbc.
*
* @param[in] handle @see LogUploadBuffer
* @param[in] buf need encrypt data
* @param[in] datalen need encrypt data length
* @return @see IotReturnCode
*/
static int _log_aes_cbc_encrypt(LogUploadBuffer *handle, uint8_t *buf, uint32_t datalen)
{
int ret = QCLOUD_RET_SUCCESS;
mbedtls_aes_context ctx;
unsigned char iv[16];
memset(iv, '0', 16);
mbedtls_aes_init(&ctx);
ret = mbedtls_aes_setkey_enc(&ctx, (uint8_t *)handle->sign_key, 128);
if (ret) {
UPLOAD_ERR("Set encry key err,ret:%d", ret);
ret = QCLOUD_ERR_FAILURE;
goto exit;
}
ret = mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_ENCRYPT, datalen, iv, buf, buf);
if (ret) {
Log_e("encrypt err,ret:%d", ret);
ret = QCLOUD_ERR_FAILURE;
goto exit;
}
mbedtls_aes_free(&ctx);
return QCLOUD_RET_SUCCESS;
exit:
mbedtls_aes_free(&ctx);
return ret;
}
#endif
/**
* @brief Log upload buffer init.
*
* @param[in] init_params @see LogUploadInitParams
* @return void* if success return @see LogUploadBuffer
*/
void *log_upload_buffer_init(const LogUploadInitParams *init_params)
{
LogUploadBuffer *handle = _log_upload_buffer_init_pre(init_params);
if (!handle) {
return NULL;
}
handle->log_buffer_head_len = LOG_BUF_FIXED_HEADER_SIZE;
memset(handle->log_buffer, '#', handle->log_buffer_head_len);
handle->log_buffer[SIGNATURE_SIZE] = 'P';
memcpy(handle->log_buffer + SIGNATURE_SIZE + CTRL_BYTES_SIZE, init_params->product_id, MAX_SIZE_OF_PRODUCT_ID);
memcpy(handle->log_buffer + SIGNATURE_SIZE + CTRL_BYTES_SIZE + MAX_SIZE_OF_PRODUCT_ID, init_params->device_name,
strlen(init_params->device_name));
handle->write_index = handle->log_buffer_head_len;
handle->host = init_params->host;
_reset_log_buffer(handle);
#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
HAL_Snprintf(sg_head_content, 128,
"Accept:application/json;*/*\r\n"
"X-TC-IotEncryType:%c\r\n"
"X-TC-IotCid:%s%s\r\n",
handle->log_buffer[SIGNATURE_SIZE], init_params->product_id, init_params->device_name);
#endif
return handle;
}
#ifdef LOG_CHECK_HTTP_RET_CODE
/**
* @brief Get http respose result.
*
* @param[in] json http response buffer
* @param[in] json_len http response buffer length
* @return true log report success
* @return false log report error
*/
static bool _get_json_ret_code(char *json, size_t json_len)
{
int rc = 0;
uint32_t ret_code;
rc = utils_json_get_uint32("Retcode", strlen("Retcode"), json, json_len, &ret_code);
if (!rc) {
if (!ret_code) {
return true;
}
}
return false;
}
#endif
/**
* @brief Update timestamp add signature post buffer.
*
* @param[in] handle @see LogUploadBuffer
* @param[in] post_buf need post data buffer
* @param[in] post_size need post data buffer length
*/
static void _update_time_and_signature(LogUploadBuffer *handle, char *post_buf, size_t post_size)
{
char timestamp[TIMESTAMP_SIZE + 1] = {0};
char signature[SIGNATURE_SIZE + 1] = {0};
uint32_t now_time = IOT_Timer_CurrentSec();
/* record the timestamp for this log uploading */
HAL_Snprintf(timestamp, TIMESTAMP_SIZE + 1, "%010d", now_time);
memcpy(post_buf + sg_log_upload_buffer_handle->log_buffer_head_len - TIMESTAMP_SIZE, timestamp, strlen(timestamp));
/* signature of this log uploading */
utils_hmac_sha1_hex(post_buf + SIGNATURE_SIZE, post_size - SIGNATURE_SIZE, (uint8_t *)handle->sign_key,
strlen(handle->sign_key), signature);
memcpy(post_buf, signature, SIGNATURE_SIZE);
}
/**
* @brief Post data to tencent cloud over http.
*
* @param[in] post_buf need post data buffer
* @param[in] post_size need post data buffer length
* @return @see IotReturnCode
*/
int post_one_http_to_server(void *log_upload_buffer_handle, char *post_buf, size_t post_size)
{
int rc = 0;
#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
char *log_encrypt_buffer = NULL;
#endif
LogUploadBuffer *handle = (LogUploadBuffer *)log_upload_buffer_handle;
_update_time_and_signature(handle, post_buf, post_size);
void *http_client = IOT_HTTP_Init();
if (!http_client) {
UPLOAD_ERR("http client init error");
goto exit;
}
IotHTTPConnectParams connect_params = {
.url = handle->host ? handle->host : LOG_UPLOAD_OLD_SERVER_URL,
.port = LOG_UPLOAD_OLD_SERVER_PORT,
.ca_crt = NULL, // TODO: support cert
};
rc = IOT_HTTP_Connect(http_client, &connect_params);
if (rc) {
UPLOAD_ERR("http connect error.");
goto exit;
}
IotHTTPRequestParams request = {
.url = handle->host ? handle->host : LOG_UPLOAD_OLD_SERVER_URL,
.method = IOT_HTTP_METHOD_POST,
.content_length = post_size,
.content_type = "text/plain;charset=utf-8",
.content = post_buf,
.header = "Accept:application/json;*/*\r\n",
};
#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
/*ANSI X.923: zero-padding with last byte as padlen */
int padlen = 16 - post_size % 16;
int datalen = post_size + padlen;
log_encrypt_buffer = HAL_Malloc(datalen + 1);
if (!log_encrypt_buffer) {
UPLOAD_ERR("malloc log encrypt buffer error. malloc length : %d", datalen);
rc = QCLOUD_ERR_MALLOC;
goto exit;
}
memset(log_encrypt_buffer, 0, datalen + 1);
memcpy(log_encrypt_buffer, post_buf, post_size);
log_encrypt_buffer[datalen - 1] = padlen;
// aes cbc
rc = _log_aes_cbc_encrypt(handle, (uint8_t *)log_encrypt_buffer, datalen);
if (QCLOUD_RET_SUCCESS != rc) {
UPLOAD_ERR("log encrypt failed, ret = %d", rc);
goto exit;
}
request.header = sg_head_content;
request.content_type = "application/octet-stream";
request.content_length = datalen;
request.content = log_encrypt_buffer;
#endif
rc = IOT_HTTP_Request(http_client, &request);
// 2. check rc
if (rc) {
UPLOAD_ERR("http post error.");
goto exit;
}
#ifdef LOG_CHECK_HTTP_RET_CODE
char buf[HTTP_RET_JSON_LENGTH] = {0};
rc = IOT_HTTP_Recv(http_client, (uint8_t *)buf, HTTP_RET_JSON_LENGTH, HTTP_WAIT_RET_TIMEOUT_MS);
if (rc < 0) {
UPLOAD_ERR("qcloud_http_recv_data failed, rc = %d", rc);
} else if (rc == 0) {
UPLOAD_ERR("HTTP Recv length == %d", rc);
rc = QCLOUD_ERR_HTTP_TIMEOUT;
} else {
buf[rc - 1] = '\0'; // json_parse relies on a string
if (strlen(buf) > 0 && _get_json_ret_code(buf, rc)) {
UPLOAD_DBG("Log server return SUCCESS: %s", buf);
rc = QCLOUD_RET_SUCCESS;
} else {
UPLOAD_ERR("Log server return FAIL: %s", buf);
rc = QCLOUD_ERR_HTTP;
}
}
#endif
exit:
if (http_client) {
IOT_HTTP_Disconnect(http_client);
IOT_HTTP_Deinit(http_client);
}
#ifdef LOG_UPLOAD_AES_ENCRYPT_POST
if (log_encrypt_buffer) {
HAL_Free(log_encrypt_buffer);
log_encrypt_buffer = NULL;
}
#endif
UPLOAD_DBG("%ld data have be post to server. rc : %d", post_size, rc);
return rc;
}
/**
* @brief Push data to log buffer.
*
* @param[in] log_content need push data
* @return if 0 success else QCLOUD_ERR_FAILURE
*/
int log_upload_buffer_append(const char *log_content)
{
uint16_t log_size = strlen(log_content);
if (HAL_MutexTryLock(sg_log_upload_buffer_handle->lock_buf) != 0) {
UPLOAD_ERR("trylock buffer failed!");
return QCLOUD_ERR_FAILURE;
}
if ((sg_log_upload_buffer_handle->write_index + log_size + 1) > sg_log_upload_buffer_handle->log_buffer_size) {
HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
UPLOAD_DBG("log upload buffer is not enough! remain : %d, need upload length: %d",
sg_log_upload_buffer_handle->log_buffer_size - sg_log_upload_buffer_handle->write_index, log_size);
return QCLOUD_ERR_BUF_TOO_SHORT;
}
memcpy(sg_log_upload_buffer_handle->log_buffer + sg_log_upload_buffer_handle->write_index, log_content, log_size);
sg_log_upload_buffer_handle->write_index += log_size;
/* replace \r\n to \n\f as delimiter */
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index - 1] = '\f';
sg_log_upload_buffer_handle->log_buffer[sg_log_upload_buffer_handle->write_index - 2] = '\n';
HAL_MutexUnlock(sg_log_upload_buffer_handle->lock_buf);
return QCLOUD_RET_SUCCESS;
}
#endif

View File

@@ -0,0 +1,133 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file log_mqtt.c
* @brief
* @author hubertxxu (hubertxxu@tencent.com)
* @version 1.0
* @date 2022-01-05
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022-01-05 <td>1.0 <td>hubertxxu <td>first commit
* </table>
*/
#include "log_upload.h"
/**
* @brief Parse log level.
*
* @param[in,out] client pointer to mqtt client
* @param[in] message message from topic
* @param[in,out] usr_data pointer to @see LogLevelResultInfo
*/
static void _log_mqtt_message_callback(void *client, const MQTTMessage *message, void *usr_data)
{
int rc;
uint32_t log_level;
Log_d("Receive log result message:%.*s", message->payload_len, message->payload_str);
// get log level
rc = utils_json_get_uint32("log_level", sizeof("log_level") - 1, message->payload_str, message->payload_len,
&log_level);
if (rc) {
return;
}
switch (log_level) {
case LOG_LEVEL_DISABLE:
log_upload_buffer_clear();
log_upload_set_upload_log_in_comm_err(true);
log_upload_set_log_upload_level(LOG_LEVEL_ERROR);
UPLOAD_DBG("Upload log level change to: %d", LOG_LEVEL_ERROR);
break;
case LOG_LEVEL_ERROR:
case LOG_LEVEL_WARN:
case LOG_LEVEL_INFO:
case LOG_LEVEL_DEBUG:
if (log_level < log_upload_get_log_upload_level()) {
log_upload_buffer_clear();
}
log_upload_set_upload_log_in_comm_err(false);
log_upload_set_log_upload_level((LogLevel)log_level);
UPLOAD_DBG("Upload log level change to: %d", log_level);
break;
default:
UPLOAD_ERR("Invalid log level: %d", log_level);
break;
}
}
/**
* @brief Check and subscribe log result topic.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
static int _log_mqtt_result_topic_check_and_sub(void *client)
{
char log_upload_topic[MAX_SIZE_OF_CLOUD_TOPIC];
/* subscribe the log topic: "$log/operation/result/${productId}/${deviceName}" */
HAL_Snprintf(log_upload_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$log/operation/result/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
sub_params.on_message_handler = _log_mqtt_message_callback;
sub_params.qos = QOS1;
return IOT_MQTT_SubscribeSync(client, log_upload_topic, &sub_params);
}
/**
* @brief Publish get log level message to log topic.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
static int _log_mqtt_get_log_level_publish(void *client)
{
static int sg_get_log_level_token_count = 0;
char topic_name[MAX_SIZE_OF_CLOUD_TOPIC];
char payload[128] = {0};
HAL_Snprintf(topic_name, sizeof(topic_name), "$log/operation/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
HAL_Snprintf(payload, sizeof(payload), "{\"type\": \"get_log_level\",\"clientToken\": \"get_log_level-%u\"}",
sg_get_log_level_token_count++);
PublishParams pub_params = DEFAULT_PUB_PARAMS;
pub_params.qos = QOS0;
pub_params.payload = payload;
pub_params.payload_len = strlen(payload);
return IOT_MQTT_Publish(client, topic_name, &pub_params) >= 0 ? QCLOUD_RET_SUCCESS : QCLOUD_ERR_FAILURE;
}
/**
* @brief Init log upload module.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int log_mqtt_init(void *client)
{
int rc = _log_mqtt_result_topic_check_and_sub(client);
if (rc) {
return rc;
}
return _log_mqtt_get_log_level_publish(client);
}

View File

@@ -0,0 +1,551 @@
/**
* @file log_upload.c
* @author {hubert} ({hubertxxu@tencent.com})
* @brief
* @version 1.0
* @date 2022-01-24
*
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @par Change Log:
* <table>
* Date Version Author Description
* 2022-01-24 1.0 hubertxxu first commit
* </table>
*/
#include "log_upload.h"
#ifdef LOG_UPLOAD_TYPE_JSON
#define LOG_DELIMITER "\", "
#else
#define LOG_DELIMITER "\n\f"
#endif
/**
* @brief Data structure for log upload.
*
*/
typedef struct {
bool upload_only_in_comm_err;
LogLevel log_level;
QcloudIotTimer upload_timer;
void *upload_buffer_handle;
const char *save_log_filename;
LogSaveFunc save_func;
LogReadFunc read_func;
LogDelFunc del_func;
LogGetSizeFunc get_size_func;
bool log_save_enabled;
bool log_client_init_done;
} QcloudIoTLogUpload;
static QcloudIoTLogUpload *sg_log_uploader_client;
// ----------------------------------------------------------------------------
// Internal API
// ----------------------------------------------------------------------------
/**
* @brief Get log upload client.
*
* @return @see QcloudIoTLogUpload
*/
static QcloudIoTLogUpload *_get_log_uploader_client(void)
{
if (!sg_log_uploader_client) {
return NULL;
}
if (!sg_log_uploader_client->log_client_init_done) {
return NULL;
}
return sg_log_uploader_client;
}
/**
* @brief Set client.
*
* @param[in] client @see QcloudIoTLogUpload
*/
static void _set_log_uploader_client(void *client)
{
sg_log_uploader_client = client;
}
/**
* @brief Set log upload in comm err state.
*
* @param state true or false
*/
void log_upload_set_upload_log_in_comm_err(bool state)
{
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
POINTER_SANITY_CHECK_RTN(log_uploader_handle);
log_uploader_handle->upload_only_in_comm_err = state;
}
/**
* @brief Change log upload level.
*
* @param[in] level @see LogLevel
*/
void log_upload_set_log_upload_level(LogLevel level)
{
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
POINTER_SANITY_CHECK_RTN(log_uploader_handle);
log_uploader_handle->log_level = level;
}
/**
* @brief Get log upload level.
*
* @return @see LogLevel
*/
LogLevel log_upload_get_log_upload_level(void)
{
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
POINTER_SANITY_CHECK(log_uploader_handle, LOG_LEVEL_DISABLE);
return log_uploader_handle->log_level;
}
/**
* @brief Check if need force upload.
*
* @param[in] force_upload
* @return true need force upload
* @return false not needed force upload
*/
static bool _check_force_upload(bool force_upload)
{
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
if (!log_uploader_handle) {
return false;
}
/* 1: check if have data to upload */
if (log_upload_buffer_is_empty(log_uploader_handle->upload_buffer_handle)) {
return false;
}
uint32_t write_index = log_upload_buffer_get_write_index(log_uploader_handle->upload_buffer_handle);
bool is_low_buffer = (log_upload_buffer_get_size() - write_index) < LOG_LOW_BUFFER_THRESHOLD ? true : false;
/* 2. check if upload only in comm err */
if (!force_upload && log_uploader_handle->upload_only_in_comm_err) {
/* buffer is low but we couldn't upload now, reset buffer */
if (is_low_buffer) {
log_upload_buffer_clear();
}
IOT_Timer_CountdownMs(&log_uploader_handle->upload_timer, LOG_UPLOAD_INTERVAL_MS);
return false;
}
/* 3: force upload is true, handle it right now */
if (force_upload) {
return true;
}
/* 4 : check if buffer is low */
if (is_low_buffer) {
/* buffer is low, handle it right now */
return true;
}
/*5: check if timeout, handle it right now*/
return IOT_Timer_Expired(&log_uploader_handle->upload_timer);
}
/**
* @brief Save log to file or flash.
*
* @param[in] log_buf need saved buffer
* @param[in] log_size need saved buffer length
* @return @see IotReturnCode
*/
static int _save_log(char *log_buf, size_t log_size)
{
int rc = 0;
size_t write_size, current_size;
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
POINTER_SANITY_CHECK(log_uploader_handle, QCLOUD_ERR_INVAL);
if (!log_size) {
UPLOAD_ERR("nothing need to save. log_size : %ld", log_size);
return -1;
}
current_size = log_uploader_handle->get_size_func(log_uploader_handle->save_log_filename);
/* overwrite the previous saved log to avoid too many saved logs */
if ((current_size + log_size) > MAX_LOG_SAVE_SIZE) {
UPLOAD_ERR("overwrite the previous saved log. %ld", current_size);
rc = log_uploader_handle->del_func(log_uploader_handle->save_log_filename);
if (rc) {
Log_e("fail to delete previous log");
}
current_size = 0;
}
UPLOAD_DBG("saved data [%ld]", log_size);
write_size =
log_uploader_handle->save_func(log_uploader_handle->save_log_filename, log_buf, log_size, current_size);
if (write_size != log_size) {
Log_e("fail to save log. RC %ld - log size %ld", write_size, log_size);
}
return write_size != log_size ? -1 : 0;
}
/**
* @brief Get at most message to upload.
*
* @param[in] post_buf post buffer
* @return size of message to upload
*/
static uint32_t _buffer_get_at_most_message(const char *post_buf)
{
uint32_t possible_size = 0;
char *next_log_buf = NULL;
size_t delimiter_len = strlen(LOG_DELIMITER);
while (possible_size < MAX_HTTP_LOG_POST_SIZE) {
/* locate the delimiter */
next_log_buf = strstr(post_buf + possible_size, LOG_DELIMITER);
if (!next_log_buf) {
return 0;
}
possible_size = (size_t)(next_log_buf - post_buf + delimiter_len);
/* end of log */
if (next_log_buf[delimiter_len] == 0 && possible_size < MAX_HTTP_LOG_POST_SIZE) {
break;
}
}
return possible_size;
}
/**
* @brief Report log buffer to the server.
*
* @param[in,out] log_uploader_handle @see QcloudIoTLogUpload
* @param[in] post_buf post buffer
* @param[in] post_size post size
* @param[in] is_log_buffer log buffer or saved log
* @return @see IotReturnCode
*/
static int _post_log_buffer_to_cloud(QcloudIoTLogUpload *log_uploader_handle, char *post_buf, size_t post_size,
bool is_log_buffer)
{
int rc = 0;
uint32_t log_buffer_head_len = log_upload_buffer_get_head_len(log_uploader_handle->upload_buffer_handle);
size_t orig_post_size = post_size;
size_t post_payload, possible_size, actual_post_payload = 0;
do {
// 1. get message in buffer
possible_size = _buffer_get_at_most_message(post_buf);
if (!possible_size) {
UPLOAD_ERR("Upload size should not be 0! Total sent: %ld. Left: %ld",
actual_post_payload + log_buffer_head_len, post_size);
if (is_log_buffer) {
log_upload_buffer_clear();
}
return QCLOUD_RET_SUCCESS;
}
if (is_log_buffer && (possible_size > log_upload_buffer_get_size() || possible_size > orig_post_size ||
possible_size <= log_buffer_head_len)) {
log_upload_buffer_clear();
UPLOAD_ERR("possible size (%ld) is too large.", possible_size);
return QCLOUD_RET_SUCCESS;
}
// 2. post to server
rc = post_one_http_to_server(log_uploader_handle->upload_buffer_handle, post_buf, possible_size);
if (rc) {
UPLOAD_ERR("Send log failed. Total sent: %ld. Left: %ld", actual_post_payload + log_buffer_head_len,
post_size);
// when send failed save log in file.
if (log_uploader_handle->log_save_enabled && is_log_buffer) {
_save_log(post_buf + log_buffer_head_len, possible_size - log_buffer_head_len);
}
}
// 3. remove have be post message
memmove(post_buf + log_buffer_head_len, post_buf + possible_size, post_size - possible_size);
post_payload = possible_size - log_buffer_head_len;
post_size -= post_payload;
actual_post_payload += post_payload;
memset(post_buf + post_size, 0, orig_post_size - post_size);
UPLOAD_DBG("post log %d OK. Total sent: %d. Left: %d ", possible_size,
actual_post_payload + log_buffer_head_len, post_size);
} while (post_size > log_buffer_head_len);
if (is_log_buffer) {
log_upload_buffer_remove_log(log_uploader_handle->upload_buffer_handle, orig_post_size);
}
return rc;
}
/**
* @brief Handle log to save flash or file.
*
* @return @see IotReturnCode
*/
static int _handle_saved_log(void)
{
int rc = QCLOUD_RET_SUCCESS;
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
if (!log_uploader_handle) {
return QCLOUD_ERR_FAILURE;
}
size_t whole_log_size = log_uploader_handle->get_size_func(log_uploader_handle->save_log_filename);
if (!whole_log_size) {
return rc;
}
uint32_t log_buffer_head_len = log_upload_buffer_get_head_len(log_uploader_handle->upload_buffer_handle);
size_t buf_size = whole_log_size + log_buffer_head_len + 1;
char *log_buf = HAL_Malloc(buf_size);
if (!log_buf) {
UPLOAD_ERR("Malloc failed, size: %ld", buf_size);
return QCLOUD_ERR_FAILURE;
}
/* read the whole log to buffer */
size_t read_len = log_uploader_handle->read_func(log_uploader_handle->save_log_filename,
log_buf + log_buffer_head_len, whole_log_size, 0);
if (read_len != whole_log_size) {
Log_e("fail to read whole saved log. Size: %ld - read: %ld", whole_log_size, read_len);
rc = QCLOUD_ERR_FAILURE;
goto exit;
}
/* copy header from global log buffer */
size_t upload_size = whole_log_size + log_buffer_head_len;
log_upload_buffer_copy_header(log_buf, log_uploader_handle->upload_buffer_handle);
log_buf[buf_size - 1] = 0;
/* post server */
rc = _post_log_buffer_to_cloud(log_uploader_handle, log_buf, upload_size, false);
if (rc == QCLOUD_RET_SUCCESS || rc == QCLOUD_ERR_INVAL || rc == QCLOUD_ERR_HTTP) {
UPLOAD_DBG("handle saved log done! Size: %ld. ", whole_log_size);
log_uploader_handle->del_func(log_uploader_handle->save_log_filename);
}
exit:
HAL_Free(log_buf);
return rc;
}
/**
* @brief Append need report log to log upload buffer.
*
* @param[in] log_level @see LogLevel
* @param[in] log_content data of need to report
*/
static void _log_upload_append_to_log_buffer(LogLevel log_level, const char *log_content)
{
int rc = 0;
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
// POINTER_SANITY_CHECK_RTN(log_uploader_handle);
if (!log_uploader_handle) {
return;
}
// if not necessary. just give up upload.
if (log_level > log_uploader_handle->log_level) {
return;
}
uint16_t log_size = strlen(log_content);
if (!log_size) {
UPLOAD_ERR("log_size is not 0!");
return;
}
rc = log_upload_buffer_append(log_content);
if (rc) {
/* log buffer is full. upload data right now */
IOT_Timer_CountdownMs(&log_uploader_handle->upload_timer, 0);
}
}
/**
* @brief Upload data to tencent cloud.
*
* @param[in] force_upload upload if necessary
* @return @see IotReturnCode
*/
static int _do_log_upload(bool force_upload)
{
int rc;
static bool sg_need_handle_saved_log = true;
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
POINTER_SANITY_CHECK(log_uploader_handle, QCLOUD_ERR_INVAL);
/* step1: handle previously saved log */
if (log_uploader_handle->log_save_enabled && sg_need_handle_saved_log) {
rc = _handle_saved_log();
if (!rc) {
sg_need_handle_saved_log = false;
}
}
/* step2: check if have data to upload */
if (!_check_force_upload(force_upload)) {
return QCLOUD_RET_SUCCESS;
}
// log_buffer_lock();
/* step3: post log buffer data to cloud */
uint32_t upload_log_size = log_upload_buffer_get_write_index(log_uploader_handle->upload_buffer_handle);
rc = _post_log_buffer_to_cloud(
log_uploader_handle, log_upload_buffer_get(log_uploader_handle->upload_buffer_handle), upload_log_size, true);
if (rc) {
sg_need_handle_saved_log = true;
}
IOT_Timer_CountdownMs(&log_uploader_handle->upload_timer, LOG_UPLOAD_INTERVAL_MS);
// log_buffer_unlock();
return QCLOUD_RET_SUCCESS;
}
/**
* @brief Init log upload previously.
*
* @param[in] init_params @see LogUploadInitParams
* @return @see IotReturnCode
*/
static int log_upload_init_pre(const LogUploadInitParams *init_params)
{
if (_get_log_uploader_client()) {
UPLOAD_DBG("log upload initialized.");
return QCLOUD_RET_SUCCESS;
}
QcloudIoTLogUpload *log_uploader_handle = HAL_Malloc(sizeof(QcloudIoTLogUpload));
if (!log_uploader_handle) {
UPLOAD_ERR("allocate for log client failed");
goto err_exit;
}
log_uploader_handle->upload_only_in_comm_err = false;
/* all the call back functions are necessary to handle log save and re-upload*/
if (init_params->save_func && init_params->read_func && init_params->del_func && init_params->get_size_func &&
init_params->save_log_filename) {
log_uploader_handle->save_log_filename = init_params->save_log_filename;
log_uploader_handle->save_func = init_params->save_func;
log_uploader_handle->read_func = init_params->read_func;
log_uploader_handle->del_func = init_params->del_func;
log_uploader_handle->get_size_func = init_params->get_size_func;
log_uploader_handle->log_save_enabled = true;
} else {
log_uploader_handle->log_save_enabled = false;
}
log_uploader_handle->upload_buffer_handle = log_upload_buffer_init(init_params);
if (!log_uploader_handle->upload_buffer_handle) {
goto err_exit;
}
_set_log_uploader_client(log_uploader_handle);
log_uploader_handle->log_level = LOG_LEVEL_ERROR;
log_uploader_handle->log_client_init_done = true;
return QCLOUD_RET_SUCCESS;
err_exit:
if (log_uploader_handle) {
log_upload_buffer_deinit(log_uploader_handle->upload_buffer_handle);
HAL_Free(log_uploader_handle);
log_uploader_handle = NULL;
}
return QCLOUD_ERR_FAILURE;
}
/**
* @brief Stop log upload add release resources.
*
* @return @see IotReturnCode
*/
static int log_upload_deinit(void)
{
QcloudIoTLogUpload *log_uploader_handle = _get_log_uploader_client();
POINTER_SANITY_CHECK(log_uploader_handle, QCLOUD_ERR_INVAL);
log_upload_buffer_deinit(log_uploader_handle->upload_buffer_handle);
HAL_Free(log_uploader_handle);
log_uploader_handle = NULL;
return QCLOUD_RET_SUCCESS;
}
// ----------------------------------------------------------------------------
// External API
// ----------------------------------------------------------------------------
/**
* @brief Init log upload previously.
*
* @param[in] init_params @see LogUploadInitParams
* @return @see IotReturnCode
*/
int IOT_Log_Upload_InitPre(const LogUploadInitParams *init_params)
{
POINTER_SANITY_CHECK(init_params, QCLOUD_ERR_INVAL);
STRING_PTR_SANITY_CHECK(init_params->product_id, QCLOUD_ERR_INVAL);
STRING_PTR_SANITY_CHECK(init_params->device_name, QCLOUD_ERR_INVAL);
STRING_PTR_SANITY_CHECK(init_params->sign_key, QCLOUD_ERR_INVAL);
return log_upload_init_pre(init_params);
}
/**
* @brief Init log upload module.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int IOT_Log_Upload_Init(void *client)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
return log_mqtt_init(client);
}
/**
* @brief Stop log upload add release resources.
*
* @return @see IotReturnCode
*/
int IOT_Log_Upload_Deinit(void)
{
return log_upload_deinit();
}
/**
* @brief Do log upload.
*
* @param[in] force_upload force upload when error
* @return @see IotReturnCode
*/
int IOT_Log_Upload(bool force_upload)
{
return _do_log_upload(force_upload);
}
/**
* @brief Append need report log to log upload buffer.
*
* @param[in] log_level @see LogLevel
* @param[in] log_content data of need to report
*/
void IOT_Log_Upload_AppendToUploadBuffer(LogLevel log_level, const char *log_content)
{
POINTER_SANITY_CHECK_RTN(log_content);
_log_upload_append_to_log_buffer(log_level, log_content);
}

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,79 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file test_broadcast.cc
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-18
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include <iostream>
#include <string>
#include "gtest/gtest.h"
#include "mqtt_client_test.h"
#include "qcloud_iot_common.h"
namespace mqtt_client_unittest {
/**
* @brief Test log upload
*
*/
TEST_F(MqttClientTest, log_upload_test) {
LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
func.log_upload = IOT_Log_Upload_AppendToUploadBuffer;
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
LogUploadInitParams log_upload_init_params = DEFAULT_LOG_UPLOAD_INIT_PARAMS;
log_upload_init_params.device_name = device_info.device_name;
log_upload_init_params.product_id = device_info.product_id;
log_upload_init_params.sign_key = device_info.device_secret;
log_upload_init_params.log_buffer_size = LOG_UPLOAD_BUFFER_SIZE;
log_upload_init_params.save_log_filename = "./tmp/upload-fail-save.log";
log_upload_init_params.read_func = HAL_File_Read;
log_upload_init_params.save_func = HAL_File_Write;
log_upload_init_params.del_func = HAL_File_Del;
log_upload_init_params.get_size_func = HAL_File_GetSize;
ASSERT_EQ(IOT_Log_Upload_InitPre(&log_upload_init_params), 0);
ASSERT_EQ(IOT_Log_Upload_Init(client), 0);
uint32_t loop_cnt = 10;
int rc = 0;
do {
Log_d("log upload test debug %d...", loop_cnt);
Log_i("log upload test info %d...", loop_cnt);
Log_w("log upload test waring %d...", loop_cnt);
Log_e("log upload test error %d...", loop_cnt);
rc = IOT_MQTT_Yield(client, 200);
ASSERT_EQ(IOT_Log_Upload(rc ? true : false), 0);
} while (loop_cnt--);
ASSERT_EQ(IOT_Log_Upload_Deinit(), 0);
func.log_upload = NULL;
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
}
} // namespace mqtt_client_unittest

View File

@@ -0,0 +1,18 @@
file(GLOB src_mqtt_client ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(inc_mqtt_client ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
set(src_services ${src_services} ${src_mqtt_client} PARENT_SCOPE)
set(inc_services ${inc_services} ${inc_mqtt_client} PARENT_SCOPE)
if( ${CONFIG_COMPILE_SAMPLE} STREQUAL "ON")
file(GLOB src_mqtt_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/mqtt_sample.c)
add_executable(mqtt_sample ${src_mqtt_sample})
target_link_libraries(mqtt_sample ${libsdk})
endif()
if( ${CONFIG_IOT_TEST} STREQUAL "ON")
file(GLOB src_unit_test ${CMAKE_CURRENT_SOURCE_DIR}/test/*.cc)
set(inc_mqtt_client_test ${CMAKE_CURRENT_SOURCE_DIR}/test)
set(src_test ${src_test} ${src_unit_test} PARENT_SCOPE)
set(inc_test ${inc_test} ${inc_mqtt_client_test} PARENT_SCOPE)
endif()

View File

@@ -0,0 +1,446 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file mqtt_client.h
* @brief mqtt client internel api
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-31
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-31 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_MQTT_CLIENT_INC_MQTT_CLIENT_H_
#define IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_MQTT_CLIENT_INC_MQTT_CLIENT_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "qcloud_iot_mqtt_client.h"
#include "mqtt_packet.h"
#include "network_interface.h"
#include "utils_list.h"
#include "utils_base64.h"
#include "utils_hmac.h"
/**
* @brief Packet id, random from [1 - 65536]
*
*/
#define MAX_PACKET_ID (65535)
/**
* @brief Max size of conn Id
*
*/
#define MAX_CONN_ID_LEN (6)
/**
* @brief Max number of topic subscribed
*
*/
#define MAX_MESSAGE_HANDLERS (10)
/**
* @brief Max number in repub list
*
*/
#define MAX_REPUB_NUM (20)
/**
* @brief Minimal wait interval when reconnect
*
*/
#define MIN_RECONNECT_WAIT_INTERVAL (1000)
/**
* @brief Minimal MQTT timeout value
*
*/
#define MIN_COMMAND_TIMEOUT (500)
/**
* @brief Maxmal MQTT timeout value
*
*/
#define MAX_COMMAND_TIMEOUT (20000)
/**
* @brief Minimal TLS handshaking timeout value (unit: ms)
*
*/
#define QCLOUD_IOT_TLS_HANDSHAKE_TIMEOUT (5 * 1000)
/**
* @brief Enable repeat packet id filter
*
*/
#define MQTT_RMDUP_MSG_ENABLED
/**
* @brief Connect status of mqtt server
*
*/
typedef enum {
NOTCONNECTED = 0,
CONNECTED = 1,
} ConnStatus;
/**
* @brief mqtt connection mode
*
*/
typedef enum {
FIRST_CONNECT = 0,
RECONNECT = 1,
} ConnMode;
/*
* @brief Subscribe status of topic.
*
*/
typedef enum {
SUB_ACK_NOT_RECEIVED = 0,
SUB_ACK_RECEIVED = 1,
} SubStatus;
/**
* @brief data structure for topic subscription handle
*/
typedef struct {
char *topic_filter; /**< topic name, wildcard filter is supported */
SubStatus status; /**< status of sub handle */
SubscribeParams params; /**< params needed to subscribe */
} SubTopicHandle;
/**
* @brief MQTT QCloud IoT Client structure
*
*/
typedef struct {
DeviceInfo *device_info; /**< device info needed to connect server */
uint16_t next_packet_id; /**< MQTT random packet id */
uint32_t command_timeout_ms; /**< MQTT command timeout, unit:ms */
uint8_t write_buf[QCLOUD_IOT_MQTT_TX_BUF_LEN]; /**< MQTT write buffer */
uint8_t read_buf[QCLOUD_IOT_MQTT_RX_BUF_LEN]; /**< MQTT read buffer */
size_t write_buf_size; /**< size of MQTT write buffer */
size_t read_buf_size; /**< size of MQTT read buffer */
MQTTEventHandler event_handle; /**< callback for MQTT event */
uint8_t auto_connect_enable; /**< enable auto connection or not */
uint8_t default_subscribe; /**< no subscribe packet send, only add subhandle */
void *lock_generic; /**< mutex/lock for this client struture */
void *lock_write_buf; /**< mutex/lock for write buffer */
void *list_pub_wait_ack; /**< puback waiting list */
void *list_sub_wait_ack; /**< suback waiting list */
char host_addr[HOST_STR_LENGTH]; /**< MQTT server host */
MQTTGetNextHostIp get_next_host_ip; // get host ip
const char *main_host;
const char *backup_host; // if host not connect will try this.
IotNetwork network_stack; /**< MQTT network stack */
MQTTPacketConnectOption options; /**< handle to connection parameters */
char conn_id[MAX_CONN_ID_LEN]; /**< connect id */
SubTopicHandle sub_handles[MAX_MESSAGE_HANDLERS]; /**< subscription handle array */
QcloudIotTimer ping_timer; /**< MQTT ping timer */
QcloudIotTimer reconnect_delay_timer; /**< MQTT reconnect delay timer */
uint8_t was_manually_disconnected; /**< was disconnect by server or device */
uint8_t is_ping_outstanding; /**< 1 = ping request is sent while ping response not arrived yet */
uint32_t current_reconnect_wait_interval; /**< unit:ms */
uint8_t is_connected; /**< is connected or not */
uint32_t counter_network_disconnected; /**< number of disconnection*/
#ifdef MQTT_RMDUP_MSG_ENABLED
#define MQTT_MAX_REPEAT_BUF_LEN 10
uint16_t repeat_packet_id_buf[MQTT_MAX_REPEAT_BUF_LEN]; /**< repeat packet id buffer */
unsigned int current_packet_id_cnt; /**< index of packet id buffer */
#endif
} QcloudIotClient;
/**
* @brief topic publish info
*
*/
typedef struct {
uint8_t *buf; /**< msg buffer */
uint32_t len; /**< msg length */
uint16_t packet_id; /**< packet id */
QcloudIotTimer pub_start_time; /**< timer for puback waiting */
} QcloudIotPubInfo;
/**
* @brief topic subscribe/unsubscribe info
*
*/
typedef struct {
uint8_t *buf; /**< msg buffer */
uint16_t len; /**< msg length */
MQTTPacketType type; /**< type: sub or unsub */
uint16_t packet_id; /**< packet id */
QcloudIotTimer sub_start_time; /**< timer for suback waiting */
SubTopicHandle handler; /**< handle of topic subscribed(unsubscribed) */
} QcloudIotSubInfo;
/**************************************************************************************
* common
**************************************************************************************/
/**
* @brief Get the next packet id object.
*
* @param[in,out] client pointer to mqtt client
* @return packet id
*/
uint16_t get_next_packet_id(QcloudIotClient *client);
/**
* @brief Get the next conn id object.
*
* @param[out] conn_id buffer to save conn id
*/
void get_next_conn_id(char *conn_id);
/**
* @brief Set the client conn state object.
*
* @param[in,out] client pointer to mqtt client
* @param[in] connected connect status, @see ConnStatus
*/
void set_client_conn_state(QcloudIotClient *client, uint8_t connected);
/**
* @brief Get the client conn state object.
*
* @param[in,out] client
* @return @see ConnStatus
*/
uint8_t get_client_conn_state(QcloudIotClient *client);
/**
* @brief Send mqtt packet, timeout = command_timeout_ms.
*
* @param[in,out] client pointer to mqtt client
* @param[in] length length of data to be sent, data is saved in client write_buf
* @return @see IotReturnCode
*/
int send_mqtt_packet(QcloudIotClient *client, size_t length);
/**************************************************************************************
* connect
**************************************************************************************/
/**
* @brief Connect MQTT server.
*
* @param[in,out] client pointer to mqtt client
* @param[in] mode when connect error. Choose different connection strategies according to the mode
* @return @see IotReturnCode
*/
int qcloud_iot_mqtt_connect(QcloudIotClient *client, ConnMode mode);
/**
* @brief Reconnect MQTT server.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int qcloud_iot_mqtt_attempt_reconnect(QcloudIotClient *client);
/**
* @brief Disconnect MQTT server.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int qcloud_iot_mqtt_disconnect(QcloudIotClient *client);
/**
* @brief Serialize and send pingreq packet.
*
* @param[in,out] client pointer to mqtt client
* @param[in] try_times if failed, retry times
* @return @see IotReturnCode
*/
int qcloud_iot_mqtt_pingreq(QcloudIotClient *client, int try_times);
/**************************************************************************************
* publish
**************************************************************************************/
/**
* @brief Serialize and send publish packet.
*
* @param[in,out] client pointer to mqtt_client
* @param[in] topic_name topic to publish
* @param[in] params publish params
* @return >=0 for packet id, < 0 for failed @see IotReturnCode
*/
int qcloud_iot_mqtt_publish(QcloudIotClient *client, const char *topic_name, const PublishParams *params);
/**
* @brief Deserialize publish packet and deliver_message.
*
* @param[in,out] client pointer to mqtt_client
* @param[in] params publish params
* @return >=0 for packet id, < 0 for failed @see IotReturnCode
*/
int qcloud_iot_mqtt_handle_publish(QcloudIotClient *client);
/**
* @brief Deserialize puback packet.
*
* @param[in,out] client pointer to mqtt_client
* @return 0 for success.
*/
int qcloud_iot_mqtt_handle_puback(QcloudIotClient *client);
/**
* @brief Process puback waiting timout.
*
* @param[in,out] client pointer to mqtt_client
*/
void qcloud_iot_mqtt_check_pub_timeout(QcloudIotClient *client);
/**************************************************************************************
* subscribe
**************************************************************************************/
/**
* @brief Serialize and send subscribe packet.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic to subscribe
* @param[in] params subscribe params
* @return >=0 for packet id, < 0 for failed @see IotReturnCode
*/
int qcloud_iot_mqtt_subscribe(QcloudIotClient *client, const char *topic_filter, const SubscribeParams *params);
/**
* @brief Deserialize suback packet and return sub result.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int qcloud_iot_mqtt_handle_suback(QcloudIotClient *client);
/**
* @brief Serialize and send unsubscribe packet.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic to unsubscribe
* @return >=0 packet id, < 0 for failed @see IotReturnCode
*/
int qcloud_iot_mqtt_unsubscribe(QcloudIotClient *client, const char *topic_filter);
/**
* @brief Deserialize unsuback packet and remove from list.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int qcloud_iot_mqtt_handle_unsuback(QcloudIotClient *client);
/**
* @brief Process suback waiting timeout.
*
* @param[in,out] client pointer to mqtt client
*/
void qcloud_iot_mqtt_check_sub_timeout(QcloudIotClient *client);
/**
* @brief Resubscribe topic when reconnect.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int qcloud_iot_mqtt_resubscribe(QcloudIotClient *client);
/**
* @brief Return if topic is sub ready.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter
* @return true for ready
* @return false for not ready
*/
bool qcloud_iot_mqtt_is_sub_ready(QcloudIotClient *client, const char *topic_filter);
/**
* @brief Get usr data, usr should handle lock/unlock usrdata itself in callback and caller.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter
* @return NULL or user data
*/
void *qcloud_iot_mqtt_get_subscribe_usr_data(QcloudIotClient *client, const char *topic_filter);
/**
* @brief Clear sub handle array.
*
* @param[in,out] client pointer to mqtt client
*/
void qcloud_iot_mqtt_sub_handle_array_clear(QcloudIotClient *client);
/**
* @brief Clear suback wait list and clear sub handle.
*
* @param[in,out] client pointer to mqtt client
*/
void qcloud_iot_mqtt_suback_wait_list_clear(QcloudIotClient *client);
/**************************************************************************************
* yield
**************************************************************************************/
/**
* @brief Check connection and keep alive state, read/handle MQTT message in synchronized way.
*
* @param[in,out] client pointer to mqtt client
* @param[in] timeout_ms timeout value (unit: ms) for this operation
*
* @return QCLOUD_RET_SUCCESS when success, QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT when try reconnecting, others @see
* IotReturnCode
*/
int qcloud_iot_mqtt_yield(QcloudIotClient *client, uint32_t timeout_ms);
/**
* @brief Wait read specific mqtt packet, such as connack.
*
* @param[in,out] client pointer to mqtt client
* @param[in] packet_type packet type except to read
* @return @see IotReturnCode
*/
int qcloud_iot_mqtt_wait_for_read(QcloudIotClient *client, uint8_t packet_type);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_MQTT_CLIENT_INC_MQTT_CLIENT_H_

View File

@@ -0,0 +1,297 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file mqtt_sample.c
* @brief a simple sample for mqtt client
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-31
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-31 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
* </table>
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "qcloud_iot_common.h"
#include "utils_log.h"
/**
* @brief MQTT event callback, @see MQTTEventHandleFun
*
* @param[in] client pointer to mqtt client
* @param[in] handle_context context
* @param[in] msg msg
*/
static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
{
MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
uintptr_t packet_id = (uintptr_t)msg->msg;
switch (msg->event_type) {
case MQTT_EVENT_UNDEF:
Log_i("undefined event occur.");
break;
case MQTT_EVENT_DISCONNECT:
Log_i("MQTT disconnect.");
break;
case MQTT_EVENT_RECONNECT:
Log_i("MQTT reconnect.");
break;
case MQTT_EVENT_PUBLISH_RECEIVED:
Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
break;
case MQTT_EVENT_SUBSCRIBE_SUCCESS:
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_SUBSCRIBE_NACK:
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_NACK:
Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_SUCCESS:
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_TIMEOUT:
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_NACK:
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
break;
default:
Log_i("Should NOT arrive here.");
break;
}
}
/**
* @brief Setup MQTT construct parameters.
*
* @param[in,out] initParams @see MQTTInitParams
* @param[in] device_info @see DeviceInfo
*/
static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
{
init_params->device_info = device_info;
init_params->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
// ----------------------------------------------------------------------------
static int sg_main_exit = 0;
#ifdef __linux__
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
static void _main_exit(int sig)
{
Log_e("demo exit by signal:%d\n", sig);
sg_main_exit = 1;
}
#endif
int main(int argc, char **argv)
{
#ifdef __linux__
signal(SIGINT, _main_exit);
#endif
int rc;
// init log level
LogHandleFunc func = 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;
}
// subscribe normal topics and wait result
rc = _subscribe_topic_wait_result(client, "data", QOS0);
if (rc) {
Log_e("Client Subscribe Topic Failed: %d", rc);
return rc;
}
do {
rc = _publish_test_msg(client, "data", QOS1);
if (rc < 0) {
Log_e("client publish topic failed :%d.", rc);
}
rc = IOT_MQTT_Yield(client, 2000);
if (rc == QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT) {
HAL_SleepMs(1000);
continue;
} else if (rc != QCLOUD_RET_SUCCESS && rc != QCLOUD_RET_MQTT_RECONNECTED) {
Log_e("exit with error: %d", rc);
break;
}
} while (!sg_main_exit);
rc = _unsubscribe_topic(client, "data");
rc |= IOT_MQTT_Destroy(&client);
utils_log_deinit();
return rc;
}

View File

@@ -0,0 +1,584 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file mqtt_client.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-28
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-07 <td>1.1 <td>fancyxu <td>support user host for unittest
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
* </table>
*/
#include "mqtt_client.h"
#define MQTT_VERSION_3_1_1 4
// 20 for timestamp length & delimiter
#define MAX_MQTT_CONNECT_USR_NAME_LEN (MAX_SIZE_OF_CLIENT_ID + QCLOUD_IOT_DEVICE_SDK_APPID_LEN + MAX_CONN_ID_LEN + 20)
#define MAX_MQTT_CONNECT_PASSWORD_LEN (51)
/**************************************************************************************
* static method
**************************************************************************************/
/**
* @brief Get random packet id, when start.
*
* @return packet id
*/
static uint16_t _get_random_start_packet_id(void)
{
srand(IOT_Timer_CurrentSec());
return rand() % 65536 + 1;
}
/**
* @brief Init list_pub_wait_ack and list_sub_wait_ack
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
static int _mqtt_client_list_init(QcloudIotClient *client)
{
IOT_FUNC_ENTRY;
UtilsListFunc func = DEFAULT_LIST_FUNCS;
client->list_pub_wait_ack = utils_list_create(func, MAX_REPUB_NUM);
if (!client->list_pub_wait_ack) {
Log_e("create pub wait list failed.");
goto error;
}
client->list_sub_wait_ack = utils_list_create(func, MAX_MESSAGE_HANDLERS);
if (!client->list_sub_wait_ack) {
Log_e("create sub wait list failed.");
goto error;
}
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
error:
utils_list_destroy(client->list_pub_wait_ack);
client->list_pub_wait_ack = NULL;
utils_list_destroy(client->list_sub_wait_ack);
client->list_sub_wait_ack = NULL;
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
/**
* @brief Init network, tcp(with AUTH_WITH_NO_TLS) or tls.
*
* @param[in,out] client pointer to mqtt client
*/
static void _mqtt_client_network_init(QcloudIotClient *client, const MQTTInitParams *params)
{
HAL_Snprintf(client->host_addr, HOST_STR_LENGTH, "%s.%s", client->device_info->product_id,
params->host ? params->host : QCLOUD_IOT_MQTT_DIRECT_DOMAIN);
client->main_host = params->host;
client->backup_host = params->backup_host;
client->get_next_host_ip = params->get_next_host_ip;
#ifndef AUTH_WITH_NO_TLS
// device param for TLS connection
#ifdef AUTH_MODE_CERT
Log_d("cert file: %s", STRING_PTR_PRINT_SANITY_CHECK(client->device_info->cert_file));
Log_d("key file: %s", STRING_PTR_PRINT_SANITY_CHECK(client->device_info->key_file));
client->network_stack.ssl_connect_params.cert_file = client->device_info->cert_file;
client->network_stack.ssl_connect_params.key_file = client->device_info->key_file;
client->network_stack.ssl_connect_params.ca_crt = iot_ca_get();
client->network_stack.ssl_connect_params.ca_crt_len = strlen(client->network_stack.ssl_connect_params.ca_crt);
#else
client->network_stack.ssl_connect_params.psk = (char *)client->device_info->device_secret_decode;
client->network_stack.ssl_connect_params.psk_length = client->device_info->device_secret_decode_len;
client->network_stack.ssl_connect_params.psk_id = client->device_info->client_id;
client->network_stack.ssl_connect_params.ca_crt = NULL;
client->network_stack.ssl_connect_params.ca_crt_len = 0;
#endif
client->network_stack.host = client->host_addr;
client->network_stack.port = MQTT_SERVER_PORT_TLS;
client->network_stack.ssl_connect_params.timeout_ms = client->command_timeout_ms > QCLOUD_IOT_TLS_HANDSHAKE_TIMEOUT
? client->command_timeout_ms
: QCLOUD_IOT_TLS_HANDSHAKE_TIMEOUT;
client->network_stack.type = IOT_NETWORK_TYPE_TLS;
#else
client->network_stack.host = client->host_addr;
client->network_stack.port = MQTT_SERVER_PORT_NO_TLS;
client->network_stack.type = IOT_NETWORK_TYPE_TCP;
#endif
qcloud_iot_network_init(&(client->network_stack));
}
/**
* @brief Init mqtt connection options.
*
* @param[in,out] client pointer to mqtt client
* @param[in] params mqtt init params, @see MQTTInitParams
* @return int
*/
static int _mqtt_client_connect_option_init(QcloudIotClient *client, const MQTTInitParams *params)
{
IOT_FUNC_ENTRY;
int rc = 0;
long cur_timesec = 0;
client->options.mqtt_version = MQTT_VERSION_3_1_1;
// Upper limit of keep alive interval is (11.5 * 60) seconds
client->options.client_id = client->device_info->client_id;
client->options.keep_alive_interval = params->keep_alive_interval > 690 ? 690 : params->keep_alive_interval;
client->options.clean_session = params->clean_session;
// calculate user name & password
client->options.username = (char *)HAL_Malloc(MAX_MQTT_CONNECT_USR_NAME_LEN);
if (!client->options.username) {
Log_e("malloc username failed!");
rc = QCLOUD_ERR_MALLOC;
goto error;
}
cur_timesec =
(MAX_ACCESS_EXPIRE_TIMEOUT <= 0) ? 0x7fffffffL : (IOT_Timer_CurrentSec() + MAX_ACCESS_EXPIRE_TIMEOUT / 1000);
get_next_conn_id(client->conn_id);
HAL_Snprintf(client->options.username, MAX_MQTT_CONNECT_USR_NAME_LEN, "%s;%s;%s;%ld", client->options.client_id,
QCLOUD_IOT_DEVICE_SDK_APPID, client->conn_id, cur_timesec);
#if defined(AUTH_WITH_NO_TLS) && defined(AUTH_MODE_KEY)
char sign[41] = {0};
client->options.password = (char *)HAL_Malloc(MAX_MQTT_CONNECT_PASSWORD_LEN);
if (!client->options.password) {
Log_e("malloc password failed!");
rc = QCLOUD_ERR_MALLOC;
goto error;
}
utils_hmac_sha1_hex(client->options.username, strlen(client->options.username),
client->device_info->device_secret_decode, client->device_info->device_secret_decode_len, sign);
HAL_Snprintf(client->options.password, MAX_MQTT_CONNECT_PASSWORD_LEN, "%s;hmacsha1", sign);
#endif
IOT_FUNC_EXIT_RC(rc);
error:
HAL_Free(client->options.username);
client->options.username = NULL;
HAL_Free(client->options.password);
client->options.password = NULL;
IOT_FUNC_EXIT_RC(rc);
}
/**
* @brief Init mqtt client.
*
* @param[in,out] client pointer to mqtt client
* @param[in] params mqtt init params, @see MQTTInitParams
* @return @see IotReturnCode
*
* @note
* 1. init device info.
* 2. init mutex and var
* 3. init list @see _mqtt_client_list_init
* 4. init connect option @see _mqtt_client_connect_option_init
* 5. init network @see _mqtt_client_network_init
*/
static int _qcloud_iot_mqtt_client_init(QcloudIotClient *client, const MQTTInitParams *params)
{
IOT_FUNC_ENTRY;
int rc = 0;
memset(client, 0x0, sizeof(QcloudIotClient));
// set device info
client->device_info = params->device_info;
rc = HAL_Snprintf(client->device_info->client_id, MAX_SIZE_OF_CLIENT_ID, "%s%s", client->device_info->product_id,
client->device_info->device_name);
#ifdef AUTH_MODE_KEY
utils_base64decode(client->device_info->device_secret_decode, MAX_SIZE_OF_DECODE_PSK_LENGTH,
&client->device_info->device_secret_decode_len, (uint8_t *)client->device_info->device_secret,
strlen(client->device_info->device_secret));
#endif
Log_i("SDK_Ver: %s, Product_ID: %s, Device_Name: %s", QCLOUD_IOT_DEVICE_SDK_VERSION,
client->device_info->product_id, client->device_info->device_name);
// command timeout should be in range [MIN_COMMAND_TIMEOUT, MAX_COMMAND_TIMEOUT]
client->command_timeout_ms =
(params->command_timeout < MIN_COMMAND_TIMEOUT)
? MIN_COMMAND_TIMEOUT
: (params->command_timeout > MAX_COMMAND_TIMEOUT ? MAX_COMMAND_TIMEOUT : params->command_timeout);
// packet id, random from [1 - 65536]
client->next_packet_id = _get_random_start_packet_id();
client->write_buf_size = QCLOUD_IOT_MQTT_TX_BUF_LEN;
client->read_buf_size = QCLOUD_IOT_MQTT_RX_BUF_LEN;
client->event_handle = params->event_handle;
client->auto_connect_enable = params->auto_connect_enable;
client->default_subscribe = params->default_subscribe;
client->lock_generic = HAL_MutexCreate();
if (!client->lock_generic) {
goto error;
}
client->lock_write_buf = HAL_MutexCreate();
if (!client->lock_write_buf) {
goto error;
}
rc = _mqtt_client_list_init(client);
if (rc) {
goto error;
}
rc = _mqtt_client_connect_option_init(client, params);
if (rc) {
goto error;
}
_mqtt_client_network_init(client, params);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
error:
HAL_MutexDestroy(client->lock_generic);
client->lock_generic = NULL;
HAL_MutexDestroy(client->lock_write_buf);
client->lock_write_buf = NULL;
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
/**
* @brief Deinit mqtt client.
*
* @param[in,out] client pointer to mqtt client
*/
static void _qcloud_iot_mqtt_client_deinit(QcloudIotClient *client)
{
HAL_Free(client->options.username);
HAL_Free(client->options.password);
HAL_MutexDestroy(client->lock_generic);
HAL_MutexDestroy(client->lock_write_buf);
qcloud_iot_mqtt_sub_handle_array_clear(client);
qcloud_iot_mqtt_suback_wait_list_clear(client);
utils_list_destroy(client->list_pub_wait_ack);
utils_list_destroy(client->list_sub_wait_ack);
Log_i("release mqtt client resources");
}
/**************************************************************************************
* API
**************************************************************************************/
/**
* @brief Create MQTT client and connect to MQTT server.
*
* @param[in] params MQTT init parameters
* @return a valid MQTT client handle when success, or NULL otherwise
*/
void *IOT_MQTT_Construct(const MQTTInitParams *params)
{
POINTER_SANITY_CHECK(params, NULL);
POINTER_SANITY_CHECK(params->device_info, NULL);
STRING_PTR_SANITY_CHECK(params->device_info->product_id, NULL);
STRING_PTR_SANITY_CHECK(params->device_info->device_name, NULL);
#ifdef AUTH_MODE_CERT
STRING_PTR_SANITY_CHECK(params->device_info->dev_cert_file_name, NULL);
STRING_PTR_SANITY_CHECK(params->device_info->dev_key_file_name, NULL);
#else
STRING_PTR_SANITY_CHECK(params->device_info->device_secret, NULL);
#endif
int rc = 0;
QcloudIotClient *client = NULL;
// create and init MQTTClient
client = (QcloudIotClient *)HAL_Malloc(sizeof(QcloudIotClient));
if (!client) {
Log_e("malloc MQTTClient failed");
return NULL;
}
rc = _qcloud_iot_mqtt_client_init(client, params);
if (rc) {
Log_e("mqtt init failed: %d", rc);
goto exit;
}
if (!params->connect_when_construct) {
return client;
}
rc = qcloud_iot_mqtt_connect(client, FIRST_CONNECT);
if (rc) {
Log_e("mqtt connect with id: %s failed: %d", STRING_PTR_PRINT_SANITY_CHECK(client->conn_id), rc);
goto exit;
}
Log_i("mqtt connect with id: %s success", client->conn_id);
return client;
exit:
_qcloud_iot_mqtt_client_deinit(client);
HAL_Free(client);
return NULL;
}
/**
* @brief Connect Mqtt server if not connect.
*
* @param[in,out] client pointer to mqtt client pointer, should using the pointer of IOT_MQTT_Construct return.
* @return @see IotReturnCode
*/
int IOT_MQTT_Connect(void *client)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
if (!get_client_conn_state(client)) {
IOT_FUNC_EXIT_RC(qcloud_iot_mqtt_connect(client, FIRST_CONNECT));
}
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
/**
* @brief Close connection and destroy MQTT client.
*
* @param[in,out] client pointer to mqtt client pointer, should using the pointer of IOT_MQTT_Construct return.
* @return @see IotReturnCode
*/
int IOT_MQTT_Destroy(void **client)
{
POINTER_SANITY_CHECK(*client, QCLOUD_ERR_INVAL);
QcloudIotClient *mqtt_client = (QcloudIotClient *)(*client);
int rc = qcloud_iot_mqtt_disconnect(mqtt_client);
if (rc) {
// disconnect network stack by force
mqtt_client->network_stack.disconnect(&(mqtt_client->network_stack));
set_client_conn_state(mqtt_client, NOTCONNECTED);
}
_qcloud_iot_mqtt_client_deinit(mqtt_client);
HAL_Free(*client);
*client = NULL;
Log_i("mqtt release!");
return rc;
}
/**
* @brief Check connection and keep alive state, read/handle MQTT packet in synchronized way.
*
* @param[in,out] client pointer to mqtt client
* @param[in] timeout_ms timeout value (unit: ms) for this operation
* @return QCLOUD_RET_SUCCESS when success, QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT when try reconnecting, others @see
* IotReturnCode
*/
int IOT_MQTT_Yield(void *client, uint32_t timeout_ms)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
return qcloud_iot_mqtt_yield(mqtt_client, timeout_ms);
}
/**
* @brief Publish MQTT message.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_name topic to publish
* @param[in] params @see PublishParams
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_MQTT_Publish(void *client, const char *topic_name, const PublishParams *params)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(params, QCLOUD_ERR_INVAL);
STRING_PTR_SANITY_CHECK(topic_name, QCLOUD_ERR_INVAL);
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
if (!get_client_conn_state(client)) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
if (strlen(topic_name) > MAX_SIZE_OF_CLOUD_TOPIC) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MAX_TOPIC_LENGTH);
}
if (QOS2 == params->qos) {
Log_e("QoS2 is not supported currently");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_QOS_NOT_SUPPORT);
}
return qcloud_iot_mqtt_publish(mqtt_client, topic_name, params);
}
/**
* @brief Subscribe MQTT topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter to subscribe
* @param[in] params @see SubscribeParams
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_MQTT_Subscribe(void *client, const char *topic_filter, const SubscribeParams *params)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(params, QCLOUD_ERR_INVAL);
STRING_PTR_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
if (!get_client_conn_state(client) && !mqtt_client->default_subscribe) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
if (strlen(topic_filter) > MAX_SIZE_OF_CLOUD_TOPIC) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MAX_TOPIC_LENGTH);
}
if (QOS2 == params->qos) {
Log_e("QoS2 is not supported currently");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_QOS_NOT_SUPPORT);
}
return qcloud_iot_mqtt_subscribe(mqtt_client, topic_filter, params);
}
/**
* @brief Unsubscribe MQTT topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter to unsubscribe
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_MQTT_Unsubscribe(void *client, const char *topic_filter)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
STRING_PTR_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
if (!get_client_conn_state(client)) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
if (strlen(topic_filter) > MAX_SIZE_OF_CLOUD_TOPIC) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MAX_TOPIC_LENGTH);
}
return qcloud_iot_mqtt_unsubscribe(mqtt_client, topic_filter);
}
/**
* @brief Check if MQTT topic has been subscribed or not
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter to subscribe
* @return true already subscribed
* @return false not ready
*/
bool IOT_MQTT_IsSubReady(void *client, const char *topic_filter)
{
POINTER_SANITY_CHECK(client, false);
STRING_PTR_SANITY_CHECK(topic_filter, false);
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
return qcloud_iot_mqtt_is_sub_ready(mqtt_client, topic_filter);
}
/**
* @brief Get user data in subscribe.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter to subscribe
* @return NULL or user data
*/
void *IOT_MQTT_GetSubUsrData(void *client, const char *topic_filter)
{
POINTER_SANITY_CHECK(client, NULL);
STRING_PTR_SANITY_CHECK(topic_filter, NULL);
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
return qcloud_iot_mqtt_get_subscribe_usr_data(mqtt_client, topic_filter);
}
/**
* @brief Subscribe and wait sub ready.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter to subscribe
* @param[in] params @see SubscribeParams
* @return @see IotReturnCode
*/
int IOT_MQTT_SubscribeSync(void *client, const char *topic_filter, const SubscribeParams *params)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(params, QCLOUD_ERR_INVAL);
STRING_PTR_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
int rc;
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
int cnt_sub = mqtt_client->command_timeout_ms / QCLOUD_IOT_MQTT_YIELD_TIMEOUT;
if (IOT_MQTT_IsSubReady(client, topic_filter)) {
// if already sub, free the user data
if (params->user_data_free) {
params->user_data_free(params->user_data);
}
return QCLOUD_RET_SUCCESS;
}
rc = IOT_MQTT_Subscribe(client, topic_filter, params);
if (rc < 0) {
Log_e("topic subscribe failed: %d, cnt: %d", rc, cnt_sub);
return rc;
}
while (cnt_sub-- >= 0 && rc >= 0 && !IOT_MQTT_IsSubReady(client, topic_filter)) {
/**
* @brief wait for subscription result
*
*/
rc = IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT);
}
return IOT_MQTT_IsSubReady(client, topic_filter) ? QCLOUD_RET_SUCCESS : QCLOUD_ERR_FAILURE;
}
/**
* @brief Check if MQTT is connected.
*
* @param[in,out] client pointer to mqtt client
* @return true connected
* @return false no connected
*/
bool IOT_MQTT_IsConnected(void *client)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
return get_client_conn_state(mqtt_client);
}
/**
* @brief Get device info using to connect mqtt server.
*
* @param[in,out] client pointer to mqtt client
* @return @see DeviceInfo
*/
DeviceInfo *IOT_MQTT_GetDeviceInfo(void *client)
{
POINTER_SANITY_CHECK(client, NULL);
QcloudIotClient *mqtt_client = (QcloudIotClient *)client;
return mqtt_client->device_info;
}

View File

@@ -0,0 +1,130 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file mqtt_client_common.c
* @brief common api
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-28
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
* </table>
*/
#include "mqtt_client.h"
/**
* @brief Get the next packet id object.
*
* @param[in,out] client pointer to mqtt client
* @return packet id
*/
uint16_t get_next_packet_id(QcloudIotClient *client)
{
IOT_FUNC_ENTRY;
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
HAL_MutexLock(client->lock_generic);
client->next_packet_id = (uint16_t)((MAX_PACKET_ID == client->next_packet_id) ? 1 : (client->next_packet_id + 1));
HAL_MutexUnlock(client->lock_generic);
IOT_FUNC_EXIT_RC(client->next_packet_id);
}
/**
* @brief Get the next conn id object.
*
* @param[out] conn_id buffer to save conn id
*/
void get_next_conn_id(char *conn_id)
{
int i;
srand(IOT_Timer_CurrentSec());
for (i = 0; i < MAX_CONN_ID_LEN - 1; i++) {
int flag = rand() % 3;
switch (flag) {
case 0:
conn_id[i] = (rand() % 26) + 'a';
break;
case 1:
conn_id[i] = (rand() % 26) + 'A';
break;
case 2:
conn_id[i] = (rand() % 10) + '0';
break;
}
}
conn_id[MAX_CONN_ID_LEN - 1] = '\0';
}
/**
* @brief Set the client conn state object.
*
* @param[in,out] client pointer to mqtt client
* @param[in] connected connect status, @see ConnStatus
*/
void set_client_conn_state(QcloudIotClient *client, uint8_t connected)
{
HAL_MutexLock(client->lock_generic);
client->is_connected = connected;
HAL_MutexUnlock(client->lock_generic);
}
/**
* @brief Get the client conn state object.
*
* @param[in,out] client
* @return @see ConnStatus
*/
uint8_t get_client_conn_state(QcloudIotClient *client)
{
IOT_FUNC_ENTRY;
uint8_t is_connected = 0;
HAL_MutexLock(client->lock_generic);
is_connected = client->is_connected;
HAL_MutexUnlock(client->lock_generic);
IOT_FUNC_EXIT_RC(is_connected);
}
/**
* @brief Send mqtt packet, timeout = command_timeout_ms.
*
* @param[in,out] client pointer to mqtt client
* @param[in] length length of data to be sent, data is saved in client write_buf
* @return @see IotReturnCode
*/
int send_mqtt_packet(QcloudIotClient *client, size_t length)
{
IOT_FUNC_ENTRY;
int rc = QCLOUD_RET_SUCCESS;
size_t sent_len = 0;
if (length >= client->write_buf_size) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
}
rc = client->network_stack.write(&(client->network_stack), client->write_buf, length, client->command_timeout_ms,
&sent_len);
rc = QCLOUD_ERR_TCP_WRITE_TIMEOUT == rc ? QCLOUD_ERR_MQTT_REQUEST_TIMEOUT : rc;
IOT_FUNC_EXIT_RC(rc);
}

View File

@@ -0,0 +1,279 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file mqtt_client_connect.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-28
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
* </table>
*/
#include "mqtt_client.h"
/**
* @brief switch to domain mode
*
*/
typedef enum {
HOST_DOMAIN = 1,
BACKUP_DOMAIN = 0,
} DomainMode;
/**
* @brief Serialize and send connect packet.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
static int _mqtt_connect(QcloudIotClient *client)
{
IOT_FUNC_ENTRY;
uint8_t session_present, connack_rc;
int rc, packet_len;
// TCP or TLS network connect
rc = client->network_stack.connect(&(client->network_stack));
if (rc) {
IOT_FUNC_EXIT_RC(rc);
}
// send MQTT CONNECT packet
HAL_MutexLock(client->lock_write_buf);
packet_len = mqtt_connect_packet_serialize(client->write_buf, client->write_buf_size, &client->options);
if (packet_len > 0) {
rc = send_mqtt_packet(client, packet_len);
} else {
rc = packet_len == MQTT_ERR_SHORT_BUFFER ? QCLOUD_ERR_BUF_TOO_SHORT : QCLOUD_ERR_FAILURE;
}
HAL_MutexUnlock(client->lock_write_buf);
if (rc) {
IOT_FUNC_EXIT_RC(rc);
}
// recv MQTT CONNACK packet
rc = qcloud_iot_mqtt_wait_for_read(client, CONNACK);
if (rc) {
IOT_FUNC_EXIT_RC(rc);
}
rc = mqtt_connack_packet_deserialize(client->read_buf, client->read_buf_size, &session_present, &connack_rc);
if (rc) {
IOT_FUNC_EXIT_RC(rc);
}
if (CONNACK_CONNECTION_ACCEPTED != connack_rc) {
IOT_FUNC_EXIT_RC(connack_rc);
}
// set connect state
set_client_conn_state(client, CONNECTED);
HAL_MutexLock(client->lock_generic);
client->was_manually_disconnected = client->is_ping_outstanding = 0;
IOT_Timer_Countdown(&client->ping_timer, client->options.keep_alive_interval);
HAL_MutexUnlock(client->lock_generic);
IOT_FUNC_EXIT_RC(rc);
}
/**
* @brief switch domain
*
* @param[in,out] client pointer to mqtt client
* @param[in] mode @see DomainMode
* @return @see IotReturnCode
*/
static void _mqtt_switch_domain(QcloudIotClient *client, DomainMode mode)
{
if (mode == BACKUP_DOMAIN && client->backup_host) {
HAL_Snprintf(client->host_addr, HOST_STR_LENGTH, "%s.%s", client->device_info->product_id, client->backup_host);
client->network_stack.host = client->host_addr;
} else if (mode == HOST_DOMAIN) {
HAL_Snprintf(client->host_addr, HOST_STR_LENGTH, "%s.%s", client->device_info->product_id, client->main_host);
client->network_stack.host = client->host_addr;
}
}
/**
* @brief set connect host
*
* @param[in,out] client pointer to mqtt client
* @param[in] srv_ip server ip string
* @return @see IotReturnCode
*/
static int _mqtt_set_srv_ip(QcloudIotClient *client, const char *srv_ip)
{
int size = HAL_Snprintf(client->host_addr, HOST_STR_LENGTH, "%s", srv_ip);
if (size < 0 || size > HOST_STR_LENGTH - 1) {
Log_e("gen host name failed: %d", size);
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
} else {
client->network_stack.host = client->host_addr;
Log_i("using HOST IP : %s", srv_ip);
}
return QCLOUD_RET_SUCCESS;
}
/**
* @brief Connect MQTT server.
*
* @param[in,out] client pointer to mqtt client
* @param[in] mode when connect error. Choose different connection strategies according to the mode
* @return @see IotReturnCode
*/
int qcloud_iot_mqtt_connect(QcloudIotClient *client, ConnMode mode)
{
IOT_FUNC_ENTRY;
int rc = 0;
// check connection state first
if (get_client_conn_state(client)) {
IOT_FUNC_EXIT_RC(QCLOUD_RET_MQTT_ALREADY_CONNECTED);
}
int connect_fail_cnt = 0;
int max_connect_fail_switch_cnt = (mode == FIRST_CONNECT) ? 3 : 1;
while ((rc = _mqtt_connect(client))) {
client->network_stack.disconnect(&(client->network_stack));
connect_fail_cnt++;
if (connect_fail_cnt == max_connect_fail_switch_cnt && client->backup_host) {
_mqtt_switch_domain(client, BACKUP_DOMAIN);
continue;
}
if (connect_fail_cnt > max_connect_fail_switch_cnt) {
if (!client->get_next_host_ip) {
break;
}
const char *srv_ip = client->get_next_host_ip();
if (!srv_ip) {
break;
}
_mqtt_set_srv_ip(client, srv_ip);
}
}
// In reconnection mode, the connection is successfully switched back to the primary domain name
if (!rc && connect_fail_cnt && mode == RECONNECT) {
_mqtt_switch_domain(client, HOST_DOMAIN);
}
IOT_FUNC_EXIT_RC(rc);
}
/**
* @brief Reconnect MQTT server.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int qcloud_iot_mqtt_attempt_reconnect(QcloudIotClient *client)
{
IOT_FUNC_ENTRY;
int rc = 0;
Log_i("attempt to reconnect...");
if (get_client_conn_state(client)) {
IOT_FUNC_EXIT_RC(QCLOUD_RET_MQTT_ALREADY_CONNECTED);
}
rc = qcloud_iot_mqtt_connect(client, RECONNECT);
if (!get_client_conn_state(client)) {
IOT_FUNC_EXIT_RC(rc);
}
#if 0 // clean session is 0, user don't need resubscribe only if mqtt server error.
if (!client->options.clean_session) {
IOT_FUNC_EXIT_RC(QCLOUD_RET_MQTT_RECONNECTED);
}
#endif
rc = qcloud_iot_mqtt_resubscribe(client);
if (rc) {
IOT_FUNC_EXIT_RC(rc);
}
IOT_FUNC_EXIT_RC(QCLOUD_RET_MQTT_RECONNECTED);
}
/**
* @brief Disconnect MQTT server.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int qcloud_iot_mqtt_disconnect(QcloudIotClient *client)
{
IOT_FUNC_ENTRY;
int rc, packet_len = 0;
if (!get_client_conn_state(client)) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
HAL_MutexLock(client->lock_write_buf);
packet_len = mqtt_disconnect_packet_serialize(client->write_buf, client->write_buf_size);
if (packet_len > 0) {
rc = send_mqtt_packet(client, packet_len);
} else {
rc = packet_len == MQTT_ERR_SHORT_BUFFER ? QCLOUD_ERR_BUF_TOO_SHORT : QCLOUD_ERR_FAILURE;
}
HAL_MutexUnlock(client->lock_write_buf);
if (rc) {
IOT_FUNC_EXIT_RC(rc);
}
client->network_stack.disconnect(&(client->network_stack));
set_client_conn_state(client, NOTCONNECTED);
client->was_manually_disconnected = 1;
Log_i("mqtt disconnect!");
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
/**
* @brief Serialize and send pingreq packet.
*
* @param[in,out] client pointer to mqtt client
* @param[in] try_times if failed, retry times
* @return @see IotReturnCode
*/
int qcloud_iot_mqtt_pingreq(QcloudIotClient *client, int try_times)
{
IOT_FUNC_ENTRY;
int rc, packet_len, i = 0;
HAL_MutexLock(client->lock_write_buf);
packet_len = mqtt_pingreq_packet_serialize(client->write_buf, client->write_buf_size);
if (packet_len > 0) {
do {
rc = send_mqtt_packet(client, packet_len);
} while (rc && (i++ < try_times));
} else {
rc = packet_len == MQTT_ERR_SHORT_BUFFER ? QCLOUD_ERR_BUF_TOO_SHORT : QCLOUD_ERR_FAILURE;
}
HAL_MutexUnlock(client->lock_write_buf);
IOT_FUNC_EXIT_RC(rc);
}

View File

@@ -0,0 +1,397 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file mqtt_client_publish.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-28
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
* </table>
*/
#include "mqtt_client.h"
/**
* @brief Push pub info to list for republish.
*
* @param[in,out] client pointer to mqtt_client
* @param[in] packet_len packet len of publish packet
* @param[in] packet_id packet id
* @param[out] node list node
* @return @see IotReturnCode
*/
static int _push_pub_info_to_list(QcloudIotClient *client, int packet_len, uint16_t packet_id, void **node)
{
IOT_FUNC_ENTRY;
void *list = client->list_pub_wait_ack;
QcloudIotPubInfo *repub_info = NULL;
// construct republish info
repub_info = (QcloudIotPubInfo *)HAL_Malloc(sizeof(QcloudIotPubInfo) + packet_len);
if (!repub_info) {
Log_e("memory malloc failed!");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
repub_info->buf = (uint8_t *)repub_info + sizeof(QcloudIotPubInfo);
repub_info->len = packet_len;
repub_info->packet_id = packet_id;
memcpy(repub_info->buf, client->write_buf, packet_len); // save the whole packet
IOT_Timer_CountdownMs(&repub_info->pub_start_time, client->command_timeout_ms);
// push republish info to list
*node = utils_list_push(list, repub_info);
if (!*node) {
HAL_Free(repub_info);
Log_e("list push failed! Check the list len!");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
/**
* @brief Check pub wait list timeout.
*
* @param[in,out] list pointer to pub wait ack list.
* @param[in] node pointer to list node
* @param[in] val pointer to value, @see QcloudIotPubInfo
* @param[in] usr_data @see QcloudIotClient
* @return @see UtilsListResult
*/
static UtilsListResult _pub_wait_list_process_check_timeout(void *list, void *node, void *val, void *usr_data)
{
IOT_FUNC_ENTRY;
MQTTEventMsg msg;
QcloudIotPubInfo *repub_info = (QcloudIotPubInfo *)val;
QcloudIotClient *client = (QcloudIotClient *)usr_data;
// check the request if timeout or not
if (IOT_Timer_Remain(&repub_info->pub_start_time) > 0) {
IOT_FUNC_EXIT_RC(LIST_TRAVERSE_CONTINUE);
}
// notify timeout event
if (client->event_handle.h_fp) {
msg.event_type = MQTT_EVENT_PUBLISH_TIMEOUT;
msg.msg = (void *)(uintptr_t)repub_info->packet_id;
client->event_handle.h_fp(client, client->event_handle.context, &msg);
}
utils_list_remove(list, node);
IOT_FUNC_EXIT_RC(LIST_TRAVERSE_CONTINUE);
}
/**
* @brief Remove info from pub wait list.
*
* @param[in,out] list pointer to pub wait ack list.
* @param[in] node pointer to list node
* @param[in] val pointer to value, @see QcloudIotPubInfo
* @param[in] usr_data pointer to packet id
* @return @see UtilsListResult
*/
static UtilsListResult _pub_wait_list_process_remove_info(void *list, void *node, void *val, void *usr_data)
{
IOT_FUNC_ENTRY;
QcloudIotPubInfo *repub_info = (QcloudIotPubInfo *)val;
if (repub_info->packet_id == *((uint16_t *)usr_data)) {
utils_list_remove(list, node);
IOT_FUNC_EXIT_RC(LIST_TRAVERSE_BREAK);
}
IOT_FUNC_EXIT_RC(LIST_TRAVERSE_CONTINUE);
}
/**
* @brief Remove node signed with packet id from publish ACK wait list.
*
* @param[in,out] client pointer to mqtt_client
* @param[in] packet_id packet id
*/
static void _remove_pub_info_from_list(QcloudIotClient *client, uint16_t packet_id)
{
utils_list_process(client->list_pub_wait_ack, LIST_HEAD, _pub_wait_list_process_remove_info, &packet_id);
}
/**
* @brief Check if topic match
*
* @param[in] topic_filter topic name filter, wildcard is supported
* @param[in] topic_name topic name, no wildcard
* @param[in] topic_name_len length of topic name
* @return 1 for matched, 0 for no matched
*
* @note assume topic filter and name is in correct format;
* # can only be at end;
* + and # can only be next to separator.
*/
static uint8_t _is_topic_matched(char *topic_filter, const char *topic_name, uint16_t topic_name_len)
{
char *curf = topic_filter;
char *curn = (char *)topic_name;
char *curn_end = curn + topic_name_len;
while (*curf && curn < curn_end) {
if (*curn == '/' && *curf != '/')
break;
if (*curf != '+' && *curf != '#' && *curf != *curn)
break;
if (*curf == '+') { // skip until we meet the next separator, or end of string
char *nextpos = curn + 1;
while (nextpos < curn_end && *nextpos != '/') nextpos = ++curn + 1;
} else if (*curf == '#')
curn = curn_end - 1; // skip until end of string
curf++;
curn++;
};
return (curn == curn_end) && (*curf == '\0');
}
/**
* @brief deliver the message to user callback
*
* @param[in,out] client pointer to mqtt client
* @param[in] message message to deliver, @see MQTTMessage
*/
static void _deliver_message(QcloudIotClient *client, MQTTMessage *message)
{
int i;
MQTTEventMsg msg;
HAL_MutexLock(client->lock_generic);
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
if (client->sub_handles[i].topic_filter &&
_is_topic_matched((char *)client->sub_handles[i].topic_filter, message->topic_name, message->topic_len)) {
if (client->sub_handles[i].params.on_message_handler) {
HAL_MutexUnlock(client->lock_generic);
// if found, then handle it, then return
client->sub_handles[i].params.on_message_handler(client, message,
client->sub_handles[i].params.user_data);
return;
}
}
}
HAL_MutexUnlock(client->lock_generic);
/* Message handler not found for topic */
/* May be we do not care change FAILURE use SUCCESS*/
Log_d("no matching any topic, call default handle function");
if (client->event_handle.h_fp) {
msg.event_type = MQTT_EVENT_PUBLISH_RECEIVED;
msg.msg = message;
client->event_handle.h_fp(client, client->event_handle.context, &msg);
}
}
#ifdef MQTT_RMDUP_MSG_ENABLED
/**
* @brief Check if packet id repeat.
*
* @param[in,out] client pointer to mqtt client
* @param[in] packet_id packet_id
* @return < 0 for failed.
*/
static int _get_packet_id_repeat_buf(QcloudIotClient *client, uint16_t packet_id)
{
int i;
for (i = 0; i < MQTT_MAX_REPEAT_BUF_LEN; ++i) {
if (packet_id == client->repeat_packet_id_buf[i]) {
return packet_id;
}
}
return -1;
}
/**
* @brief Add packet id, if buf full, then overwrite.
*
* @param[in,out] client pointer to mqtt client
* @param[in] packet_id packet_id
*/
static void _add_packet_id_to_repeat_buf(QcloudIotClient *client, uint16_t packet_id)
{
if (_get_packet_id_repeat_buf(client, packet_id) >= 0) {
return;
}
client->repeat_packet_id_buf[client->current_packet_id_cnt++] = packet_id;
client->current_packet_id_cnt %= MQTT_MAX_REPEAT_BUF_LEN;
}
#endif
/**
* @brief Serialize and send publish packet.
*
* @param[in,out] client pointer to mqtt_client
* @param[in] topic_name topic to publish
* @param[in] params publish params
* @return >=0 for packet id, < 0 for failed @see IotReturnCode
*/
int qcloud_iot_mqtt_publish(QcloudIotClient *client, const char *topic_name, const PublishParams *params)
{
IOT_FUNC_ENTRY;
int rc, packet_len;
MQTTPublishFlags flags;
void *node = NULL;
uint16_t packet_id = 0;
if (params->qos > QOS0) {
packet_id = get_next_packet_id(client);
}
Log_d("publish qos=%d|packet_id=%d|topic_name=%s|payload=%s", params->qos, packet_id, topic_name,
STRING_PTR_PRINT_SANITY_CHECK((char *)params->payload));
flags.dup = params->dup;
flags.qos = params->qos;
flags.retain = params->retain;
// serialize packet
HAL_MutexLock(client->lock_write_buf);
packet_len = mqtt_publish_packet_serialize(client->write_buf, client->write_buf_size, &flags, packet_id, topic_name,
params->payload, params->payload_len);
if (packet_len < 0) {
HAL_MutexUnlock(client->lock_write_buf);
rc = packet_len == MQTT_ERR_SHORT_BUFFER ? QCLOUD_ERR_BUF_TOO_SHORT : QCLOUD_ERR_FAILURE;
IOT_FUNC_EXIT_RC(rc);
}
if (params->qos > QOS0) {
rc = _push_pub_info_to_list(client, packet_len, packet_id, &node);
if (rc) {
Log_e("push publish info failed!");
HAL_MutexUnlock(client->lock_write_buf);
IOT_FUNC_EXIT_RC(rc);
}
}
// send the publish packet
rc = send_mqtt_packet(client, packet_len);
HAL_MutexUnlock(client->lock_write_buf);
if (rc) {
if (params->qos > QOS0) {
utils_list_remove(client->list_pub_wait_ack, node);
}
IOT_FUNC_EXIT_RC(rc);
}
IOT_FUNC_EXIT_RC(packet_id);
}
/**
* @brief Deserialize publish packet and deliver_message.
*
* @param[in,out] client pointer to mqtt_client
* @param[in] params publish params
* @return >=0 for packet id, < 0 for failed @see IotReturnCode
*/
int qcloud_iot_mqtt_handle_publish(QcloudIotClient *client)
{
IOT_FUNC_ENTRY;
int rc, packet_len = 0;
MQTTPublishFlags flags;
MQTTMessage msg;
rc = mqtt_publish_packet_deserialize(client->read_buf, client->read_buf_size, &flags, &msg.packet_id,
&msg.topic_name, &msg.topic_len, &msg.payload, &msg.payload_len);
if (rc) {
IOT_FUNC_EXIT_RC(rc);
}
msg.qos = flags.qos;
if (QOS0 == msg.qos) {
// No further processing required for QOS0
_deliver_message(client, &msg);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
// only Qos 1 is support
#ifdef MQTT_RMDUP_MSG_ENABLED
// check if packet_id has been received before
if (_get_packet_id_repeat_buf(client, msg.packet_id) < 0)
#endif
{
// deliver to msg callback
_deliver_message(client, &msg);
}
#ifdef MQTT_RMDUP_MSG_ENABLED
// just add packet id
_add_packet_id_to_repeat_buf(client, msg.packet_id);
#endif
// reply with puback
HAL_MutexLock(client->lock_write_buf);
packet_len = mqtt_puback_packet_serialize(client->write_buf, client->write_buf_size, msg.packet_id);
if (packet_len > 0) {
rc = send_mqtt_packet(client, packet_len);
} else {
rc = packet_len == MQTT_ERR_SHORT_BUFFER ? QCLOUD_ERR_BUF_TOO_SHORT : QCLOUD_ERR_FAILURE;
}
HAL_MutexUnlock(client->lock_write_buf);
IOT_FUNC_EXIT_RC(rc);
}
/**
* @brief Deserialize puback packet.
*
* @param[in,out] client pointer to mqtt_client
* @return 0 for success.
*/
int qcloud_iot_mqtt_handle_puback(QcloudIotClient *client)
{
IOT_FUNC_ENTRY;
int rc;
uint16_t packet_id;
MQTTEventMsg msg;
rc = mqtt_puback_packet_deserialize(client->read_buf, client->read_buf_size, &packet_id);
if (rc) {
IOT_FUNC_EXIT_RC(rc);
}
_remove_pub_info_from_list(client, packet_id);
/* notify this event to user callback */
if (client->event_handle.h_fp) {
msg.event_type = MQTT_EVENT_PUBLISH_SUCCESS;
msg.msg = (void *)(uintptr_t)packet_id;
client->event_handle.h_fp(client, client->event_handle.context, &msg);
}
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
/**
* @brief Process puback waiting timout.
*
* @param[in,out] client pointer to mqtt_client
*/
void qcloud_iot_mqtt_check_pub_timeout(QcloudIotClient *client)
{
IOT_FUNC_ENTRY;
utils_list_process(client->list_pub_wait_ack, LIST_HEAD, _pub_wait_list_process_check_timeout, client);
IOT_FUNC_EXIT;
}

View File

@@ -0,0 +1,643 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file mqtt_client_subscribe.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-28
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
* </table>
*/
#include "mqtt_client.h"
/**
* @brief Context of subscribe
*
*/
typedef struct {
uint16_t packet_id;
SubTopicHandle handler;
} QcloudIotSubConext;
/**
* @brief Free topic_filter and user_data
*
* @param[in] handler subtopic handle
*/
static void _clear_sub_handle(SubTopicHandle *handler)
{
if (handler->topic_filter) {
HAL_Free(handler->topic_filter);
handler->topic_filter = NULL;
}
if (handler->params.user_data && handler->params.user_data_free) {
handler->params.user_data_free(handler->params.user_data);
handler->params.user_data = NULL;
}
}
/**
* @brief Push node to subscribe(unsubscribe) ACK wait list.
*
* @param[in,out] client pointer to mqtt_client
* @param[in] packet_len packet len of publish packet
* @param[in] packet_id packet id
* @param[in] type mqtt packet type SUBSCRIBE or UNSUBSCRIBE
* @param[in] handler subtopic handle
* @param[out] node node to push to list
* @return @see IotReturnCode
*/
static int _push_sub_info_to_list(QcloudIotClient *client, int packet_len, uint16_t packet_id, MQTTPacketType type,
const SubTopicHandle *handler, void **node)
{
IOT_FUNC_ENTRY;
void *list = client->list_sub_wait_ack;
QcloudIotSubInfo *sub_info = NULL;
sub_info = (QcloudIotSubInfo *)HAL_Malloc(sizeof(QcloudIotSubInfo) + packet_len);
if (!sub_info) {
Log_e("memory malloc failed!");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
sub_info->buf = (uint8_t *)sub_info + sizeof(QcloudIotSubInfo);
sub_info->len = packet_len;
sub_info->type = type;
sub_info->packet_id = packet_id;
sub_info->handler = *handler;
memcpy(sub_info->buf, client->write_buf, packet_len);
IOT_Timer_CountdownMs(&sub_info->sub_start_time, client->command_timeout_ms);
*node = utils_list_push(list, sub_info);
if (!*node) {
HAL_Free(sub_info);
Log_e("list push failed! Check the list len!");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
/**
* @brief Check pub wait list timeout.
*
* @param[in,out] list pointer to pub wait ack list.
* @param[in] node pointer to list node
* @param[in] val pointer to value, @see QcloudIotPubInfo
* @param[in] usr_data @see QcloudIotClient
* @return @see UtilsListResult
*/
static UtilsListResult _sub_wait_list_process_pop_info(void *list, void *node, void *val, void *usr_data)
{
QcloudIotSubConext *sub_context = (QcloudIotSubConext *)usr_data;
QcloudIotSubInfo *sub_info = (QcloudIotSubInfo *)val;
if (sub_info->packet_id == sub_context->packet_id) {
memcpy(&sub_context->handler, &sub_info->handler, sizeof(SubTopicHandle));
utils_list_remove(list, node);
return LIST_TRAVERSE_BREAK;
}
return LIST_TRAVERSE_CONTINUE;
}
/**
* @brief Check sub wait list timeout.
*
* @param[in,out] list pointer to sub wait ack list.
* @param[in] node pointer to list node
* @param[in] val pointer to value, @see QcloudIotSubInfo
* @param[in] usr_data @see QcloudIotClient
* @return @see UtilsListResult
*/
static UtilsListResult _sub_wait_list_process_check_timeout(void *list, void *node, void *val, void *usr_data)
{
MQTTEventMsg msg;
QcloudIotSubInfo *sub_info = (QcloudIotSubInfo *)val;
QcloudIotClient *client = (QcloudIotClient *)usr_data;
// check the request if timeout or not
if (IOT_Timer_Remain(&sub_info->sub_start_time) > 0) {
return LIST_TRAVERSE_CONTINUE;
}
// notify timeout event
if (client->event_handle.h_fp) {
msg.event_type = SUBSCRIBE == sub_info->type ? MQTT_EVENT_SUBSCRIBE_TIMEOUT : MQTT_EVENT_UNSUBSCRIBE_TIMEOUT;
msg.msg = (void *)(uintptr_t)sub_info->packet_id;
if (sub_info->handler.params.on_sub_event_handler) {
sub_info->handler.params.on_sub_event_handler(client, MQTT_EVENT_SUBSCRIBE_TIMEOUT,
sub_info->handler.params.user_data);
}
client->event_handle.h_fp(client, client->event_handle.context, &msg);
}
_clear_sub_handle(&sub_info->handler);
utils_list_remove(list, node);
return LIST_TRAVERSE_CONTINUE;
}
/**
* @brief Clear sub wait list.
*
* @param[in,out] list pointer to sub wait list.
* @param[in] node pointer to list node
* @param[in] val pointer to value, @see QcloudIotSubInfo
* @param[in] usr_data null
* @return @see UtilsListResult
*/
static UtilsListResult _sub_wait_list_process_clear(void *list, void *node, void *val, void *usr_data)
{
QcloudIotSubInfo *sub_info = (QcloudIotSubInfo *)val;
if (sub_info->type == UNSUBSCRIBE) {
_clear_sub_handle(&sub_info->handler);
}
utils_list_remove(list, node);
return LIST_TRAVERSE_CONTINUE;
}
/**
* @brief Pop node signed with packet id from subscribe ACK wait list, and return the sub handler
*
* @param[in,out] client pointer to mqtt_client
* @param[in] packet_id packet id
* @param[out] sub_handle @see SubTopicHandle
*/
static void _pop_sub_info_from_list(QcloudIotClient *client, uint16_t packet_id, SubTopicHandle *sub_handle)
{
QcloudIotSubConext sub_context = {.packet_id = packet_id};
utils_list_process(client->list_sub_wait_ack, LIST_HEAD, _sub_wait_list_process_pop_info, &sub_context);
memcpy(sub_handle, &sub_context.handler, sizeof(SubTopicHandle));
}
/**
* @brief Remove sub handle when unsubscribe.
*
* @param[in,out] client pointer to mqtt_client
* @param[in] topic_filter topic to remove
* @return true topic exist
* @return false topic no exist
*/
static bool _remove_sub_handle_from_array(QcloudIotClient *client, const char *topic_filter)
{
int i;
bool topic_exists = false;
// remove from message handler array
HAL_MutexLock(client->lock_generic);
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
if ((client->sub_handles[i].topic_filter && !strcmp(client->sub_handles[i].topic_filter, topic_filter)) ||
strstr(topic_filter, "/#") || strstr(topic_filter, "/+")) {
// notify this event to topic subscriber
if (client->sub_handles[i].params.on_sub_event_handler) {
client->sub_handles[i].params.on_sub_event_handler(client, MQTT_EVENT_UNSUBSCRIBE,
client->sub_handles[i].params.user_data);
}
_clear_sub_handle(&client->sub_handles[i]);
// we don't want to break here, if the same topic is registered*with 2 callbacks.Unlikely scenario
topic_exists = true;
}
}
HAL_MutexUnlock(client->lock_generic);
return topic_exists;
}
/**
* @brief Add sub handle when subscribe.
*
* @param[in,out] client pointer to mqtt_client
* @param[in] sub_handle sub_handle to be add to array
* @return true topic exist
* @return false topic no exist
*/
static int _add_sub_handle_to_array(QcloudIotClient *client, const SubTopicHandle *sub_handle)
{
IOT_FUNC_ENTRY;
int i, i_free = -1;
HAL_MutexLock(client->lock_generic);
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
if (client->sub_handles[i].topic_filter) {
if (!strcmp(client->sub_handles[i].topic_filter, sub_handle->topic_filter)) {
i_free = i;
// free the memory before
_clear_sub_handle(&client->sub_handles[i]);
Log_w("Identical topic found: %s", sub_handle->topic_filter);
break;
}
} else {
i_free = i_free == -1 ? i : i_free;
}
}
if (-1 == i_free) {
Log_e("NO more @sub_handles space!");
HAL_MutexUnlock(client->lock_generic);
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
client->sub_handles[i_free] = *sub_handle;
HAL_MutexUnlock(client->lock_generic);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
/**
* @brief Set sub handle status.
*
* @param[in,out] client pointer to mqtt_client
* @param[in] topic_filter topic to set status
* @param[in] status @see SubStatus
*/
static void _set_sub_handle_status_to_array(QcloudIotClient *client, const char *topic_filter, SubStatus status)
{
IOT_FUNC_ENTRY;
int i;
HAL_MutexLock(client->lock_generic);
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
if (client->sub_handles[i].topic_filter) {
if (!strcmp(client->sub_handles[i].topic_filter, topic_filter)) {
client->sub_handles[i].status = status;
break;
}
}
}
HAL_MutexUnlock(client->lock_generic);
IOT_FUNC_EXIT;
}
/**
* @brief Serialize and send subscribe packet.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic to subscribe
* @param[in] params subscribe params
* @return >=0 for packet id, < 0 for failed @see IotReturnCode
*/
int qcloud_iot_mqtt_subscribe(QcloudIotClient *client, const char *topic_filter, const SubscribeParams *params)
{
IOT_FUNC_ENTRY;
int rc, packet_len, qos = params->qos;
uint16_t packet_id;
char *topic_filter_stored;
void *node = NULL;
SubTopicHandle sub_handle;
// topic filter should be valid in the whole sub life
topic_filter_stored = HAL_Malloc(strlen(topic_filter) + 1);
if (!topic_filter_stored) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MALLOC);
}
strncpy(topic_filter_stored, topic_filter, strlen(topic_filter) + 1);
sub_handle.topic_filter = topic_filter_stored;
sub_handle.params = *params;
sub_handle.status = client->default_subscribe ? SUB_ACK_RECEIVED : SUB_ACK_NOT_RECEIVED;
// add sub handle first to process
rc = _add_sub_handle_to_array(client, &sub_handle);
if (rc) {
goto exit;
}
if (client->default_subscribe) {
return 0;
}
packet_id = get_next_packet_id(client);
Log_d("subscribe topic_name=%s|packet_id=%d", topic_filter_stored, packet_id);
// serialize packet
HAL_MutexLock(client->lock_write_buf);
packet_len = mqtt_subscribe_packet_serialize(client->write_buf, client->write_buf_size, packet_id, 1,
&topic_filter_stored, &qos);
if (packet_len < 0) {
HAL_MutexUnlock(client->lock_write_buf);
rc = packet_len == MQTT_ERR_SHORT_BUFFER ? QCLOUD_ERR_BUF_TOO_SHORT : QCLOUD_ERR_FAILURE;
goto exit;
}
// add node into sub ack wait list
rc = _push_sub_info_to_list(client, packet_len, packet_id, SUBSCRIBE, &sub_handle, &node);
if (rc) {
HAL_MutexUnlock(client->lock_write_buf);
goto exit;
}
// send packet
rc = send_mqtt_packet(client, packet_len);
HAL_MutexUnlock(client->lock_write_buf);
if (rc) {
utils_list_remove(client->list_sub_wait_ack, node);
goto exit;
}
IOT_FUNC_EXIT_RC(packet_id);
exit:
_remove_sub_handle_from_array(client, topic_filter_stored);
IOT_FUNC_EXIT_RC(rc);
}
/**
* @brief Deserialize suback packet and return sub result.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int qcloud_iot_mqtt_handle_suback(QcloudIotClient *client)
{
IOT_FUNC_ENTRY;
int rc, count = 0;
uint16_t packet_id = 0;
int granted_qos;
MQTTEventMsg msg = {MQTT_EVENT_SUBSCRIBE_SUCCESS, NULL};
SubTopicHandle sub_handle = {0};
MQTTEventType event_type = MQTT_EVENT_SUBSCRIBE_SUCCESS;
rc = mqtt_suback_packet_deserialize(client->read_buf, client->read_buf_size, 1, &count, &packet_id, &granted_qos);
if (rc) {
IOT_FUNC_EXIT_RC(rc);
}
msg.msg = (void *)(uintptr_t)packet_id;
// pop sub info and get sub handle
_pop_sub_info_from_list(client, packet_id, &sub_handle);
if (!sub_handle.topic_filter) {
Log_w("can't get sub handle from list!");
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS); // in case of resubscribe when reconnect
}
// check return code in SUBACK packet: 0x00(QOS0, SUCCESS),0x01(QOS1, SUCCESS),0x02(QOS2,SUCCESS),0x80(Failure)
if (0x80 == granted_qos) {
msg.event_type = MQTT_EVENT_SUBSCRIBE_NACK;
event_type = MQTT_EVENT_SUBSCRIBE_NACK;
Log_e("MQTT SUBSCRIBE failed, packet_id: %u topic: %s", packet_id, sub_handle.topic_filter);
_remove_sub_handle_from_array(client, sub_handle.topic_filter);
rc = QCLOUD_ERR_MQTT_SUB;
}
if (MQTT_EVENT_SUBSCRIBE_SUCCESS == msg.event_type) {
_set_sub_handle_status_to_array(client, sub_handle.topic_filter, SUB_ACK_RECEIVED);
}
// notify this event to user callback
if (client->event_handle.h_fp) {
client->event_handle.h_fp(client, client->event_handle.context, &msg);
}
// notify this event to topic subscriber
if (sub_handle.params.on_sub_event_handler) {
sub_handle.params.on_sub_event_handler(client, event_type, sub_handle.params.user_data);
}
IOT_FUNC_EXIT_RC(rc);
}
/**
* @brief Serialize and send unsubscribe packet.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic to unsubscribe
* @return >=0 packet id, < 0 for failed @see IotReturnCode
*/
int qcloud_iot_mqtt_unsubscribe(QcloudIotClient *client, const char *topic_filter)
{
IOT_FUNC_ENTRY;
int rc, packet_len;
uint16_t packet_id;
void *node = NULL;
SubTopicHandle sub_handle;
memset(&sub_handle, 0, sizeof(SubTopicHandle));
// remove from sub handle
if (!_remove_sub_handle_from_array(client, topic_filter)) {
Log_w("subscription does not exists: %s", topic_filter);
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_UNSUB_FAIL);
}
// topic filter should be valid in the whole sub life
char *topic_filter_stored = HAL_Malloc(strlen(topic_filter) + 1);
if (!topic_filter_stored) {
Log_e("malloc failed");
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
strncpy(topic_filter_stored, topic_filter, strlen(topic_filter) + 1);
sub_handle.topic_filter = topic_filter_stored;
packet_id = get_next_packet_id(client);
Log_d("unsubscribe topic_name=%s|packet_id=%d", topic_filter_stored, packet_id);
HAL_MutexLock(client->lock_write_buf);
packet_len = mqtt_unsubscribe_packet_serialize(client->write_buf, client->write_buf_size, packet_id, 1,
&topic_filter_stored);
if (packet_len < 0) {
HAL_MutexUnlock(client->lock_write_buf);
rc = packet_len == MQTT_ERR_SHORT_BUFFER ? QCLOUD_ERR_BUF_TOO_SHORT : QCLOUD_ERR_FAILURE;
goto exit;
}
// add node into sub ack wait list
rc = _push_sub_info_to_list(client, packet_len, packet_id, UNSUBSCRIBE, &sub_handle, &node);
if (rc) {
Log_e("push unsubscribe info failed!");
HAL_MutexUnlock(client->lock_write_buf);
goto exit;
}
/* send the unsubscribe packet */
rc = send_mqtt_packet(client, packet_len);
HAL_MutexUnlock(client->lock_write_buf);
if (rc) {
utils_list_remove(client->list_sub_wait_ack, node);
goto exit;
}
IOT_FUNC_EXIT_RC(packet_id);
exit:
_clear_sub_handle(&sub_handle);
IOT_FUNC_EXIT_RC(rc);
}
/**
* @brief Deserialize unsuback packet and remove from list.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int qcloud_iot_mqtt_handle_unsuback(QcloudIotClient *client)
{
IOT_FUNC_ENTRY;
int rc;
uint16_t packet_id = 0;
SubTopicHandle sub_handle = {0};
MQTTEventMsg msg;
rc = mqtt_unsuback_packet_deserialize(client->read_buf, client->read_buf_size, &packet_id);
if (rc) {
IOT_FUNC_EXIT_RC(rc);
}
_pop_sub_info_from_list(client, packet_id, &sub_handle);
_clear_sub_handle(&sub_handle);
if (client->event_handle.h_fp) {
msg.event_type = MQTT_EVENT_UNSUBSCRIBE_SUCCESS;
msg.msg = (void *)(uintptr_t)packet_id;
client->event_handle.h_fp(client, client->event_handle.context, &msg);
}
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
/**
* @brief Process suback waiting timeout.
*
* @param[in,out] client pointer to mqtt client
*/
void qcloud_iot_mqtt_check_sub_timeout(QcloudIotClient *client)
{
IOT_FUNC_ENTRY;
utils_list_process(client->list_sub_wait_ack, LIST_HEAD, _sub_wait_list_process_check_timeout, client);
IOT_FUNC_EXIT;
}
/**
* @brief Resubscribe topic when reconnect.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int qcloud_iot_mqtt_resubscribe(QcloudIotClient *client)
{
IOT_FUNC_ENTRY;
int rc, packet_len, qos, itr = 0;
uint16_t packet_id;
SubTopicHandle *sub_handle;
for (itr = 0; itr < MAX_MESSAGE_HANDLERS; itr++) {
sub_handle = &client->sub_handles[itr];
if (!sub_handle->topic_filter) {
continue;
}
packet_id = get_next_packet_id(client);
Log_d("subscribe topic_name=%s|packet_id=%d", sub_handle->topic_filter, packet_id);
HAL_MutexLock(client->lock_write_buf);
qos = sub_handle->params.qos;
packet_len = mqtt_subscribe_packet_serialize(client->write_buf, client->write_buf_size, packet_id, 1,
&sub_handle->topic_filter, &qos);
if (packet_len < 0) {
HAL_MutexUnlock(client->lock_write_buf);
continue;
}
// send packet
rc = send_mqtt_packet(client, packet_len);
HAL_MutexUnlock(client->lock_write_buf);
if (rc) {
IOT_FUNC_EXIT_RC(rc);
}
}
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
/**
* @brief Return if topic is sub ready.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter
* @return true for ready
* @return false for not ready
*/
bool qcloud_iot_mqtt_is_sub_ready(QcloudIotClient *client, const char *topic_filter)
{
IOT_FUNC_ENTRY;
int i = 0;
HAL_MutexLock(client->lock_generic);
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
if ((client->sub_handles[i].topic_filter && !strcmp(client->sub_handles[i].topic_filter, topic_filter)) ||
strstr(topic_filter, "/#") || strstr(topic_filter, "/+")) {
HAL_MutexUnlock(client->lock_generic);
return client->sub_handles[i].status == SUB_ACK_RECEIVED;
}
}
HAL_MutexUnlock(client->lock_generic);
IOT_FUNC_EXIT_RC(false);
}
/**
* @brief Get usr data, usr should handle lock/unlock usrdata itself in callback and caller.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic_filter topic filter
* @return NULL or user data
*/
void *qcloud_iot_mqtt_get_subscribe_usr_data(QcloudIotClient *client, const char *topic_filter)
{
IOT_FUNC_ENTRY;
int i = 0;
HAL_MutexLock(client->lock_generic);
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
if ((client->sub_handles[i].topic_filter && !strcmp(client->sub_handles[i].topic_filter, topic_filter)) ||
strstr(topic_filter, "/#") || strstr(topic_filter, "/+")) {
HAL_MutexUnlock(client->lock_generic);
return client->sub_handles[i].params.user_data;
}
}
HAL_MutexUnlock(client->lock_generic);
IOT_FUNC_EXIT_RC(NULL);
}
/**
* @brief Clear sub handle array.
*
* @param[in,out] client pointer to mqtt client
*/
void qcloud_iot_mqtt_sub_handle_array_clear(QcloudIotClient *client)
{
IOT_FUNC_ENTRY;
int i;
for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
/* notify this event to topic subscriber */
if (client->sub_handles[i].topic_filter && client->sub_handles[i].params.on_sub_event_handler) {
client->sub_handles[i].params.on_sub_event_handler(client, MQTT_EVENT_CLIENT_DESTROY,
client->sub_handles[i].params.user_data);
}
_clear_sub_handle(&client->sub_handles[i]);
}
IOT_FUNC_EXIT;
}
/**
* @brief Clear suback wait list and clear sub handle.
*
* @param[in,out] client pointer to mqtt client
*/
void qcloud_iot_mqtt_suback_wait_list_clear(QcloudIotClient *client)
{
IOT_FUNC_ENTRY;
utils_list_process(client->list_sub_wait_ack, LIST_HEAD, _sub_wait_list_process_clear, client);
IOT_FUNC_EXIT;
}

View File

@@ -0,0 +1,553 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file mqtt_client_yield.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-05-28
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-05-28 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>fix code standard of IotReturnCode and QcloudIotClient
* </table>
*/
#include "mqtt_client.h"
/**
* @brief Remain waiting time after MQTT data is received (unit: ms)
*
*/
#define QCLOUD_IOT_MQTT_MAX_REMAIN_WAIT_MS (100)
/**
* @brief Read one byte from network for mqtt packet header except remaining length.
*
* @param[in,out] client pointer to mqtt_client
* @param[in] timeout_ms timeout to read
* @param[out] packet_type packet type to get
* @param[in,out] pptr pointer to buf pointer
* @return @see IotReturnCode
*/
static int _read_packet_header(QcloudIotClient *client, uint32_t timeout_ms, uint8_t *packet_type, uint8_t **pptr)
{
int rc = 0;
size_t read_len = 0;
// refresh timeout
timeout_ms = timeout_ms <= 0 ? 1 : timeout_ms;
rc = client->network_stack.read(&(client->network_stack), *pptr, 1, timeout_ms, &read_len);
if (rc == QCLOUD_ERR_SSL_NOTHING_TO_READ || rc == QCLOUD_ERR_TCP_NOTHING_TO_READ) {
return QCLOUD_ERR_MQTT_NOTHING_TO_READ;
}
// get packet type
*packet_type = (**pptr & 0xf0) >> 4;
(*pptr)++;
return rc;
}
/**
* @brief Read 1~4 bytes from network for mqtt packet header remaining length.
*
* @param[in,out] client pointer to mqtt_client
* @param[in] timeout_ms timeout to read
* @param[out] plen read len
* @param[in,out] pptr pointer to buf pointer
* @return int
*/
static int _read_packet_remaining_len(QcloudIotClient *client, uint32_t timeout_ms, uint32_t *plen, uint8_t **pptr)
{
IOT_FUNC_ENTRY;
uint8_t *buf = *pptr;
uint8_t c = 0;
uint32_t multiplier = 1;
uint32_t count = 0;
uint32_t len = 0;
size_t rlen = 0;
// refresh timeout
timeout_ms = timeout_ms <= 0 ? 1 : timeout_ms;
timeout_ms += QCLOUD_IOT_MQTT_MAX_REMAIN_WAIT_MS;
do {
if (++count > MAX_NO_OF_REMAINING_LENGTH_BYTES) {
/* bad data */
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_PACKET_READ)
}
if (client->network_stack.read(&(client->network_stack), buf, 1, timeout_ms, &rlen)) {
/* The value argument is the important value. len is just used temporarily
* and never used by the calling function for anything else */
IOT_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
c = *buf++;
len += (c & 127) * multiplier;
multiplier *= 128;
} while (c & 128);
if (plen) {
*plen = len;
}
*pptr += count;
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
/**
* @brief Discard the packet for mqtt read buf not enough.
*
* @param[in,out] client pointer to mqtt_client
* @param[in] timeout_ms timeout to read
* @param[in] rem_len remaining length to read
*/
static void _discard_packet_for_short_buf(QcloudIotClient *client, uint32_t timeout_ms, uint32_t rem_len)
{
int rc;
size_t bytes_to_be_read, total_bytes_read = 0;
size_t read_len;
timeout_ms = timeout_ms <= 0 ? 1 : timeout_ms;
timeout_ms += QCLOUD_IOT_MQTT_MAX_REMAIN_WAIT_MS;
bytes_to_be_read = client->read_buf_size;
do {
rc = client->network_stack.read(&(client->network_stack), client->read_buf, bytes_to_be_read, timeout_ms,
&read_len);
if (rc) {
break;
}
total_bytes_read += read_len;
bytes_to_be_read =
(rem_len - total_bytes_read) >= client->read_buf_size ? client->read_buf_size : rem_len - total_bytes_read;
} while (total_bytes_read < rem_len);
}
/**
* @brief Read rem_len bytes from network for mqtt packet payload.
*
* @param[in,out] client pointer to mqtt_client
* @param[in] timeout_ms timeout to read
* @param[in] rem_len length to read
* @param[out] buf buffer to save payload
* @return @see IotReturnCode
*/
static int _read_packet_payload(QcloudIotClient *client, uint32_t timeout_ms, uint32_t rem_len, uint8_t *buf)
{
int rc;
size_t read_len = 0;
timeout_ms = timeout_ms <= 0 ? 1 : timeout_ms;
timeout_ms += QCLOUD_IOT_MQTT_MAX_REMAIN_WAIT_MS;
rc = client->network_stack.read(&(client->network_stack), buf, rem_len, timeout_ms, &read_len);
if (rc) {
return rc;
}
if (read_len != rem_len) {
#ifdef AUTH_WITH_NO_TLS
return QCLOUD_ERR_TCP_READ_TIMEOUT;
#else
return QCLOUD_ERR_SSL_READ_TIMEOUT;
#endif
}
return QCLOUD_RET_SUCCESS;
}
/**
* @brief Read MQTT packet from network stack
*
* @param[in,out] client pointer to mqtt_client
* @param[in,out] timer timeout timer
* @param[out] packet_type MQTT packet type
* @return @see IotReturnCode
*
* @note
* 1. read 1st byte in fixed header and check if valid
* 2. read the remaining length
* 3. read payload according to remaining length
*/
static int _read_mqtt_packet(QcloudIotClient *client, QcloudIotTimer *timer, uint8_t *packet_type)
{
IOT_FUNC_ENTRY;
int rc = 0;
uint32_t rem_len = 0, packet_len = 0;
uint8_t *packet_read_buf = client->read_buf;
// 1. read 1st byte in fixed header and check if valid
rc = _read_packet_header(client, IOT_Timer_Remain(timer), packet_type, &packet_read_buf);
if (rc) {
IOT_FUNC_EXIT_RC(rc);
}
// 2. read the remaining length
rc = _read_packet_remaining_len(client, IOT_Timer_Remain(timer), &rem_len, &packet_read_buf);
if (rc) {
IOT_FUNC_EXIT_RC(rc);
}
// if read buffer is not enough to read the remaining length, discard the packet
packet_len = packet_read_buf - client->read_buf + rem_len;
if (packet_len >= client->read_buf_size) {
_discard_packet_for_short_buf(client, IOT_Timer_Remain(timer), rem_len);
Log_e("MQTT Recv buffer not enough: %lu < %d", client->read_buf_size, rem_len);
IOT_FUNC_EXIT_RC(QCLOUD_ERR_BUF_TOO_SHORT);
}
// 3. read payload according to remaining length
if (rem_len > 0) {
rc = _read_packet_payload(client, IOT_Timer_Remain(timer), rem_len, packet_read_buf);
}
IOT_FUNC_EXIT_RC(rc);
}
/**
* @brief Reset ping counter & ping timer.
*
* @param[in,out] client pointer to mqtt_client
*/
static void _handle_pingresp_packet(QcloudIotClient *client)
{
IOT_FUNC_ENTRY;
HAL_MutexLock(client->lock_generic);
client->is_ping_outstanding = 0;
IOT_Timer_Countdown(&client->ping_timer, client->options.keep_alive_interval);
HAL_MutexUnlock(client->lock_generic);
IOT_FUNC_EXIT;
}
/**
* @brief Read a mqtt packet from network and handle it.
*
* @param[in,out] client pointer to mqtt_client
* @param[in] timer timer for operation
* @param[out] packet_type packet type of packet read
* @return @see IotReturnCode
*/
static int _cycle_for_read(QcloudIotClient *client, QcloudIotTimer *timer, uint8_t *packet_type)
{
IOT_FUNC_ENTRY;
int rc;
/* read the socket, see what work is due */
rc = _read_mqtt_packet(client, timer, packet_type);
if (QCLOUD_ERR_MQTT_NOTHING_TO_READ == rc) {
/* Nothing to read, not a cycle failure */
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
if (rc) {
IOT_FUNC_EXIT_RC(rc);
}
switch (*packet_type) {
case CONNACK:
case PUBREC:
case PUBREL:
case PUBCOMP:
case PINGRESP:
break;
case PUBACK:
rc = qcloud_iot_mqtt_handle_puback(client);
break;
case SUBACK:
rc = qcloud_iot_mqtt_handle_suback(client);
break;
case UNSUBACK:
rc = qcloud_iot_mqtt_handle_unsuback(client);
break;
case PUBLISH:
rc = qcloud_iot_mqtt_handle_publish(client);
break;
default:
// Either unknown packet type or failure occurred should not happen
IOT_FUNC_EXIT_RC(QCLOUD_ERR_RX_MESSAGE_INVAL);
break;
}
switch (*packet_type) {
// Recv below msgs are all considered as PING OK
case PUBACK:
case SUBACK:
case UNSUBACK:
case PINGRESP:
_handle_pingresp_packet(client);
break;
// Recv downlink pub means link is OK but we still need to send PING request
case PUBLISH:
HAL_MutexLock(client->lock_generic);
client->is_ping_outstanding = 0;
HAL_MutexUnlock(client->lock_generic);
break;
}
IOT_FUNC_EXIT_RC(rc);
}
/**
* @brief Set reconnect wait interval if auto conect is enable.
*
* @param[in,out] client pointer to mqtt client
*/
static void _set_reconnect_wait_interval(QcloudIotClient *client)
{
client->counter_network_disconnected++;
if (client->auto_connect_enable) {
srand(IOT_Timer_CurrentSec());
// range: 1000 - 2000 ms, in 10ms unit
client->current_reconnect_wait_interval = (rand() % 100 + 100) * 10;
IOT_Timer_CountdownMs(&(client->reconnect_delay_timer), client->current_reconnect_wait_interval);
}
}
/**
* @brief Handle disconnect if connection is error.
*
* @param[in,out] client pointer to mqtt client
*/
static void _handle_disconnect(QcloudIotClient *client)
{
int rc;
MQTTEventMsg msg;
if (!get_client_conn_state(client)) {
_set_reconnect_wait_interval(client);
return;
}
rc = qcloud_iot_mqtt_disconnect(client);
// disconnect network stack by force
if (rc) {
client->network_stack.disconnect(&(client->network_stack));
set_client_conn_state(client, NOTCONNECTED);
}
Log_e("disconnect MQTT for some reasons..");
// notify event
if (client->event_handle.h_fp) {
msg.event_type = MQTT_EVENT_DISCONNECT;
msg.msg = NULL;
client->event_handle.h_fp(client, client->event_handle.context, &msg);
}
// exceptional disconnection
client->was_manually_disconnected = 0;
_set_reconnect_wait_interval(client);
}
/**
* @brief Handle reconnect.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
static int _handle_reconnect(QcloudIotClient *client)
{
IOT_FUNC_ENTRY;
int rc = QCLOUD_RET_MQTT_RECONNECTED;
MQTTEventMsg msg;
// reconnect control by delay timer (increase interval exponentially )
if (!IOT_Timer_Expired(&(client->reconnect_delay_timer))) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT);
}
rc = qcloud_iot_mqtt_attempt_reconnect(client);
if (QCLOUD_RET_MQTT_RECONNECTED == rc) {
Log_e("attempt to reconnect success.");
// notify event
if (client->event_handle.h_fp) {
msg.event_type = MQTT_EVENT_RECONNECT;
msg.msg = NULL;
client->event_handle.h_fp(client, client->event_handle.context, &msg);
}
IOT_FUNC_EXIT_RC(rc);
}
Log_e("attempt to reconnect failed, errCode: %d", rc);
client->current_reconnect_wait_interval *= 2;
if (MAX_RECONNECT_WAIT_INTERVAL < client->current_reconnect_wait_interval) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_RECONNECT_TIMEOUT);
}
IOT_Timer_CountdownMs(&(client->reconnect_delay_timer), client->current_reconnect_wait_interval);
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT);
}
/**
* @brief Handle MQTT keep alive (hearbeat with server).
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
static int _mqtt_keep_alive(QcloudIotClient *client)
{
#define MQTT_PING_RETRY_TIMES 2
#define MQTT_PING_SEND_RETRY_TIMES 3
IOT_FUNC_ENTRY;
int rc = 0;
if (0 == client->options.keep_alive_interval) {
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
if (!IOT_Timer_Expired(&client->ping_timer)) {
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
if (client->is_ping_outstanding >= MQTT_PING_RETRY_TIMES) {
// reaching here means we haven't received any MQTT packet for a long time (keep_alive_interval)
Log_e("Fail to recv MQTT msg. Something wrong with the connection.");
_handle_disconnect(client);
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
// there is no ping outstanding - send one
rc = qcloud_iot_mqtt_pingreq(client, MQTT_PING_SEND_RETRY_TIMES);
if (rc) {
// if sending a PING fails, propably the connection is not OK and we decide to disconnect and begin reconnection
// attempts
Log_e("Fail to send PING request. Something wrong with the connection.");
_handle_disconnect(client);
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
// start a timer to wait for PINGRESP from server
HAL_MutexLock(client->lock_generic);
client->is_ping_outstanding++;
IOT_Timer_CountdownMs(&client->ping_timer, client->command_timeout_ms);
HAL_MutexUnlock(client->lock_generic);
Log_d("PING request %u has been sent...", client->is_ping_outstanding);
IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS);
}
/**
* @brief Check connection and keep alive state, read/handle MQTT message in synchronized way.
*
* @param[in,out] client pointer to mqtt client
* @param[in] timeout_ms timeout value (unit: ms) for this operation
*
* @return QCLOUD_RET_SUCCESS when success, QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT when try reconnecting, others @see
* IotReturnCode
*/
int qcloud_iot_mqtt_yield(QcloudIotClient *client, uint32_t timeout_ms)
{
IOT_FUNC_ENTRY;
int rc = QCLOUD_RET_SUCCESS;
uint8_t packet_type;
QcloudIotTimer timer;
// 1. check if manually disconnect
if (!get_client_conn_state(client) && client->was_manually_disconnected == 1) {
IOT_FUNC_EXIT_RC(QCLOUD_RET_MQTT_MANUALLY_DISCONNECTED);
}
// 2. check connection state and if auto reconnect is enabled
if (!get_client_conn_state(client) && client->auto_connect_enable == 0) {
IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
// 3. main loop for packet reading/handling and keep alive maintainance
IOT_Timer_CountdownMs(&timer, timeout_ms);
while (!IOT_Timer_Expired(&timer)) {
// handle reconnect
if (!get_client_conn_state(client)) {
if (client->current_reconnect_wait_interval > MAX_RECONNECT_WAIT_INTERVAL) {
rc = QCLOUD_ERR_MQTT_RECONNECT_TIMEOUT;
break;
}
rc = _handle_reconnect(client);
continue;
}
// read and handle packet
rc = _cycle_for_read(client, &timer, &packet_type);
switch (rc) {
case QCLOUD_RET_SUCCESS:
// check list of wait publish ACK to remove node that is ACKED or timeout
qcloud_iot_mqtt_check_pub_timeout(client);
// check list of wait subscribe(or unsubscribe) ACK to remove node that is ACKED or timeout
qcloud_iot_mqtt_check_sub_timeout(client);
rc = _mqtt_keep_alive(client);
if (rc) {
IOT_FUNC_EXIT_RC(client->auto_connect_enable ? QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT : rc);
}
break;
case QCLOUD_ERR_SSL_READ_TIMEOUT:
case QCLOUD_ERR_SSL_READ:
case QCLOUD_ERR_TCP_PEER_SHUTDOWN:
case QCLOUD_ERR_TCP_READ_TIMEOUT:
case QCLOUD_ERR_TCP_READ_FAIL:
Log_e("network read failed, rc: %d. MQTT Disconnect.", rc);
_handle_disconnect(client);
IOT_FUNC_EXIT_RC(client->auto_connect_enable ? QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT
: QCLOUD_ERR_MQTT_NO_CONN);
break;
default: // others, just return
IOT_FUNC_EXIT_RC(rc);
}
}
IOT_FUNC_EXIT_RC(rc);
}
/**
* @brief Wait read specific mqtt packet, such as connack.
*
* @param[in,out] client pointer to mqtt client
* @param[in] packet_type packet type except to read
* @return @see IotReturnCode
*/
int qcloud_iot_mqtt_wait_for_read(QcloudIotClient *client, uint8_t packet_type)
{
IOT_FUNC_ENTRY;
int rc;
uint8_t read_packet_type = 0;
QcloudIotTimer timer;
IOT_Timer_CountdownMs(&timer, client->command_timeout_ms);
do {
if (IOT_Timer_Expired(&timer)) {
rc = QCLOUD_ERR_MQTT_REQUEST_TIMEOUT;
break;
}
rc = _cycle_for_read(client, &timer, &read_packet_type);
} while (QCLOUD_RET_SUCCESS == rc && read_packet_type != packet_type);
IOT_FUNC_EXIT_RC(rc);
}

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,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 test_mqtt_client.cc
* @brief unittest for mqtt client
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-07
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-07 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>support tls test
* <tr><td>2021-07-12 <td>1.1 <td>fancyxu <td>fix connect twice in 5s error
* </table>
*/
#include "mqtt_client_test.h"
namespace mqtt_client_unittest {
void MqttClientTest::SetUp() {
LogHandleFunc func = DEFAULT_LOG_HANDLE_FUNCS;
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
ASSERT_EQ(HAL_GetDevInfo(&device_info), 0);
MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
init_params.device_info = &device_info;
HAL_SleepMs(5000); // for iot hub can not connect twice in 5 s
client = IOT_MQTT_Construct(&init_params);
ASSERT_NE(client, nullptr);
}
void MqttClientTest::TearDown() {
IOT_MQTT_Destroy(&client);
utils_log_deinit();
}
} // namespace mqtt_client_unittest

View File

@@ -0,0 +1,53 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file mqtt_client_test.h
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-18
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_MQTT_CLIENT_TEST_MQTT_CLIENT_TEST_H_
#define IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_MQTT_CLIENT_TEST_MQTT_CLIENT_TEST_H_
#include "gtest/gtest.h"
#include "qcloud_iot_common.h"
namespace mqtt_client_unittest {
/**
* @brief test fixture of mqtt client
*
*/
class MqttClientTest : public testing::Test {
protected:
void SetUp() override;
void TearDown() override;
void *client = NULL;
DeviceInfo device_info;
};
} // namespace mqtt_client_unittest
#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_COMMON_MQTT_CLIENT_TEST_MQTT_CLIENT_TEST_H_

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 test_mqtt_client.cc
* @brief unittest for mqtt client
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-07
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-07 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2021-07-08 <td>1.1 <td>fancyxu <td>support tls test
* <tr><td>2021-07-12 <td>1.1 <td>fancyxu <td>fix connect twice in 5s error
* <tr><td>2021-07-18 <td>1.2 <td>fancyxu <td>remove MqttClientTest for common use
* </table>
*/
#include <iostream>
#include <string>
#include "mqtt_client_test.h"
namespace mqtt_client_unittest {
/**
* @brief Test subscribe.
*
*/
TEST_F(MqttClientTest, subscribe) {
int wait_cnt;
char topic_name[MAX_SIZE_OF_CLOUD_TOPIC] = {0};
HAL_Snprintf(topic_name, sizeof(topic_name), "%s/%s/data", device_info.product_id, device_info.device_name);
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
/**
* @brief QOS0
*
*/
wait_cnt = 10;
sub_params.qos = QOS0;
ASSERT_GE(IOT_MQTT_Subscribe(client, topic_name, &sub_params), 0);
while (!IOT_MQTT_IsSubReady(client, topic_name) && (wait_cnt > 0)) {
ASSERT_EQ(IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT), 0);
wait_cnt--;
}
ASSERT_NE(wait_cnt, 0);
ASSERT_GE(IOT_MQTT_Unsubscribe(client, topic_name), 0);
ASSERT_EQ(IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT), 0);
/**
* @brief QOS1
*
*/
wait_cnt = 10;
sub_params.qos = QOS1;
ASSERT_GE(IOT_MQTT_Subscribe(client, topic_name, &sub_params), 0);
while (!IOT_MQTT_IsSubReady(client, topic_name) && (wait_cnt > 0)) {
ASSERT_EQ(IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT), 0);
wait_cnt--;
}
ASSERT_NE(wait_cnt, 0);
ASSERT_GE(IOT_MQTT_Unsubscribe(client, topic_name), 0);
ASSERT_EQ(IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT), 0);
/**
* @brief sub sync
*
*/
ASSERT_GE(IOT_MQTT_SubscribeSync(client, topic_name, &sub_params), 0);
ASSERT_GE(IOT_MQTT_Unsubscribe(client, topic_name), 0);
ASSERT_EQ(IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT), 0);
}
/**
* @brief Test publish.
*
*/
TEST_F(MqttClientTest, publish) {
char topic_name[MAX_SIZE_OF_CLOUD_TOPIC] = {0};
HAL_Snprintf(topic_name, sizeof(topic_name), "%s/%s/data", device_info.product_id, device_info.device_name);
char topic_content[] = "{\"action\": \"publish_test\", \"count\": \"0\"}";
PublishParams pub_params = DEFAULT_PUB_PARAMS;
pub_params.payload = topic_content;
pub_params.payload_len = strlen(topic_content);
/**
* @brief QOS0
*
*/
pub_params.qos = QOS0;
ASSERT_GE(IOT_MQTT_Publish(client, topic_name, &pub_params), 0);
/**
* @brief QOS1
*
*/
pub_params.qos = QOS1;
ASSERT_GE(IOT_MQTT_Publish(client, topic_name, &pub_params), 0);
}
/**
* @brief Test clean session.
*
*/
TEST_F(MqttClientTest, clean_session) {
IOT_MQTT_Destroy(&client);
HAL_SleepMs(5000); // for iot hub can not connect twice in 5 s
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
char topic_name[MAX_SIZE_OF_CLOUD_TOPIC] = {0};
ASSERT_EQ(HAL_GetDevInfo(&device_info), 0);
HAL_Snprintf(topic_name, sizeof(topic_name), "%s/%s/data", device_info.product_id, device_info.device_name);
MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
init_params.device_info = &device_info;
init_params.clean_session = 0;
client = IOT_MQTT_Construct(&init_params);
ASSERT_NE(client, nullptr);
ASSERT_GE(IOT_MQTT_SubscribeSync(client, topic_name, &sub_params), 0);
IOT_MQTT_Destroy(&client);
HAL_SleepMs(5000);
init_params.clean_session = 0;
init_params.connect_when_construct = 0;
init_params.default_subscribe = 1;
client = IOT_MQTT_Construct(&init_params);
ASSERT_NE(client, nullptr);
ASSERT_GE(IOT_MQTT_SubscribeSync(client, topic_name, &sub_params), 0);
ASSERT_EQ(IOT_MQTT_Connect(client), 0);
}
} // namespace mqtt_client_unittest

View File

@@ -0,0 +1,13 @@
file(GLOB src_ota_mqtt ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(src_services ${src_services} ${src_ota_mqtt} PARENT_SCOPE)
if( ${CONFIG_COMPILE_SAMPLE} STREQUAL "ON")
file(GLOB src_ota_mqtt_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/ota_mqtt_sample.c)
add_executable(ota_mqtt_sample ${src_ota_mqtt_sample})
target_link_libraries(ota_mqtt_sample ${libsdk})
endif()
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,258 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file broadcast_sample.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-18
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "qcloud_iot_common.h"
#include "utils_log.h"
/**
* @brief MQTT event callback, @see MQTTEventHandleFun
*
* @param[in] client pointer to mqtt client
* @param[in] handle_context context
* @param[in] msg msg
*/
static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
{
MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
uintptr_t packet_id = (uintptr_t)msg->msg;
switch (msg->event_type) {
case MQTT_EVENT_UNDEF:
Log_i("undefined event occur.");
break;
case MQTT_EVENT_DISCONNECT:
Log_i("MQTT disconnect.");
break;
case MQTT_EVENT_RECONNECT:
Log_i("MQTT reconnect.");
break;
case MQTT_EVENT_PUBLISH_RECEIVED:
Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
break;
case MQTT_EVENT_SUBSCRIBE_SUCCESS:
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_SUBSCRIBE_NACK:
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_NACK:
Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_SUCCESS:
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_TIMEOUT:
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_NACK:
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
break;
default:
Log_i("Should NOT arrive here.");
break;
}
}
/**
* @brief Setup MQTT construct parameters.
*
* @param[in,out] initParams @see MQTTInitParams
* @param[in] device_info @see DeviceInfo
*/
static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
{
init_params->device_info = device_info;
init_params->event_handle.h_fp = _mqtt_event_handler;
}
// ----------------------------------------------------------------------------
// OTA function
// ----------------------------------------------------------------------------
int _firmware_download(void *client, char *buf, int buf_len, UtilsJsonValue url, UtilsJsonValue md5sum,
uint32_t file_size, const char *version)
{
Log_i("downloading firmware:url=%.*s|md5sum=%.*s|file_size=%u", url.value_len, url.value, md5sum.value_len,
md5sum.value, file_size);
IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_DOWNLOADING, 0, version);
IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_DOWNLOADING, 50, version);
IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_DOWNLOADING, 100, version);
return 0;
}
void _firmware_burn(void *client, char *buf, int buf_len, const char *version)
{
IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_UPGRADE_BEGIN, 0, version);
IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_UPGRADE_SUCCESS, 0, version);
IOT_OTA_ReportVersion(client, buf, buf_len, version);
}
// ----------------------------------------------------------------------------
// OTA callback
// ----------------------------------------------------------------------------
void _update_firmware_callback(UtilsJsonValue version, UtilsJsonValue url, UtilsJsonValue md5sum, uint32_t file_size,
void *usr_data)
{
char firmware_version[64] = {0};
char buf[256];
int buf_len = sizeof(buf);
Log_i("recv firmware: version=%.*s|url=%.*s|md5sum=%.*s|file_size=%u", version.value_len, version.value,
url.value_len, url.value, md5sum.value_len, md5sum.value, file_size);
strncpy(firmware_version, version.value, version.value_len);
int rc = _firmware_download(usr_data, buf, buf_len, url, md5sum, file_size, firmware_version);
if (rc) {
IOT_OTA_ReportProgress(usr_data, buf, buf_len, IOT_OTA_REPORT_TYPE_UPGRADE_FAIL, 0, firmware_version);
return;
}
_firmware_burn(usr_data, buf, buf_len, firmware_version);
}
void _report_version_reply_callback(int result_code, void *usr_data)
{
Log_i("recv code=%d", result_code);
}
// ----------------------------------------------------------------------------
// Main
// ----------------------------------------------------------------------------
static int sg_main_exit = 0;
#ifdef __linux__
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
static void _main_exit(int sig)
{
Log_e("demo exit by signal:%d\n", sig);
sg_main_exit = 1;
}
#endif
int main(int argc, char **argv)
{
#ifdef __linux__
signal(SIGINT, _main_exit);
#endif
int rc;
char buf[1024];
// init log level
LogHandleFunc func = 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;
}
IotOTAUpdateCallback ota_callback = {
.update_firmware_callback = _update_firmware_callback,
.report_version_reply_callback = _report_version_reply_callback,
};
rc = IOT_OTA_Init(client, ota_callback, client);
if (rc) {
Log_e("OTA init failed!, rc=%d", rc);
return rc;
}
rc = IOT_OTA_ReportVersion(client, buf, sizeof(buf), QCLOUD_IOT_DEVICE_SDK_VERSION);
if (rc) {
Log_e("OTA report version failed!, rc=%d", rc);
return rc;
}
do {
rc = IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT);
switch (rc) {
case QCLOUD_RET_SUCCESS:
break;
case QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT:
continue;
case QCLOUD_RET_MQTT_RECONNECTED:
break;
default:
Log_e("Exit loop caused of errCode:%d", rc);
goto exit;
}
} while (!sg_main_exit);
exit:
IOT_OTA_Deinit(client);
rc = IOT_MQTT_Destroy(&client);
utils_log_deinit();
return rc;
}

View File

@@ -0,0 +1,329 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file ota_mqtt.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-18
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-18 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "qcloud_iot_ota.h"
/**
* @brief Context of OTA update topic, callback and user data.
*
*/
typedef struct {
IotOTAUpdateCallback callback;
void *usr_data;
} OTAUpdateContext;
/**
* @brief OTA update type.
*
*/
typedef enum {
OTA_UPDATE_TYPE_REPORT_VERSION_RSP = 0,
OTA_UPDATE_TYPE_UPDATE_FIRMWARE,
} OTAUpdateType;
/**
* @brief Parse payload and callback.
*
* @param[in] type @see OTAUpdateType
* @param[in] message message from cloud
* @param[in] callback callback for user
* @param[in,out] usr_data user data used in callback
*/
static void _parse_update_payload_and_callback(OTAUpdateType type, const MQTTMessage *message,
const IotOTAUpdateCallback *callback, void *usr_data)
{
int rc, result_code;
uint32_t file_size = 0;
UtilsJsonValue version, md5sum, url, value_file_size, value_result_code;
// callback
switch (type) {
case OTA_UPDATE_TYPE_REPORT_VERSION_RSP:
if (!callback->report_version_reply_callback) {
return;
}
rc = utils_json_value_get("result_code", strlen("result_code"), message->payload_str, message->payload_len,
&value_result_code);
if (rc) {
goto error;
}
rc = utils_json_value_data_get(value_result_code, UTILS_JSON_VALUE_TYPE_INT32, &result_code);
if (rc) {
goto error;
}
callback->report_version_reply_callback(result_code, usr_data);
break;
case OTA_UPDATE_TYPE_UPDATE_FIRMWARE:
if (!callback->update_firmware_callback) {
return;
}
rc = utils_json_value_get("version", strlen("version"), message->payload_str, message->payload_len,
&version);
rc |= utils_json_value_get("url", strlen("url"), message->payload_str, message->payload_len, &url);
rc |= utils_json_value_get("md5sum", strlen("md5sum"), message->payload_str, message->payload_len, &md5sum);
rc |= utils_json_value_get("file_size", strlen("file_size"), message->payload_str, message->payload_len,
&value_file_size);
rc |= utils_json_value_data_get(value_file_size, UTILS_JSON_VALUE_TYPE_UINT32, &file_size);
if (rc) {
goto error;
}
callback->update_firmware_callback(version, url, md5sum, file_size, usr_data);
break;
default:
break;
}
return;
error:
Log_e("invalid format of payload!");
return;
}
/**
* @brief Mqtt message callback for property topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] message message from topic
* @param[in,out] usr_data pointer to @see OTAUpdateContext
*/
static void _ota_mqtt_message_callback(void *client, const MQTTMessage *message, void *usr_data)
{
const char *ota_update_str[] = {
[OTA_UPDATE_TYPE_REPORT_VERSION_RSP] = "report_version_rsp",
[OTA_UPDATE_TYPE_UPDATE_FIRMWARE] = "update_firmware",
};
int rc, i = 0;
OTAUpdateContext *ota_update_context = (OTAUpdateContext *)usr_data;
UtilsJsonValue update_type;
Log_d("receive ota message:%.*s", message->payload_len, message->payload_str);
rc = utils_json_value_get("type", strlen("type"), message->payload_str, message->payload_len, &update_type);
if (rc) {
Log_e("invalid ota message!");
return;
}
for (i = OTA_UPDATE_TYPE_REPORT_VERSION_RSP; i <= OTA_UPDATE_TYPE_UPDATE_FIRMWARE; i++) {
if (!strncmp(update_type.value, ota_update_str[i], update_type.value_len)) {
Log_d("callback ota message!");
_parse_update_payload_and_callback(i, message, &ota_update_context->callback, ota_update_context->usr_data);
}
}
}
/**
* @brief Check if topic already subscribed, if not then subscribe.
*
* @param[in,out] client pointer to mqtt client
* @param callback @see IotOTAUpdateCallback
* @param usr_data usr data in callback
* @return @see IotReturnCode
*/
static int _ota_mqtt_update_topic_check_and_sub(void *client, IotOTAUpdateCallback callback, void *usr_data)
{
int rc = 0;
char ota_result_topic[MAX_SIZE_OF_CLOUD_TOPIC];
HAL_Snprintf(ota_result_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$ota/update/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
OTAUpdateContext *ota_update_context = (OTAUpdateContext *)HAL_Malloc(sizeof(OTAUpdateContext));
if (!ota_update_context) {
return QCLOUD_ERR_MALLOC;
}
ota_update_context->callback = callback;
ota_update_context->usr_data = usr_data;
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
sub_params.on_message_handler = _ota_mqtt_message_callback;
sub_params.qos = QOS1;
sub_params.user_data = ota_update_context;
sub_params.user_data_free = HAL_Free;
rc = IOT_MQTT_SubscribeSync(client, ota_result_topic, &sub_params);
if (rc) {
HAL_Free(ota_update_context);
}
return rc;
}
/**
* @brief Unsubscribe topic.
*
* @param[in,out] client pointer to mqtt client
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
static int _ota_mqtt_update_topic_unsubscribe(void *client)
{
char ota_result_topic[MAX_SIZE_OF_CLOUD_TOPIC];
HAL_Snprintf(ota_result_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$ota/update/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
return IOT_MQTT_Unsubscribe(client, ota_result_topic);
}
/**
* @brief Publish to OTA report topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] qos @see QoS
* @param[in] payload payload of mqtt packet
* @param[in] payload_len payload len
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
static int _ota_mqtt_publish(void *client, QoS qos, const char *payload, int payload_len)
{
char ota_report_topic[MAX_SIZE_OF_CLOUD_TOPIC];
HAL_Snprintf(ota_report_topic, sizeof(ota_report_topic), "$ota/report/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
PublishParams pub_params = DEFAULT_PUB_PARAMS;
pub_params.qos = qos;
pub_params.payload = (void *)payload;
pub_params.payload_len = payload_len;
return IOT_MQTT_Publish(client, ota_report_topic, &pub_params);
}
/**
* @brief OTA init, subscribe update topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] callback @see IotOTAUpdateCallback
* @param[in] usr_data usr data used in callback
* @return 0 for success, or err code (<0) @see IotReturnCode
*/
int IOT_OTA_Init(void *client, IotOTAUpdateCallback callback, void *usr_data)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
return _ota_mqtt_update_topic_check_and_sub(client, callback, usr_data);
}
/**
* @brief OTA deinit, unsubscribe update topic.
*
* @param[in,out] client pointer to mqtt client
*/
void IOT_OTA_Deinit(void *client)
{
POINTER_SANITY_CHECK_RTN(client);
_ota_mqtt_update_topic_unsubscribe(client);
}
/**
* @brief Report upgrade progress.
*
* @param[in,out] client pointer to mqtt client
* @param[out] buf publish message buffer
* @param[in] buf_len buffer len
* @param[in] report_type @see IotOTAReportType
* @param[in] progress progress using in IOT_OTA_REPORT_TYPE_DOWNLOADING
* @param[in] version update firmware version
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_OTA_ReportProgress(void *client, char *buf, int buf_len, IotOTAReportType report_type, int progress,
const char *version)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
NUMBERIC_SANITY_CHECK(buf_len, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(version, QCLOUD_ERR_INVAL);
/**
* @brief order @see IotOTAReportType
*
*/
const char *ota_state[] = {
[IOT_OTA_REPORT_TYPE_DOWNLOADING] = "downloading", [IOT_OTA_REPORT_TYPE_UPGRADE_BEGIN] = "burning",
[IOT_OTA_REPORT_TYPE_UPGRADE_SUCCESS] = "done", [IOT_OTA_REPORT_TYPE_DOWNLOAD_TIMEOUT] = "fail",
[IOT_OTA_REPORT_TYPE_FILE_NOT_EXIST] = "fail", [IOT_OTA_REPORT_TYPE_AUTH_FAIL] = "fail",
[IOT_OTA_REPORT_TYPE_MD5_NOT_MATCH] = "fail", [IOT_OTA_REPORT_TYPE_UPGRADE_FAIL] = "fail",
};
int result_code[] = {
[IOT_OTA_REPORT_TYPE_DOWNLOADING] = 0, [IOT_OTA_REPORT_TYPE_UPGRADE_BEGIN] = 0,
[IOT_OTA_REPORT_TYPE_UPGRADE_SUCCESS] = 0, [IOT_OTA_REPORT_TYPE_DOWNLOAD_TIMEOUT] = -1,
[IOT_OTA_REPORT_TYPE_FILE_NOT_EXIST] = -2, [IOT_OTA_REPORT_TYPE_AUTH_FAIL] = -3,
[IOT_OTA_REPORT_TYPE_MD5_NOT_MATCH] = -4, [IOT_OTA_REPORT_TYPE_UPGRADE_FAIL] = -5,
};
const char *result_msg[] = {
[IOT_OTA_REPORT_TYPE_DOWNLOADING] = "",
[IOT_OTA_REPORT_TYPE_UPGRADE_BEGIN] = "",
[IOT_OTA_REPORT_TYPE_UPGRADE_SUCCESS] = "",
[IOT_OTA_REPORT_TYPE_DOWNLOAD_TIMEOUT] = "timeout",
[IOT_OTA_REPORT_TYPE_FILE_NOT_EXIST] = "file not exit",
[IOT_OTA_REPORT_TYPE_AUTH_FAIL] = "auth fail",
[IOT_OTA_REPORT_TYPE_MD5_NOT_MATCH] = "md5 not match",
[IOT_OTA_REPORT_TYPE_UPGRADE_FAIL] = "upgrade fail",
};
int len;
switch (report_type) {
case IOT_OTA_REPORT_TYPE_DOWNLOADING:
len = HAL_Snprintf(buf, buf_len,
"{\"type\":\"report_progress\",\"report\":{\"progress\":{\"state\":\"%s\",\"percent\":"
"\"%d\",\"result_code\":\"%d\",\"result_msg\":\"\"},\"version\":\"%s\"}}",
ota_state[report_type], progress, result_code[report_type], version);
break;
case IOT_OTA_REPORT_TYPE_UPGRADE_BEGIN:
case IOT_OTA_REPORT_TYPE_UPGRADE_SUCCESS:
case IOT_OTA_REPORT_TYPE_DOWNLOAD_TIMEOUT:
case IOT_OTA_REPORT_TYPE_FILE_NOT_EXIST:
case IOT_OTA_REPORT_TYPE_AUTH_FAIL:
case IOT_OTA_REPORT_TYPE_MD5_NOT_MATCH:
case IOT_OTA_REPORT_TYPE_UPGRADE_FAIL:
len = HAL_Snprintf(buf, buf_len,
"{\"type\":\"report_progress\",\"report\":{\"progress\":{\"state\":\"%s\",\"result_"
"code\":\"%d\",\"result_msg\":\"%s\"},\"version\":\"%s\"}}",
ota_state[report_type], result_code[report_type], result_msg[report_type], version);
break;
default:
return QCLOUD_ERR_INVAL;
}
return _ota_mqtt_publish(client, QOS0, buf, len);
}
/**
* @brief Report current firmware version.
*
* @param[in,out] client pointer to mqtt client
* @param[out] buf publish message buffer
* @param[in] buf_len buffer len
* @param[in] version current firmware version
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_OTA_ReportVersion(void *client, char *buf, int buf_len, const char *version)
{
int len = HAL_Snprintf(buf, buf_len, "{\"type\":\"report_version\",\"report\":{\"version\":\"%s\"}}",
STRING_PTR_PRINT_SANITY_CHECK(version));
return _ota_mqtt_publish(client, QOS0, buf, len);
}

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,72 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file test_ota_mqtt.cc
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-19
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-19 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include <iostream>
#include <string>
#include "gtest/gtest.h"
#include "mqtt_client_test.h"
#include "qcloud_iot_common.h"
namespace mqtt_client_unittest {
void _update_firmware_callback(UtilsJsonValue version, UtilsJsonValue url, UtilsJsonValue md5sum, uint32_t file_size,
void *usr_data) {
Log_i("recv firmware: version=%.*s|url=%.*s|md5sum=%.*s|file_size=%u", version.value_len, version.value,
url.value_len, url.value, md5sum.value_len, md5sum.value, file_size);
}
void _report_version_reply_callback(int result_code, void *usr_data) { Log_i("recv code=%d", result_code); }
/**
* @brief Test ota mqtt.
*
*/
TEST_F(MqttClientTest, ota_mqtt) {
char buf[256];
int buf_len = sizeof(buf);
const char *version = "1.0.0";
IotOTAUpdateCallback ota_callback = {
.update_firmware_callback = _update_firmware_callback,
.report_version_reply_callback = _report_version_reply_callback,
};
ASSERT_EQ(IOT_OTA_Init(client, ota_callback, client), 0);
ASSERT_GE(IOT_OTA_ReportVersion(client, buf, buf_len, version), 0);
ASSERT_GE(IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_DOWNLOADING, 0, version), 0);
ASSERT_GE(IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_DOWNLOADING, 100, version), 0);
ASSERT_GE(IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_UPGRADE_BEGIN, 0, version), 0);
ASSERT_GE(IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_UPGRADE_SUCCESS, 0, version), 0);
ASSERT_GE(IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_DOWNLOAD_TIMEOUT, 0, version), 0);
ASSERT_GE(IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_FILE_NOT_EXIST, 0, version), 0);
ASSERT_GE(IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_AUTH_FAIL, 0, version), 0);
ASSERT_GE(IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_MD5_NOT_MATCH, 0, version), 0);
ASSERT_GE(IOT_OTA_ReportProgress(client, buf, buf_len, IOT_OTA_REPORT_TYPE_UPGRADE_FAIL, 0, version), 0);
IOT_OTA_Deinit(client);
}
} // namespace mqtt_client_unittest

View File

@@ -0,0 +1,13 @@
file(GLOB src_system_mqtt ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(src_services ${src_services} ${src_system_mqtt} PARENT_SCOPE)
if( ${CONFIG_COMPILE_SAMPLE} STREQUAL "ON")
file(GLOB src_system_mqtt_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/system_mqtt_sample.c)
add_executable(system_mqtt_sample ${src_system_mqtt_sample})
target_link_libraries(system_mqtt_sample ${libsdk})
endif()
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,201 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file broadcast_sample.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-18
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2022-01-26 <td>1.1 <td>hubert <td>add serverip
* </table>
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "qcloud_iot_common.h"
#include "utils_log.h"
/**
* @brief MQTT event callback, @see MQTTEventHandleFun
*
* @param[in] client pointer to mqtt client
* @param[in] handle_context context
* @param[in] msg msg
*/
static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
{
MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
uintptr_t packet_id = (uintptr_t)msg->msg;
switch (msg->event_type) {
case MQTT_EVENT_UNDEF:
Log_i("undefined event occur.");
break;
case MQTT_EVENT_DISCONNECT:
Log_i("MQTT disconnect.");
break;
case MQTT_EVENT_RECONNECT:
Log_i("MQTT reconnect.");
break;
case MQTT_EVENT_PUBLISH_RECEIVED:
Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
break;
case MQTT_EVENT_SUBSCRIBE_SUCCESS:
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_SUBSCRIBE_NACK:
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_NACK:
Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_SUCCESS:
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_TIMEOUT:
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_NACK:
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
break;
default:
Log_i("Should NOT arrive here.");
break;
}
}
/**
* @brief Setup MQTT construct parameters.
*
* @param[in,out] initParams @see MQTTInitParams
* @param[in] device_info @see DeviceInfo
*/
static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
{
init_params->device_info = device_info;
init_params->event_handle.h_fp = _mqtt_event_handler;
init_params->host = QCLOUD_IOT_MQTT_DIRECT_DOMAIN;
init_params->backup_host = "your_backup_host_domain.com";
}
// ----------------------------------------------------------------------------
// Main
// ----------------------------------------------------------------------------
static int sg_main_exit = 0;
#ifdef __linux__
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
static void _main_exit(int sig)
{
Log_e("demo exit by signal:%d\n", sig);
sg_main_exit = 1;
}
#endif
int main(int argc, char **argv)
{
#ifdef __linux__
signal(SIGINT, _main_exit);
#endif
int rc;
// init log level
LogHandleFunc func = 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;
}
uint32_t time_stamp = 0;
rc = IOT_Sys_GetTime(client, &time_stamp);
if (rc) {
Log_e("Get system time failed!, rc=%d", rc);
} else {
Log_d("Get system time success, time %d", time_stamp);
}
rc = IOT_Sys_SyncNTPTime(client);
if (rc) {
Log_e("Get system ntp time failed!, rc=%d", rc);
}
char server_ip[256] = {0};
rc = IOT_Sys_GetServerIp(client, server_ip);
if (rc) {
Log_e("Get system server ip failed!, rc=%d", rc);
} else {
Log_i("Get system server ip list success. server ip: %s", server_ip);
}
rc = IOT_MQTT_Destroy(&client);
utils_log_deinit();
return rc;
}

View File

@@ -0,0 +1,359 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file system_mqtt.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-24
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-24 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2022-01-26 <td>1.1 <td>hubert <td>add serverip
* </table>
*/
#include "qcloud_iot_system.h"
#define MAX_SYSTEM_INFO_SIZE 256
#define IP_MAX_NUM 10
#define ONE_IP_MAX_LEN 24
#define IP_LIST_MAX_LEN (IP_MAX_NUM * ONE_IP_MAX_LEN)
#define SYS_MQTT_GET_RESOURCE_TIME "{\"type\":\"get\",\"resource\":[\"time\"]}"
#define SYS_MQTT_GET_RESOURCE_SERVERIP "{\"type\":\"get\",\"resource\":[\"serverip\"]}"
typedef enum {
RESOURCE_TIME = 0,
RESOURCE_IP,
} SysResourceType;
/**
* @brief Data structure for system time service.
*
*/
typedef struct {
bool result_recv_ok;
// time
struct {
uint32_t time;
uint64_t ntptime1;
uint64_t ntptime2;
} time;
// server ip
struct {
char ip_list[IP_LIST_MAX_LEN];
int ip_len;
} server_ip;
SysResourceType wait_type;
uint64_t result_recv_time;
} SystemResultInfo;
/**
* @brief Parse time or server ip.
*
* @param[in,out] client pointer to mqtt client
* @param[in] message message from topic
* @param[in,out] usr_data pointer to @see SystemResultInfo
*/
static void _system_mqtt_message_callback(void *client, const MQTTMessage *message, void *usr_data)
{
int rc = 0;
SystemResultInfo *result = (SystemResultInfo *)usr_data;
UtilsJsonValue value;
Log_d("Receive system result message:%.*s", message->payload_len, message->payload_str);
message->payload_str[message->payload_len] = '\0';
if (strstr(message->payload_str, "\"time\"")) {
// get time
rc = utils_json_value_get("time", strlen("time"), message->payload_str, message->payload_len, &value);
if (rc) {
return;
}
rc = utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_UINT32, &result->time.time);
if (rc) {
return;
}
// get ntptime1
rc = utils_json_value_get("ntptime1", strlen("ntptime1"), message->payload_str, message->payload_len, &value);
if (rc) {
return;
}
rc = utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_UINT64, &result->time.ntptime1);
if (rc) {
return;
}
// get ntptime2
rc = utils_json_value_get("ntptime2", strlen("ntptime2"), message->payload_str, message->payload_len, &value);
if (rc) {
return;
}
rc = utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_UINT64, &result->time.ntptime2);
if (rc) {
result->time.ntptime1 = result->time.time * 1000;
result->time.ntptime2 = result->time.time * 1000;
}
}
if (strstr(message->payload_str, "\"serverip\"")) {
// get serverip
rc = utils_json_value_get("serverip", strlen("serverip"), message->payload_str, message->payload_len, &value);
if (rc) {
return;
}
result->server_ip.ip_len = value.value_len > IP_LIST_MAX_LEN ? IP_LIST_MAX_LEN : value.value_len;
memset(result->server_ip.ip_list, 0, IP_LIST_MAX_LEN);
strncpy(result->server_ip.ip_list, value.value, result->server_ip.ip_len);
}
result->result_recv_time = HAL_Timer_CurrentMs();
result->result_recv_ok = true;
}
/**
* @brief Check and subscribe system result topic.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
static int _system_mqtt_result_topic_check_and_sub(void *client, const char *topic)
{
int rc = 0;
// 1. check topic is sub ready
if (IOT_MQTT_IsSubReady(client, topic)) {
return rc;
}
// 2. subscribe
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
SystemResultInfo *system_info = (SystemResultInfo *)HAL_Malloc(sizeof(SystemResultInfo));
if (!system_info) {
return QCLOUD_ERR_MALLOC;
}
system_info->result_recv_ok = false;
sub_params.on_message_handler = _system_mqtt_message_callback;
sub_params.qos = QOS1;
sub_params.user_data = system_info;
sub_params.user_data_free = HAL_Free;
rc = IOT_MQTT_SubscribeSync(client, topic, &sub_params);
if (rc) {
HAL_Free(system_info);
}
return rc;
}
/**
* @brief Publish get time message to system topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic system result topic
* @return >= 0 for success
*/
static int _system_mqtt_get_resource_publish(void *client, const char *topic, SysResourceType type)
{
char pub_topic_name[MAX_SIZE_OF_CLOUD_TOPIC];
HAL_Snprintf(pub_topic_name, sizeof(pub_topic_name), "$sys/operation/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
SystemResultInfo *result = IOT_MQTT_GetSubUsrData(client, topic);
if (!result) {
return QCLOUD_ERR_FAILURE;
}
result->wait_type = type;
result->result_recv_ok = false;
PublishParams pub_params = DEFAULT_PUB_PARAMS;
pub_params.qos = QOS0;
switch (type) {
case RESOURCE_TIME:
pub_params.payload = SYS_MQTT_GET_RESOURCE_TIME;
pub_params.payload_len = strlen(SYS_MQTT_GET_RESOURCE_TIME);
break;
case RESOURCE_IP:
pub_params.payload = SYS_MQTT_GET_RESOURCE_SERVERIP;
pub_params.payload_len = strlen(SYS_MQTT_GET_RESOURCE_SERVERIP);
default:
break;
}
return IOT_MQTT_Publish(client, pub_topic_name, &pub_params);
}
/**
* @brief Wait system result, timeout @see QCLOUD_IOT_MQTT_WAIT_ACK_TIMEOUT
*
* @param[in,out] client pointer to mqtt client
* @param[in] topic system result topic
* @param[out] time time from system result topic
* @return @see IotReturnCode
*/
static int _system_mqtt_result_wait(void *client, const char *topic, void *user_data)
{
int rc = 0;
QcloudIotTimer wait_result_timer;
SystemResultInfo *result;
IOT_Timer_CountdownMs(&wait_result_timer, QCLOUD_IOT_MQTT_WAIT_ACK_TIMEOUT);
result = IOT_MQTT_GetSubUsrData(client, topic);
if (!result) {
return QCLOUD_ERR_FAILURE;
}
result->result_recv_ok = false;
while (!rc && !IOT_Timer_Expired(&wait_result_timer)) {
rc = IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT);
if (result->result_recv_ok) {
switch (result->wait_type) {
case RESOURCE_TIME:
*((uint32_t *)user_data) = result->time.time;
break;
case RESOURCE_IP:
strncpy(user_data, result->server_ip.ip_list, result->server_ip.ip_len);
result->server_ip.ip_len = 0;
default:
break;
}
return QCLOUD_RET_SUCCESS;
}
}
return QCLOUD_ERR_MQTT_REQUEST_TIMEOUT;
}
/**
* @brief Publish get resource message and wait for result.
*
* @param[in,out] client pointer to mqtt client
* @param[in] type @see SysResourceType
* @param[in] user_data pointer to time or sever ip
* @return @see IotReturnCode
*/
static int _system_mqtt_get_resource(void *client, SysResourceType type, void *user_data)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(user_data, QCLOUD_ERR_INVAL);
int rc = 0;
char system_result_topic[MAX_SIZE_OF_CLOUD_TOPIC];
HAL_Snprintf(system_result_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$sys/operation/result/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
rc = _system_mqtt_result_topic_check_and_sub(client, system_result_topic);
if (rc) {
return rc;
}
rc = _system_mqtt_get_resource_publish(client, system_result_topic, type);
if (rc < 0) {
return rc;
}
return _system_mqtt_result_wait(client, system_result_topic, user_data);
}
/**
* @brief Get time from system result topic
*
* @param[in,out] client pointer to mqtt client
* @param[out] time time from system result topic
* @return @see IotReturnCode
*/
int IOT_Sys_GetTime(void *client, uint32_t *time)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(time, QCLOUD_ERR_INVAL);
return _system_mqtt_get_resource(client, RESOURCE_TIME, time);
}
/**
* @brief Get ntp time from system result topic and set to system.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int IOT_Sys_SyncNTPTime(void *client)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
int rc = 0;
uint32_t time_get = 0;
uint64_t local_publish_before, local_ntptime = 0;
SystemResultInfo *result = NULL;
char system_result_topic[MAX_SIZE_OF_CLOUD_TOPIC];
HAL_Snprintf(system_result_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$sys/operation/result/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
rc = _system_mqtt_result_topic_check_and_sub(client, system_result_topic);
if (rc) {
return rc;
}
// prepare for publish
result = IOT_MQTT_GetSubUsrData(client, system_result_topic);
if (!result) {
return -1;
}
local_publish_before = HAL_Timer_CurrentMs();
// publish and wait
rc = _system_mqtt_get_resource_publish(client, system_result_topic, RESOURCE_TIME);
if (rc < 0) {
return rc;
}
rc = _system_mqtt_result_wait(client, system_result_topic, &time_get);
if (rc) {
return rc;
}
local_ntptime =
(result->time.ntptime2 + result->time.ntptime1 + result->result_recv_time - local_publish_before) / 2;
rc = HAL_Timer_SetSystimeMs(local_ntptime);
if (rc) {
Log_e("set systime ms failed, timestamp %lld, please check permission or other ret :%d", local_ntptime, rc);
} else {
Log_i("set systime ms success, timestamp %lld ms", local_ntptime);
}
return rc;
}
/**
* @brief Get serverip from system result topic
*
* @param[in,out] client pointer to mqtt client
* @param[out] server_ip serverip from system result topic
* @return @see IotReturnCode
*/
int IOT_Sys_GetServerIp(void *client, char *server_ip)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(server_ip, QCLOUD_ERR_INVAL);
return _system_mqtt_get_resource(client, RESOURCE_IP, server_ip);
}

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,51 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file test_broadcast.cc
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-18
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
* <tr><td>2022-01-26 <td>1.1 <td>hubert <td>add serverip test
* </table>
*/
#include <iostream>
#include <string>
#include "gtest/gtest.h"
#include "mqtt_client_test.h"
#include "qcloud_iot_common.h"
namespace mqtt_client_unittest {
/**
* @brief Test system mqtt.
*
*/
TEST_F(MqttClientTest, system_mqtt) {
uint32_t time_stamp = 0;
char server_ip[256] = {0};
EXPECT_EQ(IOT_Sys_GetTime(client, &time_stamp), 0);
EXPECT_EQ(IOT_Sys_SyncNTPTime(client), 0);
ASSERT_EQ(IOT_Sys_GetServerIp(client, server_ip), 0);
}
} // namespace mqtt_client_unittest

View File

@@ -0,0 +1,16 @@
file(GLOB src_system_mqtt ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(inc_system_mqtt ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
set(src_services ${src_services} ${src_system_mqtt} PARENT_SCOPE)
set(inc_services ${inc_services} ${inc_system_mqtt} PARENT_SCOPE)
if( ${CONFIG_COMPILE_SAMPLE} STREQUAL "ON")
file(GLOB src_data_template_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/data_template_sample.c)
add_executable(data_template_sample ${src_data_template_sample})
target_link_libraries(data_template_sample ${libsdk})
endif()
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,194 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file data_template.h
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-08-23
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-08-23 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_EXPLORER_DATA_TEMPLATE_INC_DATA_TEMPLATE_H_
#define IOT_HUB_DEVICE_C_SDK_SERVICES_EXPLORER_DATA_TEMPLATE_INC_DATA_TEMPLATE_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "qcloud_iot_common.h"
#include "qcloud_iot_data_template.h"
/**************************************************************************************
* common
**************************************************************************************/
/**
* @brief Type of data template(property/event/action).
*
*/
typedef enum {
DATA_TEMPLATE_TYPE_PROPERTY = 0,
DATA_TEMPLATE_TYPE_EVENT,
DATA_TEMPLATE_TYPE_ACTION,
} DataTemplateType;
/**
* @brief Context of data template, callback and user data.
*
*/
typedef struct {
union {
PropertyMessageCallback property_callback;
EventMessageCallback event_callback;
ActionMessageCallback action_callback;
};
void *usr_data;
} DataTemplateContext;
/**
* @brief Check if topic already subscribed, if not then subscribe.
*
* @param[in,out] client pointer to mqtt client
* @param[in] type @see DataTemplateType
* @param[in] on_message_handler message handle of topic
* @param[in] context @see DataTemplateContext
* @return @see IotReturnCode
*/
int data_template_topic_check_and_sub(void *client, DataTemplateType type, OnMessageHandler on_message_handler,
DataTemplateContext context);
/**
* @brief Unsubscribe data template topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] type @see DataTemplateType
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int data_template_topic_unsubscribe(void *client, DataTemplateType type);
/**
* @brief Publish to data template topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] type @see DataTemplateType
* @param[in] qos @see QoS
* @param[in] payload payload of mqtt packet
* @param[in] payload_len payload len
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int data_template_publish(void *client, DataTemplateType type, QoS qos, const char *payload, int payload_len);
/**************************************************************************************
* property
**************************************************************************************/
typedef enum {
PROPERTY_UP_METHOD_TYPE_REPORT = 0,
PROPERTY_UP_METHOD_TYPE_REPORT_INFO,
PROPERTY_UP_METHOD_TYPE_GET_STATUS,
PROPERTY_UP_METHOD_TYPE_CLEAR_CONTROL,
PROPERTY_UP_METHOD_TYPE_CONTROL_REPLY,
} PropertyUpMethodType;
typedef union {
const char *json;
int code;
struct {
int code;
UtilsJsonValue client_token;
} control_reply;
} PropertyPublishParams;
/**
* @brief Mqtt message callback for property topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] message message from topic
* @param[in,out] usr_data pointer to @see DataTemplateContext
*/
void data_template_property_message_handler(void *client, const MQTTMessage *message, void *usr_data);
/**
* @brief Publish message to property topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] publish_type @see PropertyUpMethodType
* @param[out] buf publish message buffer
* @param[in] buf_len buffer len
* @param[in] params @see PropertyPublishParams
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int data_template_property_publish(void *client, PropertyUpMethodType publish_type, char *buf, int buf_len,
PropertyPublishParams params);
/**************************************************************************************
* event
**************************************************************************************/
/**
* @brief Mqtt message callback for event topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] message message from topic
* @param[in,out] usr_data pointer to @see DataTemplateContext
*/
void data_template_event_message_handler(void *client, const MQTTMessage *message, void *usr_data);
/**
* @brief Publish message to event topic.
*
* @param[in,out] client pointer to mqtt client
* @param[out] buf publish message buffer
* @param[in] buf_len buffer len
* @param[in] data @see IotDataTemplateEventData
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int data_template_event_reply_publish(void *client, char *buf, int buf_len, IotDataTemplateEventData data);
/**************************************************************************************
* action
**************************************************************************************/
/**
* @brief Mqtt message callback for action topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] message message from topic
* @param[in,out] usr_data pointer to @see DataTemplateContext
*/
void data_template_action_message_handler(void *client, const MQTTMessage *message, void *usr_data);
/**
* @brief Publish message to event topic.
*
* @param[in,out] client pointer to mqtt client
* @param[out] buf publish message buffer
* @param[in] buf_len buffer len
* @param[in] reply @see IotDataTemplateActionReply
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int data_template_action_reply_publish(void *client, char *buf, int buf_len, IotDataTemplateActionReply reply);
#ifdef __cplusplus
}
#endif
#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_EXPLORER_DATA_TEMPLATE_INC_DATA_TEMPLATE_H_

View File

@@ -0,0 +1,305 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file data_template_sample.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-09-27
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-09-27 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "qcloud_iot_common.h"
#include "qcloud_iot_explorer.h"
#include "utils_log.h"
/**
* @brief MQTT event callback, @see MQTTEventHandleFun
*
* @param[in] client pointer to mqtt client
* @param[in] handle_context context
* @param[in] msg msg
*/
static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
{
MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
uintptr_t packet_id = (uintptr_t)msg->msg;
switch (msg->event_type) {
case MQTT_EVENT_UNDEF:
Log_i("undefined event occur.");
break;
case MQTT_EVENT_DISCONNECT:
Log_i("MQTT disconnect.");
break;
case MQTT_EVENT_RECONNECT:
Log_i("MQTT reconnect.");
break;
case MQTT_EVENT_PUBLISH_RECEIVED:
Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
break;
case MQTT_EVENT_SUBSCRIBE_SUCCESS:
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_SUBSCRIBE_NACK:
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_NACK:
Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_SUCCESS:
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_TIMEOUT:
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_NACK:
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
break;
default:
Log_i("Should NOT arrive here.");
break;
}
}
/**
* @brief Setup MQTT construct parameters.
*
* @param[in,out] initParams @see MQTTInitParams
* @param[in] device_info @see DeviceInfo
*/
static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
{
init_params->device_info = device_info;
init_params->event_handle.h_fp = _mqtt_event_handler;
}
// ----------------------------------------------------------------------------
// Data template callback
// ----------------------------------------------------------------------------
static void _method_control_callback(UtilsJsonValue client_token, UtilsJsonValue params, void *usr_data)
{
char buf[256];
Log_i("recv msg[%.*s]: params=%.*s", client_token.value_len, client_token.value, params.value_len, params.value);
IOT_DataTemplate_PropertyControlReply(usr_data, buf, sizeof(buf), 0, client_token);
}
static void _method_report_reply_callback(UtilsJsonValue client_token, int code, void *usr_data)
{
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
}
static void _method_get_status_reply_callback(UtilsJsonValue client_token, int code, UtilsJsonValue reported,
UtilsJsonValue control, void *usr_data)
{
char buf[256];
Log_i("recv msg[%.*s]: code=%d|reported=%.*s|control=%.*s", client_token.value_len, client_token.value, code,
reported.value_len, STRING_PTR_PRINT_SANITY_CHECK(reported.value), control.value_len,
STRING_PTR_PRINT_SANITY_CHECK(control.value));
IOT_DataTemplate_PropertyClearControl(usr_data, buf, sizeof(buf));
}
static void _method_report_info_reply_callback(UtilsJsonValue client_token, int code, void *usr_data)
{
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
}
static void _method_clear_control_reply_callback(UtilsJsonValue client_token, int code, void *usr_data)
{
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
}
static void _method_event_reply_callback(UtilsJsonValue client_token, int code, void *usr_data)
{
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
}
static void _method_action_callback(UtilsJsonValue client_token, UtilsJsonValue action_id, UtilsJsonValue params,
void *usr_data)
{
char buf[256];
Log_i("recv msg[%.*s]: action_id=%.*s|params=%.*s", client_token.value_len, client_token.value, action_id.value_len,
action_id.value, params.value_len, params.value);
IotDataTemplateActionReply reply = {
.code = 0,
.client_token = client_token,
.response = "{\"err_code\":0}",
};
IOT_DataTemplate_ActionReply(usr_data, buf, sizeof(buf), reply);
}
// ----------------------------------------------------------------------------
// Data template upstream
// ----------------------------------------------------------------------------
static void _cycle_report(void *client)
{
char buf[256];
const char *report_property = "{\"power_switch\":0}";
IotDataTemplateEventData event_data = {
.event_id = "status_report",
.type = IOT_DATA_TEMPLATE_EVENT_TYPE_INFO,
.params = "{\"status\":0,\"message\":\"ok\"}",
};
static QcloudIotTimer sg_cycle_report_timer;
if (IOT_Timer_Expired(&sg_cycle_report_timer)) {
IOT_DataTemplate_PropertyReport(client, buf, sizeof(buf), report_property);
IOT_DataTemplate_EventPost(client, buf, sizeof(buf), event_data);
IOT_Timer_Countdown(&sg_cycle_report_timer, 500);
}
}
// ----------------------------------------------------------------------------
// Main
// ----------------------------------------------------------------------------
static int sg_main_exit = 0;
#ifdef __linux__
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
static void _main_exit(int sig)
{
Log_e("demo exit by signal:%d\n", sig);
sg_main_exit = 1;
}
#endif
int main(int argc, char **argv)
{
#ifdef __linux__
signal(SIGINT, _main_exit);
#endif
int rc;
char buf[1024];
// init log level
LogHandleFunc func = 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;
}
// subscribe normal topics and wait result
IotDataTemplateCallback callback = {
.property_callback = {.method_control_callback = _method_control_callback,
.method_clear_control_reply_callback = _method_clear_control_reply_callback,
.method_get_status_reply_callback = _method_get_status_reply_callback,
.method_report_info_reply_callback = _method_report_info_reply_callback,
.method_report_reply_callback = _method_report_reply_callback},
.event_callback = {.method_event_reply_callback = _method_event_reply_callback},
.action_callback = {.method_action_callback = _method_action_callback},
};
rc = IOT_DataTemplate_Init(client, callback, client);
if (rc) {
Log_e("Client Subscribe Topic Failed: %d", rc);
return rc;
}
const char *report_info =
"{\"module_hardinfo\":\"ESP8266\",\"module_softinfo\":\"V1.0\", \"fw_ver\":\"4.0.0\", "
"\"imei\":\"11-22-33-44\",\"lat\":\"22.546015\",\"lon\":\"113.941125\",\"mac\":\"11:22:33:44:55:66\",\"device_"
"label\":{\"append_"
"info\":\"your self defined info\"}}";
IOT_DataTemplate_PropertyReportInfo(client, buf, sizeof(buf), report_info);
IOT_DataTemplate_PropertyGetStatus(client, buf, sizeof(buf));
do {
rc = IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT);
switch (rc) {
case QCLOUD_RET_SUCCESS:
break;
case QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT:
continue;
case QCLOUD_RET_MQTT_RECONNECTED:
IOT_DataTemplate_PropertyGetStatus(client, buf, sizeof(buf));
break;
default:
Log_e("Exit loop caused of errCode:%d", rc);
goto exit;
}
_cycle_report(client);
} while (!sg_main_exit);
exit:
IOT_DataTemplate_Deinit(client);
rc |= IOT_MQTT_Destroy(&client);
utils_log_deinit();
return rc;
}

View File

@@ -0,0 +1,199 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file data_template.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-08-22
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-08-22 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "qcloud_iot_data_template.h"
#include "data_template.h"
/**
* @brief Check and subscribe data template topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] callback @see IotDataTemplateCallback
* @param[in] usr_data usr data used in callback
* @return 0 for success, or err code (<0) @see IotReturnCode
*/
int IOT_DataTemplate_Init(void *client, IotDataTemplateCallback callback, void *usr_data)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
int rc = 0;
DataTemplateContext data_tempale_context;
data_tempale_context.usr_data = usr_data;
data_tempale_context.property_callback = callback.property_callback;
rc |= data_template_topic_check_and_sub(client, DATA_TEMPLATE_TYPE_PROPERTY, data_template_property_message_handler,
data_tempale_context);
if (callback.event_callback.method_event_reply_callback) {
data_tempale_context.event_callback = callback.event_callback;
rc |= data_template_topic_check_and_sub(client, DATA_TEMPLATE_TYPE_EVENT, data_template_event_message_handler,
data_tempale_context);
}
if (callback.action_callback.method_action_callback) {
data_tempale_context.action_callback = callback.action_callback;
rc |= data_template_topic_check_and_sub(client, DATA_TEMPLATE_TYPE_ACTION, data_template_action_message_handler,
data_tempale_context);
}
return rc;
}
/**
* @brief Unsubscribe data template topic.
*
* @param[in,out] client pointer to mqtt client
*/
void IOT_DataTemplate_Deinit(void *client)
{
POINTER_SANITY_CHECK_RTN(client);
data_template_topic_unsubscribe(client, DATA_TEMPLATE_TYPE_PROPERTY);
data_template_topic_unsubscribe(client, DATA_TEMPLATE_TYPE_EVENT);
data_template_topic_unsubscribe(client, DATA_TEMPLATE_TYPE_ACTION);
}
/**
* @brief Report property.
*
* @param[in,out] client pointer to mqtt client
* @param[in] buf buffer for message
* @param[in] buf_len buffer length
* @param[in] params params constructed with property
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_DataTemplate_PropertyReport(void *client, char *buf, int buf_len, const char *params)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
PropertyPublishParams publish_params = {.json = params};
return data_template_property_publish(client, PROPERTY_UP_METHOD_TYPE_REPORT, buf, buf_len, publish_params);
}
/**
* @brief Get control message offline.
*
* @param[in,out] client pointer to mqtt client
* @param[in] buf buffer for message
* @param[in] buf_len buffer length
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_DataTemplate_PropertyGetStatus(void *client, char *buf, int buf_len)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
PropertyPublishParams publish_params = {0};
return data_template_property_publish(client, PROPERTY_UP_METHOD_TYPE_GET_STATUS, buf, buf_len, publish_params);
}
/**
* @brief Report device info.
*
* @param[in,out] client pointer to mqtt client
* @param[in] buf buffer for message
* @param[in] buf_len buffer length
* @param[in] params params constructed with device info
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_DataTemplate_PropertyReportInfo(void *client, char *buf, int buf_len, const char *params)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
PropertyPublishParams publish_params = {.json = params};
return data_template_property_publish(client, PROPERTY_UP_METHOD_TYPE_REPORT_INFO, buf, buf_len, publish_params);
}
/**
* @brief Clear control message offline.
*
* @param[in,out] client pointer to mqtt client
* @param[in] buf buffer for message
* @param[in] buf_len buffer length
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_DataTemplate_PropertyClearControl(void *client, char *buf, int buf_len)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
PropertyPublishParams publish_params = {0};
return data_template_property_publish(client, PROPERTY_UP_METHOD_TYPE_CLEAR_CONTROL, buf, buf_len, publish_params);
}
/**
* @brief Reply control message.
*
* @param[in,out] client pointer to mqtt client
* @param[in] buf buffer for message
* @param[in] buf_len buffer length
* @param[in] code 0 for success
* @param[in] client_token client token of control message
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_DataTemplate_PropertyControlReply(void *client, char *buf, int buf_len, int code, UtilsJsonValue client_token)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
PropertyPublishParams publish_params = {.control_reply.code = code, .control_reply.client_token = client_token};
return data_template_property_publish(client, PROPERTY_UP_METHOD_TYPE_CONTROL_REPLY, buf, buf_len, publish_params);
}
/**
* @brief Post event.
*
* @param[in,out] client pointer to mqtt client
* @param[in] buf buffer for message
* @param[in] buf_len buffer length
* @param[in] data @see IotDataTemplateEventData
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_DataTemplate_EventPost(void *client, char *buf, int buf_len, IotDataTemplateEventData data)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
return data_template_event_reply_publish(client, buf, buf_len, data);
}
/**
* @brief Reply action message.
*
* @param[in,out] client pointer to mqtt client
* @param[in] buf buffer for message
* @param[in] buf_len buffer length
* @param[in] reply @see IotDataTemplateActionReply
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_DataTemplate_ActionReply(void *client, char *buf, int buf_len, IotDataTemplateActionReply reply)
{
return data_template_action_reply_publish(client, buf, buf_len, reply);
}

View File

@@ -0,0 +1,95 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file data_template_action.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-09-26
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-09-26 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "data_template.h"
/**
* @brief Mqtt message callback for action topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] message message from topic
* @param[in,out] usr_data pointer to @see DataTemplateContext
*/
void data_template_action_message_handler(void *client, const MQTTMessage *message, void *usr_data)
{
DataTemplateContext *data_template_context = (DataTemplateContext *)usr_data;
int rc = 0;
UtilsJsonValue method, client_token, action_id, params;
Log_d("receive action message:%.*s", message->payload_len, message->payload_str);
rc = utils_json_value_get("method", strlen("method"), message->payload_str, message->payload_len, &method);
if (rc) {
return;
}
if (!strncmp(method.value, "action", method.value_len)) {
if (data_template_context->action_callback.method_action_callback) {
rc = utils_json_value_get("clientToken", strlen("clientToken"), message->payload_str, message->payload_len,
&client_token);
if (rc) {
goto error;
}
rc = utils_json_value_get("actionId", strlen("actionId"), message->payload_str, message->payload_len,
&action_id);
if (rc) {
goto error;
}
rc = utils_json_value_get("params", strlen("params"), message->payload_str, message->payload_len, &params);
if (rc) {
goto error;
}
data_template_context->action_callback.method_action_callback(client_token, action_id, params,
data_template_context->usr_data);
}
}
return;
error:
Log_e("invalid format of payload!");
}
/**
* @brief Publish message to event topic.
*
* @param[in,out] client pointer to mqtt client
* @param[out] buf publish message buffer
* @param[in] buf_len buffer len
* @param[in] reply @see IotDataTemplateActionReply
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int data_template_action_reply_publish(void *client, char *buf, int buf_len, IotDataTemplateActionReply reply)
{
int len =
HAL_Snprintf(buf, buf_len, "{\"method\":\"action_reply\",\"clientToken\":\"%.*s\",\"code\":%d,\"response\":%s}",
reply.client_token.value_len, reply.client_token.value, reply.code, reply.response);
return data_template_publish(client, DATA_TEMPLATE_TYPE_ACTION, QOS0, buf, len);
}

View File

@@ -0,0 +1,101 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file data_template_event.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-09-26
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-09-26 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "data_template.h"
/**
* @brief Mqtt message callback for event topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] message message from topic
* @param[in,out] usr_data pointer to @see DataTemplateContext
*/
void data_template_event_message_handler(void *client, const MQTTMessage *message, void *usr_data)
{
DataTemplateContext *data_template_context = (DataTemplateContext *)usr_data;
int rc, code = 0;
UtilsJsonValue method, client_token, value_code;
Log_d("receive event message:%.*s", message->payload_len, message->payload_str);
rc = utils_json_value_get("method", strlen("method"), message->payload_str, message->payload_len, &method);
if (rc) {
return;
}
if (!strncmp(method.value, "event_reply", method.value_len)) {
if (data_template_context->event_callback.method_event_reply_callback) {
rc = utils_json_value_get("clientToken", strlen("clientToken"), message->payload_str, message->payload_len,
&client_token);
if (rc) {
goto error;
}
rc = utils_json_value_get("code", strlen("code"), message->payload_str, message->payload_len, &value_code);
if (rc) {
goto error;
}
rc = utils_json_value_data_get(value_code, UTILS_JSON_VALUE_TYPE_INT32, &code);
if (rc) {
goto error;
}
data_template_context->event_callback.method_event_reply_callback(client_token, code,
data_template_context->usr_data);
}
}
return;
error:
Log_e("invalid format of payload!");
}
/**
* @brief Publish message to event topic.
*
* @param[in,out] client pointer to mqtt client
* @param[out] buf publish message buffer
* @param[in] buf_len buffer len
* @param[in] data @see IotDataTemplateEventData
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int data_template_event_reply_publish(void *client, char *buf, int buf_len, IotDataTemplateEventData data)
{
const char *event_type[] = {
[IOT_DATA_TEMPLATE_EVENT_TYPE_INFO] = "info",
[IOT_DATA_TEMPLATE_EVENT_TYPE_ALERT] = "alert",
[IOT_DATA_TEMPLATE_EVENT_TYPE_FAULT] = "fault",
};
static uint32_t token_num = 0;
int len = HAL_Snprintf(
buf, buf_len,
"{\"method\":\"event_post\",\"clientToken\":\"event-%u\",\"eventId\":\"%s\",\"type\":\"%s\",\"params\":%s}",
token_num++, data.event_id, event_type[data.type], data.params);
return data_template_publish(client, DATA_TEMPLATE_TYPE_EVENT, QOS0, buf, len);
}

View File

@@ -0,0 +1,145 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file data_template_mqtt.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-10-18
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-10-18 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "qcloud_iot_data_template.h"
#include "data_template.h"
/**
* @brief Direction(upstream/downstream) for topic.
*
*/
typedef enum {
DATA_TEMPLATE_DIRECTION_UP = 0,
DATA_TEMPLATE_DIRECTION_DOWN,
} DataTemplateDirection;
/**
* @brief Generate topic string.
*
* @param[out] buf buffer for topic name
* @param[in] buf_len buffer length
* @param[in] direction @see DataTemplateDirection
* @param[in] type @see DataTemplateType
* @param[in] product_id product id of device
* @param[in] device_name device name of device
* @return > 0 for length of topic name, others for fail.
*/
static int _data_template_topic_generate(char *buf, int buf_len, DataTemplateDirection direction, DataTemplateType type,
const char *product_id, const char *device_name)
{
const char *topic_method[] = {
[DATA_TEMPLATE_TYPE_PROPERTY] = "property",
[DATA_TEMPLATE_TYPE_EVENT] = "event",
[DATA_TEMPLATE_TYPE_ACTION] = "action",
};
// $thing/down/property/C283SMY3W3/test1
return HAL_Snprintf(buf, buf_len, "$thing/%s/%s/%s/%s", direction ? "down" : "up", topic_method[type],
STRING_PTR_PRINT_SANITY_CHECK(product_id), STRING_PTR_PRINT_SANITY_CHECK(device_name));
}
/**
* @brief Check if topic already subscribed, if not then subscribe.
*
* @param[in,out] client pointer to mqtt client
* @param[in] type @see DataTemplateType
* @param[in] on_message_handler message handle of topic
* @param[in] context @see DataTemplateContext
* @return @see IotReturnCode
*/
int data_template_topic_check_and_sub(void *client, DataTemplateType type, OnMessageHandler on_message_handler,
DataTemplateContext context)
{
char data_template_topic[MAX_SIZE_OF_CLOUD_TOPIC];
_data_template_topic_generate(data_template_topic, MAX_SIZE_OF_CLOUD_TOPIC, DATA_TEMPLATE_DIRECTION_DOWN, type,
IOT_MQTT_GetDeviceInfo(client)->product_id,
IOT_MQTT_GetDeviceInfo(client)->device_name);
int rc = 0;
DataTemplateContext *data_template_context = HAL_Malloc(sizeof(DataTemplateContext));
if (!data_template_context) {
return QCLOUD_ERR_MALLOC;
}
*data_template_context = context;
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
sub_params.on_message_handler = on_message_handler;
sub_params.qos = QOS1;
sub_params.user_data = data_template_context;
sub_params.user_data_free = HAL_Free;
rc = IOT_MQTT_SubscribeSync(client, data_template_topic, &sub_params);
if (rc) {
Log_e("subscribe topic %s failed!", data_template_topic);
HAL_Free(data_template_context);
}
return rc;
}
/**
* @brief Unsubscribe data template topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] type @see DataTemplateType
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int data_template_topic_unsubscribe(void *client, DataTemplateType type)
{
char data_template_topic[MAX_SIZE_OF_CLOUD_TOPIC];
_data_template_topic_generate(data_template_topic, MAX_SIZE_OF_CLOUD_TOPIC, DATA_TEMPLATE_DIRECTION_DOWN, type,
IOT_MQTT_GetDeviceInfo(client)->product_id,
IOT_MQTT_GetDeviceInfo(client)->device_name);
return IOT_MQTT_Unsubscribe(client, data_template_topic);
}
/**
* @brief Publish to data template topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] type @see DataTemplateType
* @param[in] qos @see QoS
* @param[in] payload payload of mqtt packet
* @param[in] payload_len payload len
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int data_template_publish(void *client, DataTemplateType type, QoS qos, const char *payload, int payload_len)
{
NUMBERIC_SANITY_CHECK(payload_len, QCLOUD_ERR_BUF_TOO_SHORT);
char data_template_topic[MAX_SIZE_OF_CLOUD_TOPIC];
_data_template_topic_generate(data_template_topic, MAX_SIZE_OF_CLOUD_TOPIC, DATA_TEMPLATE_DIRECTION_UP, type,
IOT_MQTT_GetDeviceInfo(client)->product_id,
IOT_MQTT_GetDeviceInfo(client)->device_name);
PublishParams pub_params = DEFAULT_PUB_PARAMS;
pub_params.qos = qos;
pub_params.payload = (void *)payload;
pub_params.payload_len = payload_len;
return IOT_MQTT_Publish(client, data_template_topic, &pub_params);
}

View File

@@ -0,0 +1,215 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file data_template_property.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-08-23
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-08-23 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "qcloud_iot_data_template.h"
#include "data_template.h"
/**
* @brief Down method type.
*
*/
typedef enum {
PROPERTY_DOWN_METHOD_TYPE_CONTROL = 0,
PROPERTY_DOWN_METHOD_TYPE_REPORT_REPLY,
PROPERTY_DOWN_METHOD_TYPE_GET_STATUS_REPLY,
PROPERTY_DOWN_METHOD_TYPE_REPORT_INFO_REPLY,
PROPERTY_DOWN_METHOD_TYPE_CLEAR_CONTROL_REPLY,
} PropertyDownMethodType;
/**
* @brief Parse payload and callback.
*
* @param[in] type @see PropertyDownMethodType
* @param[in] message message from cloud
* @param[in] callback callback for user
* @param[in,out] usr_data user data used in callback
*/
static void _parse_method_payload_and_callback(PropertyDownMethodType type, const MQTTMessage *message,
const PropertyMessageCallback *callback, void *usr_data)
{
int rc = 0, code;
UtilsJsonValue client_token, value_code, params, reported, control;
// get client token
rc = utils_json_value_get("clientToken", strlen("clientToken"), message->payload_str, message->payload_len,
&client_token);
if (rc) {
goto error;
}
// get code
if (PROPERTY_DOWN_METHOD_TYPE_REPORT_REPLY == type || PROPERTY_DOWN_METHOD_TYPE_GET_STATUS_REPLY == type ||
PROPERTY_DOWN_METHOD_TYPE_REPORT_INFO_REPLY == type || PROPERTY_DOWN_METHOD_TYPE_CLEAR_CONTROL_REPLY == type) {
rc = utils_json_value_get("code", strlen("code"), message->payload_str, message->payload_len, &value_code);
if (rc) {
goto error;
}
rc = utils_json_value_data_get(value_code, UTILS_JSON_VALUE_TYPE_INT32, &code);
if (rc) {
goto error;
}
}
// callback
switch (type) {
case PROPERTY_DOWN_METHOD_TYPE_CONTROL:
if (callback->method_control_callback) {
rc = utils_json_value_get("params", strlen("params"), message->payload_str, message->payload_len,
&params);
if (rc) {
goto error;
}
callback->method_control_callback(client_token, params, usr_data);
}
break;
case PROPERTY_DOWN_METHOD_TYPE_REPORT_REPLY:
if (callback->method_report_reply_callback) {
callback->method_report_reply_callback(client_token, code, usr_data);
}
break;
case PROPERTY_DOWN_METHOD_TYPE_GET_STATUS_REPLY:
if (callback->method_get_status_reply_callback) {
reported.value = NULL;
reported.value_len = 0;
control.value = NULL;
control.value_len = 0;
rc = utils_json_value_get("data.reported", strlen("data.reported"), message->payload_str,
message->payload_len, &reported);
rc &= utils_json_value_get("data.control", strlen("data.control"), message->payload_str,
message->payload_len, &control);
if (rc) {
goto error;
}
callback->method_get_status_reply_callback(client_token, code, reported, control, usr_data);
}
break;
case PROPERTY_DOWN_METHOD_TYPE_REPORT_INFO_REPLY:
if (callback->method_report_info_reply_callback) {
callback->method_report_info_reply_callback(client_token, code, usr_data);
}
break;
case PROPERTY_DOWN_METHOD_TYPE_CLEAR_CONTROL_REPLY:
if (callback->method_clear_control_reply_callback) {
callback->method_clear_control_reply_callback(client_token, code, usr_data);
}
break;
default:
break;
}
return;
error:
Log_e("invalid format of payload!");
return;
}
/**
* @brief Mqtt message callback for property topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] message message from topic
* @param[in,out] usr_data pointer to @see DataTemplateContext
*/
void data_template_property_message_handler(void *client, const MQTTMessage *message, void *usr_data)
{
const char *property_down_method_str[] = {
[PROPERTY_DOWN_METHOD_TYPE_CONTROL] = "control",
[PROPERTY_DOWN_METHOD_TYPE_REPORT_REPLY] = "report_reply",
[PROPERTY_DOWN_METHOD_TYPE_GET_STATUS_REPLY] = "get_status_reply",
[PROPERTY_DOWN_METHOD_TYPE_REPORT_INFO_REPLY] = "report_info_reply",
[PROPERTY_DOWN_METHOD_TYPE_CLEAR_CONTROL_REPLY] = "clear_control_reply",
};
int rc;
DataTemplateContext *data_template_context = (DataTemplateContext *)usr_data;
UtilsJsonValue method;
Log_d("receive property message:%.*s", message->payload_len, message->payload_str);
rc = utils_json_value_get("method", strlen("method"), message->payload_str, message->payload_len, &method);
if (rc) {
return;
}
PropertyDownMethodType i;
for (i = PROPERTY_DOWN_METHOD_TYPE_CONTROL; i <= PROPERTY_DOWN_METHOD_TYPE_CLEAR_CONTROL_REPLY; i++) {
if (!strncmp(method.value, property_down_method_str[i], method.value_len)) {
_parse_method_payload_and_callback(i, message, &data_template_context->property_callback,
data_template_context->usr_data);
}
}
}
/**
* @brief Publish message to property topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] publish_type @see PropertyUpMethodType
* @param[out] buf publish message buffer
* @param[in] buf_len buffer len
* @param[in] params @see PropertyPublishParams
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int data_template_property_publish(void *client, PropertyUpMethodType publish_type, char *buf, int buf_len,
PropertyPublishParams params)
{
static uint32_t token_num = 0;
int len = 0;
switch (publish_type) {
case PROPERTY_UP_METHOD_TYPE_REPORT:
len = HAL_Snprintf(buf, buf_len, "{\"method\":\"report\",\"clientToken\":\"property-%u\",\"params\":%s}",
token_num++, params.json);
break;
case PROPERTY_UP_METHOD_TYPE_REPORT_INFO:
len =
HAL_Snprintf(buf, buf_len, "{\"method\":\"report_info\",\"clientToken\":\"property-%u\",\"params\":%s}",
token_num++, params.json);
break;
case PROPERTY_UP_METHOD_TYPE_GET_STATUS:
len =
HAL_Snprintf(buf, buf_len, "{\"method\":\"get_status\",\"clientToken\":\"property-%u\"}", token_num++);
break;
case PROPERTY_UP_METHOD_TYPE_CLEAR_CONTROL:
len = HAL_Snprintf(buf, buf_len, "{\"method\":\"clear_control\",\"clientToken\":\"property-%u\"}",
token_num++);
break;
case PROPERTY_UP_METHOD_TYPE_CONTROL_REPLY:
len = HAL_Snprintf(buf, buf_len, "{\"method\":\"control_reply\",\"clientToken\":\"%.*s\",\"code\":%d}",
params.control_reply.client_token.value_len, params.control_reply.client_token.value,
params.control_reply.code);
break;
default:
return QCLOUD_ERR_FAILURE;
}
return data_template_publish(client, DATA_TEMPLATE_TYPE_PROPERTY, QOS0, buf, len);
}

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,156 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file test_data_template.cc
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-09-29
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-09-29 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include <iostream>
#include <string>
#include "gtest/gtest.h"
#include "mqtt_client_test.h"
#include "qcloud_iot_common.h"
#include "qcloud_iot_explorer.h"
namespace mqtt_client_unittest {
static void _method_control_callback(UtilsJsonValue client_token, UtilsJsonValue params, void *usr_data) {
char buf[256];
Log_i("recv msg[%.*s]: params=%.*s", client_token.value_len, client_token.value, params.value_len, params.value);
IOT_DataTemplate_PropertyControlReply(usr_data, buf, sizeof(buf), 0, client_token);
}
static void _method_report_reply_callback(UtilsJsonValue client_token, int code, void *usr_data) {
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
}
static void _method_get_status_reply_callback(UtilsJsonValue client_token, int code, UtilsJsonValue reported,
UtilsJsonValue control, void *usr_data) {
char buf[256];
Log_i("recv msg[%.*s]: code=%d|reported=%.*s|control=%.*s", client_token.value_len, client_token.value, code,
reported.value_len, STRING_PTR_PRINT_SANITY_CHECK(reported.value), control.value_len,
STRING_PTR_PRINT_SANITY_CHECK(control.value));
IOT_DataTemplate_PropertyClearControl(usr_data, buf, sizeof(buf));
}
static void _method_report_info_reply_callback(UtilsJsonValue client_token, int code, void *usr_data) {
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
}
static void _method_clear_control_reply_callback(UtilsJsonValue client_token, int code, void *usr_data) {
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
}
static void _method_event_reply_callback(UtilsJsonValue client_token, int code, void *usr_data) {
Log_i("recv msg[%.*s]: code=%d", client_token.value_len, client_token.value, code);
}
static void _method_action_callback(UtilsJsonValue client_token, UtilsJsonValue action_id, UtilsJsonValue params,
void *usr_data) {
char buf[256];
Log_i("recv msg[%.*s]: action_id=%.*s|params=%.*s", client_token.value_len, client_token.value, action_id.value_len,
action_id.value, params.value_len, params.value);
IotDataTemplateActionReply reply = {
.client_token = client_token,
.code = 0,
.response = "{\"err_code\":0}",
};
ASSERT_GE(IOT_DataTemplate_ActionReply(usr_data, buf, sizeof(buf), reply), 0);
}
/**
* @brief Test data template property.
*
*/
TEST_F(MqttClientTest, data_template_property) {
char buf[1024];
IotDataTemplateCallback callback = {
.property_callback = {.method_control_callback = _method_control_callback,
.method_report_reply_callback = _method_report_reply_callback,
.method_get_status_reply_callback = _method_get_status_reply_callback,
.method_report_info_reply_callback = _method_report_info_reply_callback,
.method_clear_control_reply_callback = _method_clear_control_reply_callback},
.event_callback = {.method_event_reply_callback = NULL},
.action_callback = {.method_action_callback = NULL},
};
const char *report_info =
"{\"module_hardinfo\":\"ESP8266\",\"module_softinfo\":\"V1.0\", \"fw_ver\":\"4.0.0\", "
"\"imei\":\"11-22-33-44\",\"lat\":\"22.546015\",\"lon\":\"113.941125\",\"mac\":\"11:22:33:44:55:66\",\"device_"
"label\":{\"append_"
"info\":\"your self defined info\"}}";
ASSERT_EQ(IOT_DataTemplate_Init(client, callback, client), 0);
ASSERT_EQ(IOT_DataTemplate_PropertyReportInfo(client, buf, sizeof(buf), report_info), 0);
ASSERT_EQ(IOT_DataTemplate_PropertyGetStatus(client, buf, sizeof(buf)), 0);
IOT_DataTemplate_Deinit(client);
}
/**
* @brief Test data template event.
*
*/
TEST_F(MqttClientTest, data_template_event) {
char buf[1024];
IotDataTemplateCallback callback = {
.property_callback = {0},
.event_callback = {.method_event_reply_callback = _method_event_reply_callback},
.action_callback = {.method_action_callback = _method_action_callback},
};
const char *report_property = "{\"power_switch\":0}";
IotDataTemplateEventData event_data = {
.event_id = "status_report",
.type = IOT_DATA_TEMPLATE_EVENT_TYPE_INFO,
.params = "{\"status\":0,\"message\":\"ok\"}",
};
ASSERT_EQ(IOT_DataTemplate_Init(client, callback, client), 0);
ASSERT_GE(IOT_DataTemplate_EventPost(client, buf, sizeof(buf), event_data), 0);
IOT_DataTemplate_Deinit(client);
}
/**
* @brief Test data template action.
*
*/
TEST_F(MqttClientTest, data_template_action) {
char buf[1024];
IotDataTemplateCallback callback = {
.property_callback = {0},
.event_callback = {.method_event_reply_callback = NULL},
.action_callback = {.method_action_callback = _method_action_callback},
};
ASSERT_EQ(IOT_DataTemplate_Init(client, callback, client), 0);
IOT_DataTemplate_Deinit(client);
}
} // namespace mqtt_client_unittest

View File

@@ -0,0 +1,25 @@
file(GLOB src_service_mqtt
${CMAKE_CURRENT_SOURCE_DIR}/src/*.c
${CMAKE_CURRENT_SOURCE_DIR}/src/file_manage/*.c
${CMAKE_CURRENT_SOURCE_DIR}/src/gateway_scene/*.c
)
set(inc_service_mqtt ${CMAKE_CURRENT_SOURCE_DIR}/inc/)
set(src_services ${src_services} ${src_service_mqtt} PARENT_SCOPE)
set(inc_services ${inc_services} ${inc_service_mqtt} PARENT_SCOPE)
if(${CONFIG_COMPILE_SAMPLE} STREQUAL "ON")
file(GLOB src_file_manage_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/file_manage_sample.c)
file(GLOB src_gateway_scene_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/gateway_scene_sample.c)
add_executable(file_manage_sample ${src_file_manage_sample})
add_executable(gateway_scene_sample ${src_gateway_scene_sample})
target_link_libraries(file_manage_sample ${libsdk})
target_link_libraries(gateway_scene_sample ${libsdk})
endif()
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,100 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file service_mqtt.h
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2022-01-11
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022-01-11 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#ifndef IOT_HUB_DEVICE_C_SDK_SERVICES_EXPLORER_SERVICE_MQTT_INC_SERVICE_MQTT_H_
#define IOT_HUB_DEVICE_C_SDK_SERVICES_EXPLORER_SERVICE_MQTT_INC_SERVICE_MQTT_H_
#include "qcloud_iot_common.h"
#include "utils_list.h"
/**
* @brief Service type, only file manage supportted now.
*
*/
typedef enum {
SERVICE_TYPE_FILE_MANAGE = 0,
SERVICE_TYPE_GATEWAY_SCENE,
} ServiceType;
/**
* @brief Register to server list.
*
*/
typedef struct {
ServiceType type;
const char **method_list;
int method_num;
OnMessageHandler message_handle;
void *usr_data;
void (*user_data_free)(void *);
} ServiceRegisterParams;
/**
* @brief Subscribe service mqtt.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int service_mqtt_init(void *client);
/**
* @brief If no service in the service list, then unsubscribe.
*
* @param[in,out] client pointer to mqtt client
*/
void service_mqtt_deinit(void *client);
/**
* @brief Publish to service mqtt topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] qos @see QoS
* @param[in] payload payload of mqtt packet
* @param[in] payload_len payload len
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int service_mqtt_publish(void *client, QoS qos, const char *payload, int payload_len);
/**
* @brief Register server handler to server list.
*
* @param[in,out] client pointer to mqtt client
* @param[in] params @see ServiceRegisterParams
* @return @see IotReturnCode
*/
int service_mqtt_service_register(void *client, const ServiceRegisterParams *params);
/**
* @brief Unregister server handler from server list.
*
* @param[in,out] client pointer to mqtt client
* @param[in] type @see ServiceType
*/
void service_mqtt_service_unregister(void *client, ServiceType type);
#endif // IOT_HUB_DEVICE_C_SDK_SERVICES_EXPLORER_SERVICE_MQTT_INC_SERVICE_MQTT_H_

View File

@@ -0,0 +1,340 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file broadcast_sample.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-18
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "qcloud_iot_explorer.h"
#include "utils_log.h"
/**
* @brief MQTT event callback, @see MQTTEventHandleFun
*
* @param[in] client pointer to mqtt client
* @param[in] handle_context context
* @param[in] msg msg
*/
static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
{
MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
uintptr_t packet_id = (uintptr_t)msg->msg;
switch (msg->event_type) {
case MQTT_EVENT_UNDEF:
Log_i("undefined event occur.");
break;
case MQTT_EVENT_DISCONNECT:
Log_i("MQTT disconnect.");
break;
case MQTT_EVENT_RECONNECT:
Log_i("MQTT reconnect.");
break;
case MQTT_EVENT_PUBLISH_RECEIVED:
Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
break;
case MQTT_EVENT_SUBSCRIBE_SUCCESS:
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_SUBSCRIBE_NACK:
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_NACK:
Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_SUCCESS:
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_TIMEOUT:
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_NACK:
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
break;
default:
Log_i("Should NOT arrive here.");
break;
}
}
/**
* @brief Setup MQTT construct parameters.
*
* @param[in,out] initParams @see MQTTInitParams
* @param[in] device_info @see DeviceInfo
*/
static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
{
init_params->device_info = device_info;
init_params->event_handle.h_fp = _mqtt_event_handler;
}
// ----------------------------------------------------------------------------
// file list function
// ----------------------------------------------------------------------------
static IotFileManageFileInfo sg_file_list[5];
static void _file_info_init(IotFileManageFileInfo *file_info, UtilsJsonValue file_name, UtilsJsonValue file_type,
UtilsJsonValue version)
{
strncpy(file_info->file_name, file_name.value, file_name.value_len);
strncpy(file_info->file_version, version.value, version.value_len);
file_info->file_type = IOT_FileManage_GetFileType(file_type.value, file_type.value_len);
}
static void _add_file_info_to_list(IotFileManageFileInfo *file_info)
{
int i_free = -1;
for (int i = 0; i < sizeof(sg_file_list) / sizeof(sg_file_list[0]); i++) {
if (!strcmp(sg_file_list[i].file_name, file_info->file_name)) {
memcpy(sg_file_list + i, file_info, sizeof(IotFileManageFileInfo));
return;
}
if (sg_file_list[i].file_name[0] == '\0' && i_free == -1) {
i_free = i;
}
}
if (i_free != -1) {
memcpy(sg_file_list + i_free, file_info, sizeof(IotFileManageFileInfo));
}
}
static void _del_file_info_to_list(IotFileManageFileInfo *file_info)
{
for (int i = 0; i < sizeof(sg_file_list) / sizeof(sg_file_list[0]); i++) {
if (!strcmp(sg_file_list[i].file_name, file_info->file_name)) {
sg_file_list[i].file_name[0] = '\0';
}
}
}
// ----------------------------------------------------------------------------
// file function
// ----------------------------------------------------------------------------
static int _file_download(void *client, char *buf, int buf_len, UtilsJsonValue url, UtilsJsonValue md5sum,
uint32_t file_size, IotFileManageFileInfo *file_info)
{
Log_i("downloading file:url=%.*s|md5sum=%.*s|file_size=%u", url.value_len, url.value, md5sum.value_len,
md5sum.value, file_size);
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOADING, 0, file_info->file_name,
file_info->file_version);
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOADING, 50, file_info->file_name,
file_info->file_version);
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOADING, 100, file_info->file_name,
file_info->file_version);
return 0;
}
static int _file_upgrade(void *client, char *buf, int buf_len, IotFileManageFileInfo *file_info)
{
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_BEGIN, 0, file_info->file_name,
file_info->file_version);
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_SUCCESS, 0, file_info->file_name,
file_info->file_version);
_add_file_info_to_list(file_info);
return 0;
}
static int _file_del(void *client, char *buf, int buf_len, IotFileManageFileInfo *file_info)
{
_del_file_info_to_list(file_info);
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_DEL_SUCCESS, 0, file_info->file_name,
file_info->file_version);
return 0;
}
// ----------------------------------------------------------------------------
// OTA callback
// ----------------------------------------------------------------------------
static void _file_manage_update_file_callback(UtilsJsonValue file_name, UtilsJsonValue file_type,
UtilsJsonValue version, UtilsJsonValue url, UtilsJsonValue md5sum,
uint32_t file_size, void *usr_data)
{
IotFileManageFileInfo file_info = {0};
char buf[256];
int buf_len = sizeof(buf);
Log_i("recv file: file_name=%.*s|type=%.*s|version=%.*s|url=%.*s|md5sum=%.*s|file_size=%u", file_name.value_len,
file_name.value, file_type.value_len, file_type.value, version.value_len, version.value, url.value_len,
url.value, md5sum.value_len, md5sum.value, file_size);
_file_info_init(&file_info, file_name, file_type, version);
int rc = _file_download(usr_data, buf, buf_len, url, md5sum, file_size, &file_info);
if (rc) {
IOT_FileManage_Report(usr_data, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_FAIL, 0, file_info.file_name,
file_info.file_version);
return;
}
_file_upgrade(usr_data, buf, buf_len, &file_info);
IOT_FileManage_ReportFileList(usr_data, buf, buf_len, sg_file_list, sizeof(sg_file_list) / sizeof(sg_file_list[0]));
}
static void _file_manage_del_file_callback(UtilsJsonValue file_name, UtilsJsonValue file_type, UtilsJsonValue version,
void *usr_data)
{
char buf[256];
int buf_len = sizeof(buf);
IotFileManageFileInfo file_info = {0};
_file_info_init(&file_info, file_name, file_type, version);
_file_del(usr_data, buf, buf_len, &file_info);
IOT_FileManage_ReportFileList(usr_data, buf, buf_len, sg_file_list, sizeof(sg_file_list) / sizeof(sg_file_list[0]));
}
static void _file_manage_report_file_version_reponse_callback(UtilsJsonValue file_list, int result_code, void *usr_data)
{
Log_i("recv file_list=%.*s|result_code=%d", file_list.value_len, file_list.value, result_code);
}
static void _file_manage_request_file_url_response_callback(UtilsJsonValue url, UtilsJsonValue file_token,
int result_code, void *usr_data)
{
Log_i("recv response: url=%.*s|file_token=%.*s|result_code=%d", url.value_len, url.value, file_token.value_len,
file_token.value, result_code);
}
// ----------------------------------------------------------------------------
// Main
// ----------------------------------------------------------------------------
static int sg_main_exit = 0;
#ifdef __linux__
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
static void _main_exit(int sig)
{
Log_e("demo exit by signal:%d\n", sig);
sg_main_exit = 1;
}
#endif
int main(int argc, char **argv)
{
#ifdef __linux__
signal(SIGINT, _main_exit);
#endif
int rc;
char buf[1024];
// init log level
LogHandleFunc func = 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!");
goto exit;
}
IotFileManageCallback file_manage_callback = {
.update_file_callback = _file_manage_update_file_callback,
.del_file_callback = _file_manage_del_file_callback,
.report_file_version_reponse_callback = _file_manage_report_file_version_reponse_callback,
.request_file_url_response_callback = _file_manage_request_file_url_response_callback,
};
rc = IOT_FileManage_Init(client, file_manage_callback, client);
if (rc) {
Log_e("OTA init failed!, rc=%d", rc);
goto exit;
}
rc = IOT_FileManage_ReportFileList(client, buf, sizeof(buf), sg_file_list,
sizeof(sg_file_list) / sizeof(sg_file_list[0]));
if (rc) {
Log_e("OTA report version failed!, rc=%d", rc);
goto exit;
}
do {
rc = IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT);
switch (rc) {
case QCLOUD_RET_SUCCESS:
break;
case QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT:
continue;
case QCLOUD_RET_MQTT_RECONNECTED:
break;
default:
Log_e("Exit loop caused of errCode:%d", rc);
goto exit;
}
} while (!sg_main_exit);
exit:
IOT_FileManage_Deinit(client);
rc = IOT_MQTT_Destroy(&client);
utils_log_deinit();
return rc;
}

View File

@@ -0,0 +1,238 @@
/**
* @file gateway_scene_sample.c
* @author {hubert} ({hubertxxu@tencent.com})
* @brief
* @version 1.0
* @date 2022-06-16
*
* @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-16 1.0 hubertxxu first commit
* </table>
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "qcloud_iot_common.h"
#include "qcloud_iot_explorer.h"
#include "utils_log.h"
/**
* @brief MQTT event callback, @see MQTTEventHandleFun
*
* @param[in] client pointer to mqtt client
* @param[in] handle_context context
* @param[in] msg msg
*/
static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
{
MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
uintptr_t packet_id = (uintptr_t)msg->msg;
switch (msg->event_type) {
case MQTT_EVENT_UNDEF:
Log_i("undefined event occur.");
break;
case MQTT_EVENT_DISCONNECT:
Log_i("MQTT disconnect.");
break;
case MQTT_EVENT_RECONNECT:
Log_i("MQTT reconnect.");
break;
case MQTT_EVENT_PUBLISH_RECEIVED:
Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
break;
case MQTT_EVENT_SUBSCRIBE_SUCCESS:
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_SUBSCRIBE_NACK:
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_NACK:
Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_SUCCESS:
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_TIMEOUT:
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_NACK:
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
break;
default:
Log_i("Should NOT arrive here.");
break;
}
}
/**
* @brief Setup MQTT construct parameters.
*
* @param[in,out] initParams @see MQTTInitParams
* @param[in] device_info @see DeviceInfo
*/
static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
{
init_params->device_info = device_info;
init_params->event_handle.h_fp = _mqtt_event_handler;
}
// ----------------------------------------------------------------------------
// Gateway scene callbacks
// ----------------------------------------------------------------------------
static int _gateway_scene_handles_callback(UtilsJsonValue scene_id, UtilsJsonValue scene_name,
UtilsJsonValue scene_update_time, UtilsJsonValue scene_handles, void *usr_data)
{
Log_d("recv scene handles : id:%.*s| name:%.*s| update time:%.*s| handles:%.*s", scene_id.value_len,
scene_id.value, scene_name.value_len, scene_name.value, scene_update_time.value_len, scene_update_time.value,
scene_handles.value_len, scene_handles.value);
return 0;
}
static int _gateway_run_scene_callback(UtilsJsonValue scene_id, void *usr_data)
{
Log_d("run scene: id:%.*s", scene_id.value_len, scene_id.value);
return 0;
}
static int _gateway_delete_scene_callback(UtilsJsonValue scene_id, void *usr_data)
{
Log_d("delete scene: id:%.*s", scene_id.value_len, scene_id.value);
return 0;
}
static int _gateway_reload_scene_reply_callback(int result_code, UtilsJsonValue status, UtilsJsonValue scene_result,
void *usr_data)
{
Log_d("reload scene reply: result code:%d|status:%.*s|result:%.*s", result_code, status.value_len, status.value,
scene_result.value_len, scene_result.value);
return 0;
}
// ----------------------------------------------------------------------------
// 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[4096];
// 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;
}
IoTGatewaySceneCallback cbs = {
.gateway_delete_scene_callback = _gateway_delete_scene_callback,
.gateway_reload_scene_reply_callback = _gateway_reload_scene_reply_callback,
.gateway_run_scene_callback = _gateway_run_scene_callback,
.gateway_scene_handles_callback = _gateway_scene_handles_callback,
};
rc = IOT_GatewayScene_Init(client, cbs, NULL);
rc = IOT_GatewayScene_Reload(client, buf, sizeof(buf));
IoTGatewaySceneInnerList scene_inner_list[MAX_LENGTH_INNER_SCENE_LIST];
for (int i = 0; i < MAX_LENGTH_INNER_SCENE_LIST; i++) {
HAL_Snprintf(scene_inner_list[i].inner_scene_id, MAX_LENGTH_INNER_SCENE_ID, "inner_scene_id_%d", i);
HAL_Snprintf(scene_inner_list[i].inner_scene_name, MAX_LENGTH_INNER_SCENE_NAME, "inner_scene_name_%d", i);
}
rc = IOT_GatewayScene_ReportInnerList(client, buf, sizeof(buf), scene_inner_list, MAX_LENGTH_INNER_SCENE_LIST);
do {
rc = IOT_MQTT_Yield(client, 2000);
if (rc == QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT) {
HAL_SleepMs(1000);
continue;
} else if (rc != QCLOUD_RET_SUCCESS && rc != QCLOUD_RET_MQTT_RECONNECTED) {
Log_e("exit with error: %d", rc);
break;
}
} while (!sg_main_exit);
IOT_GatewayScene_Deinit(client);
rc |= IOT_MQTT_Destroy(&client);
utils_log_deinit();
return rc;
}

View File

@@ -0,0 +1,455 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file file_manage.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2022-01-11
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022-01-11 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "qcloud_iot_file_manage.h"
#include "service_mqtt.h"
/**
* @brief Context of file manage, callback and user data.
*
*/
typedef struct {
IotFileManageCallback callback;
void *usr_data;
} FileManageContext;
/**
* @brief File manage down stream message type.
*
*/
typedef enum {
FILE_MANAGE_DOWN_MESSAGE_TYPE_UPDATE = 0,
FILE_MANAGE_DOWN_MESSAGE_TYPE_DEL,
FILE_MANAGE_DOWN_MESSAGE_TYPE_REPORT_VERSION_RESPONSE,
FILE_MANAGE_DOWN_MESSAGE_TYPE_POST_REQUEST_RESPONSE,
} FileManageDownMessageType;
/**
* @brief Method string of down stream message. Order @see FileManageDownMessageType.
*
*/
static const char *sg_file_manage_method_str[] = {
[FILE_MANAGE_DOWN_MESSAGE_TYPE_UPDATE] = "update_resource",
[FILE_MANAGE_DOWN_MESSAGE_TYPE_DEL] = "del_resource",
[FILE_MANAGE_DOWN_MESSAGE_TYPE_REPORT_VERSION_RESPONSE] = "report_version_rsp",
[FILE_MANAGE_DOWN_MESSAGE_TYPE_POST_REQUEST_RESPONSE] = "request_url_resp",
};
/**
* @brief Method string of down stream message. Order @see IotFileManageFileType.
*
*/
static const char *sg_file_manage_file_type_str[] = {
[IOT_FILE_MANAGE_FILE_TYPE_FILE] = "FILE",
[IOT_FILE_MANAGE_FILE_TYPE_AUDIO] = "AUDIO",
[IOT_FILE_MANAGE_FILE_TYPE_VOICE] = "VOICE",
[IOT_FILE_MANAGE_FILE_TYPE_VIDEO] = "VIDEO",
};
/**
* @brief Handle file mange down stream message.
*
* @param[in] type @see FileManageDownMessageType
* @param[in] message @see MQTTMessage
* @param[in] callback @see IotFileManageCallback
* @param[in,out] usr_data user define in IOT_FileManage_Init
*/
static void _parse_update_payload_and_callback(FileManageDownMessageType type, const MQTTMessage *message,
const IotFileManageCallback *callback, void *usr_data)
{
int rc, result_code;
uint32_t file_size = 0;
UtilsJsonValue file_name, file_type, version, md5sum, url, value_result_code, file_token, file_list = {0};
// callback
switch (type) {
case FILE_MANAGE_DOWN_MESSAGE_TYPE_UPDATE:
if (!callback->update_file_callback) {
return;
}
rc = utils_json_value_get("resource_name", sizeof("resource_name") - 1, message->payload_str,
message->payload_len, &file_name);
rc |= utils_json_value_get("resource_type", sizeof("resource_type") - 1, message->payload_str,
message->payload_len, &file_type);
rc |= utils_json_value_get("version", sizeof("version") - 1, message->payload_str, message->payload_len,
&version);
rc |= utils_json_value_get("url", sizeof("url") - 1, message->payload_str, message->payload_len, &url);
rc |= utils_json_value_get("md5sum", sizeof("md5sum") - 1, message->payload_str, message->payload_len,
&md5sum);
rc |= utils_json_get_uint32("file_size", sizeof("file_size") - 1, message->payload_str,
message->payload_len, &file_size);
if (rc) {
goto error;
}
callback->update_file_callback(file_name, file_type, version, url, md5sum, file_size, usr_data);
break;
case FILE_MANAGE_DOWN_MESSAGE_TYPE_DEL:
if (!callback->del_file_callback) {
return;
}
rc = utils_json_value_get("resource_name", sizeof("resource_name") - 1, message->payload_str,
message->payload_len, &file_name);
rc |= utils_json_value_get("resource_type", sizeof("resource_type") - 1, message->payload_str,
message->payload_len, &file_type);
rc |= utils_json_value_get("version", sizeof("version") - 1, message->payload_str, message->payload_len,
&version);
if (rc) {
goto error;
}
callback->del_file_callback(file_name, file_type, version, usr_data);
break;
case FILE_MANAGE_DOWN_MESSAGE_TYPE_REPORT_VERSION_RESPONSE:
if (!callback->report_file_version_reponse_callback) {
return;
}
// maybe no resource
utils_json_value_get("resource_list", sizeof("resource_list") - 1, message->payload_str,
message->payload_len, &file_list);
rc = utils_json_value_get("result_code", sizeof("result_code") - 1, message->payload_str,
message->payload_len, &value_result_code);
rc |= utils_json_value_data_get(value_result_code, UTILS_JSON_VALUE_TYPE_INT32, &result_code);
if (rc) {
goto error;
}
callback->report_file_version_reponse_callback(file_list, result_code, usr_data);
break;
case FILE_MANAGE_DOWN_MESSAGE_TYPE_POST_REQUEST_RESPONSE:
if (!callback->request_file_url_response_callback) {
return;
}
rc = utils_json_value_get("resource_url", sizeof("resource_url") - 1, message->payload_str,
message->payload_len, &url);
rc |= utils_json_value_get("resource_token", sizeof("resource_token") - 1, message->payload_str,
message->payload_len, &file_token);
rc |= utils_json_value_get("result_code", sizeof("result_code") - 1, message->payload_str,
message->payload_len, &value_result_code);
rc |= utils_json_value_data_get(value_result_code, UTILS_JSON_VALUE_TYPE_INT32, &result_code);
if (rc) {
goto error;
}
callback->request_file_url_response_callback(file_list, file_token, result_code, usr_data);
break;
default:
break;
}
return;
error:
Log_e("invalid format of payload!");
return;
}
/**
* @brief Mqtt message callback for file manage.
*
* @param[in,out] client pointer to mqtt client
* @param[in] message message from topic
* @param[in,out] usr_data pointer to @see FileManageContext
*/
static void _file_manage_message_callback(void *client, const MQTTMessage *message, void *usr_data)
{
const char *file_manage_method_str[] = {
[FILE_MANAGE_DOWN_MESSAGE_TYPE_UPDATE] = "update_resource",
[FILE_MANAGE_DOWN_MESSAGE_TYPE_DEL] = "del_resource",
[FILE_MANAGE_DOWN_MESSAGE_TYPE_REPORT_VERSION_RESPONSE] = "report_version_rsp",
[FILE_MANAGE_DOWN_MESSAGE_TYPE_POST_REQUEST_RESPONSE] = "request_url_resp",
};
int rc, i = 0;
FileManageContext *file_manage_context = (FileManageContext *)usr_data;
UtilsJsonValue method;
Log_d("receive file manage message:%.*s", message->payload_len, message->payload_str);
rc = utils_json_value_get("method", sizeof("method") - 1, message->payload_str, message->payload_len, &method);
if (rc) {
Log_e("invalid file manage message!");
return;
}
for (i = FILE_MANAGE_DOWN_MESSAGE_TYPE_UPDATE; i <= FILE_MANAGE_DOWN_MESSAGE_TYPE_POST_REQUEST_RESPONSE; i++) {
if (!strncmp(method.value, file_manage_method_str[i], method.value_len)) {
Log_d("callback file manage message!");
_parse_update_payload_and_callback(i, message, &file_manage_context->callback,
file_manage_context->usr_data);
}
}
}
/**
* @brief File manage init, register handler to server list.
*
* @param[in,out] client pointer to mqtt client
* @param[in] callback @see IotFileManageCallback
* @param[in] usr_data usr data used in callback
* @return 0 for success, or err code (<0) @see IotReturnCode
*/
int IOT_FileManage_Init(void *client, IotFileManageCallback callback, void *usr_data)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
int rc = service_mqtt_init(client);
if (rc) {
return rc;
}
FileManageContext *file_manage_context = (FileManageContext *)HAL_Malloc(sizeof(FileManageContext));
if (!file_manage_context) {
return QCLOUD_ERR_MALLOC;
}
file_manage_context->callback = callback;
file_manage_context->usr_data = usr_data;
ServiceRegisterParams params = {
.type = SERVICE_TYPE_FILE_MANAGE,
.method_list = sg_file_manage_method_str,
.method_num = sizeof(sg_file_manage_method_str) / sizeof(sg_file_manage_method_str[0]),
.message_handle = _file_manage_message_callback,
.usr_data = file_manage_context,
.user_data_free = HAL_Free,
};
rc = service_mqtt_service_register(client, &params);
if (rc) {
HAL_Free(file_manage_context);
}
return rc;
}
/**
* @brief File manage deinit, unregister handler from server list.
*
* @param[in,out] client pointer to mqtt client
*/
void IOT_FileManage_Deinit(void *client)
{
POINTER_SANITY_CHECK_RTN(client);
service_mqtt_service_unregister(client, SERVICE_TYPE_FILE_MANAGE);
service_mqtt_deinit(client);
}
/**
* @brief Report file manage message.
*
* @param[in,out] client pointer to mqtt client
* @param[out] buf publish message buffer
* @param[in] buf_len buffer len
* @param[in] report_type @see IotFileManageReportType
* @param[in] progress progress using in IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOADING
* @param[in] file_name_or_token token using in post event;file name using in other event
* @param[in] version file version
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_FileManage_Report(void *client, char *buf, int buf_len, IotFileManageReportType report_type, int progress,
const char *file_name_or_token, const char *version)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
NUMBERIC_SANITY_CHECK(buf_len, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(file_name_or_token, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(version, QCLOUD_ERR_INVAL);
/**
* @brief order @see IotOTAReportType
*
*/
const char *state_string[] = {
[IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOADING] = "downloading",
[IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_BEGIN] = "burning",
[IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_SUCCESS] = "done",
[IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOAD_TIMEOUT] = "fail",
[IOT_FILE_MANAGE_REPORT_TYPE_FILE_NOT_EXIST] = "fail",
[IOT_FILE_MANAGE_REPORT_TYPE_AUTH_FAIL] = "fail",
[IOT_FILE_MANAGE_REPORT_TYPE_MD5_NOT_MATCH] = "fail",
[IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_FAIL] = "fail",
[IOT_FILE_MANAGE_REPORT_TYPE_SPACE_NOT_ENOUGH] = "fail",
[IOT_FILE_MANAGE_REPORT_TYPE_DEL_SUCCESS] = "done",
[IOT_FILE_MANAGE_REPORT_TYPE_DEL_FAIL] = "fail",
[IOT_FILE_MANAGE_REPORT_TYPE_POST_SUCCESS] = "done",
[IOT_FILE_MANAGE_REPORT_TYPE_POST_FAIL] = "fail",
};
int result_code[] = {
[IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOADING] = 0, [IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_BEGIN] = 0,
[IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_SUCCESS] = 0, [IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOAD_TIMEOUT] = -1,
[IOT_FILE_MANAGE_REPORT_TYPE_FILE_NOT_EXIST] = -2, [IOT_FILE_MANAGE_REPORT_TYPE_AUTH_FAIL] = -3,
[IOT_FILE_MANAGE_REPORT_TYPE_MD5_NOT_MATCH] = -4, [IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_FAIL] = -5,
[IOT_FILE_MANAGE_REPORT_TYPE_SPACE_NOT_ENOUGH] = -6, [IOT_FILE_MANAGE_REPORT_TYPE_DEL_SUCCESS] = 0,
[IOT_FILE_MANAGE_REPORT_TYPE_DEL_FAIL] = -7, [IOT_FILE_MANAGE_REPORT_TYPE_POST_SUCCESS] = 0,
[IOT_FILE_MANAGE_REPORT_TYPE_POST_FAIL] = -8,
};
const char *result_msg[] = {
[IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOADING] = "",
[IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_BEGIN] = "",
[IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_SUCCESS] = "",
[IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOAD_TIMEOUT] = "timeout",
[IOT_FILE_MANAGE_REPORT_TYPE_FILE_NOT_EXIST] = "file not exit",
[IOT_FILE_MANAGE_REPORT_TYPE_AUTH_FAIL] = "auth fail",
[IOT_FILE_MANAGE_REPORT_TYPE_MD5_NOT_MATCH] = "md5 not match",
[IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_FAIL] = "upgrade fail",
[IOT_FILE_MANAGE_REPORT_TYPE_SPACE_NOT_ENOUGH] = "space not enough",
[IOT_FILE_MANAGE_REPORT_TYPE_DEL_SUCCESS] = "",
[IOT_FILE_MANAGE_REPORT_TYPE_DEL_FAIL] = "del fail",
[IOT_FILE_MANAGE_REPORT_TYPE_POST_SUCCESS] = "",
[IOT_FILE_MANAGE_REPORT_TYPE_POST_FAIL] = "post fail",
};
int len;
switch (report_type) {
case IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOADING:
len = HAL_Snprintf(
buf, buf_len,
"{\"method\":\"report_progress\",\"report\":{\"progress\":{\"resource_name\":\"%s\",\"state\":\"%s\","
"\"percent\":\"%d\",\"result_code\":\"%d\",\"result_msg\":\"\"},\"version\":\"%s\"}}",
file_name_or_token, state_string[report_type], progress, result_code[report_type], version);
break;
case IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_BEGIN:
case IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_SUCCESS:
case IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOAD_TIMEOUT:
case IOT_FILE_MANAGE_REPORT_TYPE_FILE_NOT_EXIST:
case IOT_FILE_MANAGE_REPORT_TYPE_AUTH_FAIL:
case IOT_FILE_MANAGE_REPORT_TYPE_MD5_NOT_MATCH:
case IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_FAIL:
case IOT_FILE_MANAGE_REPORT_TYPE_SPACE_NOT_ENOUGH:
len = HAL_Snprintf(buf, buf_len,
"{\"method\":\"report_result\",\"report\":{\"progress\":{\"resource_name\":\"%s\","
"\"state\":\"%s\",\"result_code\":\"%d\",\"result_msg\":\"%s\"},\"version\":\"%s\"}}",
file_name_or_token, state_string[report_type], result_code[report_type],
result_msg[report_type], version);
break;
case IOT_FILE_MANAGE_REPORT_TYPE_DEL_SUCCESS:
case IOT_FILE_MANAGE_REPORT_TYPE_DEL_FAIL:
len = HAL_Snprintf(buf, buf_len,
"{\"method\":\"del_result\",\"report\":{\"progress\":{\"resource_name\":\"%s\","
"\"state\":\"%s\",\"result_code\":\"%d\", \"result_msg\":\"%s\"}, \"version\":\"%s\"}}",
file_name_or_token, state_string[report_type], result_code[report_type],
result_msg[report_type], version);
break;
case IOT_FILE_MANAGE_REPORT_TYPE_POST_SUCCESS:
case IOT_FILE_MANAGE_REPORT_TYPE_POST_FAIL:
len = HAL_Snprintf(buf, buf_len,
"{\"method\":\"report_post_result\",\"report\":{\"progress\":{\"resource_token\":\"%s\","
"\"state\":\"%s\",\"result_code\":\"%d\", \"result_msg\":\"%s\"}}}",
file_name_or_token, state_string[report_type], result_code[report_type],
result_msg[report_type]);
break;
default:
return QCLOUD_ERR_INVAL;
}
return service_mqtt_publish(client, QOS0, buf, len);
}
/**
* @brief Report file list to server mqtt topic.
*
* @param[in,out] client pointer to mqtt client
* @param[out] buf publish message buffer
* @param[in] buf_len buffer len
* @param[in] file_list file list of @see IotFileManageFileInfo; file name which is "\0" means invalid.
* @param[in] max_num max num of file list.
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_FileManage_ReportFileList(void *client, char *buf, int buf_len, const IotFileManageFileInfo file_list[],
int max_num)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
NUMBERIC_SANITY_CHECK(buf_len, QCLOUD_ERR_INVAL);
int len = HAL_Snprintf(buf, buf_len, "{\"method\":\"report_version\",\"report\":{\"resource_list\":[");
for (int i = 0; i < max_num; i++) {
if (file_list[i].file_type == IOT_FILE_MANAGE_FILE_TYPE_UNKOWN) {
continue;
}
if (file_list[i].file_name[0] != '\0') {
len += HAL_Snprintf(buf + len, buf_len - len,
"{\"resource_name\":\"%s\",\"version\":\"%s\",\"resource_type\":\"%s\"},",
file_list[i].file_name, file_list[i].file_version,
sg_file_manage_file_type_str[file_list[i].file_type]);
}
}
if (buf[len - 1] != '[') {
len--; // remove the last ','
}
len += HAL_Snprintf(buf + len, buf_len - len, "]}}");
return service_mqtt_publish(client, QOS0, buf, len);
}
/**
* @brief Request url to upload.
*
* @param[in,out] client pointer to mqtt client
* @param[out] buf publish message buffer
* @param[in] buf_len buffer len
* @param[in] file_info file to upload, @see IotFileManageFileInfo
* @param[in] request_id user defined, to keep unique.
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int IOT_FileManage_PostRequest(void *client, char *buf, int buf_len, const IotFileManageFileInfo *file_info,
int request_id)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(buf, QCLOUD_ERR_INVAL);
NUMBERIC_SANITY_CHECK(buf_len, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(file_info, QCLOUD_ERR_INVAL);
NUMBERIC_SANITY_CHECK(file_info->file_type, QCLOUD_ERR_INVAL);
int len = HAL_Snprintf(buf, buf_len,
"{\"method\":\"request_url\",\"request_id\":\"%d\","
"\"report\":{\"resource_name\":\"%s\",\"version\":\"%s\",\"resource_type\":\"%s\"}}",
request_id, file_info->file_name, file_info->file_version,
sg_file_manage_file_type_str[file_info->file_type]);
return service_mqtt_publish(client, QOS0, buf, len);
}
/**
* @brief Get file type according to string. @see sg_file_manage_file_type_str.
*
* @param[in] file_type file type string
* @param[in] len string length
* @return @see IotFileManageFileType
*/
IotFileManageFileType IOT_FileManage_GetFileType(const char *file_type, int len)
{
for (int i = 0; i < sizeof(sg_file_manage_file_type_str) / sizeof(sg_file_manage_file_type_str[0]); i++) {
if (len != strlen(sg_file_manage_file_type_str[i])) {
continue;
}
if (!strncmp(file_type, sg_file_manage_file_type_str[i], len)) {
return i;
}
}
return IOT_FILE_MANAGE_FILE_TYPE_UNKOWN;
}

View File

@@ -0,0 +1,287 @@
/**
* @file gateway_scene.c
* @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>
*/
#include "service_mqtt.h"
#include "qcloud_iot_gateway_scene.h"
typedef enum {
GATEWAY_SCENE_DOWN_MESSAGE_TYPE_SCENE_HANDLES = 0,
GATEWAY_SCENE_DOWN_MESSAGE_TYPE_RUN_SCENE,
GATEWAY_SCENE_DOWN_MESSAGE_TYPE_DELETE_SCENE,
GATEWAY_SCENE_DOWN_MESSAGE_TYPE_RELOAD_SCENE_HANDLES_REPLY,
GATEWAY_SCENE_DOWN_MESSAGE_TYPE_REPORT_INNER_SCENE_LIST_REPLY,
} GatewaySceneDownMessageType;
static const char *sg_gateway_scene_method_str[] = {
[GATEWAY_SCENE_DOWN_MESSAGE_TYPE_SCENE_HANDLES] = "gateway_scene_handles",
[GATEWAY_SCENE_DOWN_MESSAGE_TYPE_RUN_SCENE] = "gateway_run_scene",
[GATEWAY_SCENE_DOWN_MESSAGE_TYPE_DELETE_SCENE] = "gateway_delete_scene",
[GATEWAY_SCENE_DOWN_MESSAGE_TYPE_RELOAD_SCENE_HANDLES_REPLY] = "gateway_reload_scene_handles_reply",
[GATEWAY_SCENE_DOWN_MESSAGE_TYPE_REPORT_INNER_SCENE_LIST_REPLY] = "gateway_report_inner_scene_list_reply",
};
typedef struct {
IoTGatewaySceneCallback callback;
void *usr_data;
} GatewaySceneContext;
/**
* @brief Handle file mange down stream message.
*
* @param[in] type @see FileManageDownMessageType
* @param[in] message @see MQTTMessage
* @param[in] callback @see IotFileManageCallback
* @param[in,out] usr_data user define in IOT_FileManage_Init
*/
static void _parse_update_payload_and_callback(GatewaySceneDownMessageType type, const MQTTMessage *message,
const IoTGatewaySceneCallback *callback, void *usr_data)
{
int rc, result_code;
UtilsJsonValue scene_id, scene_name, scene_update_time, scene_handles, scene_result, status;
// callback
switch (type) {
case GATEWAY_SCENE_DOWN_MESSAGE_TYPE_SCENE_HANDLES:
if (!callback->gateway_scene_handles_callback) {
return;
}
rc = utils_json_value_get("params.SceneId", sizeof("params.sceneId") - 1, message->payload_str,
message->payload_len, &scene_id);
rc |= utils_json_value_get("params.SceneName", sizeof("params.SceneName") - 1, message->payload_str,
message->payload_len, &scene_name);
rc |= utils_json_value_get("params.SceneUpdateTime", sizeof("params.SceneUpdateTime") - 1,
message->payload_str, message->payload_len, &scene_update_time);
rc |= utils_json_value_get("params.Handles", sizeof("params.Handles") - 1, message->payload_str,
message->payload_len, &scene_handles);
if (rc) {
goto error;
}
callback->gateway_scene_handles_callback(scene_id, scene_name, scene_update_time, scene_handles, usr_data);
break;
case GATEWAY_SCENE_DOWN_MESSAGE_TYPE_RUN_SCENE:
if (!callback->gateway_run_scene_callback) {
return;
}
rc = utils_json_value_get("params.SceneId", sizeof("params.sceneId") - 1, message->payload_str,
message->payload_len, &scene_id);
if (rc) {
goto error;
}
callback->gateway_run_scene_callback(scene_id, usr_data);
break;
case GATEWAY_SCENE_DOWN_MESSAGE_TYPE_DELETE_SCENE:
if (!callback->gateway_delete_scene_callback) {
return;
}
rc = utils_json_value_get("params.SceneId", sizeof("params.sceneId") - 1, message->payload_str,
message->payload_len, &scene_id);
if (rc) {
goto error;
}
callback->gateway_delete_scene_callback(scene_id, usr_data);
break;
case GATEWAY_SCENE_DOWN_MESSAGE_TYPE_RELOAD_SCENE_HANDLES_REPLY:
if (!callback->gateway_reload_scene_reply_callback) {
return;
}
rc = utils_json_get_int32("code", sizeof("code") - 1, message->payload_str, message->payload_len,
&result_code);
rc |= utils_json_value_get("status", sizeof("status") - 1, message->payload_str, message->payload_len,
&status);
rc |= utils_json_value_get("sceneResult", sizeof("sceneResult") - 1, message->payload_str,
message->payload_len, &scene_result);
if (rc) {
goto error;
}
callback->gateway_reload_scene_reply_callback(result_code, status, scene_result, usr_data);
break;
default:
break;
}
return;
error:
Log_e("invalid format of payload!");
return;
}
/**
* @brief Gateway scene callback
*
* @param[in,out] client pointer to mqtt client
* @param[in] message message from topic
* @param[in,out] usr_data pointer to @see GatewaySceneContext
*/
static void _gateway_scene_message_callback(void *client, const MQTTMessage *message, void *usr_data)
{
const char *gateway_scene_method_str[] = {
[GATEWAY_SCENE_DOWN_MESSAGE_TYPE_SCENE_HANDLES] = "gateway_scene_handles",
[GATEWAY_SCENE_DOWN_MESSAGE_TYPE_RUN_SCENE] = "gateway_run_scene",
[GATEWAY_SCENE_DOWN_MESSAGE_TYPE_DELETE_SCENE] = "gateway_delete_scene",
[GATEWAY_SCENE_DOWN_MESSAGE_TYPE_RELOAD_SCENE_HANDLES_REPLY] = "gateway_reload_scene_handles_reply",
[GATEWAY_SCENE_DOWN_MESSAGE_TYPE_REPORT_INNER_SCENE_LIST_REPLY] = "gateway_report_inner_scene_list_reply",
};
int rc, i = 0;
GatewaySceneContext *gateway_scene_context = (GatewaySceneContext *)usr_data;
UtilsJsonValue method;
Log_d("receive gateway scene message:%.*s", message->payload_len, message->payload_str);
rc = utils_json_value_get("method", sizeof("method") - 1, message->payload_str, message->payload_len, &method);
if (rc) {
Log_e("invalid file manage message!");
return;
}
for (i = GATEWAY_SCENE_DOWN_MESSAGE_TYPE_SCENE_HANDLES;
i <= GATEWAY_SCENE_DOWN_MESSAGE_TYPE_REPORT_INNER_SCENE_LIST_REPLY; i++) {
if (!strncmp(method.value, gateway_scene_method_str[i], method.value_len)) {
Log_d("callback file manage message!");
_parse_update_payload_and_callback(i, message, &gateway_scene_context->callback,
gateway_scene_context->usr_data);
}
}
}
/**
* @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)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
int rc = service_mqtt_init(client);
if (rc) {
return rc;
}
GatewaySceneContext *gateway_scene_context = (GatewaySceneContext *)HAL_Malloc(sizeof(GatewaySceneContext));
if (!gateway_scene_context) {
return QCLOUD_ERR_MALLOC;
}
gateway_scene_context->callback = callback;
gateway_scene_context->usr_data = usr_data;
ServiceRegisterParams params = {
.type = SERVICE_TYPE_GATEWAY_SCENE,
.method_list = sg_gateway_scene_method_str,
.method_num = sizeof(sg_gateway_scene_method_str) / sizeof(sg_gateway_scene_method_str[0]),
.message_handle = _gateway_scene_message_callback,
.usr_data = gateway_scene_context,
.user_data_free = HAL_Free,
};
rc = service_mqtt_service_register(client, &params);
if (rc) {
HAL_Free(gateway_scene_context);
}
return rc;
}
/**
* @brief Gateway scene deinit, unregister handler from server list.
*
* @param[in,out] client pointer to mqtt client
*/
void IOT_GatewayScene_Deinit(void *client)
{
POINTER_SANITY_CHECK_RTN(client);
service_mqtt_service_unregister(client, SERVICE_TYPE_GATEWAY_SCENE);
service_mqtt_deinit(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)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_FAILURE);
STRING_PTR_SANITY_CHECK(buf, QCLOUD_ERR_FAILURE);
static int index = 0;
DeviceInfo *devInfo = IOT_MQTT_GetDeviceInfo(client);
HAL_Snprintf(buf, buf_len,
"{\"method\":\"gateway_reload_scene_handles\",\"clientToken\":\"gateway-reload-scene-handles-%s-%d\", "
"\"params\":{\"SceneId\":\"*\"}}",
devInfo->product_id, index++);
return service_mqtt_publish(client, QOS0, buf, 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)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_FAILURE);
STRING_PTR_SANITY_CHECK(buf, QCLOUD_ERR_FAILURE);
static int index = 0;
DeviceInfo *devInfo = IOT_MQTT_GetDeviceInfo(client);
int format_len = HAL_Snprintf(
buf, buf_len,
"{\"method\":\"gateway_report_inner_scene_list\",\"clientToken\":\"gateway_report_inner_scene_list-%s-%d\", "
"\"timestamp\":%ld,\"params\":{\"InnerSceneList\":[",
devInfo->product_id, index++, IOT_Timer_CurrentSec());
for (int i = 0; i < list_count; i++) {
format_len += HAL_Snprintf(buf + format_len, buf_len - format_len,
"{\"InnerSceneId\":\"%s\", \"InnerSceneName\":\"%s\"},", list[i].inner_scene_id,
list[i].inner_scene_name);
if (format_len >= buf_len) {
Log_e("buffer is too small(%d)", buf_len);
return QCLOUD_ERR_FAILURE;
}
}
buf[format_len - 1] = ']';
buf[format_len] = '}';
buf[format_len + 1] = '}';
buf[format_len + 2] = '\0';
return service_mqtt_publish(client, QOS0, buf, buf_len);
}

View File

@@ -0,0 +1,357 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file sevice_mqtt.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2022-01-11
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022-01-11 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "service_mqtt.h"
/**
* @brief Direction(upstream/downstream) for topic.
*
*/
typedef enum {
SERVICE_TOPIC_DIRECTION_UP = 0,
SERVICE_TOPIC_DIRECTION_DOWN,
} ServiceTopicDirection;
/**
* @brief Context of service mqtt, callback and user data.
*
*/
typedef struct {
void *service_list;
void *usr_data;
} ServiceMqttContext;
/**
* @brief Service list conext.
*
*/
typedef struct {
const char *method;
int method_len;
const MQTTMessage *message;
} ServiceListContext;
/**
* @brief Generate topic string.
*
* @param[out] buf buffer for topic name
* @param[in] buf_len buffer length
* @param[in] direction @see ServiceTopicDirection
* @param[in] product_id product id of device
* @param[in] device_name device name of device
* @return > 0 for length of topic name, others for fail.
*/
static int _service_mqtt_topic_generate(char *buf, int buf_len, ServiceTopicDirection direction, const char *product_id,
const char *device_name)
{
return HAL_Snprintf(buf, buf_len, "$thing/%s/service/%s/%s", direction ? "down" : "up",
STRING_PTR_PRINT_SANITY_CHECK(product_id), STRING_PTR_PRINT_SANITY_CHECK(device_name));
}
// ----------------------------------------------------------------------------
// server mqtt context
// ----------------------------------------------------------------------------
/**
* @brief Malloc service context and service list.
*
* @return @see ServiceMqttContext
*/
static ServiceMqttContext *_service_mqtt_context_malloc(void)
{
ServiceMqttContext *context = (ServiceMqttContext *)HAL_Malloc(sizeof(ServiceMqttContext));
if (!context) {
return NULL;
}
UtilsListFunc func = DEFAULT_LIST_FUNCS;
context->service_list = utils_list_create(func, 10);
if (!context->service_list) {
HAL_Free(context);
context = NULL;
}
return context;
}
/**
* @brief Free service context and service list.
*
* @param usr_data @see ServiceMqttContext
*/
static void _service_mqtt_context_free(void *usr_data)
{
ServiceMqttContext *context = (ServiceMqttContext *)usr_data;
utils_list_destroy(context->service_list);
HAL_Free(context);
}
/**
* @brief Get service mqtt context from client.
*
* @param[in,out] client pointer to mqtt client
* @return @see ServiceMqttContext
*/
static ServiceMqttContext *_service_mqtt_context_get(void *client)
{
char service_mqtt_topic[MAX_SIZE_OF_CLOUD_TOPIC];
_service_mqtt_topic_generate(service_mqtt_topic, MAX_SIZE_OF_CLOUD_TOPIC, SERVICE_TOPIC_DIRECTION_DOWN,
IOT_MQTT_GetDeviceInfo(client)->product_id,
IOT_MQTT_GetDeviceInfo(client)->device_name);
return (ServiceMqttContext *)IOT_MQTT_GetSubUsrData(client, service_mqtt_topic);
}
// ----------------------------------------------------------------------------
// server list
// ----------------------------------------------------------------------------
/**
* @brief Unregister server from server list.
*
* @param[in] list pointer to server list
* @param[in] node pointer tot server list node
* @param[in] val @see ServiceRegisterParams
* @param[in] usr_data @see ServiceType
* @return @see UtilsListResult
*/
static UtilsListResult _server_list_unregister_callback(void *list, void *node, void *val, void *usr_data)
{
ServiceRegisterParams *params = (ServiceRegisterParams *)val;
ServiceType type = *(ServiceType *)usr_data;
if (params->type == type) {
if (params->user_data_free) {
params->user_data_free(params->usr_data);
}
utils_list_remove(list, node);
return LIST_TRAVERSE_BREAK;
}
return LIST_TRAVERSE_CONTINUE;
}
/**
* @brief Callback server handler from server list.
*
* @param[in] list pointer to server list
* @param[in] node pointer tot server list node
* @param[in] val @see ServiceRegisterParams
* @param[in] usr_data @see ServiceListContext
* @return @see UtilsListResult
*/
static UtilsListResult _server_list_process_message_callback(void *list, void *node, void *val, void *usr_data)
{
ServiceRegisterParams *params = (ServiceRegisterParams *)val;
ServiceListContext *context = (ServiceListContext *)usr_data;
for (int i = 0; i < params->method_num; i++) {
int len = strlen(params->method_list[i]);
if (len == context->method_len && !strncmp(context->method, params->method_list[i], len)) {
params->message_handle(usr_data, context->message, params->usr_data);
return LIST_TRAVERSE_BREAK;
}
}
return LIST_TRAVERSE_CONTINUE;
}
// ----------------------------------------------------------------------------
// mqtt
// ----------------------------------------------------------------------------
/**
* @brief Handle server mqtt topic down stream message.
*
* @param[in,out] client pointer to mqtt client
* @param[in] message @see MQTTMessage
* @param[in] usr_data @see ServiceMqttContext
*/
static void _service_mqtt_message_handle(void *client, const MQTTMessage *message, void *usr_data)
{
Log_d("receive service message:%.*s", message->payload_len, message->payload_str);
UtilsJsonValue method;
int rc = utils_json_value_get("method", sizeof("method") - 1, message->payload_str, message->payload_len, &method);
if (rc) {
return;
}
// process list
ServiceListContext server_list_context = {
.method = method.value,
.method_len = method.value_len,
.message = message,
};
void *list = ((ServiceMqttContext *)usr_data)->service_list;
utils_list_process(list, LIST_HEAD, _server_list_process_message_callback, &server_list_context);
}
/**
* @brief Check if topic already subscribed, if not then subscribe.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int _service_mqtt_topic_check_and_sub(void *client)
{
char service_mqtt_topic[MAX_SIZE_OF_CLOUD_TOPIC];
_service_mqtt_topic_generate(service_mqtt_topic, MAX_SIZE_OF_CLOUD_TOPIC, SERVICE_TOPIC_DIRECTION_DOWN,
IOT_MQTT_GetDeviceInfo(client)->product_id,
IOT_MQTT_GetDeviceInfo(client)->device_name);
int rc = 0;
ServiceMqttContext *context = _service_mqtt_context_malloc();
if (!context) {
return QCLOUD_ERR_MALLOC;
}
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
sub_params.on_message_handler = _service_mqtt_message_handle;
sub_params.qos = QOS1;
sub_params.user_data = context;
sub_params.user_data_free = _service_mqtt_context_free;
rc = IOT_MQTT_SubscribeSync(client, service_mqtt_topic, &sub_params);
if (rc) {
_service_mqtt_context_free(context);
Log_e("subscribe topic %s failed!", service_mqtt_topic);
}
return rc;
}
/**
* @brief Unsubscribe service mqtt topic.
*
* @param[in,out] client pointer to mqtt client
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int _service_mqtt_unsubscribe(void *client)
{
char service_mqtt_topic[MAX_SIZE_OF_CLOUD_TOPIC];
_service_mqtt_topic_generate(service_mqtt_topic, MAX_SIZE_OF_CLOUD_TOPIC, SERVICE_TOPIC_DIRECTION_DOWN,
IOT_MQTT_GetDeviceInfo(client)->product_id,
IOT_MQTT_GetDeviceInfo(client)->device_name);
return IOT_MQTT_Unsubscribe(client, service_mqtt_topic);
}
// ----------------------------------------------------------------------------
// api
// ----------------------------------------------------------------------------
/**
* @brief Subscribe service mqtt.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int service_mqtt_init(void *client)
{
return _service_mqtt_topic_check_and_sub(client);
}
/**
* @brief If no service in the service list, then unsubscribe.
*
* @param[in,out] client pointer to mqtt client
*/
void service_mqtt_deinit(void *client)
{
ServiceMqttContext *context = _service_mqtt_context_get(client);
if (!context) {
return;
}
if (!utils_list_len_get(context->service_list)) {
_service_mqtt_unsubscribe(client);
}
}
/**
* @brief Publish to service mqtt topic.
*
* @param[in,out] client pointer to mqtt client
* @param[in] qos @see QoS
* @param[in] payload payload of mqtt packet
* @param[in] payload_len payload len
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
*/
int service_mqtt_publish(void *client, QoS qos, const char *payload, int payload_len)
{
char service_mqtt_topic[MAX_SIZE_OF_CLOUD_TOPIC];
_service_mqtt_topic_generate(service_mqtt_topic, MAX_SIZE_OF_CLOUD_TOPIC, SERVICE_TOPIC_DIRECTION_UP,
IOT_MQTT_GetDeviceInfo(client)->product_id,
IOT_MQTT_GetDeviceInfo(client)->device_name);
PublishParams pub_params = DEFAULT_PUB_PARAMS;
pub_params.qos = qos;
pub_params.payload = (void *)payload;
pub_params.payload_len = payload_len;
return IOT_MQTT_Publish(client, service_mqtt_topic, &pub_params);
}
/**
* @brief Register server handler to server list.
*
* @param[in,out] client pointer to mqtt client
* @param[in] params @see ServiceRegisterParams
* @return @see IotReturnCode
*/
int service_mqtt_service_register(void *client, const ServiceRegisterParams *params)
{
ServiceMqttContext *context = _service_mqtt_context_get(client);
if (!context) {
return QCLOUD_ERR_FAILURE;
}
ServiceRegisterParams *register_params = (ServiceRegisterParams *)HAL_Malloc(sizeof(ServiceRegisterParams));
if (!register_params) {
return QCLOUD_ERR_MALLOC;
}
memcpy(register_params, params, sizeof(ServiceRegisterParams));
void *node = utils_list_push(context->service_list, register_params);
if (!node) {
HAL_Free(register_params);
return QCLOUD_ERR_MALLOC;
}
return QCLOUD_RET_SUCCESS;
}
/**
* @brief Unregister server handler from server list.
*
* @param[in,out] client pointer to mqtt client
* @param[in] type @see ServiceType
*/
void service_mqtt_service_unregister(void *client, ServiceType type)
{
ServiceMqttContext *context = _service_mqtt_context_get(client);
if (!context) {
return;
}
utils_list_process(context->service_list, LIST_HEAD, _server_list_unregister_callback, &type);
}

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,132 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2022 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file test_ota_mqtt.cc
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2022-01-18
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2022-01-18 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include <iostream>
#include <string>
#include "gtest/gtest.h"
#include "mqtt_client_test.h"
#include "qcloud_iot_explorer.h"
namespace mqtt_client_unittest {
static void _file_manage_update_file_callback(UtilsJsonValue file_name, UtilsJsonValue file_type,
UtilsJsonValue version, UtilsJsonValue url, UtilsJsonValue md5sum,
uint32_t file_size, void *usr_data) {
Log_i("recv update file: file_name=%.*s|type=%.*s|version=%.*s|url=%.*s|md5sum=%.*s|file_size=%u",
file_name.value_len, file_name.value, file_type.value_len, file_type.value, version.value_len, version.value,
url.value_len, url.value, md5sum.value_len, md5sum.value, file_size);
}
static void _file_manage_del_file_callback(UtilsJsonValue file_name, UtilsJsonValue file_type, UtilsJsonValue version,
void *usr_data) {
Log_i("recv del file: file_name=%.*s|type=%.*s|version=%.*s|", file_name.value_len, file_name.value,
file_type.value_len, file_type.value, version.value_len, version.value);
}
static void _file_manage_report_file_version_reponse_callback(UtilsJsonValue file_list, int result_code,
void *usr_data) {
Log_i("recv file version: file_list=%.*s|result_code=%d", file_list, result_code);
}
static void _file_manage_request_file_url_response_callback(UtilsJsonValue url, UtilsJsonValue file_token,
int result_code, void *usr_data) {
Log_i("recv request response: url=%.*s|file_token=%.*s|result_code=%d", url.value_len, url.value,
file_token.value_len, file_token.value, result_code);
}
/**
* @brief Test file manage.
*
*/
TEST_F(MqttClientTest, file_manage) {
char buf[256];
int buf_len = sizeof(buf);
IotFileManageFileInfo file_info = {0};
const char *file_name = "test.txt";
const char *version = "1.0.0";
strncpy(file_info.file_name, file_name, strlen(file_name));
strncpy(file_info.file_version, version, strlen(version));
file_info.file_type = IOT_FILE_MANAGE_FILE_TYPE_FILE;
IotFileManageCallback file_manage_callback = {
.update_file_callback = _file_manage_update_file_callback,
.del_file_callback = _file_manage_del_file_callback,
.report_file_version_reponse_callback = _file_manage_report_file_version_reponse_callback,
.request_file_url_response_callback = _file_manage_request_file_url_response_callback,
};
ASSERT_EQ(IOT_FileManage_Init(client, file_manage_callback, client), 0);
ASSERT_GE(IOT_FileManage_ReportFileList(client, buf, buf_len, NULL, 0), 0);
ASSERT_GE(IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOADING, 0, file_name, version),
0);
ASSERT_GE(
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_BEGIN, 100, file_name, version),
0);
ASSERT_GE(
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_SUCCESS, 0, file_name, version),
0);
ASSERT_GE(
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_DOWNLOAD_TIMEOUT, 0, file_name, version),
0);
ASSERT_GE(
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_FILE_NOT_EXIST, 0, file_name, version),
0);
ASSERT_GE(IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_AUTH_FAIL, 0, file_name, version),
0);
ASSERT_GE(
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_MD5_NOT_MATCH, 0, file_name, version), 0);
ASSERT_GE(
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_UPGRADE_FAIL, 0, file_name, version), 0);
ASSERT_GE(
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_SPACE_NOT_ENOUGH, 0, file_name, version),
0);
ASSERT_GE(IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_DEL_SUCCESS, 0, file_name, version),
0);
ASSERT_GE(IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_DEL_FAIL, 0, file_name, version),
0);
ASSERT_GE(IOT_FileManage_PostRequest(client, buf, buf_len, &file_info, 0), 0);
ASSERT_EQ(IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT), 0);
ASSERT_GE(
IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_POST_SUCCESS, 0, file_name, version), 0);
ASSERT_GE(IOT_FileManage_Report(client, buf, buf_len, IOT_FILE_MANAGE_REPORT_TYPE_POST_FAIL, 0, file_name, version),
0);
ASSERT_EQ(IOT_FileManage_GetFileType("FILE", sizeof("FILE") - 1), IOT_FILE_MANAGE_FILE_TYPE_FILE);
ASSERT_EQ(IOT_FileManage_GetFileType("AUDIO", sizeof("AUDIO") - 1), IOT_FILE_MANAGE_FILE_TYPE_AUDIO);
ASSERT_EQ(IOT_FileManage_GetFileType("VOICE", sizeof("VOICE") - 1), IOT_FILE_MANAGE_FILE_TYPE_VOICE);
ASSERT_EQ(IOT_FileManage_GetFileType("VIDEO", sizeof("VIDEO") - 1), IOT_FILE_MANAGE_FILE_TYPE_VIDEO);
ASSERT_EQ(IOT_FileManage_GetFileType("TEXT", sizeof("TEXT") - 1), IOT_FILE_MANAGE_FILE_TYPE_UNKOWN);
IOT_FileManage_Deinit(client);
}
} // namespace mqtt_client_unittest

View File

@@ -0,0 +1,90 @@
/**
* @file test_service_gateway_scene.cc
* @author {hubert} ({hubertxxu@tencent.com})
* @brief
* @version 1.0
* @date 2022-06-16
*
* @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-16 1.0 hubertxxu first commit
* </table>
*/
#include <iostream>
#include <string>
#include "gtest/gtest.h"
#include "mqtt_client_test.h"
#include "qcloud_iot_explorer.h"
namespace mqtt_client_unittest {
static int _gateway_scene_handles_callback(UtilsJsonValue scene_id, UtilsJsonValue scene_name,
UtilsJsonValue scene_update_time, UtilsJsonValue scene_handles,
void *usr_data) {
Log_d("recv scene handles : id:%.*s| name:%.*s| update time:%.*s| handles:%.*s", scene_id.value_len, scene_id.value,
scene_name.value_len, scene_name.value, scene_update_time.value_len, scene_update_time.value,
scene_handles.value_len, scene_handles.value);
return 0;
}
static int _gateway_run_scene_callback(UtilsJsonValue scene_id, void *usr_data) {
Log_d("run scene: id:%.*s", scene_id.value_len, scene_id.value);
return 0;
}
static int _gateway_delete_scene_callback(UtilsJsonValue scene_id, void *usr_data) {
Log_d("delete scene: id:%.*s", scene_id.value_len, scene_id.value);
return 0;
}
static int _gateway_reload_scene_reply_callback(int result_code, UtilsJsonValue status, UtilsJsonValue scene_result,
void *usr_data) {
Log_d("reload scene reply: result code:%d|status:%.*s|result:%.*s", result_code, status.value_len, status.value,
scene_result.value_len, scene_result.value);
return 0;
}
/**
* @brief Test gateway scene
*
*/
TEST_F(MqttClientTest, gateway_scene) {
char buf[4096];
int buf_len = sizeof(buf);
IoTGatewaySceneCallback cbs = {
.gateway_delete_scene_callback = _gateway_delete_scene_callback,
.gateway_reload_scene_reply_callback = _gateway_reload_scene_reply_callback,
.gateway_run_scene_callback = _gateway_run_scene_callback,
.gateway_scene_handles_callback = _gateway_scene_handles_callback,
};
IoTGatewaySceneInnerList scene_inner_list[MAX_LENGTH_INNER_SCENE_LIST];
for (int i = 0; i < MAX_LENGTH_INNER_SCENE_LIST; i++) {
HAL_Snprintf(scene_inner_list[i].inner_scene_id, MAX_LENGTH_INNER_SCENE_ID, "inner_scene_id_%d", i);
HAL_Snprintf(scene_inner_list[i].inner_scene_name, MAX_LENGTH_INNER_SCENE_NAME, "inner_scene_name_%d", i);
}
ASSERT_EQ(IOT_GatewayScene_Init(client, cbs, client), 0);
ASSERT_GE(IOT_GatewayScene_Reload(client, buf, buf_len), 0);
ASSERT_GE(IOT_GatewayScene_ReportInnerList(client, buf, buf_len, scene_inner_list, MAX_LENGTH_INNER_SCENE_LIST), 0);
IOT_GatewayScene_Deinit(client);
}
} // namespace mqtt_client_unittest

View File

@@ -0,0 +1,16 @@
file(GLOB src_broadcast ${CMAKE_CURRENT_SOURCE_DIR}/src/*.c)
set(inc_broadcast ${CMAKE_CURRENT_SOURCE_DIR}/inc)
set(src_services ${src_services} ${src_broadcast} PARENT_SCOPE)
set(inc_services ${inc_services} ${inc_broadcast} PARENT_SCOPE)
if( ${CONFIG_COMPILE_SAMPLE} STREQUAL "ON")
file(GLOB src_broadcast_sample ${CMAKE_CURRENT_SOURCE_DIR}/sample/broadcast_sample.c)
add_executable(broadcast_sample ${src_broadcast_sample})
target_link_libraries(broadcast_sample ${libsdk})
endif()
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,211 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file broadcast_sample.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-18
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "qcloud_iot_common.h"
#include "qcloud_iot_hub.h"
#include "utils_log.h"
/**
* @brief MQTT event callback, @see MQTTEventHandleFun
*
* @param[in] client pointer to mqtt client
* @param[in] handle_context context
* @param[in] msg msg
*/
static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
{
MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
uintptr_t packet_id = (uintptr_t)msg->msg;
switch (msg->event_type) {
case MQTT_EVENT_UNDEF:
Log_i("undefined event occur.");
break;
case MQTT_EVENT_DISCONNECT:
Log_i("MQTT disconnect.");
break;
case MQTT_EVENT_RECONNECT:
Log_i("MQTT reconnect.");
break;
case MQTT_EVENT_PUBLISH_RECEIVED:
Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
break;
case MQTT_EVENT_SUBSCRIBE_SUCCESS:
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_SUBSCRIBE_NACK:
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_UNSUBSCRIBE_NACK:
Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_SUCCESS:
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_TIMEOUT:
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
break;
case MQTT_EVENT_PUBLISH_NACK:
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
break;
default:
Log_i("Should NOT arrive here.");
break;
}
}
/**
* @brief Setup MQTT construct parameters.
*
* @param[in,out] initParams @see MQTTInitParams
* @param[in] device_info @see DeviceInfo
*/
static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
{
init_params->device_info = device_info;
init_params->event_handle.h_fp = _mqtt_event_handler;
}
/**
* @brief Callback when MQTT msg arrives @see OnMessageHandler
*
* @param[in, out] client pointer to mqtt client
* @param[in] message publish message from server
* @param[in] usr_data user data of SubscribeParams, @see SubscribeParams
*/
static void _on_broadcast_arrived_callback(void *client, const char *msg, int msg_len, void *usr_data)
{
Log_i("Receive broadcast message:%.*s, usr data:%d", msg_len, STRING_PTR_PRINT_SANITY_CHECK(msg), *(int *)usr_data);
(*(int *)usr_data)++;
}
// ----------------------------------------------------------------------------
// Main
// ----------------------------------------------------------------------------
static int sg_main_exit = 0;
#ifdef __linux__
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
static void _main_exit(int sig)
{
Log_e("demo exit by signal:%d\n", sig);
sg_main_exit = 1;
}
#endif
int main(int argc, char **argv)
{
#ifdef __linux__
signal(SIGINT, _main_exit);
#endif
int rc;
// init log level
LogHandleFunc func = 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;
}
// subscribe normal topics and wait result
static int test_usr_data = 0;
rc = IOT_Broadcast_Init(client, _on_broadcast_arrived_callback, &test_usr_data);
if (rc) {
Log_e("Client Subscribe Topic Failed: %d", rc);
return rc;
}
do {
rc = IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT);
if (rc == QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT) {
HAL_SleepMs(1000);
continue;
} else if (rc != QCLOUD_RET_SUCCESS && rc != QCLOUD_RET_MQTT_RECONNECTED) {
Log_e("exit with error: %d", rc);
break;
}
} while (!sg_main_exit);
rc = IOT_Broadcast_Deinit(client);
rc |= IOT_MQTT_Destroy(&client);
utils_log_deinit();
return rc;
}

View File

@@ -0,0 +1,120 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file broadcast.c
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-18
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include "qcloud_iot_broadcast.h"
/**
* @brief Context of broadcast message.
*
*/
typedef struct {
OnBroadcastArrivedCallback callback; /**< callback to handle message */
void * usr_data; /**< usr data using in callback */
} QcloudIotBroadcastContext;
/**
* @brief Callback for broadcast topic message
*
* @param[in,out] client pointer to mqtt client
* @param[in] message broadcast message @see MQTTMessage
* @param[in,out] context @see QcloudIotBroadcastContext
*/
static void _broadcast_message_cb(void *client, const MQTTMessage *message, void *context)
{
QcloudIotBroadcastContext *broadcast_context = (QcloudIotBroadcastContext *)context;
Log_d("topic=%.*s", message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(message->topic_name));
Log_i("len=%u, topic_msg=%.*s", message->payload_len, message->payload_len,
STRING_PTR_PRINT_SANITY_CHECK(message->payload_str));
if (broadcast_context->callback) {
broadcast_context->callback(client, message->payload_str, message->payload_len, broadcast_context->usr_data);
}
}
/**
* @brief Subscribe broadcast topic with callback.
*
* @param[in,out] client pointer to mqtt client
* @param[in] callback callback to handle message
* @param[in] usr_data usr data using in callback
* @return @see IotReturnCode
*/
int IOT_Broadcast_Init(void *client, OnBroadcastArrivedCallback callback, void *usr_data)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
POINTER_SANITY_CHECK(callback, QCLOUD_ERR_INVAL);
int rc = 0;
char broadcast_topic[MAX_SIZE_OF_CLOUD_TOPIC];
SubscribeParams sub_params = DEFAULT_SUB_PARAMS;
/**
* @brief Using static for only one broardcast topic can be subscribed to.
*
*/
QcloudIotBroadcastContext *broadcast_context =
(QcloudIotBroadcastContext *)HAL_Malloc(sizeof(QcloudIotBroadcastContext));
if (!broadcast_context) {
return QCLOUD_ERR_MALLOC;
}
broadcast_context->callback = callback;
broadcast_context->usr_data = usr_data;
HAL_Snprintf(broadcast_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$broadcast/rxd/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
sub_params.on_message_handler = _broadcast_message_cb;
sub_params.qos = QOS1;
sub_params.user_data = broadcast_context;
sub_params.user_data_free = HAL_Free;
rc = IOT_MQTT_SubscribeSync(client, broadcast_topic, &sub_params);
if (rc) {
HAL_Free(broadcast_context);
}
return rc;
}
/**
* @brief Unsubscribe broadcast topic.
*
* @param[in,out] client pointer to mqtt client
* @return @see IotReturnCode
*/
int IOT_Broadcast_Deinit(void *client)
{
POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
char broadcast_topic[MAX_SIZE_OF_CLOUD_TOPIC];
HAL_Snprintf(broadcast_topic, MAX_SIZE_OF_CLOUD_TOPIC, "$broadcast/rxd/%s/%s",
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->product_id),
STRING_PTR_PRINT_SANITY_CHECK(IOT_MQTT_GetDeviceInfo(client)->device_name));
return IOT_MQTT_Unsubscribe(client, broadcast_topic) > 0 ? QCLOUD_RET_SUCCESS : QCLOUD_ERR_FAILURE;
}

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,62 @@
/**
* @copyright
*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
*
* Licensed under the MIT License(the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*
* @file test_broadcast.cc
* @brief
* @author fancyxu (fancyxu@tencent.com)
* @version 1.0
* @date 2021-07-18
*
* @par Change Log:
* <table>
* <tr><th>Date <th>Version <th>Author <th>Description
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
* </table>
*/
#include <iostream>
#include <string>
#include "gtest/gtest.h"
#include "mqtt_client_test.h"
#include "qcloud_iot_common.h"
#include "qcloud_iot_hub.h"
namespace mqtt_client_unittest {
/**
* @brief Callback when MQTT msg arrives @see OnMessageHandler
*
* @param[in, out] client pointer to mqtt client
* @param[in] message publish message from server
* @param[in] usr_data user data of SubscribeParams, @see SubscribeParams
*/
void on_broadcast_arrived_callback(void *client, const char *msg, int msg_len, void *usr_data) {
Log_i("Receive broadcast message:%.*s, usr data:%d", msg_len, STRING_PTR_PRINT_SANITY_CHECK(msg),
*(reinterpret_cast<int *>(usr_data)));
*(reinterpret_cast<int *>(usr_data)) += 1;
}
/**
* @brief Test broadcast.
*
*/
TEST_F(MqttClientTest, broadcast) {
int usr_data = 0;
ASSERT_EQ(IOT_Broadcast_Init(client, on_broadcast_arrived_callback, &usr_data), 0);
ASSERT_EQ(IOT_Broadcast_Deinit(client), 0);
}
} // namespace mqtt_client_unittest