diff --git a/.gitignore b/.gitignore index 0e20f8f..1ba9cb0 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,5 @@ sdk-tests/certs/unittest_private.key sdk-tests/certs/unittest_private.key .vscode/settings.json +.vs/ +/mplayer \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 773613d..145db97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,8 +12,8 @@ set(BUILD_TYPE "release") set(EXTRACT_SRC OFF) # 编译工具链 -set(COMPILE_TOOLS "gcc") -set(PLATFORM "linux") + set(COMPILE_TOOLS "gcc") + set(PLATFORM "linux") #set(COMPILE_TOOLS "MSVC") #set(PLATFORM "windows") diff --git a/Makefile b/Makefile index 97e5e51..39155ea 100644 --- a/Makefile +++ b/Makefile @@ -77,6 +77,7 @@ $(call CompLib_Map, OTA_COMM_ENABLED, \ $(call CompLib_Map, RESOURCE_UPDATE_ENABLED, \ $(SRC_DIR)/services/resource \ $(SRC_DIR)/services/service_com \ + $(SRC_DIR)/services/device_bind \ ) $(call CompLib_Map, FILE_MANAGE_ENABLED, \ diff --git a/device_info.json b/device_info.json index 7c29768..1cbb5bf 100644 --- a/device_info.json +++ b/device_info.json @@ -11,31 +11,19 @@ "devPrivateKeyFile": "YOUR_DEVICE_PRIVATE_KEY_FILE_NAME" }, "subDev": { - "subdev_num": 6, + "subdev_num": 3, "subdev_list": [ { - "sub_productId": "WPDA0S6S08", - "sub_devName": "dev001" + "sub_productId": "VQ2AWWC1R6", + "sub_devName": "subDev001" }, { - "sub_productId": "WPDA0S6S08", - "sub_devName": "dev002" + "sub_productId": "VQ2AWWC1R6", + "sub_devName": "subDev002" }, { - "sub_productId": "WPDA0S6S08", - "sub_devName": "dev003" - }, - { - "sub_productId": "Y8T6NB8DM0", - "sub_devName": "test001" - }, - { - "sub_productId": "Y8T6NB8DM0", - "sub_devName": "test002" - }, - { - "sub_productId": "Y8T6NB8DM0", - "sub_devName": "test003" + "sub_productId": "VQ2AWWC1R6", + "sub_devName": "subDev003" } ] }, diff --git "a/docs/C-SDK_API\345\217\212\345\217\257\345\217\230\345\217\202\346\225\260\350\257\264\346\230\216.md" "b/docs/C-SDK_API\345\217\212\345\217\257\345\217\230\345\217\202\346\225\260\350\257\264\346\230\216.md" index 05f0eeb..06166c9 100644 --- "a/docs/C-SDK_API\345\217\212\345\217\257\345\217\230\345\217\202\346\225\260\350\257\264\346\230\216.md" +++ "b/docs/C-SDK_API\345\217\212\345\217\257\345\217\230\345\217\202\346\225\260\350\257\264\346\230\216.md" @@ -149,3 +149,13 @@ SDK对于MQTT接口在多线程环境下的使用有如下注意事项: | 5 | IOT_Gateway_Subscribe | 订阅 MQTT 主题 | | 6 | IOT_Gateway_Unsubscribe | 取消订阅已订阅的 MQTT 主题 | +### 8. 设备绑定/解绑接口 + +微信小程序/平台 删除设备时会触发设备解绑,设备也可以主动发起解绑 + +| 序号 | 函数名 | 说明 | +| ---- | ------------------------- | ----------------------------------------------------------- | +| 1 | IOT_Unbind_Device_ByCloud | 注册回调函数,当微信小程序/平台删除设备时,会调用该回调函数 | +| 2 | IOT_Unbind_Device_Request | 终端设备主动发起设备解绑请求 | +| 3 | | | + diff --git "a/docs/\347\275\221\345\205\263\345\255\220\350\256\276\345\244\207\345\234\272\346\231\257\347\244\272\344\276\213\350\257\264\346\230\216.md" "b/docs/\347\275\221\345\205\263\345\255\220\350\256\276\345\244\207\345\234\272\346\231\257\347\244\272\344\276\213\350\257\264\346\230\216.md" new file mode 100644 index 0000000..5ad5c55 --- /dev/null +++ "b/docs/\347\275\221\345\205\263\345\255\220\350\256\276\345\244\207\345\234\272\346\231\257\347\244\272\344\276\213\350\257\264\346\230\216.md" @@ -0,0 +1,77 @@ +# 网关子设备场景示例说明 +本文档将讲述如何在腾讯物联网开发平台(IoT Explorer)控制台申请网关设备并绑定子设备, 并通过C-SDK中的**gateway_sim_sample**,展示网关设备如何管理子设备**subdev_sim_sample**,并代理子设备的上下行消息。 + +## 一. 创建网关设备和子设备 +这部分操作可参考[网关设备快速入门](./网关设备快速入门.md) + +## 二. 运行示例 +### 1. 编译 +在CMakeLists.txt中把FEATURE_GATEWAY_ENABLED这个参数设置为ON,然后执行./cmake_build.sh,即可在output/release/bin下面生成gateway_sim_sample和subdev_sim_sample。 +### 2. 运行 +(1) 在device_info.json中填入网关的设备信息,包括:productId(对应着控制台的产品ID)、deviceName(对应着控制台的设备名称)、key_deviceinfo->deviceSecret(对应着控制台的设备密钥); +(2) 在控制台运行gateway_sim_sample,日志输出应该如下: +``` +INF|2021-07-06 15:42:58|qcloud_iot_device.c|iot_device_info_set(56): SDK_Ver: 3.1.6, Product_ID: XKVP9QORAR, Device_Name: test001 +DBG|2021-07-06 15:42:58|HAL_TLS_mbedtls.c|HAL_TLS_Connect(224): Setting up the SSL/TLS structure... +DBG|2021-07-06 15:42:58|HAL_TLS_mbedtls.c|HAL_TLS_Connect(266): Performing the SSL/TLS handshake... +DBG|2021-07-06 15:42:58|HAL_TLS_mbedtls.c|HAL_TLS_Connect(267): Connecting to /XKVP9QORAR.iotcloud.tencentdevices.com/8883... +DBG|2021-07-06 15:42:58|HAL_TLS_mbedtls.c|HAL_TLS_Connect(289): connected with /XKVP9QORAR.iotcloud.tencentdevices.com/8883... +INF|2021-07-06 15:42:58|mqtt_client.c|IOT_MQTT_Construct(125): mqtt connect with id: yfSdT success // 网关连接平台成功 +DBG|2021-07-06 15:42:58|mqtt_client_subscribe.c|qcloud_iot_mqtt_subscribe(147): topicName=$gateway/operation/result/XKVP9QORAR/test001|packet_id=4444 // 订阅网关管理topic +DBG|2021-07-06 15:42:58|gateway_api.c|_gateway_event_handler(80): gateway sub|unsub(3) success, packet-id=4444 // 订阅成功 +``` +(3) 在另外一个控制台运行subdev_sim_sample,运行的格式为./output/release/bin/subdev_sim_sample -p QYEWBRB1PS -d dev1,其中-p后接的是子设备的产品ID,-d后是的子设备的设备名称,运行之后,子设备会和gateway_sim_sample建立连接,并通知网关设备子设备上线,然后会循环上报属性。子设备可运行多个,打开多个控制台,每个控制台运行1个,需要指定不同的productId或deviceName。 + +子设备的日志如下: +``` +DBG|2021-07-06 15:49:36|subdev_sim_sample.c|main(164): connect gateway OK, subdev is going to be online // 通知网关设备子设备上线 +DBG|2021-07-06 15:49:37|subdev_sim_sample.c|main(205): Report property {"subdev_type": "report","product_id": "QYEWBRB1PS","device_name": "dev1","msg": "{"method":"report","clientToken":"subdev-1871908611","timestamp":1625557777,"power_switch":0,"color":0,"brightness":0}} // 上报子设备属性到网关设备 +DBG|2021-07-06 15:49:53|subdev_sim_sample.c|main(205): Report property {"subdev_type": "report","product_id": "QYEWBRB1PS","device_name": "dev1","msg": "{"method":"report","clientToken":"subdev-1836984528","timestamp":1625557793,"power_switch":0,"color":0,"brightness":0}} +DBG|2021-07-06 15:49:53|subdev_sim_sample.c|main(192): recv {"method":"report_reply","clientToken":"subdev-1836984528","code":0,"status":"success"} // 接收到平台返回的上报成功 +``` +网关设备的日志如下: +``` +DBG|2021-07-06 15:46:45|gateway_sim_sample.c|_accept_new_subdev(339): Recv conn from 127.0.0.1 29285 // 发现新的子设备 +DBG|2021-07-06 15:46:47|gateway_sim_sample.c|_deal_with_subdev_msg(382): Get msg {"subdev_type":"online","product_id":"QYEWBRB1PS","device_name":"dev1"} from subdev // 子设备上线 +DBG|2021-07-06 15:46:47|gateway_sim_sample.c|_deal_with_subdev_msg(402): itype is 0, pid QYEWBRB1PS dname dev1 +DBG|2021-07-06 15:46:47|gateway_api.c|IOT_Gateway_Subdev_Online(191): there is no session, create a new session // 网关新增子设备管理session +DBG|2021-07-06 15:46:47|mqtt_client_publish.c|qcloud_iot_mqtt_publish(347): publish packetID=0|topicName=$gateway/operation/XKVP9QORAR/test001|payload={"type":"online","payload":{"devices":[{"product_id":"QYEWBRB1PS","device_name":"dev1"}]}} // 通知平台子设备在线 +INF|2021-07-06 15:46:47|gateway_common.c|_gateway_message_handler(387): client_id(QYEWBRB1PS/dev1), online result 0 +DBG|2021-07-06 15:46:47|mqtt_client_subscribe.c|qcloud_iot_mqtt_subscribe(147): topicName=$thing/down/property/QYEWBRB1PS/dev1|packet_id=4445 // 代理子设备订阅属性下行topic +DBG|2021-07-06 15:46:47|gateway_api.c|_gateway_event_handler(80): gateway sub|unsub(3) success, packet-id=4445 +INF|2021-07-06 15:46:47|gateway_sim_sample.c|_event_handler(94): subscribe success, packet-id=4445 // 订阅成功 +DBG|2021-07-06 15:46:48|gateway_sim_sample.c|_deal_with_subdev_msg(421): sub subdev OK +DBG|2021-07-06 15:47:02|gateway_sim_sample.c|_deal_with_subdev_msg(382): Get msg {"subdev_type": "report","product_id": "QYEWBRB1PS","device_name": "dev1","msg": "{"method":"report","clientToken":"subdev-1531564604","timestamp":1625557622,"power_switch":0,"color":0,"brightness":0}} from subdev // 收到子设备的消息 +DBG|2021-07-06 15:47:02|gateway_sim_sample.c|_deal_with_subdev_msg(402): itype is 2, pid QYEWBRB1PS dname dev1 +DBG|2021-07-06 15:47:02|gateway_sim_sample.c|_deal_with_subdev_msg(425): msg {"subdev_type": "report","product_id": "QYEWBRB1PS","device_name": "dev1","msg": "{"method":"report","clientToken":"subdev-1531564604","timestamp":1625557622,"power_switch":0,"color":0,"brightness":0}} +DBG|2021-07-06 15:47:02|gateway_sim_sample.c|_property_topic_publish(246): pid QYEWBRB1PS dname dev1 +DBG|2021-07-06 15:47:02|gateway_sim_sample.c|_property_topic_publish(259): topic $thing/up/property/QYEWBRB1PS/dev1 +DBG|2021-07-06 15:47:02|gateway_sim_sample.c|_property_topic_publish(260): publish msg {"method":"report","clientToken":"subdev-1531564604","timestamp":1625557622,"params":{"power_switch":0,"color":0,"brightness":0}} // 代理子设备上报属性 +DBG|2021-07-06 15:47:02|mqtt_client_publish.c|qcloud_iot_mqtt_publish(347): publish packetID=0|topicName=$thing/up/property/QYEWBRB1PS/dev1|payload={"method":"report","clientToken":"subdev-1531564604","timestamp":1625557622,"params":{"power_switch":0,"color":0,"brightness":0}} +INF|2021-07-06 15:47:03|gateway_sim_sample.c|_message_handler(154): Receive Message With topicName:$thing/down/property/QYEWBRB1PS/dev1, payload:{"method":"report_reply","clientToken":"subdev-1531564604","code":0,"status":"success"} // 接收到平台返回的上报返回消息 +``` +下发控制消息时,网关设备的日志如下: +``` +INF|2021-07-06 15:51:02|gateway_sim_sample.c|_message_handler(154): Receive Message With topicName:$thing/down/property/QYEWBRB1PS/dev1, payload:{"method":"control","clientToken":"clientToken-2fd030d1-7f82-4708-ad5a-8905505ff197","params":{"power_switch":1,"color":0,"brightness":80}} // 收到子设备的下行消息,透传给子设备 +DBG|2021-07-06 15:51:03|gateway_sim_sample.c|_deal_with_subdev_msg(382): Get msg {"method":"control_reply","clientToken":"clientToken-2fd030d1-7f82-4708-ad5a-8905505ff197","code":0} from subdev // 获取子设备的control回复消息 +DBG|2021-07-06 15:51:03|gateway_sim_sample.c|_property_topic_publish(246): pid QYEWBRB1PS dname dev1 +DBG|2021-07-06 15:51:03|gateway_sim_sample.c|_property_topic_publish(259): topic $thing/up/property/QYEWBRB1PS/dev1 +DBG|2021-07-06 15:51:03|gateway_sim_sample.c|_property_topic_publish(260): publish msg {"method":"control_reply","clientToken":"clientToken-2fd030d1-7f82-4708-ad5a-8905505ff197","code":0} +DBG|2021-07-06 15:51:03|mqtt_client_publish.c|qcloud_iot_mqtt_publish(347): publish packetID=0|topicName=$thing/up/property/QYEWBRB1PS/dev1|payload={"method":"control_reply","clientToken":"clientToken-2fd030d1-7f82-4708-ad5a-8905505ff197","code":0} // 代理子设备回复control消息 +``` +子设备的日志如下: +``` +DBG|2021-07-06 15:51:02|subdev_sim_sample.c|main(192): recv {"method":"control","clientToken":"clientToken-2fd030d1-7f82-4708-ad5a-8905505ff197","params":{"power_switch":1,"color":0,"brightness":80}} // 从网关设备透传下来的控制消息 +[ lighting ]|[color: RED ]|[brightness:||||||||||||||||----] // 执行控制操作 +DBG|2021-07-06 15:51:07|subdev_sim_sample.c|main(205): Report property {"subdev_type": "report","product_id": "QYEWBRB1PS","device_name": "dev1","msg": "{"method":"report","clientToken":"subdev-2058913913","timestamp":1625557867,"power_switch":1,"color":0,"brightness":80}} // 上报更新之后的属性 +``` +子设备在运行一段时间后,会离线,离线后网关设备会代理子设备向平台发送offline消息,网关设备的日志如下: +``` +DBG|2021-07-06 16:01:30|gateway_sim_sample.c|_subdev_leave(371): unsubscribe $thing/down/property/QYEWBRB1PS/dev1 return 10391 // 解订阅对应的子设备下行topic +DBG|2021-07-06 16:01:30|mqtt_client_publish.c|qcloud_iot_mqtt_publish(347): publish packetID=0|topicName=$gateway/operation/XKVP9QORAR/test001|payload={"type":"offline","payload":{"devices":[{"product_id":"QYEWBRB1PS","device_name":"dev1"}]}} // 通知子设备离线 +DBG|2021-07-06 16:01:30|gateway_api.c|_gateway_event_handler(80): gateway sub|unsub(6) success, packet-id=10391 // 解订阅成功 +INF|2021-07-06 16:01:30|gateway_sim_sample.c|_event_handler(109): unsubscribe success, packet-id=10391 +``` +## 三. 代码说明 +1. 网关和子设备之间使用了本地TCP socket通信来模拟,在实际的场景下,可以换成BLE通信、ZigBee通信等等; +2. 子设备只演示了Property上行和下行操作,如果要使用Event和Action,可以参考light_data_template_sample.c中的代码添加到网关设备和子设备的程序中,网关设备需要添加代理订阅对应下行topic的代码,子设备则需要添加上下行消息的处理代码。 \ No newline at end of file diff --git a/include/exports/qcloud_iot_export_data_template.h b/include/exports/qcloud_iot_export_data_template.h index 2779d04..e3fce03 100644 --- a/include/exports/qcloud_iot_export_data_template.h +++ b/include/exports/qcloud_iot_export_data_template.h @@ -34,14 +34,15 @@ extern "C" { * @brief Data type of template */ -#define TYPE_TEMPLATE_INT JINT32 -#define TYPE_TEMPLATE_ENUM JINT32 -#define TYPE_TEMPLATE_FLOAT JFLOAT -#define TYPE_TEMPLATE_BOOL JINT8 -#define TYPE_TEMPLATE_STRING JSTRING -#define TYPE_TEMPLATE_TIME JUINT32 -#define TYPE_TEMPLATE_JOBJECT JOBJECT -#define TYPE_TEMPLATE_STRINGENUM JSTRING +#define TYPE_TEMPLATE_INT JINT32 +#define TYPE_TEMPLATE_ENUM JINT32 +#define TYPE_TEMPLATE_FLOAT JFLOAT +#define TYPE_TEMPLATE_BOOL JINT8 +#define TYPE_TEMPLATE_STRING JSTRING +#define TYPE_TEMPLATE_TIME JUINT32 +#define TYPE_TEMPLATE_JOBJECT JOBJECT +#define TYPE_TEMPLATE_STRINGENUM JSTRING +#define TYPE_TEMPLATE_ARRAY JARRAY typedef int32_t TYPE_DEF_TEMPLATE_INT; typedef int32_t TYPE_DEF_TEMPLATE_ENUM; @@ -52,7 +53,6 @@ typedef uint32_t TYPE_DEF_TEMPLATE_TIME; typedef void * TYPE_DEF_TEMPLATE_OBJECT; typedef char TYPE_DEF_TEMPLATE_STRINGENUM; - #ifdef EVENT_POST_ENABLED // enable event function of data_template #define TYPE_STR_INFO "info" @@ -85,10 +85,10 @@ typedef struct _sEvent_ { #endif -typedef enum{ - eGET_CTL, - eOPERATION_CTL, -}eControlType; +typedef enum { + eGET_CTL, + eOPERATION_CTL, +} eControlType; typedef void (*ControlMsgCb)(void *pClient, char *control_str, eControlType type); /* The structure of data_template init parameters */ @@ -114,8 +114,8 @@ typedef struct { uint8_t auto_connect_enable; // flag of auto reconnection, 1 is enable and // recommended - MQTTEventHandler event_handle; // event callback - ControlMsgCb usr_control_handle; // user can register cb to handle control msg instend of sdk's handle + MQTTEventHandler event_handle; // event callback + ControlMsgCb usr_control_handle; // user can register cb to handle control msg instend of sdk's handle } TemplateInitParams; #ifdef AUTH_MODE_CERT diff --git a/include/exports/qcloud_iot_export_device_bind.h b/include/exports/qcloud_iot_export_device_bind.h index 31ec975..57f0710 100644 --- a/include/exports/qcloud_iot_export_device_bind.h +++ b/include/exports/qcloud_iot_export_device_bind.h @@ -23,23 +23,32 @@ extern "C" { /** * @brief Register a callback function to receive the device unbinding message - * sent by the platform + * sent by the platform. + * before use it you must call qcloud_service_mqtt_init() to sub service topic * * @param callback [in] a callback function * @param context [in] the program context * @return QCLOUD_RET_SUCCESS for success, or err code for failure */ int IOT_Unbind_Device_Register(void *callback, void *context); +/** + * @brief Register a callback function to receive the device unbinding message + * sent by the platform + * + * @param mqtt_client the mqtt client + * @param callback [in] a callback function + * @param context [in] the program context + * @return QCLOUD_RET_SUCCESS for success, or err code for failure + */ +int IOT_Unbind_Device_ByCloud(void *mqtt_client, void *callback, void *context); /** * @brief Actively initiate a request to unbind the device to the platform * * @param mqtt_client the mqtt client - * @param productId deviceInfo.productId - * @param deviceName deviceInfo.deviceName * @param timeout_ms timeout value (unit: ms) for this operation * @return QCLOUD_RET_SUCCESS for success, or err code for failure */ -int IOT_Unbind_Device_Request(void *mqtt_client, const char *productId, const char *deviceName, int timeout_ms); +int IOT_Unbind_Device_Request(void *mqtt_client, int timeout_ms); #ifdef __cplusplus } diff --git a/include/exports/qcloud_iot_export_gateway.h b/include/exports/qcloud_iot_export_gateway.h index 98e0a3d..05d0db8 100755 --- a/include/exports/qcloud_iot_export_gateway.h +++ b/include/exports/qcloud_iot_export_gateway.h @@ -281,6 +281,9 @@ int IOT_Gateway_EnableLocalAutoMation(void *client, QCLOUD_IO_GATEWAY_AUTOMATION #endif +int IOT_Gateway_Subdev_GetBindList(void *client, GatewayParam *param, SubdevBindList *subdev_bindlist); +void IOT_Gateway_Subdev_DestoryBindList(SubdevBindList *subdev_bindlist); + #ifdef __cplusplus } #endif diff --git a/include/exports/qcloud_iot_export_method.h b/include/exports/qcloud_iot_export_method.h old mode 100644 new mode 100755 index 2bc1c10..68dfaa6 --- a/include/exports/qcloud_iot_export_method.h +++ b/include/exports/qcloud_iot_export_method.h @@ -49,17 +49,37 @@ typedef enum { /** * @brief JSON data type */ -typedef enum { JINT32, JINT16, JINT8, JUINT32, JUINT16, JUINT8, JFLOAT, JDOUBLE, JBOOL, JSTRING, JOBJECT } JsonDataType; +typedef enum { + JINT32, + JINT16, + JINT8, + JUINT32, + JUINT16, + JUINT8, + JFLOAT, + JDOUBLE, + JBOOL, + JSTRING, + JOBJECT, + JARRAY, +} JsonDataType; /** * @brief Define a device property, as a JSON document node */ +#if defined(__CC_ARM) +#pragma anon_unions +#endif typedef struct _JSONNode { char *key; // Key of this JSON node void *data; // Value of this JSON node union { uint16_t data_buff_len; // data buff len, for string type value update uint16_t struct_obj_num; // member number of struct + struct { + uint16_t array_size; + JsonDataType array_type; + } array_info; }; JsonDataType type; // Data type of this JSON node } DeviceProperty; diff --git a/include/lite-utils.h b/include/lite-utils.h index 5ab3244..0442b2a 100644 --- a/include/lite-utils.h +++ b/include/lite-utils.h @@ -95,6 +95,81 @@ typedef struct _json_key_t { pos = (void *)list_first_entry((list_head_t *)keylist, json_key_t, list), iter_key = ((json_key_t *)pos)->key; \ (iter_key = ((json_key_t *)pos)->key); pos = list_next_entry((json_key_t *)pos, list, json_key_t)) +typedef struct { + void * str_data; + size_t str_len; +} dt_array_elem_t; + +/** + * @brief callback function type of parsing one object json string to struct + * + * @param json_str json string + * @param str_len the length of json_str + * @param obj the buffer for the struct + * @param item_sz the size of struct + * @return 0 for success, negative for failure + */ +typedef int (*json_object_parse_t)(const char *json_str, size_t str_len, void *obj, size_t obj_len); + +/** + * @brief format array json string for array of string or object + * + * @param out_res the buffer for the result string + * @param out_sz the size of out_res + * @param items the input items, if array is for object, convert items to json string + * @param item_sz the size of items + * @return true for success + */ +int LITE_dt_format_strobj_array(char *out_res, size_t out_sz, char *items[], size_t item_sz); + +/** + * @brief format array json string for array of primitive types, int & float supported + * + * @param out_res the buffer for the result string + * @param out_sz the size of out_res + * @param data the input data storing primitive types + * @param data_sz the size of data in bytes + * @param type the primitive type enum + * @return true for success + */ +int LITE_dt_format_primitive_array(char *out_res, size_t out_sz, void *data, size_t data_sz, int type); + +/** + * @brief parse array of primitive type(int32 or float) [12, 34] -> int32 array {12, 34} + * [12.34, 56.78] -> float array {12.34, 56.78} + * + * @param out_res the buffer for array of the primitive type + * @param out_len the buffer length of out_res + * @param json_str the array json string + * @param type the primitive type int32 or float + * @return the length of json array, negative if there is error + */ +int LITE_dt_parse_primitive_array(void *out_res, size_t out_len, const char *json_str, int type); + +/** + * @brief parse array of string, ["aaaaa", "bbbbb", "ccccc"] -> char *str[] = {"aaaaa", "bbbbb", "ccccc"} + * + * @param out_res the buffer for result, a array of char * pointers + * @param arr_len the array length of string + * @param json_str the array json string + * @return the length of array, negative if there is error + */ +int LITE_dt_parse_str_array(char *out_res[], size_t arr_len, size_t str_len, const char *json_str); + +/** + * @brief parse array of object, [{"Red":100, "Green":128, "Blue":34}, {"Red":20, "Green":28, "Blue":34}] -> struct rgb + * {int red, green, blue} rgbs[2] + * {{.red = 100, .green = 128, .blue = 34}, {.red = 20, .green = 28, .blue = 34}} + * + * @param out_res the buffer for result, a array of struct + * @param arr_len the array length + * @param obj_len the length of the struct + * @param json_str the array json string + * @param parse_fn the function of parsing a object json string to struct + * @return the length of array, negative if there is error + */ +int LITE_dt_parse_obj_array(void *out_res, size_t arr_len, size_t obj_len, const char *json_str, + json_object_parse_t parse_fn); #ifdef __cplusplus } #endif diff --git a/include/qcloud_iot_import.h b/include/qcloud_iot_import.h index 5a4e9ce..fcb4040 100644 --- a/include/qcloud_iot_import.h +++ b/include/qcloud_iot_import.h @@ -501,7 +501,21 @@ int HAL_DTLS_Read(uintptr_t handle, unsigned char *data, size_t datalen, uint32_ * @return TCP socket handle (value>0) when success, or 0 otherwise */ uintptr_t HAL_TCP_Connect(const char *host, uint16_t port); - +/** + * @brief Creat tcp server + * + * @host server address + * @port server port + * @return TCP socket handle (value>0) when success, or 0 otherwise + */ +uintptr_t HAL_TCP_CreatBind(const char *host, uint16_t port); +/** + * @brief tcp server accept + * + * @server_fd tcp server file discriptor + * @return TCP socket handle (value>0) when success, or 0 otherwise + */ +uintptr_t HAL_TCP_Accept(int server_fd); /** * @brief Disconnect with server and release resource * diff --git a/platform/os/linux/HAL_TCP_linux.c b/platform/os/linux/HAL_TCP_linux.c index 29816ce..7b9bc77 100644 --- a/platform/os/linux/HAL_TCP_linux.c +++ b/platform/os/linux/HAL_TCP_linux.c @@ -116,6 +116,72 @@ uintptr_t HAL_TCP_Connect(const char *host, uint16_t port) return (uintptr_t)ret; } +uintptr_t HAL_TCP_CreatBind(const char *host, uint16_t port) +{ + int ret; + struct addrinfo hints, *addr_list, *cur; + int fd = 0; + + char port_str[6]; + HAL_Snprintf(port_str, 6, "%d", port); + + memset(&hints, 0x00, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + ret = getaddrinfo(host, port_str, &hints, &addr_list); + if (ret) { + if (ret == EAI_SYSTEM) + Log_e("getaddrinfo(%s:%s) error: %s", STRING_PTR_PRINT_SANITY_CHECK(host), port_str, strerror(errno)); + else + Log_e("getaddrinfo(%s:%s) error: %s", STRING_PTR_PRINT_SANITY_CHECK(host), port_str, gai_strerror(ret)); + return 0; + } + + for (cur = addr_list; cur != NULL; cur = cur->ai_next) { + fd = (int)socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol); + if (fd < 0) { + ret = 0; + continue; + } + + if (bind(fd, cur->ai_addr, cur->ai_addrlen) == 0) { + if(listen(fd, 5)== 0){ + ret = fd; + goto exit; + } + } + close(fd); + ret = 0; + } +exit: + if (0 == ret) { + Log_e("fail to creat TCP server: %s:%s", STRING_PTR_PRINT_SANITY_CHECK(host), port_str); + } else { + /* reduce log print due to frequent log server connect/disconnect */ + if (strstr(host, LOG_UPLOAD_SERVER_PATTEN)) + UPLOAD_DBG("creat TCP server: %s:%s", STRING_PTR_PRINT_SANITY_CHECK(host), port_str); + else + Log_i("creat TCP server: %s:%s", STRING_PTR_PRINT_SANITY_CHECK(host), port_str); + } + + freeaddrinfo(addr_list); + + return (uintptr_t)ret; +} + +uintptr_t HAL_TCP_Accept(int server_fd) +{ + struct sockaddr_in cli_addr; + socklen_t sz; + int fd = accept(server_fd, (struct sockaddr *)&cli_addr, &sz); + if (fd < 0) + return -1; + Log_d("connected from %s %d", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port)); + return fd; +} + int HAL_TCP_Disconnect(uintptr_t fd) { int rc; diff --git a/platform/os/windows/HAL_TCP_win.c b/platform/os/windows/HAL_TCP_win.c index 017cce1..f78165f 100644 --- a/platform/os/windows/HAL_TCP_win.c +++ b/platform/os/windows/HAL_TCP_win.c @@ -118,6 +118,80 @@ int HAL_TCP_Disconnect(uintptr_t fd) return 0; } +uintptr_t HAL_TCP_CreatBind(const char *host, uint16_t port) +{ + WORD sockVersion = MAKEWORD(2, 2); + WSADATA data; + + if (WSAStartup(sockVersion, &data) != 0) { + return 0; + } + int ret; + struct addrinfo hints, *addr_list, *cur; + int fd = 0; + + char port_str[6]; + HAL_Snprintf(port_str, 6, "%d", port); + + memset(&hints, 0x00, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + ret = getaddrinfo(host, port_str, &hints, &addr_list); + if (ret) { + if (ret == INVALID_SOCKET) + Log_e("getaddrinfo(%s:%s) error: %s", STRING_PTR_PRINT_SANITY_CHECK(host), port_str, strerror(errno)); + else + Log_e("getaddrinfo(%s:%s) error: %s", STRING_PTR_PRINT_SANITY_CHECK(host), port_str, gai_strerror(ret)); + return 0; + } + + for (cur = addr_list; cur != NULL; cur = cur->ai_next) { + fd = (int)socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol); + if (fd < 0) { + ret = 0; + continue; + } + + if (bind(fd, cur->ai_addr, cur->ai_addrlen) == 0) { + if (listen(fd, 5) == 0) { + ret = fd; + goto exit; + } + } + closesocket(fd); + ret = 0; + } +exit: + if (0 == ret) { + Log_e("fail to creat TCP server: %s:%s", STRING_PTR_PRINT_SANITY_CHECK(host), port_str); + } else { + /* reduce log print due to frequent log server connect/disconnect */ + if (strstr(host, LOG_UPLOAD_SERVER_PATTEN)) + UPLOAD_DBG("creat TCP server: %s:%s", STRING_PTR_PRINT_SANITY_CHECK(host), port_str); + else + Log_i("creat TCP server: %s:%s", STRING_PTR_PRINT_SANITY_CHECK(host), port_str); + } + + freeaddrinfo(addr_list); + + return (uintptr_t)ret; +} + +uintptr_t HAL_TCP_Accept(int server_fd) +{ + SOCKADDR client_addr; + + int sz = sizeof(SOCKADDR); + int fd = accept(server_fd, (SOCKADDR *)&client_addr, &sz); + if (fd < 0) + return fd; + struct sockaddr_in *sock = (struct sockaddr_in *)&client_addr; + Log_d("connected from %s %d", inet_ntoa(sock->sin_addr), ntohs(sock->sin_port)); + return fd; +} + int HAL_TCP_Write(uintptr_t fd, const unsigned char *buf, uint32_t len, uint32_t timeout_ms, size_t *written_len) { int ret; diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 98e0172..593b2c8 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -58,6 +58,12 @@ if(${FEATURE_GATEWAY_ENABLED} STREQUAL "ON") file(GLOB src_gateway_sample ${PROJECT_SOURCE_DIR}/samples/gateway/*.c) add_executable(gateway_sample ${src_gateway_sample}) target_link_libraries(gateway_sample ${lib}) + file(GLOB src_gateway_sim ${PROJECT_SOURCE_DIR}/samples/scenarized/gateway_sim_sample.c) + add_executable(gateway_sim_sample ${src_gateway_sim}) + target_link_libraries(gateway_sim_sample ${lib}) + file(GLOB src_subdev_sample ${PROJECT_SOURCE_DIR}/samples/scenarized/subdev_sim_sample.c) + add_executable(subdev_sim_sample ${src_subdev_sample}) + target_link_libraries(subdev_sim_sample ${lib}) endif() # OTA MQTT diff --git a/samples/Makefile b/samples/Makefile index fe41dab..0f63a08 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -42,9 +42,9 @@ ifeq ($(FEATURE_AUTH_MODE),CERT) CFLAGS += -DAUTH_MODE_CERT endif -.PHONY: ota_mqtt_sample resource_mqtt_sample data_template_sample file_mqtt_sample asr_data_template_sample gateway_sample mqtt_sample dynreg_dev_sample wifi_config_sample kgmusic_data_template_sample +.PHONY: ota_mqtt_sample resource_mqtt_sample data_template_sample asr_data_template_sample gateway_sample mqtt_sample dynreg_dev_sample wifi_config_sample gateway_sim_sample subdev_sim_sample file_mqtt_sample kgmusic_data_template_sample -all: ota_mqtt_sample resource_mqtt_sample data_template_sample file_mqtt_sample asr_data_template_sample gateway_sample mqtt_sample dynreg_dev_sample wifi_config_sample kgmusic_data_template_sample +all: ota_mqtt_sample resource_mqtt_sample data_template_sample asr_data_template_sample gateway_sample mqtt_sample dynreg_dev_sample wifi_config_sample gateway_sim_sample subdev_sim_sample file_mqtt_sample kgmusic_data_template_sample ifneq (,$(filter -DOTA_COMM_ENABLED,$(CFLAGS))) @@ -114,6 +114,16 @@ gateway_sample: $(TOP_Q) \ $(PLATFORM_CC) $(CFLAGS) $(SAMPLE_DIR)/gateway/*.c $(LDFLAGS) -o $@ + $(TOP_Q) \ + mv $@ $(FINAL_DIR)/bin +gateway_sim_sample: + $(TOP_Q) \ + $(PLATFORM_CC) $(CFLAGS) $(SAMPLE_DIR)/scenarized/gateway_sim_sample.c $(LDFLAGS) -o $@ + $(TOP_Q) \ + mv $@ $(FINAL_DIR)/bin +subdev_sim_sample: + $(TOP_Q) \ + $(PLATFORM_CC) $(CFLAGS) $(SAMPLE_DIR)/scenarized/subdev_sim_sample.c $(LDFLAGS) -o $@ $(TOP_Q) \ mv $@ $(FINAL_DIR)/bin endif diff --git a/samples/kgmusic/kgmusic_data_template_sample.c b/samples/kgmusic/kgmusic_data_template_sample.c index b3269a6..2de0e98 100644 --- a/samples/kgmusic/kgmusic_data_template_sample.c +++ b/samples/kgmusic/kgmusic_data_template_sample.c @@ -33,62 +33,6 @@ static bool sg_control_msg_arrived = false; static char sg_data_report_buffer[2048]; static size_t sg_data_report_buffersize = sizeof(sg_data_report_buffer) / sizeof(sg_data_report_buffer[0]); -#ifdef EVENT_POST_ENABLED - -#include "events_config.c" -static void update_events_timestamp(sEvent *pEvents, int count) -{ - int i; - - for (i = 0; i < count; i++) { - if (NULL == (&pEvents[i])) { - Log_e("null event pointer"); - return; - } -#ifdef EVENT_TIMESTAMP_USED - pEvents[i].timestamp = time(NULL); // should be UTC and accurate -#else - pEvents[i].timestamp = 0; -#endif - } -} - -static void event_post_cb(void *pClient, MQTTMessage *msg) -{ - Log_d("Reply:%.*s", msg->payload_len, msg->payload); - // IOT_Event_clearFlag(pClient, FLAG_EVENT0 | FLAG_EVENT1 | FLAG_EVENT2); -} - -// event check and post -static void eventPostCheck(void *client) -{ - int i; - int rc; - uint32_t eflag; - uint8_t event_count; - sEvent * pEventList[EVENT_COUNTS]; - - eflag = IOT_Event_getFlag(client); - if ((EVENT_COUNTS > 0) && (eflag > 0)) { - event_count = 0; - for (i = 0; i < EVENT_COUNTS; i++) { - if ((eflag & (1 << i)) & ALL_EVENTS_MASK) { - pEventList[event_count++] = &(g_events[i]); - update_events_timestamp(&g_events[i], 1); - IOT_Event_clearFlag(client, (1 << i) & ALL_EVENTS_MASK); - } - } - - rc = IOT_Post_Event(client, sg_data_report_buffer, sg_data_report_buffersize, event_count, pEventList, - event_post_cb); - if (rc < 0) { - Log_e("events post failed: %d", rc); - } - } -} - -#endif - #ifdef ACTION_ENABLED #include "action_config.c" @@ -256,10 +200,6 @@ void deal_down_stream_user_logic(void *client, ProductDataDefine *pData) { Log_d("someting about your own product logic wait to be done"); -#ifdef EVENT_POST_ENABLED - // IOT_Event_setFlag(client, FLAG_EVENT0); //set the events flag when the evnts your defined occured, see - // events_config.c -#endif } /*get local property data, like sensor data*/ @@ -1994,9 +1934,7 @@ int main(int argc, char **argv) } else { // Log_d("no data need to be reported or someting goes wrong"); } -#ifdef EVENT_POST_ENABLED - eventPostCheck(client); -#endif + void *mqtt_client = IOT_Template_Get_MQTT_Client(client); if (_kgmusic_downmsg_play_proc(mqtt_client)) { _kgmusic_playsong(mqtt_client); diff --git a/samples/mqtt/mqtt_sample.c b/samples/mqtt/mqtt_sample.c index 1dda93a..bedd8ea 100644 --- a/samples/mqtt/mqtt_sample.c +++ b/samples/mqtt/mqtt_sample.c @@ -26,29 +26,28 @@ #include "qcloud_iot_export.h" #include "qcloud_iot_import.h" +#include "service_mqtt.h" #include "utils_getopt.h" #include "lite-utils.h" - #ifdef AUTH_MODE_CERT static char sg_cert_file[PATH_MAX + 1]; // full path of device cert file static char sg_key_file[PATH_MAX + 1]; // full path of device key file #endif typedef struct { - bool power_off; - uint8_t brightness; + bool power_off; + uint8_t brightness; uint16_t color; - char device_name[MAX_SIZE_OF_DEVICE_NAME + 1]; -}LedInfo; + char device_name[MAX_SIZE_OF_DEVICE_NAME + 1]; +} LedInfo; static DeviceInfo sg_devInfo; -static LedInfo sg_led_info; +static LedInfo sg_led_info; // led attributes, corresponding to struct LedInfo static char *sg_property_name[] = {"power_switch", "brightness", "color", "name"}; - // user's log print callback static bool log_handler(const char *message) { @@ -192,23 +191,25 @@ static void property_topic_publish(void *pClient, const char *message, int messa static void property_get_status(void *pClient) { - char message[256] = {0}; - int message_len = 0; + char message[256] = {0}; + int message_len = 0; static int sg_get_status_index = 0; sg_get_status_index++; - message_len = HAL_Snprintf(message, sizeof(message), "{\"method\":\"get_status\", \"clientToken\":\"%s-%d\"}", sg_devInfo.product_id, sg_get_status_index); + message_len = HAL_Snprintf(message, sizeof(message), "{\"method\":\"get_status\", \"clientToken\":\"%s-%d\"}", + sg_devInfo.product_id, sg_get_status_index); property_topic_publish(pClient, message, message_len); } static void property_report(void *pClient) { - char message[256] = {0}; - int message_len = 0; + char message[256] = {0}; + int message_len = 0; static int sg_report_index = 0; sg_report_index++; - message_len = HAL_Snprintf(message, sizeof(message), "{\"method\":\"report\", \"clientToken\":\"%s-%d\", " + message_len = HAL_Snprintf(message, sizeof(message), + "{\"method\":\"report\", \"clientToken\":\"%s-%d\", " "\"params\":{\"power_switch\":%d, \"color\":%d, \"brightness\":%d, \"name\":\"%s\"}}", sg_devInfo.product_id, sg_report_index, sg_led_info.power_off, sg_led_info.color, sg_led_info.brightness, sg_devInfo.device_name); @@ -220,18 +221,18 @@ static void property_report(void *pClient) static void property_control_handle(void *pClient, const char *token, const char *control_data) { - char *params = NULL; + char *params = NULL; char *property_param = NULL; - char message[256] = {0}; - int message_len = 0; + char message[256] = {0}; + int message_len = 0; params = LITE_json_value_of("params", (char *)control_data); - if (NULL == params){ + if (NULL == params) { Log_e("Fail to parse params"); return; } - for (int i = 0; i < sizeof(sg_property_name) / sizeof(sg_property_name[0]); i++){ + for (int i = 0; i < sizeof(sg_property_name) / sizeof(sg_property_name[0]); i++) { property_param = LITE_json_value_of(sg_property_name[i], params); if (NULL != property_param) { Log_i("\t%-16s = %-10s", sg_property_name[i], property_param); @@ -242,9 +243,10 @@ static void property_control_handle(void *pClient, const char *token, const char HAL_Free(property_param); } } - - //method: control_reply - message_len = HAL_Snprintf(message, sizeof(message), "{\"method\":\"control_reply\", \"code\":0, \"clientToken\":\"%s\"}", token); + + // method: control_reply + message_len = HAL_Snprintf(message, sizeof(message), + "{\"method\":\"control_reply\", \"code\":0, \"clientToken\":\"%s\"}", token); property_topic_publish(pClient, message, message_len); HAL_Free(params); @@ -252,27 +254,27 @@ static void property_control_handle(void *pClient, const char *token, const char static void property_get_status_reply_handle(const char *get_status_reply_data) { - char *data = NULL; - char *report = NULL; + char *data = NULL; + char *report = NULL; char *property_param = NULL; data = LITE_json_value_of("data", (char *)get_status_reply_data); - if (NULL == data){ + if (NULL == data) { Log_e("Fail to parse data"); return; } report = LITE_json_value_of("reported", (char *)data); - if (NULL == report){ + if (NULL == report) { Log_e("Fail to parse report"); HAL_Free(data); return; } - for (int i = 0; i < sizeof(sg_property_name) / sizeof(sg_property_name[0]); i++){ + for (int i = 0; i < sizeof(sg_property_name) / sizeof(sg_property_name[0]); i++) { property_param = LITE_json_value_of(sg_property_name[i], report); if (NULL != property_param) { Log_i("\t%-16s = %-10s", sg_property_name[i], property_param); - if (i == 1){ + if (i == 1) { sg_led_info.brightness = atoi(property_param); } HAL_Free(property_param); @@ -288,7 +290,7 @@ static void property_report_reply_handle(const char *report_reply_data) char *status = NULL; status = LITE_json_value_of("status", (char *)report_reply_data); - if (NULL == status){ + if (NULL == status) { Log_e("Fail to parse data"); return; } @@ -296,13 +298,18 @@ static void property_report_reply_handle(const char *report_reply_data) HAL_Free(status); } +void unbind_device_callback(void *pContext, const char *msg, uint32_t msgLen) +{ + Log_i("unbind device."); +} + // callback when MQTT msg arrives static void property_message_callback(void *pClient, MQTTMessage *message, void *userData) { - char property_data_buf[QCLOUD_IOT_MQTT_RX_BUF_LEN + 1] = {0}; - int property_data_len = 0; - char *type_str = NULL; - char *token_str = NULL; + char property_data_buf[QCLOUD_IOT_MQTT_RX_BUF_LEN + 1] = {0}; + int property_data_len = 0; + char *type_str = NULL; + char *token_str = NULL; if (message == NULL) { return; @@ -310,7 +317,8 @@ static void property_message_callback(void *pClient, MQTTMessage *message, void Log_i("Receive Message With topicName:%.*s, payload:%.*s", (int)message->topic_len, message->ptopic, (int)message->payload_len, (char *)message->payload); - property_data_len = sizeof(property_data_buf) > message->payload_len ? message->payload_len : QCLOUD_IOT_MQTT_RX_BUF_LEN; + property_data_len = + sizeof(property_data_buf) > message->payload_len ? message->payload_len : QCLOUD_IOT_MQTT_RX_BUF_LEN; memcpy(property_data_buf, message->payload, property_data_len); type_str = LITE_json_value_of("method", property_data_buf); @@ -325,16 +333,16 @@ static void property_message_callback(void *pClient, MQTTMessage *message, void return; } - if (0 == strncmp(type_str, "control", sizeof("control") - 1)){ - //method: control + if (0 == strncmp(type_str, "control", sizeof("control") - 1)) { + // method: control property_control_handle(pClient, token_str, property_data_buf); - }else if (0 == strncmp(type_str, "get_status_reply", sizeof("get_status_reply") - 1)){ - //method: get_status_reply + } else if (0 == strncmp(type_str, "get_status_reply", sizeof("get_status_reply") - 1)) { + // method: get_status_reply property_get_status_reply_handle(property_data_buf); - }else if (0 == strncmp(type_str, "report_reply", sizeof("report_reply") - 1)){ - //method: report_reply + } else if (0 == strncmp(type_str, "report_reply", sizeof("report_reply") - 1)) { + // method: report_reply property_report_reply_handle(property_data_buf); - }else{ + } else { // do nothing } @@ -347,15 +355,15 @@ static int subscribe_property_topic_wait_result(void *client) { char topic_name[128] = {0}; - int size = HAL_Snprintf(topic_name, sizeof(topic_name), "$thing/down/property/%s/%s", sg_devInfo.product_id, \ + int size = HAL_Snprintf(topic_name, sizeof(topic_name), "$thing/down/property/%s/%s", sg_devInfo.product_id, sg_devInfo.device_name); if (size < 0 || size > sizeof(topic_name) - 1) { Log_e("topic content length not enough! content size:%d buf size:%d", size, (int)sizeof(topic_name)); return QCLOUD_ERR_FAILURE; } - SubscribeParams sub_params = DEFAULT_SUB_PARAMS; - sub_params.qos = QOS0; + SubscribeParams sub_params = DEFAULT_SUB_PARAMS; + sub_params.qos = QOS0; sub_params.on_message_handler = property_message_callback; int rc = IOT_MQTT_Subscribe(client, topic_name, &sub_params); @@ -390,8 +398,8 @@ static int _init_log_upload(MQTTInitParams *init_params) LogUploadInitParams log_init_params; memset(&log_init_params, 0, sizeof(LogUploadInitParams)); - log_init_params.region = init_params->region; - log_init_params.product_id = init_params->product_id; + log_init_params.region = init_params->region; + log_init_params.product_id = init_params->product_id; log_init_params.device_name = init_params->device_name; #ifdef AUTH_MODE_CERT log_init_params.sign_key = init_params->cert_file; @@ -400,9 +408,9 @@ static int _init_log_upload(MQTTInitParams *init_params) #endif #if defined(__linux__) || defined(WIN32) - log_init_params.read_func = HAL_Log_Read; - log_init_params.save_func = HAL_Log_Save; - log_init_params.del_func = HAL_Log_Del; + log_init_params.read_func = HAL_Log_Read; + log_init_params.save_func = HAL_Log_Save; + log_init_params.del_func = HAL_Log_Del; log_init_params.get_size_func = HAL_Log_Get_Size; #endif @@ -492,7 +500,9 @@ int main(int argc, char **argv) Log_e("Client Subscribe Topic Failed: %d", rc); return rc; } - //method: get_status + // when platform unbind this device. the callback function will run + IOT_Unbind_Device_ByCloud(client, unbind_device_callback, NULL); + // method: get_status property_get_status(client); do { rc = IOT_MQTT_Yield(client, 500); @@ -507,11 +517,11 @@ int main(int argc, char **argv) if (sg_loop_test) HAL_SleepMs(1000); - //method: report + // method: report property_report(client); } while (sg_loop_test); - rc = IOT_Unbind_Device_Request(client, sg_devInfo.product_id, sg_devInfo.device_name, 5000); - if(rc != QCLOUD_RET_SUCCESS){ + rc = IOT_Unbind_Device_Request(client, 5000); + if (rc != QCLOUD_RET_SUCCESS) { Log_e("unbind device request error."); } rc = IOT_MQTT_Destroy(&client); diff --git a/samples/scenarized/gateway_sim_sample.c b/samples/scenarized/gateway_sim_sample.c new file mode 100644 index 0000000..21fd381 --- /dev/null +++ b/samples/scenarized/gateway_sim_sample.c @@ -0,0 +1,531 @@ +/* + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "lite-utils.h" +#include "qcloud_iot_export.h" +#include "utils_getopt.h" + +#define MAX_SIZE_OF_TOPIC (128) +#define MAX_SIZE_OF_DATA (128) + +#define LOCAL_SERVER_PORT (20000) +#define LOCAL_HOST "127.0.0.1" + +#define MAX_SUB_CLIENTS (32) + +static int sg_sub_packet_id = -1; +static int sg_loop_count = 1; +static GatewayDeviceInfo sg_GWdevInfo; + +#ifdef AUTH_MODE_CERT +static char sg_cert_file[PATH_MAX + 1]; // full path of device cert file +static char sg_key_file[PATH_MAX + 1]; // full path of device key file +#endif + +#define TOPIC_PROP_UP_FMT "$thing/up/property/%s/%s" +#define TOPIC_PROP_DOWN_FMT "$thing/down/property/%s/%s" + +enum { + SUBDEV_STATUS_INIT, + SUBDEV_STATUS_ONLINE, + SUBDEV_STATUS_OFFLINE, +}; + +struct subdev_local_info { + int fd; + char pid[MAX_SIZE_OF_PRODUCT_ID + 1]; + char dname[MAX_SIZE_OF_DEVICE_NAME + 1]; + int status; +}; +static struct subdev_local_info sg_clients_info[MAX_SUB_CLIENTS]; + +void _event_handler(void *client, void *context, MQTTEventMsg *msg) +{ + MQTTMessage *mqtt_messge = (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_RECVEIVED: + Log_i( + "topic message arrived but without any related handle: topic=%.*s, " + "topic_msg=%.*s", + mqtt_messge->topic_len, mqtt_messge->ptopic, mqtt_messge->payload_len, mqtt_messge->payload); + break; + case MQTT_EVENT_SUBCRIBE_SUCCESS: + Log_i("subscribe success, packet-id=%u", (unsigned int)packet_id); + sg_sub_packet_id = packet_id; + break; + + case MQTT_EVENT_SUBCRIBE_TIMEOUT: + Log_i("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id); + sg_sub_packet_id = packet_id; + break; + + case MQTT_EVENT_SUBCRIBE_NACK: + Log_i("subscribe nack, packet-id=%u", (unsigned int)packet_id); + sg_sub_packet_id = packet_id; + break; + + case MQTT_EVENT_UNSUBCRIBE_SUCCESS: + Log_i("unsubscribe success, packet-id=%u", (unsigned int)packet_id); + break; + + case MQTT_EVENT_UNSUBCRIBE_TIMEOUT: + Log_i("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id); + break; + + case MQTT_EVENT_UNSUBCRIBE_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; + case MQTT_EVENT_GATEWAY_SEARCH: + Log_d("gateway search subdev status:%d", *(int32_t *)(msg->msg)); + break; + + case MQTT_EVENT_GATEWAY_CHANGE: { + gw_change_notify_t *p_notify = (gw_change_notify_t *)(msg->msg); + Log_d("Get change notice: sub devices %s to %s", p_notify->devices, p_notify->status ? "bind" : "unbind"); + } break; + + default: + Log_i("Should NOT arrive here."); + break; + } +} + +static void _message_handler(void *client, MQTTMessage *message, void *user_data) +{ + struct subdev_local_info *subdev = (struct subdev_local_info *)user_data; + if (!message || !subdev || subdev->fd <= 0 || message->topic_len > 127) { + return; + } + + Log_i("Receive Message With topicName:%.*s, payload:%.*s", (int)message->topic_len, message->ptopic, + (int)message->payload_len, (char *)message->payload); + char tmp_topic[128]; + HAL_Snprintf(tmp_topic, 127, TOPIC_PROP_DOWN_FMT, subdev->pid, subdev->dname); + if (strncmp(tmp_topic, message->ptopic, message->topic_len)) { + Log_e("Not my topic"); + return; + } + char *msg = (char *)HAL_Malloc(message->payload_len + 32); + memcpy(msg, message->payload, message->payload_len); + msg[message->payload_len] = '\0'; + size_t write_len = 0; + if (HAL_TCP_Write(subdev->fd, (unsigned char *)msg, message->payload_len + 1, 1000, &write_len) < 0) { + Log_e("Failed to send subdev msg %s", msg); + } + HAL_Free(msg); +} + +static int _setup_connect_init_params(GatewayInitParam *init_params) +{ + int rc; + + rc = HAL_GetGwDevInfo((void *)&sg_GWdevInfo); + if (QCLOUD_RET_SUCCESS != rc) { + Log_e("Get gateway dev info err,rc:%d", rc); + return rc; + } + + init_params->init_param.region = sg_GWdevInfo.gw_info.region; + init_params->init_param.product_id = sg_GWdevInfo.gw_info.product_id; + init_params->init_param.device_name = sg_GWdevInfo.gw_info.device_name; + +#ifdef AUTH_MODE_CERT + char certs_dir[PATH_MAX + 1] = "certs"; + char current_path[PATH_MAX + 1]; + char *cwd = getcwd(current_path, sizeof(current_path)); + if (cwd == NULL) { + Log_e("getcwd return NULL"); + return QCLOUD_ERR_FAILURE; + } + sprintf(sg_cert_file, "%s/%s/%s", current_path, certs_dir, sg_GWdevInfo.gw_info.dev_cert_file_name); + sprintf(sg_key_file, "%s/%s/%s", current_path, certs_dir, sg_GWdevInfo.gw_info.dev_key_file_name); + + init_params->init_param.cert_file = sg_cert_file; + init_params->init_param.key_file = sg_key_file; +#else + init_params->init_param.device_secret = sg_GWdevInfo.gw_info.device_secret; +#endif + + init_params->init_param.command_timeout = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT; + init_params->init_param.auto_connect_enable = 1; + init_params->init_param.event_handle.h_fp = _event_handler; + init_params->init_param.keep_alive_interval_ms = QCLOUD_IOT_MQTT_KEEP_ALIVE_INTERNAL; + + return QCLOUD_RET_SUCCESS; +} + +// for reply code, pls check https://cloud.tencent.com/document/product/634/45960 +#define GATEWAY_RC_REPEAT_BIND 809 + +static int parse_arguments(int argc, char **argv) +{ + int c; + while ((c = utils_getopt(argc, argv, "c:l:")) != EOF) switch (c) { + case 'c': + if (HAL_SetDevInfoFile(utils_optarg)) + return -1; + break; + + case 'l': + sg_loop_count = atoi(utils_optarg); + if (sg_loop_count > 10000) + sg_loop_count = 10000; + else if (sg_loop_count < 0) + sg_loop_count = 1; + break; + + default: + HAL_Printf( + "usage: %s [options]\n" + " [-c ] \n" + " [-l ] \n", + argv[0]); + return -1; + } + return 0; +} + +static void _property_topic_publish(void *pClient, const char *pid, const char *dname, const char *message, + int message_len) +{ + char topic[256] = {0}; + int size; + Log_d("pid %s dname %s", pid, dname); + + size = HAL_Snprintf(topic, 256, "$thing/up/property/%s/%s", pid, dname); + if (size < 0 || size > 256 - 1) { + Log_e("buf size < topic length!"); + return; + } + + PublishParams pubParams = DEFAULT_PUB_PARAMS; + pubParams.qos = QOS0; + pubParams.payload_len = message_len; + pubParams.payload = (void *)message; + + Log_d("topic %s", topic); + Log_d("publish msg %s", message); + + IOT_MQTT_Publish(IOT_Gateway_Get_Mqtt_Client(pClient), topic, &pubParams); +} + +// subscribe subdev MQTT topic and wait for sub result +static int _subscribe_subdev_topic_wait_result(void *client, char *topic_name, QoS qos, GatewayParam *gw_info, + struct subdev_local_info *subdev) +{ + SubscribeParams sub_params = DEFAULT_SUB_PARAMS; + sub_params.qos = qos; + sub_params.on_message_handler = _message_handler; + sub_params.user_data = subdev; + + int rc = IOT_Gateway_Subscribe(client, topic_name, &sub_params); + if (rc < 0) { + Log_e("IOT_Gateway_Subscribe failed."); + return rc; + } + + int wait_cnt = 10; + while (!IOT_Gateway_IsSubReady(client, topic_name) && (wait_cnt-- > 0)) { + // wait for subscription result + rc = IOT_Gateway_Yield(client, 1000); + if (rc) { + Log_e("MQTT error: %d", rc); + return rc; + } + } + + if (wait_cnt > 0) { + return QCLOUD_RET_SUCCESS; + } + Log_e("wait for subscribe result timeout!"); + return QCLOUD_ERR_FAILURE; +} + +static int _get_idle_slot() +{ + int i; + for (i = 0; i < MAX_SUB_CLIENTS; ++i) { + if (sg_clients_info[i].fd <= 0) + return i; + } + + return -1; +} + +enum { + LOCAL_MSG_TYPE_ONLINE, + LOCAL_MSG_TYPE_OFFLINE, + LOCAL_MSG_TYPE_REPORT, + + LOCAL_MSG_TYPE_SZ, + LOCAL_MSG_TYPE_UNKNOWN = -1, +}; + +static int _get_type_index(const char *type) +{ + if (!strcmp(type, "online")) + return LOCAL_MSG_TYPE_ONLINE; + if (!strcmp(type, "offline")) + return LOCAL_MSG_TYPE_OFFLINE; + if (!strcmp(type, "report")) + return LOCAL_MSG_TYPE_REPORT; + + return LOCAL_MSG_TYPE_UNKNOWN; +} + +static int _subdev_leave(void *gw_client, GatewayParam *param) +{ + char sub_topic[128]; + int ret; + HAL_Snprintf(sub_topic, 127, TOPIC_PROP_DOWN_FMT, param->subdev_product_id, param->subdev_device_name); + if (QCLOUD_RET_SUCCESS != (ret = IOT_Gateway_Unsubscribe(gw_client, sub_topic))) { + Log_d("unsubscribe %s return %d", sub_topic, ret); + } + if (QCLOUD_RET_SUCCESS != IOT_Gateway_Subdev_Offline(gw_client, param)) { + Log_e("Failed to set %s %d to offline", param->subdev_product_id, param->subdev_device_name); + return -1; + } + return 0; +} + +static int _deal_with_subdev_msg(char *msg, void *gw_client, GatewayParam *param, int idx) +{ + int ret = 0; + Log_d("Get msg %s from subdev", msg); + char *sub_pid = NULL, *sub_dname = NULL; + char *type = LITE_json_value_of("subdev_type", msg); + if (!type) { + Log_e("Failed to parse type"); + if (sg_clients_info[idx].status == SUBDEV_STATUS_ONLINE) { + _property_topic_publish(gw_client, sg_clients_info[idx].pid, sg_clients_info[idx].dname, msg, strlen(msg)); + } + goto exit; + } + int itype = _get_type_index(type); + if (itype == LOCAL_MSG_TYPE_UNKNOWN) + goto exit; + sub_pid = LITE_json_value_of("product_id", msg); + sub_dname = LITE_json_value_of("device_name", msg); + if (!sub_pid || !sub_dname) + goto exit; + param->subdev_product_id = sub_pid; + param->subdev_device_name = sub_dname; + char sub_topic[128]; + Log_d("itype is %d, pid %s dname %s", itype, sub_pid, sub_dname); + switch (itype) { + case LOCAL_MSG_TYPE_OFFLINE: + sg_clients_info[idx].status = SUBDEV_STATUS_OFFLINE; + ret = _subdev_leave(gw_client, param); + break; + case LOCAL_MSG_TYPE_ONLINE: + ret = IOT_Gateway_Subdev_Online(gw_client, param); + if (QCLOUD_RET_SUCCESS != ret) { + Log_e("Failed to set %s %d to %d", param->subdev_product_id, param->subdev_device_name, itype); + break; + } + strncpy(sg_clients_info[idx].pid, sub_pid, MAX_SIZE_OF_PRODUCT_ID + 1); + strncpy(sg_clients_info[idx].dname, sub_dname, MAX_SIZE_OF_DEVICE_NAME + 1); + HAL_Snprintf(sub_topic, 127, TOPIC_PROP_DOWN_FMT, sub_pid, sub_dname); + if (_subscribe_subdev_topic_wait_result(gw_client, sub_topic, 1, param, &sg_clients_info[idx])) { + Log_e("Failed to subscribe subdev topic"); + } else { + sg_clients_info[idx].status = SUBDEV_STATUS_ONLINE; + Log_d("sub subdev OK"); + } + break; + case LOCAL_MSG_TYPE_REPORT: { + Log_d("msg %s", msg); + char *client_token = LITE_json_value_of("msg.clientToken", msg); + char *timestamp = LITE_json_value_of("msg.timestamp", msg); + char *pwr = LITE_json_value_of("msg.power_switch", msg); + char *color = LITE_json_value_of("msg.color", msg); + char *brightness = LITE_json_value_of("msg.brightness", msg); +#define REPORT_SUBDEV_FMT \ + "{\"method\":\"report\",\"clientToken\":\"%s\",\"timestamp\":%s,\"params\":{\"power_switch\":%s,\"color\":%s," \ + "\"brightness\":%s}}" + char report_msg[512]; + int _report_len = + HAL_Snprintf(report_msg, 512, REPORT_SUBDEV_FMT, client_token, timestamp, pwr, color, brightness); + if (_report_len > 0 && _report_len < 512) + _property_topic_publish(gw_client, sub_pid, sub_dname, report_msg, _report_len); + HAL_Free(client_token); + HAL_Free(timestamp); + HAL_Free(pwr); + HAL_Free(color); + HAL_Free(brightness); + } break; + default: + break; + } +exit: + HAL_Free(type); + HAL_Free(sub_pid); + HAL_Free(sub_dname); + + return ret; +} + +static int _select_local(int max_fd, fd_set *rd_set, int gw_fd) +{ + int i; + struct timeval tv; + FD_ZERO(rd_set); + FD_SET(gw_fd, rd_set); + for (i = 0; i < MAX_SUB_CLIENTS; ++i) { + if (sg_clients_info[i].fd > 0) { + FD_SET(sg_clients_info[i].fd, rd_set); + } + } + tv.tv_sec = 2; + tv.tv_usec = 0; + + return select(max_fd + 1, rd_set, NULL, NULL, &tv); +} + +/*Gateway should enable multithread*/ +int main(int argc, char **argv) +{ + int rc = QCLOUD_ERR_FAILURE; + int i; + void * client = NULL; + GatewayDeviceInfo *gw = &sg_GWdevInfo; + GatewayParam param = DEFAULT_GATEWAY_PARAMS; + + IOT_Log_Set_Level(eLOG_DEBUG); + // parse arguments for device info file and loop test; + rc = parse_arguments(argc, argv); + if (rc != QCLOUD_RET_SUCCESS) { + Log_e("parse arguments error, rc = %d", rc); + return rc; + } + + GatewayInitParam init_params = DEFAULT_GATEWAY_INIT_PARAMS; + rc = _setup_connect_init_params(&init_params); + if (rc != QCLOUD_RET_SUCCESS) { + Log_e("init params err,rc=%d", rc); + return rc; + } + + client = IOT_Gateway_Construct(&init_params); + if (client == NULL) { + Log_e("client constructed failed."); + return QCLOUD_ERR_FAILURE; + } + param.product_id = gw->gw_info.product_id; + param.device_name = gw->gw_info.device_name; + + int gw_fd = HAL_TCP_CreatBind(LOCAL_HOST, LOCAL_SERVER_PORT); + if (gw_fd < 0) + goto exit; + int max_fd = gw_fd; + fd_set rd_set; + memset(sg_clients_info, 0, sizeof(sg_clients_info)); + while (1) { + rc = _select_local(max_fd, &rd_set, gw_fd); + if (rc < 0) { + Log_e("select return %s", strerror(errno)); + goto exit; + } + // nothing happened + if (rc == 0) { + IOT_Gateway_Yield(client, 1000); + continue; + } + int _done = 0; + + if (FD_ISSET(gw_fd, &rd_set)) { + int slot = _get_idle_slot(); + if (slot < 0) { + Log_e("No space for new sub device"); + } else { + sg_clients_info[slot].fd = HAL_TCP_Accept(gw_fd); + max_fd = max_fd > sg_clients_info[slot].fd ? max_fd : sg_clients_info[slot].fd; + } + _done++; + } + for (i = 0; i < MAX_SUB_CLIENTS; ++i) { + if (_done >= rc) // all was done + break; + if (sg_clients_info[i].fd <= 0 || !FD_ISSET(sg_clients_info[i].fd, &rd_set)) + continue; + static char sg_subdev_msg[512]; + size_t rd_len = 0; // read(sg_clients_info[i].fd, sg_subdev_msg, sizeof(sg_subdev_msg) - 1); + HAL_TCP_Read(sg_clients_info[i].fd, (unsigned char *)sg_subdev_msg, sizeof(sg_subdev_msg) - 1, 100, + &rd_len); + param.subdev_product_id = sg_clients_info[i].pid; + param.subdev_device_name = sg_clients_info[i].dname; + if (rd_len > 0) { + sg_subdev_msg[rd_len] = '\0'; + _deal_with_subdev_msg(sg_subdev_msg, client, ¶m, i); + } else if (rd_len == 0) { + Log_d("socket of %s:%s closed", sg_clients_info[i].pid, sg_clients_info[i].dname); + HAL_TCP_Disconnect(sg_clients_info[i].fd); + sg_clients_info[i].fd = -1; + _subdev_leave(client, ¶m); + } + _done++; + } + IOT_Gateway_Yield(client, 1000); + } + +exit: + if (gw_fd > 0) + HAL_TCP_Disconnect(gw_fd); + for (i = 0; i < MAX_SUB_CLIENTS; ++i) { + if (sg_clients_info[i].fd > 0) { + HAL_TCP_Disconnect(sg_clients_info[i].fd); + } + } + return IOT_Gateway_Destroy(client); +} \ No newline at end of file diff --git a/samples/scenarized/light_data_template_sample.c b/samples/scenarized/light_data_template_sample.c index f5b1f8d..c0a6227 100644 --- a/samples/scenarized/light_data_template_sample.c +++ b/samples/scenarized/light_data_template_sample.c @@ -29,10 +29,13 @@ static char sg_cert_file[PATH_MAX + 1]; // full path of device cert file static char sg_key_file[PATH_MAX + 1]; // full path of device key file #endif +// #define TEST_ARRAY 1 +// #define COLOR_TYPE_STRINGENUM + static DeviceInfo sg_devInfo; static Timer sg_reportTimer; -#define DATA_TEMPLATE_WITHOUT_STRUCT 1 +#define DATA_TEMPLATE_WITHOUT_STRUCT 1 /* anis color control codes */ #define ANSI_COLOR_RED "\x1b[31m" #define ANSI_COLOR_GREEN "\x1b[32m" @@ -47,8 +50,15 @@ static size_t sg_data_report_buffersize = sizeof(sg_data_report_buffer) / /*data_config.c can be generated by tools/codegen.py -c xx/product.json*/ /*-----------------data config start -------------------*/ +#if TEST_ARRAY +#define TOTAL_PROPERTY_COUNT 8 +#define MAX_ARRAY_JSON_STR_LEN (512) +#define MAX_ARRAY_ITEM_STR_LEN (64) +#else #define TOTAL_PROPERTY_COUNT 5 -#define MAX_STR_NAME_LEN (64) +#endif + +#define MAX_STR_NAME_LEN (64) static sDataPoint sg_DataTemplate[TOTAL_PROPERTY_COUNT]; @@ -63,11 +73,20 @@ typedef enum { #define eCOLOR_STRING_BLUE "blue" typedef struct _ProductDataDefine { - TYPE_DEF_TEMPLATE_BOOL m_light_switch; + TYPE_DEF_TEMPLATE_BOOL m_light_switch; +#ifdef COLOR_TYPE_STRINGENUM TYPE_DEF_TEMPLATE_STRINGENUM m_color[MAX_STR_NAME_LEN + 1]; - TYPE_DEF_TEMPLATE_INT m_brightness; - TYPE_DEF_TEMPLATE_STRING m_name[MAX_STR_NAME_LEN + 1]; - TYPE_DEF_TEMPLATE_OBJECT m_position; +#else + TYPE_DEF_TEMPLATE_ENUM m_color; +#endif + TYPE_DEF_TEMPLATE_INT m_brightness; + TYPE_DEF_TEMPLATE_STRING m_name[MAX_STR_NAME_LEN + 1]; + TYPE_DEF_TEMPLATE_OBJECT m_position; +#if TEST_ARRAY + TYPE_DEF_TEMPLATE_STRING m_alias_name[MAX_ARRAY_JSON_STR_LEN + 1]; + TYPE_DEF_TEMPLATE_STRING m_power_consumption[MAX_ARRAY_JSON_STR_LEN + 1]; + TYPE_DEF_TEMPLATE_STRING m_recent_status[MAX_ARRAY_JSON_STR_LEN + 1]; +#endif } ProductDataDefine; static ProductDataDefine sg_ProductData; @@ -98,6 +117,24 @@ static void _init_struct_position(void) sg_StructTemplatePosition[1].state = eCHANGED; }; +#define MAX_SAMPLE_ARRAY_SIZE 16 + +#if TEST_ARRAY +typedef struct { + int brightness; + float temperature; +} recent_status_t; + +static int sg_alias_name_array_size = 0; +static char sg_alias_name_array[MAX_SAMPLE_ARRAY_SIZE][64]; + +static int sg_power_consumption_array_size = 0; +static int sg_power_consumption_array[MAX_SAMPLE_ARRAY_SIZE]; + +static int sg_recent_status_array_size = 0; +static recent_status_t sg_recent_status_array[MAX_SAMPLE_ARRAY_SIZE]; +#endif + static void _init_data_template(void) { memset((void *)&sg_ProductData, 0, sizeof(ProductDataDefine)); @@ -106,15 +143,20 @@ static void _init_data_template(void) sg_DataTemplate[0].data_property.key = "power_switch"; sg_DataTemplate[0].data_property.data = &sg_ProductData.m_light_switch; sg_DataTemplate[0].data_property.type = TYPE_TEMPLATE_BOOL; - +#ifdef COLOR_TYPE_STRINGENUM // sg_ProductData.m_color = eCOLOR_RED; strcpy(sg_ProductData.m_color, eCOLOR_STRING_RED); sg_ProductData.m_color[strlen(eCOLOR_STRING_RED)] = '\0'; sg_DataTemplate[1].data_property.key = "color"; - sg_DataTemplate[1].data_property.data = &sg_ProductData.m_color; + sg_DataTemplate[1].data_property.data = sg_ProductData.m_color; sg_DataTemplate[1].data_property.data_buff_len = MAX_STR_NAME_LEN; sg_DataTemplate[1].data_property.type = TYPE_TEMPLATE_STRINGENUM; - +#else + sg_ProductData.m_color = eCOLOR_RED; + sg_DataTemplate[1].data_property.key = "color"; + sg_DataTemplate[1].data_property.data = &sg_ProductData.m_color; + sg_DataTemplate[1].data_property.type = TYPE_TEMPLATE_ENUM; +#endif sg_ProductData.m_brightness = 0; sg_DataTemplate[2].data_property.key = "brightness"; sg_DataTemplate[2].data_property.data = &sg_ProductData.m_brightness; @@ -128,12 +170,36 @@ static void _init_data_template(void) sg_DataTemplate[3].data_property.type = TYPE_TEMPLATE_STRING; _init_struct_position(); - sg_ProductData.m_position = (void *)&sg_StructTemplatePosition; - sg_DataTemplate[4].data_property.data = sg_ProductData.m_position; + sg_ProductData.m_position = (void *)&sg_StructTemplatePosition; + sg_DataTemplate[4].data_property.data = sg_ProductData.m_position; sg_DataTemplate[4].data_property.struct_obj_num = TOTAL_PROPERTY_STRUCT_POSITION_COUNT; - sg_DataTemplate[4].data_property.key = "position"; - sg_DataTemplate[4].data_property.type = TYPE_TEMPLATE_JOBJECT; - sg_DataTemplate[4].state = eCHANGED; + sg_DataTemplate[4].data_property.key = "position"; + sg_DataTemplate[4].data_property.type = TYPE_TEMPLATE_JOBJECT; + sg_DataTemplate[4].state = eCHANGED; + +#if TEST_ARRAY + memset(sg_ProductData.m_alias_name, 0, sizeof(sg_ProductData.m_alias_name)); + sg_DataTemplate[5].data_property.data = sg_ProductData.m_alias_name; + sg_DataTemplate[5].data_property.array_info.array_size = MAX_ARRAY_JSON_STR_LEN; + sg_DataTemplate[5].data_property.array_info.array_type = TYPE_TEMPLATE_STRING; + sg_DataTemplate[5].data_property.key = "alias_name"; + sg_DataTemplate[5].data_property.type = TYPE_TEMPLATE_ARRAY; + sg_DataTemplate[5].state = eCHANGED; + memset(sg_ProductData.m_power_consumption, 0, sizeof(sg_ProductData.m_power_consumption)); + sg_DataTemplate[6].data_property.data = sg_ProductData.m_power_consumption; + sg_DataTemplate[6].data_property.array_info.array_size = MAX_ARRAY_JSON_STR_LEN; + sg_DataTemplate[6].data_property.array_info.array_type = TYPE_TEMPLATE_INT; + sg_DataTemplate[6].data_property.key = "power_consumption"; + sg_DataTemplate[6].data_property.type = TYPE_TEMPLATE_ARRAY; + sg_DataTemplate[6].state = eCHANGED; + memset(sg_ProductData.m_recent_status, 0, sizeof(sg_ProductData.m_recent_status)); + sg_DataTemplate[7].data_property.data = sg_ProductData.m_recent_status; + sg_DataTemplate[7].data_property.array_info.array_size = MAX_ARRAY_JSON_STR_LEN; + sg_DataTemplate[7].data_property.array_info.array_type = TYPE_TEMPLATE_JOBJECT; + sg_DataTemplate[7].data_property.key = "recent_status"; + sg_DataTemplate[7].data_property.type = TYPE_TEMPLATE_ARRAY; + sg_DataTemplate[7].state = eCHANGED; +#endif }; /*-----------------data config end -------------------*/ @@ -150,8 +216,11 @@ static DeviceProperty g_propertyEvent_status_report[] = { {.key = "status", .data = &sg_status, .type = TYPE_TEMPLATE_BOOL}, {.key = "message", .data = sg_message, .type = TYPE_TEMPLATE_STRING}, - {.key = "color", .data = sg_ProductData.m_color, .type = TYPE_TEMPLATE_STRINGENUM} -}; +#ifdef COLOR_TYPE_STRINGENUM + {.key = "color", .data = sg_ProductData.m_color, .type = TYPE_TEMPLATE_STRINGENUM}}; +#else + {.key = "color", .data = &sg_ProductData.m_color, .type = TYPE_TEMPLATE_ENUM}}; +#endif static TYPE_DEF_TEMPLATE_FLOAT sg_voltage; static DeviceProperty g_propertyEvent_low_voltage[] = { @@ -257,15 +326,14 @@ static void eventPostCheck(void *client) static TYPE_DEF_TEMPLATE_INT sg_blink_in_period = 5; static DeviceProperty g_actionInput_blink[] = { {.key = "period", .data = &sg_blink_in_period, .type = TYPE_TEMPLATE_INT}}; -static TYPE_DEF_TEMPLATE_BOOL sg_blink_out_result = 0; -static TYPE_DEF_TEMPLATE_INT sg_blink_out_period = 0; -static TYPE_DEF_TEMPLATE_STRINGENUM sg_blink_out_color[5+1]={0}; -static DeviceProperty g_actionOutput_blink[] = { +static TYPE_DEF_TEMPLATE_BOOL sg_blink_out_result = 0; +static TYPE_DEF_TEMPLATE_INT sg_blink_out_period = 0; +static TYPE_DEF_TEMPLATE_STRINGENUM sg_blink_out_color[5 + 1] = {0}; +static DeviceProperty g_actionOutput_blink[] = { {.key = "result", .data = &sg_blink_out_result, .type = TYPE_TEMPLATE_BOOL}, {.key = "period", .data = &sg_blink_out_period, .type = TYPE_TEMPLATE_INT}, - {.key = "color", .data = sg_blink_out_color, .type = TYPE_TEMPLATE_STRINGENUM} -}; + {.key = "color", .data = sg_blink_out_color, .type = TYPE_TEMPLATE_STRINGENUM}}; static DeviceAction g_actions[] = { @@ -312,7 +380,6 @@ static void OnActionCallback(void *pClient, const char *pClientToken, DeviceActi DeviceProperty *pActionOutnput = pAction->pOutput; *(int *)(pActionOutnput[0].data) = 0; // set result *(int *)(pActionOutnput[1].data) = period; - strcpy(pActionOutnput[2].data , sg_ProductData.m_color); IOT_Action_Reply(pClient, pClientToken, sg_data_report_buffer, sg_data_report_buffersize, pAction, &replyPara); } @@ -419,7 +486,7 @@ static int _setup_connect_init_params(TemplateInitParams *initParams) initParams->cert_file = sg_cert_file; initParams->key_file = sg_key_file; #else - initParams->device_secret = sg_devInfo.device_secret; + initParams->device_secret = sg_devInfo.device_secret; #endif initParams->command_timeout = QCLOUD_IOT_MQTT_COMMAND_TIMEOUT; @@ -458,19 +525,159 @@ static int _init_log_upload(TemplateInitParams *init_params) } #endif +#if TEST_ARRAY +static void _refresh_alias_name() +{ + DeviceProperty *p_prop = &(sg_DataTemplate[5].data_property); + char * obj_strs[MAX_SAMPLE_ARRAY_SIZE]; + size_t array_str_sz = p_prop->array_info.array_size; + int ret; + + for (int ii = 0; ii < sg_alias_name_array_size; ++ii) { + obj_strs[ii] = HAL_Malloc(MAX_ARRAY_ITEM_STR_LEN); + ret = HAL_Snprintf(obj_strs[ii], MAX_ARRAY_ITEM_STR_LEN - 1, "\"%s\"", sg_alias_name_array[ii]); + obj_strs[ii][ret] = '\0'; + } + ret = LITE_dt_format_strobj_array(p_prop->data, array_str_sz, obj_strs, sg_alias_name_array_size); + for (int ii = 0; ii < sg_alias_name_array_size; ++ii) { + HAL_Free(obj_strs[ii]); + } + if (ret < 0) + Log_e("Refresh array alias error"); +} + +static void _refresh_power_consumption() +{ + DeviceProperty *p_prop = &(sg_DataTemplate[6].data_property); + int array_type = p_prop->array_info.array_type; + void * array_data = sg_power_consumption_array; + size_t data_size = sg_power_consumption_array_size * sizeof(int); + size_t array_str_sz = p_prop->array_info.array_size; + char * pwr_cons = (char *)(p_prop->data); + pwr_cons[0] = 0; + int ret = LITE_dt_format_primitive_array(pwr_cons, array_str_sz, array_data, data_size, array_type); + if (ret < 0) { + sg_DataTemplate[6].state = eNOCHANGE; + Log_e("Refresh array power consumption error"); + } +} + +static void _refresh_recent_status() +{ + // for struct, convert it to json object string before construct array + char * obj_strs[MAX_SAMPLE_ARRAY_SIZE]; + DeviceProperty *p_prop = &(sg_DataTemplate[7].data_property); + int ret; + size_t array_str_sz = p_prop->array_info.array_size; + + for (int ii = 0; ii < sg_recent_status_array_size; ++ii) { + obj_strs[ii] = HAL_Malloc(MAX_ARRAY_ITEM_STR_LEN); + ret = HAL_Snprintf(obj_strs[ii], MAX_ARRAY_ITEM_STR_LEN - 1, "{\"brightness\":%d, \"temperature\":%f}", + sg_recent_status_array[ii].brightness, sg_recent_status_array[ii].temperature); + obj_strs[ii][ret] = '\0'; + } + + ret = LITE_dt_format_strobj_array(p_prop->data, array_str_sz, obj_strs, sg_recent_status_array_size); + + for (int ii = 0; ii < sg_recent_status_array_size; ++ii) { + HAL_Free(obj_strs[ii]); + } + if (ret < 0) + Log_e("Refresh array recent status error"); +} + +static int _parse_rs_obj(const char *json_str, size_t str_len, void *obj, size_t obj_len) +{ + int ret = 0; + if (obj_len != sizeof(recent_status_t)) { + Log_e("obj_len %zu != %zu", obj_len, sizeof(recent_status_t)); + + return QCLOUD_ERR_FAILURE; + } + recent_status_t *p_rs = (recent_status_t *)obj; + + char temp[256]; + char *p = NULL; + memcpy(temp, json_str, str_len); + temp[str_len] = '\0'; + + if (!(p = LITE_json_value_of("brightness", temp))) + return QCLOUD_ERR_FAILURE; + ret = LITE_get_int32(&(p_rs->brightness), p); + HAL_Free(p); + if (ret < 0) + return ret; + + if (!(p = LITE_json_value_of("temperature", temp))) + return QCLOUD_ERR_FAILURE; + ret = LITE_get_float(&(p_rs->temperature), p); + HAL_Free(p); + + return ret; +} + +/* do with the array information received */ +static int _on_recv_array_msg(DeviceProperty *p_prop) +{ + int res = 0; + if (p_prop->type != JARRAY) + return 0; + const char *key = p_prop->key; + Log_d("key : [%s] data : [%s] ", p_prop->key, p_prop->data); + if (!strcmp(key, "alias_name")) { + sg_DataTemplate[5].state = eCHANGED; + char *alias[MAX_SAMPLE_ARRAY_SIZE]; + for (int i = 0; i < MAX_SAMPLE_ARRAY_SIZE; ++i) { + alias[i] = sg_alias_name_array[i]; + } + res = LITE_dt_parse_str_array(alias, MAX_SAMPLE_ARRAY_SIZE, sizeof(sg_alias_name_array[0]), p_prop->data); + if (res > 0) { + sg_alias_name_array_size = res; + Log_d("string array size : %d", res); + _refresh_alias_name(); + } + } else if (!strcmp(key, "power_consumption")) { + sg_DataTemplate[6].state = eCHANGED; + // The local state has been changed + res = LITE_dt_parse_primitive_array(sg_power_consumption_array, sizeof(sg_power_consumption_array), + p_prop->data, JINT32); + if (res > 0) { + sg_power_consumption_array_size = res; + _refresh_power_consumption(); + } + } else if (!strcmp(key, "recent_status")) { + sg_DataTemplate[7].state = eCHANGED; + res = LITE_dt_parse_obj_array(sg_recent_status_array, MAX_SAMPLE_ARRAY_SIZE, sizeof(recent_status_t), + p_prop->data, _parse_rs_obj); + if (res > 0) { + sg_recent_status_array_size = res; + _refresh_recent_status(); + } + } + if (res < 0) { + HAL_Snprintf(p_prop->data, MAX_ARRAY_JSON_STR_LEN, "[]"); + } + + return 0; +} +#endif + /*control msg from server will trigger this callback*/ static void OnControlMsgCallback(void *pClient, const char *pJsonValueBuffer, uint32_t valueLength, DeviceProperty *pProperty) { + Log_d("recv json buffer %s", pJsonValueBuffer); int i = 0; for (i = 0; i < TOTAL_PROPERTY_COUNT; i++) { - /* handle self defined string/json here. Other properties are dealed in - * _handle_delta()*/ + /* handle self defined string/json here. Other properties are dealed in _handle_delta()*/ if (strcmp(sg_DataTemplate[i].data_property.key, pProperty->key) == 0) { sg_DataTemplate[i].state = eCHANGED; Log_i("Property=%s changed", pProperty->key); sg_control_msg_arrived = true; +#if TEST_ARRAY + _on_recv_array_msg(pProperty); +#endif return; } } @@ -540,7 +747,22 @@ static void deal_down_stream_user_logic(void *client, ProductDataDefine *light) char brightness_bar[] = "||||||||||||||||||||"; int brightness_bar_len = strlen(brightness_bar); - /* light color +/* light color */ +#ifdef COLOR_TYPE_STRINGENUM + if (!strcmp(light->m_color, eCOLOR_STRING_RED)) { + ansi_color = ANSI_COLOR_RED; + ansi_color_name = " RED "; + } else if (!strcmp(light->m_color, eCOLOR_STRING_GREEN)) { + ansi_color = ANSI_COLOR_GREEN; + ansi_color_name = "GREEN"; + } else if (!strcmp(light->m_color, eCOLOR_STRING_BLUE)) { + ansi_color = ANSI_COLOR_BLUE; + ansi_color_name = " BLUE"; + } else { + ansi_color = ANSI_COLOR_YELLOW; + ansi_color_name = "UNKNOWN"; + } +#else switch (light->m_color) { case eCOLOR_RED: ansi_color = ANSI_COLOR_RED; @@ -558,22 +780,8 @@ static void deal_down_stream_user_logic(void *client, ProductDataDefine *light) ansi_color = ANSI_COLOR_YELLOW; ansi_color_name = "UNKNOWN"; break; - }*/ - - if (!strcmp(light->m_color, eCOLOR_STRING_RED)) { - ansi_color = ANSI_COLOR_RED; - ansi_color_name = " RED "; - } else if (!strcmp(light->m_color, eCOLOR_STRING_GREEN)) { - ansi_color = ANSI_COLOR_GREEN; - ansi_color_name = "GREEN"; - } else if (!strcmp(light->m_color, eCOLOR_STRING_BLUE)) { - ansi_color = ANSI_COLOR_BLUE; - ansi_color_name = " BLUE"; - } else { - ansi_color = ANSI_COLOR_YELLOW; - ansi_color_name = "UNKNOWN"; } - +#endif /* light brightness bar */ brightness_bar_len = (light->m_brightness >= 100) ? brightness_bar_len : (int)((light->m_brightness * brightness_bar_len) / 100); @@ -622,7 +830,7 @@ static void cycle_report(Timer *reportTimer) int i; if (expired(reportTimer)) { - for (i = 0; i < TOTAL_PROPERTY_COUNT - DATA_TEMPLATE_WITHOUT_STRUCT; i++) { + for (i = 0; i < TOTAL_PROPERTY_COUNT; i++) { set_property_state(sg_DataTemplate[i].data_property.data, eCHANGED); countdown_ms(reportTimer, 5000); } @@ -632,7 +840,6 @@ static void cycle_report(Timer *reportTimer) /*get local property data, like sensor data*/ static void _refresh_local_property(void) { - // add your local property refresh logic, cycle report for example cycle_report(&sg_reportTimer); } @@ -681,7 +888,7 @@ static int _get_sys_info(void *handle, char *pJsonDoc, size_t sizeOfBuffer) /*self define info*/ DeviceProperty self_info[] = { - {.key = "append_info", .type = TYPE_TEMPLATE_STRING, .data = "your self define info"}, + {.key = "append_info", .type = TYPE_TEMPLATE_STRING, .data = "your self define info"}, {.key = NULL, .data = NULL} // end }; @@ -740,13 +947,12 @@ int main(int argc, char **argv) #endif void *client = IOT_Template_Construct(&init_params, NULL); - if (client != NULL) { - Log_i("Cloud Device Construct Success"); - } else { + if (!client) { Log_e("Cloud Device Construct Failed"); return QCLOUD_ERR_FAILURE; } + Log_i("Cloud Device Construct Success"); #ifdef MULTITHREAD_ENABLED if (QCLOUD_RET_SUCCESS != IOT_Template_Start_Yield_Thread(client)) { Log_e("start template yield thread fail"); @@ -809,8 +1015,10 @@ int main(int argc, char **argv) if (rc == QCLOUD_ERR_MQTT_ATTEMPTING_RECONNECT) { HAL_SleepMs(1000); continue; - } else if (rc != QCLOUD_RET_SUCCESS) { + } + if (rc != QCLOUD_RET_SUCCESS) { Log_e("Exit loop caused of errCode: %d", rc); + break; } /* handle control msg from server */ @@ -841,12 +1049,12 @@ int main(int argc, char **argv) rc = IOT_Template_Report(client, sg_data_report_buffer, sg_data_report_buffersize, OnReportReplyCallback, NULL, QCLOUD_IOT_MQTT_COMMAND_TIMEOUT); if (rc == QCLOUD_RET_SUCCESS) { - Log_i("data template reporte success"); + Log_i("data template reported successfully"); } else { - Log_e("data template reporte failed, err: %d", rc); + Log_e("data template reported error, err: %d", rc); } } else { - Log_e("construct reporte data failed, err: %d", rc); + Log_e("construct reported data failed, err: %d", rc); } } @@ -857,7 +1065,6 @@ int main(int argc, char **argv) } exit: - #ifdef MULTITHREAD_ENABLED IOT_Template_Stop_Yield_Thread(client); #endif diff --git a/samples/scenarized/subdev_sim_sample.c b/samples/scenarized/subdev_sim_sample.c new file mode 100644 index 0000000..4f42b95 --- /dev/null +++ b/samples/scenarized/subdev_sim_sample.c @@ -0,0 +1,210 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "qcloud_iot_export.h" +#include "utils_getopt.h" +#include "lite-utils.h" + +#define LOCAL_SERVER_PORT (20000) +#define LOCAL_HOST "127.0.0.1" + +static char sg_pid[MAX_SIZE_OF_PRODUCT_ID + 1] = "SUB_PID"; +static char sg_dname[MAX_SIZE_OF_DEVICE_NAME + 1] = "SUB_DEVICENAME"; + +static int parse_arguments(int argc, char **argv) +{ + int c; + while ((c = utils_getopt(argc, argv, "p:d:")) != EOF) switch (c) { + case 'p': + Log_d("Get product id %s", utils_optarg); + strncpy(sg_pid, utils_optarg, MAX_SIZE_OF_PRODUCT_ID + 1); + break; + + case 'd': + Log_d("Get device name %s", utils_optarg); + strncpy(sg_dname, utils_optarg, MAX_SIZE_OF_DEVICE_NAME + 1); + break; + + default: + HAL_Printf( + "usage: %s [options]\n" + " [-p ] \n" + " [-d ] \n", + argv[0]); + return -1; + } + return 0; +} + +#define STATUS_MSG_FMT "{\"subdev_type\":\"%s\",\"product_id\":\"%s\",\"device_name\":\"%s\"}" +#define ANSI_COLOR_RED "\x1b[31m" +#define ANSI_COLOR_GREEN "\x1b[32m" +#define ANSI_COLOR_YELLOW "\x1b[33m" +#define ANSI_COLOR_BLUE "\x1b[34m" +#define ANSI_COLOR_RESET "\x1b[0m" + +static int sg_pwr_switch = 0; +static int sg_color = 0; +static int sg_brightness = 0; +static char sg_client_token[64]; + +static void _deal_with_msg(char *msg, int fd) +{ + int i; + char *s_pwr_switch = LITE_json_value_of("params.power_switch", msg); + char *s_color = LITE_json_value_of("params.color", msg); + char *s_brightness = LITE_json_value_of("params.brightness", msg); + char *s_client_token = LITE_json_value_of("clientToken", msg); + char *method = LITE_json_value_of("method", msg); + + if (strcmp(method, "control")) { + Log_d("only control message supported"); + goto exit; + } + + if (!s_client_token) { + Log_e("Failed to get clientToken"); + goto exit; + } + strncpy(sg_client_token, s_client_token, 64); + sg_client_token[63] = 0; + if (s_pwr_switch) + LITE_get_int32(&sg_pwr_switch, s_pwr_switch); + if (s_color) + LITE_get_int32(&sg_color, s_color); + if (s_brightness) + LITE_get_int32(&sg_brightness, s_brightness); + const char *ansi_color = NULL; + const char *ansi_color_name = NULL; + char brightness_bar[] = "||||||||||||||||||||"; + int brightness_bar_len = strlen(brightness_bar); + /* light color */ + switch (sg_color) { + case 0: + ansi_color = ANSI_COLOR_RED; + ansi_color_name = " RED "; + break; + case 1: + ansi_color = ANSI_COLOR_GREEN; + ansi_color_name = "GREEN"; + break; + case 2: + ansi_color = ANSI_COLOR_BLUE; + ansi_color_name = " BLUE"; + break; + default: + ansi_color = ANSI_COLOR_YELLOW; + ansi_color_name = "UNKNOWN"; + break; + } + /* light brightness bar */ + brightness_bar_len = + (sg_brightness >= 100) ? brightness_bar_len : (int)((sg_brightness * brightness_bar_len) / 100); + for (i = brightness_bar_len; i < strlen(brightness_bar); i++) { + brightness_bar[i] = '-'; + } + + if (sg_pwr_switch) { + /* light is on , show with the properties*/ + HAL_Printf("%s[ lighting ]|[color:%s]|[brightness:%s]\n" ANSI_COLOR_RESET, ansi_color, ansi_color_name, + brightness_bar); + } else { + /* light is off */ + HAL_Printf(ANSI_COLOR_YELLOW "[ light is off ]|[color:%s]|[brightness:%s]\n" ANSI_COLOR_RESET, ansi_color_name, + brightness_bar); + } + + // reply control message + char reply_control[256]; + HAL_Snprintf(reply_control, 256, "{\"method\":\"control_reply\",\"clientToken\":\"%s\",\"code\":%d}", + sg_client_token, 0); + size_t write_len = 0; + HAL_TCP_Write(fd, (unsigned char *)reply_control, strlen(reply_control) + 1, 100, &write_len); + +exit: + HAL_Free(method); + HAL_Free(s_client_token); + HAL_Free(s_pwr_switch); + HAL_Free(s_color); + HAL_Free(s_brightness); +} + +int main(int argc, char **argv) +{ + int ret = 0; + int fd = 0; + int i; + size_t write_len = 0; + size_t read_len = 0; + + ret = parse_arguments(argc, argv); + if (ret != QCLOUD_RET_SUCCESS) { + Log_e("parse arguments error, rc = %d", ret); + return ret; + } + + IOT_Log_Set_Level(eLOG_DEBUG); + fd = HAL_TCP_Connect(LOCAL_HOST, LOCAL_SERVER_PORT); + if (fd <= 0) { + Log_e("Failed to connect to gateway"); + goto exit; + } + + char msg[256]; + // set online + { + Log_d("connect gateway OK, subdev is going to be online"); + ret = HAL_Snprintf(msg, 256, STATUS_MSG_FMT, "online", sg_pid, sg_dname); + ret = HAL_TCP_Write(fd, (unsigned char *)msg, ret + 1, 1000, &write_len); + if (ret < 0) { + Log_e("Failed to write"); + goto exit; + } + } + + srand(time(NULL)); + // loop + for (i = 0; i < 100; ++i) { + read_len = 0; + ret = HAL_TCP_Read(fd, (unsigned char *)msg, 256, 1000, &read_len); + if (read_len) { + msg[read_len] = 0; + Log_d("recv %s", msg); + _deal_with_msg(msg, fd); + continue; + } + if (i & 0xf) + continue; +#define REPLY_MSG_FMT \ + "{\"subdev_type\": \"report\",\"product_id\": \"%s\",\"device_name\": \"%s\",\"msg\": " \ + "\"{\"method\":\"%s\",\"clientToken\":\"subdev-%d\",\"timestamp\":%lu,\"power_switch\":%d,\"color\":%d," \ + "\"brightness\":%d}}" + char report_msg[256]; + ret = HAL_Snprintf(report_msg, 256, REPLY_MSG_FMT, sg_pid, sg_dname, "report", rand(), time(NULL), + sg_pwr_switch, sg_color, sg_brightness); + Log_d("Report property %s report len : %d", report_msg, ret); + HAL_TCP_Write(fd, (unsigned char *)report_msg, ret + 1, 1000, &write_len); + } + + // set offline + { + Log_d("set subdev offline"); + ret = HAL_Snprintf(msg, 256, STATUS_MSG_FMT, "offline", sg_pid, sg_dname); + ret = HAL_TCP_Write(fd, (unsigned char *)msg, ret + 1, 1000, &write_len); + if (ret < 0) { + Log_e("Failed to write %d", ret); + goto exit; + } + HAL_SleepMs(5000); + } +exit: + if (fd > 0) + HAL_TCP_Disconnect(fd); + + return ret; +} \ No newline at end of file diff --git a/sdk_src/protocol/mqtt/mqtt_client_common.c b/sdk_src/protocol/mqtt/mqtt_client_common.c index 8ea952d..004ad52 100755 --- a/sdk_src/protocol/mqtt/mqtt_client_common.c +++ b/sdk_src/protocol/mqtt/mqtt_client_common.c @@ -1157,7 +1157,7 @@ static int _handle_publish_packet(Qcloud_IoT_Client *pClient, Timer *timer) if (expired(timer)) { /* send timeout */ - //Log_w("puback timer expired! left:%d, increase a bit", left_ms(timer)); + // Log_w("puback timer expired! left:%d, increase a bit", left_ms(timer)); countdown_ms(timer, 100); } @@ -1193,7 +1193,7 @@ static int _handle_pubrec_packet(Qcloud_IoT_Client *pClient, Timer *timer) if (expired(timer)) { /* send timeout */ - //Log_w("pubrec timer expired! left:%d, increase a bit", left_ms(timer)); + // Log_w("pubrec timer expired! left:%d, increase a bit", left_ms(timer)); countdown_ms(timer, 100); } diff --git a/sdk_src/services/data_template/data_template_action.c b/sdk_src/services/data_template/data_template_action.c index ba53adb..d41653f 100644 --- a/sdk_src/services/data_template/data_template_action.c +++ b/sdk_src/services/data_template/data_template_action.c @@ -43,37 +43,38 @@ static int _parse_action_input(DeviceAction *pAction, char *pInput) // check and copy for (i = 0; i < pAction->input_num; i++) { if (JSTRING == pActionInput[i].type) { - pActionInput[i].data = LITE_json_value_of(pActionInput[i].key, pInput); - if (NULL == pActionInput[i].data) { + char *_p = LITE_json_value_of(pActionInput[i].key, pInput); + if (!_p) { Log_e("action input data [%s] not found!", STRING_PTR_PRINT_SANITY_CHECK(pActionInput[i].key)); return -1; } + strncpy(pActionInput[i].data, _p, pActionInput[i].data_buff_len); + HAL_Free(_p); } else { - temp = LITE_json_value_of(pActionInput[i].key, pInput); + temp = LITE_json_value_of(pActionInput[i].key, pInput); + int ret = 0; if (NULL == temp) { Log_e("action input data [%s] not found!", STRING_PTR_PRINT_SANITY_CHECK(pActionInput[i].key)); return -1; } - if (JINT32 == pActionInput[i].type) { - if (sscanf(temp, "%" SCNi32, (int32_t *)pActionInput[i].data) != 1) { - HAL_Free(temp); - Log_e("parse code failed, errCode: %d", QCLOUD_ERR_JSON_PARSE); - return -1; - } - } else if (JFLOAT == pActionInput[i].type) { - if (sscanf(temp, "%f", (float *)pActionInput[i].data) != 1) { - HAL_Free(temp); - Log_e("parse code failed, errCode: %d", QCLOUD_ERR_JSON_PARSE); - return -1; - } - } else if (JUINT32 == pActionInput[i].type) { - if (sscanf(temp, "%" SCNu32, (uint32_t *)pActionInput[i].data) != 1) { - HAL_Free(temp); - Log_e("parse code failed, errCode: %d", QCLOUD_ERR_JSON_PARSE); - return -1; - } + switch (pActionInput[i].type) { + case JINT32: + ret = sscanf(temp, "%" SCNi32, (int32_t *)pActionInput[i].data); + break; + case JFLOAT: + ret = sscanf(temp, "%f", (float *)pActionInput[i].data); + break; + case JUINT32: + ret = sscanf(temp, "%" SCNu32, (int32_t *)pActionInput[i].data); + break; + default: + ret = QCLOUD_ERR_MQTT_UNKNOWN; + Log_e("type %d not supported", pActionInput[i].type); + break; } HAL_Free(temp); + if (ret < 0) + return -1; } } @@ -110,13 +111,11 @@ static void _handle_action(Qcloud_IoT_Template *pTemplate, List *list, const cha ActionHandler *pActionHandle = (ActionHandler *)node->val; // check action id and call callback - if (0 == strcmp(pActionId, ((DeviceAction *)pActionHandle->action)->pActionId)) { - if (NULL != pActionHandle->callback) { - if (!_parse_action_input(pActionHandle->action, pInput)) { - ((DeviceAction *)pActionHandle->action)->timestamp = timestamp; - pActionHandle->callback(pTemplate, pClientToken, pActionHandle->action); - } - } + if (strcmp(pActionId, ((DeviceAction *)pActionHandle->action)->pActionId) || !(pActionHandle->callback)) + continue; + if (!_parse_action_input(pActionHandle->action, pInput)) { + ((DeviceAction *)pActionHandle->action)->timestamp = timestamp; + pActionHandle->callback(pTemplate, pClientToken, pActionHandle->action); } } list_iterator_destroy(iter); @@ -140,6 +139,8 @@ static void _on_action_handle_callback(void *pClient, MQTTMessage *message, void int timestamp = 0; Log_d("recv:%.*s", (int)message->payload_len, (char *)message->payload); + if (!template_client) + return; // prase_method if (!parse_template_method_type((char *)message->payload, &type_str)) { @@ -176,21 +177,20 @@ static void _on_action_handle_callback(void *pClient, MQTTMessage *message, void } // find action ID in register list and call handle - if (template_client != NULL) - _handle_action(template_client, template_client->inner_data.action_handle_list, client_token, action_id, - timestamp, pInput); + _handle_action(template_client, template_client->inner_data.action_handle_list, client_token, action_id, timestamp, + pInput); EXIT: HAL_Free(type_str); HAL_Free(client_token); HAL_Free(pInput); - return; + HAL_Free(action_id); } int IOT_Action_Init(void *c) { Qcloud_IoT_Template *pTemplate = (Qcloud_IoT_Template *)c; - char topic_name[MAX_SIZE_OF_CLOUD_TOPIC] = {0}; + static char topic_name[MAX_SIZE_OF_CLOUD_TOPIC] = {0}; int size = HAL_Snprintf(topic_name, MAX_SIZE_OF_CLOUD_TOPIC, "$thing/down/action/%s/%s", pTemplate->device_info.product_id, pTemplate->device_info.device_name); @@ -334,18 +334,20 @@ static int _iot_construct_action_json(void *handle, char *jsonBuffer, size_t siz } DeviceProperty *pJsonNode = pAction->pOutput; - for (i = 0; i < pAction->output_num; i++) { - if (pJsonNode != NULL && pJsonNode->key != NULL) { - rc = template_put_json_node(jsonBuffer, remain_size, pJsonNode->key, pJsonNode->data, pJsonNode->type); - - if (rc != QCLOUD_RET_SUCCESS) { - return rc; - } - } else { + if (!pJsonNode) { + Log_e("Output json node is null"); + return QCLOUD_ERR_INVAL; + } + for (i = 0; i < pAction->output_num; i++, pJsonNode++) { + if (pJsonNode->key == NULL) { Log_e("%dth/%d null event property data", i, pAction->output_num); return QCLOUD_ERR_INVAL; } - pJsonNode++; + rc = template_put_json_node(jsonBuffer, remain_size, pJsonNode->key, pJsonNode->data, pJsonNode->type); + + if (rc != QCLOUD_RET_SUCCESS) { + return rc; + } } if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; diff --git a/sdk_src/services/data_template/data_template_client.c b/sdk_src/services/data_template/data_template_client.c index 1739ded..ca3a6d8 100644 --- a/sdk_src/services/data_template/data_template_client.c +++ b/sdk_src/services/data_template/data_template_client.c @@ -242,7 +242,7 @@ static int _template_ConstructControlReply(char *jsonBuffer, size_t sizeOfBuffer return rc; } - if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { + if ((ssize_t)(remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; } @@ -265,26 +265,24 @@ static void _template_mqtt_event_handler(void *pclient, void *context, MQTTEvent Qcloud_IoT_Template *pTemplate = (Qcloud_IoT_Template *)context; MQTTMessage * topic_info = (MQTTMessage *)msg->msg; - if (pTemplate) { - if (pclient != pTemplate->mqtt) { - //Log_d("not template topic event"); - return; - } + if (!pTemplate || pclient != pTemplate->mqtt) { + // Log_d("not template topic event"); + return; } switch (msg->event_type) { case MQTT_EVENT_SUBCRIBE_SUCCESS: - Log_d("template subscribe success, packet-id=%u", packet_id); + Log_d("template subscribed successfully, packet-id=%u", packet_id); if (pTemplate->inner_data.sync_status > 0) pTemplate->inner_data.sync_status = 0; break; case MQTT_EVENT_SUBCRIBE_TIMEOUT: - Log_d("template subscribe wait ack timeout, packet-id=%u", packet_id); + Log_d("template subscribed ack timeout, packet-id=%u", packet_id); if (pTemplate->inner_data.sync_status > 0) pTemplate->inner_data.sync_status = -1; break; case MQTT_EVENT_SUBCRIBE_NACK: - Log_d("template subscribe nack, packet-id=%u", packet_id); + Log_d("template subscribed nack, packet-id=%u", packet_id); if (pTemplate->inner_data.sync_status > 0) pTemplate->inner_data.sync_status = -1; break; @@ -304,7 +302,7 @@ static void _template_mqtt_event_handler(void *pclient, void *context, MQTTEvent } int IOT_Template_JSON_ConstructReportArray(void *pClient, char *jsonBuffer, size_t sizeOfBuffer, uint8_t count, - DeviceProperty *pDeviceProperties[]) + DeviceProperty *pDeviceProperties[]) { POINTER_SANITY_CHECK(jsonBuffer, QCLOUD_ERR_INVAL); POINTER_SANITY_CHECK(pDeviceProperties, QCLOUD_ERR_INVAL); @@ -318,7 +316,7 @@ int IOT_Template_JSON_ConstructReportArray(void *pClient, char *jsonBuffer, size int8_t i; build_empty_json(&(pTemplate->inner_data.token_num), jsonBuffer, pTemplate->device_info.product_id); - if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { + if ((ssize_t)(remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; } @@ -331,18 +329,17 @@ int IOT_Template_JSON_ConstructReportArray(void *pClient, char *jsonBuffer, size for (i = 0; i < count; i++) { DeviceProperty *pJsonNode = pDeviceProperties[i]; - if (pJsonNode != NULL && pJsonNode->key != NULL) { - rc = put_json_node(jsonBuffer, remain_size, pJsonNode); - - if (rc != QCLOUD_RET_SUCCESS) { - return rc; - } - } else { + if (!pJsonNode || !pJsonNode->key) { return QCLOUD_ERR_INVAL; } + rc = put_json_node(jsonBuffer, remain_size, pJsonNode); + + if (rc != QCLOUD_RET_SUCCESS) { + return rc; + } } - if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { + if ((ssize_t)(remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; } rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer) - 1, remain_size, "}}"); @@ -350,7 +347,6 @@ int IOT_Template_JSON_ConstructReportArray(void *pClient, char *jsonBuffer, size if (rc != QCLOUD_RET_SUCCESS) { Log_e("construct datatemplate report array failed: %d", rc); - return rc; } return rc; @@ -453,11 +449,13 @@ int IOT_Template_Report_Sync(void *pClient, char *pJsonDoc, size_t sizeOfBuffer, } if (ACK_ACCEPTED == ack_report) { - rc = QCLOUD_RET_SUCCESS; - } else if (ACK_TIMEOUT == ack_report) { - rc = QCLOUD_ERR_REPORT_TIMEOUT; - } else if (ACK_REJECTED == ack_report) { - rc = QCLOUD_ERR_REPORT_REJECTED; + IOT_FUNC_EXIT_RC(QCLOUD_RET_SUCCESS); + } + if (ACK_TIMEOUT == ack_report) { + IOT_FUNC_EXIT_RC(QCLOUD_ERR_REPORT_TIMEOUT); + } + if (ACK_REJECTED == ack_report) { + IOT_FUNC_EXIT_RC(QCLOUD_ERR_REPORT_REJECTED); } IOT_FUNC_EXIT_RC(rc); @@ -477,7 +475,7 @@ int IOT_Template_JSON_ConstructSysInfo(void *pClient, char *jsonBuffer, size_t s int rc; build_empty_json(&(pTemplate->inner_data.token_num), jsonBuffer, pTemplate->device_info.product_id); - if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { + if ((ssize_t)(remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; } @@ -497,7 +495,7 @@ int IOT_Template_JSON_ConstructSysInfo(void *pClient, char *jsonBuffer, size_t s pJsonNode++; } - if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { + if ((ssize_t)(remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; } rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer) - 1, remain_size, "},"); @@ -507,7 +505,7 @@ int IOT_Template_JSON_ConstructSysInfo(void *pClient, char *jsonBuffer, size_t s return rc; } - if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { + if ((ssize_t)(remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; } @@ -538,7 +536,7 @@ int IOT_Template_JSON_ConstructSysInfo(void *pClient, char *jsonBuffer, size_t s } end: - if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { + if ((ssize_t)(remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; } rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer) - 1, remain_size, "}"); @@ -700,7 +698,7 @@ int IOT_Template_ControlReply(void *pClient, char *pJsonDoc, size_t sizeOfBuffer IOT_FUNC_EXIT_RC(QCLOUD_ERR_MQTT_NO_CONN); } - // if topic $thing/down/property subscribe not success before, subsrcibe again + // if topic $thing/down/property was not subscribed successfully before, then subsrcibe again if (pTemplate->inner_data.sync_status < 0) { rc = subscribe_template_downstream_topic(pTemplate); if (rc < 0) { @@ -717,7 +715,6 @@ int IOT_Template_ControlReply(void *pClient, char *pJsonDoc, size_t sizeOfBuffer RequestParams request_params = DEFAULT_REQUEST_PARAMS; _init_request_params(&request_params, REPLY, NULL, NULL, replyPara->timeout_ms / 1000); - rc = send_template_request(pTemplate, &request_params, pJsonDoc, sizeOfBuffer); IOT_FUNC_EXIT_RC(rc); } @@ -728,8 +725,8 @@ int IOT_Template_Start_Yield_Thread(void *pClient) { POINTER_SANITY_CHECK(pClient, QCLOUD_ERR_INVAL); Qcloud_IoT_Template *pTemplate = (Qcloud_IoT_Template *)pClient; - int rc = IOT_MQTT_StartLoop(pTemplate->mqtt); - if(QCLOUD_RET_SUCCESS == rc) { + int rc = IOT_MQTT_StartLoop(pTemplate->mqtt); + if (QCLOUD_RET_SUCCESS == rc) { pTemplate->yield_thread_running = true; } else { pTemplate->yield_thread_running = false; @@ -741,7 +738,7 @@ int IOT_Template_Start_Yield_Thread(void *pClient) void IOT_Template_Stop_Yield_Thread(void *pClient) { POINTER_SANITY_CHECK_RTN(pClient); - Qcloud_IoT_Template *pTemplate = (Qcloud_IoT_Template *)pClient; + Qcloud_IoT_Template *pTemplate = (Qcloud_IoT_Template *)pClient; IOT_MQTT_StopLoop(pTemplate->mqtt); pTemplate->yield_thread_running = false; @@ -754,7 +751,7 @@ bool IOT_Template_Get_Yield_Status(void *pClient, int *exit_code) { POINTER_SANITY_CHECK(pClient, false); - Qcloud_IoT_Template *pTemplate = (Qcloud_IoT_Template *)pClient; + Qcloud_IoT_Template *pTemplate = (Qcloud_IoT_Template *)pClient; pTemplate->yield_thread_running = IOT_MQTT_GetLoopStatus(pTemplate->mqtt, exit_code); return pTemplate->yield_thread_running; @@ -788,7 +785,6 @@ int IOT_Template_Destroy_Except_MQTT(void *pClient) } #endif - int IOT_Template_Yield(void *pClient, uint32_t timeout_ms) { IOT_FUNC_ENTRY; @@ -856,8 +852,8 @@ void *IOT_Template_Construct(TemplateInitParams *pParams, void *pMqttClient) #endif pTemplate->mqtt = mqtt_client; - pTemplate->event_handle = pParams->event_handle; - pTemplate->usr_control_handle = pParams->usr_control_handle; + pTemplate->event_handle = pParams->event_handle; + pTemplate->usr_control_handle = pParams->usr_control_handle; pTemplate->inner_data.upstream_topic = NULL; pTemplate->inner_data.downstream_topic = NULL; pTemplate->inner_data.token_num = 0; diff --git a/sdk_src/services/data_template/data_template_client_json.c b/sdk_src/services/data_template/data_template_client_json.c index 34ad2e4..bae6211 100644 --- a/sdk_src/services/data_template/data_template_client_json.c +++ b/sdk_src/services/data_template/data_template_client_json.c @@ -61,116 +61,176 @@ void insert_str(char *pDestStr, char *pSourceStr, int pos) static int _direct_update_value(char *value, DeviceProperty *pProperty) { - int rc = QCLOUD_RET_SUCCESS; + int rc = QCLOUD_RET_SUCCESS; uint16_t index = 0; - if (pProperty->type == JBOOL) { - rc = LITE_get_boolean(pProperty->data, value); - } else if (pProperty->type == JINT32) { - rc = LITE_get_int32(pProperty->data, value); - } else if (pProperty->type == JINT16) { - rc = LITE_get_int16(pProperty->data, value); - } else if (pProperty->type == JINT8) { - rc = LITE_get_int8(pProperty->data, value); - } else if (pProperty->type == JUINT32) { - rc = LITE_get_uint32(pProperty->data, value); - } else if (pProperty->type == JUINT16) { - rc = LITE_get_uint16(pProperty->data, value); - } else if (pProperty->type == JUINT8) { - rc = LITE_get_uint8(pProperty->data, value); - } else if (pProperty->type == JFLOAT) { - rc = LITE_get_float(pProperty->data, value); - } else if (pProperty->type == JDOUBLE) { - rc = LITE_get_double(pProperty->data, value); - } else if (pProperty->type == JSTRING) { - rc = LITE_get_string(pProperty->data, value, pProperty->data_buff_len); - } else if (pProperty->type == JOBJECT) { - for (index = 0; index < pProperty->struct_obj_num; index++) { - DeviceProperty *pJsonNode = &((((sDataPoint *)(pProperty->data)) + index)->data_property); - if ((pJsonNode != NULL) && (pJsonNode->key != NULL)) { - update_value_if_key_match(value, pJsonNode); + switch (pProperty->type) { + case JBOOL: + rc = LITE_get_boolean(pProperty->data, value); + break; + case JINT32: + rc = LITE_get_int32(pProperty->data, value); + break; + case JINT16: + rc = LITE_get_int16(pProperty->data, value); + break; + case JINT8: + rc = LITE_get_int8(pProperty->data, value); + break; + case JUINT32: + rc = LITE_get_uint32(pProperty->data, value); + break; + case JUINT16: + rc = LITE_get_uint16(pProperty->data, value); + break; + case JUINT8: + rc = LITE_get_uint8(pProperty->data, value); + break; + case JFLOAT: + rc = LITE_get_float(pProperty->data, value); + break; + case JDOUBLE: + rc = LITE_get_double(pProperty->data, value); + break; + case JSTRING: + Log_d("property data_buff_len %d", pProperty->data_buff_len); + rc = LITE_get_string(pProperty->data, value, pProperty->data_buff_len); + break; + case JOBJECT: + for (index = 0; index < pProperty->struct_obj_num; index++) { + DeviceProperty *pJsonNode = &((((sDataPoint *)(pProperty->data)) + index)->data_property); + if ((pJsonNode != NULL) && (pJsonNode->key != NULL)) { + update_value_if_key_match(value, pJsonNode); + } } - } - } else { - Log_e("pProperty type unknow,%d", pProperty->type); + break; + case JARRAY: + rc = LITE_get_string(pProperty->data, value, pProperty->data_buff_len); + break; + default: + Log_e("Unknown type %d", pProperty->type); + break; } return rc; } -int put_json_node(char *jsonBuffer, size_t sizeOfBuffer, DeviceProperty *pJsonNode) +static int _format_json_buffer(char *jsonBuffer, size_t remain_size, int type, void *pData) { - int rc; - int32_t rc_of_snprintf = 0; - size_t remain_size = 0; - char *pKey = pJsonNode->key; - void *pData = pJsonNode->data; - JsonDataType type = pJsonNode->type; + int rc_of_snprintf = -1; + switch (type) { + case JINT32: + rc_of_snprintf = HAL_Snprintf(jsonBuffer, remain_size, "%" PRIi32 ",", *(int32_t *)(pData)); + break; + case JINT16: + rc_of_snprintf = HAL_Snprintf(jsonBuffer, remain_size, "%" PRIi16 ",", *(int16_t *)(pData)); + break; + case JINT8: + rc_of_snprintf = HAL_Snprintf(jsonBuffer, remain_size, "%" PRIi8 ",", *(int8_t *)(pData)); + break; + case JUINT32: + rc_of_snprintf = HAL_Snprintf(jsonBuffer, remain_size, "%" PRIu32 ",", *(uint32_t *)(pData)); + break; + case JUINT16: + rc_of_snprintf = HAL_Snprintf(jsonBuffer, remain_size, "%" PRIu16 ",", *(uint16_t *)(pData)); + break; + case JUINT8: + rc_of_snprintf = HAL_Snprintf(jsonBuffer, remain_size, "%" PRIu8 ",", *(uint8_t *)(pData)); + break; + case JDOUBLE: + rc_of_snprintf = HAL_Snprintf(jsonBuffer, remain_size, "%f,", *(double *)(pData)); + break; + case JFLOAT: + rc_of_snprintf = HAL_Snprintf(jsonBuffer, remain_size, "%f,", *(float *)(pData)); + break; + case JSTRING: + rc_of_snprintf = HAL_Snprintf(jsonBuffer, remain_size, "\"%s\",", (char *)(pData)); + break; + default: + Log_e("Shouldnt reach here"); + break; + } + + return rc_of_snprintf; +} - if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { +int put_json_node(char *jsonBuffer, size_t sizeOfBuffer, DeviceProperty *pJsonNode) +{ + int rc; + int32_t rc_of_snprintf = 0; + size_t remain_size = 0; + size_t current_size = 0; + char * pKey = pJsonNode->key; + void * pData = pJsonNode->data; + JsonDataType type = pJsonNode->type; + + current_size = strlen(jsonBuffer); + if ((ssize_t)(remain_size = sizeOfBuffer - current_size) <= 1) { return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; } rc_of_snprintf = - HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "\"%s\":", STRING_PTR_PRINT_SANITY_CHECK(pKey)); + HAL_Snprintf(jsonBuffer + current_size, remain_size, "\"%s\":", STRING_PTR_PRINT_SANITY_CHECK(pKey)); rc = check_snprintf_return(rc_of_snprintf, remain_size); if (rc != QCLOUD_RET_SUCCESS) { return rc; } + current_size += rc_of_snprintf; - if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { + if ((ssize_t)(remain_size = sizeOfBuffer - current_size) <= 1) { return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; } if (pData == NULL) { - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "null,"); - } else { - if (type == JINT32) { - rc_of_snprintf = - HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%" PRIi32 ",", *(int32_t *)(pData)); - } else if (type == JINT16) { - rc_of_snprintf = - HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%" PRIi16 ",", *(int16_t *)(pData)); - } else if (type == JINT8) { - rc_of_snprintf = - HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%" PRIi8 ",", *(int8_t *)(pData)); - } else if (type == JUINT32) { - rc_of_snprintf = - HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%" PRIu32 ",", *(uint32_t *)(pData)); - } else if (type == JUINT16) { - rc_of_snprintf = - HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%" PRIu16 ",", *(uint16_t *)(pData)); - } else if (type == JUINT8) { - rc_of_snprintf = - HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%" PRIu8 ",", *(uint8_t *)(pData)); - } else if (type == JDOUBLE) { - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%f,", *(double *)(pData)); - } else if (type == JFLOAT) { - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%f,", *(float *)(pData)); - } else if (type == JBOOL) { + rc_of_snprintf = HAL_Snprintf(jsonBuffer + current_size, remain_size, "null,"); + return check_snprintf_return(rc_of_snprintf, remain_size); + } + + if (type != JBOOL && type != JOBJECT && type != JARRAY) { + return check_snprintf_return(_format_json_buffer(jsonBuffer + current_size, remain_size, type, pData), + remain_size); + } + + switch (type) { + case JBOOL: rc_of_snprintf = - HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%s,", *(bool *)(pData) ? "true" : "false"); - } else if (type == JSTRING) { - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "\"%s\",", (char *)(pData)); - } else if (type == JOBJECT) { - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "{"); - rc = check_snprintf_return(rc_of_snprintf, remain_size); + HAL_Snprintf(jsonBuffer + current_size, remain_size, "%s,", *(bool *)(pData) ? "true" : "false"); + break; + case JOBJECT: + rc_of_snprintf = HAL_Snprintf(jsonBuffer + current_size, remain_size, "{"); + rc = check_snprintf_return(rc_of_snprintf, remain_size); if (rc != QCLOUD_RET_SUCCESS) { return rc; } + current_size += rc_of_snprintf; + remain_size = sizeOfBuffer - current_size; + if ((ssize_t)remain_size <= 1) { + return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; + } uint16_t index = 0; for (index = 0; index < pJsonNode->struct_obj_num; index++) { DeviceProperty *pNode = &((((sDataPoint *)(pJsonNode->data)) + index)->data_property); if ((pNode != NULL) && (pNode->key) != NULL) { - rc = put_json_node(jsonBuffer + strlen(jsonBuffer), remain_size, pNode); + rc = put_json_node(jsonBuffer + current_size, remain_size, pNode); if (rc != QCLOUD_RET_SUCCESS) { return rc; } + current_size = strlen(jsonBuffer); + remain_size = sizeOfBuffer - current_size; + if ((ssize_t)remain_size <= 1) { + return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; + } } } - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer) - 1, remain_size, "},"); - } + rc_of_snprintf = HAL_Snprintf(jsonBuffer + current_size - 1, remain_size, "},"); + break; + case JARRAY: + rc_of_snprintf = HAL_Snprintf(jsonBuffer + current_size, remain_size, "%s,", (char *)(pData)); + break; + default: + Log_e("Type %d is unknown", type); + break; } rc = check_snprintf_return(rc_of_snprintf, remain_size); @@ -182,56 +242,48 @@ int template_put_json_node(char *jsonBuffer, size_t sizeOfBuffer, const char *pK { int rc; int32_t rc_of_snprintf = 0; - size_t remain_size = 0; + size_t current_size = strlen(jsonBuffer); + size_t remain_size = sizeOfBuffer - current_size; - if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { + if ((ssize_t)remain_size <= 1) { return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; } rc_of_snprintf = - HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "\"%s\":", STRING_PTR_PRINT_SANITY_CHECK(pKey)); + HAL_Snprintf(jsonBuffer + current_size, remain_size, "\"%s\":", STRING_PTR_PRINT_SANITY_CHECK(pKey)); rc = check_snprintf_return(rc_of_snprintf, remain_size); if (rc != QCLOUD_RET_SUCCESS) { return rc; } - if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { + current_size += rc_of_snprintf; + if ((ssize_t)(remain_size = sizeOfBuffer - current_size) <= 1) { return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; } if (pData == NULL) { - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "null,"); - } else { - if (type == JINT32) { - rc_of_snprintf = - HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%" PRIi32 ",", *(int32_t *)(pData)); - } else if (type == JINT16) { - rc_of_snprintf = - HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%" PRIi16 ",", *(int16_t *)(pData)); - } else if (type == JINT8) { - rc_of_snprintf = - HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%" PRIi8 ",", *(int8_t *)(pData)); - } else if (type == JUINT32) { - rc_of_snprintf = - HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%" PRIu32 ",", *(uint32_t *)(pData)); - } else if (type == JUINT16) { - rc_of_snprintf = - HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%" PRIu16 ",", *(uint16_t *)(pData)); - } else if (type == JUINT8) { - rc_of_snprintf = - HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%" PRIu8 ",", *(uint8_t *)(pData)); - } else if (type == JDOUBLE) { - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%f,", *(double *)(pData)); - } else if (type == JFLOAT) { - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%f,", *(float *)(pData)); - } else if (type == JBOOL) { - rc_of_snprintf = - HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%u,", *(bool *)(pData) ? 1 : 0); - } else if (type == JSTRING) { - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "\"%s\",", (char *)(pData)); - } else if (type == JOBJECT) { - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "%s,", (char *)(pData)); - } + rc_of_snprintf = HAL_Snprintf(jsonBuffer + current_size, remain_size, "null,"); + return check_snprintf_return(rc_of_snprintf, remain_size); + } + + if (type != JBOOL && type != JOBJECT && type != JARRAY) { + return check_snprintf_return(_format_json_buffer(jsonBuffer + current_size, remain_size, type, pData), + remain_size); + } + + switch (type) { + case JBOOL: + rc_of_snprintf = HAL_Snprintf(jsonBuffer + current_size, remain_size, "%u,", *(bool *)(pData) ? 1 : 0); + break; + case JOBJECT: + rc_of_snprintf = HAL_Snprintf(jsonBuffer + current_size, remain_size, "%s,", (char *)(pData)); + break; + case JARRAY: + rc_of_snprintf = HAL_Snprintf(jsonBuffer + current_size, remain_size, "\"%s\",", (char *)(pData)); + break; + default: + Log_e("Unknown type %d", type); + break; } rc = check_snprintf_return(rc_of_snprintf, remain_size); @@ -319,9 +371,7 @@ bool update_value_if_key_match(char *pJsonDoc, DeviceProperty *pProperty) ret = true; } - if (property_data) { - HAL_Free(property_data); - } + HAL_Free(property_data); return ret; } diff --git a/sdk_src/services/data_template/data_template_client_manager.c b/sdk_src/services/data_template/data_template_client_manager.c index 639ea59..a3e0806 100644 --- a/sdk_src/services/data_template/data_template_client_manager.c +++ b/sdk_src/services/data_template/data_template_client_manager.c @@ -456,11 +456,12 @@ static void _handle_template_reply_callback(Qcloud_IoT_Template *pTemplate, List if (parse_template_get_control(sg_template_cloud_rcv_buf, &control_str)) { Log_d("control data from get_status_reply"); _set_control_clientToken(pClientToken); - if(NULL != pTemplate->usr_control_handle){ //call usr's cb if delta_handle registered,otherwise use _handle_delta - pTemplate->usr_control_handle(pTemplate, control_str, eGET_CTL); - }else{ - _handle_control(pTemplate, control_str); - } + if (NULL != pTemplate->usr_control_handle) { // call usr's cb if delta_handle registered,otherwise + // use _handle_delta + pTemplate->usr_control_handle(pTemplate, control_str, eGET_CTL); + } else { + _handle_control(pTemplate, control_str); + } HAL_Free(control_str); *((ReplyAck *)request->user_context) = ACK_ACCEPTED; // prepare for clear_control } @@ -506,7 +507,7 @@ static void _on_template_downstream_topic_handler(void *pClient, MQTTMessage *me memset(sg_template_cloud_rcv_buf, 0, sizeof(sg_template_cloud_rcv_buf)); memcpy(sg_template_cloud_rcv_buf, message->payload, cloud_rcv_len + 1); sg_template_cloud_rcv_buf[cloud_rcv_len] = '\0'; // jsmn_parse relies on a string - //Log_i("recv:%s", sg_template_cloud_rcv_buf); + // Log_i("recv:%s", sg_template_cloud_rcv_buf); // parse the message type from topic $thing/down/property if (!parse_template_method_type(sg_template_cloud_rcv_buf, &type_str)) { @@ -526,11 +527,13 @@ static void _on_template_downstream_topic_handler(void *pClient, MQTTMessage *me if (parse_template_cmd_control(sg_template_cloud_rcv_buf, &control_str)) { // Log_d("control_str:%s", control_str); _set_control_clientToken(client_token); - if(NULL != template_client->usr_control_handle){ //call usr's cb if delta_handle registered,otherwise use _handle_delta - template_client->usr_control_handle(template_client, control_str, eOPERATION_CTL); - }else{ - _handle_control(template_client, control_str); - } + if (NULL != + template_client + ->usr_control_handle) { // call usr's cb if delta_handle registered,otherwise use _handle_delta + template_client->usr_control_handle(template_client, control_str, eOPERATION_CTL); + } else { + _handle_control(template_client, control_str); + } HAL_Free(control_str); } diff --git a/sdk_src/services/data_template/data_template_event.c b/sdk_src/services/data_template/data_template_event.c index 2d71c04..e06b1d8 100644 --- a/sdk_src/services/data_template/data_template_event.c +++ b/sdk_src/services/data_template/data_template_event.c @@ -227,134 +227,80 @@ static int _iot_construct_event_json(void *handle, char *jsonBuffer, size_t size } // Log_d("event_count:%d, Doc_init:%s",event_count, jsonBuffer); - if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { + if ((ssize_t)(remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; } if (event_count > SINGLE_EVENT) { // mutlti event - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "\"events\":["); + rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, "\"events\":[{"); rc = check_snprintf_return(rc_of_snprintf, remain_size); if (rc != QCLOUD_RET_SUCCESS) { return rc; } - if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { + if ((ssize_t)(remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; } + } - for (i = 0; i < event_count; i++) { - sEvent *pEvent = pEventArry[i]; - if (NULL == pEvent) { - Log_e("%dth/%d null event", i, event_count); - return QCLOUD_ERR_INVAL; - } - - if (0 == pEvent->timestamp) { // no accurate UTC time, set 0 - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, - "{\"eventId\":\"%s\", \"type\":\"%s\", " - "\"timestamp\":0, \"params\":{", - STRING_PTR_PRINT_SANITY_CHECK(pEvent->event_name), - STRING_PTR_PRINT_SANITY_CHECK(pEvent->type)); - } else { // accurate UTC time is second,change to ms - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, - "{\"eventId\":\"%s\", \"type\":\"%s\", " - "\"timestamp\":%u000, \"params\":{", - STRING_PTR_PRINT_SANITY_CHECK(pEvent->event_name), - STRING_PTR_PRINT_SANITY_CHECK(pEvent->type), pEvent->timestamp); - } + for (i = 0; i < event_count; i++) { + sEvent *pEvent = pEventArry[i]; + if (NULL == pEvent) { + Log_e("%dth/%d null event", i, event_count); + return QCLOUD_ERR_INVAL; + } - rc = check_snprintf_return(rc_of_snprintf, remain_size); - if (rc != QCLOUD_RET_SUCCESS) { - return rc; - } + rc_of_snprintf = + HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, + "\"eventId\":\"%s\", \"type\":\"%s\", " + "\"timestamp\":%llu, \"params\":{", + STRING_PTR_PRINT_SANITY_CHECK(pEvent->event_name), STRING_PTR_PRINT_SANITY_CHECK(pEvent->type), + ((uint64_t)(pEvent->timestamp) * 1000)); + rc = check_snprintf_return(rc_of_snprintf, remain_size); + if (rc != QCLOUD_RET_SUCCESS) { + return rc; + } - if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { - return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; - } + if ((ssize_t)(remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { + return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; + } - DeviceProperty *pJsonNode = pEvent->pEventData; - for (j = 0; j < pEvent->eventDataNum; j++) { - if (pJsonNode != NULL && pJsonNode->key != NULL) { - rc = template_put_json_node(jsonBuffer, remain_size, pJsonNode->key, pJsonNode->data, - pJsonNode->type); - - if (rc != QCLOUD_RET_SUCCESS) { - return rc; - } - } else { - Log_e("%dth/%d null event property data", i, pEvent->eventDataNum); - return QCLOUD_ERR_INVAL; - } - pJsonNode++; - } - if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { - return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; + DeviceProperty *pJsonNode = pEvent->pEventData; + for (j = 0; j < pEvent->eventDataNum; j++) { + if (pJsonNode == NULL || pJsonNode->key == NULL) { + Log_e("%dth/%d null event property data", i, pEvent->eventDataNum); + return QCLOUD_ERR_INVAL; } + rc = template_put_json_node(jsonBuffer, remain_size, pJsonNode->key, pJsonNode->data, pJsonNode->type); - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer) - 1, remain_size, "}},"); - - rc = check_snprintf_return(rc_of_snprintf, remain_size); if (rc != QCLOUD_RET_SUCCESS) { return rc; } - - pEvent++; + pJsonNode++; } - - if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { + if ((ssize_t)(remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; } - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer) - 1, remain_size, "]"); - rc = check_snprintf_return(rc_of_snprintf, remain_size); - if (rc != QCLOUD_RET_SUCCESS) { - return rc; - } - - } else { // single - sEvent *pEvent = pEventArry[0]; - if (0 == pEvent->timestamp) { // no accurate UTC time, set 0 - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, - "\"eventId\":\"%s\", \"type\":\"%s\", \"timestamp\":0, \"params\":{", - STRING_PTR_PRINT_SANITY_CHECK(pEvent->event_name), - STRING_PTR_PRINT_SANITY_CHECK(pEvent->type)); - } else { // accurate UTC time is second,change to ms - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer), remain_size, - "\"eventId\":\"%s\", \"type\":\"%s\", " - "\"timestamp\":%u000, \"params\":{", - STRING_PTR_PRINT_SANITY_CHECK(pEvent->event_name), - STRING_PTR_PRINT_SANITY_CHECK(pEvent->type), pEvent->timestamp); + char *end_braces = "}},{"; + if ((event_count > SINGLE_EVENT) && (i == (event_count - 1))) { + end_braces = "}}"; + }else if(event_count == SINGLE_EVENT){ + end_braces = "}"; } + rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer) - 1, remain_size, end_braces); rc = check_snprintf_return(rc_of_snprintf, remain_size); if (rc != QCLOUD_RET_SUCCESS) { return rc; } + } + if (event_count > SINGLE_EVENT) { if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; } - - DeviceProperty *pJsonNode = pEvent->pEventData; - for (i = 0; i < pEvent->eventDataNum; i++) { - if (pJsonNode != NULL && pJsonNode->key != NULL) { - rc = template_put_json_node(jsonBuffer, remain_size, pJsonNode->key, pJsonNode->data, pJsonNode->type); - - if (rc != QCLOUD_RET_SUCCESS) { - return rc; - } - } else { - Log_e("%dth/%d null event property data", i, pEvent->eventDataNum); - return QCLOUD_ERR_INVAL; - } - pJsonNode++; - } - if ((remain_size = sizeOfBuffer - strlen(jsonBuffer)) <= 1) { - return QCLOUD_ERR_JSON_BUFFER_TOO_SMALL; - } - - rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer) - 1, remain_size, "}"); - - rc = check_snprintf_return(rc_of_snprintf, remain_size); + rc_of_snprintf = HAL_Snprintf(jsonBuffer + strlen(jsonBuffer) - 1, remain_size, "}]"); + rc = check_snprintf_return(rc_of_snprintf, remain_size); if (rc != QCLOUD_RET_SUCCESS) { return rc; } diff --git a/sdk_src/services/device_bind/device_bind.c b/sdk_src/services/device_bind/device_bind.c index bd2f0c3..09750bc 100644 --- a/sdk_src/services/device_bind/device_bind.c +++ b/sdk_src/services/device_bind/device_bind.c @@ -56,19 +56,30 @@ int IOT_Unbind_Device_Register(void *callback, void *context) { POINTER_SANITY_CHECK(callback, QCLOUD_ERR_INVAL); OnServiceMessageCallback cb = (OnServiceMessageCallback)callback; - Log_d("cb : %p", cb); return qcloud_service_mqtt_event_register(eSERVICE_UNBIND_DEV, cb, context); } -int IOT_Unbind_Device_Request(void *mqtt_client, const char *productId, const char *deviceName, int timeout_ms) +int IOT_Unbind_Device_ByCloud(void *mqtt_client, void *callback, void *context) +{ + int rc = -1; + POINTER_SANITY_CHECK(mqtt_client, QCLOUD_ERR_INVAL); + Qcloud_IoT_Client *m_client = (Qcloud_IoT_Client *)mqtt_client; + rc = qcloud_service_mqtt_init(m_client->device_info.product_id, m_client->device_info.device_name, mqtt_client); + if (rc < 0) { + Log_e("service init failed: %d", rc); + return rc; + } + return IOT_Unbind_Device_Register(callback, context); +} + +int IOT_Unbind_Device_Request(void *mqtt_client, int timeout_ms) { int rc; char message[256] = {0}; static int sg_unbind_device_index = 0; POINTER_SANITY_CHECK(mqtt_client, QCLOUD_ERR_INVAL); - POINTER_SANITY_CHECK(productId, QCLOUD_ERR_INVAL); - POINTER_SANITY_CHECK(deviceName, QCLOUD_ERR_INVAL); - rc = qcloud_service_mqtt_init(productId, deviceName, mqtt_client); + Qcloud_IoT_Client *m_client = (Qcloud_IoT_Client *)mqtt_client; + rc = qcloud_service_mqtt_init(m_client->device_info.product_id, m_client->device_info.device_name, mqtt_client); if (rc < 0) { Log_e("service init failed: %d", rc); return rc; @@ -82,7 +93,8 @@ int IOT_Unbind_Device_Request(void *mqtt_client, const char *productId, const ch HAL_Snprintf(message, sizeof(message), "{\"method\":\"unbind_device_request\", \"clientToken\":\"%s-%d\", \"deviceName\":\"%s\", " "\"productId\":\"%s\"}", - productId, sg_unbind_device_index, deviceName, productId); + m_client->device_info.product_id, sg_unbind_device_index, m_client->device_info.device_name, + m_client->device_info.product_id); rc = qcloud_service_mqtt_post_msg(mqtt_client, message, QOS0); if (QCLOUD_RET_SUCCESS > rc) { Log_e("service mqtt post msg failed"); diff --git a/sdk_src/services/file_manage/file_manage_client.c b/sdk_src/services/file_manage/file_manage_client.c index c233fb6..f1c1770 100644 --- a/sdk_src/services/file_manage/file_manage_client.c +++ b/sdk_src/services/file_manage/file_manage_client.c @@ -150,7 +150,8 @@ static int _gen_file_manage_ver_info(char *buf, size_t bufLen, uint16_t res_num, } } - pos = (i > 0) ? sizeof(",") : 0; + // Remove the last comma + pos = (i > 0) ? 1 : 0; ret = HAL_Snprintf(buf + strlen(buf) - pos, bufLen - strlen(buf), "]}}"); diff --git a/sdk_src/utils/json_token.c b/sdk_src/utils/json_token.c index 202ba88..655bbb3 100644 --- a/sdk_src/utils/json_token.c +++ b/sdk_src/utils/json_token.c @@ -22,6 +22,8 @@ #include "json_parser.h" #include "lite-utils.h" #include "qcloud_iot_export_error.h" +#include "qcloud_iot_export_method.h" +#include "qcloud_iot_export_log.h" #ifndef SCNi8 #define SCNi8 "hhi" @@ -226,3 +228,170 @@ int LITE_get_string(int8_t *value, char *src, uint16_t max_len) return rc; } + +/* Input string must be like \"aaaa\" or {\"aaaa\":1234} */ +int LITE_dt_format_strobj_array(char *out_res, size_t out_sz, char *items[], size_t item_size) +{ + char * p = out_res; + size_t left_sz = out_sz; + int i, ret = HAL_Snprintf(p, left_sz, "["); + if (ret <= 0) + return QCLOUD_ERR_FAILURE; + p += ret; + left_sz -= ret; + + for (i = 0; i < (int)item_size - 1; ++i) { + ret = HAL_Snprintf(p, left_sz, "%s, ", items[i]); + if (ret <= 0) + return QCLOUD_ERR_FAILURE; + p += ret; + left_sz -= ret; + } + if (i < item_size) { + ret = HAL_Snprintf(p, left_sz, "%s]", items[i]); + } else { + ret = HAL_Snprintf(p, left_sz, "]"); + } + if (ret < 0) + return QCLOUD_ERR_FAILURE; + + return QCLOUD_RET_SUCCESS; +} + +int LITE_dt_format_primitive_array(char *out_res, size_t out_sz, void *data, size_t data_size, int type) +{ + char * p = out_res; + size_t left_sz = out_sz; + int ret, i, item_sz; + + if (JINT32 != type && JFLOAT != type) { + Log_e("type %d not supported", type); + return QCLOUD_ERR_FAILURE; + } + if (0 == data_size) { + ret = HAL_Snprintf(p, left_sz, "[]"); + return ret > 0 ? QCLOUD_RET_SUCCESS : QCLOUD_ERR_FAILURE; + } + + if ((ret = HAL_Snprintf(p, left_sz, "[")) <= 0) + return QCLOUD_ERR_FAILURE; + left_sz -= ret; + p += ret; + + int32_t *int_data = (int32_t *)data; + float * float_data = (float *)data; + item_sz = (JINT32 == type) ? (data_size / sizeof(int32_t)) : (data_size / sizeof(float)); + for (i = 0; i < item_sz - 1; i++) { + if (JINT32 == type) + ret = HAL_Snprintf(p, left_sz, "%d,", int_data[i]); + else + ret = HAL_Snprintf(p, left_sz, "%f,", float_data[i]); + if (ret <= 0) + return QCLOUD_ERR_JSON; + left_sz -= ret; + p += ret; + } + if (JINT32 == type) + ret = HAL_Snprintf(p, left_sz, "%d]", int_data[i]); + else + ret = HAL_Snprintf(p, left_sz, "%f]", float_data[i]); + + return ret > 0 ? QCLOUD_RET_SUCCESS : QCLOUD_ERR_FAILURE; +} + +/* json_str [1,2,3] or [1.23, 3.45] */ +int LITE_dt_parse_primitive_array(void *out_res, size_t out_len, const char *json_str, int type) +{ + if (type != JINT32 && type != JFLOAT) { + Log_e("type %d not supported", type); + return QCLOUD_ERR_FAILURE; + } + + char *pos = NULL; + char *entry = NULL; + int entry_len = 0; + int entry_type = 0; + char old_ch = 0; + int res = 0; + + json_array_for_each_entry((char *)json_str, pos, entry, entry_len, entry_type) + { + if (!entry || entry_type != JSNUMBER) + continue; + backup_json_str_last_char(entry, entry_len, old_ch); + if ((ssize_t)out_len <= 0) + break; + if (type == JINT32) { + int32_t *out_val = (int32_t *)((char *)out_res + (res++) * sizeof(int32_t)); + LITE_get_int32(out_val, entry); + out_len -= sizeof(int32_t); + } else { + float *out_val = (float *)((char *)out_res + (res++) * sizeof(float)); + LITE_get_float(out_val, entry); + out_len -= sizeof(float); + } + restore_json_str_last_char(entry, entry_len, old_ch); + } + + return res; +} + +int LITE_dt_parse_str_array(char *out_res[], size_t arr_len, size_t str_len, const char *json_str) +{ + char *pos = NULL; + char *entry = NULL; + int entry_len = 0; + int entry_type = 0; + char old_ch = 0; + int res = 0; + + json_array_for_each_entry((char *)json_str, pos, entry, entry_len, entry_type) + { + if (!entry || entry_type != JSSTRING) + continue; + backup_json_str_last_char(entry, entry_len, old_ch); + if (res >= arr_len) + break; + + char *val = out_res[res++]; + strncpy(val, entry, str_len - 1); + val[str_len - 1] = '\0'; + restore_json_str_last_char(entry, entry_len, old_ch); + } + + return res; +} + +int LITE_dt_parse_obj_array(void *out_res, size_t out_len, size_t obj_len, const char *json_str, + json_object_parse_t parse_fn) +{ + if (!parse_fn) { + Log_e("Parsing function unavailable"); + return QCLOUD_ERR_FAILURE; + } + char *pos = NULL; + char *entry = NULL; + int entry_len = 0; + int entry_type = 0; + char old_ch = 0; + int res = 0; + + json_array_for_each_entry((char *)json_str, pos, entry, entry_len, entry_type) + { + if (!entry || entry_type != JSOBJECT) + continue; + backup_json_str_last_char(entry, entry_len, old_ch); + if ((ssize_t)out_len <= 0) + break; + + if (0 != parse_fn(entry, entry_len, out_res, obj_len)) { + Log_e("%s parsed error", entry); + } + out_res = (char *)out_res + obj_len; + out_len -= obj_len; + res++; + restore_json_str_last_char(entry, entry_len, old_ch); + } + + return res; +} \ No newline at end of file