From: @CHENG <2393524978@qq.com> Date: Wed, 5 Jun 2024 12:17:45 +0000 (+0800) Subject: gengxin X-Git-Url: http://git.snicker.com.cn/?a=commitdiff_plain;h=47b456b2408f875c0e8c530068fde8e29f167fb9;p=mirrors%2FASR3603_Z5.git gengxin --- diff --git a/2.sdk/sdk/product/craneg_modem/pcac/wsclient/build/wsclient.mak b/2.sdk/sdk/product/craneg_modem/pcac/wsclient/build/wsclient.mak index 46c14c12..3406e6f0 100644 --- a/2.sdk/sdk/product/craneg_modem/pcac/wsclient/build/wsclient.mak +++ b/2.sdk/sdk/product/craneg_modem/pcac/wsclient/build/wsclient.mak @@ -67,8 +67,12 @@ PACKAGE_SRC_FILES = \ web_esp_tls_mbedtls.c \ sim_esp_tls.c \ web_transport_ssl.c \ + mmi_aigc_trans_websocket.c \ + mmi_aigc_chat_websocket_client.c \ mmi_aigc_chat_websocket.c \ - mmi_aigc_trans_websocket.c + mmi_aigc_tts_websocket_client.c \ + mmi_aigc_tts_websocket.c + # These are the tool flags specific to the ci_stub package only. # The environment, target, and group also set flags. diff --git a/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/inc/mmi_aigc_chat_websocket_client.h b/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/inc/mmi_aigc_chat_websocket_client.h new file mode 100644 index 00000000..2bf4d134 --- /dev/null +++ b/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/inc/mmi_aigc_chat_websocket_client.h @@ -0,0 +1,285 @@ +// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// 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. + +#ifndef __MMI_AIGC_CHAT_WEBSOCKET_CLIENT_H__ +#define __MMI_AIGC_CHAT_WEBSOCKET_CLIENT_H__ + + +#include +#include +#include +#include "web_esp_transport_ws.h" +#include "esp_err.h" +#include "web_esp_transport.h" +#include "osa_old_api.h" + +#ifdef __cplusplus +extern "C" { +#endif +typedef unsigned long UINT32; + +#define sim_portTICK_RATE_MS 1000 +#define sim_portTICK_PERIOD_MS 1000 + +//ESP_EVENT_DECLARE_BASE(WEBSOCKET_EVENTS); // declaration of the task events family + +/****************************************** + function declared in here + *****************************************/ +extern char* strdup(const char* str); +extern void free( void *p ); +extern void *malloc( size_t Size ); +extern void *calloc( size_t nItems, size_t itemSize ); +//extern int strtol(const char *p1, char **p2, int base); + +typedef struct sim_esp_websocket_client *sim_esp_websocket_client_handle_t; +typedef void (*sim_esp_event_handler_t)(int32_t event_id, void* event_data); /**< function called when an event is posted to the queue */ + +/** + * @brief Websocket Client events id + */ +typedef enum { + WEBSOCKET_EVENT_ANY = -1, + WEBSOCKET_EVENT_ERROR = 0, /*!< This event occurs when there are any errors during execution */ + WEBSOCKET_EVENT_CONNECTED, /*!< Once the Websocket has been connected to the server, no data exchange has been performed */ + WEBSOCKET_EVENT_DISCONNECTED, /*!< The connection has been disconnected */ + WEBSOCKET_EVENT_DATA, /*!< When receiving data from the server, possibly multiple portions of the packet */ + WEBSOCKET_EVENT_CLOSED, + WEBSOCKET_EVENT_MAX +} sim_esp_websocket_event_id_t; + +/** + * @brief Websocket event data + */ +typedef struct { + char *data_ptr; /*!< Data pointer */ + int data_len; /*!< Data length */ + uint8_t op_code; /*!< Received opcode */ + sim_esp_websocket_client_handle_t client; /*!< sim_esp_websocket_client_handle_t context */ + void *user_context; /*!< user_data context, from sim_esp_websocket_client_config_t user_data */ + int payload_len; /*!< Total payload length, payloads exceeding buffer will be posted through multiple events */ + int payload_offset; /*!< Actual offset for the data associated with this event */ + sim_esp_websocket_event_id_t event; +} sim_esp_websocket_event_data_t; + +typedef struct { + UINT32 task_stack; + int task_prio; + char *uri; + char *p_host; + char host[64]; + char *p_path; + char path[1024]; + char scheme[16]; + char *username; + char *password; + int port; + bool auto_reconnect; + void *user_context; + int network_timeout_ms; + char *subprotocol; +} sim_websocket_config_storage_t; + +typedef enum { + WEBSOCKET_STATE_ERROR = -1, + WEBSOCKET_STATE_UNKNOW = 0, + WEBSOCKET_STATE_INIT, + WEBSOCKET_STATE_CONNECTED, + WEBSOCKET_STATE_WAIT_TIMEOUT, + WEBSOCKET_STATE_CLOSING, +} sim_websocket_client_state_t; + +struct sim_esp_websocket_client { + sim_esp_event_handler_t handler; + wb_esp_transport_list_handle_t transport_list; + wb_esp_transport_handle_t transport; + sim_websocket_config_storage_t *config; + sim_websocket_client_state_t state; + OSAMutexRef lock; + UINT32 keepalive_tick_ms; + UINT32 reconnect_tick_ms; + UINT32 ping_tick_ms; + int wait_timeout_ms; + int auto_reconnect; + char *rx_buffer; + char *tx_buffer; + int buffer_size; + ws_transport_opcodes_t last_opcode; + int payload_len; + int payload_offset; +}; + +/** + * @brief Websocket Client transport + */ +typedef enum { + WEBSOCKET_TRANSPORT_UNKNOWN = 0x0, /*!< Transport unknown */ + WEBSOCKET_TRANSPORT_OVER_TCP, /*!< Transport over tcp */ + WEBSOCKET_TRANSPORT_OVER_SSL, /*!< Transport over ssl */ +} sim_esp_websocket_transport_t; + +/** + * @brief Websocket client setup configuration + */ +typedef struct { + const char *uri; /*!< Websocket URI, the information on the URI can be overrides the other fields below, if any */ + const char *host; /*!< Domain or IP as string */ + int port; /*!< Port to connect, default depend on sim_esp_websocket_transport_t (80 or 443) */ + const char *username; /*!< Using for Http authentication - Not supported for now */ + const char *password; /*!< Using for Http authentication - Not supported for now */ + const char *path; /*!< HTTP Path, if not set, default is `/` */ + bool disable_auto_reconnect; /*!< Disable the automatic reconnect function when disconnected */ + void *user_context; /*!< HTTP user data context */ + int task_prio; /*!< Websocket task priority */ + int task_stack; /*!< Websocket task stack */ + int buffer_size; /*!< Websocket buffer size */ + const char *cert_pem; /*!< SSL Certification, PEM format as string, if the client requires to verify server */ + sim_esp_websocket_transport_t transport; /*!< Websocket transport type, see `sim_esp_websocket_transport_t */ + char *subprotocol; /*!< Websocket subprotocol */ +} sim_esp_websocket_client_config_t; + +/** + * @brief Start a Websocket session + * This function must be the first function to call, + * and it returns a sim_esp_websocket_client_handle_t that you must use as input to other functions in the interface. + * This call MUST have a corresponding call to sim_esp_websocket_client_destroy when the operation is complete. + * + * @param[in] config The configuration + * + * @return + * - `sim_esp_websocket_client_handle_t` + * - NULL if any errors + */ +sim_esp_websocket_client_handle_t chat_websocket_client_init(const sim_esp_websocket_client_config_t *config); + +/** + * @brief Set URL for client, when performing this behavior, the options in the URL will replace the old ones + * Must stop the WebSocket client before set URI if the client has been connected + * + * @param[in] client The client + * @param[in] uri The uri + * + * @return esp_err_t + */ +esp_err_t chat_websocket_client_set_uri(sim_esp_websocket_client_handle_t client, const char *uri); + +/** + * @brief Open the WebSocket connection + * + * @param[in] client The client + * + * @return esp_err_t + */ +esp_err_t chat_websocket_client_start(sim_esp_websocket_client_handle_t client); + +/** + * @brief Close the WebSocket connection + * + * @param[in] client The client + * + * @return esp_err_t + */ +esp_err_t chat_websocket_client_stop(sim_esp_websocket_client_handle_t client); + +/** + * @brief Destroy the WebSocket connection and free all resources. + * This function must be the last function to call for an session. + * It is the opposite of the sim_esp_websocket_client_init function and must be called with the same handle as input that a esp_websocket_client_init call returned. + * This might close all connections this handle has used. + * + * @param[in] client The client + * + * @return esp_err_t + */ +esp_err_t chat_websocket_client_destroy(sim_esp_websocket_client_handle_t client); + +/** + * @brief Generic write data to the WebSocket connection; defaults to binary send + * + * @param[in] client The client + * @param[in] data The data + * @param[in] len The length + * @param[in] timeout Write data timeout + * + * @return + * - Number of data was sent + * - (-1) if any errors + */ +int chat_websocket_client_send(sim_esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout); + +/** + * @brief Write binary data to the WebSocket connection (data send with WS OPCODE=02, i.e. binary) + * + * @param[in] client The client + * @param[in] data The data + * @param[in] len The length + * @param[in] timeout Write data timeout + * + * @return + * - Number of data was sent + * - (-1) if any errors + */ +int chat_websocket_client_send_bin(sim_esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout); + +static int chat_websocket_client_send_close(sim_esp_websocket_client_handle_t client, int code, const char *additional_data, int total_len, TickType_t timeout); + +static int chat_websocket_client_close_with_optional_body(sim_esp_websocket_client_handle_t client, bool send_body, int code, const char *data, int len, TickType_t timeout); + +int chat_websocket_client_close_with_code(sim_esp_websocket_client_handle_t client, int code, const char *data, int len, TickType_t timeout); + +int chat_websocket_client_close(sim_esp_websocket_client_handle_t client, TickType_t timeout); + +/** + * @brief Write textual data to the WebSocket connection (data send with WS OPCODE=01, i.e. text) + * + * @param[in] client The client + * @param[in] data The data + * @param[in] len The length + * @param[in] timeout Write data timeout + * + * @return + * - Number of data was sent + * - (-1) if any errors + */ +int chat_websocket_client_send_text(sim_esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout); + +/** + * @brief Check the WebSocket connection status + * + * @param[in] client The client handle + * + * @return + * - true + * - false + */ +bool chat_websocket_client_is_connected(sim_esp_websocket_client_handle_t client); + +/** + * @brief Register the Websocket Events + * + * @param client The client handle + * @param event The event id + * @param event_handler The callback function + * @param event_handler_arg User context + * @return esp_err_t + */ +esp_err_t chat_websocket_register_events(sim_esp_websocket_client_handle_t client, + sim_esp_event_handler_t event_handler); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/inc/mmi_aigc_tts_websocket_client.h b/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/inc/mmi_aigc_tts_websocket_client.h new file mode 100644 index 00000000..900773bb --- /dev/null +++ b/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/inc/mmi_aigc_tts_websocket_client.h @@ -0,0 +1,285 @@ +// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// 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. + +#ifndef __MMI_AIGC_TTS_WEBSOCKET_CLIENT_H__ +#define __MMI_AIGC_TTS_WEBSOCKET_CLIENT_H__ + + +#include +#include +#include +#include "web_esp_transport_ws.h" +#include "esp_err.h" +#include "web_esp_transport.h" +#include "osa_old_api.h" + +#ifdef __cplusplus +extern "C" { +#endif +typedef unsigned long UINT32; + +#define sim_portTICK_RATE_MS 1000 +#define sim_portTICK_PERIOD_MS 1000 + +//ESP_EVENT_DECLARE_BASE(WEBSOCKET_EVENTS); // declaration of the task events family + +/****************************************** + function declared in here + *****************************************/ +extern char* strdup(const char* str); +extern void free( void *p ); +extern void *malloc( size_t Size ); +extern void *calloc( size_t nItems, size_t itemSize ); +//extern int strtol(const char *p1, char **p2, int base); + +typedef struct sim_esp_websocket_client *sim_esp_websocket_client_handle_t; +typedef void (*sim_esp_event_handler_t)(int32_t event_id, void* event_data); /**< function called when an event is posted to the queue */ + +/** + * @brief Websocket Client events id + */ +typedef enum { + WEBSOCKET_EVENT_ANY = -1, + WEBSOCKET_EVENT_ERROR = 0, /*!< This event occurs when there are any errors during execution */ + WEBSOCKET_EVENT_CONNECTED, /*!< Once the Websocket has been connected to the server, no data exchange has been performed */ + WEBSOCKET_EVENT_DISCONNECTED, /*!< The connection has been disconnected */ + WEBSOCKET_EVENT_DATA, /*!< When receiving data from the server, possibly multiple portions of the packet */ + WEBSOCKET_EVENT_CLOSED, + WEBSOCKET_EVENT_MAX +} sim_esp_websocket_event_id_t; + +/** + * @brief Websocket event data + */ +typedef struct { + char *data_ptr; /*!< Data pointer */ + int data_len; /*!< Data length */ + uint8_t op_code; /*!< Received opcode */ + sim_esp_websocket_client_handle_t client; /*!< sim_esp_websocket_client_handle_t context */ + void *user_context; /*!< user_data context, from sim_esp_websocket_client_config_t user_data */ + int payload_len; /*!< Total payload length, payloads exceeding buffer will be posted through multiple events */ + int payload_offset; /*!< Actual offset for the data associated with this event */ + sim_esp_websocket_event_id_t event; +} sim_esp_websocket_event_data_t; + +typedef struct { + UINT32 task_stack; + int task_prio; + char *uri; + char *p_host; + char host[64]; + char *p_path; + char path[1024]; + char scheme[16]; + char *username; + char *password; + int port; + bool auto_reconnect; + void *user_context; + int network_timeout_ms; + char *subprotocol; +} sim_websocket_config_storage_t; + +typedef enum { + WEBSOCKET_STATE_ERROR = -1, + WEBSOCKET_STATE_UNKNOW = 0, + WEBSOCKET_STATE_INIT, + WEBSOCKET_STATE_CONNECTED, + WEBSOCKET_STATE_WAIT_TIMEOUT, + WEBSOCKET_STATE_CLOSING, +} sim_websocket_client_state_t; + +struct sim_esp_websocket_client { + sim_esp_event_handler_t handler; + wb_esp_transport_list_handle_t transport_list; + wb_esp_transport_handle_t transport; + sim_websocket_config_storage_t *config; + sim_websocket_client_state_t state; + OSAMutexRef lock; + UINT32 keepalive_tick_ms; + UINT32 reconnect_tick_ms; + UINT32 ping_tick_ms; + int wait_timeout_ms; + int auto_reconnect; + char *rx_buffer; + char *tx_buffer; + int buffer_size; + ws_transport_opcodes_t last_opcode; + int payload_len; + int payload_offset; +}; + +/** + * @brief Websocket Client transport + */ +typedef enum { + WEBSOCKET_TRANSPORT_UNKNOWN = 0x0, /*!< Transport unknown */ + WEBSOCKET_TRANSPORT_OVER_TCP, /*!< Transport over tcp */ + WEBSOCKET_TRANSPORT_OVER_SSL, /*!< Transport over ssl */ +} sim_esp_websocket_transport_t; + +/** + * @brief Websocket client setup configuration + */ +typedef struct { + const char *uri; /*!< Websocket URI, the information on the URI can be overrides the other fields below, if any */ + const char *host; /*!< Domain or IP as string */ + int port; /*!< Port to connect, default depend on sim_esp_websocket_transport_t (80 or 443) */ + const char *username; /*!< Using for Http authentication - Not supported for now */ + const char *password; /*!< Using for Http authentication - Not supported for now */ + const char *path; /*!< HTTP Path, if not set, default is `/` */ + bool disable_auto_reconnect; /*!< Disable the automatic reconnect function when disconnected */ + void *user_context; /*!< HTTP user data context */ + int task_prio; /*!< Websocket task priority */ + int task_stack; /*!< Websocket task stack */ + int buffer_size; /*!< Websocket buffer size */ + const char *cert_pem; /*!< SSL Certification, PEM format as string, if the client requires to verify server */ + sim_esp_websocket_transport_t transport; /*!< Websocket transport type, see `sim_esp_websocket_transport_t */ + char *subprotocol; /*!< Websocket subprotocol */ +} sim_esp_websocket_client_config_t; + +/** + * @brief Start a Websocket session + * This function must be the first function to call, + * and it returns a sim_esp_websocket_client_handle_t that you must use as input to other functions in the interface. + * This call MUST have a corresponding call to sim_esp_websocket_client_destroy when the operation is complete. + * + * @param[in] config The configuration + * + * @return + * - `sim_esp_websocket_client_handle_t` + * - NULL if any errors + */ +sim_esp_websocket_client_handle_t tts_websocket_client_init(const sim_esp_websocket_client_config_t *config); + +/** + * @brief Set URL for client, when performing this behavior, the options in the URL will replace the old ones + * Must stop the WebSocket client before set URI if the client has been connected + * + * @param[in] client The client + * @param[in] uri The uri + * + * @return esp_err_t + */ +esp_err_t tts_websocket_client_set_uri(sim_esp_websocket_client_handle_t client, const char *uri); + +/** + * @brief Open the WebSocket connection + * + * @param[in] client The client + * + * @return esp_err_t + */ +esp_err_t tts_websocket_client_start(sim_esp_websocket_client_handle_t client); + +/** + * @brief Close the WebSocket connection + * + * @param[in] client The client + * + * @return esp_err_t + */ +esp_err_t tts_websocket_client_stop(sim_esp_websocket_client_handle_t client); + +/** + * @brief Destroy the WebSocket connection and free all resources. + * This function must be the last function to call for an session. + * It is the opposite of the sim_esp_websocket_client_init function and must be called with the same handle as input that a esp_websocket_client_init call returned. + * This might close all connections this handle has used. + * + * @param[in] client The client + * + * @return esp_err_t + */ +esp_err_t tts_websocket_client_destroy(sim_esp_websocket_client_handle_t client); + +/** + * @brief Generic write data to the WebSocket connection; defaults to binary send + * + * @param[in] client The client + * @param[in] data The data + * @param[in] len The length + * @param[in] timeout Write data timeout + * + * @return + * - Number of data was sent + * - (-1) if any errors + */ +int tts_websocket_client_send(sim_esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout); + +/** + * @brief Write binary data to the WebSocket connection (data send with WS OPCODE=02, i.e. binary) + * + * @param[in] client The client + * @param[in] data The data + * @param[in] len The length + * @param[in] timeout Write data timeout + * + * @return + * - Number of data was sent + * - (-1) if any errors + */ +int tts_websocket_client_send_bin(sim_esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout); + +static int tts_websocket_client_send_close(sim_esp_websocket_client_handle_t client, int code, const char *additional_data, int total_len, TickType_t timeout); + +static int tts_websocket_client_close_with_optional_body(sim_esp_websocket_client_handle_t client, bool send_body, int code, const char *data, int len, TickType_t timeout); + +int tts_websocket_client_close_with_code(sim_esp_websocket_client_handle_t client, int code, const char *data, int len, TickType_t timeout); + +int tts_websocket_client_close(sim_esp_websocket_client_handle_t client, TickType_t timeout); + +/** + * @brief Write textual data to the WebSocket connection (data send with WS OPCODE=01, i.e. text) + * + * @param[in] client The client + * @param[in] data The data + * @param[in] len The length + * @param[in] timeout Write data timeout + * + * @return + * - Number of data was sent + * - (-1) if any errors + */ +int tts_websocket_client_send_text(sim_esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout); + +/** + * @brief Check the WebSocket connection status + * + * @param[in] client The client handle + * + * @return + * - true + * - false + */ +bool tts_websocket_client_is_connected(sim_esp_websocket_client_handle_t client); + +/** + * @brief Register the Websocket Events + * + * @param client The client handle + * @param event The event id + * @param event_handler The callback function + * @param event_handler_arg User context + * @return esp_err_t + */ +esp_err_t tts_websocket_register_events(sim_esp_websocket_client_handle_t client, + sim_esp_event_handler_t event_handler); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/inc/mmi_aigc_websocket.h b/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/inc/mmi_aigc_websocket.h index e871cf28..79dcc9b6 100644 --- a/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/inc/mmi_aigc_websocket.h +++ b/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/inc/mmi_aigc_websocket.h @@ -21,8 +21,6 @@ extern "C" { #endif -#define MMI_AIGC_TRANS_WEBSOCKET_URI "ws://121.40.67.151:9099/asr/ws" - int mmi_aigc_trans_websocket_send_text_data(char * data_ptr, int data_len); int mmi_aigc_trans_websocket_send_binary_data(char * data_ptr, int data_len); @@ -30,16 +28,17 @@ void mmi_aigc_trans_websocket_app_start(void); void mmi_aigc_trans_websocket_app_stop(void); - -#define MMI_AIGC_CHAT_WEBSOCKET_URI "ws://121.40.67.151:9099/chat/ws" - - int mmi_aigc_chat_websocket_send_text_data(char * data_ptr, int data_len); int mmi_aigc_chat_websocket_send_binary_data(char * data_ptr, int data_len); void mmi_aigc_chat_websocket_app_start(void); void mmi_aigc_chat_websocket_app_stop(void); +int mmi_aigc_tts_websocket_send_text_data(char * data_ptr, int data_len); +int mmi_aigc_tts_websocket_send_binary_data(char * data_ptr, int data_len); +void mmi_aigc_tts_websocket_app_start(void); +void mmi_aigc_tts_websocket_app_stop(void); + #endif diff --git a/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/src/mmi_aigc_chat_websocket.c b/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/src/mmi_aigc_chat_websocket.c index 34d02e8f..f353cffd 100644 --- a/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/src/mmi_aigc_chat_websocket.c +++ b/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/src/mmi_aigc_chat_websocket.c @@ -4,8 +4,10 @@ #if 1 // def __MMI_CUSTOM_AIGC_SUPPORT__ #include "uart.h" -#include "sim_esp_websocket_client.h" -#include "Mmi_aigc_chat_websocket.h" +#include "mmi_aigc_chat_websocket_client.h" +#include "mmi_aigc_websocket.h" + +#define MMI_AIGC_CHAT_WEBSOCKET_URI "ws://121.40.67.151:9099/chat/ws" static sim_esp_websocket_client_handle_t s_aigc_chat_ws_client_p = NULL; @@ -18,7 +20,7 @@ int mmi_aigc_chat_websocket_send_text_data(char * data_ptr, int data_len) int ret = 0; AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: \n", __FUNCTION__, __LINE__); - ret = sim_esp_websocket_client_send_text(s_aigc_chat_ws_client_p, data_ptr, data_len, portMAX_DELAY); + ret = chat_websocket_client_send_text(s_aigc_chat_ws_client_p, data_ptr, data_len, portMAX_DELAY); if (0 > ret) { AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: \n", __FUNCTION__, __LINE__); @@ -32,7 +34,7 @@ int mmi_aigc_chat_websocket_send_binary_data(char * data_ptr, int data_len) int ret = 0; AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: \n", __FUNCTION__, __LINE__); - ret = sim_esp_websocket_client_send_bin(s_aigc_chat_ws_client_p, data_ptr, data_len, portMAX_DELAY); + ret = chat_websocket_client_send_bin(s_aigc_chat_ws_client_p, data_ptr, data_len, portMAX_DELAY); if (0 > ret) { AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: \n", __FUNCTION__, __LINE__); @@ -109,16 +111,16 @@ void mmi_aigc_chat_websocket_app_start(void) AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: url=%s\n", __FUNCTION__, __LINE__, websocket_cfg.uri); - s_aigc_chat_ws_client_p = sim_esp_websocket_client_init(&websocket_cfg); + s_aigc_chat_ws_client_p = chat_websocket_client_init(&websocket_cfg); if(NULL == s_aigc_chat_ws_client_p) { AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: websocket client init fail\n", __FUNCTION__, __LINE__); return; } - sim_esp_websocket_register_events(s_aigc_chat_ws_client_p, mmi_aigc_chat_websocket_event_handler); + chat_websocket_register_events(s_aigc_chat_ws_client_p, mmi_aigc_chat_websocket_event_handler); - esp_err_t err_no = sim_esp_websocket_client_start(s_aigc_chat_ws_client_p); + esp_err_t err_no = chat_websocket_client_start(s_aigc_chat_ws_client_p); if(ESP_OK != err_no) { AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: websocket client start fail,%d\n", __FUNCTION__, __LINE__, err_no); @@ -134,7 +136,7 @@ void mmi_aigc_chat_websocket_app_stop(void) { AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: \n", __FUNCTION__, __LINE__); - sim_esp_websocket_client_destroy(s_aigc_chat_ws_client_p); + chat_websocket_client_destroy(s_aigc_chat_ws_client_p); } #endif diff --git a/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/src/mmi_aigc_chat_websocket_client.c b/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/src/mmi_aigc_chat_websocket_client.c new file mode 100644 index 00000000..f12ebaf2 --- /dev/null +++ b/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/src/mmi_aigc_chat_websocket_client.c @@ -0,0 +1,826 @@ +// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// 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 "mmi_aigc_chat_websocket_client.h" +#include "web_esp_transport.h" +#include "web_esp_transport_tcp.h" +#include "web_esp_transport_ssl.h" +#include "web_esp_transport_ws.h" + +/* using uri parser */ +#include "web_http_parser.h" +#include "queue.h" +#include "esp_log.h" +#include "sockets.h" +#include "osa.h" + +static int task_run = 0; +static OSTaskRef chat_websocket_client_ref = NULL; +static void * chat_websocket_client_stack_ptr = NULL; + +#define websocket_version "asr_websocket_v1.10_20210926" +#define WEBSOCKET_TCP_DEFAULT_PORT (80) +#define WEBSOCKET_SSL_DEFAULT_PORT (443) +#define WEBSOCKET_BUFFER_SIZE_BYTE (8*1024) +#define WEBSOCKET_RECONNECT_TIMEOUT_MS (5*60*1000) +#define WEBSOCKET_TASK_PRIORITY (90) +#define WEBSOCKET_TASK_STACK (8*1024) +#define WEBSOCKET_NETWORK_TIMEOUT_MS (10*1000) +#define WEBSOCKET_PING_TIMEOUT_MS (15*60*1000) +#define WEBSOCKET_EVENT_QUEUE_SIZE (1) + +#define ESP_WS_CLIENT_MEM_CHECK(a, action) if (!(a)) { \ + ESP_LOGE("%s:%d (%s): %s", __FILE__, __LINE__, __FUNCTION__, "Memory exhausted"); \ + action; \ + } +#define ESP_EVENT_DECLARE_BASE(id) extern esp_event_base_t id +#define ESP_EVENT_DEFINE_BASE(id) esp_event_base_t id = #id + +static int chat_websocket_client_send_with_opcode(sim_esp_websocket_client_handle_t client, ws_transport_opcodes_t opcode, const char *data, int len, TickType_t timeout); +static void asr_ws_loop_run_task(void *pv); +extern OSMsgQRef asr_ws_msgQ_ref; +extern int esp_transport_ws_poll_connection_closed(wb_esp_transport_handle_t t, int timeout_ms); + +static UINT32 _tick_get_ms(void) +{ + return (5*OSAGetTicks()); //get ms + //return esp_timer_get_time()/1000; +} + +static int chat_websocket_client_dispatch_event(sim_esp_websocket_client_handle_t client, + sim_esp_websocket_event_id_t event, + const char *data, + int data_len) +{ + if (client->handler) { + if (NULL == data) { + client->handler(event, NULL); + } else { + sim_esp_websocket_event_data_t *event_data = NULL; + event_data = malloc(sizeof(sim_esp_websocket_event_data_t)); + if (NULL == event_data) { + ESP_LOGE("%s, event_data malloc failed", __FUNCTION__); + return -1; + } else { + memset(event_data, '\0', sizeof(sim_esp_websocket_event_data_t)); + event_data->data_ptr = malloc(data_len); + if (event_data->data_ptr) { + memset(event_data->data_ptr, 0, data_len); + memcpy(event_data->data_ptr, data, data_len); + } + } + + event_data->data_len = data_len; + event_data->op_code = client->last_opcode; + //shenxj+++ + event_data->payload_len = client->payload_len; + event_data->payload_offset = client->payload_offset; + //shenxj--- + + client->handler(event, event_data); + } + } + + return 0; +} + +static int chat_websocket_client_abort_connection(sim_esp_websocket_client_handle_t client) +{ + int ret = 0; + + wb_esp_transport_close(client->transport); + client->wait_timeout_ms = WEBSOCKET_RECONNECT_TIMEOUT_MS; + client->reconnect_tick_ms = _tick_get_ms(); + client->state = WEBSOCKET_STATE_WAIT_TIMEOUT; + ESP_LOGI("Reconnect after %d ms", client->wait_timeout_ms); + ret = chat_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_DISCONNECTED, NULL, 0); + ESP_LOGD("%s, client abort connection ret is %d", __FUNCTION__, ret); + return ret; +} + +static esp_err_t chat_websocket_client_set_config(sim_esp_websocket_client_handle_t client, const sim_esp_websocket_client_config_t *config) +{ + sim_websocket_config_storage_t *cfg = client->config; + cfg->task_prio = config->task_prio; + if (cfg->task_prio <= 0) { + cfg->task_prio = WEBSOCKET_TASK_PRIORITY; + } + + cfg->task_stack = config->task_stack; + if (cfg->task_stack == 0) { + cfg->task_stack = WEBSOCKET_TASK_STACK; + } + + if (config->host) { + cfg->p_host = strdup(config->host); + memcpy(cfg->host, cfg->p_host, strlen(config->host)); + ESP_WS_CLIENT_MEM_CHECK(cfg->host, return ESP_ERR_NO_MEM); + } + + if (config->port) { + cfg->port = config->port; + } + + if (config->username) { + //free(cfg->username); + cfg->username = strdup(config->username); + ESP_WS_CLIENT_MEM_CHECK(cfg->username, return ESP_ERR_NO_MEM); + } + + if (config->password) { + //free(cfg->password); + cfg->password = strdup(config->password); + ESP_WS_CLIENT_MEM_CHECK(cfg->password, return ESP_ERR_NO_MEM); + } + + if (config->uri) { + //free(cfg->uri); + cfg->uri = strdup(config->uri); + ESP_WS_CLIENT_MEM_CHECK(cfg->uri, return ESP_ERR_NO_MEM); + } + if (config->path) { + //free(cfg->path); + cfg->p_path = strdup(config->path); + memcpy(cfg->path, cfg->p_path, strlen(config->path)); + ESP_WS_CLIENT_MEM_CHECK(cfg->path, return ESP_ERR_NO_MEM); + } + if (config->subprotocol) { + //free(cfg->subprotocol); + cfg->subprotocol = strdup(config->subprotocol); + ESP_WS_CLIENT_MEM_CHECK(cfg->subprotocol, return ESP_ERR_NO_MEM); + } + + cfg->network_timeout_ms = WEBSOCKET_NETWORK_TIMEOUT_MS; + cfg->user_context = config->user_context; + cfg->auto_reconnect = true; + if (config->disable_auto_reconnect) { + cfg->auto_reconnect = false; + } + + + return ESP_OK; +} + +static esp_err_t chat_websocket_client_destroy_config(sim_esp_websocket_client_handle_t client) +{ + if (client == NULL) { + return ESP_ERR_INVALID_ARG; + } + sim_websocket_config_storage_t *cfg = client->config; + if (client->config == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (NULL != cfg->p_host) { + free(cfg->p_host); + cfg->p_host = NULL; + } + if (NULL != cfg->uri) { + free(cfg->uri); + cfg->uri = NULL; + } + if (NULL != cfg->p_path) { + free(cfg->p_path); + cfg->p_path = NULL; + } + if (NULL != cfg->username) { + free(cfg->username); + cfg->username = NULL; + } + if (NULL != cfg->password) { + free(cfg->password); + cfg->password = NULL; + } + if (NULL != cfg->subprotocol) { + free(cfg->subprotocol); + cfg->subprotocol = NULL; + } + + memset(cfg, 0, sizeof(sim_websocket_config_storage_t)); + free(client->config); + client->config = NULL; + return ESP_OK; +} + +static void set_websocket_transport_optional_settings(sim_esp_websocket_client_handle_t client, wb_esp_transport_handle_t trans) +{ + if (trans && client->config->path) { + wb_esp_transport_ws_set_path(trans, client->config->path); + } + if (trans && client->config->subprotocol) { + wb_esp_transport_ws_set_subprotocol(trans, client->config->subprotocol); + } +} + +sim_esp_websocket_client_handle_t chat_websocket_client_init(const sim_esp_websocket_client_config_t *config) +{ + wb_esp_transport_handle_t tcp = NULL; + wb_esp_transport_handle_t ws = NULL; + wb_esp_transport_handle_t ssl = NULL; + wb_esp_transport_handle_t wss = NULL; + int buffer_size = 0; + OS_STATUS status; + + sim_esp_websocket_client_handle_t client = (sim_esp_websocket_client_handle_t)malloc(1*sizeof(struct sim_esp_websocket_client)); + ESP_WS_CLIENT_MEM_CHECK(client, return NULL); + memset(client, '\0', sizeof(struct sim_esp_websocket_client)); + client->state = WEBSOCKET_STATE_UNKNOW; + + status = OSAMutexCreate(&client->lock, OSA_FIFO); + if (OS_SUCCESS != status) { + ESP_LOGE("Failed to create client lock"); + } + + client->config = calloc(1, sizeof(sim_websocket_config_storage_t)); + ESP_WS_CLIENT_MEM_CHECK(client->config, goto _websocket_init_fail); + + client->transport_list = wb_esp_transport_list_init(); + ESP_WS_CLIENT_MEM_CHECK(client->transport_list, goto _websocket_init_fail); + + tcp = wb_esp_transport_tcp_init(); + ESP_WS_CLIENT_MEM_CHECK(tcp, goto _websocket_init_fail); + + wb_esp_transport_set_default_port(tcp, WEBSOCKET_TCP_DEFAULT_PORT); + wb_esp_transport_list_add(client->transport_list, tcp, "_tcp"); // need to save to transport list, for cleanup + + + ws = wb_esp_transport_ws_init(tcp); + ESP_WS_CLIENT_MEM_CHECK(ws, goto _websocket_init_fail); + + wb_esp_transport_set_default_port(ws, WEBSOCKET_TCP_DEFAULT_PORT); + wb_esp_transport_list_add(client->transport_list, ws, "ws"); + if (config->transport == WEBSOCKET_TRANSPORT_OVER_TCP) { + sprintf(client->config->scheme, "%s", "ws"); + ESP_WS_CLIENT_MEM_CHECK(client->config->scheme, goto _websocket_init_fail); + } + + ssl = wb_esp_transport_ssl_init(); + ESP_WS_CLIENT_MEM_CHECK(ssl, goto _websocket_init_fail); + + wb_esp_transport_set_default_port(ssl, WEBSOCKET_SSL_DEFAULT_PORT); + if (config->cert_pem) { + wb_esp_transport_ssl_set_cert_data(ssl, config->cert_pem, strlen(config->cert_pem)); + } + wb_esp_transport_list_add(client->transport_list, ssl, "_ssl"); // need to save to transport list, for cleanup + + wss = wb_esp_transport_ws_init(ssl); + ESP_WS_CLIENT_MEM_CHECK(wss, goto _websocket_init_fail); + + wb_esp_transport_set_default_port(wss, WEBSOCKET_SSL_DEFAULT_PORT); + + wb_esp_transport_list_add(client->transport_list, wss, "wss"); + if (config->transport == WEBSOCKET_TRANSPORT_OVER_SSL) { + sprintf(client->config->scheme, "%s", "wss"); + ESP_WS_CLIENT_MEM_CHECK(client->config->scheme, goto _websocket_init_fail); + } + + if (config->uri) { + if (chat_websocket_client_set_uri(client, config->uri) != ESP_OK) { + ESP_LOGE("Invalid uri"); + goto _websocket_init_fail; + } + } + + if (chat_websocket_client_set_config(client, config) != ESP_OK) { + ESP_LOGE("Failed to set the configuration"); + goto _websocket_init_fail; + } + + if (client->config->scheme == NULL) { + sprintf(client->config->scheme, "%s", "ws"); + ESP_WS_CLIENT_MEM_CHECK(client->config->scheme, goto _websocket_init_fail); + } + + set_websocket_transport_optional_settings(client, wb_esp_transport_list_get_transport(client->transport_list, "ws")); + set_websocket_transport_optional_settings(client, wb_esp_transport_list_get_transport(client->transport_list, "wss")); + + client->keepalive_tick_ms = _tick_get_ms(); + client->reconnect_tick_ms = _tick_get_ms(); + client->ping_tick_ms = _tick_get_ms(); + + buffer_size = config->buffer_size; + if (buffer_size <= 0) { + buffer_size = WEBSOCKET_BUFFER_SIZE_BYTE; + } + client->rx_buffer = malloc(buffer_size); + ESP_WS_CLIENT_MEM_CHECK(client->rx_buffer, { + goto _websocket_init_fail; + }); + client->tx_buffer = malloc(buffer_size); + ESP_WS_CLIENT_MEM_CHECK(client->tx_buffer, { + goto _websocket_init_fail; + }); + + client->buffer_size = buffer_size; + return client; + +_websocket_init_fail: + chat_websocket_client_destroy(client); + return NULL; +} + +esp_err_t chat_websocket_client_destroy(sim_esp_websocket_client_handle_t client) +{ + if (client == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (1 == task_run) { + chat_websocket_client_stop(client); + } + + OSAMutexDelete(client->lock); + + chat_websocket_client_destroy_config(client); + + wb_esp_transport_list_destroy(client->transport_list); + + if (NULL != client->tx_buffer) { + free(client->tx_buffer); + client->tx_buffer = NULL; + } + if (NULL != client->rx_buffer) { + free(client->rx_buffer); + client->rx_buffer = NULL; + } + if (NULL != client) { + free(client); + client = NULL; + } + + return ESP_OK; +} + +esp_err_t chat_websocket_client_set_uri(sim_esp_websocket_client_handle_t client, const char *uri) +{ + if (client == NULL || uri == NULL) { + return ESP_ERR_INVALID_ARG; + } + struct http_parser_url puri; + wb_http_parser_url_init(&puri); + int parser_status = wb_http_parser_parse_url(uri, strlen(uri), 0, &puri); + if (parser_status != 0) { + ESP_LOGE("Error parse uri = %s", uri); + return ESP_FAIL; + } + if (puri.field_data[UF_SCHEMA].len) { + //free(client->config->scheme); + sprintf(client->config->scheme, "%.*s", puri.field_data[UF_SCHEMA].len, uri + puri.field_data[UF_SCHEMA].off); + ESP_WS_CLIENT_MEM_CHECK(client->config->scheme, return ESP_ERR_NO_MEM); + } + + if (puri.field_data[UF_HOST].len) { + //free(client->config->host); + sprintf(client->config->host, "%.*s", puri.field_data[UF_HOST].len, uri + puri.field_data[UF_HOST].off); + ESP_WS_CLIENT_MEM_CHECK(client->config->host, return ESP_ERR_NO_MEM); + } + + + if (puri.field_data[UF_PATH].len || puri.field_data[UF_QUERY].len) { + //free(client->config->path); + if (puri.field_data[UF_QUERY].len == 0) { + sprintf(client->config->path, "%.*s", puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off); + } else if (puri.field_data[UF_PATH].len == 0) { + sprintf(client->config->path, "/?%.*s", puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off); + } else { + sprintf(client->config->path, "%.*s?%.*s", puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off, + puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off); + } + ESP_WS_CLIENT_MEM_CHECK(client->config->path, return ESP_ERR_NO_MEM); + } + if (puri.field_data[UF_PORT].off) { + client->config->port = strtol((const char*)(uri + puri.field_data[UF_PORT].off), NULL, 10); + } + + if (puri.field_data[UF_USERINFO].len) { + char user_info[1024]; + memset(user_info, '\0', 1024); + sprintf(user_info, "%.*s", puri.field_data[UF_USERINFO].len, uri + puri.field_data[UF_USERINFO].off); + if (user_info) { + char *pass = strchr(user_info, ':'); + if (pass) { + pass[0] = 0; //terminal username + pass ++; + //free(client->config->password); + client->config->password = strdup(pass); + ESP_WS_CLIENT_MEM_CHECK(client->config->password, return ESP_ERR_NO_MEM); + } + //free(client->config->username); + client->config->username = strdup(user_info); + ESP_WS_CLIENT_MEM_CHECK(client->config->username, return ESP_ERR_NO_MEM); + //free(user_info); + } else { + return ESP_ERR_NO_MEM; + } + } + return ESP_OK; +} + +static esp_err_t chat_websocket_client_recv(sim_esp_websocket_client_handle_t client) +{ + int rlen; + client->payload_offset = 0; + memset(client->rx_buffer, 0, client->buffer_size); + do { + rlen = wb_esp_transport_read(client->transport, client->rx_buffer, client->buffer_size, client->config->network_timeout_ms); + if (rlen <= 0) { + ESP_LOGE("Error read data"); + chat_websocket_client_abort_connection(client); + return ESP_FAIL; + } + client->payload_len = wb_esp_transport_ws_get_read_payload_len(client->transport); + client->last_opcode = wb_esp_transport_ws_get_read_opcode(client->transport); + + chat_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_DATA, client->rx_buffer, rlen); + + client->payload_offset += rlen; + } while (client->payload_offset < client->payload_len); + + // if a PING message received -> send out the PONG, this will not work for PING messages with payload longer than buffer len + if (client->last_opcode == WS_TRANSPORT_OPCODES_PING) { + const char *data = (client->payload_len == 0) ? NULL : client->rx_buffer; + wb_esp_transport_ws_send_raw(client->transport, WS_TRANSPORT_OPCODES_PONG, data, client->payload_len, + client->config->network_timeout_ms); + } else if (client->last_opcode == WS_TRANSPORT_OPCODES_CLOSE) { + ESP_LOGD("Received close frame"); + client->state = WEBSOCKET_STATE_CLOSING; + } + + return ESP_OK; +} + +static void chat_websocket_client_task(void *pv) +{ + OSA_STATUS status; + int ret = 0; + const int lock_timeout = portMAX_DELAY; + sim_esp_websocket_client_handle_t client = (sim_esp_websocket_client_handle_t) pv; + task_run = 1; + + //get transport by scheme + client->transport = wb_esp_transport_list_get_transport(client->transport_list, client->config->scheme); + + if (client->transport == NULL) { + ESP_LOGE("There are no transports valid, stop websocket client"); + task_run = 0; + } + //default port + if (client->config->port == 0) { + client->config->port = wb_esp_transport_get_default_port(client->transport); + } + + client->state = WEBSOCKET_STATE_INIT; + int read_select = 0; + while (1) { + if (0 == task_run) { + ESP_LOGE("%s: task_run=0 and task exit", __FUNCTION__); + break; + } + + //ESP_LOGD("%s, client state is %d, client->run is %d", + // __FUNCTION__, (int)client->state, task_run); + + status = OSAMutexLock(client->lock, OSA_SUSPEND); + if (status != OS_SUCCESS) { + ESP_LOGE("%s: get lock failed and task exit", __FUNCTION__); + break; + } + switch ((int)client->state) { + case WEBSOCKET_STATE_INIT: + if (client->transport == NULL) { + ESP_LOGE("There are no transport"); + task_run = 0; + break; + } + + if (wb_esp_transport_connect(client->transport, + client->config->host, + client->config->port, + client->config->network_timeout_ms) < 0) { + ESP_LOGE("Error transport connect"); + ret = chat_websocket_client_abort_connection(client); + ESP_LOGD("%s, client abort connection ret is %d", __FUNCTION__, ret); + break; + } + ESP_LOGD("Transport connected to %s://%s:%d", client->config->scheme, client->config->host, client->config->port); + + client->state = WEBSOCKET_STATE_CONNECTED; + chat_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_CONNECTED, NULL, 0); + + break; + case WEBSOCKET_STATE_CONNECTED: + if (_tick_get_ms() - client->ping_tick_ms > WEBSOCKET_PING_TIMEOUT_MS) { + client->ping_tick_ms = _tick_get_ms(); + ESP_LOGD("Sending PING..."); + wb_esp_transport_ws_send_raw(client->transport, WS_TRANSPORT_OPCODES_PING, NULL, 0, client->config->network_timeout_ms); + } + + if (read_select == 0) { + //ESP_LOGV("Read poll timeout: skipping esp_transport_read()..."); + break; + } + client->ping_tick_ms = _tick_get_ms(); + + if (chat_websocket_client_recv(client) == ESP_FAIL) { + ESP_LOGE("Error receive data"); + chat_websocket_client_abort_connection(client); + break; + } + break; + case WEBSOCKET_STATE_WAIT_TIMEOUT: + if (!client->config->auto_reconnect) { + task_run = 0; + break; + } + if (_tick_get_ms() - client->reconnect_tick_ms > client->wait_timeout_ms) { + client->state = WEBSOCKET_STATE_INIT; + client->reconnect_tick_ms = _tick_get_ms(); + ESP_LOGD("Reconnecting..."); + } else { + client->state = WEBSOCKET_STATE_WAIT_TIMEOUT; + ESP_LOGD("Waiting connect..."); + } + break; + case WEBSOCKET_STATE_CLOSING: + // if closing not initiated by the client echo the close message back + ESP_LOGD("Closing initiated by the server, sending close frame"); + wb_esp_transport_ws_send_raw(client->transport, WS_TRANSPORT_OPCODES_CLOSE | WS_TRANSPORT_OPCODES_FIN, NULL, 0, client->config->network_timeout_ms); + break; + + default: + ESP_LOGD("Client run iteration in a default state: %d", client->state); + break; + } + OSAMutexUnlock(client->lock); + + if (WEBSOCKET_STATE_CONNECTED == client->state) { + read_select = wb_esp_transport_poll_read(client->transport, 1000); //Poll every 1000ms + if (read_select < 0) { + ESP_LOGE("Network error: esp_transport_poll_read() returned %d, errno=%d", read_select, errno); + chat_websocket_client_abort_connection(client); + } + } else if (WEBSOCKET_STATE_WAIT_TIMEOUT == client->state) { + // waiting for reconnecting... + OSATaskSleep(client->wait_timeout_ms / 2 / sim_portTICK_RATE_MS); + } else if (WEBSOCKET_STATE_CLOSING == client->state) { + ESP_LOGD(" Waiting for TCP connection to be closed by the server"); + ret = esp_transport_ws_poll_connection_closed(client->transport, 1000); + if (ret == 0) { + // still waiting + break; + } + if (ret < 0) { + ESP_LOGW("Connection terminated while waiting for clean TCP close"); + } + task_run = 0; + client->state = WEBSOCKET_STATE_UNKNOW; + chat_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_CLOSED, NULL, 0); + break; + } + } + + wb_esp_transport_close(client->transport); + client->state = WEBSOCKET_STATE_UNKNOW; + ESP_LOGD("%s task exit!task_run=%d, client state=%d", + __FUNCTION__, task_run, client->state); + return; +} + +esp_err_t chat_websocket_client_start(sim_esp_websocket_client_handle_t client) +{ + OS_STATUS status = OS_SUCCESS; + + if (client == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (client->state > WEBSOCKET_STATE_INIT) { + ESP_LOGE("The client has started and client state is %d", client->state); + return ESP_FAIL; + } + + if (NULL == chat_websocket_client_ref) { + if (NULL == chat_websocket_client_stack_ptr) { + chat_websocket_client_stack_ptr = malloc(8*1024); + if (NULL == chat_websocket_client_stack_ptr) { + ESP_LOGE("websocket task stack malloc failed."); + return ESP_ERR_NO_MEM; + } + } + status = OSATaskCreate(&chat_websocket_client_ref, + chat_websocket_client_stack_ptr, + 8*1024, + 90, + "chat_ws_task", + chat_websocket_client_task, + (void *)client); + if (OS_SUCCESS != status) { + if (chat_websocket_client_stack_ptr) { + free(chat_websocket_client_stack_ptr); + chat_websocket_client_stack_ptr = NULL; + } + ESP_LOGE("Error create websocket task"); + } + } else { + ESP_LOGD("websocket task is exist!"); + } + + ESP_LOGD("websocket_version: %s.", websocket_version); + ESP_LOGD("create ws task SUCCESS!"); + return ESP_OK; +} + +esp_err_t chat_websocket_client_stop(sim_esp_websocket_client_handle_t client) +{ + if (client == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (0 == task_run) { + ESP_LOGW("Client was not started"); + return ESP_FAIL; + } + task_run = 0; + client->state = WEBSOCKET_STATE_UNKNOW; + //delete task + if(chat_websocket_client_ref == NULL) { + ESP_LOGD("websocket task is not exist!"); + return ESP_FAIL; + } + ESP_LOGD("task deleting..."); +#if 1 //liangrf @ fixed Bug #50522 (release select_cb) + lwip_trigger_process("select", chat_websocket_client_ref); +#endif + OSA_STATUS delete_status = OSATaskDelete(chat_websocket_client_ref); + ESP_LOGD("task deleted, deleteTaskRet is %d...", delete_status); + if(chat_websocket_client_stack_ptr) { + free(chat_websocket_client_stack_ptr); + chat_websocket_client_stack_ptr = NULL; + } + chat_websocket_client_ref = NULL; + //delete task + + ESP_LOGD("%s, client state is %d, task_run is %d", + __FUNCTION__, (int)client->state, task_run); + return ESP_OK; +} + +static int chat_websocket_client_send_close(sim_esp_websocket_client_handle_t client, int code, const char *additional_data, int total_len, TickType_t timeout) +{ + char *close_status_data = NULL; + // RFC6455#section-5.5.1: The Close frame MAY contain a body (indicated by total_len >= 2) + if (total_len >= 2) { + close_status_data = calloc(1, total_len); + ESP_WS_CLIENT_MEM_CHECK(close_status_data, return -1); + // RFC6455#section-5.5.1: The first two bytes of the body MUST be a 2-byte representing a status + uint16_t *code_network_order = (uint16_t *) close_status_data; + *code_network_order = htons(code); + memcpy(close_status_data + 2, additional_data, total_len - 2); + } + int ret = chat_websocket_client_send_with_opcode(client, WS_TRANSPORT_OPCODES_CLOSE, close_status_data, total_len, timeout); + if (NULL != close_status_data) { + free(close_status_data); + close_status_data = NULL; + } + return ret; +} + + +static int chat_websocket_client_close_with_optional_body(sim_esp_websocket_client_handle_t client, bool send_body, int code, const char *data, int len, TickType_t timeout) +{ + int ret = 0; + + if (client == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (0 == task_run) { + ESP_LOGW("Client was not started"); + return ESP_FAIL; + } + + if (send_body) { + ret = chat_websocket_client_send_close(client, code, data, len + 2, portMAX_DELAY); // len + 2 -> always sending the code + } else { + ret = chat_websocket_client_send_close(client, 0, NULL, 0, portMAX_DELAY); // only opcode frame + } + + // If could not close gracefully within timeout, stop the client and disconnect + //task_run = 0; + //client->state = WEBSOCKET_STATE_UNKNOW; + if (ret >= 0) { + return ESP_OK; + } else { + ESP_LOGW("Client send close failed"); + return ESP_FAIL; + } +} + +int chat_websocket_client_close_with_code(sim_esp_websocket_client_handle_t client, int code, const char *data, int len, TickType_t timeout) +{ + return chat_websocket_client_close_with_optional_body(client, true, code, data, len, timeout); +} + +int chat_websocket_client_close(sim_esp_websocket_client_handle_t client, TickType_t timeout) +{ + return chat_websocket_client_close_with_optional_body(client, false, 0, NULL, 0, timeout); +} + +int chat_websocket_client_send_text(sim_esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout) +{ + return chat_websocket_client_send_with_opcode(client, WS_TRANSPORT_OPCODES_TEXT, data, len, timeout); +} + +int chat_websocket_client_send(sim_esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout) +{ + return chat_websocket_client_send_with_opcode(client, WS_TRANSPORT_OPCODES_BINARY, data, len, timeout); +} + +int chat_websocket_client_send_bin(sim_esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout) +{ + return chat_websocket_client_send_with_opcode(client, WS_TRANSPORT_OPCODES_BINARY, data, len, timeout); +} + +static int chat_websocket_client_send_with_opcode(sim_esp_websocket_client_handle_t client, ws_transport_opcodes_t opcode, const char *data, int len, TickType_t timeout) +{ + int need_write = len; + int wlen = 0, widx = 0; + int ret = ESP_FAIL; + UINT32 current_opcode = opcode; + OSA_STATUS status; + + if (client == NULL || len < 0 || + (opcode != WS_TRANSPORT_OPCODES_CLOSE && (data == NULL || len <= 0))) { + ESP_LOGE("Invalid arguments"); + return ESP_FAIL; + } + + status = OSAMutexLock(client->lock, timeout); + if (status != OS_SUCCESS) { + ESP_LOGE("%s: get lock failed and return error", __FUNCTION__); + return ESP_FAIL; + } + if (!chat_websocket_client_is_connected(client)) { + ESP_LOGE("Websocket client is not connected"); + goto unlock_and_return; + } + + if (client->transport == NULL) { + ESP_LOGE("Invalid transport"); + goto unlock_and_return; + } + + while (widx < len || current_opcode) { // allow for sending "current_opcode" only message with len==0 + if (need_write > client->buffer_size) { + need_write = client->buffer_size; + } else { + current_opcode |= WS_TRANSPORT_OPCODES_FIN; + } + memcpy(client->tx_buffer, data + widx, need_write); + // send with ws specific way and specific opcode + wlen = wb_esp_transport_ws_send_raw(client->transport, current_opcode, (char *)client->tx_buffer, need_write, + (timeout==portMAX_DELAY)? -1 : timeout * sim_portTICK_PERIOD_MS); + if (wlen < 0 || (wlen == 0 && need_write != 0)) { + ret = wlen; + ESP_LOGE("Network error: esp_transport_write() returned %d, errno=%d", ret, errno); + chat_websocket_client_abort_connection(client); + goto unlock_and_return; + } + current_opcode = 0; + widx += wlen; + need_write = len - widx; + } + + ret = widx; +unlock_and_return: + OSAMutexUnlock(client->lock); + return ret; +} + +bool chat_websocket_client_is_connected(sim_esp_websocket_client_handle_t client) +{ + if (client == NULL) { + return false; + } + return client->state == WEBSOCKET_STATE_CONNECTED; +} + +esp_err_t chat_websocket_register_events(sim_esp_websocket_client_handle_t client, + sim_esp_event_handler_t event_handler) +{ + if (client == NULL) { + return -1; + } + ESP_LOGD("client handler register success!!"); + client->handler = event_handler; + return 0; +} diff --git a/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/src/mmi_aigc_trans_websocket.c b/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/src/mmi_aigc_trans_websocket.c index 18902425..d58e5976 100644 --- a/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/src/mmi_aigc_trans_websocket.c +++ b/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/src/mmi_aigc_trans_websocket.c @@ -5,7 +5,9 @@ #include "uart.h" #include "sim_esp_websocket_client.h" -#include "Mmi_aigc_trans_websocket.h" +#include "mmi_aigc_websocket.h" + +#define MMI_AIGC_TRANS_WEBSOCKET_URI "ws://121.40.67.151:9099/asr/ws" static sim_esp_websocket_client_handle_t s_aigc_trans_ws_client_p = NULL; diff --git a/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/src/mmi_aigc_tts_websocket.c b/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/src/mmi_aigc_tts_websocket.c new file mode 100644 index 00000000..19bee17c --- /dev/null +++ b/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/src/mmi_aigc_tts_websocket.c @@ -0,0 +1,143 @@ + + + +#if 1 // def __MMI_CUSTOM_AIGC_SUPPORT__ + +#include "uart.h" +#include "mmi_aigc_tts_websocket_client.h" +#include "mmi_aigc_websocket.h" + +#define MMI_AIGC_TTS_WEBSOCKET_URI "ws://121.40.67.151:9099/tts/stream" + +static sim_esp_websocket_client_handle_t s_aigc_tts_ws_client_p = NULL; + +extern void mmi_aigc_tts_websocket_recv_data_handler(char * data_ptr, int data_len, int payload_len, int payload_offset); + + + +int mmi_aigc_tts_websocket_send_text_data(char * data_ptr, int data_len) +{ + int ret = 0; + + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: \n", __FUNCTION__, __LINE__); + ret = tts_websocket_client_send_text(s_aigc_tts_ws_client_p, data_ptr, data_len, portMAX_DELAY); + if (0 > ret) + { + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: \n", __FUNCTION__, __LINE__); + } + + return ret; +} + +int mmi_aigc_tts_websocket_send_binary_data(char * data_ptr, int data_len) +{ + int ret = 0; + + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: \n", __FUNCTION__, __LINE__); + ret = tts_websocket_client_send_bin(s_aigc_tts_ws_client_p, data_ptr, data_len, portMAX_DELAY); + if (0 > ret) + { + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: \n", __FUNCTION__, __LINE__); + } + + return ret; +} + +static void mmi_aigc_tts_websocket_event_handler(int32_t event_id, void *event_data) +{ + sim_esp_websocket_event_data_t *data = (sim_esp_websocket_event_data_t *)event_data; + + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: event_id=%d\n", __FUNCTION__, __LINE__, event_id); + switch (event_id) + { + case WEBSOCKET_EVENT_CONNECTED: + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%d]: WEBSOCKET_EVENT_CONNECTED\n", __LINE__); + break; + + case WEBSOCKET_EVENT_DISCONNECTED: + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%d]: WEBSOCKET_EVENT_DISCONNECTED\n", __LINE__); + break; + + case WEBSOCKET_EVENT_DATA: + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%d]: WEBSOCKET_EVENT_DATA\n", __LINE__); + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG]: data opcode=%d,data len=%d", data->op_code, data->data_len); + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG]: Total payload len=%d,current payload offset=%d\r\n", data->payload_len, data->payload_offset); + if (data->op_code == 0x08 && data->data_len == 2) { + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG]: Received closed message with code=%d", 256*data->data_ptr[0] + data->data_ptr[1]); + } + else + { + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG]: Received=%.*s", data->data_len, (char *)data->data_ptr); + mmi_aigc_tts_websocket_recv_data_handler(data->data_ptr, data->data_len, data->payload_len, data->payload_offset); + } + //OSATimerStart(shutdown_signal_timer, portMAX_DELAY, 0, shutdown_signaler, 0); + break; + + case WEBSOCKET_EVENT_ERROR: + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%d]: WEBSOCKET_EVENT_ERROR\n", __LINE__); + break; + + case WEBSOCKET_EVENT_CLOSED: + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%d]: WEBSOCKET_EVENT_CLOSED\n", __LINE__); + break; + + default: + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%d]: default\n", __LINE__); + break; + } + + if (NULL != data) + { + if (NULL != data->data_ptr) + { + free(data->data_ptr); + data->data_ptr = NULL; + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: free data ptr\n", __FUNCTION__, __LINE__); + } + + free(data); + data = NULL; + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: free data\n", __FUNCTION__, __LINE__); + } +} + +void mmi_aigc_tts_websocket_app_start(void) +{ + OS_STATUS status = 0; + int ret = 0; + sim_esp_websocket_client_config_t websocket_cfg = {0}; + + websocket_cfg.uri = MMI_AIGC_TTS_WEBSOCKET_URI; + + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: url=%s\n", __FUNCTION__, __LINE__, websocket_cfg.uri); + + s_aigc_tts_ws_client_p = tts_websocket_client_init(&websocket_cfg); + if(NULL == s_aigc_tts_ws_client_p) + { + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: websocket client init fail\n", __FUNCTION__, __LINE__); + return; + } + + tts_websocket_register_events(s_aigc_tts_ws_client_p, mmi_aigc_tts_websocket_event_handler); + + esp_err_t err_no = tts_websocket_client_start(s_aigc_tts_ws_client_p); + if(ESP_OK != err_no) + { + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: websocket client start fail,%d\n", __FUNCTION__, __LINE__, err_no); + return; + } + else + { + + } +} + +void mmi_aigc_tts_websocket_app_stop(void) +{ + AIGC_WB_DEBUG_PRINTF("[@CHENG_DEBUG][%s][%d]: \n", __FUNCTION__, __LINE__); + + tts_websocket_client_destroy(s_aigc_tts_ws_client_p); +} + +#endif + diff --git a/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/src/mmi_aigc_tts_websocket_client.c b/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/src/mmi_aigc_tts_websocket_client.c new file mode 100644 index 00000000..0eb73922 --- /dev/null +++ b/2.sdk/sdk/product/craneg_modem/pcac/wsclient/libwebsockets/src/mmi_aigc_tts_websocket_client.c @@ -0,0 +1,826 @@ +// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// 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 "mmi_aigc_tts_websocket_client.h" +#include "web_esp_transport.h" +#include "web_esp_transport_tcp.h" +#include "web_esp_transport_ssl.h" +#include "web_esp_transport_ws.h" + +/* using uri parser */ +#include "web_http_parser.h" +#include "queue.h" +#include "esp_log.h" +#include "sockets.h" +#include "osa.h" + +static int task_run = 0; +static OSTaskRef tts_websocket_client_ref = NULL; +static void * tts_websocket_client_stack_ptr = NULL; + +#define websocket_version "asr_websocket_v1.10_20210926" +#define WEBSOCKET_TCP_DEFAULT_PORT (80) +#define WEBSOCKET_SSL_DEFAULT_PORT (443) +#define WEBSOCKET_BUFFER_SIZE_BYTE (8*1024) +#define WEBSOCKET_RECONNECT_TIMEOUT_MS (5*60*1000) +#define WEBSOCKET_TASK_PRIORITY (90) +#define WEBSOCKET_TASK_STACK (8*1024) +#define WEBSOCKET_NETWORK_TIMEOUT_MS (10*1000) +#define WEBSOCKET_PING_TIMEOUT_MS (15*60*1000) +#define WEBSOCKET_EVENT_QUEUE_SIZE (1) + +#define ESP_WS_CLIENT_MEM_CHECK(a, action) if (!(a)) { \ + ESP_LOGE("%s:%d (%s): %s", __FILE__, __LINE__, __FUNCTION__, "Memory exhausted"); \ + action; \ + } +#define ESP_EVENT_DECLARE_BASE(id) extern esp_event_base_t id +#define ESP_EVENT_DEFINE_BASE(id) esp_event_base_t id = #id + +static int tts_websocket_client_send_with_opcode(sim_esp_websocket_client_handle_t client, ws_transport_opcodes_t opcode, const char *data, int len, TickType_t timeout); +static void asr_ws_loop_run_task(void *pv); +extern OSMsgQRef asr_ws_msgQ_ref; +extern int esp_transport_ws_poll_connection_closed(wb_esp_transport_handle_t t, int timeout_ms); + +static UINT32 _tick_get_ms(void) +{ + return (5*OSAGetTicks()); //get ms + //return esp_timer_get_time()/1000; +} + +static int tts_websocket_client_dispatch_event(sim_esp_websocket_client_handle_t client, + sim_esp_websocket_event_id_t event, + const char *data, + int data_len) +{ + if (client->handler) { + if (NULL == data) { + client->handler(event, NULL); + } else { + sim_esp_websocket_event_data_t *event_data = NULL; + event_data = malloc(sizeof(sim_esp_websocket_event_data_t)); + if (NULL == event_data) { + ESP_LOGE("%s, event_data malloc failed", __FUNCTION__); + return -1; + } else { + memset(event_data, '\0', sizeof(sim_esp_websocket_event_data_t)); + event_data->data_ptr = malloc(data_len); + if (event_data->data_ptr) { + memset(event_data->data_ptr, 0, data_len); + memcpy(event_data->data_ptr, data, data_len); + } + } + + event_data->data_len = data_len; + event_data->op_code = client->last_opcode; + //shenxj+++ + event_data->payload_len = client->payload_len; + event_data->payload_offset = client->payload_offset; + //shenxj--- + + client->handler(event, event_data); + } + } + + return 0; +} + +static int tts_websocket_client_abort_connection(sim_esp_websocket_client_handle_t client) +{ + int ret = 0; + + wb_esp_transport_close(client->transport); + client->wait_timeout_ms = WEBSOCKET_RECONNECT_TIMEOUT_MS; + client->reconnect_tick_ms = _tick_get_ms(); + client->state = WEBSOCKET_STATE_WAIT_TIMEOUT; + ESP_LOGI("Reconnect after %d ms", client->wait_timeout_ms); + ret = tts_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_DISCONNECTED, NULL, 0); + ESP_LOGD("%s, client abort connection ret is %d", __FUNCTION__, ret); + return ret; +} + +static esp_err_t tts_websocket_client_set_config(sim_esp_websocket_client_handle_t client, const sim_esp_websocket_client_config_t *config) +{ + sim_websocket_config_storage_t *cfg = client->config; + cfg->task_prio = config->task_prio; + if (cfg->task_prio <= 0) { + cfg->task_prio = WEBSOCKET_TASK_PRIORITY; + } + + cfg->task_stack = config->task_stack; + if (cfg->task_stack == 0) { + cfg->task_stack = WEBSOCKET_TASK_STACK; + } + + if (config->host) { + cfg->p_host = strdup(config->host); + memcpy(cfg->host, cfg->p_host, strlen(config->host)); + ESP_WS_CLIENT_MEM_CHECK(cfg->host, return ESP_ERR_NO_MEM); + } + + if (config->port) { + cfg->port = config->port; + } + + if (config->username) { + //free(cfg->username); + cfg->username = strdup(config->username); + ESP_WS_CLIENT_MEM_CHECK(cfg->username, return ESP_ERR_NO_MEM); + } + + if (config->password) { + //free(cfg->password); + cfg->password = strdup(config->password); + ESP_WS_CLIENT_MEM_CHECK(cfg->password, return ESP_ERR_NO_MEM); + } + + if (config->uri) { + //free(cfg->uri); + cfg->uri = strdup(config->uri); + ESP_WS_CLIENT_MEM_CHECK(cfg->uri, return ESP_ERR_NO_MEM); + } + if (config->path) { + //free(cfg->path); + cfg->p_path = strdup(config->path); + memcpy(cfg->path, cfg->p_path, strlen(config->path)); + ESP_WS_CLIENT_MEM_CHECK(cfg->path, return ESP_ERR_NO_MEM); + } + if (config->subprotocol) { + //free(cfg->subprotocol); + cfg->subprotocol = strdup(config->subprotocol); + ESP_WS_CLIENT_MEM_CHECK(cfg->subprotocol, return ESP_ERR_NO_MEM); + } + + cfg->network_timeout_ms = WEBSOCKET_NETWORK_TIMEOUT_MS; + cfg->user_context = config->user_context; + cfg->auto_reconnect = true; + if (config->disable_auto_reconnect) { + cfg->auto_reconnect = false; + } + + + return ESP_OK; +} + +static esp_err_t tts_websocket_client_destroy_config(sim_esp_websocket_client_handle_t client) +{ + if (client == NULL) { + return ESP_ERR_INVALID_ARG; + } + sim_websocket_config_storage_t *cfg = client->config; + if (client->config == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (NULL != cfg->p_host) { + free(cfg->p_host); + cfg->p_host = NULL; + } + if (NULL != cfg->uri) { + free(cfg->uri); + cfg->uri = NULL; + } + if (NULL != cfg->p_path) { + free(cfg->p_path); + cfg->p_path = NULL; + } + if (NULL != cfg->username) { + free(cfg->username); + cfg->username = NULL; + } + if (NULL != cfg->password) { + free(cfg->password); + cfg->password = NULL; + } + if (NULL != cfg->subprotocol) { + free(cfg->subprotocol); + cfg->subprotocol = NULL; + } + + memset(cfg, 0, sizeof(sim_websocket_config_storage_t)); + free(client->config); + client->config = NULL; + return ESP_OK; +} + +static void set_websocket_transport_optional_settings(sim_esp_websocket_client_handle_t client, wb_esp_transport_handle_t trans) +{ + if (trans && client->config->path) { + wb_esp_transport_ws_set_path(trans, client->config->path); + } + if (trans && client->config->subprotocol) { + wb_esp_transport_ws_set_subprotocol(trans, client->config->subprotocol); + } +} + +sim_esp_websocket_client_handle_t tts_websocket_client_init(const sim_esp_websocket_client_config_t *config) +{ + wb_esp_transport_handle_t tcp = NULL; + wb_esp_transport_handle_t ws = NULL; + wb_esp_transport_handle_t ssl = NULL; + wb_esp_transport_handle_t wss = NULL; + int buffer_size = 0; + OS_STATUS status; + + sim_esp_websocket_client_handle_t client = (sim_esp_websocket_client_handle_t)malloc(1*sizeof(struct sim_esp_websocket_client)); + ESP_WS_CLIENT_MEM_CHECK(client, return NULL); + memset(client, '\0', sizeof(struct sim_esp_websocket_client)); + client->state = WEBSOCKET_STATE_UNKNOW; + + status = OSAMutexCreate(&client->lock, OSA_FIFO); + if (OS_SUCCESS != status) { + ESP_LOGE("Failed to create client lock"); + } + + client->config = calloc(1, sizeof(sim_websocket_config_storage_t)); + ESP_WS_CLIENT_MEM_CHECK(client->config, goto _websocket_init_fail); + + client->transport_list = wb_esp_transport_list_init(); + ESP_WS_CLIENT_MEM_CHECK(client->transport_list, goto _websocket_init_fail); + + tcp = wb_esp_transport_tcp_init(); + ESP_WS_CLIENT_MEM_CHECK(tcp, goto _websocket_init_fail); + + wb_esp_transport_set_default_port(tcp, WEBSOCKET_TCP_DEFAULT_PORT); + wb_esp_transport_list_add(client->transport_list, tcp, "_tcp"); // need to save to transport list, for cleanup + + + ws = wb_esp_transport_ws_init(tcp); + ESP_WS_CLIENT_MEM_CHECK(ws, goto _websocket_init_fail); + + wb_esp_transport_set_default_port(ws, WEBSOCKET_TCP_DEFAULT_PORT); + wb_esp_transport_list_add(client->transport_list, ws, "ws"); + if (config->transport == WEBSOCKET_TRANSPORT_OVER_TCP) { + sprintf(client->config->scheme, "%s", "ws"); + ESP_WS_CLIENT_MEM_CHECK(client->config->scheme, goto _websocket_init_fail); + } + + ssl = wb_esp_transport_ssl_init(); + ESP_WS_CLIENT_MEM_CHECK(ssl, goto _websocket_init_fail); + + wb_esp_transport_set_default_port(ssl, WEBSOCKET_SSL_DEFAULT_PORT); + if (config->cert_pem) { + wb_esp_transport_ssl_set_cert_data(ssl, config->cert_pem, strlen(config->cert_pem)); + } + wb_esp_transport_list_add(client->transport_list, ssl, "_ssl"); // need to save to transport list, for cleanup + + wss = wb_esp_transport_ws_init(ssl); + ESP_WS_CLIENT_MEM_CHECK(wss, goto _websocket_init_fail); + + wb_esp_transport_set_default_port(wss, WEBSOCKET_SSL_DEFAULT_PORT); + + wb_esp_transport_list_add(client->transport_list, wss, "wss"); + if (config->transport == WEBSOCKET_TRANSPORT_OVER_SSL) { + sprintf(client->config->scheme, "%s", "wss"); + ESP_WS_CLIENT_MEM_CHECK(client->config->scheme, goto _websocket_init_fail); + } + + if (config->uri) { + if (tts_websocket_client_set_uri(client, config->uri) != ESP_OK) { + ESP_LOGE("Invalid uri"); + goto _websocket_init_fail; + } + } + + if (tts_websocket_client_set_config(client, config) != ESP_OK) { + ESP_LOGE("Failed to set the configuration"); + goto _websocket_init_fail; + } + + if (client->config->scheme == NULL) { + sprintf(client->config->scheme, "%s", "ws"); + ESP_WS_CLIENT_MEM_CHECK(client->config->scheme, goto _websocket_init_fail); + } + + set_websocket_transport_optional_settings(client, wb_esp_transport_list_get_transport(client->transport_list, "ws")); + set_websocket_transport_optional_settings(client, wb_esp_transport_list_get_transport(client->transport_list, "wss")); + + client->keepalive_tick_ms = _tick_get_ms(); + client->reconnect_tick_ms = _tick_get_ms(); + client->ping_tick_ms = _tick_get_ms(); + + buffer_size = config->buffer_size; + if (buffer_size <= 0) { + buffer_size = WEBSOCKET_BUFFER_SIZE_BYTE; + } + client->rx_buffer = malloc(buffer_size); + ESP_WS_CLIENT_MEM_CHECK(client->rx_buffer, { + goto _websocket_init_fail; + }); + client->tx_buffer = malloc(buffer_size); + ESP_WS_CLIENT_MEM_CHECK(client->tx_buffer, { + goto _websocket_init_fail; + }); + + client->buffer_size = buffer_size; + return client; + +_websocket_init_fail: + tts_websocket_client_destroy(client); + return NULL; +} + +esp_err_t tts_websocket_client_destroy(sim_esp_websocket_client_handle_t client) +{ + if (client == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (1 == task_run) { + tts_websocket_client_stop(client); + } + + OSAMutexDelete(client->lock); + + tts_websocket_client_destroy_config(client); + + wb_esp_transport_list_destroy(client->transport_list); + + if (NULL != client->tx_buffer) { + free(client->tx_buffer); + client->tx_buffer = NULL; + } + if (NULL != client->rx_buffer) { + free(client->rx_buffer); + client->rx_buffer = NULL; + } + if (NULL != client) { + free(client); + client = NULL; + } + + return ESP_OK; +} + +esp_err_t tts_websocket_client_set_uri(sim_esp_websocket_client_handle_t client, const char *uri) +{ + if (client == NULL || uri == NULL) { + return ESP_ERR_INVALID_ARG; + } + struct http_parser_url puri; + wb_http_parser_url_init(&puri); + int parser_status = wb_http_parser_parse_url(uri, strlen(uri), 0, &puri); + if (parser_status != 0) { + ESP_LOGE("Error parse uri = %s", uri); + return ESP_FAIL; + } + if (puri.field_data[UF_SCHEMA].len) { + //free(client->config->scheme); + sprintf(client->config->scheme, "%.*s", puri.field_data[UF_SCHEMA].len, uri + puri.field_data[UF_SCHEMA].off); + ESP_WS_CLIENT_MEM_CHECK(client->config->scheme, return ESP_ERR_NO_MEM); + } + + if (puri.field_data[UF_HOST].len) { + //free(client->config->host); + sprintf(client->config->host, "%.*s", puri.field_data[UF_HOST].len, uri + puri.field_data[UF_HOST].off); + ESP_WS_CLIENT_MEM_CHECK(client->config->host, return ESP_ERR_NO_MEM); + } + + + if (puri.field_data[UF_PATH].len || puri.field_data[UF_QUERY].len) { + //free(client->config->path); + if (puri.field_data[UF_QUERY].len == 0) { + sprintf(client->config->path, "%.*s", puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off); + } else if (puri.field_data[UF_PATH].len == 0) { + sprintf(client->config->path, "/?%.*s", puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off); + } else { + sprintf(client->config->path, "%.*s?%.*s", puri.field_data[UF_PATH].len, uri + puri.field_data[UF_PATH].off, + puri.field_data[UF_QUERY].len, uri + puri.field_data[UF_QUERY].off); + } + ESP_WS_CLIENT_MEM_CHECK(client->config->path, return ESP_ERR_NO_MEM); + } + if (puri.field_data[UF_PORT].off) { + client->config->port = strtol((const char*)(uri + puri.field_data[UF_PORT].off), NULL, 10); + } + + if (puri.field_data[UF_USERINFO].len) { + char user_info[1024]; + memset(user_info, '\0', 1024); + sprintf(user_info, "%.*s", puri.field_data[UF_USERINFO].len, uri + puri.field_data[UF_USERINFO].off); + if (user_info) { + char *pass = strchr(user_info, ':'); + if (pass) { + pass[0] = 0; //terminal username + pass ++; + //free(client->config->password); + client->config->password = strdup(pass); + ESP_WS_CLIENT_MEM_CHECK(client->config->password, return ESP_ERR_NO_MEM); + } + //free(client->config->username); + client->config->username = strdup(user_info); + ESP_WS_CLIENT_MEM_CHECK(client->config->username, return ESP_ERR_NO_MEM); + //free(user_info); + } else { + return ESP_ERR_NO_MEM; + } + } + return ESP_OK; +} + +static esp_err_t tts_websocket_client_recv(sim_esp_websocket_client_handle_t client) +{ + int rlen; + client->payload_offset = 0; + memset(client->rx_buffer, 0, client->buffer_size); + do { + rlen = wb_esp_transport_read(client->transport, client->rx_buffer, client->buffer_size, client->config->network_timeout_ms); + if (rlen <= 0) { + ESP_LOGE("Error read data"); + tts_websocket_client_abort_connection(client); + return ESP_FAIL; + } + client->payload_len = wb_esp_transport_ws_get_read_payload_len(client->transport); + client->last_opcode = wb_esp_transport_ws_get_read_opcode(client->transport); + + tts_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_DATA, client->rx_buffer, rlen); + + client->payload_offset += rlen; + } while (client->payload_offset < client->payload_len); + + // if a PING message received -> send out the PONG, this will not work for PING messages with payload longer than buffer len + if (client->last_opcode == WS_TRANSPORT_OPCODES_PING) { + const char *data = (client->payload_len == 0) ? NULL : client->rx_buffer; + wb_esp_transport_ws_send_raw(client->transport, WS_TRANSPORT_OPCODES_PONG, data, client->payload_len, + client->config->network_timeout_ms); + } else if (client->last_opcode == WS_TRANSPORT_OPCODES_CLOSE) { + ESP_LOGD("Received close frame"); + client->state = WEBSOCKET_STATE_CLOSING; + } + + return ESP_OK; +} + +static void tts_websocket_client_task(void *pv) +{ + OSA_STATUS status; + int ret = 0; + const int lock_timeout = portMAX_DELAY; + sim_esp_websocket_client_handle_t client = (sim_esp_websocket_client_handle_t) pv; + task_run = 1; + + //get transport by scheme + client->transport = wb_esp_transport_list_get_transport(client->transport_list, client->config->scheme); + + if (client->transport == NULL) { + ESP_LOGE("There are no transports valid, stop websocket client"); + task_run = 0; + } + //default port + if (client->config->port == 0) { + client->config->port = wb_esp_transport_get_default_port(client->transport); + } + + client->state = WEBSOCKET_STATE_INIT; + int read_select = 0; + while (1) { + if (0 == task_run) { + ESP_LOGE("%s: task_run=0 and task exit", __FUNCTION__); + break; + } + + //ESP_LOGD("%s, client state is %d, client->run is %d", + // __FUNCTION__, (int)client->state, task_run); + + status = OSAMutexLock(client->lock, OSA_SUSPEND); + if (status != OS_SUCCESS) { + ESP_LOGE("%s: get lock failed and task exit", __FUNCTION__); + break; + } + switch ((int)client->state) { + case WEBSOCKET_STATE_INIT: + if (client->transport == NULL) { + ESP_LOGE("There are no transport"); + task_run = 0; + break; + } + + if (wb_esp_transport_connect(client->transport, + client->config->host, + client->config->port, + client->config->network_timeout_ms) < 0) { + ESP_LOGE("Error transport connect"); + ret = tts_websocket_client_abort_connection(client); + ESP_LOGD("%s, client abort connection ret is %d", __FUNCTION__, ret); + break; + } + ESP_LOGD("Transport connected to %s://%s:%d", client->config->scheme, client->config->host, client->config->port); + + client->state = WEBSOCKET_STATE_CONNECTED; + tts_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_CONNECTED, NULL, 0); + + break; + case WEBSOCKET_STATE_CONNECTED: + if (_tick_get_ms() - client->ping_tick_ms > WEBSOCKET_PING_TIMEOUT_MS) { + client->ping_tick_ms = _tick_get_ms(); + ESP_LOGD("Sending PING..."); + wb_esp_transport_ws_send_raw(client->transport, WS_TRANSPORT_OPCODES_PING, NULL, 0, client->config->network_timeout_ms); + } + + if (read_select == 0) { + //ESP_LOGV("Read poll timeout: skipping esp_transport_read()..."); + break; + } + client->ping_tick_ms = _tick_get_ms(); + + if (tts_websocket_client_recv(client) == ESP_FAIL) { + ESP_LOGE("Error receive data"); + tts_websocket_client_abort_connection(client); + break; + } + break; + case WEBSOCKET_STATE_WAIT_TIMEOUT: + if (!client->config->auto_reconnect) { + task_run = 0; + break; + } + if (_tick_get_ms() - client->reconnect_tick_ms > client->wait_timeout_ms) { + client->state = WEBSOCKET_STATE_INIT; + client->reconnect_tick_ms = _tick_get_ms(); + ESP_LOGD("Reconnecting..."); + } else { + client->state = WEBSOCKET_STATE_WAIT_TIMEOUT; + ESP_LOGD("Waiting connect..."); + } + break; + case WEBSOCKET_STATE_CLOSING: + // if closing not initiated by the client echo the close message back + ESP_LOGD("Closing initiated by the server, sending close frame"); + wb_esp_transport_ws_send_raw(client->transport, WS_TRANSPORT_OPCODES_CLOSE | WS_TRANSPORT_OPCODES_FIN, NULL, 0, client->config->network_timeout_ms); + break; + + default: + ESP_LOGD("Client run iteration in a default state: %d", client->state); + break; + } + OSAMutexUnlock(client->lock); + + if (WEBSOCKET_STATE_CONNECTED == client->state) { + read_select = wb_esp_transport_poll_read(client->transport, 1000); //Poll every 1000ms + if (read_select < 0) { + ESP_LOGE("Network error: esp_transport_poll_read() returned %d, errno=%d", read_select, errno); + tts_websocket_client_abort_connection(client); + } + } else if (WEBSOCKET_STATE_WAIT_TIMEOUT == client->state) { + // waiting for reconnecting... + OSATaskSleep(client->wait_timeout_ms / 2 / sim_portTICK_RATE_MS); + } else if (WEBSOCKET_STATE_CLOSING == client->state) { + ESP_LOGD(" Waiting for TCP connection to be closed by the server"); + ret = esp_transport_ws_poll_connection_closed(client->transport, 1000); + if (ret == 0) { + // still waiting + break; + } + if (ret < 0) { + ESP_LOGW("Connection terminated while waiting for clean TCP close"); + } + task_run = 0; + client->state = WEBSOCKET_STATE_UNKNOW; + tts_websocket_client_dispatch_event(client, WEBSOCKET_EVENT_CLOSED, NULL, 0); + break; + } + } + + wb_esp_transport_close(client->transport); + client->state = WEBSOCKET_STATE_UNKNOW; + ESP_LOGD("%s task exit!task_run=%d, client state=%d", + __FUNCTION__, task_run, client->state); + return; +} + +esp_err_t tts_websocket_client_start(sim_esp_websocket_client_handle_t client) +{ + OS_STATUS status = OS_SUCCESS; + + if (client == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (client->state > WEBSOCKET_STATE_INIT) { + ESP_LOGE("The client has started and client state is %d", client->state); + return ESP_FAIL; + } + + if (NULL == tts_websocket_client_ref) { + if (NULL == tts_websocket_client_stack_ptr) { + tts_websocket_client_stack_ptr = malloc(8*1024); + if (NULL == tts_websocket_client_stack_ptr) { + ESP_LOGE("websocket task stack malloc failed."); + return ESP_ERR_NO_MEM; + } + } + status = OSATaskCreate(&tts_websocket_client_ref, + tts_websocket_client_stack_ptr, + 8*1024, + 90, + "tts_ws_task", + tts_websocket_client_task, + (void *)client); + if (OS_SUCCESS != status) { + if (tts_websocket_client_stack_ptr) { + free(tts_websocket_client_stack_ptr); + tts_websocket_client_stack_ptr = NULL; + } + ESP_LOGE("Error create websocket task"); + } + } else { + ESP_LOGD("websocket task is exist!"); + } + + ESP_LOGD("websocket_version: %s.", websocket_version); + ESP_LOGD("create ws task SUCCESS!"); + return ESP_OK; +} + +esp_err_t tts_websocket_client_stop(sim_esp_websocket_client_handle_t client) +{ + if (client == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (0 == task_run) { + ESP_LOGW("Client was not started"); + return ESP_FAIL; + } + task_run = 0; + client->state = WEBSOCKET_STATE_UNKNOW; + //delete task + if(tts_websocket_client_ref == NULL) { + ESP_LOGD("websocket task is not exist!"); + return ESP_FAIL; + } + ESP_LOGD("task deleting..."); +#if 1 //liangrf @ fixed Bug #50522 (release select_cb) + lwip_trigger_process("select", tts_websocket_client_ref); +#endif + OSA_STATUS delete_status = OSATaskDelete(tts_websocket_client_ref); + ESP_LOGD("task deleted, deleteTaskRet is %d...", delete_status); + if(tts_websocket_client_stack_ptr) { + free(tts_websocket_client_stack_ptr); + tts_websocket_client_stack_ptr = NULL; + } + tts_websocket_client_ref = NULL; + //delete task + + ESP_LOGD("%s, client state is %d, task_run is %d", + __FUNCTION__, (int)client->state, task_run); + return ESP_OK; +} + +static int tts_websocket_client_send_close(sim_esp_websocket_client_handle_t client, int code, const char *additional_data, int total_len, TickType_t timeout) +{ + char *close_status_data = NULL; + // RFC6455#section-5.5.1: The Close frame MAY contain a body (indicated by total_len >= 2) + if (total_len >= 2) { + close_status_data = calloc(1, total_len); + ESP_WS_CLIENT_MEM_CHECK(close_status_data, return -1); + // RFC6455#section-5.5.1: The first two bytes of the body MUST be a 2-byte representing a status + uint16_t *code_network_order = (uint16_t *) close_status_data; + *code_network_order = htons(code); + memcpy(close_status_data + 2, additional_data, total_len - 2); + } + int ret = tts_websocket_client_send_with_opcode(client, WS_TRANSPORT_OPCODES_CLOSE, close_status_data, total_len, timeout); + if (NULL != close_status_data) { + free(close_status_data); + close_status_data = NULL; + } + return ret; +} + + +static int tts_websocket_client_close_with_optional_body(sim_esp_websocket_client_handle_t client, bool send_body, int code, const char *data, int len, TickType_t timeout) +{ + int ret = 0; + + if (client == NULL) { + return ESP_ERR_INVALID_ARG; + } + if (0 == task_run) { + ESP_LOGW("Client was not started"); + return ESP_FAIL; + } + + if (send_body) { + ret = tts_websocket_client_send_close(client, code, data, len + 2, portMAX_DELAY); // len + 2 -> always sending the code + } else { + ret = tts_websocket_client_send_close(client, 0, NULL, 0, portMAX_DELAY); // only opcode frame + } + + // If could not close gracefully within timeout, stop the client and disconnect + //task_run = 0; + //client->state = WEBSOCKET_STATE_UNKNOW; + if (ret >= 0) { + return ESP_OK; + } else { + ESP_LOGW("Client send close failed"); + return ESP_FAIL; + } +} + +int tts_websocket_client_close_with_code(sim_esp_websocket_client_handle_t client, int code, const char *data, int len, TickType_t timeout) +{ + return tts_websocket_client_close_with_optional_body(client, true, code, data, len, timeout); +} + +int tts_websocket_client_close(sim_esp_websocket_client_handle_t client, TickType_t timeout) +{ + return tts_websocket_client_close_with_optional_body(client, false, 0, NULL, 0, timeout); +} + +int tts_websocket_client_send_text(sim_esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout) +{ + return tts_websocket_client_send_with_opcode(client, WS_TRANSPORT_OPCODES_TEXT, data, len, timeout); +} + +int tts_websocket_client_send(sim_esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout) +{ + return tts_websocket_client_send_with_opcode(client, WS_TRANSPORT_OPCODES_BINARY, data, len, timeout); +} + +int tts_websocket_client_send_bin(sim_esp_websocket_client_handle_t client, const char *data, int len, TickType_t timeout) +{ + return tts_websocket_client_send_with_opcode(client, WS_TRANSPORT_OPCODES_BINARY, data, len, timeout); +} + +static int tts_websocket_client_send_with_opcode(sim_esp_websocket_client_handle_t client, ws_transport_opcodes_t opcode, const char *data, int len, TickType_t timeout) +{ + int need_write = len; + int wlen = 0, widx = 0; + int ret = ESP_FAIL; + UINT32 current_opcode = opcode; + OSA_STATUS status; + + if (client == NULL || len < 0 || + (opcode != WS_TRANSPORT_OPCODES_CLOSE && (data == NULL || len <= 0))) { + ESP_LOGE("Invalid arguments"); + return ESP_FAIL; + } + + status = OSAMutexLock(client->lock, timeout); + if (status != OS_SUCCESS) { + ESP_LOGE("%s: get lock failed and return error", __FUNCTION__); + return ESP_FAIL; + } + if (!tts_websocket_client_is_connected(client)) { + ESP_LOGE("Websocket client is not connected"); + goto unlock_and_return; + } + + if (client->transport == NULL) { + ESP_LOGE("Invalid transport"); + goto unlock_and_return; + } + + while (widx < len || current_opcode) { // allow for sending "current_opcode" only message with len==0 + if (need_write > client->buffer_size) { + need_write = client->buffer_size; + } else { + current_opcode |= WS_TRANSPORT_OPCODES_FIN; + } + memcpy(client->tx_buffer, data + widx, need_write); + // send with ws specific way and specific opcode + wlen = wb_esp_transport_ws_send_raw(client->transport, current_opcode, (char *)client->tx_buffer, need_write, + (timeout==portMAX_DELAY)? -1 : timeout * sim_portTICK_PERIOD_MS); + if (wlen < 0 || (wlen == 0 && need_write != 0)) { + ret = wlen; + ESP_LOGE("Network error: esp_transport_write() returned %d, errno=%d", ret, errno); + tts_websocket_client_abort_connection(client); + goto unlock_and_return; + } + current_opcode = 0; + widx += wlen; + need_write = len - widx; + } + + ret = widx; +unlock_and_return: + OSAMutexUnlock(client->lock); + return ret; +} + +bool tts_websocket_client_is_connected(sim_esp_websocket_client_handle_t client) +{ + if (client == NULL) { + return false; + } + return client->state == WEBSOCKET_STATE_CONNECTED; +} + +esp_err_t tts_websocket_register_events(sim_esp_websocket_client_handle_t client, + sim_esp_event_handler_t event_handler) +{ + if (client == NULL) { + return -1; + } + ESP_LOGD("client handler register success!!"); + client->handler = event_handler; + return 0; +}