first commit for opensource

first commit for opensource
This commit is contained in:
supowang
2019-09-16 13:19:50 +08:00
parent 08ab013b8e
commit edb2879617
6303 changed files with 5472815 additions and 23 deletions

View File

@@ -0,0 +1,936 @@
/*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright (C) 2016 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.
*
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "qcloud.h"
__QCLOUD_STATIC__ qcloud_err_t shadow_client_construct(qcloud_shadow_client_t *client,
mqtt_event_handler_fn_t handler,
shadow_type_t shadow_type,
qcloud_device_t *device)
{
int topic_len = 0;
client->shadow_type = shadow_type;
client->event_handler.handler = handler;
client->version = 0;
client->token_num = 0;
client->sync_state = QCLOUD_SHADOW_SYNC_STATE_NONE;
client->request_list_counter = 0;
qcloud_list_init(&client->request_list);
qcloud_list_init(&client->property_list);
memset(client->request_topic_subscribe, 0, sizeof(client->request_topic_subscribe));
if (shadow_type == SHADOW_TYPE_TEMPLATE) {
topic_len = osal_snprintf(client->request_topic_subscribe, QCLOUD_MQTT_TOPIC_SIZE_MAX,
"$template/operation/result/%s/%s",
device->product_id, device->device_name);
} else {
topic_len = osal_snprintf(client->request_topic_subscribe, QCLOUD_MQTT_TOPIC_SIZE_MAX,
"$shadow/operation/result/%s/%s",
device->product_id, device->device_name);
}
if (topic_len < 0 || topic_len >= QCLOUD_MQTT_TOPIC_SIZE_MAX) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
memset(client->request_topic_publish, 0, sizeof(client->request_topic_publish));
if (shadow_type == SHADOW_TYPE_TEMPLATE) {
topic_len = osal_snprintf(client->request_topic_publish, QCLOUD_MQTT_TOPIC_SIZE_MAX,
"$template/operation/%s/%s",
device->product_id, device->device_name);
} else {
topic_len = osal_snprintf(client->request_topic_publish, QCLOUD_MQTT_TOPIC_SIZE_MAX,
"$shadow/operation/%s/%s",
device->product_id, device->device_name);
}
if (topic_len < 0 || topic_len >= QCLOUD_MQTT_TOPIC_SIZE_MAX) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
strncpy(client->device_product_id, device->product_id, sizeof(client->device_product_id));
client->device_product_id[QCLOUD_DEVICE_PRODUCT_ID_MAX] = '\0';
client->global_lock = osal_mutex_create();
if (!client->global_lock) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
}
__QCLOUD_STATIC__ void shadow_incoming_msg_handler(void *client, void *context, mqtt_event_t *event)
{
uint16_t packet_id;
mqtt_incoming_msg_t *mqtt_msg;
qcloud_shadow_client_t *shadow_client = NULL;
shadow_client = (qcloud_shadow_client_t *)context;
switch (event->type) {
case MQTT_EVENT_SUBCRIBE_SUCCESS:
packet_id = *(uint16_t *)event->message;
QCLOUD_LOG_D("subscribe success, packet id=%u", (uint32_t)packet_id);
shadow_client->sync_state = QCLOUD_SHADOW_SYNC_STATE_SUCCESS;
break;
case MQTT_EVENT_SUBCRIBE_TIMEOUT:
packet_id = *(uint16_t *)event->message;
QCLOUD_LOG_D("subscribe wait ack timeout, packet id=%u", (uint32_t)packet_id);
shadow_client->sync_state = QCLOUD_SHADOW_SYNC_STATE_TIMEOUT;
break;
case MQTT_EVENT_SUBCRIBE_NACK:
packet_id = *(uint16_t *)event->message;
QCLOUD_LOG_D("subscribe nack, packet id=%u", (uint32_t)packet_id);
shadow_client->sync_state = QCLOUD_SHADOW_SYNC_STATE_NACK;
break;
case MQTT_EVENT_PUBLISH_RECVEIVED:
mqtt_msg = (mqtt_incoming_msg_t *)event->message;
QCLOUD_LOG_D("topic arrived without handler: topic=%.*s, payload=%.*s",
mqtt_msg->topic_len,
mqtt_msg->topic,
mqtt_msg->payload_len,
mqtt_msg->payload);
break;
default:
break;
}
if (shadow_client->event_handler.handler) {
shadow_client->event_handler.handler(shadow_client, shadow_client->event_handler.context, event);
}
}
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_create(qcloud_shadow_client_t *client,
qcloud_device_t *device,
mqtt_event_handler_fn_t handler,
shadow_type_t shadow_type)
{
QCLOUD_FUNC_ENTRY;
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(device, QCLOUD_ERR_INVAL);
qcloud_err_t rc;
memset(client, 0, sizeof(qcloud_shadow_client_t));
rc = qcloud_mqtt_client_create(&client->mqtt_client, device, shadow_incoming_msg_handler, (void *)client, QCLOUD_AUTO_CONN_STATE_ENABLED);
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
rc = qcloud_mqtt_connect_opt_create(&client->mqtt_connect_opt, device, MQTT_VERSION_3_1_1, 240, MQTT_CLEAN_SESSION_STATE_ENABLED);
if (rc != QCLOUD_ERR_SUCCESS) {
goto errout;
}
rc = shadow_client_construct(client, handler, shadow_type, device);
if (rc != QCLOUD_ERR_SUCCESS) {
goto errout;
}
rc = qcloud_mqtt_client_connect(&client->mqtt_client, &client->mqtt_connect_opt);
if (rc != QCLOUD_ERR_SUCCESS) {
goto errout;
}
rc = shadow_glue_operation_request_subscribe(client);
if (rc != QCLOUD_ERR_SUCCESS) {
goto errout;
}
client->sync_state = QCLOUD_SHADOW_SYNC_STATE_PENDACK;
while (client->sync_state == QCLOUD_SHADOW_SYNC_STATE_PENDACK) {
qcloud_shadow_client_yield(client, 100);
}
if (client->sync_state != QCLOUD_SHADOW_SYNC_STATE_SUCCESS) {
goto errout;
}
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
errout:
qcloud_mqtt_client_destroy(&client->mqtt_client);
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_yield(qcloud_shadow_client_t *client, uint32_t timeout)
{
QCLOUD_FUNC_ENTRY;
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_NUMBERIC_SANITY_CHECK(timeout, QCLOUD_ERR_INVAL);
shadow_glue_request_list_scan(client);
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_client_yield(&client->mqtt_client, &client->mqtt_connect_opt, timeout));
}
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_publish(qcloud_shadow_client_t *client, char *topic, mqtt_publish_opt_t *publish_opt)
{
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(topic, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(publish_opt, QCLOUD_ERR_INVAL);
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_publish(&client->mqtt_client, topic, publish_opt));
}
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_subscribe(qcloud_shadow_client_t *client, const char *topic_filter, mqtt_subscribe_opt_t *subscribe_opt)
{
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(subscribe_opt, QCLOUD_ERR_INVAL);
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_subscribe(&client->mqtt_client, topic_filter, subscribe_opt));
}
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_unsubscribe(qcloud_shadow_client_t *client, const char *topic_filter)
{
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(topic_filter, QCLOUD_ERR_INVAL);
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_unsubscribe(&client->mqtt_client, topic_filter));
}
__QCLOUD_API__ int qcloud_shadow_client_is_connected(qcloud_shadow_client_t *client)
{
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
return qcloud_mqtt_client_is_connected(&client->mqtt_client);
}
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_destroy(qcloud_shadow_client_t *client)
{
QCLOUD_FUNC_ENTRY;
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
shadow_glue_operation_request_unsubscribe(client);
shadow_glue_property_list_destroy(client);
shadow_glue_request_list_destroy(client);
qcloud_mqtt_client_destroy(&client->mqtt_client);
if (client->global_lock) {
osal_mutex_destroy(client->global_lock);
}
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS)
}
__QCLOUD_STATIC__ int shadow_device_property_is_exist(qcloud_shadow_client_t *client, shadow_dev_property_t *that_dev_property)
{
QCLOUD_FUNC_ENTRY;
qcloud_list_t *curr, *next;
qcloud_shadow_prop_info_t *property_info = NULL;
shadow_dev_property_t *this_dev_property = NULL;
if (qcloud_list_empty(&client->property_list)) {
return QCLOUD_FALSE;
}
osal_mutex_lock(client->global_lock);
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->property_list) {
property_info = QCLOUD_LIST_ENTRY(curr, qcloud_shadow_prop_info_t, list);
this_dev_property = property_info->dev_property;
if (strcmp(this_dev_property->key, that_dev_property->key) != 0 ||
this_dev_property->type != that_dev_property->type) {
continue;
}
osal_mutex_unlock(client->global_lock);
return QCLOUD_TRUE;
}
osal_mutex_unlock(client->global_lock);
return QCLOUD_FALSE;
}
__QCLOUD_STATIC__ qcloud_err_t shadow_device_property_do_register(qcloud_shadow_client_t *client,
shadow_dev_property_t *dev_property,
shadow_property_delta_handler_fn_t handler)
{
qcloud_shadow_prop_info_t *property_info = NULL;
property_info = (qcloud_shadow_prop_info_t *)osal_malloc(sizeof(qcloud_shadow_prop_info_t));
if (!property_info) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
}
qcloud_list_init(&property_info->list);
property_info->dev_property = dev_property;
property_info->handler = handler;
qcloud_list_add(&property_info->list, &client->property_list);
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
}
__QCLOUD_API__ qcloud_err_t qcloud_shadow_device_property_register(qcloud_shadow_client_t *client,
shadow_dev_property_t *dev_property,
shadow_property_delta_handler_fn_t handler)
{
QCLOUD_FUNC_ENTRY;
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(dev_property, QCLOUD_ERR_INVAL);
if (!qcloud_mqtt_client_is_connected(&client->mqtt_client)) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
if (shadow_device_property_is_exist(client, dev_property)) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SHADOW_PROPERTY_EXIST);
}
QCLOUD_FUNC_EXIT_RC(shadow_device_property_do_register(client, dev_property, handler));
}
__QCLOUD_STATIC__ qcloud_err_t shadow_device_property_do_unregister(qcloud_shadow_client_t *client, shadow_dev_property_t *that_dev_property)
{
QCLOUD_FUNC_ENTRY;
qcloud_list_t *curr, *next;
qcloud_shadow_prop_info_t *property_info = NULL;
shadow_dev_property_t *this_dev_property = NULL;
if (qcloud_list_empty(&client->property_list)) {
return QCLOUD_ERR_FAILURE;
}
osal_mutex_lock(client->global_lock);
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->property_list) {
property_info = QCLOUD_LIST_ENTRY(curr, qcloud_shadow_prop_info_t, list);
this_dev_property = property_info->dev_property;
if (strcmp(this_dev_property->key, that_dev_property->key) != 0 ||
this_dev_property->type != that_dev_property->type) {
continue;
}
qcloud_list_del(&property_info->list);
osal_free(property_info);
osal_mutex_unlock(client->global_lock);
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
}
osal_mutex_unlock(client->global_lock);
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
__QCLOUD_API__ qcloud_err_t qcloud_shadow_device_property_unregister(qcloud_shadow_client_t *client, shadow_dev_property_t *dev_property)
{
QCLOUD_FUNC_ENTRY;
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(dev_property, QCLOUD_ERR_INVAL);
if (!qcloud_mqtt_client_is_connected(&client->mqtt_client)) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
if (!shadow_device_property_is_exist(client, dev_property)) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SHADOW_NOT_PROPERTY_EXIST);
}
QCLOUD_FUNC_EXIT_RC(shadow_device_property_do_unregister(client, dev_property));
}
__QCLOUD_STATIC__ void shadow_req_state_update_handler(void *client, qcloud_shadow_req_method_t method, qcloud_shadow_req_state_t req_state, const char *json_doc, void *context)
{
QCLOUD_LOG_D("request state=%d", req_state);
if (json_doc) {
QCLOUD_LOG_D("json doc=%s", json_doc);
} else {
QCLOUD_LOG_D("json doc NULL");
}
*((qcloud_shadow_req_state_t *)context) = req_state;
}
__QCLOUD_STATIC__ qcloud_err_t shadow_request_state2errno(qcloud_shadow_req_state_t state)
{
switch (state) {
case QCLOUD_SHADOW_REQUEST_STATE_ACCEPTED:
return QCLOUD_ERR_SUCCESS;
case QCLOUD_SHADOW_REQUEST_STATE_TIMEOUT:
return QCLOUD_ERR_SHADOW_UPDATE_TIMEOUT;
case QCLOUD_SHADOW_REQUEST_STATE_REJECTED:
return QCLOUD_ERR_SHADOW_UPDATE_REJECTED;
}
return QCLOUD_ERR_INVAL;
}
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_update_async(qcloud_shadow_client_t *client,
char *json_doc,
size_t json_doc_size,
shadow_requset_handler_fn_t handler,
void *context,
uint32_t timeout)
{
QCLOUD_FUNC_ENTRY;
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
QCLOUD_NUMBERIC_SANITY_CHECK(timeout, QCLOUD_ERR_INVAL);
qcloud_shadow_req_opt_t request_opt;
if (!qcloud_mqtt_client_is_connected(&client->mqtt_client)) {
QCLOUD_LOG_E("mqtt disconnected");
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
// 如果没有之前没有订阅$shadow/operation/result成功再一次订阅
if (client->sync_state != QCLOUD_SHADOW_SYNC_STATE_SUCCESS) {
shadow_glue_operation_request_subscribe(client);
}
QCLOUD_LOG_D("update request docment: %s", json_doc);
request_opt.method = QCLOUD_SHADOW_REQUEST_METHOD_UPDATE;
request_opt.handler = handler;
request_opt.context = context;
request_opt.timeout = timeout; // in seconds
QCLOUD_FUNC_EXIT_RC(shadow_glue_request_post(client, &request_opt, json_doc, json_doc_size));
}
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_update_sync(qcloud_shadow_client_t *client, char *json_doc, size_t json_doc_size, uint32_t timeout)
{
QCLOUD_FUNC_ENTRY;
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
QCLOUD_NUMBERIC_SANITY_CHECK(timeout, QCLOUD_ERR_INVAL);
qcloud_err_t rc = QCLOUD_ERR_SUCCESS;
qcloud_shadow_req_state_t req_state = QCLOUD_SHADOW_REQUEST_STATE_NONE;
if (!qcloud_mqtt_client_is_connected(&client->mqtt_client)) {
QCLOUD_LOG_E("shadow disconnected");
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
rc = qcloud_shadow_client_update_async(client, json_doc, json_doc_size, shadow_req_state_update_handler, &req_state, timeout);
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
while (req_state == QCLOUD_SHADOW_REQUEST_STATE_NONE) {
qcloud_shadow_client_yield(client, 200);
}
return shadow_request_state2errno(req_state);
}
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_get_async(qcloud_shadow_client_t *client,
shadow_requset_handler_fn_t handler,
void *context,
uint32_t timeout)
{
QCLOUD_FUNC_ENTRY;
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(handler, QCLOUD_ERR_INVAL);
QCLOUD_NUMBERIC_SANITY_CHECK(timeout, QCLOUD_ERR_INVAL);
qcloud_err_t rc;
qcloud_shadow_req_opt_t request_opt;
char request_json_buffer[QCLOUD_SHADOW_JSON_WITH_CLIENT_TOKEN_MAX];
if (!qcloud_mqtt_client_is_connected(&client->mqtt_client)) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
// 如果没有之前没有订阅$shadow/operation/result成功再一次订阅
if (client->sync_state != QCLOUD_SHADOW_SYNC_STATE_SUCCESS) {
shadow_glue_operation_request_subscribe(client);
}
++client->token_num;
rc = shadow_json_empty_doc_build(request_json_buffer, client->token_num, client->device_product_id);
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
QCLOUD_LOG_D("get request document: %s", request_json_buffer);
request_opt.method = QCLOUD_SHADOW_REQUEST_METHOD_GET;
request_opt.handler = handler;
request_opt.context = context;
request_opt.timeout = timeout; // in seconds
QCLOUD_FUNC_EXIT_RC(shadow_glue_request_post(client, &request_opt, request_json_buffer, sizeof(request_json_buffer)));
}
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_get_sync(qcloud_shadow_client_t *client, uint32_t timeout)
{
QCLOUD_FUNC_ENTRY;
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_NUMBERIC_SANITY_CHECK(timeout, QCLOUD_ERR_INVAL);
qcloud_err_t rc = QCLOUD_ERR_SUCCESS;
qcloud_shadow_req_state_t req_state = QCLOUD_SHADOW_REQUEST_STATE_NONE;
if (!qcloud_mqtt_client_is_connected(&client->mqtt_client)) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN);
}
rc = qcloud_shadow_client_get_async(client, shadow_req_state_update_handler, &req_state, timeout);
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, QCLOUD_ERR_FAILURE);
while (req_state == QCLOUD_SHADOW_REQUEST_STATE_NONE) {
qcloud_shadow_client_yield(client, 200);
}
QCLOUD_FUNC_EXIT_RC(shadow_request_state2errno(req_state));
}
/**
* @brief 初始化一个JSON文档
*
* 本函数主要是为JSON文档添加state字段, 即 "{\"state\":{", 所以在生成JSON文档时, 请先调用该方法
*
* @param jsonBuffer 为存储JSON文档准备的字符串缓冲区
* @param sizeOfBuffer 缓冲区大小
* @return 返回QCLOUD_ERR_SUCCESS, 表示初始化成功
*/
__QCLOUD_STATIC__ qcloud_err_t shadow_jsondoc_init(qcloud_shadow_client_t *client,
char *json_doc,
size_t json_doc_size,
int is_overwrite)
{
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
int rc = 0;
if (is_overwrite) {
rc = osal_snprintf(json_doc, json_doc_size, "{\"version\":%d, \"overwriteUpdate\":true, \"state\":{", client->version);
} else {
rc = osal_snprintf(json_doc, json_doc_size, "{\"version\":%d, \"state\":{", client->version);
}
QCLOUD_FUNC_EXIT_RC(shadow_json_snprintf_rc2errno(rc, json_doc_size));
}
/**
* @brief 在JSON文档中添加结尾部分的内容, 包括clientToken字段、version字段
*
* @param jsonBuffer 为存储JSON文档准备的字符串缓冲区
* @param sizeOfBuffer 缓冲区大小
* @return 返回QCLOUD_ERR_SUCCESS, 表示成功
*/
__QCLOUD_STATIC__ qcloud_err_t shadow_jsondoc_finalize(qcloud_shadow_client_t *client, char *json_doc, size_t json_doc_size)
{
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
int rc_snprintf;
qcloud_err_t rc;
size_t remain_size = 0;
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
}
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc) - 1, remain_size, "}, \"%s\":\"", SHADOW_FIELD_CLIENT_TOKEN);
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
}
++client->token_num;
rc = shadow_json_client_token_generate(json_doc + strlen(json_doc), remain_size, client->token_num, client->device_product_id);
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
return QCLOUD_ERR_JSON_BUFFER_TOO_SHORT;
}
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"}");
QCLOUD_FUNC_EXIT_RC(shadow_json_snprintf_rc2errno(rc_snprintf, remain_size));
}
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_report_construct(qcloud_shadow_client_t *client,
char *json_doc,
size_t json_doc_size,
int count, ...)
{
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
qcloud_err_t rc;
va_list args;
size_t remain_size = 0;
int i = 0, rc_snprintf = 0;
shadow_dev_property_t *dev_property;
rc = shadow_jsondoc_init(client, json_doc, json_doc_size, QCLOUD_FALSE);
if (rc != QCLOUD_ERR_SUCCESS) {
QCLOUD_LOG_E("json init failed: %d", rc);
QCLOUD_FUNC_EXIT_RC(rc);
}
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
}
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"reported\":{");
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
va_start(args, count);
for (i = 0; i < count; ++i) {
dev_property = va_arg(args, shadow_dev_property_t *);
if (!dev_property || !dev_property->key) {
va_end(args);
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
}
rc = shadow_json_node_add(json_doc, remain_size, dev_property->key, dev_property->data, dev_property->type);
if (rc != QCLOUD_ERR_SUCCESS) {
va_end(args);
QCLOUD_FUNC_EXIT_RC(rc);
}
}
va_end(args);
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
return QCLOUD_ERR_JSON_BUFFER_TOO_SHORT;
}
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc) - 1, remain_size, "},");
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
if (rc != QCLOUD_ERR_SUCCESS) {
QCLOUD_LOG_E("json add report failed: %d", rc);
QCLOUD_FUNC_EXIT_RC(rc);
}
rc = shadow_jsondoc_finalize(client, json_doc, json_doc_size);
if (rc != QCLOUD_ERR_SUCCESS) {
QCLOUD_LOG_E("json finalize failed: %d", rc);
}
QCLOUD_FUNC_EXIT_RC(rc);
}
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_report_construct_array(qcloud_shadow_client_t *client,
char *json_doc,
size_t json_doc_size,
int count,
shadow_dev_property_t *dev_propertys[])
{
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(dev_propertys, QCLOUD_ERR_INVAL);
qcloud_err_t rc;
int8_t i = 0, rc_snprintf = 0;
size_t remain_size = 0;
shadow_dev_property_t *dev_property;
rc = shadow_jsondoc_init(client, json_doc, json_doc_size, QCLOUD_FALSE);
if (rc != QCLOUD_ERR_SUCCESS) {
QCLOUD_LOG_E("json init failed: %d", rc);
QCLOUD_FUNC_EXIT_RC(rc);
}
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
}
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"reported\":{");
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
for (i = 0; i < count; ++i) {
dev_property = dev_propertys[i];
if (!dev_property || !dev_property->key) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
}
rc = shadow_json_node_add(json_doc, remain_size, dev_property->key, dev_property->data, dev_property->type);
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
}
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
return QCLOUD_ERR_JSON_BUFFER_TOO_SHORT;
}
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc) - 1, remain_size, "},");
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
if (rc != QCLOUD_ERR_SUCCESS) {
QCLOUD_LOG_E("json add report failed: %d", rc);
return rc;
}
rc = shadow_jsondoc_finalize(client, json_doc, json_doc_size);
if (rc != QCLOUD_ERR_SUCCESS) {
QCLOUD_LOG_E("json finalize failed: %d", rc);
}
return rc;
}
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_overwrite_report_construct(qcloud_shadow_client_t *client,
char *json_doc,
size_t json_doc_size,
int count, ...)
{
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
qcloud_err_t rc;
va_list args;
size_t remain_size = 0;
int rc_snprintf = 0, i = 0;
shadow_dev_property_t *dev_property;
rc = shadow_jsondoc_init(client, json_doc, json_doc_size, QCLOUD_TRUE);
if (rc != QCLOUD_ERR_SUCCESS) {
QCLOUD_LOG_E("json init failed: %d", rc);
QCLOUD_FUNC_EXIT_RC(rc);
}
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
}
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"reported\":{");
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
va_start(args, count);
for (i = 0; i < count; ++i) {
dev_property = va_arg(args, shadow_dev_property_t *);
if (!dev_property || !dev_property->key) {
va_end(args);
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
}
rc = shadow_json_node_add(json_doc, remain_size, dev_property->key, dev_property->data, dev_property->type);
if (rc != QCLOUD_ERR_SUCCESS) {
va_end(args);
QCLOUD_FUNC_EXIT_RC(rc);
}
}
va_end(args);
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
return QCLOUD_ERR_JSON_BUFFER_TOO_SHORT;
}
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc) - 1, remain_size, "},");
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
if (rc != QCLOUD_ERR_SUCCESS) {
QCLOUD_LOG_E("shadow json add report failed: %d", rc);
QCLOUD_FUNC_EXIT_RC(rc);
}
rc = shadow_jsondoc_finalize(client, json_doc, json_doc_size);
if (rc != QCLOUD_ERR_SUCCESS) {
QCLOUD_LOG_E("shadow json finalize failed: %d", rc);
}
QCLOUD_FUNC_EXIT_RC(rc);
}
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_report_with_desire_null_construct(qcloud_shadow_client_t *client,
char *json_doc,
size_t json_doc_size,
int count, ...)
{
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
qcloud_err_t rc;
va_list args;
size_t remain_size = 0;
int rc_snprintf = 0, i = 0;
shadow_dev_property_t *dev_property;
rc = shadow_jsondoc_init(client, json_doc, json_doc_size, QCLOUD_FALSE);
if (rc != QCLOUD_ERR_SUCCESS) {
QCLOUD_LOG_E("json init failed: %d", rc);
QCLOUD_FUNC_EXIT_RC(rc);
}
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
}
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"reported\":{");
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
va_start(args, count);
for (i = 0; i < count; ++i) {
dev_property = va_arg(args, shadow_dev_property_t *);
if (!dev_property || !dev_property->key) {
va_end(args);
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
}
rc = shadow_json_node_add(json_doc, remain_size, dev_property->key, dev_property->data, dev_property->type);
if (rc != QCLOUD_ERR_SUCCESS) {
va_end(args);
QCLOUD_FUNC_EXIT_RC(rc);
}
}
va_end(args);
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
return QCLOUD_ERR_JSON_BUFFER_TOO_SHORT;
}
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc) - 1, remain_size, "},");
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
if (rc != QCLOUD_ERR_SUCCESS) {
QCLOUD_LOG_E("shadow json add report failed: %d", rc);
QCLOUD_FUNC_EXIT_RC(rc);
}
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"desired\": null ");
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
QCLOUD_FUNC_EXIT_RC(shadow_jsondoc_finalize(client, json_doc, json_doc_size));
}
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_desire_null_construct(qcloud_shadow_client_t *client,
char *json_doc,
size_t json_doc_size)
{
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
qcloud_err_t rc;
size_t remain_size = 0;
int rc_snprintf = 0;
rc = shadow_jsondoc_init(client, json_doc, json_doc_size, QCLOUD_FALSE);
if (rc != QCLOUD_ERR_SUCCESS) {
QCLOUD_LOG_E("json init failed: %d", rc);
QCLOUD_FUNC_EXIT_RC(rc);
}
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
}
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"desired\": null ");
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
QCLOUD_FUNC_EXIT_RC(shadow_jsondoc_finalize(client, json_doc, json_doc_size));
}
__QCLOUD_API__ qcloud_err_t qcloud_shadow_client_desire_construct(qcloud_shadow_client_t *client,
char *json_doc,
size_t json_doc_size,
int count, ...)
{
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
qcloud_err_t rc;
va_list args;
int i = 0, rc_snprintf = 0;
size_t remain_size = 0;
shadow_dev_property_t *dev_property;
rc = shadow_jsondoc_init(client, json_doc, json_doc_size, QCLOUD_FALSE);
if (rc != QCLOUD_ERR_SUCCESS) {
QCLOUD_LOG_E("shadow json init failed: %d", rc);
QCLOUD_FUNC_EXIT_RC(rc);
}
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
}
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"desired\":{");
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
va_start(args, count);
for (i = 0; i < count; ++i) {
dev_property = va_arg(args, shadow_dev_property_t *);
if (!dev_property || !dev_property->key) {
va_end(args);
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
}
rc = shadow_json_node_add(json_doc, remain_size, dev_property->key, dev_property->data, dev_property->type);
if (rc != QCLOUD_ERR_SUCCESS) {
va_end(args);
QCLOUD_FUNC_EXIT_RC(rc);
}
}
va_end(args);
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
}
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc) - 1, remain_size, "},");
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
if (rc != QCLOUD_ERR_SUCCESS) {
QCLOUD_LOG_E("json add desired failed: %d", rc);
QCLOUD_FUNC_EXIT_RC(rc);
}
QCLOUD_FUNC_EXIT_RC(shadow_jsondoc_finalize(client, json_doc, json_doc_size));
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,466 @@
/*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright (C) 2016 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.
*
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "qcloud.h"
static char incoming_msg_from_cloud[QCLOUD_SHADOW_JSON_BUF_MAX];
__QCLOUD_STATIC__ void shadow_glue_json_node_insert(char *json_doc, char *json_node, int pos)
{
int i, n;
int len = strlen(json_doc);
int nlen = strlen(json_node);
for (i = len - 1; i >= pos; --i) {
*(json_doc + i + nlen) = *(json_doc + i);
}
for (n = 0; n < nlen; n++) {
*(json_doc + pos + n) = *json_node++;
}
*(json_doc + len + nlen) = 0;
}
/**
* @brief 根据RequestParams、Method来给json填入type字段的值
*/
__QCLOUD_STATIC__ qcloud_err_t shadow_glue_json_request_method_set(char *json_doc, size_t json_doc_size, qcloud_shadow_req_method_t requst_method)
{
QCLOUD_FUNC_ENTRY;
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
char *type = NULL;
char json_node[64] = {0};
size_t json_len, size_remaining, json_node_len;
switch (requst_method) {
case QCLOUD_SHADOW_REQUEST_METHOD_GET:
type = SHADOW_OPERATION_GET;
break;
case QCLOUD_SHADOW_REQUEST_METHOD_UPDATE:
type = SHADOW_OPERATION_UPDATE;
break;
}
json_len = strlen(json_doc);
size_remaining = json_doc_size - json_len;
osal_snprintf(json_node, 64, "\"type\":\"%s\", ", type);
json_node_len = strlen(json_node);
if (json_node_len >= size_remaining - 1) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
}
shadow_glue_json_node_insert(json_doc, json_node, 1);
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
}
__QCLOUD_STATIC__ void shadow_glue_request_destroy(qcloud_shadow_request_t *request)
{
qcloud_list_del(&request->list);
osal_free(request);
}
/**
* @brief 执行设备影子操作的回调函数
*/
__QCLOUD_STATIC__ void shadow_glue_operation_request_do_handle(qcloud_shadow_client_t *client, const char *client_token, const char *method_type)
{
QCLOUD_FUNC_ENTRY;
QCLOUD_POINTER_SANITY_CHECK_RTN(client);
QCLOUD_POINTER_SANITY_CHECK_RTN(client_token);
QCLOUD_POINTER_SANITY_CHECK_RTN(method_type);
char *delta = NULL;
int16_t result_code;
qcloud_list_t *curr, *next;
qcloud_shadow_request_t *request = NULL;
qcloud_shadow_req_state_t req_state = QCLOUD_SHADOW_REQUEST_STATE_NONE;
if (qcloud_list_empty(&client->request_list)) {
return;
}
osal_mutex_lock(client->global_lock);
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->request_list) {
request = QCLOUD_LIST_ENTRY(curr, qcloud_shadow_request_t, list);
if (strcmp(request->client_token, client_token) != 0) {
continue;
}
// ͨ<><CDA8> payload <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> result <20><>ȷ<EFBFBD><C8B7><EFBFBD><EFBFBD>Ӧ<EFBFBD>IJ<EFBFBD><C4B2><EFBFBD><EFBFBD>Ƿ<EFBFBD><C7B7>ɹ<EFBFBD>
// <20><>result = 0ʱ<30><CAB1>payload<61><64>Ϊ<EFBFBD>գ<EFBFBD>result<6C><74><30><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>updateʧ<65><CAA7>
if (!shadow_json_operation_result_code_parse(incoming_msg_from_cloud, &result_code)) {
QCLOUD_LOG_E("parse result code failed.");
shadow_glue_request_destroy(request);
continue;
}
req_state = (result_code == 0 ? QCLOUD_SHADOW_REQUEST_STATE_ACCEPTED : QCLOUD_SHADOW_REQUEST_STATE_REJECTED);
if ((strcmp(method_type, SHADOW_OPERATION_GET) == 0 && req_state == QCLOUD_SHADOW_REQUEST_STATE_ACCEPTED) ||
(strcmp(method_type, SHADOW_OPERATION_UPDATE) == 0 && req_state == QCLOUD_SHADOW_REQUEST_STATE_REJECTED)) {
if (shadow_json_operation_delta_get(incoming_msg_from_cloud, &delta)) {
shadow_glue_delta_handle(client, delta);
osal_free(delta);
}
}
if (request->handler) {
request->handler(client, request->method, req_state, incoming_msg_from_cloud, request->context);
}
shadow_glue_request_destroy(request);
--client->request_list_counter;
}
osal_mutex_unlock(client->global_lock);
QCLOUD_FUNC_EXIT;
}
/**
* @brief 文档操作请求结果的回调函数
* 客户端先订阅 $shadow/operation/result/{ProductId}/{DeviceName}, 收到该topic的消息则会调用该回调函数
* 在这个回调函数中, 解析出各个设备影子文档操作的结果
*/
__QCLOUD_STATIC__ void shadow_glue_operation_result_handler(void *client, mqtt_incoming_msg_t *message, void *private_data)
{
QCLOUD_FUNC_ENTRY;
QCLOUD_POINTER_SANITY_CHECK_RTN(client);
QCLOUD_POINTER_SANITY_CHECK_RTN(message);
int cloud_rcv_len;
uint32_t version = 0;
char *client_token = NULL, *method_type = NULL, *delta = NULL;
qcloud_mqtt_client_t *mqtt_client = NULL;
qcloud_shadow_client_t *shadow_client = NULL;
mqtt_client = (qcloud_mqtt_client_t *)client;
shadow_client = (qcloud_shadow_client_t*)mqtt_client->event_handler.context;
if (!message->topic || message->topic_len <= 0) {
return;
}
if (message->payload_len > QCLOUD_SHADOW_JSON_BUF_MAX) {
QCLOUD_LOG_E("received len exceeds limit");
goto out;
}
cloud_rcv_len = QCLOUD_MIN(QCLOUD_SHADOW_JSON_BUF_MAX - 1, message->payload_len);
memcpy(incoming_msg_from_cloud, message->payload, cloud_rcv_len + 1);
incoming_msg_from_cloud[cloud_rcv_len] = '\0'; // json_parse relies on a string
// 解析shadow result topic消息类型
if (!shadow_json_operation_type_parse(incoming_msg_from_cloud, &method_type)) {
QCLOUD_LOG_E("fail to parse type!");
goto out;
}
// 非delta消息的push一定由设备端触发找到设备段对应的client_token
if (strcmp(method_type, SHADOW_OPERATION_DELTA) != 0 &&
!shadow_json_client_token_parse(incoming_msg_from_cloud, &client_token)) {
QCLOUD_LOG_E("fail to parse client token! json=%s", incoming_msg_from_cloud);
goto out;
}
// 获取shadow push消息version如果比本地的version则修改本地version比本地可能是由于服务器回滚或出错
if (shadow_json_version_parse(incoming_msg_from_cloud, &version) &&
version > shadow_client->version) {
shadow_client->version = version;
}
if (strcmp(method_type, SHADOW_OPERATION_DELTA) == 0) {
if (shadow_json_delta_parse(incoming_msg_from_cloud, &delta)) {
QCLOUD_LOG_D("delta: %s", delta);
shadow_glue_delta_handle(shadow_client, delta);
}
goto out;
}
if (shadow_client) {
shadow_glue_operation_request_do_handle(shadow_client, client_token, method_type);
}
out:
if (!method_type) {
osal_free(method_type);
}
if (!client_token) {
osal_free(client_token);
}
if (!delta) {
osal_free(delta);
}
}
/**
* @brief 发布文档请求到物联云
*
* @param client Qcloud_IoT_Client对象
* @param method 文档操作方式
* @param pJsonDoc 等待发送的文档
* @return 返回QCLOUD_ERR_SUCCESS, 表示发布文档请求成功
*/
__QCLOUD_STATIC__ qcloud_err_t shadow_glue_operation_request_publish(qcloud_shadow_client_t *client,
char *json_doc)
{
QCLOUD_FUNC_ENTRY;
mqtt_publish_opt_t publish_opt;
memset(&publish_opt, 0, sizeof(mqtt_publish_opt_t));
publish_opt.qos = MQTT_QOS0;
publish_opt.payload = (void *)json_doc;
publish_opt.payload_len = strlen(json_doc);
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_client_publish(&client->mqtt_client, client->request_topic_publish, &publish_opt));
}
__QCLOUD_INTERNAL__ qcloud_err_t shadow_glue_operation_request_subscribe(qcloud_shadow_client_t *client)
{
mqtt_subscribe_opt_t subscribe_opt;
subscribe_opt.message_handler = shadow_glue_operation_result_handler;
subscribe_opt.private_data = NULL;
subscribe_opt.qos = MQTT_QOS0;
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_client_subscribe(&client->mqtt_client, client->request_topic_subscribe, &subscribe_opt));
}
/**
* @brief 取消订阅topic: $shadow/operation/result/{ProductId}/{DeviceName}
*/
__QCLOUD_INTERNAL__ qcloud_err_t shadow_glue_operation_request_unsubscribe(qcloud_shadow_client_t *client)
{
QCLOUD_FUNC_ENTRY;
QCLOUD_FUNC_EXIT_RC(qcloud_mqtt_client_unsubscribe(&client->mqtt_client, client->request_topic_subscribe));
}
__QCLOUD_INTERNAL__ qcloud_err_t shadow_glue_request_list_scan(qcloud_shadow_client_t *client)
{
QCLOUD_FUNC_ENTRY;
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
qcloud_list_t *curr, *next;
qcloud_shadow_request_t *request;
if (qcloud_list_empty(&client->request_list)) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
}
osal_mutex_lock(client->global_lock);
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->request_list) {
request = QCLOUD_LIST_ENTRY(curr, qcloud_shadow_request_t, list);
// check whether the ack is timeout
if (!osal_timer_is_expired(&request->timer)) {
continue;
}
if (request->handler) {
request->handler(client, request->method, QCLOUD_SHADOW_REQUEST_STATE_TIMEOUT, incoming_msg_from_cloud, request->context);
}
shadow_glue_request_destroy(request);
--client->request_list_counter;
}
osal_mutex_unlock(client->global_lock);
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
}
/**
* @brief 将设备影子文档的操作请求保存在列表中
*/
__QCLOUD_STATIC__ qcloud_err_t shadow_glue_request_record(qcloud_shadow_client_t *client,
const char *client_token,
qcloud_shadow_req_opt_t *request_opt)
{
QCLOUD_FUNC_ENTRY;
qcloud_shadow_request_t *request = NULL;
if (client->request_list_counter >= QCLOUD_SHADOW_REQUEST_PENDING_MAX) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_MAX_APPENDING_REQUEST);
}
request = (qcloud_shadow_request_t *)osal_malloc(sizeof(qcloud_shadow_request_t));
if (!request) {
QCLOUD_LOG_E("malloc failed!");
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_FAILURE);
}
request->handler = request_opt->handler;
request->context = request_opt->context;
request->method = request_opt->method;
strncpy(request->client_token, client_token, QCLOUD_SHADOW_CLIENT_TOKEN_MAX);
osal_timer_init(&request->timer);
osal_timer_countdown(&request->timer, request_opt->timeout);
qcloud_list_init(&request->list);
osal_mutex_lock(client->global_lock);
qcloud_list_add(&request->list, &client->request_list);
++client->request_list_counter;
osal_mutex_unlock(client->global_lock);
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_SUCCESS);
}
__QCLOUD_INTERNAL__ qcloud_err_t shadow_glue_request_post(qcloud_shadow_client_t *client,
qcloud_shadow_req_opt_t *request_opt,
char *json_doc,
size_t json_doc_size)
{
QCLOUD_FUNC_ENTRY;
QCLOUD_POINTER_SANITY_CHECK(client, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(request_opt, QCLOUD_ERR_INVAL);
QCLOUD_POINTER_SANITY_CHECK(json_doc, QCLOUD_ERR_INVAL);
qcloud_err_t rc = QCLOUD_ERR_SUCCESS;
char *client_token = NULL;
// 解析文档中的clientToken, 如果解析失败, 直接返回错误
if (!shadow_json_client_token_parse(json_doc, &client_token)) {
QCLOUD_LOG_E("fail to parse client token!");
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_INVAL);
}
rc = shadow_glue_json_request_method_set(json_doc, json_doc_size, request_opt->method);
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
// 相应的 operation topic 订阅成功或已经订阅
rc = shadow_glue_operation_request_publish(client, json_doc);
if (rc == QCLOUD_ERR_SUCCESS) {
rc = shadow_glue_request_record(client, client_token, request_opt);
}
osal_free(client_token);
QCLOUD_FUNC_EXIT_RC(rc);
}
/**
* @brief 处理注册属性的回调函数
* 当订阅的$shadow/operation/result/{ProductId}/{DeviceName}返回消息时,
* 若对应的type为delta, 则执行该函数
*
*/
__QCLOUD_INTERNAL__ void shadow_glue_delta_handle(qcloud_shadow_client_t *client, char *delta)
{
QCLOUD_FUNC_ENTRY;
qcloud_list_t *curr, *next;
qcloud_shadow_prop_info_t *property_info = NULL;
shadow_dev_property_t *dev_property = NULL;
if (qcloud_list_empty(&client->property_list)) {
return;
}
osal_mutex_lock(client->global_lock);
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->property_list) {
property_info = QCLOUD_LIST_ENTRY(curr, qcloud_shadow_prop_info_t, list);
dev_property = property_info->dev_property;
if (shadow_json_value_update(delta, dev_property)) {
property_info->handler(client, delta, strlen(delta), dev_property);
}
}
osal_mutex_unlock(client->global_lock);
QCLOUD_FUNC_EXIT;
}
__QCLOUD_INTERNAL__ void shadow_glue_property_list_destroy(qcloud_shadow_client_t *client)
{
QCLOUD_FUNC_ENTRY;
qcloud_list_t *curr, *next;
qcloud_shadow_prop_info_t *property_info = NULL;
if (qcloud_list_empty(&client->property_list)) {
return;
}
osal_mutex_lock(client->global_lock);
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->property_list) {
property_info = QCLOUD_LIST_ENTRY(curr, qcloud_shadow_prop_info_t, list);
qcloud_list_del(&property_info->list);
osal_free(property_info);
}
osal_mutex_unlock(client->global_lock);
QCLOUD_FUNC_EXIT;
}
__QCLOUD_INTERNAL__ void shadow_glue_request_list_destroy(qcloud_shadow_client_t *client)
{
QCLOUD_FUNC_ENTRY;
qcloud_list_t *curr, *next;
qcloud_shadow_request_t *request = NULL;
if (qcloud_list_empty(&client->request_list)) {
return;
}
osal_mutex_lock(client->global_lock);
QCLOUD_LIST_FOR_EACH_SAFE(curr, next, &client->request_list) {
request = QCLOUD_LIST_ENTRY(curr, qcloud_shadow_request_t, list);
qcloud_list_del(&request->list);
osal_free(request);
}
osal_mutex_unlock(client->global_lock);
QCLOUD_FUNC_EXIT;
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,285 @@
/*
* Tencent is pleased to support the open source community by making IoT Hub available.
* Copyright (C) 2016 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.
*
*/
#ifdef __cplusplus
extern "C" {
#endif
#include "qcloud.h"
#define PRIi32 "i"
#define PRIi16 "i"
#define PRIi8 "i"
#define PRIu32 "u"
#define PRIu16 "u"
#define PRIu8 "u"
#define SCNi8 "hhi"
#define SCNu8 "hhu"
#define SCNi16 "hi"
#define SCNu16 "hu"
#define SCNi32 "i"
#define SCNu32 "u"
__QCLOUD_STATIC__ qcloud_err_t shadow_json_value_do_update(char *value, shadow_dev_property_t *dev_property)
{
switch (dev_property->type) {
case JSON_DATA_TYPE_BOOL:
return LITE_get_boolean(dev_property->data, value);
case JSON_DATA_TYPE_INT32:
return LITE_get_int32(dev_property->data, value);
case JSON_DATA_TYPE_INT16:
return LITE_get_int16(dev_property->data, value);
case JSON_DATA_TYPE_INT8:
return LITE_get_int8(dev_property->data, value);
case JSON_DATA_TYPE_UINT32:
return LITE_get_uint32(dev_property->data, value);
case JSON_DATA_TYPE_UINT16:
return LITE_get_uint16(dev_property->data, value);
case JSON_DATA_TYPE_UINT8:
return LITE_get_uint8(dev_property->data, value);
case JSON_DATA_TYPE_FLOAT:
return LITE_get_float(dev_property->data, value);
case JSON_DATA_TYPE_DOUBLE:
return LITE_get_double(dev_property->data, value);
case JSON_DATA_TYPE_STRING:
case JSON_DATA_TYPE_OBJECT:
QCLOUD_LOG_D("string/object to be deal, %d %s", dev_property->type, value);
return QCLOUD_ERR_SUCCESS;
default:
QCLOUD_LOG_E("unknow type, %d",dev_property->type);
return QCLOUD_ERR_FAILURE;
}
}
/**
* @brief 检查函数snprintf的返回值
*
* @param returnCode 函数snprintf的返回值
* @param maxSizeOfWrite 可写最大字节数
* @return 返回QCLOUD_ERR_JSON, 表示出错; 返回QCLOUD_ERR_JSON_BUFFER_TRUNCATED, 表示截断
*/
__QCLOUD_INTERNAL__ qcloud_err_t shadow_json_snprintf_rc2errno(int rc, size_t write_max)
{
if (rc < 0) {
return QCLOUD_ERR_JSON;
}
if (rc >= write_max) {
return QCLOUD_ERR_JSON_BUFFER_TRUNCATED;
}
return QCLOUD_ERR_SUCCESS;
}
__QCLOUD_INTERNAL__ qcloud_err_t shadow_json_client_token_generate(char *json_doc, size_t json_doc_size, uint32_t token_num, char *device_product_id)
{
int rc_snprintf;
rc_snprintf = osal_snprintf(json_doc, json_doc_size, "%s-%u", device_product_id, token_num);
return shadow_json_snprintf_rc2errno(rc_snprintf, json_doc_size);
}
__QCLOUD_INTERNAL__ qcloud_err_t shadow_json_node_add(char *json_doc, size_t json_doc_size, const char *key, void *data, json_data_type_t type)
{
qcloud_err_t rc;
int32_t rc_snprintf = 0;
size_t remain_size = 0;
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
return QCLOUD_ERR_JSON_BUFFER_TOO_SHORT;
}
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"%s\":", key);
rc = shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
QCLOUD_FUNC_EXIT_RC_IF_NOT(rc, QCLOUD_ERR_SUCCESS, rc);
if ((remain_size = json_doc_size - strlen(json_doc)) <= 1) {
QCLOUD_FUNC_EXIT_RC(QCLOUD_ERR_JSON_BUFFER_TOO_SHORT);
}
if (!data) {
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "null,");
QCLOUD_FUNC_EXIT_RC(shadow_json_snprintf_rc2errno(rc_snprintf, remain_size));
}
switch (type) {
case JSON_DATA_TYPE_INT32:
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%"
PRIi32
",", *(int32_t *)(data));
break;
case JSON_DATA_TYPE_INT16:
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%"
PRIi16
",", *(int16_t *)(data));
break;
case JSON_DATA_TYPE_INT8:
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%"
PRIi8
",", *(int8_t *)(data));
break;
case JSON_DATA_TYPE_UINT32:
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%"
PRIu32
",", *(uint32_t *)(data));
break;
case JSON_DATA_TYPE_UINT16:
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%"
PRIu16
",", *(uint16_t *)(data));
break;
case JSON_DATA_TYPE_UINT8:
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%"
PRIu8
",", *(uint8_t *)(data));
break;
case JSON_DATA_TYPE_DOUBLE:
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%f,", *(double *)(data));
break;
case JSON_DATA_TYPE_FLOAT:
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%f,", *(float *)(data));
break;
case JSON_DATA_TYPE_BOOL:
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%s,", *(bool *)(data) ? "true" : "false");
break;
case JSON_DATA_TYPE_STRING:
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "\"%s\",", (char *)(data));
break;
case JSON_DATA_TYPE_OBJECT:
rc_snprintf = osal_snprintf(json_doc + strlen(json_doc), remain_size, "%s,", (char *)(data));
break;
}
return shadow_json_snprintf_rc2errno(rc_snprintf, remain_size);
}
__QCLOUD_INTERNAL__ qcloud_err_t shadow_json_empty_doc_build(char *json_doc, uint32_t token_num, char *device_product_id)
{
int rc_snprintf;
rc_snprintf = osal_snprintf(json_doc, QCLOUD_SHADOW_JSON_WITH_CLIENT_TOKEN_MAX, "{\"clientToken\":\"%s-%u\"}", device_product_id, token_num);
return shadow_json_snprintf_rc2errno(rc_snprintf, QCLOUD_SHADOW_JSON_WITH_CLIENT_TOKEN_MAX);
}
__QCLOUD_INTERNAL__ int shadow_json_client_token_parse(char *json_doc, char **client_token)
{
*client_token = LITE_json_value_of(SHADOW_FIELD_CLIENT_TOKEN, json_doc);
return *client_token != NULL;
}
__QCLOUD_INTERNAL__ int shadow_json_version_parse(char *json_doc, uint32_t *version)
{
int rc = QCLOUD_TRUE;
char *version_str;
version_str = LITE_json_value_of(SHADOW_PAYLOAD_VERSION, json_doc);
if (!version_str) {
return QCLOUD_FALSE;
}
if (sscanf(version_str, "%" SCNu32, version) != 1) {
QCLOUD_LOG_E("parse shadow version failed, rc: %d", QCLOUD_ERR_JSON_PARSE);
rc = QCLOUD_FALSE;
}
osal_free(version_str);
return rc;
}
__QCLOUD_INTERNAL__ int shadow_json_operation_type_parse(char *json_doc, char **type)
{
*type = LITE_json_value_of(SHADOW_FIELD_TYPE, json_doc);
return *type != NULL;
}
__QCLOUD_INTERNAL__ int shadow_json_operation_result_code_parse(char *json_doc, int16_t *result_code)
{
int rc = QCLOUD_TRUE;
char *result_code_str;
result_code_str = LITE_json_value_of(SHADOW_FIELD_RESULT, json_doc);
if (!result_code_str) {
return QCLOUD_FALSE;
}
if (sscanf(result_code_str, "%" SCNi16, result_code) != 1) {
QCLOUD_LOG_E("parse result code failed, %d", QCLOUD_ERR_JSON_PARSE);
rc = QCLOUD_FALSE;
}
osal_free(result_code_str);
return rc;
}
__QCLOUD_INTERNAL__ int shadow_json_delta_parse(char *json_doc, char **delta)
{
*delta = LITE_json_value_of(SHADOW_PAYLOAD_STATE, json_doc);
return *delta != NULL;
}
__QCLOUD_INTERNAL__ int shadow_json_operation_delta_get(char *json_doc, char **delta)
{
*delta = LITE_json_value_of(SHADOW_PAYLOAD_STATE_DELTA, json_doc);
return *delta != NULL;
}
__QCLOUD_INTERNAL__ int shadow_state_parse(char *json_doc, char **state)
{
*state = LITE_json_value_of(SHADOW_PAYLOAD_VERSION, json_doc);
return *state != NULL;
}
__QCLOUD_INTERNAL__ int shadow_json_value_update(char *json, shadow_dev_property_t *dev_property)
{
char *data;
data = LITE_json_value_of((char *)dev_property->key, json);
if (!data || strncmp(data, "null", 4) == 0 || strncmp(data, "NULL", 4) == 0) {
return QCLOUD_FALSE;
}
shadow_json_value_do_update(data, dev_property);
osal_free(data);
return QCLOUD_TRUE;
}
#ifdef __cplusplus
}
#endif