add new qloud-c-sdk component
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
file(GLOB src_app ${CMAKE_CURRENT_SOURCE_DIR}/*.c)
|
||||
add_executable(app_data_template ${src_app})
|
||||
target_link_libraries(app_data_template ${libsdk})
|
@@ -0,0 +1,330 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is
|
||||
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @file data_template_sample.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-09-27
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-09-27 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "qcloud_iot_common.h"
|
||||
#include "qcloud_iot_explorer.h"
|
||||
#include "data_template_config.h"
|
||||
|
||||
#include "utils_log.h"
|
||||
|
||||
/**
|
||||
* @brief MQTT event callback, @see MQTTEventHandleFun
|
||||
*
|
||||
* @param[in] client pointer to mqtt client
|
||||
* @param[in] handle_context context
|
||||
* @param[in] msg msg
|
||||
*/
|
||||
static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
|
||||
{
|
||||
MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
|
||||
uintptr_t packet_id = (uintptr_t)msg->msg;
|
||||
|
||||
switch (msg->event_type) {
|
||||
case MQTT_EVENT_UNDEF:
|
||||
Log_i("undefined event occur.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DISCONNECT:
|
||||
Log_i("MQTT disconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_RECONNECT:
|
||||
Log_i("MQTT reconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_RECEIVED:
|
||||
Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
|
||||
mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
|
||||
mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK((char *)mqtt_message->payload));
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_SUCCESS:
|
||||
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
|
||||
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_NACK:
|
||||
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
|
||||
Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
|
||||
Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_NACK:
|
||||
Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_SUCCESS:
|
||||
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_TIMEOUT:
|
||||
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_NACK:
|
||||
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
default:
|
||||
Log_i("Should NOT arrive here.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Setup MQTT construct parameters.
|
||||
*
|
||||
* @param[in,out] initParams @see MQTTInitParams
|
||||
* @param[in] device_info @see DeviceInfo
|
||||
*/
|
||||
static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
|
||||
{
|
||||
init_params->device_info = device_info;
|
||||
init_params->command_timeout = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT;
|
||||
init_params->keep_alive_interval_ms = QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL;
|
||||
init_params->auto_connect_enable = 1;
|
||||
init_params->event_handle.h_fp = _mqtt_event_handler;
|
||||
init_params->event_handle.context = NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Data template callback
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static void _method_control_callback(UtilsJsonValue client_token, UtilsJsonValue params, void *usr_data)
|
||||
{
|
||||
char buf[256];
|
||||
Log_i("recv msg[%.*s]: params=%.*s", client_token.value_len, client_token.value, params.value_len, params.value);
|
||||
IOT_DataTemplate_PropertyControlReply(usr_data, buf, sizeof(buf), 0, client_token);
|
||||
usr_data_template_property_parse(params);
|
||||
}
|
||||
|
||||
static void _method_get_status_reply_callback(UtilsJsonValue client_token, int code, UtilsJsonValue reported,
|
||||
UtilsJsonValue control, void *usr_data)
|
||||
{
|
||||
char buf[256];
|
||||
Log_i("recv msg[%.*s]: code=%d|reported=%.*s|control=%.*s", client_token.value_len, client_token.value, code,
|
||||
reported.value_len, STRING_PTR_PRINT_SANITY_CHECK(reported.value), control.value_len,
|
||||
STRING_PTR_PRINT_SANITY_CHECK(control.value));
|
||||
usr_data_template_property_parse(control);
|
||||
IOT_DataTemplate_PropertyClearControl(usr_data, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static void _method_action_callback(UtilsJsonValue client_token, UtilsJsonValue action_id, UtilsJsonValue params,
|
||||
void *usr_data)
|
||||
{
|
||||
UsrActionIndex index;
|
||||
int rc;
|
||||
DataTemplatePropertyValue value_time, value_color, value_total_time;
|
||||
|
||||
char buf[256];
|
||||
|
||||
Log_i("recv msg[%.*s]: action_id=%.*s|params=%.*s", client_token.value_len, client_token.value, action_id.value_len,
|
||||
action_id.value, params.value_len, params.value);
|
||||
|
||||
rc = usr_data_template_action_parse(action_id, params, &index);
|
||||
if (rc) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (index) {
|
||||
case USR_ACTION_INDEX_LIGHT_BLINK:
|
||||
value_time = usr_data_template_action_input_value_get(USR_ACTION_INDEX_LIGHT_BLINK,
|
||||
USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME);
|
||||
value_color = usr_data_template_action_input_value_get(USR_ACTION_INDEX_LIGHT_BLINK,
|
||||
USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR);
|
||||
value_total_time = usr_data_template_action_input_value_get(USR_ACTION_INDEX_LIGHT_BLINK,
|
||||
USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME);
|
||||
Log_i("light[%d] blink %d every %d s ", value_color.value_enum, value_time.value_int,
|
||||
value_total_time.value_int);
|
||||
usr_data_template_action_reply(usr_data, buf, sizeof(buf), index, client_token, 0, "{\"err_code\":0}");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Data template upstream
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Report status.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
*/
|
||||
static void _cycle_report(void *client)
|
||||
{
|
||||
char buf[256];
|
||||
static Timer sg_cycle_report_timer;
|
||||
if (HAL_Timer_Expired(&sg_cycle_report_timer)) {
|
||||
usr_data_template_event_post(client, buf, sizeof(buf), USR_EVENT_INDEX_STATUS_REPORT,
|
||||
"{\"status\":0,\"message\":\"ok\"}");
|
||||
HAL_Timer_Countdown(&sg_cycle_report_timer, 60);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Init usr data template and data.
|
||||
*
|
||||
*/
|
||||
static void _usr_init(void)
|
||||
{
|
||||
usr_data_template_init();
|
||||
|
||||
DataTemplatePropertyValue value;
|
||||
value.value_int = 0;
|
||||
usr_data_template_property_value_set(USR_PROPERTY_INDEX_POWER_SWITCH, value);
|
||||
value.value_enum = 0;
|
||||
usr_data_template_property_value_set(USR_PROPERTY_INDEX_COLOR, value);
|
||||
value.value_int = 10;
|
||||
usr_data_template_property_value_set(USR_PROPERTY_INDEX_BRIGHTNESS, value);
|
||||
value.value_string = "light";
|
||||
usr_data_template_property_value_set(USR_PROPERTY_INDEX_NAME, value);
|
||||
value.value_int = 30;
|
||||
usr_data_template_property_struct_value_set(USR_PROPERTY_INDEX_POSITION, USR_PROPERTY_POSITION_INDEX_LONGITUDE,
|
||||
value);
|
||||
value.value_int = 30;
|
||||
usr_data_template_property_struct_value_set(USR_PROPERTY_INDEX_POSITION, USR_PROPERTY_POSITION_INDEX_LATITUDE,
|
||||
value);
|
||||
value.value_string = "high";
|
||||
usr_data_template_property_value_set(USR_PROPERTY_INDEX_POWER, value);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Main
|
||||
// ----------------------------------------------------------------------------
|
||||
static int sg_main_exit = 0;
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void _main_exit(int sig)
|
||||
{
|
||||
Log_e("demo exit by signal:%d", sig);
|
||||
sg_main_exit = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#ifdef __linux__
|
||||
signal(SIGINT, _main_exit);
|
||||
#endif
|
||||
|
||||
int rc;
|
||||
|
||||
char buf[1024];
|
||||
|
||||
// init log level
|
||||
LogHandleFunc func = {0};
|
||||
|
||||
func.log_malloc = HAL_Malloc;
|
||||
func.log_free = HAL_Free;
|
||||
func.log_get_current_time_str = HAL_Timer_Current;
|
||||
func.log_printf = HAL_Printf;
|
||||
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
|
||||
|
||||
DeviceInfo device_info;
|
||||
|
||||
rc = HAL_GetDevInfo((void *)&device_info);
|
||||
if (rc) {
|
||||
Log_e("get device info failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
_usr_init();
|
||||
|
||||
// init connection
|
||||
MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
|
||||
_setup_connect_init_params(&init_params, &device_info);
|
||||
|
||||
// create MQTT client and connect with server
|
||||
void *client = IOT_MQTT_Construct(&init_params);
|
||||
if (client) {
|
||||
Log_i("Cloud Device Construct Success");
|
||||
} else {
|
||||
Log_e("MQTT Construct failed!");
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
// subscribe normal topics and wait result
|
||||
IotDataTemplateCallback callback = DEFAULT_DATA_TEMPLATE_CALLBACK;
|
||||
callback.property_callback.method_control_callback = _method_control_callback;
|
||||
callback.property_callback.method_get_status_reply_callback = _method_get_status_reply_callback;
|
||||
callback.action_callback.method_action_callback = _method_action_callback;
|
||||
|
||||
rc = IOT_DataTemplate_Init(client, callback, client);
|
||||
if (rc) {
|
||||
Log_e("Client Subscribe Topic Failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
IOT_DataTemplate_PropertyGetStatus(client, buf, sizeof(buf));
|
||||
|
||||
do {
|
||||
rc = IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT);
|
||||
switch (rc) {
|
||||
case QCLOUD_RET_SUCCESS:
|
||||
break;
|
||||
case QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT:
|
||||
continue;
|
||||
case QCLOUD_RET_MQTT_RECONNECTED:
|
||||
IOT_DataTemplate_PropertyGetStatus(client, buf, sizeof(buf));
|
||||
break;
|
||||
default:
|
||||
Log_e("Exit loop caused of errCode:%d", rc);
|
||||
goto exit;
|
||||
}
|
||||
_cycle_report(client);
|
||||
usr_data_template_property_report(client, buf, sizeof(buf));
|
||||
} while (!sg_main_exit);
|
||||
|
||||
exit:
|
||||
IOT_DataTemplate_Deinit(client);
|
||||
rc |= IOT_MQTT_Destroy(&client);
|
||||
utils_log_deinit();
|
||||
return rc;
|
||||
}
|
@@ -0,0 +1,494 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is
|
||||
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @file data_template_config.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-10-09
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-10-09 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "data_template_config.h"
|
||||
|
||||
/**
|
||||
* @brief Set property value.
|
||||
*
|
||||
* @param[in,out] property pointer to property
|
||||
* @param[in] value value to set, should match with property type
|
||||
* @return 0 for success.
|
||||
*/
|
||||
static int _set_property_value(DataTemplateProperty* property, UtilsJsonValue value)
|
||||
{
|
||||
int i, rc = 0;
|
||||
|
||||
switch (property->type) {
|
||||
case DATA_TEMPLATE_TYPE_INT:
|
||||
case DATA_TEMPLATE_TYPE_ENUM:
|
||||
case DATA_TEMPLATE_TYPE_BOOL:
|
||||
return utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_INT32, &property->value.value_int);
|
||||
case DATA_TEMPLATE_TYPE_TIME:
|
||||
return utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_UINT32, &property->value.value_time);
|
||||
case DATA_TEMPLATE_TYPE_STRING:
|
||||
case DATA_TEMPLATE_TYPE_STRING_ENUM:
|
||||
if (!property->value.value_string) { // no need copy
|
||||
return 0;
|
||||
}
|
||||
strncpy(property->value.value_string, value.value, value.value_len);
|
||||
property->value.value_string[value.value_len] = '\0';
|
||||
return 0;
|
||||
case DATA_TEMPLATE_TYPE_FLOAT:
|
||||
return utils_json_value_data_get(value, UTILS_JSON_VALUE_TYPE_FLOAT, &property->value.value_float);
|
||||
case DATA_TEMPLATE_TYPE_STRUCT:
|
||||
for (i = 0; i < property->value.value_struct.count; i++) {
|
||||
rc |= _set_property_value(property->value.value_struct.property + i, value);
|
||||
}
|
||||
return rc;
|
||||
case DATA_TEMPLATE_TYPE_ARRAY:
|
||||
Log_e("array type is not supportted yet!");
|
||||
return -1;
|
||||
default:
|
||||
Log_e("unkown type!");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get property node in json.
|
||||
*
|
||||
* @param[out] json_buf buffer to put node
|
||||
* @param[in] buf_len buffer length
|
||||
* @param[in] property property to get node
|
||||
* @return 0 for success.
|
||||
*/
|
||||
static int _get_property_node(char* json_buf, int buf_len, const DataTemplateProperty* property)
|
||||
{
|
||||
int len, i;
|
||||
switch (property->type) {
|
||||
case DATA_TEMPLATE_TYPE_INT:
|
||||
case DATA_TEMPLATE_TYPE_ENUM:
|
||||
case DATA_TEMPLATE_TYPE_BOOL:
|
||||
return HAL_Snprintf(json_buf, buf_len, "\"%s\":%d", property->key, property->value.value_int);
|
||||
case DATA_TEMPLATE_TYPE_TIME:
|
||||
return HAL_Snprintf(json_buf, buf_len, "\"%s\":%u", property->key, property->value.value_time);
|
||||
case DATA_TEMPLATE_TYPE_STRING:
|
||||
case DATA_TEMPLATE_TYPE_STRING_ENUM:
|
||||
if (!property->value.value_string) {
|
||||
return 0;
|
||||
}
|
||||
return HAL_Snprintf(json_buf, buf_len, "\"%s\":\"%s\"", property->key, property->value.value_string);
|
||||
case DATA_TEMPLATE_TYPE_FLOAT:
|
||||
return HAL_Snprintf(json_buf, buf_len, "\"%s\":%f", property->key, property->value.value_float);
|
||||
case DATA_TEMPLATE_TYPE_STRUCT:
|
||||
len = HAL_Snprintf(json_buf, buf_len, "\"%s\":{", property->key);
|
||||
for (i = 0; i < property->value.value_struct.count; i++) {
|
||||
len += _get_property_node(json_buf + len, buf_len - len, property->value.value_struct.property + i);
|
||||
json_buf[len++] = ',';
|
||||
}
|
||||
json_buf[--len] = '}';
|
||||
return len + 1;
|
||||
case DATA_TEMPLATE_TYPE_ARRAY:
|
||||
Log_e("array type is not supportted yet!");
|
||||
return -1;
|
||||
default:
|
||||
Log_e("unkown type!");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse property array.
|
||||
*
|
||||
* @param[in] json_buf json string to parse
|
||||
* @param[in] buf_len json len
|
||||
* @param[in,out] properties pointer to property array
|
||||
* @param[in] property_count count of property
|
||||
*/
|
||||
static void _parse_property_array(const char* json_buf, int buf_len, DataTemplateProperty* properties,
|
||||
int property_count)
|
||||
{
|
||||
DataTemplateProperty* property;
|
||||
UtilsJsonValue value;
|
||||
for (int i = 0; i < property_count; i++) {
|
||||
property = &properties[i];
|
||||
if (!utils_json_value_get(property->key, strlen(property->key), json_buf, buf_len, &value)) {
|
||||
property->need_report = !_set_property_value(property, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************************
|
||||
* user property
|
||||
**************************************************************************************/
|
||||
|
||||
#define TOTAL_USR_PROPERTY_COUNT 6
|
||||
|
||||
static DataTemplateProperty sg_usr_data_template_property[TOTAL_USR_PROPERTY_COUNT];
|
||||
|
||||
#define TOTAL_USR_PROPERTY_STRUCT_POSITION_COUNT 2
|
||||
|
||||
static DataTemplateProperty sg_usr_property_position[TOTAL_USR_PROPERTY_STRUCT_POSITION_COUNT];
|
||||
|
||||
static void _init_data_template_property_position(void)
|
||||
{
|
||||
sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LONGITUDE].value.value_int = 0;
|
||||
sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LONGITUDE].key = "longitude";
|
||||
sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LONGITUDE].type = DATA_TEMPLATE_TYPE_INT;
|
||||
|
||||
sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LATITUDE].value.value_int = 0;
|
||||
sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LATITUDE].key = "latitude";
|
||||
sg_usr_property_position[USR_PROPERTY_POSITION_INDEX_LATITUDE].type = DATA_TEMPLATE_TYPE_INT;
|
||||
}
|
||||
|
||||
static void _init_data_template_property(void)
|
||||
{
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER_SWITCH].value.value_bool = 0;
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER_SWITCH].key = "power_switch";
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER_SWITCH].type = DATA_TEMPLATE_TYPE_BOOL;
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER_SWITCH].need_report = 1;
|
||||
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_COLOR].value.value_enum = 0;
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_COLOR].key = "color";
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_COLOR].type = DATA_TEMPLATE_TYPE_ENUM;
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_COLOR].need_report = 1;
|
||||
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_BRIGHTNESS].value.value_int = 0;
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_BRIGHTNESS].key = "brightness";
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_BRIGHTNESS].type = DATA_TEMPLATE_TYPE_INT;
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_BRIGHTNESS].need_report = 1;
|
||||
|
||||
static char sg_usr_property_name[64 + 1];
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_NAME].value.value_string = sg_usr_property_name;
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_NAME].key = "name";
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_NAME].type = DATA_TEMPLATE_TYPE_STRING;
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_NAME].need_report = 1;
|
||||
|
||||
_init_data_template_property_position();
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_POSITION].value.value_struct.property = sg_usr_property_position;
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_POSITION].value.value_struct.count =
|
||||
TOTAL_USR_PROPERTY_STRUCT_POSITION_COUNT;
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_POSITION].key = "position";
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_POSITION].type = DATA_TEMPLATE_TYPE_STRUCT;
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_POSITION].need_report = 1;
|
||||
|
||||
static char sg_usr_property_power[64 + 1];
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER].value.value_string_enum = sg_usr_property_power;
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER].key = "power";
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER].type = DATA_TEMPLATE_TYPE_STRING_ENUM;
|
||||
sg_usr_data_template_property[USR_PROPERTY_INDEX_POWER].need_report = 1;
|
||||
}
|
||||
|
||||
/**************************************************************************************
|
||||
* user event
|
||||
**************************************************************************************/
|
||||
|
||||
#define TOTAL_USR_EVENT_COUNT 3
|
||||
|
||||
DataTemplateEvent sg_usr_data_template_event[TOTAL_USR_EVENT_COUNT];
|
||||
|
||||
/**
|
||||
* @brief Sample of event post params.
|
||||
*
|
||||
*/
|
||||
static const char* sg_usr_event_status_report_params = "{\"status\":0,\"message\":\"ok\"}";
|
||||
static const char* sg_usr_event_low_voltage_params = "{\"voltage\":1.0}";
|
||||
static const char* sg_usr_event_hardware_fault_params = "{\"name\":\"broken\",\"error_code\":-1}";
|
||||
|
||||
static void _init_data_template_event(void)
|
||||
{
|
||||
sg_usr_data_template_event[USR_EVENT_INDEX_STATUS_REPORT].event_id = "status_report";
|
||||
sg_usr_data_template_event[USR_EVENT_INDEX_STATUS_REPORT].type = IOT_DATA_TEMPLATE_EVENT_TYPE_INFO;
|
||||
sg_usr_data_template_event[USR_EVENT_INDEX_STATUS_REPORT].params = sg_usr_event_status_report_params;
|
||||
|
||||
sg_usr_data_template_event[USR_EVENT_INDEX_LOW_VOLTAGE].event_id = "low_voltage";
|
||||
sg_usr_data_template_event[USR_EVENT_INDEX_LOW_VOLTAGE].type = IOT_DATA_TEMPLATE_EVENT_TYPE_ALERT;
|
||||
sg_usr_data_template_event[USR_EVENT_INDEX_LOW_VOLTAGE].params = sg_usr_event_low_voltage_params;
|
||||
|
||||
sg_usr_data_template_event[USR_EVENT_INDEX_HARDWARE_FAULT].event_id = "hardware_fault";
|
||||
sg_usr_data_template_event[USR_EVENT_INDEX_HARDWARE_FAULT].type = IOT_DATA_TEMPLATE_EVENT_TYPE_FAULT;
|
||||
sg_usr_data_template_event[USR_EVENT_INDEX_HARDWARE_FAULT].params = sg_usr_event_hardware_fault_params;
|
||||
}
|
||||
|
||||
/**************************************************************************************
|
||||
* user action
|
||||
**************************************************************************************/
|
||||
|
||||
#define TOTAL_USR_ACTION_COUNT 1
|
||||
|
||||
DataTemplateAction sg_usr_data_template_action[TOTAL_USR_ACTION_COUNT];
|
||||
|
||||
#define TOTAL_USR_ACTION_LIGHT_BLINK_INPUT_PARAMS_COUNT 3
|
||||
|
||||
static DataTemplateProperty sg_usr_action_light_blink_input[TOTAL_USR_ACTION_LIGHT_BLINK_INPUT_PARAMS_COUNT];
|
||||
|
||||
static void _init_data_template_action_light_blink_input(void)
|
||||
{
|
||||
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME].value.value_int = 0;
|
||||
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME].key = "time";
|
||||
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME].type = DATA_TEMPLATE_TYPE_INT;
|
||||
|
||||
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR].value.value_int = 0;
|
||||
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR].key = "color";
|
||||
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR].type = DATA_TEMPLATE_TYPE_ENUM;
|
||||
|
||||
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME].value.value_int = 0;
|
||||
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME].key = "total_time";
|
||||
sg_usr_action_light_blink_input[USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME].type = DATA_TEMPLATE_TYPE_INT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sample of action reply.
|
||||
*
|
||||
*/
|
||||
static IotDataTemplateActionReply sg_usr_action_light_blink_reply = {
|
||||
.code = 0,
|
||||
.client_token = {.value = "test", .value_len = 4},
|
||||
.response = "{\"err_code\":0}",
|
||||
};
|
||||
|
||||
static void _init_data_template_action(void)
|
||||
{
|
||||
_init_data_template_action_light_blink_input();
|
||||
sg_usr_data_template_action[USR_ACTION_INDEX_LIGHT_BLINK].action_id = "light_blink";
|
||||
sg_usr_data_template_action[USR_ACTION_INDEX_LIGHT_BLINK].input_struct.value_struct.property =
|
||||
sg_usr_action_light_blink_input;
|
||||
sg_usr_data_template_action[USR_ACTION_INDEX_LIGHT_BLINK].input_struct.value_struct.count =
|
||||
TOTAL_USR_ACTION_LIGHT_BLINK_INPUT_PARAMS_COUNT;
|
||||
sg_usr_data_template_action[USR_ACTION_INDEX_LIGHT_BLINK].reply = sg_usr_action_light_blink_reply;
|
||||
}
|
||||
|
||||
/**************************************************************************************
|
||||
* API
|
||||
**************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Init user data template(property/event/action).
|
||||
*
|
||||
*/
|
||||
void usr_data_template_init(void)
|
||||
{
|
||||
_init_data_template_property();
|
||||
_init_data_template_event();
|
||||
_init_data_template_action();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get property value.
|
||||
*
|
||||
* @param[in] index @see UsrPropertyIndex
|
||||
* @return @see DataTemplatePropertyValue
|
||||
*/
|
||||
DataTemplatePropertyValue usr_data_template_property_value_get(UsrPropertyIndex index)
|
||||
{
|
||||
return sg_usr_data_template_property[index].value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set property value.
|
||||
*
|
||||
* @param[in] index @see UsrPropertyIndex
|
||||
* @param[in] value @see DataTemplatePropertyValue, @note value should match property type.
|
||||
*/
|
||||
void usr_data_template_property_value_set(UsrPropertyIndex index, DataTemplatePropertyValue value)
|
||||
{
|
||||
if (sg_usr_data_template_property[index].type == DATA_TEMPLATE_TYPE_STRING ||
|
||||
sg_usr_data_template_property[index].type == DATA_TEMPLATE_TYPE_STRING_ENUM) {
|
||||
strncpy(sg_usr_data_template_property[index].value.value_string, value.value_string,
|
||||
strlen(value.value_string));
|
||||
}
|
||||
sg_usr_data_template_property[index].value = value;
|
||||
sg_usr_data_template_property[index].need_report = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get property(struct) value.
|
||||
*
|
||||
* @param[in] struct_index @see UsrPropertyIndex, @note DATA_TEMPLATE_TYPE_STRUCT is required here.
|
||||
* @param[in] property_index depends on which struct
|
||||
* @return @see DataTemplatePropertyValue
|
||||
*/
|
||||
DataTemplatePropertyValue usr_data_template_property_struct_value_get(UsrPropertyIndex struct_index, int property_index)
|
||||
{
|
||||
return sg_usr_data_template_property[struct_index].value.value_struct.property[property_index].value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set property(struct) value.
|
||||
*
|
||||
* @param[in] struct_index @see UsrPropertyIndex, @note DATA_TEMPLATE_TYPE_STRUCT is required here.
|
||||
* @param[in] property_index depends on which struct
|
||||
* @param[in] value @see DataTemplatePropertyValue, @note value should match property type.
|
||||
*/
|
||||
void usr_data_template_property_struct_value_set(UsrPropertyIndex struct_index, int property_index,
|
||||
DataTemplatePropertyValue value)
|
||||
{
|
||||
if (sg_usr_data_template_property[struct_index].value.value_struct.property[property_index].type ==
|
||||
DATA_TEMPLATE_TYPE_STRING ||
|
||||
sg_usr_data_template_property[struct_index].value.value_struct.property[property_index].type ==
|
||||
DATA_TEMPLATE_TYPE_STRING_ENUM) {
|
||||
strncpy(
|
||||
sg_usr_data_template_property[struct_index].value.value_struct.property[property_index].value.value_string,
|
||||
value.value_string, strlen(value.value_string));
|
||||
}
|
||||
sg_usr_data_template_property[struct_index].need_report = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse control message and set property value.
|
||||
*
|
||||
* @param[in] params params filed of control message
|
||||
*/
|
||||
void usr_data_template_property_parse(UtilsJsonValue params)
|
||||
{
|
||||
_parse_property_array(params.value, params.value_len, sg_usr_data_template_property, TOTAL_USR_PROPERTY_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get property status.
|
||||
*
|
||||
* @param[in] index @see UsrPropertyIndex
|
||||
* @return need_report
|
||||
*/
|
||||
int usr_data_template_property_status_get(UsrPropertyIndex index)
|
||||
{
|
||||
return sg_usr_data_template_property[index].need_report;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Report all the properties needed report.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] buf buffer to report
|
||||
* @param[in] buf_len buffer length
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int usr_data_template_property_report(void* client, char* buf, int buf_len)
|
||||
{
|
||||
char params[512];
|
||||
memset(params, 0, sizeof(params));
|
||||
params[0] = '{';
|
||||
int offset = 1;
|
||||
for (int i = 0; i < TOTAL_USR_PROPERTY_COUNT; i++) {
|
||||
DataTemplateProperty* property = &sg_usr_data_template_property[i];
|
||||
if (property->need_report) {
|
||||
offset += _get_property_node(params + offset, sizeof(params) - offset, property);
|
||||
if (offset > 0) {
|
||||
params[offset++] = ',';
|
||||
}
|
||||
property->need_report = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset > 0) {
|
||||
params[--offset] = '}';
|
||||
return IOT_DataTemplate_PropertyReport(client, buf, buf_len, params);
|
||||
}
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Post event.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] buf buffer to report
|
||||
* @param[in] buf_len buffer length
|
||||
* @param[in] id @see UsrEventIndex
|
||||
* @param[in] params user should construct params according to event.
|
||||
*/
|
||||
void usr_data_template_event_post(void* client, char* buf, int buf_len, UsrEventIndex id, const char* params)
|
||||
{
|
||||
if (params) {
|
||||
sg_usr_data_template_event[id].params = params;
|
||||
}
|
||||
IOT_DataTemplate_EventPost(client, buf, buf_len, sg_usr_data_template_event[id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse action message and set action input params.
|
||||
*
|
||||
* @param[in] action_id action id
|
||||
* @param[in] params params of action
|
||||
* @param[out] index @see UsrActionIndex
|
||||
* @return 0 for success, QCLOUD_ERR_JSON_PARSE for invalid json.
|
||||
*/
|
||||
int usr_data_template_action_parse(UtilsJsonValue action_id, UtilsJsonValue params, UsrActionIndex* index)
|
||||
{
|
||||
DataTemplateProperty* property;
|
||||
|
||||
int input_property_count;
|
||||
|
||||
for (int i = 0; i < TOTAL_USR_ACTION_COUNT; i++) {
|
||||
if (!strncmp(action_id.value, sg_usr_data_template_action[i].action_id, action_id.value_len)) {
|
||||
property = sg_usr_data_template_action[i].input_struct.value_struct.property;
|
||||
input_property_count = sg_usr_data_template_action[i].input_struct.value_struct.count;
|
||||
|
||||
// 1. reset need report
|
||||
for (int j = 0; j < input_property_count; j++) {
|
||||
property[j].need_report = 0;
|
||||
}
|
||||
|
||||
// 2. parse
|
||||
_parse_property_array(params.value, params.value_len, property, input_property_count);
|
||||
|
||||
// 3. check all the input params is set
|
||||
for (int j = 0; j < input_property_count; j++) {
|
||||
if (!property[j].need_report) {
|
||||
return QCLOUD_ERR_JSON_PARSE;
|
||||
}
|
||||
}
|
||||
*index = i;
|
||||
return QCLOUD_RET_SUCCESS;
|
||||
}
|
||||
}
|
||||
return QCLOUD_ERR_JSON_PARSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get input value, should call after usr_data_template_action_parse
|
||||
*
|
||||
* @param[in] index index get from usr_data_template_action_parse
|
||||
* @param[in] property_index property index of action input params
|
||||
* @return @see DataTemplatePropertyValue
|
||||
*/
|
||||
DataTemplatePropertyValue usr_data_template_action_input_value_get(UsrActionIndex index, int property_index)
|
||||
{
|
||||
return sg_usr_data_template_action[index].input_struct.value_struct.property[property_index].value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reply action, should call after parse action message.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] buf buffer to report
|
||||
* @param[in] buf_len buffer length
|
||||
* @param[in] index @see UsrActionIndex
|
||||
* @param[in] client_token client token of action message
|
||||
* @param[in] code result code for action, 0 for success
|
||||
* @param[in] response action output params, user should construct params according to action
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int usr_data_template_action_reply(void* client, char* buf, int buf_len, UsrActionIndex index,
|
||||
UtilsJsonValue client_token, int code, const char* response)
|
||||
{
|
||||
sg_usr_data_template_action[index].reply.code = code;
|
||||
sg_usr_data_template_action[index].reply.client_token = client_token;
|
||||
sg_usr_data_template_action[index].reply.response = response;
|
||||
return IOT_DataTemplate_ActionReply(client, buf, buf_len, sg_usr_data_template_action[index].reply);
|
||||
}
|
@@ -0,0 +1,264 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is
|
||||
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @file data_template_config.h
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-10-09
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-10-09 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#ifndef IOT_HUB_DEVICE_C_SDK_APP_DATA_TEMPLATE_DATA_TEMPLATE_CONFIG_H_
|
||||
#define IOT_HUB_DEVICE_C_SDK_APP_DATA_TEMPLATE_DATA_TEMPLATE_CONFIG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "qcloud_iot_common.h"
|
||||
#include "qcloud_iot_data_template.h"
|
||||
|
||||
/**
|
||||
* @brief Type of property.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
// basic type
|
||||
DATA_TEMPLATE_TYPE_INT,
|
||||
DATA_TEMPLATE_TYPE_ENUM,
|
||||
DATA_TEMPLATE_TYPE_STRING_ENUM,
|
||||
DATA_TEMPLATE_TYPE_FLOAT,
|
||||
DATA_TEMPLATE_TYPE_BOOL,
|
||||
DATA_TEMPLATE_TYPE_STRING,
|
||||
DATA_TEMPLATE_TYPE_TIME,
|
||||
// construct by basic type
|
||||
DATA_TEMPLATE_TYPE_STRUCT,
|
||||
DATA_TEMPLATE_TYPE_ARRAY,
|
||||
} DataTemplatePropertyType;
|
||||
|
||||
/**
|
||||
* @brief Declaration.
|
||||
*
|
||||
*/
|
||||
typedef struct DataTemplateProperty DataTemplateProperty;
|
||||
typedef union DataTemplatePropertyValue DataTemplatePropertyValue;
|
||||
|
||||
/**
|
||||
* @brief Property value definition.
|
||||
*
|
||||
*/
|
||||
union DataTemplatePropertyValue {
|
||||
int32_t value_int;
|
||||
int32_t value_enum;
|
||||
char* value_string_enum;
|
||||
float value_float;
|
||||
int32_t value_bool;
|
||||
char* value_string;
|
||||
uint32_t value_time;
|
||||
struct {
|
||||
DataTemplateProperty* property;
|
||||
int count;
|
||||
} value_struct;
|
||||
DataTemplatePropertyValue* value_arrary; /**< not supportted yet */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Property definition.
|
||||
*
|
||||
*/
|
||||
struct DataTemplateProperty {
|
||||
DataTemplatePropertyType type;
|
||||
const char* key;
|
||||
DataTemplatePropertyValue value;
|
||||
int need_report;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Event definition.
|
||||
*
|
||||
*/
|
||||
#define DataTemplateEvent IotDataTemplateEventData
|
||||
|
||||
/**
|
||||
* @brief Action definition.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
const char* action_id;
|
||||
DataTemplatePropertyValue input_struct;
|
||||
IotDataTemplateActionReply reply;
|
||||
} DataTemplateAction;
|
||||
|
||||
/**************************************************************************************
|
||||
* usr data template definition
|
||||
**************************************************************************************/
|
||||
|
||||
typedef enum {
|
||||
USR_PROPERTY_INDEX_POWER_SWITCH = 0,
|
||||
USR_PROPERTY_INDEX_COLOR,
|
||||
USR_PROPERTY_INDEX_BRIGHTNESS,
|
||||
USR_PROPERTY_INDEX_NAME,
|
||||
USR_PROPERTY_INDEX_POSITION,
|
||||
USR_PROPERTY_INDEX_POWER,
|
||||
} UsrPropertyIndex;
|
||||
|
||||
typedef enum {
|
||||
USR_PROPERTY_POSITION_INDEX_LONGITUDE = 0,
|
||||
USR_PROPERTY_POSITION_INDEX_LATITUDE,
|
||||
} UsrPropertyPositionIndex;
|
||||
|
||||
typedef enum {
|
||||
USR_EVENT_INDEX_STATUS_REPORT = 0,
|
||||
USR_EVENT_INDEX_LOW_VOLTAGE,
|
||||
USR_EVENT_INDEX_HARDWARE_FAULT,
|
||||
} UsrEventIndex;
|
||||
|
||||
typedef enum {
|
||||
USR_ACTION_INDEX_LIGHT_BLINK = 0,
|
||||
} UsrActionIndex;
|
||||
|
||||
typedef enum {
|
||||
USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TIME = 0,
|
||||
USR_ACTION_LIGHT_BLINK_INPUT_INDEX_COLOR,
|
||||
USR_ACTION_LIGHT_BLINK_INPUT_INDEX_TOTAL_TIME,
|
||||
} UsrActionLightBlinkInputIndex;
|
||||
|
||||
/**************************************************************************************
|
||||
* api for user data template
|
||||
**************************************************************************************/
|
||||
|
||||
/**
|
||||
* @brief Init user data template(property/event/action).
|
||||
*
|
||||
*/
|
||||
void usr_data_template_init(void);
|
||||
|
||||
/**
|
||||
* @brief Get property value.
|
||||
*
|
||||
* @param[in] index @see UsrPropertyIndex
|
||||
* @return @see DataTemplatePropertyValue
|
||||
*/
|
||||
DataTemplatePropertyValue usr_data_template_property_value_get(UsrPropertyIndex index);
|
||||
|
||||
/**
|
||||
* @brief Set property value.
|
||||
*
|
||||
* @param[in] index @see UsrPropertyIndex
|
||||
* @param[in] value @see DataTemplatePropertyValue, @note value should match property type.
|
||||
*/
|
||||
void usr_data_template_property_value_set(UsrPropertyIndex index, DataTemplatePropertyValue value);
|
||||
|
||||
/**
|
||||
* @brief Get property(struct) value.
|
||||
*
|
||||
* @param[in] struct_index @see UsrPropertyIndex, @note DATA_TEMPLATE_TYPE_STRUCT is required here.
|
||||
* @param[in] property_index depends on which struct
|
||||
* @return @see DataTemplatePropertyValue
|
||||
*/
|
||||
DataTemplatePropertyValue usr_data_template_property_struct_value_get(UsrPropertyIndex struct_index,
|
||||
int property_index);
|
||||
|
||||
/**
|
||||
* @brief Set property(struct) value.
|
||||
*
|
||||
* @param[in] struct_index @see UsrPropertyIndex, @note DATA_TEMPLATE_TYPE_STRUCT is required here.
|
||||
* @param[in] property_index depends on which struct
|
||||
* @param[in] value @see DataTemplatePropertyValue, @note value should match property type.
|
||||
*/
|
||||
void usr_data_template_property_struct_value_set(UsrPropertyIndex struct_index, int property_index,
|
||||
DataTemplatePropertyValue value);
|
||||
|
||||
/**
|
||||
* @brief Parse control message and set property value.
|
||||
*
|
||||
* @param[in] params params filed of control message
|
||||
*/
|
||||
void usr_data_template_property_parse(UtilsJsonValue params);
|
||||
|
||||
/**
|
||||
* @brief Get property status.
|
||||
*
|
||||
* @param[in] index @see UsrPropertyIndex
|
||||
* @return need_report
|
||||
*/
|
||||
int usr_data_template_property_status_get(UsrPropertyIndex index);
|
||||
|
||||
/**
|
||||
* @brief Report all the properties needed report.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] buf buffer to report
|
||||
* @param[in] buf_len buffer length
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int usr_data_template_property_report(void* client, char* buf, int buf_len);
|
||||
|
||||
/**
|
||||
* @brief Post event.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] buf buffer to report
|
||||
* @param[in] buf_len buffer length
|
||||
* @param[in] id @see UsrEventIndex
|
||||
* @param[in] params user should construct params according to event.
|
||||
*/
|
||||
void usr_data_template_event_post(void* client, char* buf, int buf_len, UsrEventIndex id, const char* params);
|
||||
|
||||
/**
|
||||
* @brief Parse action message and set action input params.
|
||||
*
|
||||
* @param[in] action_id action id
|
||||
* @param[in] params params of action
|
||||
* @param[out] index @see UsrActionIndex
|
||||
* @return 0 for success, QCLOUD_ERR_JSON_PARSE for invalid json.
|
||||
*/
|
||||
int usr_data_template_action_parse(UtilsJsonValue action_id, UtilsJsonValue params, UsrActionIndex* index);
|
||||
|
||||
/**
|
||||
* @brief Get input value, should call after usr_data_template_action_parse
|
||||
*
|
||||
* @param[in] index index get from usr_data_template_action_parse
|
||||
* @param[in] property_index property index of action input params
|
||||
* @return @see DataTemplatePropertyValue
|
||||
*/
|
||||
DataTemplatePropertyValue usr_data_template_action_input_value_get(UsrActionIndex index, int property_index);
|
||||
|
||||
/**
|
||||
* @brief Reply action, should call after parse action message.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @param[in] buf buffer to report
|
||||
* @param[in] buf_len buffer length
|
||||
* @param[in] index @see UsrActionIndex
|
||||
* @param[in] client_token client token of action message
|
||||
* @param[in] code result code for action, 0 for success
|
||||
* @param[in] response action output params, user should construct params according to action
|
||||
* @return packet id (>=0) when success, or err code (<0) @see IotReturnCode
|
||||
*/
|
||||
int usr_data_template_action_reply(void* client, char* buf, int buf_len, UsrActionIndex index,
|
||||
UtilsJsonValue client_token, int code, const char* response);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // IOT_HUB_DEVICE_C_SDK_APP_DATA_TEMPLATE_DATA_TEMPLATE_CONFIG_H_
|
@@ -0,0 +1,3 @@
|
||||
file(GLOB src_app ${CMAKE_CURRENT_SOURCE_DIR}/*.c)
|
||||
add_executable(app_ota ${src_app})
|
||||
target_link_libraries(app_ota ${libsdk})
|
247
components/connectivity/iot-hub-device-c-sdk/app/ota/ota_app.c
Normal file
247
components/connectivity/iot-hub-device-c-sdk/app/ota/ota_app.c
Normal file
@@ -0,0 +1,247 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is
|
||||
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @file broadcast_sample.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-07-18
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-07-18 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "qcloud_iot_common.h"
|
||||
#include "ota_downloader.h"
|
||||
|
||||
#include "utils_log.h"
|
||||
|
||||
/**
|
||||
* @brief MQTT event callback, @see MQTTEventHandleFun
|
||||
*
|
||||
* @param[in] client pointer to mqtt client
|
||||
* @param[in] handle_context context
|
||||
* @param[in] msg msg
|
||||
*/
|
||||
static void _mqtt_event_handler(void *client, void *handle_context, MQTTEventMsg *msg)
|
||||
{
|
||||
MQTTMessage *mqtt_message = (MQTTMessage *)msg->msg;
|
||||
uintptr_t packet_id = (uintptr_t)msg->msg;
|
||||
|
||||
switch (msg->event_type) {
|
||||
case MQTT_EVENT_UNDEF:
|
||||
Log_i("undefined event occur.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_DISCONNECT:
|
||||
Log_i("MQTT disconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_RECONNECT:
|
||||
Log_i("MQTT reconnect.");
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_RECEIVED:
|
||||
Log_i("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
|
||||
mqtt_message->topic_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->topic_name),
|
||||
mqtt_message->payload_len, STRING_PTR_PRINT_SANITY_CHECK(mqtt_message->payload_str));
|
||||
break;
|
||||
case MQTT_EVENT_SUBSCRIBE_SUCCESS:
|
||||
Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_TIMEOUT:
|
||||
Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_SUBSCRIBE_NACK:
|
||||
Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_SUCCESS:
|
||||
Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_TIMEOUT:
|
||||
Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_UNSUBSCRIBE_NACK:
|
||||
Log_i("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_SUCCESS:
|
||||
Log_i("publish success, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_TIMEOUT:
|
||||
Log_i("publish timeout, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
|
||||
case MQTT_EVENT_PUBLISH_NACK:
|
||||
Log_i("publish nack, packet-id=%u", (unsigned int)packet_id);
|
||||
break;
|
||||
default:
|
||||
Log_i("Should NOT arrive here.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Setup MQTT construct parameters.
|
||||
*
|
||||
* @param[in,out] initParams @see MQTTInitParams
|
||||
* @param[in] device_info @see DeviceInfo
|
||||
*/
|
||||
static void _setup_connect_init_params(MQTTInitParams *init_params, DeviceInfo *device_info)
|
||||
{
|
||||
init_params->device_info = device_info;
|
||||
init_params->command_timeout = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT;
|
||||
init_params->keep_alive_interval_ms = QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL;
|
||||
init_params->auto_connect_enable = 1;
|
||||
init_params->event_handle.h_fp = _mqtt_event_handler;
|
||||
init_params->event_handle.context = NULL;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// OTA callback
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void _update_firmware_callback(UtilsJsonValue version, UtilsJsonValue url, UtilsJsonValue md5sum, uint32_t file_size,
|
||||
void *usr_data)
|
||||
{
|
||||
Log_i("recv firmware: version=%.*s|url=%.*s|md5sum=%.*s|file_size=%u", version.value_len, version.value,
|
||||
url.value_len, url.value, md5sum.value_len, md5sum.value, file_size);
|
||||
// only one firmware one time is supportted now
|
||||
OTAFirmwareInfo firmware_info;
|
||||
memset(&firmware_info, 0, sizeof(OTAFirmwareInfo));
|
||||
strncpy(firmware_info.version, version.value, version.value_len);
|
||||
strncpy(firmware_info.md5sum, md5sum.value, md5sum.value_len);
|
||||
firmware_info.file_size = file_size;
|
||||
ota_downloader_info_set(&firmware_info, url.value, url.value_len);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Main
|
||||
// ----------------------------------------------------------------------------
|
||||
static int sg_main_exit = 0;
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void _main_exit(int sig)
|
||||
{
|
||||
Log_e("demo exit by signal:%d\n", sig);
|
||||
sg_main_exit = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#ifdef __linux__
|
||||
signal(SIGINT, _main_exit);
|
||||
#endif
|
||||
|
||||
int rc;
|
||||
|
||||
char buf[1024];
|
||||
|
||||
// init log level
|
||||
LogHandleFunc func = {0};
|
||||
|
||||
func.log_malloc = HAL_Malloc;
|
||||
func.log_free = HAL_Free;
|
||||
func.log_get_current_time_str = HAL_Timer_Current;
|
||||
func.log_printf = HAL_Printf;
|
||||
utils_log_init(func, LOG_LEVEL_DEBUG, 2048);
|
||||
|
||||
DeviceInfo device_info;
|
||||
rc = HAL_GetDevInfo((void *)&device_info);
|
||||
if (rc) {
|
||||
Log_e("get device info failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// init connection
|
||||
MQTTInitParams init_params = DEFAULT_MQTT_INIT_PARAMS;
|
||||
_setup_connect_init_params(&init_params, &device_info);
|
||||
|
||||
// create MQTT client and connect with server
|
||||
void *client = IOT_MQTT_Construct(&init_params);
|
||||
if (client) {
|
||||
Log_i("Cloud Device Construct Success");
|
||||
} else {
|
||||
Log_e("MQTT Construct failed!");
|
||||
return QCLOUD_ERR_FAILURE;
|
||||
}
|
||||
|
||||
// OTA init
|
||||
IotOTAUpdateCallback ota_callback = {
|
||||
.update_firmware_callback = _update_firmware_callback,
|
||||
.report_version_reply_callback = NULL,
|
||||
};
|
||||
|
||||
rc = IOT_OTA_Init(client, ota_callback, NULL);
|
||||
if (rc) {
|
||||
Log_e("OTA init failed!, rc=%d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = IOT_OTA_ReportVersion(client, buf, sizeof(buf), QCLOUD_IOT_DEVICE_SDK_VERSION);
|
||||
if (rc) {
|
||||
Log_e("OTA report version failed!, rc=%d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = ota_downloader_init(client);
|
||||
if (rc) {
|
||||
Log_e("OTA downloader init failed!, rc=%d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
do {
|
||||
ota_downloader_process();
|
||||
|
||||
rc = IOT_MQTT_Yield(client, QCLOUD_IOT_MQTT_YIELD_TIMEOUT);
|
||||
switch (rc) {
|
||||
case QCLOUD_RET_SUCCESS:
|
||||
break;
|
||||
case QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT:
|
||||
continue;
|
||||
case QCLOUD_RET_MQTT_RECONNECTED:
|
||||
break;
|
||||
default:
|
||||
Log_e("Exit loop caused of errCode:%d", rc);
|
||||
goto exit;
|
||||
}
|
||||
} while (!sg_main_exit);
|
||||
exit:
|
||||
ota_downloader_deinit();
|
||||
IOT_OTA_Deinit(client);
|
||||
rc = IOT_MQTT_Destroy(&client);
|
||||
utils_log_deinit();
|
||||
return rc;
|
||||
}
|
@@ -0,0 +1,400 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is
|
||||
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @file ota_downloader.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-10-20
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-10-20 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "ota_downloader.h"
|
||||
#include "ota_firmware_save.h"
|
||||
#include "utils_log.h"
|
||||
#include "utils_md5.h"
|
||||
|
||||
#define OTA_HTTP_TIMEOUT_MS 5000
|
||||
#define OTA_HTTP_BUF_SIZE 1024
|
||||
#define MAX_SIZE_OF_DOWNLOAD_URL 512
|
||||
|
||||
/**
|
||||
* @brief Break point info.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
OTAFirmwareInfo file_id;
|
||||
uint32_t downloaded_size;
|
||||
} OTADownloadInfo;
|
||||
|
||||
/**
|
||||
* @brief OTA downloader handle.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
void* cos_download;
|
||||
void* mqtt_client;
|
||||
void* downloader;
|
||||
|
||||
OTAFirmwareInfo download_now;
|
||||
OTADownloadInfo break_point;
|
||||
|
||||
char download_url[MAX_SIZE_OF_DOWNLOAD_URL];
|
||||
uint8_t download_buff[OTA_HTTP_BUF_SIZE];
|
||||
uint32_t download_size;
|
||||
IotMd5Context download_md5_ctx;
|
||||
|
||||
OTADownloaderStatus status;
|
||||
} OTADownloaderHandle;
|
||||
|
||||
/**
|
||||
* @brief Handle.
|
||||
*
|
||||
*/
|
||||
static OTADownloaderHandle sg_ota_downloader_handle = {0};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Downloader function
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// break point function
|
||||
|
||||
/**
|
||||
* @brief Read break point from file.
|
||||
*
|
||||
* @param[in,out] usr_data @see OTADownloaderHandle
|
||||
* @return 0 for success
|
||||
*/
|
||||
static int _ota_break_point_init(void* usr_data)
|
||||
{
|
||||
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
|
||||
|
||||
sg_ota_downloader_handle.cos_download = NULL;
|
||||
utils_md5_reset(&handle->download_md5_ctx);
|
||||
return ota_break_point_read((uint8_t*)&handle->break_point, sizeof(handle->break_point)) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Memset break point.
|
||||
*
|
||||
* @param[in,out] usr_data @see OTADownloaderHandle
|
||||
*/
|
||||
static void _ota_break_point_deinit(void* usr_data)
|
||||
{
|
||||
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
|
||||
memset(&handle->break_point, 0, sizeof(handle->break_point));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set break point using download now info.
|
||||
*
|
||||
* @param[in,out] usr_data @see OTADownloaderHandle
|
||||
* @return 0 for success
|
||||
*/
|
||||
static int _ota_break_point_set(void* usr_data)
|
||||
{
|
||||
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
|
||||
// read break point info from flash
|
||||
memset(&handle->break_point, 0, sizeof(handle->break_point));
|
||||
memcpy(&handle->break_point.file_id, &handle->download_now, sizeof(handle->break_point.file_id));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update break point and save.
|
||||
*
|
||||
* @param[in,out] usr_data @see OTADownloaderHandle
|
||||
* @return 0 for success
|
||||
*/
|
||||
static int _ota_break_point_save(void* usr_data)
|
||||
{
|
||||
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
|
||||
|
||||
// update md5 sum & download_size
|
||||
handle->break_point.downloaded_size += handle->download_size;
|
||||
utils_md5_update(&handle->download_md5_ctx, handle->download_buff, handle->download_size);
|
||||
|
||||
// report progress
|
||||
char buf[256];
|
||||
int buf_len = sizeof(buf);
|
||||
IOT_OTA_ReportProgress(handle->mqtt_client, buf, buf_len, IOT_OTA_REPORT_TYPE_DOWNLOADING,
|
||||
handle->break_point.downloaded_size * 100 / handle->download_now.file_size,
|
||||
handle->break_point.file_id.version);
|
||||
|
||||
// write to local, only write downloaded size is ok
|
||||
return ota_break_point_write((uint8_t*)&handle->break_point, sizeof(OTADownloadInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if break point matches download now info.
|
||||
*
|
||||
* @param[in,out] usr_data @see OTADownloaderHandle
|
||||
* @return 0 for success
|
||||
*/
|
||||
static int _ota_break_point_check(void* usr_data)
|
||||
{
|
||||
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
|
||||
// version should be the same, download size should not bigger than file size
|
||||
Log_d("download now:%s,%d,%s", handle->download_now.version, handle->download_now.file_size,
|
||||
handle->download_now.md5sum);
|
||||
Log_d("break point:%s,%d,%d,%s", handle->break_point.file_id.version, handle->break_point.file_id.file_size,
|
||||
handle->break_point.downloaded_size, handle->break_point.file_id.md5sum);
|
||||
return strncmp(handle->break_point.file_id.version, handle->download_now.version, MAX_SIZE_OF_FW_VERSION) ||
|
||||
handle->break_point.file_id.file_size != handle->download_now.file_size ||
|
||||
handle->break_point.downloaded_size > handle->download_now.file_size ||
|
||||
strncmp(handle->break_point.file_id.md5sum, handle->download_now.md5sum, 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculate md5 sum according break point.
|
||||
*
|
||||
* @param[in,out] usr_data @see OTADownloaderHandle
|
||||
* @return 0 for success
|
||||
*/
|
||||
static int _ota_break_point_restore(void* usr_data)
|
||||
{
|
||||
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
|
||||
|
||||
// update md5 according downloaded data
|
||||
size_t rlen, total_read = 0, size = 0;
|
||||
|
||||
size = handle->break_point.downloaded_size;
|
||||
|
||||
while (size > 0) {
|
||||
rlen = (size > OTA_HTTP_BUF_SIZE) ? OTA_HTTP_BUF_SIZE : size;
|
||||
if (ota_firmware_read(handle->download_buff, rlen, total_read) < 0) {
|
||||
Log_e("read data failed");
|
||||
handle->break_point.downloaded_size = 0;
|
||||
break;
|
||||
}
|
||||
utils_md5_update(&handle->download_md5_ctx, handle->download_buff, rlen);
|
||||
size -= rlen;
|
||||
total_read += rlen;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// data download function
|
||||
|
||||
/**
|
||||
* @brief Init cos downloader.
|
||||
*
|
||||
* @param[in,out] usr_data @see OTADownloaderHandle
|
||||
* @return 0 for success
|
||||
*/
|
||||
static int _ota_data_download_init(void* usr_data)
|
||||
{
|
||||
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
|
||||
IotCosDownloadParams params = {
|
||||
.url = handle->download_url,
|
||||
.offset = handle->break_point.downloaded_size,
|
||||
.file_size = handle->break_point.file_id.file_size,
|
||||
.is_fragmentation = false,
|
||||
.is_https_enabled = false,
|
||||
};
|
||||
handle->cos_download = IOT_COS_DownloadInit(¶ms);
|
||||
return handle->cos_download ? 0 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deinit cos downloader.
|
||||
*
|
||||
* @param[in,out] usr_data @see OTADownloaderHandle
|
||||
*/
|
||||
static void _ota_data_download_deinit(void* usr_data)
|
||||
{
|
||||
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
|
||||
IOT_COS_DownloadDeinit(handle->cos_download);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if download finished.
|
||||
*
|
||||
* @param[in,out] usr_data @see OTADownloaderHandle
|
||||
* @return 0 for success
|
||||
*/
|
||||
static int _ota_data_download_is_over(void* usr_data)
|
||||
{
|
||||
// check download is over
|
||||
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
|
||||
return handle->break_point.downloaded_size == handle->download_now.file_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Download from cos server.
|
||||
*
|
||||
* @param[in,out] usr_data @see OTADownloaderHandle
|
||||
* @return 0 for success
|
||||
*/
|
||||
static int _ota_data_download_recv(void* usr_data)
|
||||
{
|
||||
// download data using http
|
||||
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
|
||||
// TODO: https download
|
||||
handle->download_size =
|
||||
IOT_COS_DownloadFetch(handle->cos_download, handle->download_buff, OTA_HTTP_BUF_SIZE, OTA_HTTP_TIMEOUT_MS);
|
||||
return handle->download_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sava firmware to file.
|
||||
*
|
||||
* @param[in,out] usr_data @see OTADownloaderHandle
|
||||
* @return 0 for success
|
||||
*/
|
||||
static int _ota_data_download_save(void* usr_data)
|
||||
{
|
||||
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
|
||||
return handle->download_size > 0
|
||||
? ota_firmware_write(handle->download_buff, handle->download_size, handle->break_point.downloaded_size)
|
||||
: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process download result.
|
||||
*
|
||||
* @param[in,out] usr_data @see OTADownloaderHandle
|
||||
* @param[in] status status when finish download, @see UtilsDownloaderStatus
|
||||
* @return 0 for success
|
||||
*/
|
||||
static int _ota_data_download_finish(void* usr_data, UtilsDownloaderStatus status)
|
||||
{
|
||||
OTADownloaderHandle* handle = (OTADownloaderHandle*)usr_data;
|
||||
|
||||
int rc = 0;
|
||||
char buf[256];
|
||||
int buf_len = sizeof(buf);
|
||||
|
||||
switch (status) {
|
||||
case UTILS_DOWNLOADER_STATUS_SUCCESS:
|
||||
utils_md5_finish(&handle->download_md5_ctx);
|
||||
ota_firmware_finish(handle->download_now.file_size);
|
||||
|
||||
int valid = !utils_md5_compare(&handle->download_md5_ctx, handle->download_now.md5sum);
|
||||
|
||||
if (!valid) {
|
||||
memset(&handle->break_point, 0, sizeof(handle->break_point));
|
||||
ota_break_point_write((uint8_t*)&handle->break_point, sizeof(handle->break_point));
|
||||
}
|
||||
|
||||
rc = valid ? IOT_OTA_ReportProgress(handle->mqtt_client, buf, buf_len, IOT_OTA_REPORT_TYPE_UPGRADE_SUCCESS,
|
||||
0, handle->download_now.version)
|
||||
: IOT_OTA_ReportProgress(handle->mqtt_client, buf, buf_len, IOT_OTA_REPORT_TYPE_MD5_NOT_MATCH, 0,
|
||||
handle->download_now.version);
|
||||
break;
|
||||
case UTILS_DOWNLOADER_STATUS_NETWORK_FAILED:
|
||||
rc = IOT_OTA_ReportProgress(handle->mqtt_client, buf, buf_len, IOT_OTA_REPORT_TYPE_DOWNLOAD_TIMEOUT, 0,
|
||||
handle->download_now.version);
|
||||
break;
|
||||
case UTILS_DOWNLOADER_STATUS_BREAK_POINT_FAILED:
|
||||
case UTILS_DOWNLOADER_STATUS_DATA_DOWNLOAD_FAILED:
|
||||
rc = IOT_OTA_ReportProgress(handle->mqtt_client, buf, buf_len, IOT_OTA_REPORT_TYPE_UPGRADE_FAIL, 0,
|
||||
handle->download_now.version);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
handle->status = OTA_DOWNLOADER_STATUS_FINISHED;
|
||||
return rc;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// API
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Init ota downloader.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return 0 for success.
|
||||
*/
|
||||
int ota_downloader_init(void* client)
|
||||
{
|
||||
// downloader init
|
||||
UtilsDownloaderFunction ota_callback = {
|
||||
.downloader_malloc = HAL_Malloc,
|
||||
.downloader_free = HAL_Free,
|
||||
// break point
|
||||
.break_point_init = _ota_break_point_init,
|
||||
.break_point_deinit = _ota_break_point_deinit,
|
||||
.break_point_set = _ota_break_point_set,
|
||||
.break_point_save = _ota_break_point_save,
|
||||
.break_point_check = _ota_break_point_check,
|
||||
.break_point_restore = _ota_break_point_restore,
|
||||
|
||||
// data download
|
||||
.data_download_init = _ota_data_download_init,
|
||||
.data_download_deinit = _ota_data_download_deinit,
|
||||
.data_download_is_over = _ota_data_download_is_over,
|
||||
.data_download_recv = _ota_data_download_recv,
|
||||
.data_download_save = _ota_data_download_save,
|
||||
.data_download_finish = _ota_data_download_finish,
|
||||
};
|
||||
|
||||
sg_ota_downloader_handle.downloader = utils_downloader_init(ota_callback, &sg_ota_downloader_handle);
|
||||
if (!sg_ota_downloader_handle.downloader) {
|
||||
Log_e("initialize downloaded failed");
|
||||
return -1;
|
||||
}
|
||||
sg_ota_downloader_handle.mqtt_client = client;
|
||||
sg_ota_downloader_handle.status = OTA_DOWNLOADER_STATUS_INITTED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set download info of ota firmware.
|
||||
*
|
||||
* @param[in] firmware_info pointer to firmware info
|
||||
* @param[in] url url of cos download
|
||||
* @param[in] url_len download length
|
||||
*/
|
||||
void ota_downloader_info_set(OTAFirmwareInfo* firmware_info, const char* url, int url_len)
|
||||
{
|
||||
if (OTA_DOWNLOADER_STATUS_DOWNLOADING != sg_ota_downloader_handle.status) {
|
||||
memcpy(&sg_ota_downloader_handle.download_now, firmware_info, sizeof(OTAFirmwareInfo));
|
||||
strncpy(sg_ota_downloader_handle.download_url, url, url_len);
|
||||
sg_ota_downloader_handle.download_url[url_len] = '\0';
|
||||
|
||||
sg_ota_downloader_handle.status = OTA_DOWNLOADER_STATUS_DOWNLOADING;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process ota download.
|
||||
*
|
||||
* @return @see OTADownloaderStatus
|
||||
*/
|
||||
OTADownloaderStatus ota_downloader_process(void)
|
||||
{
|
||||
if (OTA_DOWNLOADER_STATUS_DOWNLOADING == sg_ota_downloader_handle.status) {
|
||||
utils_downloader_process(sg_ota_downloader_handle.downloader);
|
||||
}
|
||||
return sg_ota_downloader_handle.status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deinit ota downloader.
|
||||
*
|
||||
*/
|
||||
void ota_downloader_deinit(void)
|
||||
{
|
||||
utils_downloader_deinit(sg_ota_downloader_handle.downloader);
|
||||
memset(&sg_ota_downloader_handle, 0, sizeof(sg_ota_downloader_handle));
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is
|
||||
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @file ota_downloader.h
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-10-20
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-10-20 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#ifndef IOT_HUB_DEVICE_C_SDK_APP_OTA_OTA_DOWNLOADER_H_
|
||||
#define IOT_HUB_DEVICE_C_SDK_APP_OTA_OTA_DOWNLOADER_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "qcloud_iot_common.h"
|
||||
#include "utils_downloader.h"
|
||||
|
||||
/**
|
||||
* @brief Firmware version size.
|
||||
*
|
||||
*/
|
||||
#define MAX_SIZE_OF_FW_VERSION 32
|
||||
|
||||
/**
|
||||
* @brief OTA firmware info.
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
char version[MAX_SIZE_OF_FW_VERSION + 4];
|
||||
uint32_t file_size;
|
||||
char md5sum[33];
|
||||
} OTAFirmwareInfo;
|
||||
|
||||
/**
|
||||
* @brief Downloader status.
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
OTA_DOWNLOADER_STATUS_NO_INITTED = 0,
|
||||
OTA_DOWNLOADER_STATUS_INITTED,
|
||||
OTA_DOWNLOADER_STATUS_DOWNLOADING,
|
||||
OTA_DOWNLOADER_STATUS_FINISHED,
|
||||
} OTADownloaderStatus;
|
||||
|
||||
/**
|
||||
* @brief Init ota downloader.
|
||||
*
|
||||
* @param[in,out] client pointer to mqtt client
|
||||
* @return 0 for success.
|
||||
*/
|
||||
int ota_downloader_init(void* client);
|
||||
|
||||
/**
|
||||
* @brief Set download info of ota firmware.
|
||||
*
|
||||
* @param[in] firmware_info pointer to firmware info
|
||||
* @param[in] url url of cos download
|
||||
* @param[in] url_len download length
|
||||
*/
|
||||
void ota_downloader_info_set(OTAFirmwareInfo* firmware_info, const char* url, int url_len);
|
||||
|
||||
/**
|
||||
* @brief Process ota download.
|
||||
*
|
||||
* @return @see OTADownloaderStatus
|
||||
*/
|
||||
OTADownloaderStatus ota_downloader_process(void);
|
||||
|
||||
/**
|
||||
* @brief Deinit ota downloader.
|
||||
*
|
||||
*/
|
||||
void ota_downloader_deinit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // IOT_HUB_DEVICE_C_SDK_APP_OTA_OTA_DOWNLOADER_H_
|
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* @copyright
|
||||
*
|
||||
* Tencent is pleased to support the open source community by making IoT Hub available.
|
||||
* Copyright(C) 2018 - 2021 THL A29 Limited, a Tencent company.All rights reserved.
|
||||
*
|
||||
* Licensed under the MIT License(the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is
|
||||
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @file ota_firmware_save.c
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-10-20
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-10-20 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#include "ota_firmware_save.h"
|
||||
|
||||
#include "utils_log.h"
|
||||
|
||||
#define OTA_FILE_PATH "./app_ota_fw.bin"
|
||||
#define OTA_BREAK_POINT_FILE_PATH "./break_point.dat"
|
||||
|
||||
/**
|
||||
* @brief Read ota break point from file.
|
||||
*
|
||||
* @param[out] buf data to read
|
||||
* @param[in] buf_len data buffer len
|
||||
* @return > 0 for read len, -1 for fail
|
||||
*/
|
||||
int ota_break_point_read(uint8_t *buf, uint32_t buf_len)
|
||||
{
|
||||
FILE *fp = fopen(OTA_BREAK_POINT_FILE_PATH, "rb");
|
||||
if (!fp) {
|
||||
fp = fopen(OTA_BREAK_POINT_FILE_PATH, "wb+");
|
||||
if (!fp) {
|
||||
Log_e("create file failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int ret = fread(buf, 1, buf_len, fp);
|
||||
fclose(fp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write ota break point to file.
|
||||
*
|
||||
* @param[in] data break point data to write
|
||||
* @param[in] data_len data length
|
||||
* @return 0 for success
|
||||
*/
|
||||
int ota_break_point_write(const uint8_t *data, uint32_t data_len)
|
||||
{
|
||||
FILE *fp = fopen(OTA_BREAK_POINT_FILE_PATH, "wb+");
|
||||
if (!fp) {
|
||||
Log_e("open file failed");
|
||||
return -1;
|
||||
}
|
||||
fwrite(data, 1, data_len, fp);
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read firmware from file.
|
||||
*
|
||||
* @param[out] buf data to read
|
||||
* @param[in] buf_len data buffer len
|
||||
* @param[in] offset offset of file
|
||||
* @return > 0 for read len, -1 for fail
|
||||
*/
|
||||
int ota_firmware_read(uint8_t *buf, uint32_t buf_len, uint32_t offset)
|
||||
{
|
||||
int ret = -1;
|
||||
FILE *fp = fopen(OTA_FILE_PATH, "rb");
|
||||
if (!fp) {
|
||||
fp = fopen(OTA_FILE_PATH, "wb+");
|
||||
if (!fp) {
|
||||
Log_e("create file failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ret = fseek(fp, offset, SEEK_SET);
|
||||
if (!ret) {
|
||||
ret = fread(buf, 1, buf_len, fp);
|
||||
}
|
||||
fclose(fp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write firmware to file.
|
||||
*
|
||||
* @param[in] data firmware data to write
|
||||
* @param[in] data_len data length
|
||||
* @param[in] offset offset of file
|
||||
* @return 0 for success
|
||||
*/
|
||||
int ota_firmware_write(uint8_t *data, uint32_t data_len, uint32_t offset)
|
||||
{
|
||||
int ret = -1;
|
||||
FILE *fp = fopen(OTA_FILE_PATH, "rb+");
|
||||
if (!fp) {
|
||||
fp = fopen(OTA_FILE_PATH, "wb+");
|
||||
if (!fp) {
|
||||
Log_e("create file failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ret = fseek(fp, offset, SEEK_SET);
|
||||
if (!ret) {
|
||||
fwrite(data, 1, data_len, fp);
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finish write firmware.
|
||||
*
|
||||
* @param[in] total_len total length of firmware
|
||||
* @return 0 for success
|
||||
*/
|
||||
int ota_firmware_finish(uint32_t total_len)
|
||||
{
|
||||
return 0;
|
||||
}
|
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* @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_firmware_save.h
|
||||
* @brief
|
||||
* @author fancyxu (fancyxu@tencent.com)
|
||||
* @version 1.0
|
||||
* @date 2021-10-20
|
||||
*
|
||||
* @par Change Log:
|
||||
* <table>
|
||||
* <tr><th>Date <th>Version <th>Author <th>Description
|
||||
* <tr><td>2021-10-20 <td>1.0 <td>fancyxu <td>first commit
|
||||
* </table>
|
||||
*/
|
||||
|
||||
#ifndef IOT_HUB_DEVICE_C_SDK_APP_OTA_OTA_FIRMWARE_SAVE_H_
|
||||
#define IOT_HUB_DEVICE_C_SDK_APP_OTA_OTA_FIRMWARE_SAVE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Read ota break point from file.
|
||||
*
|
||||
* @param[out] buf data to read
|
||||
* @param[in] buf_len data buffer len
|
||||
* @return > 0 for read len, -1 for fail
|
||||
*/
|
||||
int ota_break_point_read(uint8_t *buf, uint32_t buf_len);
|
||||
|
||||
/**
|
||||
* @brief Write ota break point to file.
|
||||
*
|
||||
* @param[in] data break point data to write
|
||||
* @param[in] data_len data length
|
||||
* @return 0 for success
|
||||
*/
|
||||
int ota_break_point_write(const uint8_t *data, uint32_t data_len);
|
||||
|
||||
/**
|
||||
* @brief Read firmware from file.
|
||||
*
|
||||
* @param[out] buf data to read
|
||||
* @param[in] buf_len data buffer len
|
||||
* @param[in] offset offset of file
|
||||
* @return > 0 for read len, -1 for fail
|
||||
*/
|
||||
int ota_firmware_read(uint8_t *buf, uint32_t buf_len, uint32_t offset);
|
||||
|
||||
/**
|
||||
* @brief Write firmware to file.
|
||||
*
|
||||
* @param[in] data firmware data to write
|
||||
* @param[in] data_len data length
|
||||
* @param[in] offset offset of file
|
||||
* @return 0 for success
|
||||
*/
|
||||
int ota_firmware_write(uint8_t *data, uint32_t data_len, uint32_t offset);
|
||||
|
||||
/**
|
||||
* @brief Finish write firmware.
|
||||
*
|
||||
* @param[in] total_len total length of firmware
|
||||
* @return 0 for success
|
||||
*/
|
||||
int ota_firmware_finish(uint32_t total_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // IOT_HUB_DEVICE_C_SDK_APP_OTA_OTA_FIRMWARE_SAVE_H_
|
Reference in New Issue
Block a user